@strongkeep/watchdog 0.1.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.
Files changed (67) hide show
  1. package/assets/PINNED_PUBLIC_KEY.txt +13 -0
  2. package/assets/healthcheck-skill/SKILL.md +28 -0
  3. package/assets/skill/SKILL.md +30 -0
  4. package/dist/appliers/add-hook.d.ts +19 -0
  5. package/dist/appliers/add-hook.js +81 -0
  6. package/dist/appliers/add-hook.js.map +1 -0
  7. package/dist/appliers/applier.d.ts +86 -0
  8. package/dist/appliers/applier.js +60 -0
  9. package/dist/appliers/applier.js.map +1 -0
  10. package/dist/appliers/block-domain.d.ts +20 -0
  11. package/dist/appliers/block-domain.js +77 -0
  12. package/dist/appliers/block-domain.js.map +1 -0
  13. package/dist/appliers/fs-guard.d.ts +36 -0
  14. package/dist/appliers/fs-guard.js +109 -0
  15. package/dist/appliers/fs-guard.js.map +1 -0
  16. package/dist/appliers/min-version.d.ts +15 -0
  17. package/dist/appliers/min-version.js +47 -0
  18. package/dist/appliers/min-version.js.map +1 -0
  19. package/dist/appliers/pin-mcp.d.ts +17 -0
  20. package/dist/appliers/pin-mcp.js +99 -0
  21. package/dist/appliers/pin-mcp.js.map +1 -0
  22. package/dist/appliers/set-config.d.ts +12 -0
  23. package/dist/appliers/set-config.js +67 -0
  24. package/dist/appliers/set-config.js.map +1 -0
  25. package/dist/appliers/update-rule-file.d.ts +15 -0
  26. package/dist/appliers/update-rule-file.js +69 -0
  27. package/dist/appliers/update-rule-file.js.map +1 -0
  28. package/dist/autonomy.d.ts +24 -0
  29. package/dist/autonomy.js +35 -0
  30. package/dist/autonomy.js.map +1 -0
  31. package/dist/cli/guard-check.d.ts +16 -0
  32. package/dist/cli/guard-check.js +108 -0
  33. package/dist/cli/guard-check.js.map +1 -0
  34. package/dist/cli/init.d.ts +37 -0
  35. package/dist/cli/init.js +226 -0
  36. package/dist/cli/init.js.map +1 -0
  37. package/dist/cli/login.d.ts +32 -0
  38. package/dist/cli/login.js +144 -0
  39. package/dist/cli/login.js.map +1 -0
  40. package/dist/cli/mcp.d.ts +23 -0
  41. package/dist/cli/mcp.js +84 -0
  42. package/dist/cli/mcp.js.map +1 -0
  43. package/dist/cli/poll.d.ts +20 -0
  44. package/dist/cli/poll.js +111 -0
  45. package/dist/cli/poll.js.map +1 -0
  46. package/dist/cli/revert.d.ts +31 -0
  47. package/dist/cli/revert.js +129 -0
  48. package/dist/cli/revert.js.map +1 -0
  49. package/dist/feed-client.d.ts +110 -0
  50. package/dist/feed-client.js +178 -0
  51. package/dist/feed-client.js.map +1 -0
  52. package/dist/fires.d.ts +28 -0
  53. package/dist/fires.js +36 -0
  54. package/dist/fires.js.map +1 -0
  55. package/dist/guard-check.d.ts +39 -0
  56. package/dist/guard-check.js +94 -0
  57. package/dist/guard-check.js.map +1 -0
  58. package/dist/init.d.ts +105 -0
  59. package/dist/init.js +283 -0
  60. package/dist/init.js.map +1 -0
  61. package/dist/pipeline.d.ts +50 -0
  62. package/dist/pipeline.js +110 -0
  63. package/dist/pipeline.js.map +1 -0
  64. package/dist/undo.d.ts +98 -0
  65. package/dist/undo.js +67 -0
  66. package/dist/undo.js.map +1 -0
  67. package/package.json +40 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Apply pipeline — the orchestrator that ties verified entries → autonomy → applier → undo.
3
+ * This is the runtime the harness hook invokes on each poll (steady state, Journey B step 2).
4
+ *
5
+ * For each VERIFIED, parsed entry (from feed-client.pollFeed), for each of its actions:
6
+ * 1. decideDisposition(action, consumerTier, entryTier) (autonomy.ts)
7
+ * 2. report → surface to the human, no write.
8
+ * stage → applyAction(dryRun:true) → write a 'staged' UndoRecord + a pending diff file
9
+ * (the PR-style pending change a human approves via {@link applyStaged}).
10
+ * apply → applyAction(dryRun:false) → append 'applied' UndoRecord.
11
+ * 3. All apply/stage outcomes append to the audit/undo log; a refused action is counted and
12
+ * reported, never silently dropped.
13
+ *
14
+ * The pipeline NEVER executes an entry body and NEVER runs feed-supplied text; it only routes
15
+ * closed-vocabulary actions to allowlisted appliers. Crash-safety (D3): every applier writes
16
+ * its byte-exact out-of-log BACKUP before touching a target file, so prior state is
17
+ * recoverable on disk even if the process dies between the write and the log append.
18
+ */
19
+ import { randomUUID } from 'node:crypto';
20
+ import { mkdir, writeFile } from 'node:fs/promises';
21
+ import { join } from 'node:path';
22
+ import { applyAction, RefusedActionError } from './appliers/applier.js';
23
+ import { decideDisposition } from './autonomy.js';
24
+ import { appendUndoRecord, listApplied } from './undo.js';
25
+ /** Where staged PR-style diffs are written, under the log dir. */
26
+ export const PENDING_DIR = 'pending';
27
+ /**
28
+ * Process all verified entries through the autonomy → applier → undo pipeline.
29
+ * Aggregates per-action failures (refusals) instead of aborting the whole run.
30
+ */
31
+ export async function runPipeline(entries, config) {
32
+ const summary = { reported: 0, staged: 0, applied: 0, refused: 0, messages: [] };
33
+ for (const entry of entries) {
34
+ const fm = entry.frontmatter;
35
+ for (const action of fm.actions) {
36
+ const disposition = decideDisposition(action, config.consumerTier, fm.autonomy);
37
+ const why = `${fm.id}: ${fm.title}`;
38
+ try {
39
+ if (disposition === 'report') {
40
+ summary.reported++;
41
+ summary.messages.push(`report ${fm.id}: ${action.type} advised (tier gives no write authority)`);
42
+ continue;
43
+ }
44
+ if (disposition === 'stage') {
45
+ // Dry-run computes the diff + inverse without writing anything live.
46
+ const result = await applyAction(action, { ...config.applyCtx, dryRun: true });
47
+ const record = {
48
+ id: randomUUID(),
49
+ entryId: fm.id,
50
+ action,
51
+ appliedAt: new Date().toISOString(),
52
+ status: 'staged',
53
+ why,
54
+ inverse: result.inverse,
55
+ };
56
+ await appendUndoRecord(config.logDir, record);
57
+ const pendingDir = join(config.logDir, PENDING_DIR);
58
+ await mkdir(pendingDir, { recursive: true });
59
+ await writeFile(join(pendingDir, `${record.id}.diff`), result.diffPreview + '\n', 'utf8');
60
+ summary.staged++;
61
+ summary.messages.push(`staged ${fm.id}: ${result.summary} (approve with applyStaged/${record.id})`);
62
+ continue;
63
+ }
64
+ // apply: the applier writes its out-of-log backup BEFORE the live write.
65
+ const result = await applyAction(action, { ...config.applyCtx, dryRun: false });
66
+ await appendUndoRecord(config.logDir, {
67
+ id: randomUUID(),
68
+ entryId: fm.id,
69
+ action,
70
+ appliedAt: new Date().toISOString(),
71
+ status: 'applied',
72
+ why,
73
+ inverse: result.inverse,
74
+ });
75
+ summary.applied++;
76
+ summary.messages.push(`applied ${fm.id}: ${result.summary}`);
77
+ }
78
+ catch (err) {
79
+ if (err instanceof RefusedActionError) {
80
+ summary.refused++;
81
+ summary.messages.push(`refused ${fm.id}: ${err.message}`);
82
+ continue; // a refused action never aborts the run — but it is never silent either
83
+ }
84
+ throw err;
85
+ }
86
+ }
87
+ }
88
+ return summary;
89
+ }
90
+ /**
91
+ * Human-approval path for a staged change: apply a previously-staged record for real and log
92
+ * the transition under the SAME record id (staged → applied → revertible). This is the
93
+ * "human applies the PR-style diff" step of the propose tier.
94
+ */
95
+ export async function applyStaged(recordId, config) {
96
+ const records = await listApplied(config.logDir);
97
+ const staged = records.find((r) => r.id === recordId && r.status === 'staged');
98
+ if (staged === undefined)
99
+ throw new Error(`no staged record with id "${recordId}"`);
100
+ // The live apply captures the REAL inverse (incl. the byte-exact file backup pointer).
101
+ const result = await applyAction(staged.action, { ...config.applyCtx, dryRun: false });
102
+ await appendUndoRecord(config.logDir, {
103
+ ...staged,
104
+ appliedAt: new Date().toISOString(),
105
+ status: 'applied',
106
+ inverse: result.inverse,
107
+ });
108
+ return { summary: result.summary };
109
+ }
110
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
package/dist/undo.d.ts ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Undo record model + audit log — the counterweight to autonomy (BUILD-PLAN #5 + D3).
3
+ *
4
+ * Every applied action writes an UndoRecord (capturing prior state via its applier's inverse)
5
+ * so `strongkeep-revert` can restore exactly. The undo log doubles as the audit trail: it is
6
+ * APPEND-ONLY (state transitions are new lines, never edits) and holds the plain-language
7
+ * "why applied" + the exact inverse needed to revert.
8
+ *
9
+ * D3 rule (binding): "the applier and its paired undo ship together or the action isn't
10
+ * shippable." An applier without a working undo path must refuse to apply.
11
+ *
12
+ * SECURITY:
13
+ * - Undo records capture only config LOCATORS + scalar prior values. Where prior content is a
14
+ * whole file (which could contain a secret), the applier backs the file up OUT of the log
15
+ * (under .strongkeep/backups/) and only the backup POINTER (`backupRef`) is stored here —
16
+ * same discipline as Phase 1 redaction.
17
+ * - The log is local, append-only, and is the source of truth for `strongkeep-revert`.
18
+ *
19
+ * ponytail: the log is a single NDJSON file (one record per line, append = atomicity unit).
20
+ * Upgrade path: per-record files if concurrent writers ever exist (they don't in v1 — the
21
+ * pipeline is the only writer).
22
+ */
23
+ import type { ConstrainedAction } from '@strongkeep/core';
24
+ /** Lifecycle status of an applied action. */
25
+ export type UndoStatus = 'staged' | 'applied' | 'reverted';
26
+ /** A single applied (or staged) action + everything needed to revert it. */
27
+ export interface UndoRecord {
28
+ /** Unique id for this application (what `strongkeep-revert <id>` takes). */
29
+ id: string;
30
+ /** The feed entry that motivated this action. */
31
+ entryId: string;
32
+ /** The action that was applied (closed-vocabulary). */
33
+ action: ConstrainedAction;
34
+ /** ISO timestamp of application. */
35
+ appliedAt: string;
36
+ status: UndoStatus;
37
+ /** Plain-language reason surfaced in the "recently applied" list (D3). No jargon, no emoji. */
38
+ why: string;
39
+ /**
40
+ * The inverse needed to restore prior state, produced by the action's paired undo builder.
41
+ * Interpreted by the same applier that created it.
42
+ */
43
+ inverse: UndoInverse;
44
+ }
45
+ /**
46
+ * The captured inverse of an applied action. Discriminated by the action type so the right
47
+ * applier reverses it. Carries prior scalar state + out-of-log backup pointers — never a
48
+ * secret value inline.
49
+ */
50
+ export type UndoInverse = {
51
+ kind: 'set_config';
52
+ file: string;
53
+ keypath: string;
54
+ priorValue: string | number | boolean | undefined;
55
+ /** Byte-exact backup of the prior file (relative to harness root), when the file existed. */
56
+ backupRef?: string;
57
+ /** True when the config file itself did not exist before apply (revert deletes it). */
58
+ fileExistedBefore: boolean;
59
+ } | {
60
+ kind: 'add_hook';
61
+ hookPackId: string;
62
+ existedBefore: boolean;
63
+ path: string;
64
+ backupRef?: string;
65
+ } | {
66
+ kind: 'block_domain';
67
+ domain: string;
68
+ existedBefore: boolean;
69
+ } | {
70
+ kind: 'pin_mcp';
71
+ server: string;
72
+ priorVersion: string | undefined;
73
+ backupRef?: string;
74
+ } | {
75
+ kind: 'update_rule_file';
76
+ path: string;
77
+ existedBefore: boolean;
78
+ backupRef?: string;
79
+ } | {
80
+ kind: 'min_version';
81
+ noop: true;
82
+ };
83
+ /**
84
+ * Append an undo record to the local, append-only log. Called by the pipeline at apply time
85
+ * (the applier has already written its out-of-log backup BEFORE touching the target file, so
86
+ * prior state is recoverable even across a crash between write and log-append).
87
+ */
88
+ export declare function appendUndoRecord(logDir: string, record: UndoRecord): Promise<void>;
89
+ /**
90
+ * Read the "recently applied" list for the revert UX (D3), newest first. The log is a stream
91
+ * of state transitions; the LATEST line per id wins (e.g. applied → reverted).
92
+ */
93
+ export declare function listApplied(logDir: string): Promise<UndoRecord[]>;
94
+ /**
95
+ * Mark a record reverted after its inverse has been applied. Appends a `reverted` transition
96
+ * line (the log stays append-only).
97
+ */
98
+ export declare function markReverted(logDir: string, id: string): Promise<void>;
package/dist/undo.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Undo record model + audit log — the counterweight to autonomy (BUILD-PLAN #5 + D3).
3
+ *
4
+ * Every applied action writes an UndoRecord (capturing prior state via its applier's inverse)
5
+ * so `strongkeep-revert` can restore exactly. The undo log doubles as the audit trail: it is
6
+ * APPEND-ONLY (state transitions are new lines, never edits) and holds the plain-language
7
+ * "why applied" + the exact inverse needed to revert.
8
+ *
9
+ * D3 rule (binding): "the applier and its paired undo ship together or the action isn't
10
+ * shippable." An applier without a working undo path must refuse to apply.
11
+ *
12
+ * SECURITY:
13
+ * - Undo records capture only config LOCATORS + scalar prior values. Where prior content is a
14
+ * whole file (which could contain a secret), the applier backs the file up OUT of the log
15
+ * (under .strongkeep/backups/) and only the backup POINTER (`backupRef`) is stored here —
16
+ * same discipline as Phase 1 redaction.
17
+ * - The log is local, append-only, and is the source of truth for `strongkeep-revert`.
18
+ *
19
+ * ponytail: the log is a single NDJSON file (one record per line, append = atomicity unit).
20
+ * Upgrade path: per-record files if concurrent writers ever exist (they don't in v1 — the
21
+ * pipeline is the only writer).
22
+ */
23
+ import { appendFile, mkdir, readFile } from 'node:fs/promises';
24
+ import { join } from 'node:path';
25
+ const LOG_FILE = 'undo-log.ndjson';
26
+ /**
27
+ * Append an undo record to the local, append-only log. Called by the pipeline at apply time
28
+ * (the applier has already written its out-of-log backup BEFORE touching the target file, so
29
+ * prior state is recoverable even across a crash between write and log-append).
30
+ */
31
+ export async function appendUndoRecord(logDir, record) {
32
+ await mkdir(logDir, { recursive: true });
33
+ await appendFile(join(logDir, LOG_FILE), JSON.stringify(record) + '\n', 'utf8');
34
+ }
35
+ /**
36
+ * Read the "recently applied" list for the revert UX (D3), newest first. The log is a stream
37
+ * of state transitions; the LATEST line per id wins (e.g. applied → reverted).
38
+ */
39
+ export async function listApplied(logDir) {
40
+ let text;
41
+ try {
42
+ text = await readFile(join(logDir, LOG_FILE), 'utf8');
43
+ }
44
+ catch {
45
+ return []; // no log yet — nothing applied
46
+ }
47
+ const latest = new Map();
48
+ for (const line of text.split('\n')) {
49
+ if (line.trim() === '')
50
+ continue;
51
+ const record = JSON.parse(line);
52
+ latest.set(record.id, record); // later lines supersede earlier states for the same id
53
+ }
54
+ return [...latest.values()].reverse(); // newest first (insertion order = log order)
55
+ }
56
+ /**
57
+ * Mark a record reverted after its inverse has been applied. Appends a `reverted` transition
58
+ * line (the log stays append-only).
59
+ */
60
+ export async function markReverted(logDir, id) {
61
+ const records = await listApplied(logDir);
62
+ const record = records.find((r) => r.id === id);
63
+ if (record === undefined)
64
+ throw new Error(`no undo record with id "${id}"`);
65
+ await appendUndoRecord(logDir, { ...record, status: 'reverted' });
66
+ }
67
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.js","sourceRoot":"","sources":["../src/undo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgDjC,MAAM,QAAQ,GAAG,iBAAiB,CAAC;AAEnC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,MAAkB;IACvE,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc;IAC9C,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,+BAA+B;IAC5C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAS;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,uDAAuD;IACxF,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,6CAA6C;AACtF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,EAAU;IAC3D,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,EAAE,GAAG,CAAC,CAAC;IAC5E,MAAM,gBAAgB,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AACpE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@strongkeep/watchdog",
3
+ "version": "0.1.1",
4
+ "description": "Consumer (subscriber-side) config pack for the AI Security feed (D6 — NOT a daemon). strongkeep-init writes hooks/rules/skill/MCP config into the subscriber's own harness; the feed client verifies + applies signed ConstrainedActions per autonomy tier with a paired undo; strongkeep-revert is one-click self-serve undo.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "bin": {
8
+ "strongkeep-login": "dist/cli/login.js",
9
+ "strongkeep-init": "dist/cli/init.js",
10
+ "strongkeep-poll": "dist/cli/poll.js",
11
+ "strongkeep-revert": "dist/cli/revert.js",
12
+ "strongkeep-mcp": "dist/cli/mcp.js",
13
+ "strongkeep-guard-check": "dist/cli/guard-check.js"
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "assets/",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.json",
25
+ "prepublishOnly": "npm run build",
26
+ "test": "npm run build && node --experimental-strip-types --test 'test/**/*.test.ts'",
27
+ "test:acceptance": "npm run build && node --experimental-strip-types --test 'test/acceptance/**/*.test.ts'"
28
+ },
29
+ "dependencies": {
30
+ "@strongkeep/core": "^0.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "typescript": "^5.6.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "tag": "beta"
39
+ }
40
+ }