plainstamp 0.0.1
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/AI-DISCLOSURE.md +39 -0
- package/CHANGELOG.md +57 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/coverage.d.ts +48 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +96 -0
- package/dist/coverage.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/lookup.d.ts +42 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +170 -0
- package/dist/lookup.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +199 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/rules-loader.d.ts +10 -0
- package/dist/rules-loader.d.ts.map +1 -0
- package/dist/rules-loader.js +23 -0
- package/dist/rules-loader.js.map +1 -0
- package/dist/schema.d.ts +526 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +96 -0
- package/dist/schema.js.map +1 -0
- package/dist/watcher/cli.d.ts +3 -0
- package/dist/watcher/cli.d.ts.map +1 -0
- package/dist/watcher/cli.js +47 -0
- package/dist/watcher/cli.js.map +1 -0
- package/dist/watcher/index.d.ts +23 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +71 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/watcher/sources/federal-register.d.ts +13 -0
- package/dist/watcher/sources/federal-register.d.ts.map +1 -0
- package/dist/watcher/sources/federal-register.js +44 -0
- package/dist/watcher/sources/federal-register.js.map +1 -0
- package/dist/watcher/sources/url-monitor.d.ts +33 -0
- package/dist/watcher/sources/url-monitor.d.ts.map +1 -0
- package/dist/watcher/sources/url-monitor.js +67 -0
- package/dist/watcher/sources/url-monitor.js.map +1 -0
- package/dist/watcher/state-store.d.ts +9 -0
- package/dist/watcher/state-store.d.ts.map +1 -0
- package/dist/watcher/state-store.js +23 -0
- package/dist/watcher/state-store.js.map +1 -0
- package/dist/watcher/types.d.ts +59 -0
- package/dist/watcher/types.d.ts.map +1 -0
- package/dist/watcher/types.js +14 -0
- package/dist/watcher/types.js.map +1 -0
- package/package.json +60 -0
- package/rules/seed.json +620 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* plainstamp-watcher — runs all configured regulatory-update sources,
|
|
4
|
+
* compares against persisted state, and emits a JSON digest of new articles
|
|
5
|
+
* to stdout.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* plainstamp-watcher [--state path/to/watcher-state.json] [--dry-run]
|
|
9
|
+
*
|
|
10
|
+
* Defaults: state file at <cwd>/watcher-state.json. Designed to be run
|
|
11
|
+
* periodically (cron, GitHub Actions, etc.) — each run records what it saw,
|
|
12
|
+
* and only newly-appeared articles are emitted.
|
|
13
|
+
*/
|
|
14
|
+
import { parseArgs } from "node:util";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { runWatcher } from "./index.js";
|
|
17
|
+
import { federalRegisterSource } from "./sources/federal-register.js";
|
|
18
|
+
import { rulesCitationsUrlMonitor } from "./sources/url-monitor.js";
|
|
19
|
+
const { values } = parseArgs({
|
|
20
|
+
options: {
|
|
21
|
+
state: { type: "string" },
|
|
22
|
+
"dry-run": { type: "boolean" },
|
|
23
|
+
},
|
|
24
|
+
strict: true,
|
|
25
|
+
});
|
|
26
|
+
const statePath = values.state ?? join(process.cwd(), "watcher-state.json");
|
|
27
|
+
const sources = [
|
|
28
|
+
federalRegisterSource({ searchTerm: "artificial intelligence" }),
|
|
29
|
+
federalRegisterSource({ searchTerm: "automated decision" }),
|
|
30
|
+
federalRegisterSource({ searchTerm: "algorithmic" }),
|
|
31
|
+
rulesCitationsUrlMonitor(),
|
|
32
|
+
];
|
|
33
|
+
const report = await runWatcher({
|
|
34
|
+
sources,
|
|
35
|
+
statePath,
|
|
36
|
+
...(values["dry-run"] === true ? { dryRun: true } : {}),
|
|
37
|
+
});
|
|
38
|
+
const totalNew = report.sources.reduce((n, s) => n + s.new_articles.length, 0);
|
|
39
|
+
console.log(JSON.stringify({
|
|
40
|
+
run_at: report.run_at,
|
|
41
|
+
state_path: statePath,
|
|
42
|
+
dry_run: values["dry-run"] === true,
|
|
43
|
+
total_new_articles: totalNew,
|
|
44
|
+
sources: report.sources,
|
|
45
|
+
}, null, 2));
|
|
46
|
+
process.exit(report.sources.some((s) => !s.ok) ? 1 : 0);
|
|
47
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/watcher/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE;QACP,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;KAC/B;IACD,MAAM,EAAE,IAAI;CACb,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,oBAAoB,CAAC,CAAC;AAE5E,MAAM,OAAO,GAAG;IACd,qBAAqB,CAAC,EAAE,UAAU,EAAE,yBAAyB,EAAE,CAAC;IAChE,qBAAqB,CAAC,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAC;IAC3D,qBAAqB,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;IACpD,wBAAwB,EAAE;CAC3B,CAAC;AAEF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;IAC9B,OAAO;IACP,SAAS;IACT,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CACxD,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,EACnC,CAAC,CACF,CAAC;AAEF,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;IACE,MAAM,EAAE,MAAM,CAAC,MAAM;IACrB,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,IAAI;IACnC,kBAAkB,EAAE,QAAQ;IAC5B,OAAO,EAAE,MAAM,CAAC,OAAO;CACxB,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;AAEF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Article, RunReport, Source, SourceRunReport, WatcherState } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Computes the new articles for a source — those whose ids do not appear in
|
|
4
|
+
* `seen`. Returns the articles in fetched-order. Pure function; safe to test.
|
|
5
|
+
*/
|
|
6
|
+
export declare function diffArticles(fetched: Article[], seen: string[]): Article[];
|
|
7
|
+
/**
|
|
8
|
+
* Runs all sources, computes the diff against persisted state, returns a
|
|
9
|
+
* report, and updates the state file with the union of (previously seen) ∪
|
|
10
|
+
* (newly fetched ids). Source fetch failures are caught and reported per-
|
|
11
|
+
* source; one failing source does not abort the run.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runWatcher(opts: {
|
|
14
|
+
sources: Source[];
|
|
15
|
+
statePath: string;
|
|
16
|
+
/** If true, do NOT persist the new state (useful for dry-run inspection). */
|
|
17
|
+
dryRun?: boolean;
|
|
18
|
+
}): Promise<RunReport>;
|
|
19
|
+
export type { Article, Source, RunReport, SourceRunReport, WatcherState };
|
|
20
|
+
export { readState, writeState } from "./state-store.js";
|
|
21
|
+
export { federalRegisterSource } from "./sources/federal-register.js";
|
|
22
|
+
export { urlMonitorSource, rulesCitationsUrlMonitor, hashContent, } from "./sources/url-monitor.js";
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,MAAM,EACN,eAAe,EACf,YAAY,EACb,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAO1E;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,SAAS,CAAC,CAiDrB;AAED,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,GACZ,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readState, writeState } from "./state-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* Computes the new articles for a source — those whose ids do not appear in
|
|
4
|
+
* `seen`. Returns the articles in fetched-order. Pure function; safe to test.
|
|
5
|
+
*/
|
|
6
|
+
export function diffArticles(fetched, seen) {
|
|
7
|
+
const seenSet = new Set(seen);
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const a of fetched) {
|
|
10
|
+
if (!seenSet.has(a.id))
|
|
11
|
+
out.push(a);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runs all sources, computes the diff against persisted state, returns a
|
|
17
|
+
* report, and updates the state file with the union of (previously seen) ∪
|
|
18
|
+
* (newly fetched ids). Source fetch failures are caught and reported per-
|
|
19
|
+
* source; one failing source does not abort the run.
|
|
20
|
+
*/
|
|
21
|
+
export async function runWatcher(opts) {
|
|
22
|
+
const state = readState(opts.statePath);
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
const sourceReports = [];
|
|
25
|
+
for (const source of opts.sources) {
|
|
26
|
+
const prev = state.sources[source.id] ?? { seen: [], lastRunAt: null };
|
|
27
|
+
let articles = [];
|
|
28
|
+
let errMsg;
|
|
29
|
+
try {
|
|
30
|
+
articles = await source.fetch();
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
errMsg = err.message;
|
|
34
|
+
}
|
|
35
|
+
if (errMsg !== undefined) {
|
|
36
|
+
sourceReports.push({
|
|
37
|
+
source_id: source.id,
|
|
38
|
+
description: source.description,
|
|
39
|
+
ok: false,
|
|
40
|
+
error: errMsg,
|
|
41
|
+
new_articles: [],
|
|
42
|
+
total_seen: prev.seen.length,
|
|
43
|
+
});
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const newArticles = diffArticles(articles, prev.seen);
|
|
47
|
+
sourceReports.push({
|
|
48
|
+
source_id: source.id,
|
|
49
|
+
description: source.description,
|
|
50
|
+
ok: true,
|
|
51
|
+
new_articles: newArticles,
|
|
52
|
+
total_seen: prev.seen.length + newArticles.length,
|
|
53
|
+
});
|
|
54
|
+
if (!opts.dryRun) {
|
|
55
|
+
const merged = new Set(prev.seen);
|
|
56
|
+
for (const a of articles)
|
|
57
|
+
merged.add(a.id);
|
|
58
|
+
state.sources[source.id] = {
|
|
59
|
+
seen: [...merged],
|
|
60
|
+
lastRunAt: now,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!opts.dryRun)
|
|
65
|
+
writeState(opts.statePath, state);
|
|
66
|
+
return { run_at: now, sources: sourceReports };
|
|
67
|
+
}
|
|
68
|
+
export { readState, writeState } from "./state-store.js";
|
|
69
|
+
export { federalRegisterSource } from "./sources/federal-register.js";
|
|
70
|
+
export { urlMonitorSource, rulesCitationsUrlMonitor, hashContent, } from "./sources/url-monitor.js";
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Source } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Federal Register source. Fetches recent rules and proposed rules matching
|
|
4
|
+
* a search term (default: "artificial intelligence"). The Federal Register
|
|
5
|
+
* search is permissive — results often include items only tangentially
|
|
6
|
+
* related — so the orchestrator should treat new articles as candidates for
|
|
7
|
+
* review rather than guaranteed plainstamp-relevant updates.
|
|
8
|
+
*/
|
|
9
|
+
export declare function federalRegisterSource(opts?: {
|
|
10
|
+
searchTerm?: string;
|
|
11
|
+
perPage?: number;
|
|
12
|
+
}): Source;
|
|
13
|
+
//# sourceMappingURL=federal-register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federal-register.d.ts","sourceRoot":"","sources":["../../../src/watcher/sources/federal-register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,aAAa,CAAC;AAmBnD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAsCT"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const ENDPOINT = "https://www.federalregister.gov/api/v1/articles.json";
|
|
2
|
+
/**
|
|
3
|
+
* Federal Register source. Fetches recent rules and proposed rules matching
|
|
4
|
+
* a search term (default: "artificial intelligence"). The Federal Register
|
|
5
|
+
* search is permissive — results often include items only tangentially
|
|
6
|
+
* related — so the orchestrator should treat new articles as candidates for
|
|
7
|
+
* review rather than guaranteed plainstamp-relevant updates.
|
|
8
|
+
*/
|
|
9
|
+
export function federalRegisterSource(opts) {
|
|
10
|
+
const term = opts?.searchTerm ?? "artificial intelligence";
|
|
11
|
+
const perPage = opts?.perPage ?? 20;
|
|
12
|
+
const id = `federal-register-${term.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`;
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
description: `Federal Register articles (Rules and Proposed Rules) matching "${term}"`,
|
|
16
|
+
async fetch() {
|
|
17
|
+
const url = new URL(ENDPOINT);
|
|
18
|
+
url.searchParams.set("conditions[term]", term);
|
|
19
|
+
url.searchParams.append("conditions[type][]", "RULE");
|
|
20
|
+
url.searchParams.append("conditions[type][]", "PRORULE");
|
|
21
|
+
url.searchParams.set("per_page", String(perPage));
|
|
22
|
+
url.searchParams.set("order", "newest");
|
|
23
|
+
const res = await fetch(url.toString());
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
throw new Error(`Federal Register API returned HTTP ${res.status} for ${url.toString()}`);
|
|
26
|
+
}
|
|
27
|
+
const json = (await res.json());
|
|
28
|
+
return json.results.map((r) => ({
|
|
29
|
+
id: r.document_number,
|
|
30
|
+
title: r.title,
|
|
31
|
+
url: r.html_url,
|
|
32
|
+
publishedAt: r.publication_date,
|
|
33
|
+
...(r.abstract !== null && r.abstract !== undefined
|
|
34
|
+
? { summary: r.abstract }
|
|
35
|
+
: {}),
|
|
36
|
+
...(r.agencies !== undefined
|
|
37
|
+
? { agencies: r.agencies.map((a) => a.name) }
|
|
38
|
+
: {}),
|
|
39
|
+
extra: { type: r.type },
|
|
40
|
+
}));
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=federal-register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federal-register.js","sourceRoot":"","sources":["../../../src/watcher/sources/federal-register.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAG,sDAAsD,CAAC;AAiBxE;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAGrC;IACC,MAAM,IAAI,GAAG,IAAI,EAAE,UAAU,IAAI,yBAAyB,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IACpC,MAAM,EAAE,GAAG,oBAAoB,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;IAEjF,OAAO;QACL,EAAE;QACF,WAAW,EAAE,kEAAkE,IAAI,GAAG;QACtF,KAAK,CAAC,KAAK;YACT,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC9B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;YAC/C,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACtD,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;YACzD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAClD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAExC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,sCAAsC,GAAG,CAAC,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,EAAE,CACzE,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAe,CAAC;YAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9B,EAAE,EAAE,CAAC,CAAC,eAAe;gBACrB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,GAAG,EAAE,CAAC,CAAC,QAAQ;gBACf,WAAW,EAAE,CAAC,CAAC,gBAAgB;gBAC/B,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;oBACjD,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;oBACzB,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS;oBAC1B,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;oBAC7C,CAAC,CAAC,EAAE,CAAC;gBACP,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;aACxB,CAAC,CAAC,CAAC;QACN,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Source } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generic URL-monitor source. Fetches a list of URLs, computes a content
|
|
4
|
+
* hash for each, and emits an article per URL whose id is `${url}#${hash}`.
|
|
5
|
+
* When a URL's content changes, the hash changes, and the new id appears as
|
|
6
|
+
* a "new article" in the diff against persisted state.
|
|
7
|
+
*
|
|
8
|
+
* Use this for tracking regulator-published source pages that don't have a
|
|
9
|
+
* dedicated API or RSS feed. Failures fetching individual URLs are isolated
|
|
10
|
+
* — one bad URL does not abort the run.
|
|
11
|
+
*/
|
|
12
|
+
export declare function urlMonitorSource(opts: {
|
|
13
|
+
id: string;
|
|
14
|
+
description: string;
|
|
15
|
+
urls: string[];
|
|
16
|
+
/** Optional fetch shim for testing. Defaults to global fetch. */
|
|
17
|
+
fetcher?: typeof fetch;
|
|
18
|
+
}): Source;
|
|
19
|
+
/**
|
|
20
|
+
* Convenience: a source that monitors every bundled rule's `citation.source_url`.
|
|
21
|
+
* When any cited statute or regulation page changes content, the next watcher
|
|
22
|
+
* run surfaces the URL as a "new" article and the agent flags the corresponding
|
|
23
|
+
* rule's `last_verified` as potentially stale.
|
|
24
|
+
*
|
|
25
|
+
* Deduplicates URLs (the same source_url could be cited by multiple rules; we
|
|
26
|
+
* fetch each unique URL once).
|
|
27
|
+
*/
|
|
28
|
+
export declare function rulesCitationsUrlMonitor(opts?: {
|
|
29
|
+
fetcher?: typeof fetch;
|
|
30
|
+
}): Source;
|
|
31
|
+
/** Internal: SHA-256 content hash truncated to 16 hex chars (64 bits) — collision-resistant for change detection at this scale. */
|
|
32
|
+
export declare function hashContent(text: string): string;
|
|
33
|
+
//# sourceMappingURL=url-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-monitor.d.ts","sourceRoot":"","sources":["../../../src/watcher/sources/url-monitor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,aAAa,CAAC;AAGnD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,GAAG,MAAM,CA6BT;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE;IAC9C,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,GAAG,MAAM,CAUT;AAED,mIAAmI;AACnI,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { loadBundledRules } from "../../rules-loader.js";
|
|
3
|
+
/**
|
|
4
|
+
* Generic URL-monitor source. Fetches a list of URLs, computes a content
|
|
5
|
+
* hash for each, and emits an article per URL whose id is `${url}#${hash}`.
|
|
6
|
+
* When a URL's content changes, the hash changes, and the new id appears as
|
|
7
|
+
* a "new article" in the diff against persisted state.
|
|
8
|
+
*
|
|
9
|
+
* Use this for tracking regulator-published source pages that don't have a
|
|
10
|
+
* dedicated API or RSS feed. Failures fetching individual URLs are isolated
|
|
11
|
+
* — one bad URL does not abort the run.
|
|
12
|
+
*/
|
|
13
|
+
export function urlMonitorSource(opts) {
|
|
14
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
15
|
+
const today = () => new Date().toISOString().slice(0, 10);
|
|
16
|
+
return {
|
|
17
|
+
id: opts.id,
|
|
18
|
+
description: opts.description,
|
|
19
|
+
async fetch() {
|
|
20
|
+
const articles = [];
|
|
21
|
+
for (const url of opts.urls) {
|
|
22
|
+
try {
|
|
23
|
+
const res = await fetcher(url, { redirect: "follow" });
|
|
24
|
+
if (!res.ok)
|
|
25
|
+
continue;
|
|
26
|
+
const text = await res.text();
|
|
27
|
+
const hash = hashContent(text);
|
|
28
|
+
articles.push({
|
|
29
|
+
id: `${url}#${hash}`,
|
|
30
|
+
title: url,
|
|
31
|
+
url,
|
|
32
|
+
publishedAt: today(),
|
|
33
|
+
extra: { content_hash: hash, content_length: text.length },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Transient fetch failure — skip this URL for this run, try again next time.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return articles;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Convenience: a source that monitors every bundled rule's `citation.source_url`.
|
|
46
|
+
* When any cited statute or regulation page changes content, the next watcher
|
|
47
|
+
* run surfaces the URL as a "new" article and the agent flags the corresponding
|
|
48
|
+
* rule's `last_verified` as potentially stale.
|
|
49
|
+
*
|
|
50
|
+
* Deduplicates URLs (the same source_url could be cited by multiple rules; we
|
|
51
|
+
* fetch each unique URL once).
|
|
52
|
+
*/
|
|
53
|
+
export function rulesCitationsUrlMonitor(opts) {
|
|
54
|
+
const rules = loadBundledRules();
|
|
55
|
+
const urls = [...new Set(rules.rules.map((r) => r.citation.source_url))];
|
|
56
|
+
return urlMonitorSource({
|
|
57
|
+
id: "rules-citation-urls",
|
|
58
|
+
description: "Hash-based change detection on every bundled rule's citation source URL. Surfaces a new article when any cited regulator-published page's content has changed since the last successful run.",
|
|
59
|
+
urls,
|
|
60
|
+
...(opts?.fetcher !== undefined ? { fetcher: opts.fetcher } : {}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/** Internal: SHA-256 content hash truncated to 16 hex chars (64 bits) — collision-resistant for change detection at this scale. */
|
|
64
|
+
export function hashContent(text) {
|
|
65
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=url-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-monitor.js","sourceRoot":"","sources":["../../../src/watcher/sources/url-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAMhC;IACC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;IACtC,MAAM,KAAK,GAAG,GAAW,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAElE,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,KAAK,CAAC,KAAK;YACT,MAAM,QAAQ,GAAc,EAAE,CAAC;YAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;oBACvD,IAAI,CAAC,GAAG,CAAC,EAAE;wBAAE,SAAS;oBACtB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;oBAC/B,QAAQ,CAAC,IAAI,CAAC;wBACZ,EAAE,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE;wBACpB,KAAK,EAAE,GAAG;wBACV,GAAG;wBACH,WAAW,EAAE,KAAK,EAAE;wBACpB,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,MAAM,EAAE;qBAC3D,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,6EAA6E;gBAC/E,CAAC;YACH,CAAC;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAExC;IACC,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,gBAAgB,CAAC;QACtB,EAAE,EAAE,qBAAqB;QACzB,WAAW,EACT,8LAA8L;QAChM,IAAI;QACJ,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClE,CAAC,CAAC;AACL,CAAC;AAED,mIAAmI;AACnI,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WatcherState } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Reads the watcher state from `path`. If the file does not exist, returns a
|
|
4
|
+
* fresh empty state. The state file is plain JSON — the agent can read it
|
|
5
|
+
* directly to inspect what has been seen across runs.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readState(path: string): WatcherState;
|
|
8
|
+
export declare function writeState(path: string, state: WatcherState): void;
|
|
9
|
+
//# sourceMappingURL=state-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/watcher/state-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAYpD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAGlE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Reads the watcher state from `path`. If the file does not exist, returns a
|
|
5
|
+
* fresh empty state. The state file is plain JSON — the agent can read it
|
|
6
|
+
* directly to inspect what has been seen across runs.
|
|
7
|
+
*/
|
|
8
|
+
export function readState(path) {
|
|
9
|
+
if (!existsSync(path)) {
|
|
10
|
+
return { schema_version: 1, sources: {} };
|
|
11
|
+
}
|
|
12
|
+
const raw = readFileSync(path, "utf-8");
|
|
13
|
+
const parsed = JSON.parse(raw);
|
|
14
|
+
if (parsed.schema_version !== 1) {
|
|
15
|
+
throw new Error(`Unknown watcher-state schema_version: ${parsed.schema_version}. Expected 1.`);
|
|
16
|
+
}
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
export function writeState(path, state) {
|
|
20
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
21
|
+
writeFileSync(path, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=state-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-store.js","sourceRoot":"","sources":["../../src/watcher/state-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IAC/C,IAAI,MAAM,CAAC,cAAc,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,yCAAyC,MAAM,CAAC,cAAc,eAAe,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAmB;IAC1D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regulatory-update watcher types.
|
|
3
|
+
*
|
|
4
|
+
* The watcher polls public regulator feeds for new AI-relevant rules, compares
|
|
5
|
+
* what it sees against a persisted "last seen" snapshot, and emits a digest
|
|
6
|
+
* of new documents. This is the moat for plainstamp — the bundled rules in
|
|
7
|
+
* `rules/seed.json` need ongoing maintenance, and this watcher surfaces the
|
|
8
|
+
* documents that may justify a rule addition or `last_verified` bump.
|
|
9
|
+
*
|
|
10
|
+
* Sources are pluggable. Each source exposes a stable id, a fetch function
|
|
11
|
+
* that returns a list of `Article`s, and a description for human auditors.
|
|
12
|
+
*/
|
|
13
|
+
export interface Article {
|
|
14
|
+
/** Stable identifier the source uses for this document (e.g. Federal Register `document_number`). */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Human-readable title of the document. */
|
|
17
|
+
title: string;
|
|
18
|
+
/** Publisher's canonical URL for the document. */
|
|
19
|
+
url: string;
|
|
20
|
+
/** YYYY-MM-DD publication date. */
|
|
21
|
+
publishedAt: string;
|
|
22
|
+
/** Free-form summary the source provides (abstract, summary, first paragraph, etc.). */
|
|
23
|
+
summary?: string;
|
|
24
|
+
/** Names of the issuing agencies / publishers, where the source provides them. */
|
|
25
|
+
agencies?: string[];
|
|
26
|
+
/** Anything else the source returns that an auditor might want to inspect. */
|
|
27
|
+
extra?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
export interface Source {
|
|
30
|
+
/** Stable id used as the key in the state store. Lowercase kebab-case. */
|
|
31
|
+
id: string;
|
|
32
|
+
/** Human-readable description for the audit log. */
|
|
33
|
+
description: string;
|
|
34
|
+
/** Fetches recent articles from the regulator. May throw on transient network failure; the orchestrator catches and skips. */
|
|
35
|
+
fetch: () => Promise<Article[]>;
|
|
36
|
+
}
|
|
37
|
+
export interface SourceState {
|
|
38
|
+
/** IDs of articles seen in prior runs. */
|
|
39
|
+
seen: string[];
|
|
40
|
+
/** Timestamp of the last successful run for this source. */
|
|
41
|
+
lastRunAt: string | null;
|
|
42
|
+
}
|
|
43
|
+
export interface WatcherState {
|
|
44
|
+
schema_version: 1;
|
|
45
|
+
sources: Record<string, SourceState>;
|
|
46
|
+
}
|
|
47
|
+
export interface SourceRunReport {
|
|
48
|
+
source_id: string;
|
|
49
|
+
description: string;
|
|
50
|
+
ok: boolean;
|
|
51
|
+
error?: string;
|
|
52
|
+
new_articles: Article[];
|
|
53
|
+
total_seen: number;
|
|
54
|
+
}
|
|
55
|
+
export interface RunReport {
|
|
56
|
+
run_at: string;
|
|
57
|
+
sources: SourceRunReport[];
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/watcher/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,OAAO;IACtB,qGAAqG;IACrG,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,8HAA8H;IAC9H,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,CAAC,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regulatory-update watcher types.
|
|
3
|
+
*
|
|
4
|
+
* The watcher polls public regulator feeds for new AI-relevant rules, compares
|
|
5
|
+
* what it sees against a persisted "last seen" snapshot, and emits a digest
|
|
6
|
+
* of new documents. This is the moat for plainstamp — the bundled rules in
|
|
7
|
+
* `rules/seed.json` need ongoing maintenance, and this watcher surfaces the
|
|
8
|
+
* documents that may justify a rule addition or `last_verified` bump.
|
|
9
|
+
*
|
|
10
|
+
* Sources are pluggable. Each source exposes a stable id, a fetch function
|
|
11
|
+
* that returns a list of `Article`s, and a description for human auditors.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/watcher/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "plainstamp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "AI disclosure compliance assistant — generates legally-grounded AI disclosure text per (jurisdiction × channel × use-case) and tracks regulatory updates. Operated by an autonomous AI agent under KS Elevated Solutions LLC.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "plainstamp (operated by KS Elevated Solutions LLC)",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"email": "helpfulbutton140@agentmail.to"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"bin": {
|
|
14
|
+
"plainstamp": "dist/cli.js",
|
|
15
|
+
"plainstamp-watcher": "dist/watcher/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"rules",
|
|
20
|
+
"README.md",
|
|
21
|
+
"AI-DISCLOSURE.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"test": "tsx --test \"src/**/*.test.ts\"",
|
|
31
|
+
"test:watch": "tsx --test --watch \"src/**/*.test.ts\"",
|
|
32
|
+
"lint": "tsc --noEmit",
|
|
33
|
+
"mcp": "node dist/mcp-server.js",
|
|
34
|
+
"cli": "node dist/cli.js",
|
|
35
|
+
"prepublishOnly": "npm run lint && npm run test && npm run build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
39
|
+
"zod": "^3.23.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"tsx": "^4.19.0",
|
|
44
|
+
"typescript": "^5.6.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"mcp",
|
|
51
|
+
"model-context-protocol",
|
|
52
|
+
"ai-disclosure",
|
|
53
|
+
"compliance",
|
|
54
|
+
"ccpa",
|
|
55
|
+
"eu-ai-act",
|
|
56
|
+
"ftc",
|
|
57
|
+
"agent",
|
|
58
|
+
"autonomous-ai"
|
|
59
|
+
]
|
|
60
|
+
}
|