@sapiom/harness 0.0.0 → 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.
- package/LICENSE +21 -0
- package/README.md +44 -43
- package/dist/cli/auth.d.ts +26 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +39 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +163 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/consent.d.ts +15 -0
- package/dist/cli/consent.d.ts.map +1 -0
- package/dist/cli/consent.js +81 -0
- package/dist/cli/consent.js.map +1 -0
- package/dist/cli/doctor.d.ts +27 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +74 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/machine-id.d.ts +19 -0
- package/dist/cli/machine-id.d.ts.map +1 -0
- package/dist/cli/machine-id.js +75 -0
- package/dist/cli/machine-id.js.map +1 -0
- package/dist/cli/paths.d.ts +3 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/paths.js +11 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/settings.d.ts +26 -0
- package/dist/cli/settings.d.ts.map +1 -0
- package/dist/cli/settings.js +123 -0
- package/dist/cli/settings.js.map +1 -0
- package/dist/core/adapters/claude-code.d.ts +41 -0
- package/dist/core/adapters/claude-code.d.ts.map +1 -0
- package/dist/core/adapters/claude-code.js +238 -0
- package/dist/core/adapters/claude-code.js.map +1 -0
- package/dist/core/adapters/codex.d.ts +59 -0
- package/dist/core/adapters/codex.d.ts.map +1 -0
- package/dist/core/adapters/codex.js +319 -0
- package/dist/core/adapters/codex.js.map +1 -0
- package/dist/core/canvas-body.d.ts +45 -0
- package/dist/core/canvas-body.d.ts.map +1 -0
- package/dist/core/canvas-body.js +94 -0
- package/dist/core/canvas-body.js.map +1 -0
- package/dist/core/canvas-cache.d.ts +23 -0
- package/dist/core/canvas-cache.d.ts.map +1 -0
- package/dist/core/canvas-cache.js +64 -0
- package/dist/core/canvas-cache.js.map +1 -0
- package/dist/core/canvas-enrich.d.ts +106 -0
- package/dist/core/canvas-enrich.d.ts.map +1 -0
- package/dist/core/canvas-enrich.js +178 -0
- package/dist/core/canvas-enrich.js.map +1 -0
- package/dist/core/canvas-enrichment.d.ts +144 -0
- package/dist/core/canvas-enrichment.d.ts.map +1 -0
- package/dist/core/canvas-enrichment.js +192 -0
- package/dist/core/canvas-enrichment.js.map +1 -0
- package/dist/core/canvas-graph.d.ts +71 -0
- package/dist/core/canvas-graph.d.ts.map +1 -0
- package/dist/core/canvas-graph.js +111 -0
- package/dist/core/canvas-graph.js.map +1 -0
- package/dist/core/canvas-index-classify.d.ts +41 -0
- package/dist/core/canvas-index-classify.d.ts.map +1 -0
- package/dist/core/canvas-index-classify.js +63 -0
- package/dist/core/canvas-index-classify.js.map +1 -0
- package/dist/core/canvas-interconnections.d.ts +24 -0
- package/dist/core/canvas-interconnections.d.ts.map +1 -0
- package/dist/core/canvas-interconnections.js +96 -0
- package/dist/core/canvas-interconnections.js.map +1 -0
- package/dist/core/canvas-manifest-check.d.ts +15 -0
- package/dist/core/canvas-manifest-check.d.ts.map +1 -0
- package/dist/core/canvas-manifest-check.js +90 -0
- package/dist/core/canvas-manifest-check.js.map +1 -0
- package/dist/core/canvas-render.d.ts +73 -0
- package/dist/core/canvas-render.d.ts.map +1 -0
- package/dist/core/canvas-render.js +145 -0
- package/dist/core/canvas-render.js.map +1 -0
- package/dist/core/canvas-svg.d.ts +50 -0
- package/dist/core/canvas-svg.d.ts.map +1 -0
- package/dist/core/canvas-svg.js +240 -0
- package/dist/core/canvas-svg.js.map +1 -0
- package/dist/core/canvas-template.d.ts +43 -0
- package/dist/core/canvas-template.d.ts.map +1 -0
- package/dist/core/canvas-template.js +365 -0
- package/dist/core/canvas-template.js.map +1 -0
- package/dist/core/canvas-watcher.d.ts +25 -0
- package/dist/core/canvas-watcher.d.ts.map +1 -0
- package/dist/core/canvas-watcher.js +144 -0
- package/dist/core/canvas-watcher.js.map +1 -0
- package/dist/core/collector/batcher.d.ts +57 -0
- package/dist/core/collector/batcher.d.ts.map +1 -0
- package/dist/core/collector/batcher.js +143 -0
- package/dist/core/collector/batcher.js.map +1 -0
- package/dist/core/collector/codex-tailer.d.ts +85 -0
- package/dist/core/collector/codex-tailer.d.ts.map +1 -0
- package/dist/core/collector/codex-tailer.js +270 -0
- package/dist/core/collector/codex-tailer.js.map +1 -0
- package/dist/core/collector/normalizer.d.ts +33 -0
- package/dist/core/collector/normalizer.d.ts.map +1 -0
- package/dist/core/collector/normalizer.js +94 -0
- package/dist/core/collector/normalizer.js.map +1 -0
- package/dist/core/collector/seq.d.ts +14 -0
- package/dist/core/collector/seq.d.ts.map +1 -0
- package/dist/core/collector/seq.js +20 -0
- package/dist/core/collector/seq.js.map +1 -0
- package/dist/core/collector/store.d.ts +16 -0
- package/dist/core/collector/store.d.ts.map +1 -0
- package/dist/core/collector/store.js +30 -0
- package/dist/core/collector/store.js.map +1 -0
- package/dist/core/collector/transcript.d.ts +38 -0
- package/dist/core/collector/transcript.d.ts.map +1 -0
- package/dist/core/collector/transcript.js +110 -0
- package/dist/core/collector/transcript.js.map +1 -0
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/example-seed.d.ts +34 -0
- package/dist/core/example-seed.d.ts.map +1 -0
- package/dist/core/example-seed.js +277 -0
- package/dist/core/example-seed.js.map +1 -0
- package/dist/core/inject/claude-settings.d.ts +34 -0
- package/dist/core/inject/claude-settings.d.ts.map +1 -0
- package/dist/core/inject/claude-settings.js +152 -0
- package/dist/core/inject/claude-settings.js.map +1 -0
- package/dist/core/inject/mcp-config.d.ts +26 -0
- package/dist/core/inject/mcp-config.d.ts.map +1 -0
- package/dist/core/inject/mcp-config.js +39 -0
- package/dist/core/inject/mcp-config.js.map +1 -0
- package/dist/core/inject/retention.d.ts +66 -0
- package/dist/core/inject/retention.d.ts.map +1 -0
- package/dist/core/inject/retention.js +119 -0
- package/dist/core/inject/retention.js.map +1 -0
- package/dist/core/inject/system-prompt.d.ts +11 -0
- package/dist/core/inject/system-prompt.d.ts.map +1 -0
- package/dist/core/inject/system-prompt.js +22 -0
- package/dist/core/inject/system-prompt.js.map +1 -0
- package/dist/core/macro-runner.d.ts +36 -0
- package/dist/core/macro-runner.d.ts.map +1 -0
- package/dist/core/macro-runner.js +66 -0
- package/dist/core/macro-runner.js.map +1 -0
- package/dist/core/macros.d.ts +10 -0
- package/dist/core/macros.d.ts.map +1 -0
- package/dist/core/macros.js +59 -0
- package/dist/core/macros.js.map +1 -0
- package/dist/core/path-safety.d.ts +33 -0
- package/dist/core/path-safety.d.ts.map +1 -0
- package/dist/core/path-safety.js +63 -0
- package/dist/core/path-safety.js.map +1 -0
- package/dist/core/paths.d.ts +27 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +43 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/port-detector.d.ts +69 -0
- package/dist/core/port-detector.d.ts.map +1 -0
- package/dist/core/port-detector.js +121 -0
- package/dist/core/port-detector.js.map +1 -0
- package/dist/core/session-manager.d.ts +275 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +729 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/task-manager.d.ts +138 -0
- package/dist/core/task-manager.d.ts.map +1 -0
- package/dist/core/task-manager.js +286 -0
- package/dist/core/task-manager.js.map +1 -0
- package/dist/core/task-stream.d.ts +32 -0
- package/dist/core/task-stream.d.ts.map +1 -0
- package/dist/core/task-stream.js +87 -0
- package/dist/core/task-stream.js.map +1 -0
- package/dist/core/workflow-registry.d.ts +36 -0
- package/dist/core/workflow-registry.d.ts.map +1 -0
- package/dist/core/workflow-registry.js +220 -0
- package/dist/core/workflow-registry.js.map +1 -0
- package/dist/core/workspace-context.d.ts +43 -0
- package/dist/core/workspace-context.d.ts.map +1 -0
- package/dist/core/workspace-context.js +108 -0
- package/dist/core/workspace-context.js.map +1 -0
- package/dist/core/workspace-watcher.d.ts +26 -0
- package/dist/core/workspace-watcher.d.ts.map +1 -0
- package/dist/core/workspace-watcher.js +174 -0
- package/dist/core/workspace-watcher.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/profiles/canvas-guidelines.d.ts +20 -0
- package/dist/profiles/canvas-guidelines.d.ts.map +1 -0
- package/dist/profiles/canvas-guidelines.js +48 -0
- package/dist/profiles/canvas-guidelines.js.map +1 -0
- package/dist/profiles/default.d.ts +10 -0
- package/dist/profiles/default.d.ts.map +1 -0
- package/dist/profiles/default.js +70 -0
- package/dist/profiles/default.js.map +1 -0
- package/dist/server/auth.d.ts +9 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/canvas-render.d.ts +18 -0
- package/dist/server/canvas-render.d.ts.map +1 -0
- package/dist/server/canvas-render.js +24 -0
- package/dist/server/canvas-render.js.map +1 -0
- package/dist/server/canvas.d.ts +12 -0
- package/dist/server/canvas.d.ts.map +1 -0
- package/dist/server/canvas.js +174 -0
- package/dist/server/canvas.js.map +1 -0
- package/dist/server/events-ws.d.ts +11 -0
- package/dist/server/events-ws.d.ts.map +1 -0
- package/dist/server/events-ws.js +24 -0
- package/dist/server/events-ws.js.map +1 -0
- package/dist/server/fs.d.ts +4 -0
- package/dist/server/fs.d.ts.map +1 -0
- package/dist/server/fs.js +88 -0
- package/dist/server/fs.js.map +1 -0
- package/dist/server/index.d.ts +83 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/ingest.d.ts +74 -0
- package/dist/server/ingest.d.ts.map +1 -0
- package/dist/server/ingest.js +74 -0
- package/dist/server/ingest.js.map +1 -0
- package/dist/server/macros.d.ts +32 -0
- package/dist/server/macros.d.ts.map +1 -0
- package/dist/server/macros.js +76 -0
- package/dist/server/macros.js.map +1 -0
- package/dist/server/rest.d.ts +75 -0
- package/dist/server/rest.d.ts.map +1 -0
- package/dist/server/rest.js +235 -0
- package/dist/server/rest.js.map +1 -0
- package/dist/server/static.d.ts +8 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +36 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/terminal-ws.d.ts +9 -0
- package/dist/server/terminal-ws.d.ts.map +1 -0
- package/dist/server/terminal-ws.js +59 -0
- package/dist/server/terminal-ws.js.map +1 -0
- package/dist/server/ws-router.d.ts +16 -0
- package/dist/server/ws-router.d.ts.map +1 -0
- package/dist/server/ws-router.js +26 -0
- package/dist/server/ws-router.js.map +1 -0
- package/dist/shared/types.d.ts +485 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +80 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/web/assets/index-B9R4TzH7.css +32 -0
- package/dist/web/assets/index-Dh97QAgq.js +192 -0
- package/dist/web/index.html +27 -0
- package/package.json +84 -7
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as crypto from "node:crypto";
|
|
4
|
+
import { HARNESS_PATHS } from "../shared/types.js";
|
|
5
|
+
import { expandHome } from "./paths.js";
|
|
6
|
+
const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7
|
+
/**
|
|
8
|
+
* Stable anonymous install id, used to tag every analytics event
|
|
9
|
+
* (`AnalyticsEvent.machineId`) regardless of which Sapiom user is signed in.
|
|
10
|
+
* Created once on first run and reused forever after.
|
|
11
|
+
*
|
|
12
|
+
* Hardened per `@sapiom/analytics-core`'s `identity.ts` pattern: private
|
|
13
|
+
* file permissions (mode 0600, on both create and a pre-existing file),
|
|
14
|
+
* silent regeneration when the stored content is missing/corrupt, and
|
|
15
|
+
* graceful degradation — an unreadable/unwritable HOME must never crash
|
|
16
|
+
* server boot over an analytics nicety. Kept as our own plain-text
|
|
17
|
+
* `~/.sapiom/harness/machine-id` file for now; converging onto her
|
|
18
|
+
* `~/.sapiom/analytics.json` identity is a separate, later conversation.
|
|
19
|
+
*
|
|
20
|
+
* @param filePath Where the id file lives. Defaults to the real
|
|
21
|
+
* `HARNESS_PATHS.machineId`; tests and scripted checks pass a path under
|
|
22
|
+
* their scratch state root so a boot never writes to the real home dir.
|
|
23
|
+
*/
|
|
24
|
+
export async function getOrCreateMachineId(filePath = expandHome(HARNESS_PATHS.machineId)) {
|
|
25
|
+
try {
|
|
26
|
+
const existing = await readMachineId(filePath);
|
|
27
|
+
if (existing) {
|
|
28
|
+
// Re-assert privacy even when we're not writing: a file created by a
|
|
29
|
+
// pre-hardening version of the harness (default `fs.writeFile` perms,
|
|
30
|
+
// typically 0644) would otherwise stay loosely-permissioned forever,
|
|
31
|
+
// since its content never needs to change again once it exists.
|
|
32
|
+
await hardenPermissions(filePath);
|
|
33
|
+
return existing;
|
|
34
|
+
}
|
|
35
|
+
const id = crypto.randomUUID();
|
|
36
|
+
await writeMachineId(filePath, id);
|
|
37
|
+
return id;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Persistence is impossible (unwritable/unreadable HOME, permissions,
|
|
41
|
+
// whatever) — hand back a fresh id for this process rather than taking
|
|
42
|
+
// the server down over it. It won't be stable across runs, but every
|
|
43
|
+
// event within this run is at least internally consistent.
|
|
44
|
+
return crypto.randomUUID();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Reads and validates the persisted id. Missing, empty, or malformed
|
|
48
|
+
* content returns `undefined` so the caller regenerates — same recovery
|
|
49
|
+
* posture as `identity.ts`'s corrupt/wrong-shape handling. */
|
|
50
|
+
async function readMachineId(filePath) {
|
|
51
|
+
try {
|
|
52
|
+
const raw = (await fs.readFile(filePath, "utf-8")).trim();
|
|
53
|
+
return UUID_SHAPE.test(raw) ? raw : undefined;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function writeMachineId(filePath, id) {
|
|
60
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
61
|
+
// `mode` on writeFile only applies when creating the file — the follow-up
|
|
62
|
+
// hardenPermissions() covers the (unlikely but possible) case of writing
|
|
63
|
+
// over a pre-existing file at this path with looser permissions.
|
|
64
|
+
await fs.writeFile(filePath, id + "\n", { mode: 0o600 });
|
|
65
|
+
await hardenPermissions(filePath);
|
|
66
|
+
}
|
|
67
|
+
async function hardenPermissions(filePath) {
|
|
68
|
+
try {
|
|
69
|
+
await fs.chmod(filePath, 0o600);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Best effort.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=machine-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine-id.js","sourceRoot":"","sources":["../../src/cli/machine-id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,GAAG,iEAAiE,CAAC;AAErF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC;IAEtD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACb,qEAAqE;YACrE,sEAAsE;YACtE,qEAAqE;YACrE,gEAAgE;YAChE,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAClC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/B,MAAM,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,uEAAuE;QACvE,qEAAqE;QACrE,2DAA2D;QAC3D,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;+DAE+D;AAC/D,KAAK,UAAU,aAAa,CAAC,QAAgB;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,EAAU;IACxD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,iEAAiE;IACjE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/cli/paths.ts"],"names":[],"mappings":"AAGA,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5C"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
/** Expand a leading `~` (as used by HARNESS_PATHS) to the user's home directory. */
|
|
4
|
+
export function expandHome(p) {
|
|
5
|
+
if (p === "~")
|
|
6
|
+
return os.homedir();
|
|
7
|
+
if (p.startsWith("~/"))
|
|
8
|
+
return path.join(os.homedir(), p.slice(2));
|
|
9
|
+
return p;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/cli/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type HarnessSettings } from "../shared/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validates, normalizes, case-sensitively dedupes (first occurrence wins —
|
|
4
|
+
* callers order newest-first), and caps a candidate recent-dirs list.
|
|
5
|
+
* Applied on every write (via saveSettings) and on every read (via
|
|
6
|
+
* loadSettings), so junk already on disk self-heals the next time it's
|
|
7
|
+
* loaded instead of requiring a migration.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sanitizeRecentDirs(candidates: string[]): Promise<string[]>;
|
|
10
|
+
/** Whether settings have ever been persisted — used to detect first run. */
|
|
11
|
+
export declare function hasStoredSettings(settingsPath?: string): Promise<boolean>;
|
|
12
|
+
export declare function loadSettings(settingsPath?: string): Promise<HarnessSettings>;
|
|
13
|
+
export declare function saveSettings(settings: HarnessSettings, settingsPath?: string): Promise<void>;
|
|
14
|
+
/** Record `cwd` as the most-recently-used project directory (validated, deduped, capped). */
|
|
15
|
+
export declare function recordRecentDir(cwd: string, settingsPath?: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Boot-time hygiene: persist the removal of recent-dir entries whose path no
|
|
18
|
+
* longer exists on disk. sanitizeRecentDirs() already drops them from every
|
|
19
|
+
* in-memory read, but a read alone never rewrites the file — so a dead entry
|
|
20
|
+
* (deleted project, a crashed check's temp dir) would otherwise sit in
|
|
21
|
+
* settings.json indefinitely. Returns the entries that were dropped; leaves
|
|
22
|
+
* the file completely untouched (not even created) when there is nothing to
|
|
23
|
+
* prune, so this can never break first-run detection (hasStoredSettings).
|
|
24
|
+
*/
|
|
25
|
+
export declare function pruneDeadRecentDirs(settingsPath?: string): Promise<string[]>;
|
|
26
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAqCzE;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWhF;AAED,4EAA4E;AAC5E,wBAAsB,iBAAiB,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1G;AAED,wBAAsB,YAAY,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,eAAe,CAAC,CAQ7G;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,eAAe,EACzB,YAAY,GAAE,MAAkC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,6FAA6F;AAC7F,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlH;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB7G"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { HARNESS_PATHS } from "../shared/types.js";
|
|
4
|
+
import { expandHome } from "./paths.js";
|
|
5
|
+
const DEFAULT_SETTINGS = {
|
|
6
|
+
telemetryOptIn: false,
|
|
7
|
+
recentDirs: [],
|
|
8
|
+
};
|
|
9
|
+
const MAX_RECENT_DIRS = 8;
|
|
10
|
+
/** The real settings file. Every function below accepts an explicit path so
|
|
11
|
+
* tests and scripted checks can point at a scratch state root instead —
|
|
12
|
+
* omitted, they operate on the real file, unchanged for CLI users. */
|
|
13
|
+
function defaultSettingsFilePath() {
|
|
14
|
+
return expandHome(HARNESS_PATHS.settings);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolves a candidate recent-dir entry to an absolute, existing directory
|
|
18
|
+
* path, or null if it doesn't qualify. Rejects anything that isn't a real
|
|
19
|
+
* directory on disk right now — relative paths, stray free text (e.g. typed
|
|
20
|
+
* into the directory field by mistake), and directories that have since been
|
|
21
|
+
* deleted all fall out here rather than accumulating in the list.
|
|
22
|
+
*/
|
|
23
|
+
async function normalizeRecentDir(candidate) {
|
|
24
|
+
const expanded = expandHome(candidate.trim());
|
|
25
|
+
if (!path.isAbsolute(expanded))
|
|
26
|
+
return null;
|
|
27
|
+
const resolved = path.resolve(expanded);
|
|
28
|
+
try {
|
|
29
|
+
const stat = await fs.stat(resolved);
|
|
30
|
+
if (!stat.isDirectory())
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return resolved;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validates, normalizes, case-sensitively dedupes (first occurrence wins —
|
|
40
|
+
* callers order newest-first), and caps a candidate recent-dirs list.
|
|
41
|
+
* Applied on every write (via saveSettings) and on every read (via
|
|
42
|
+
* loadSettings), so junk already on disk self-heals the next time it's
|
|
43
|
+
* loaded instead of requiring a migration.
|
|
44
|
+
*/
|
|
45
|
+
export async function sanitizeRecentDirs(candidates) {
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
if (result.length >= MAX_RECENT_DIRS)
|
|
50
|
+
break;
|
|
51
|
+
const normalized = await normalizeRecentDir(candidate);
|
|
52
|
+
if (normalized === null || seen.has(normalized))
|
|
53
|
+
continue;
|
|
54
|
+
seen.add(normalized);
|
|
55
|
+
result.push(normalized);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/** Whether settings have ever been persisted — used to detect first run. */
|
|
60
|
+
export async function hasStoredSettings(settingsPath = defaultSettingsFilePath()) {
|
|
61
|
+
try {
|
|
62
|
+
await fs.access(settingsPath);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function loadSettings(settingsPath = defaultSettingsFilePath()) {
|
|
70
|
+
try {
|
|
71
|
+
const raw = await fs.readFile(settingsPath, "utf-8");
|
|
72
|
+
const parsed = { ...DEFAULT_SETTINGS, ...JSON.parse(raw) };
|
|
73
|
+
return { ...parsed, recentDirs: await sanitizeRecentDirs(parsed.recentDirs) };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return { ...DEFAULT_SETTINGS };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function saveSettings(settings, settingsPath = defaultSettingsFilePath()) {
|
|
80
|
+
const sanitized = {
|
|
81
|
+
...settings,
|
|
82
|
+
recentDirs: await sanitizeRecentDirs(settings.recentDirs),
|
|
83
|
+
};
|
|
84
|
+
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
85
|
+
await fs.writeFile(settingsPath, JSON.stringify(sanitized, null, 2) + "\n");
|
|
86
|
+
}
|
|
87
|
+
/** Record `cwd` as the most-recently-used project directory (validated, deduped, capped). */
|
|
88
|
+
export async function recordRecentDir(cwd, settingsPath = defaultSettingsFilePath()) {
|
|
89
|
+
const settings = await loadSettings(settingsPath);
|
|
90
|
+
await saveSettings({ ...settings, recentDirs: [cwd, ...settings.recentDirs] }, settingsPath);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Boot-time hygiene: persist the removal of recent-dir entries whose path no
|
|
94
|
+
* longer exists on disk. sanitizeRecentDirs() already drops them from every
|
|
95
|
+
* in-memory read, but a read alone never rewrites the file — so a dead entry
|
|
96
|
+
* (deleted project, a crashed check's temp dir) would otherwise sit in
|
|
97
|
+
* settings.json indefinitely. Returns the entries that were dropped; leaves
|
|
98
|
+
* the file completely untouched (not even created) when there is nothing to
|
|
99
|
+
* prune, so this can never break first-run detection (hasStoredSettings).
|
|
100
|
+
*/
|
|
101
|
+
export async function pruneDeadRecentDirs(settingsPath = defaultSettingsFilePath()) {
|
|
102
|
+
let stored;
|
|
103
|
+
try {
|
|
104
|
+
stored = JSON.parse(await fs.readFile(settingsPath, "utf-8"));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return []; // no settings file yet (or unreadable) — nothing to prune
|
|
108
|
+
}
|
|
109
|
+
const recentDirs = Array.isArray(stored.recentDirs)
|
|
110
|
+
? stored.recentDirs.filter((entry) => typeof entry === "string")
|
|
111
|
+
: [];
|
|
112
|
+
const dead = [];
|
|
113
|
+
for (const entry of recentDirs) {
|
|
114
|
+
if ((await normalizeRecentDir(entry)) === null)
|
|
115
|
+
dead.push(entry);
|
|
116
|
+
}
|
|
117
|
+
if (dead.length === 0)
|
|
118
|
+
return [];
|
|
119
|
+
// saveSettings' own sanitize pass is what actually drops the dead entries.
|
|
120
|
+
await saveSettings({ ...DEFAULT_SETTINGS, ...stored, recentDirs }, settingsPath);
|
|
121
|
+
return dead;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAwB,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,gBAAgB,GAAoB;IACxC,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,EAAE;CACf,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B;;uEAEuE;AACvE,SAAS,uBAAuB;IAC9B,OAAO,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,kBAAkB,CAAC,SAAiB;IACjD,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,IAAI,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAoB;IAC3D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,MAAM,CAAC,MAAM,IAAI,eAAe;YAAE,MAAM;QAC5C,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,UAAU,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,SAAS;QAC1D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,eAAuB,uBAAuB,EAAE;IACtF,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,eAAuB,uBAAuB,EAAE;IACjF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,EAAE,GAAG,gBAAgB,EAAE,GAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAA8B,EAAE,CAAC;QACzF,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAyB,EACzB,eAAuB,uBAAuB,EAAE;IAEhD,MAAM,SAAS,GAAoB;QACjC,GAAG,QAAQ;QACX,UAAU,EAAE,MAAM,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC;KAC1D,CAAC;IACF,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9E,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,eAAuB,uBAAuB,EAAE;IACjG,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,YAAY,CAAC,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,eAAuB,uBAAuB,EAAE;IACxF,IAAI,MAAgC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAA6B,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,0DAA0D;IACvE,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QACjD,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;QACjF,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,kBAAkB,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,2EAA2E;IAC3E,MAAM,YAAY,CAAC,EAAE,GAAG,gBAAgB,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,YAAY,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-code adapter — launches/resumes the `claude` CLI and scans its
|
|
3
|
+
* transcript store for resumable history. All Sapiom-ness is injected via
|
|
4
|
+
* flags (settings/mcp-config/system-prompt); nothing here mutates the user's
|
|
5
|
+
* own `~/.claude` config.
|
|
6
|
+
*/
|
|
7
|
+
import type { DoctorCheck, HarnessAdapter, LaunchOpts, SessionSummary, SpawnSpec } from "../../shared/types.js";
|
|
8
|
+
export interface ClaudeCodeAdapterOptions {
|
|
9
|
+
/** Overridable for tests (e.g. spawn `bash` instead of a real, auth-gated `claude`). */
|
|
10
|
+
binary?: string;
|
|
11
|
+
/** Overridable for tests. Defaults to the real home directory. */
|
|
12
|
+
homeDir?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class ClaudeCodeAdapter implements HarnessAdapter {
|
|
15
|
+
readonly id: "claude-code";
|
|
16
|
+
readonly eventSource: "hooks";
|
|
17
|
+
private readonly binary;
|
|
18
|
+
private readonly homeDir;
|
|
19
|
+
constructor(options?: ClaudeCodeAdapterOptions);
|
|
20
|
+
doctor(): Promise<DoctorCheck[]>;
|
|
21
|
+
launch(opts: LaunchOpts): SpawnSpec;
|
|
22
|
+
resume(agentSessionId: string, opts: LaunchOpts): SpawnSpec;
|
|
23
|
+
/**
|
|
24
|
+
* Headless one-shot run for TaskManager (see HarnessAdapter.launchTask).
|
|
25
|
+
* Verified against a real `claude` binary: `-p` carries the exact same
|
|
26
|
+
* --settings/--mcp-config/--append-system-prompt injection as launch()
|
|
27
|
+
* (all six hooks fire), skips the trust dialog entirely, and exits on its
|
|
28
|
+
* own when the turn completes. The extra flags:
|
|
29
|
+
* - --permission-mode acceptEdits: a headless task has no human to click
|
|
30
|
+
* through a permission prompt — without it a tool call hangs forever.
|
|
31
|
+
* - --output-format stream-json --verbose: line-oriented JSON progress on
|
|
32
|
+
* stdout (parsed by core/task-stream.ts) instead of a bare final answer.
|
|
33
|
+
* - --model / --max-turns: only when the caller sets them — a bounded task
|
|
34
|
+
* (canvas enrichment) pins a cheaper model and a hard turn cap instead of
|
|
35
|
+
* inheriting the user's interactive defaults.
|
|
36
|
+
*/
|
|
37
|
+
launchTask(opts: LaunchOpts): SpawnSpec;
|
|
38
|
+
listPastSessions(cwd: string): Promise<SessionSummary[]>;
|
|
39
|
+
}
|
|
40
|
+
export declare function createClaudeCodeAdapter(options?: ClaudeCodeAdapterOptions): HarnessAdapter;
|
|
41
|
+
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,cAAc,EACd,SAAS,EACV,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuGD,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,EAAE,EAAG,aAAa,CAAU;IACrC,QAAQ,CAAC,WAAW,EAAG,OAAO,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,wBAA6B;IAK5C,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAetC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAgBnC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS;IAa3D;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAmBjC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CA8B/D;AAcD,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAE1F"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-code adapter — launches/resumes the `claude` CLI and scans its
|
|
3
|
+
* transcript store for resumable history. All Sapiom-ness is injected via
|
|
4
|
+
* flags (settings/mcp-config/system-prompt); nothing here mutates the user's
|
|
5
|
+
* own `~/.claude` config.
|
|
6
|
+
*/
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { readFile, readdir, stat, open } from "node:fs/promises";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { basename, join } from "node:path";
|
|
12
|
+
import { promisify } from "node:util";
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
/** Bytes read from the tail of a transcript file. Files can be 100MB+; the
|
|
15
|
+
* summary/title we need is always near the end. */
|
|
16
|
+
const TRANSCRIPT_TAIL_BYTES = 65536;
|
|
17
|
+
/**
|
|
18
|
+
* Claude Code stores transcripts at `~/.claude/projects/<encoded-cwd>/*.jsonl`.
|
|
19
|
+
* The encoding strips the leading `/` and replaces `/` and `.` with `-`.
|
|
20
|
+
* If Claude Code changes this scheme, discovery silently returns no history
|
|
21
|
+
* (listPastSessions degrades to an empty list, not an error).
|
|
22
|
+
*/
|
|
23
|
+
function encodeProjectPath(cwd) {
|
|
24
|
+
const normalized = cwd.replace(/\\/g, "/");
|
|
25
|
+
return normalized.replace(/:/g, "").replace(/[/.]/g, "-");
|
|
26
|
+
}
|
|
27
|
+
function extractTextFromContent(content) {
|
|
28
|
+
if (typeof content === "string")
|
|
29
|
+
return content;
|
|
30
|
+
if (Array.isArray(content)) {
|
|
31
|
+
const texts = content
|
|
32
|
+
.map((block) => typeof block === "object" && block !== null && "text" in block
|
|
33
|
+
? block.text
|
|
34
|
+
: undefined)
|
|
35
|
+
.filter((text) => typeof text === "string");
|
|
36
|
+
return texts.length > 0 ? texts.join(" ") : undefined;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Read only the tail of a (possibly huge) JSONL transcript file. */
|
|
41
|
+
async function readTranscriptTail(filePath, maxBytes = TRANSCRIPT_TAIL_BYTES) {
|
|
42
|
+
let content;
|
|
43
|
+
let startedMidFile = false;
|
|
44
|
+
try {
|
|
45
|
+
const { size } = await stat(filePath);
|
|
46
|
+
if (size <= maxBytes) {
|
|
47
|
+
content = await readFile(filePath, "utf8");
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const offset = size - maxBytes;
|
|
51
|
+
const handle = await open(filePath, "r");
|
|
52
|
+
try {
|
|
53
|
+
const buffer = Buffer.allocUnsafe(maxBytes);
|
|
54
|
+
await handle.read(buffer, 0, maxBytes, offset);
|
|
55
|
+
content = buffer.toString("utf8");
|
|
56
|
+
startedMidFile = true;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
await handle.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
// A tail read may start mid-line; drop the (possibly truncated) first line.
|
|
67
|
+
const firstNewline = content.indexOf("\n");
|
|
68
|
+
const safeContent = startedMidFile && firstNewline >= 0 ? content.slice(firstNewline + 1) : content;
|
|
69
|
+
const entries = [];
|
|
70
|
+
for (const line of safeContent.split("\n")) {
|
|
71
|
+
const trimmed = line.trim();
|
|
72
|
+
if (!trimmed)
|
|
73
|
+
continue;
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(trimmed);
|
|
76
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
77
|
+
entries.push(parsed);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Skip malformed/truncated lines (expected at the start of a tail read).
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return entries;
|
|
85
|
+
}
|
|
86
|
+
function extractTitle(entries, fallback) {
|
|
87
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
88
|
+
const entry = entries[i];
|
|
89
|
+
if (entry?.type === "summary" && typeof entry.summary === "string" && entry.summary.trim()) {
|
|
90
|
+
return entry.summary.trim();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
if (entry?.type !== "user")
|
|
95
|
+
continue;
|
|
96
|
+
const text = extractTextFromContent(entry.message?.content)?.trim();
|
|
97
|
+
if (text)
|
|
98
|
+
return text.length > 120 ? `${text.slice(0, 120)}...` : text;
|
|
99
|
+
}
|
|
100
|
+
return fallback;
|
|
101
|
+
}
|
|
102
|
+
function buildConfigArgs(opts) {
|
|
103
|
+
const args = [];
|
|
104
|
+
if (opts.settingsFile)
|
|
105
|
+
args.push("--settings", opts.settingsFile);
|
|
106
|
+
if (opts.mcpConfigFile)
|
|
107
|
+
args.push("--mcp-config", opts.mcpConfigFile);
|
|
108
|
+
return args;
|
|
109
|
+
}
|
|
110
|
+
export class ClaudeCodeAdapter {
|
|
111
|
+
constructor(options = {}) {
|
|
112
|
+
this.id = "claude-code";
|
|
113
|
+
this.eventSource = "hooks";
|
|
114
|
+
this.binary = options.binary ?? "claude";
|
|
115
|
+
this.homeDir = options.homeDir ?? homedir();
|
|
116
|
+
}
|
|
117
|
+
async doctor() {
|
|
118
|
+
try {
|
|
119
|
+
const { stdout } = await execFileAsync(this.binary, ["--version"], { timeout: 5000 });
|
|
120
|
+
return [{ name: "claude", ok: true, detail: stdout.trim() || "installed" }];
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return [
|
|
124
|
+
{
|
|
125
|
+
name: "claude",
|
|
126
|
+
ok: false,
|
|
127
|
+
detail: `\`${this.binary}\` not found on PATH. Install Claude Code: https://docs.claude.com/en/docs/claude-code/setup`,
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
launch(opts) {
|
|
133
|
+
const args = buildConfigArgs(opts);
|
|
134
|
+
if (opts.systemPromptFile) {
|
|
135
|
+
args.push("--append-system-prompt", readPromptFile(opts.systemPromptFile));
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
command: this.binary,
|
|
139
|
+
args,
|
|
140
|
+
// Nested-agent conflict: Claude Code refuses to run "inside itself" if
|
|
141
|
+
// CLAUDECODE is already set, which it will be if the harness server
|
|
142
|
+
// itself was launched from within a Claude Code session.
|
|
143
|
+
env: { CLAUDECODE: null },
|
|
144
|
+
cwd: opts.cwd,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
resume(agentSessionId, opts) {
|
|
148
|
+
const args = ["--resume", agentSessionId, ...buildConfigArgs(opts)];
|
|
149
|
+
if (opts.systemPromptFile) {
|
|
150
|
+
args.push("--append-system-prompt", readPromptFile(opts.systemPromptFile));
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
command: this.binary,
|
|
154
|
+
args,
|
|
155
|
+
env: { CLAUDECODE: null },
|
|
156
|
+
cwd: opts.cwd,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Headless one-shot run for TaskManager (see HarnessAdapter.launchTask).
|
|
161
|
+
* Verified against a real `claude` binary: `-p` carries the exact same
|
|
162
|
+
* --settings/--mcp-config/--append-system-prompt injection as launch()
|
|
163
|
+
* (all six hooks fire), skips the trust dialog entirely, and exits on its
|
|
164
|
+
* own when the turn completes. The extra flags:
|
|
165
|
+
* - --permission-mode acceptEdits: a headless task has no human to click
|
|
166
|
+
* through a permission prompt — without it a tool call hangs forever.
|
|
167
|
+
* - --output-format stream-json --verbose: line-oriented JSON progress on
|
|
168
|
+
* stdout (parsed by core/task-stream.ts) instead of a bare final answer.
|
|
169
|
+
* - --model / --max-turns: only when the caller sets them — a bounded task
|
|
170
|
+
* (canvas enrichment) pins a cheaper model and a hard turn cap instead of
|
|
171
|
+
* inheriting the user's interactive defaults.
|
|
172
|
+
*/
|
|
173
|
+
launchTask(opts) {
|
|
174
|
+
if (!opts.prompt) {
|
|
175
|
+
throw new Error("claude-code adapter: launchTask requires opts.prompt");
|
|
176
|
+
}
|
|
177
|
+
const args = ["-p", opts.prompt, ...buildConfigArgs(opts)];
|
|
178
|
+
if (opts.systemPromptFile) {
|
|
179
|
+
args.push("--append-system-prompt", readPromptFile(opts.systemPromptFile));
|
|
180
|
+
}
|
|
181
|
+
if (opts.model)
|
|
182
|
+
args.push("--model", opts.model);
|
|
183
|
+
if (opts.maxTurns != null)
|
|
184
|
+
args.push("--max-turns", String(opts.maxTurns));
|
|
185
|
+
args.push("--permission-mode", "acceptEdits", "--output-format", "stream-json", "--verbose");
|
|
186
|
+
return {
|
|
187
|
+
command: this.binary,
|
|
188
|
+
args,
|
|
189
|
+
env: { CLAUDECODE: null },
|
|
190
|
+
cwd: opts.cwd,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async listPastSessions(cwd) {
|
|
194
|
+
const projectDir = join(this.homeDir, ".claude", "projects", encodeProjectPath(cwd));
|
|
195
|
+
let entries;
|
|
196
|
+
try {
|
|
197
|
+
entries = await readdir(projectDir);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
const summaries = [];
|
|
203
|
+
for (const file of entries) {
|
|
204
|
+
if (!file.endsWith(".jsonl"))
|
|
205
|
+
continue;
|
|
206
|
+
const filePath = join(projectDir, file);
|
|
207
|
+
const agentSessionId = basename(file, ".jsonl");
|
|
208
|
+
const transcript = await readTranscriptTail(filePath);
|
|
209
|
+
if (transcript.length === 0)
|
|
210
|
+
continue;
|
|
211
|
+
const fileStat = await stat(filePath).catch(() => undefined);
|
|
212
|
+
const lastActiveAt = fileStat ? fileStat.mtime.toISOString() : new Date(0).toISOString();
|
|
213
|
+
summaries.push({
|
|
214
|
+
agentSessionId,
|
|
215
|
+
harness: "claude-code",
|
|
216
|
+
cwd,
|
|
217
|
+
title: extractTitle(transcript, agentSessionId),
|
|
218
|
+
lastActiveAt,
|
|
219
|
+
source: "transcript",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return summaries.sort((a, b) => (a.lastActiveAt < b.lastActiveAt ? 1 : -1));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function readPromptFile(path) {
|
|
226
|
+
// launch()/resume() are synchronous per the adapter contract, so this reads
|
|
227
|
+
// synchronously; profile prompts are small (a few KB), read once at spawn time.
|
|
228
|
+
try {
|
|
229
|
+
return readFileSync(path, "utf8");
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
throw new Error(`claude-code adapter: failed to read systemPromptFile "${path}": ${err.message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
export function createClaudeCodeAdapter(options) {
|
|
236
|
+
return new ClaudeCodeAdapter(options);
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/core/adapters/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codex adapter — launches/resumes the `codex` CLI and scans its rollout
|
|
3
|
+
* store for resumable history. Codex has no hook system (see
|
|
4
|
+
* core/collector/codex-tailer.ts for how its analytics eventSource works
|
|
5
|
+
* instead); this file only covers process launch/resume/doctor/history.
|
|
6
|
+
*
|
|
7
|
+
* Verified against a locally installed `codex-cli 0.134.0`: `codex resume
|
|
8
|
+
* [SESSION_ID] [PROMPT]` (positional UUID or thread name) via `codex resume
|
|
9
|
+
* --help`; the generic `-c key=value` config-override mechanism via `codex
|
|
10
|
+
* --help`; and, via real spawns with `--strict-config`, that both
|
|
11
|
+
* `developer_instructions` and `model_instructions_file` are real,
|
|
12
|
+
* recognized keys (an earlier version of this file used the latter and
|
|
13
|
+
* flagged both as unconfirmed — see buildConfigArgs below for why the
|
|
14
|
+
* adapter now uses `developer_instructions` instead: `model_instructions_file`
|
|
15
|
+
* makes codex's own startup depend on re-reading a file we already have the
|
|
16
|
+
* content of, and an unreadable path there kills the process instantly with
|
|
17
|
+
* no trust prompt, no TUI — precisely the "session has no live pty" a user
|
|
18
|
+
* sees with zero indication why).
|
|
19
|
+
*/
|
|
20
|
+
import type { DoctorCheck, HarnessAdapter, LaunchOpts, SessionSummary, SpawnSpec } from "../../shared/types.js";
|
|
21
|
+
/** Strips ANSI/CSI/OSC control sequences a pty stream is otherwise full of,
|
|
22
|
+
* so text pattern-matching (e.g. `TRUST_PROMPT_PATTERN`) sees the words a
|
|
23
|
+
* human would actually read rather than raw terminal-control bytes. Not
|
|
24
|
+
* exhaustive of every escape sequence a TUI could emit, but covers what
|
|
25
|
+
* Codex's TUI is confirmed (via real captures) to use: CSI (cursor moves,
|
|
26
|
+
* SGR color/style) and OSC (window title) sequences. */
|
|
27
|
+
export declare function stripAnsi(text: string): string;
|
|
28
|
+
export interface CodexAdapterOptions {
|
|
29
|
+
/** Overridable for tests. */
|
|
30
|
+
binary?: string;
|
|
31
|
+
/** Overridable for tests. Defaults to the real home directory. */
|
|
32
|
+
homeDir?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class CodexAdapter implements HarnessAdapter {
|
|
35
|
+
readonly id: "codex";
|
|
36
|
+
readonly eventSource: "transcript-tail";
|
|
37
|
+
private readonly binary;
|
|
38
|
+
private readonly homeDir;
|
|
39
|
+
constructor(options?: CodexAdapterOptions);
|
|
40
|
+
doctor(): Promise<DoctorCheck[]>;
|
|
41
|
+
launch(opts: LaunchOpts): SpawnSpec;
|
|
42
|
+
resume(agentSessionId: string, opts: LaunchOpts): SpawnSpec;
|
|
43
|
+
/**
|
|
44
|
+
* See `HarnessAdapter.detectBlockingPrompt`. Codex needs this (Claude Code
|
|
45
|
+
* doesn't) because its rollout file — and therefore the SessionStart-
|
|
46
|
+
* equivalent event `SessionManager` otherwise waits on — isn't written
|
|
47
|
+
* until the *first* turn is actually submitted, confirmed empirically
|
|
48
|
+
* against a real `codex-cli 0.134.0`: an idle, fully-interactive session
|
|
49
|
+
* (trust prompt already accepted, composer visibly ready) produces zero
|
|
50
|
+
* files under `~/.codex/sessions` for as long as nothing is submitted.
|
|
51
|
+
* That real signal therefore can't arrive before the very first
|
|
52
|
+
* injection that needs it — a scrollback check is the only thing that
|
|
53
|
+
* can stand in for it up to that point.
|
|
54
|
+
*/
|
|
55
|
+
detectBlockingPrompt(scrollback: string): boolean;
|
|
56
|
+
listPastSessions(cwd: string): Promise<SessionSummary[]>;
|
|
57
|
+
}
|
|
58
|
+
export declare function createCodexAdapter(options?: CodexAdapterOptions): HarnessAdapter;
|
|
59
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,cAAc,EACd,SAAS,EACV,MAAM,uBAAuB,CAAC;AA0B/B;;;;;yDAKyD;AACzD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgB9C;AAED,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmGD,qBAAa,YAAa,YAAW,cAAc;IACjD,QAAQ,CAAC,EAAE,EAAG,OAAO,CAAU;IAC/B,QAAQ,CAAC,WAAW,EAAG,iBAAiB,CAAU;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,mBAAwB;IAKvC,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAetC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAWnC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS;IAS3D;;;;;;;;;;;OAWG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI3C,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CAwC/D;AA8DD,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,cAAc,CAEhF"}
|