mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 14:37:26 +00:00
Add feed discovery plugin
This commit is contained in:
parent
e47616bf9d
commit
5cfeea6b57
37 changed files with 2432 additions and 5 deletions
|
|
@ -16,6 +16,7 @@
|
|||
"dependencies": {
|
||||
"@logicsrc/plugin-core": "file:../plugin-core",
|
||||
"@logicsrc/plugin-coinpay": "file:../../plugins/coinpay",
|
||||
"@logicsrc/plugin-feed-discovery": "file:../../plugins/feed-discovery",
|
||||
"@logicsrc/plugin-ugig": "file:../../plugins/ugig",
|
||||
"@logicsrc/tui": "file:../tui",
|
||||
"@logicsrc/validators": "file:../validators",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ describe("CLI registry", () => {
|
|||
|
||||
expect(ids).toContain("coinpay");
|
||||
expect(ids).toContain("ugig");
|
||||
expect(ids).toContain("feed-discovery");
|
||||
expect(ids).not.toContain("sh1pt");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
import { readFileSync } from "node:fs";
|
||||
import { Command } from "commander";
|
||||
import { discoverFeeds, listFeedProviders, probeSite, renderDiscoveryOutput, validateFeed, type FeedKind, type FeedOutputFormat } from "@logicsrc/plugin-feed-discovery";
|
||||
import { renderArcadeList, renderPluginStatus, renderTui, runArcadeSession, type TaskSnapshot } from "@logicsrc/tui";
|
||||
import { assertSchemaKind, parseDocument, validate } from "@logicsrc/validators";
|
||||
import { getConfigValue, readConfig, setConfigValue, writeConfig } from "./config.js";
|
||||
|
|
@ -295,6 +296,76 @@ openspec
|
|||
outDir: options.out
|
||||
}), "json"));
|
||||
|
||||
const feeds = program.command("feeds").description("Discover, validate, probe, and export feed sources.");
|
||||
|
||||
feeds
|
||||
.command("discover")
|
||||
.argument("<keyword>", "Keyword, phrase, or homepage URL")
|
||||
.option("--type <type>", "Feed kind or all", "all")
|
||||
.option("--format <format>", "json, opml, rss, atom, or json-feed", "json")
|
||||
.option("--limit <limit>", "Maximum results", "25")
|
||||
.option("--freshness-days <days>", "Freshness window for callers that need it")
|
||||
.option("--include-dead-feeds", "Include feeds that fail validation")
|
||||
.option("--include-unvalidated", "Return provider candidates without validation")
|
||||
.option("--providers <providers>", "Comma-separated provider ids")
|
||||
.description("Discover canonical feed URLs by keyword.")
|
||||
.action(async (keyword, options) => {
|
||||
const response = await discoverFeeds({
|
||||
q: keyword,
|
||||
type: options.type as FeedKind | "all",
|
||||
limit: Number(options.limit),
|
||||
freshnessDays: options.freshnessDays ? Number(options.freshnessDays) : undefined,
|
||||
includeDeadFeeds: Boolean(options.includeDeadFeeds),
|
||||
includeUnvalidated: Boolean(options.includeUnvalidated),
|
||||
providers: splitOption(options.providers)
|
||||
});
|
||||
console.log(renderDiscoveryOutput(response, options.format as FeedOutputFormat));
|
||||
});
|
||||
|
||||
feeds
|
||||
.command("validate")
|
||||
.argument("<feed-url>", "Feed URL")
|
||||
.option("--format <format>", "json, table, or markdown", "json")
|
||||
.description("Validate a feed URL with SSRF protections.")
|
||||
.action(async (feedUrl, options) => {
|
||||
const result = await validateFeed(feedUrl);
|
||||
print(result, options.format as OutputFormat);
|
||||
if (!result.ok) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
});
|
||||
|
||||
feeds
|
||||
.command("probe")
|
||||
.argument("<homepage-url>", "Homepage URL")
|
||||
.option("--format <format>", "json, table, or markdown", "json")
|
||||
.description("Probe a homepage for alternate feed links and common feed paths.")
|
||||
.action(async (homepageUrl, options) => {
|
||||
const result = await probeSite(homepageUrl);
|
||||
print(result, options.format as OutputFormat);
|
||||
if (result.feeds.length === 0) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
});
|
||||
|
||||
feeds
|
||||
.command("providers")
|
||||
.option("--format <format>", "table, json, or markdown", "table")
|
||||
.description("List feed discovery providers.")
|
||||
.action((options) => {
|
||||
print(listFeedProviders(), options.format as OutputFormat);
|
||||
});
|
||||
|
||||
feeds
|
||||
.command("export-opml")
|
||||
.argument("<keyword>", "Keyword or phrase")
|
||||
.option("--limit <limit>", "Maximum results", "100")
|
||||
.description("Discover feeds and print OPML.")
|
||||
.action(async (keyword, options) => {
|
||||
const response = await discoverFeeds({ q: keyword, limit: Number(options.limit) });
|
||||
console.log(renderDiscoveryOutput(response, "opml"));
|
||||
});
|
||||
|
||||
program.command("plugins").option("--format <format>", "table, json, or markdown", "table").description("Show plugin status.").action((options) => {
|
||||
const snapshot = defaultPluginRegistry().snapshot();
|
||||
print(snapshot.plugins, options.format as OutputFormat);
|
||||
|
|
@ -395,6 +466,13 @@ function resolveArcadeGame(options: { arcade?: string | boolean; waitingArcade?:
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function splitOption(value: string | undefined) {
|
||||
return value
|
||||
?.split(",")
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
async function runYoloArcade(game: string, repo?: string) {
|
||||
const task: TaskSnapshot = {
|
||||
id: "agentswarm_yolo",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { createPluginRegistry } from "@logicsrc/plugin-core";
|
||||
import { coinPayPlugin } from "@logicsrc/plugin-coinpay";
|
||||
import { feedDiscoveryPlugin } from "@logicsrc/plugin-feed-discovery";
|
||||
import { uGigPlugin } from "@logicsrc/plugin-ugig";
|
||||
|
||||
export function defaultPluginRegistry() {
|
||||
return createPluginRegistry([coinPayPlugin, uGigPlugin]);
|
||||
return createPluginRegistry([coinPayPlugin, uGigPlugin, feedDiscoveryPlugin]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue