@automatelab/citation-intelligence 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +302 -0
- package/dist/adapters/anthropic.d.ts +3 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +52 -0
- package/dist/adapters/anthropic.js.map +1 -0
- package/dist/adapters/bing.d.ts +3 -0
- package/dist/adapters/bing.d.ts.map +1 -0
- package/dist/adapters/bing.js +31 -0
- package/dist/adapters/bing.js.map +1 -0
- package/dist/adapters/gemini.d.ts +3 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +47 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/openai.d.ts +3 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +49 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/perplexity.d.ts +3 -0
- package/dist/adapters/perplexity.d.ts.map +1 -0
- package/dist/adapters/perplexity.js +55 -0
- package/dist/adapters/perplexity.js.map +1 -0
- package/dist/adapters/predictors.d.ts +42 -0
- package/dist/adapters/predictors.d.ts.map +1 -0
- package/dist/adapters/predictors.js +495 -0
- package/dist/adapters/predictors.js.map +1 -0
- package/dist/adapters/serpapi.d.ts +8 -0
- package/dist/adapters/serpapi.d.ts.map +1 -0
- package/dist/adapters/serpapi.js +45 -0
- package/dist/adapters/serpapi.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache.d.ts +33 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +110 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +12 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/fetch.d.ts +18 -0
- package/dist/lib/fetch.d.ts.map +1 -0
- package/dist/lib/fetch.js +75 -0
- package/dist/lib/fetch.js.map +1 -0
- package/dist/lib/log.d.ts +11 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +37 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/panels.d.ts +30 -0
- package/dist/lib/panels.d.ts.map +1 -0
- package/dist/lib/panels.js +69 -0
- package/dist/lib/panels.js.map +1 -0
- package/dist/tools/ai-overview.d.ts +29 -0
- package/dist/tools/ai-overview.d.ts.map +1 -0
- package/dist/tools/ai-overview.js +47 -0
- package/dist/tools/ai-overview.js.map +1 -0
- package/dist/tools/am-i-cited.d.ts +39 -0
- package/dist/tools/am-i-cited.d.ts.map +1 -0
- package/dist/tools/am-i-cited.js +73 -0
- package/dist/tools/am-i-cited.js.map +1 -0
- package/dist/tools/audit-sitemap.d.ts +49 -0
- package/dist/tools/audit-sitemap.d.ts.map +1 -0
- package/dist/tools/audit-sitemap.js +100 -0
- package/dist/tools/audit-sitemap.js.map +1 -0
- package/dist/tools/check-citations.d.ts +30 -0
- package/dist/tools/check-citations.d.ts.map +1 -0
- package/dist/tools/check-citations.js +101 -0
- package/dist/tools/check-citations.js.map +1 -0
- package/dist/tools/citation-freshness-score.d.ts +53 -0
- package/dist/tools/citation-freshness-score.d.ts.map +1 -0
- package/dist/tools/citation-freshness-score.js +89 -0
- package/dist/tools/citation-freshness-score.js.map +1 -0
- package/dist/tools/citation-trend.d.ts +47 -0
- package/dist/tools/citation-trend.d.ts.map +1 -0
- package/dist/tools/citation-trend.js +48 -0
- package/dist/tools/citation-trend.js.map +1 -0
- package/dist/tools/cited-for-diff.d.ts +54 -0
- package/dist/tools/cited-for-diff.d.ts.map +1 -0
- package/dist/tools/cited-for-diff.js +89 -0
- package/dist/tools/cited-for-diff.js.map +1 -0
- package/dist/tools/cited-for.d.ts +39 -0
- package/dist/tools/cited-for.d.ts.map +1 -0
- package/dist/tools/cited-for.js +28 -0
- package/dist/tools/cited-for.js.map +1 -0
- package/dist/tools/compare-domains.d.ts +36 -0
- package/dist/tools/compare-domains.d.ts.map +1 -0
- package/dist/tools/compare-domains.js +54 -0
- package/dist/tools/compare-domains.js.map +1 -0
- package/dist/tools/compete-for-query.d.ts +72 -0
- package/dist/tools/compete-for-query.d.ts.map +1 -0
- package/dist/tools/compete-for-query.js +91 -0
- package/dist/tools/compete-for-query.js.map +1 -0
- package/dist/tools/gsc-citation-gap.d.ts +77 -0
- package/dist/tools/gsc-citation-gap.d.ts.map +1 -0
- package/dist/tools/gsc-citation-gap.js +121 -0
- package/dist/tools/gsc-citation-gap.js.map +1 -0
- package/dist/tools/predict-citation.d.ts +25 -0
- package/dist/tools/predict-citation.d.ts.map +1 -0
- package/dist/tools/predict-citation.js +32 -0
- package/dist/tools/predict-citation.js.map +1 -0
- package/dist/tools/run-panel.d.ts +46 -0
- package/dist/tools/run-panel.d.ts.map +1 -0
- package/dist/tools/run-panel.js +60 -0
- package/dist/tools/run-panel.js.map +1 -0
- package/dist/tools/track-queries.d.ts +52 -0
- package/dist/tools/track-queries.d.ts.map +1 -0
- package/dist/tools/track-queries.js +52 -0
- package/dist/tools/track-queries.js.map +1 -0
- package/dist/tools/wikipedia-mentions.d.ts +32 -0
- package/dist/tools/wikipedia-mentions.d.ts.map +1 -0
- package/dist/tools/wikipedia-mentions.js +56 -0
- package/dist/tools/wikipedia-mentions.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Citation Intelligence MCP - entrypoint.
|
|
3
|
+
// All logging goes to stderr. stdout is reserved for JSON-RPC transport.
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { checkCitations, checkCitationsInputSchema, } from "./tools/check-citations.js";
|
|
7
|
+
import { amICited, amICitedInputSchema } from "./tools/am-i-cited.js";
|
|
8
|
+
import { aiOverview, aiOverviewInputSchema } from "./tools/ai-overview.js";
|
|
9
|
+
import { citedFor, citedForInputSchema } from "./tools/cited-for.js";
|
|
10
|
+
import { predictCitation, predictCitationInputSchema, } from "./tools/predict-citation.js";
|
|
11
|
+
import { trackQueries, trackQueriesInputSchema } from "./tools/track-queries.js";
|
|
12
|
+
import { runPanel, runPanelInputSchema } from "./tools/run-panel.js";
|
|
13
|
+
import { citationTrend, citationTrendInputSchema } from "./tools/citation-trend.js";
|
|
14
|
+
import { compareDomains, compareDomainsInputSchema } from "./tools/compare-domains.js";
|
|
15
|
+
import { wikipediaMentions, wikipediaMentionsInputSchema } from "./tools/wikipedia-mentions.js";
|
|
16
|
+
import { auditSitemap, auditSitemapInputSchema } from "./tools/audit-sitemap.js";
|
|
17
|
+
import { gscCitationGap, gscCitationGapInputSchema } from "./tools/gsc-citation-gap.js";
|
|
18
|
+
import { competeForQuery, competeForQueryInputSchema } from "./tools/compete-for-query.js";
|
|
19
|
+
import { citationFreshnessScore, citationFreshnessScoreInputSchema } from "./tools/citation-freshness-score.js";
|
|
20
|
+
import { citedForDiff, citedForDiffInputSchema } from "./tools/cited-for-diff.js";
|
|
21
|
+
import { ToolFetchError } from "./lib/fetch.js";
|
|
22
|
+
import { log } from "./lib/log.js";
|
|
23
|
+
const server = new McpServer({
|
|
24
|
+
name: "@automatelab/citation-intelligence",
|
|
25
|
+
version: "0.4.0",
|
|
26
|
+
});
|
|
27
|
+
function toolError(err) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: JSON.stringify(err, null, 2) }],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function wrapHandler(handler) {
|
|
34
|
+
return handler()
|
|
35
|
+
.then((result) => ({
|
|
36
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
37
|
+
structuredContent: result,
|
|
38
|
+
}))
|
|
39
|
+
.catch((err) => {
|
|
40
|
+
if (err instanceof ToolFetchError) {
|
|
41
|
+
log.warn("tool returned ToolFetchError", err.toolError);
|
|
42
|
+
return toolError(err.toolError);
|
|
43
|
+
}
|
|
44
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
+
log.error("unhandled tool exception", { message });
|
|
46
|
+
return toolError({ type: "fetch_error", url: "", message });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
server.registerTool("check_citations", {
|
|
50
|
+
description: "Return URLs cited by an AI engine (Perplexity, Claude, ChatGPT, Gemini, or Bing) for a query. Use this when an agent or user wants to see what sources an AI search engine grounds answers on. Requires at least one engine API key; auto-picks the first available.",
|
|
51
|
+
inputSchema: checkCitationsInputSchema,
|
|
52
|
+
}, (args) => wrapHandler(() => checkCitations(args)));
|
|
53
|
+
server.registerTool("am_i_cited", {
|
|
54
|
+
description: "Check whether a domain is cited by an AI engine across a cluster of queries. Returns per-query presence, rank, and a citation-rate summary. Use to measure visibility for a brand, product, or content site in AI search.",
|
|
55
|
+
inputSchema: amICitedInputSchema,
|
|
56
|
+
}, (args) => wrapHandler(() => amICited(args)));
|
|
57
|
+
server.registerTool("ai_overview", {
|
|
58
|
+
description: "Check whether Google shows an AI Overview for a query, and which URLs it cites. Uses SerpAPI (free tier: 100/month). Set SERPAPI_KEY.",
|
|
59
|
+
inputSchema: aiOverviewInputSchema,
|
|
60
|
+
}, (args) => wrapHandler(() => aiOverview(args)));
|
|
61
|
+
server.registerTool("cited_for", {
|
|
62
|
+
description: "List queries that the given domain has been cited for, served from the local cache. Build up a corpus by calling check_citations or am_i_cited first; cited_for queries it without spending API budget.",
|
|
63
|
+
inputSchema: citedForInputSchema,
|
|
64
|
+
}, (args) => wrapHandler(() => citedFor(args)));
|
|
65
|
+
server.registerTool("predict_citation", {
|
|
66
|
+
description: "Score citation likelihood for a URL from public signals (Wikipedia link presence, schema.org markup, /llms.txt, GitHub and Reddit references, canonical hygiene, HTTPS). No LLM fired - all heuristic. Returns 0-100 score, grade, signal breakdown, and ranked fixes.",
|
|
67
|
+
inputSchema: predictCitationInputSchema,
|
|
68
|
+
}, (args) => wrapHandler(() => predictCitation(args)));
|
|
69
|
+
server.registerTool("track_queries", {
|
|
70
|
+
description: "Save, load, or list named query panels. A panel is a persisted set of queries you want to monitor over time (e.g. editorial-watchlist). Use action=save with queries[] to create, action=load to read, action=list to enumerate. Panels live under <config>/panels/<name>.json.",
|
|
71
|
+
inputSchema: trackQueriesInputSchema,
|
|
72
|
+
}, (args) => wrapHandler(() => trackQueries(args)));
|
|
73
|
+
server.registerTool("run_panel", {
|
|
74
|
+
description: "Run a saved panel through am_i_cited and append a timestamped snapshot. Snapshots live under <config>/snapshots/<panel>/<iso>.json. Feeds citation_trend.",
|
|
75
|
+
inputSchema: runPanelInputSchema,
|
|
76
|
+
}, (args) => wrapHandler(() => runPanel(args)));
|
|
77
|
+
server.registerTool("citation_trend", {
|
|
78
|
+
description: "Report citation rate over time for a panel from stored snapshots. Returns the series of citation_rate per snapshot plus per-query deltas (gained/lost/unchanged) between first and last snapshot.",
|
|
79
|
+
inputSchema: citationTrendInputSchema,
|
|
80
|
+
}, (args) => wrapHandler(() => citationTrend(args)));
|
|
81
|
+
server.registerTool("compare_domains", {
|
|
82
|
+
description: "Run predict_citation on 2-10 URLs and return a side-by-side signal table plus a list of signals where the URLs diverge. Use to compare your URL to top-cited competitors for the same query.",
|
|
83
|
+
inputSchema: compareDomainsInputSchema,
|
|
84
|
+
}, (args) => wrapHandler(() => compareDomains(args)));
|
|
85
|
+
server.registerTool("wikipedia_mentions", {
|
|
86
|
+
description: "List Wikipedia articles that reference the given domain. Wikipedia citation is the highest-lift signal for LLM training corpora. Zero keys required.",
|
|
87
|
+
inputSchema: wikipediaMentionsInputSchema,
|
|
88
|
+
}, (args) => wrapHandler(() => wikipediaMentions(args)));
|
|
89
|
+
server.registerTool("audit_sitemap", {
|
|
90
|
+
description: "Fetch a sitemap.xml (or sitemap index) and run predict_citation on every URL. Returns results sorted worst-score-first. Surfaces systemic issues across a whole site in one pass. Zero engine keys needed.",
|
|
91
|
+
inputSchema: auditSitemapInputSchema,
|
|
92
|
+
}, (args) => wrapHandler(() => auditSitemap(args)));
|
|
93
|
+
server.registerTool("compete_for_query", {
|
|
94
|
+
description: "End-to-end competitive snapshot for a single query. Calls check_citations to get the cited URLs, then runs compare_domains on your_url vs the top cited competitors. Returns your score, the average competitor score, and the gap.",
|
|
95
|
+
inputSchema: competeForQueryInputSchema,
|
|
96
|
+
}, (args) => wrapHandler(() => competeForQuery(args)));
|
|
97
|
+
server.registerTool("citation_freshness_score", {
|
|
98
|
+
description: "Score how recent the pages cited for a query are. Calls check_citations, then collects dateModified for each cited URL, returns a 0-100 recency_score (halflife=365d) plus per-URL freshness bucket (fresh/current/stale/ancient/unknown). Surfaces queries where AI cites old content - opportunity to ship fresher.",
|
|
99
|
+
inputSchema: citationFreshnessScoreInputSchema,
|
|
100
|
+
}, (args) => wrapHandler(() => citationFreshnessScore(args)));
|
|
101
|
+
server.registerTool("cited_for_diff", {
|
|
102
|
+
description: "Diff cited_for between two time windows for a domain. Returns queries gained (cited now, not before baseline_until) and queries lost (cited before, not since current_since). Cache-only, no API spend. Use to track citation drift over time after publishing or migrating content.",
|
|
103
|
+
inputSchema: citedForDiffInputSchema,
|
|
104
|
+
}, (args) => wrapHandler(() => citedForDiff(args)));
|
|
105
|
+
server.registerTool("gsc_citation_gap", {
|
|
106
|
+
description: "Join Google Search Console performance with am_i_cited per query. Surfaces queries where the domain ranks well in Google but is not cited in AI - the closest editorial wins. Requires GCP service account creds (credentials_path or GOOGLE_APPLICATION_CREDENTIALS env).",
|
|
107
|
+
inputSchema: gscCitationGapInputSchema,
|
|
108
|
+
}, (args) => wrapHandler(() => gscCitationGap(args)));
|
|
109
|
+
const transport = new StdioServerTransport();
|
|
110
|
+
await server.connect(transport);
|
|
111
|
+
log.info("server ready on stdio", {
|
|
112
|
+
version: "0.4.0",
|
|
113
|
+
log_level: log.level(),
|
|
114
|
+
tools: [
|
|
115
|
+
"check_citations",
|
|
116
|
+
"am_i_cited",
|
|
117
|
+
"ai_overview",
|
|
118
|
+
"cited_for",
|
|
119
|
+
"predict_citation",
|
|
120
|
+
"track_queries",
|
|
121
|
+
"run_panel",
|
|
122
|
+
"citation_trend",
|
|
123
|
+
"compare_domains",
|
|
124
|
+
"wikipedia_mentions",
|
|
125
|
+
"audit_sitemap",
|
|
126
|
+
"gsc_citation_gap",
|
|
127
|
+
"compete_for_query",
|
|
128
|
+
"citation_freshness_score",
|
|
129
|
+
"cited_for_diff",
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Citation, Engine } from "../types.js";
|
|
2
|
+
type CitationEntry = {
|
|
3
|
+
type: "citation_check";
|
|
4
|
+
engine: Engine;
|
|
5
|
+
query: string;
|
|
6
|
+
fetched_at: string;
|
|
7
|
+
citations: Citation[];
|
|
8
|
+
raw_answer?: string;
|
|
9
|
+
};
|
|
10
|
+
type AiOverviewEntry = {
|
|
11
|
+
type: "ai_overview";
|
|
12
|
+
query: string;
|
|
13
|
+
location?: string;
|
|
14
|
+
hl?: string;
|
|
15
|
+
fetched_at: string;
|
|
16
|
+
ai_overview_present: boolean;
|
|
17
|
+
ai_overview_text?: string;
|
|
18
|
+
sources: Citation[];
|
|
19
|
+
};
|
|
20
|
+
export type CacheEntry = CitationEntry | AiOverviewEntry;
|
|
21
|
+
export declare function getCitations(query: string, engine: Engine): Promise<CitationEntry | null>;
|
|
22
|
+
export declare function putCitations(entry: CitationEntry): Promise<void>;
|
|
23
|
+
export declare function getAiOverview(query: string, location: string | undefined, hl: string | undefined): Promise<AiOverviewEntry | null>;
|
|
24
|
+
export declare function putAiOverview(entry: AiOverviewEntry): Promise<void>;
|
|
25
|
+
export declare function citedForDomain(domain: string, since: string | undefined, engineFilter: string | undefined, limit: number): Promise<Array<{
|
|
26
|
+
query: string;
|
|
27
|
+
engine: Engine;
|
|
28
|
+
rank: number;
|
|
29
|
+
fetched_at: string;
|
|
30
|
+
url: string;
|
|
31
|
+
}>>;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEpD,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,eAAe,CAAC;AAwCzD,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAW/B;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAYtE;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,EAAE,EAAE,MAAM,GAAG,SAAS,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAYjC;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAazE;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CACR,KAAK,CAAC;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CACH,CAkCA"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { CACHE_FILE, CACHE_TTL_DAYS, AI_OVERVIEW_TTL_DAYS } from "./config.js";
|
|
4
|
+
const EMPTY = { version: 1, entries: [] };
|
|
5
|
+
let memory = null;
|
|
6
|
+
async function load() {
|
|
7
|
+
if (memory)
|
|
8
|
+
return memory;
|
|
9
|
+
try {
|
|
10
|
+
const raw = await readFile(CACHE_FILE, "utf8");
|
|
11
|
+
const parsed = JSON.parse(raw);
|
|
12
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {
|
|
13
|
+
memory = { ...EMPTY };
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
memory = parsed;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
memory = { ...EMPTY };
|
|
21
|
+
}
|
|
22
|
+
return memory;
|
|
23
|
+
}
|
|
24
|
+
async function persist() {
|
|
25
|
+
if (!memory)
|
|
26
|
+
return;
|
|
27
|
+
await mkdir(dirname(CACHE_FILE), { recursive: true });
|
|
28
|
+
await writeFile(CACHE_FILE, JSON.stringify(memory, null, 2), "utf8");
|
|
29
|
+
}
|
|
30
|
+
function isFresh(fetched_at, ttlDays) {
|
|
31
|
+
const t = Date.parse(fetched_at);
|
|
32
|
+
if (Number.isNaN(t))
|
|
33
|
+
return false;
|
|
34
|
+
const ageMs = Date.now() - t;
|
|
35
|
+
return ageMs < ttlDays * 86_400_000;
|
|
36
|
+
}
|
|
37
|
+
export async function getCitations(query, engine) {
|
|
38
|
+
const cache = await load();
|
|
39
|
+
const hit = cache.entries.find((e) => e.type === "citation_check" &&
|
|
40
|
+
e.engine === engine &&
|
|
41
|
+
e.query.toLowerCase() === query.toLowerCase());
|
|
42
|
+
if (!hit)
|
|
43
|
+
return null;
|
|
44
|
+
if (!isFresh(hit.fetched_at, CACHE_TTL_DAYS))
|
|
45
|
+
return null;
|
|
46
|
+
return hit;
|
|
47
|
+
}
|
|
48
|
+
export async function putCitations(entry) {
|
|
49
|
+
const cache = await load();
|
|
50
|
+
cache.entries = cache.entries.filter((e) => !(e.type === "citation_check" &&
|
|
51
|
+
e.engine === entry.engine &&
|
|
52
|
+
e.query.toLowerCase() === entry.query.toLowerCase()));
|
|
53
|
+
cache.entries.push(entry);
|
|
54
|
+
await persist();
|
|
55
|
+
}
|
|
56
|
+
export async function getAiOverview(query, location, hl) {
|
|
57
|
+
const cache = await load();
|
|
58
|
+
const hit = cache.entries.find((e) => e.type === "ai_overview" &&
|
|
59
|
+
e.query.toLowerCase() === query.toLowerCase() &&
|
|
60
|
+
(e.location ?? "") === (location ?? "") &&
|
|
61
|
+
(e.hl ?? "") === (hl ?? ""));
|
|
62
|
+
if (!hit)
|
|
63
|
+
return null;
|
|
64
|
+
if (!isFresh(hit.fetched_at, AI_OVERVIEW_TTL_DAYS))
|
|
65
|
+
return null;
|
|
66
|
+
return hit;
|
|
67
|
+
}
|
|
68
|
+
export async function putAiOverview(entry) {
|
|
69
|
+
const cache = await load();
|
|
70
|
+
cache.entries = cache.entries.filter((e) => !(e.type === "ai_overview" &&
|
|
71
|
+
e.query.toLowerCase() === entry.query.toLowerCase() &&
|
|
72
|
+
(e.location ?? "") === (entry.location ?? "") &&
|
|
73
|
+
(e.hl ?? "") === (entry.hl ?? "")));
|
|
74
|
+
cache.entries.push(entry);
|
|
75
|
+
await persist();
|
|
76
|
+
}
|
|
77
|
+
export async function citedForDomain(domain, since, engineFilter, limit) {
|
|
78
|
+
const cache = await load();
|
|
79
|
+
const sinceMs = since ? Date.parse(since) : 0;
|
|
80
|
+
const out = [];
|
|
81
|
+
const needle = domain.replace(/^https?:\/\//, "").replace(/\/$/, "").toLowerCase();
|
|
82
|
+
for (const e of cache.entries) {
|
|
83
|
+
if (e.type !== "citation_check")
|
|
84
|
+
continue;
|
|
85
|
+
if (engineFilter && e.engine !== engineFilter)
|
|
86
|
+
continue;
|
|
87
|
+
if (sinceMs && Date.parse(e.fetched_at) < sinceMs)
|
|
88
|
+
continue;
|
|
89
|
+
for (const c of e.citations) {
|
|
90
|
+
try {
|
|
91
|
+
const u = new URL(c.url);
|
|
92
|
+
if (u.hostname.toLowerCase().endsWith(needle)) {
|
|
93
|
+
out.push({
|
|
94
|
+
query: e.query,
|
|
95
|
+
engine: e.engine,
|
|
96
|
+
rank: c.rank,
|
|
97
|
+
fetched_at: e.fetched_at,
|
|
98
|
+
url: c.url,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// skip malformed URLs
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
out.sort((a, b) => b.fetched_at.localeCompare(a.fetched_at));
|
|
108
|
+
return out.slice(0, limit);
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const CONFIG_DIR: string;
|
|
2
|
+
export declare const CACHE_FILE: string;
|
|
3
|
+
export declare const CACHE_TTL_DAYS: number;
|
|
4
|
+
export declare const AI_OVERVIEW_TTL_DAYS: number;
|
|
5
|
+
export declare function envKey(name: string): string | undefined;
|
|
6
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,QAE8B,CAAC;AAEtD,eAAO,MAAM,UAAU,QAAiC,CAAC;AAEzD,eAAO,MAAM,cAAc,QAE1B,CAAC;AAEF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGvD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const CONFIG_DIR = process.env.CITATION_CONFIG_DIR ??
|
|
4
|
+
join(homedir(), ".config", "citation-intelligence");
|
|
5
|
+
export const CACHE_FILE = join(CONFIG_DIR, "cache.json");
|
|
6
|
+
export const CACHE_TTL_DAYS = Number(process.env.CITATION_CACHE_TTL_DAYS ?? "7");
|
|
7
|
+
export const AI_OVERVIEW_TTL_DAYS = Number(process.env.CITATION_AI_OVERVIEW_TTL_DAYS ?? "1");
|
|
8
|
+
export function envKey(name) {
|
|
9
|
+
const v = process.env[name];
|
|
10
|
+
return v && v.trim().length > 0 ? v.trim() : undefined;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC,mBAAmB;IAC/B,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,uBAAuB,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAClC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,CAC3C,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CACxC,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,GAAG,CACjD,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolError } from "../types.js";
|
|
2
|
+
export declare class ToolFetchError extends Error {
|
|
3
|
+
toolError: ToolError;
|
|
4
|
+
constructor(toolError: ToolError);
|
|
5
|
+
}
|
|
6
|
+
export type FetchOpts = {
|
|
7
|
+
method?: "GET" | "POST";
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
body?: string;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function fetchJson<T = unknown>(url: string, opts?: FetchOpts): Promise<T>;
|
|
13
|
+
export declare function fetchText(url: string, opts?: FetchOpts): Promise<{
|
|
14
|
+
text: string;
|
|
15
|
+
status: number;
|
|
16
|
+
finalUrl: string;
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/lib/fetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAS,EAAE,SAAS,CAAC;gBACT,SAAS,EAAE,SAAS;CAIjC;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EACzC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,CAAC,CAAC,CAsCZ;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmB7D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { request } from "undici";
|
|
2
|
+
export class ToolFetchError extends Error {
|
|
3
|
+
toolError;
|
|
4
|
+
constructor(toolError) {
|
|
5
|
+
super(toolError.message);
|
|
6
|
+
this.toolError = toolError;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export async function fetchJson(url, opts = {}) {
|
|
10
|
+
const { method = "GET", headers = {}, body, timeoutMs = 30_000 } = opts;
|
|
11
|
+
const ac = new AbortController();
|
|
12
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
13
|
+
try {
|
|
14
|
+
const res = await request(url, {
|
|
15
|
+
method,
|
|
16
|
+
headers: { "user-agent": "citation-intelligence-mcp/0.1", ...headers },
|
|
17
|
+
body,
|
|
18
|
+
signal: ac.signal,
|
|
19
|
+
});
|
|
20
|
+
const text = await res.body.text();
|
|
21
|
+
if (res.statusCode >= 400) {
|
|
22
|
+
throw new ToolFetchError({
|
|
23
|
+
type: "fetch_error",
|
|
24
|
+
url,
|
|
25
|
+
status: res.statusCode,
|
|
26
|
+
message: `${res.statusCode}: ${text.slice(0, 500)}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (!text)
|
|
30
|
+
return undefined;
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(text);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new ToolFetchError({
|
|
36
|
+
type: "fetch_error",
|
|
37
|
+
url,
|
|
38
|
+
status: res.statusCode,
|
|
39
|
+
message: `non-JSON response: ${text.slice(0, 200)}`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof ToolFetchError)
|
|
45
|
+
throw err;
|
|
46
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
47
|
+
throw new ToolFetchError({ type: "fetch_error", url, message: msg });
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function fetchText(url, opts = {}) {
|
|
54
|
+
const { method = "GET", headers = {}, body, timeoutMs = 30_000 } = opts;
|
|
55
|
+
const ac = new AbortController();
|
|
56
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
57
|
+
try {
|
|
58
|
+
const res = await request(url, {
|
|
59
|
+
method,
|
|
60
|
+
headers: { "user-agent": "citation-intelligence-mcp/0.1", ...headers },
|
|
61
|
+
body,
|
|
62
|
+
signal: ac.signal,
|
|
63
|
+
});
|
|
64
|
+
const text = await res.body.text();
|
|
65
|
+
return { text, status: res.statusCode, finalUrl: url };
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
69
|
+
throw new ToolFetchError({ type: "fetch_error", url, message: msg });
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/lib/fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
2
|
+
declare function resolveLevel(): LogLevel;
|
|
3
|
+
export declare const log: {
|
|
4
|
+
debug: (msg: string, extra?: unknown) => void;
|
|
5
|
+
info: (msg: string, extra?: unknown) => void;
|
|
6
|
+
warn: (msg: string, extra?: unknown) => void;
|
|
7
|
+
error: (msg: string, extra?: unknown) => void;
|
|
8
|
+
level: typeof resolveLevel;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAS3D,iBAAS,YAAY,IAAI,QAAQ,CAIhC;AAkBD,eAAO,MAAM,GAAG;iBACD,MAAM,UAAU,OAAO;gBACxB,MAAM,UAAU,OAAO;gBACvB,MAAM,UAAU,OAAO;iBACtB,MAAM,UAAU,OAAO;;CAErC,CAAC"}
|
package/dist/lib/log.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Stderr logger. stdout is reserved for JSON-RPC transport.
|
|
2
|
+
// Level controlled by CITATION_LOG_LEVEL env var: debug | info | warn | error (default: info).
|
|
3
|
+
const LEVELS = {
|
|
4
|
+
debug: 10,
|
|
5
|
+
info: 20,
|
|
6
|
+
warn: 30,
|
|
7
|
+
error: 40,
|
|
8
|
+
};
|
|
9
|
+
function resolveLevel() {
|
|
10
|
+
const raw = (process.env.CITATION_LOG_LEVEL ?? "info").toLowerCase();
|
|
11
|
+
if (raw === "debug" || raw === "info" || raw === "warn" || raw === "error")
|
|
12
|
+
return raw;
|
|
13
|
+
return "info";
|
|
14
|
+
}
|
|
15
|
+
const TAG = "[citation-intelligence]";
|
|
16
|
+
function emit(level, msg, extra) {
|
|
17
|
+
if (LEVELS[level] < LEVELS[resolveLevel()])
|
|
18
|
+
return;
|
|
19
|
+
const line = extra === undefined ? `${TAG} ${level}: ${msg}` : `${TAG} ${level}: ${msg} ${safeJson(extra)}`;
|
|
20
|
+
process.stderr.write(line + "\n");
|
|
21
|
+
}
|
|
22
|
+
function safeJson(v) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.stringify(v);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return String(v);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export const log = {
|
|
31
|
+
debug: (msg, extra) => emit("debug", msg, extra),
|
|
32
|
+
info: (msg, extra) => emit("info", msg, extra),
|
|
33
|
+
warn: (msg, extra) => emit("warn", msg, extra),
|
|
34
|
+
error: (msg, extra) => emit("error", msg, extra),
|
|
35
|
+
level: resolveLevel,
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,+FAA+F;AAI/F,MAAM,MAAM,GAA6B;IACvC,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;CACV,CAAC;AAEF,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IACrE,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC;IACvF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,GAAG,GAAG,yBAAyB,CAAC;AAEtC,SAAS,IAAI,CAAC,KAAe,EAAE,GAAW,EAAE,KAAe;IACzD,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;QAAE,OAAO;IACnD,MAAM,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5G,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC1B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,KAAK,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC;IAClE,IAAI,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;IAChE,IAAI,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;IAChE,KAAK,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC;IAClE,KAAK,EAAE,YAAY;CACpB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type Panel = {
|
|
2
|
+
name: string;
|
|
3
|
+
queries: string[];
|
|
4
|
+
domain?: string;
|
|
5
|
+
created_at: string;
|
|
6
|
+
updated_at: string;
|
|
7
|
+
};
|
|
8
|
+
export type Snapshot = {
|
|
9
|
+
panel: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
engine: string;
|
|
12
|
+
taken_at: string;
|
|
13
|
+
per_query: Array<{
|
|
14
|
+
query: string;
|
|
15
|
+
cited: boolean;
|
|
16
|
+
rank?: number;
|
|
17
|
+
matching_urls: string[];
|
|
18
|
+
}>;
|
|
19
|
+
summary: {
|
|
20
|
+
queries_total: number;
|
|
21
|
+
queries_cited: number;
|
|
22
|
+
citation_rate: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare function savePanel(panel: Panel): Promise<void>;
|
|
26
|
+
export declare function loadPanel(name: string): Promise<Panel | null>;
|
|
27
|
+
export declare function listPanels(): Promise<string[]>;
|
|
28
|
+
export declare function appendSnapshot(snap: Snapshot): Promise<string>;
|
|
29
|
+
export declare function readSnapshots(panel: string, since?: string): Promise<Snapshot[]>;
|
|
30
|
+
//# sourceMappingURL=panels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../src/lib/panels.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC,CAAC;IACH,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH,CAAC;AAiBF,wBAAsB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAOnE;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpD;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAOpE;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,QAAQ,EAAE,CAAC,CAoBrB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { CONFIG_DIR } from "./config.js";
|
|
4
|
+
const PANEL_DIR = join(CONFIG_DIR, "panels");
|
|
5
|
+
const SNAPSHOT_DIR = join(CONFIG_DIR, "snapshots");
|
|
6
|
+
function panelFile(name) {
|
|
7
|
+
return join(PANEL_DIR, `${sanitize(name)}.json`);
|
|
8
|
+
}
|
|
9
|
+
function snapshotDir(name) {
|
|
10
|
+
return join(SNAPSHOT_DIR, sanitize(name));
|
|
11
|
+
}
|
|
12
|
+
function sanitize(name) {
|
|
13
|
+
return name.replace(/[^a-z0-9_-]/gi, "_").toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
export async function savePanel(panel) {
|
|
16
|
+
await mkdir(PANEL_DIR, { recursive: true });
|
|
17
|
+
await writeFile(panelFile(panel.name), JSON.stringify(panel, null, 2), "utf8");
|
|
18
|
+
}
|
|
19
|
+
export async function loadPanel(name) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = await readFile(panelFile(name), "utf8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function listPanels() {
|
|
29
|
+
try {
|
|
30
|
+
const files = await readdir(PANEL_DIR);
|
|
31
|
+
return files.filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function appendSnapshot(snap) {
|
|
38
|
+
const dir = snapshotDir(snap.panel);
|
|
39
|
+
await mkdir(dir, { recursive: true });
|
|
40
|
+
const filename = `${snap.taken_at.replace(/[:.]/g, "-")}.json`;
|
|
41
|
+
const path = join(dir, filename);
|
|
42
|
+
await writeFile(path, JSON.stringify(snap, null, 2), "utf8");
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
export async function readSnapshots(panel, since) {
|
|
46
|
+
const dir = snapshotDir(panel);
|
|
47
|
+
let files = [];
|
|
48
|
+
try {
|
|
49
|
+
files = await readdir(dir);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const snapshots = [];
|
|
55
|
+
for (const f of files.filter((f) => f.endsWith(".json")).sort()) {
|
|
56
|
+
try {
|
|
57
|
+
const raw = await readFile(join(dir, f), "utf8");
|
|
58
|
+
const snap = JSON.parse(raw);
|
|
59
|
+
if (since && snap.taken_at < since)
|
|
60
|
+
continue;
|
|
61
|
+
snapshots.push(snap);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// ignore corrupt snapshot
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return snapshots;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=panels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels.js","sourceRoot":"","sources":["../../src/lib/panels.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const aiOverviewInputSchema: {
|
|
3
|
+
query: z.ZodString;
|
|
4
|
+
location: z.ZodOptional<z.ZodString>;
|
|
5
|
+
hl: z.ZodDefault<z.ZodString>;
|
|
6
|
+
};
|
|
7
|
+
declare const inputSchema: z.ZodObject<{
|
|
8
|
+
query: z.ZodString;
|
|
9
|
+
location: z.ZodOptional<z.ZodString>;
|
|
10
|
+
hl: z.ZodDefault<z.ZodString>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
query: string;
|
|
13
|
+
hl: string;
|
|
14
|
+
location?: string | undefined;
|
|
15
|
+
}, {
|
|
16
|
+
query: string;
|
|
17
|
+
hl?: string | undefined;
|
|
18
|
+
location?: string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function aiOverview(input: z.infer<typeof inputSchema>): Promise<{
|
|
21
|
+
query: string;
|
|
22
|
+
fetched_at: string;
|
|
23
|
+
ai_overview_present: boolean;
|
|
24
|
+
ai_overview_text: string | undefined;
|
|
25
|
+
sources: import("../types.js").Citation[];
|
|
26
|
+
cached: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=ai-overview.d.ts.map
|