@sapiom/harness 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +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,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas kit: a prewritten template — CSS (light + dark, following the
|
|
3
|
+
* app's own theme) and a documented set of markup building blocks — that an
|
|
4
|
+
* agent clones and fills in with real content, instead of hand-writing a
|
|
5
|
+
* canvas from scratch every time. This is deliberately NOT a JSON-data +
|
|
6
|
+
* JS-renderer scheme: the agent authors real HTML/SVG using the template's
|
|
7
|
+
* classes, so it keeps full expressive freedom over the graph itself while
|
|
8
|
+
* the CSS (and therefore the visual language: colors, glow, edge styling,
|
|
9
|
+
* legend markers) is locked and can't drift.
|
|
10
|
+
*
|
|
11
|
+
* `renderCanvasDocument(bodyHtml)` is the single shared shell (CSS + theme
|
|
12
|
+
* switch) — `TEMPLATE_HTML` (the pristine, empty-state document) and
|
|
13
|
+
* `scripts/seed-example.mjs` (prefilled with real content) both wrap their
|
|
14
|
+
* body through it, so the kit and the seed can't drift from each other.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from "node:fs/promises";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
import { CANVAS_DIR, CANVAS_INDEX } from "../shared/types.js";
|
|
19
|
+
import { isLegacyDeterministicCanvas } from "./canvas-index-classify.js";
|
|
20
|
+
/** Lives alongside index.html, in the same CANVAS_DIR — a pristine copy of
|
|
21
|
+
* the template, written once and never touched again, so "clone the
|
|
22
|
+
* template" always has a clean, un-filled-in source to clone from. */
|
|
23
|
+
export const CANVAS_TEMPLATE_FILE = `${CANVAS_DIR}/_template.html`;
|
|
24
|
+
function themeStyleBlock() {
|
|
25
|
+
// Values ported 1:1 from web/src/styles.css's :root (light, default) and
|
|
26
|
+
// [data-theme="dark"] tokens — kept in sync by eye; see this module's own
|
|
27
|
+
// doc comment. Dark is also this canvas kit's own fallback default
|
|
28
|
+
// (prefers-color-scheme: no-preference) since it's the app's own default
|
|
29
|
+
// canvas look before per-theme passthrough landed for embedded iframes.
|
|
30
|
+
return `
|
|
31
|
+
:root {
|
|
32
|
+
--canvas-bg: #f3f4f6;
|
|
33
|
+
--canvas-panel: #f5f5f5;
|
|
34
|
+
--canvas-border: #e5e5e5;
|
|
35
|
+
--canvas-border-strong: #d4d4d8;
|
|
36
|
+
--canvas-text: #1a1a1a;
|
|
37
|
+
--canvas-text-dim: #737373;
|
|
38
|
+
--canvas-accent: #05a9bc;
|
|
39
|
+
--canvas-success: #05a9bc;
|
|
40
|
+
--canvas-escalation: #b45309;
|
|
41
|
+
--canvas-failure: #ef4444;
|
|
42
|
+
}
|
|
43
|
+
:root[data-canvas-theme="dark"] {
|
|
44
|
+
--canvas-bg: #0f0f0f;
|
|
45
|
+
--canvas-panel: #1a1a1a;
|
|
46
|
+
--canvas-border: #2e2e2e;
|
|
47
|
+
--canvas-border-strong: #3a3a3a;
|
|
48
|
+
--canvas-text: #fafafa;
|
|
49
|
+
--canvas-text-dim: #a1a1aa;
|
|
50
|
+
--canvas-accent: #6be195;
|
|
51
|
+
--canvas-success: #6be195;
|
|
52
|
+
--canvas-escalation: #f59e0b;
|
|
53
|
+
--canvas-failure: #f87171;
|
|
54
|
+
}
|
|
55
|
+
@media (prefers-color-scheme: dark) {
|
|
56
|
+
:root:not([data-canvas-theme]) {
|
|
57
|
+
--canvas-bg: #0f0f0f;
|
|
58
|
+
--canvas-panel: #1a1a1a;
|
|
59
|
+
--canvas-border: #2e2e2e;
|
|
60
|
+
--canvas-border-strong: #3a3a3a;
|
|
61
|
+
--canvas-text: #fafafa;
|
|
62
|
+
--canvas-text-dim: #a1a1aa;
|
|
63
|
+
--canvas-accent: #6be195;
|
|
64
|
+
--canvas-success: #6be195;
|
|
65
|
+
--canvas-escalation: #f59e0b;
|
|
66
|
+
--canvas-failure: #f87171;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
* { box-sizing: border-box; }
|
|
70
|
+
html, body {
|
|
71
|
+
margin: 0; min-height: 100%; background: var(--canvas-bg); color: var(--canvas-text);
|
|
72
|
+
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
73
|
+
}
|
|
74
|
+
#canvas-root { max-width: 1100px; margin: 0 auto; padding: 24px 20px; display: flex; flex-direction: column; gap: 18px; }
|
|
75
|
+
|
|
76
|
+
/* --- structural classes: keep these, and their names, untouched --- */
|
|
77
|
+
.canvas-panel { background: var(--canvas-panel); border: 1px solid var(--canvas-border); border-radius: 16px; padding: 20px; }
|
|
78
|
+
.canvas-header { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
|
|
79
|
+
.canvas-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
80
|
+
.canvas-title { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.01em; }
|
|
81
|
+
.canvas-badge {
|
|
82
|
+
font-size: 11px; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--canvas-border-strong);
|
|
83
|
+
color: var(--canvas-text-dim);
|
|
84
|
+
}
|
|
85
|
+
.canvas-badge--stale { color: var(--canvas-escalation); border-color: var(--canvas-escalation); }
|
|
86
|
+
.canvas-subtitle { margin: 0; color: var(--canvas-text-dim); font-size: 12.5px; }
|
|
87
|
+
.canvas-stats { display: flex; gap: 22px; margin-top: 2px; }
|
|
88
|
+
.canvas-stat { display: flex; flex-direction: column; }
|
|
89
|
+
.canvas-stat-value { font-size: 17px; font-weight: 600; }
|
|
90
|
+
.canvas-stat-label { font-size: 10px; color: var(--canvas-text-dim); text-transform: uppercase; letter-spacing: 0.06em; }
|
|
91
|
+
.canvas-diagram-panel { overflow-x: auto; }
|
|
92
|
+
.canvas-empty-note { color: var(--canvas-text-dim); font-size: 13px; text-align: center; padding: 60px 20px; margin: 0; }
|
|
93
|
+
/* The <svg> carries explicit width/height, so it always renders at its
|
|
94
|
+
NATURAL (1×) size — a narrow single-column graph can never stretch to fill
|
|
95
|
+
the pane (the old width:100% did that, ballooning nodes ~4×). Centered when
|
|
96
|
+
it fits; a graph wider than the pane keeps full size and scrolls via
|
|
97
|
+
.canvas-diagram-panel's overflow-x, staying legible instead of shrinking. */
|
|
98
|
+
.canvas-graph-svg { display: block; margin: 0 auto; }
|
|
99
|
+
|
|
100
|
+
/* --- node kinds: entry | step | pause | terminal-success | terminal-warn | launched-workflow --- */
|
|
101
|
+
.canvas-node .canvas-node-rect { fill: var(--canvas-panel); stroke-width: 1.5; stroke: var(--canvas-border-strong); }
|
|
102
|
+
.node--entry .canvas-node-rect { stroke: var(--canvas-accent); }
|
|
103
|
+
.node--pause .canvas-node-rect { stroke: var(--canvas-text-dim); stroke-dasharray: 5 4; }
|
|
104
|
+
.node--terminal-success .canvas-node-rect { stroke: var(--canvas-success); }
|
|
105
|
+
.node--terminal-warn .canvas-node-rect { stroke: var(--canvas-escalation); }
|
|
106
|
+
.node--launched-workflow .canvas-node-rect { stroke: var(--canvas-accent); stroke-dasharray: 5 4; }
|
|
107
|
+
.canvas-node-title { fill: var(--canvas-text); font-size: 13px; font-weight: 600; text-anchor: middle; dominant-baseline: middle; }
|
|
108
|
+
.canvas-node-sub { fill: var(--canvas-text-dim); font-size: 9.5px; text-anchor: middle; dominant-baseline: middle; }
|
|
109
|
+
|
|
110
|
+
/* --- enrichment layout hints: group bands sit behind edges and nodes --- */
|
|
111
|
+
.canvas-group-band { fill: var(--canvas-accent); fill-opacity: 0.06; stroke: var(--canvas-border); stroke-dasharray: 3 5; }
|
|
112
|
+
.canvas-group-label { fill: var(--canvas-text-dim); font-size: 9px; text-transform: uppercase; letter-spacing: 0.06em; }
|
|
113
|
+
|
|
114
|
+
/* --- edge kinds: sequential (base) | branching (--success/--warn) | cross-workflow signal/handoff (--cross) --- */
|
|
115
|
+
.canvas-edge { fill: none; stroke-width: 1.8; stroke: var(--canvas-border-strong); }
|
|
116
|
+
.canvas-edge--success { stroke: var(--canvas-success); }
|
|
117
|
+
.canvas-edge--warn { stroke: var(--canvas-escalation); }
|
|
118
|
+
.canvas-edge--cross { stroke: var(--canvas-text-dim); stroke-dasharray: 4 4; }
|
|
119
|
+
.canvas-edge--launch { stroke: var(--canvas-accent); stroke-dasharray: 4 4; }
|
|
120
|
+
.canvas-edge-label { fill: var(--canvas-text-dim); font-size: 9px; }
|
|
121
|
+
.canvas-arrow-fill { fill: var(--canvas-border-strong); }
|
|
122
|
+
.canvas-arrow-fill--success { fill: var(--canvas-success); }
|
|
123
|
+
.canvas-arrow-fill--warn { fill: var(--canvas-escalation); }
|
|
124
|
+
|
|
125
|
+
/* --- legend + interconnections --- */
|
|
126
|
+
.canvas-legend { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; font-size: 11px; color: var(--canvas-text-dim); }
|
|
127
|
+
.canvas-legend-item { display: flex; align-items: center; gap: 6px; }
|
|
128
|
+
.canvas-legend-marker { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
|
|
129
|
+
.canvas-legend-marker--entry { background: var(--canvas-accent); }
|
|
130
|
+
.canvas-legend-marker--step { border: 1.5px solid var(--canvas-border-strong); background: transparent; }
|
|
131
|
+
.canvas-legend-marker--pause { border: 1.5px dashed var(--canvas-text-dim); background: transparent; }
|
|
132
|
+
.canvas-legend-marker--terminal-success { background: var(--canvas-success); border-radius: 3px; }
|
|
133
|
+
.canvas-legend-marker--terminal-warn { background: var(--canvas-escalation); border-radius: 3px; }
|
|
134
|
+
.canvas-legend-marker--cross { border: 1.5px dashed var(--canvas-text-dim); border-radius: 2px; background: transparent; }
|
|
135
|
+
.canvas-legend-marker--launched-workflow { border: 1.5px dashed var(--canvas-accent); border-radius: 3px; background: transparent; }
|
|
136
|
+
.canvas-interconnections { display: flex; flex-direction: column; gap: 12px; }
|
|
137
|
+
.canvas-panel-title { margin: 0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--canvas-text-dim); }
|
|
138
|
+
.canvas-interconnection-row { display: grid; grid-template-columns: 12px 1fr auto; column-gap: 8px; row-gap: 2px; align-items: baseline; }
|
|
139
|
+
.canvas-interconnection-title { font-weight: 600; font-size: 13px; }
|
|
140
|
+
.canvas-interconnection-tag { font-size: 10px; color: var(--canvas-text-dim); border: 1px solid var(--canvas-border-strong); border-radius: 6px; padding: 1px 6px; }
|
|
141
|
+
.canvas-interconnection-desc { grid-column: 2 / -1; margin: 0; font-size: 11.5px; color: var(--canvas-text-dim); }
|
|
142
|
+
.canvas-footer { display: flex; flex-direction: column; gap: 10px; padding: 4px 2px 2px; }
|
|
143
|
+
.canvas-note { margin: 0; font-size: 11px; color: var(--canvas-text-dim); font-style: italic; }
|
|
144
|
+
.canvas-notes { margin: 0; padding-left: 16px; display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--canvas-text-dim); }
|
|
145
|
+
.canvas-cross-workflow { margin: 0; font-size: 11.5px; color: var(--canvas-text-dim); }
|
|
146
|
+
template { display: none; }
|
|
147
|
+
`.trim();
|
|
148
|
+
}
|
|
149
|
+
/** Reads the current theme from `?theme=light|dark`, falling back to
|
|
150
|
+
* `prefers-color-scheme` (the CSS `@media` block above) when the param is
|
|
151
|
+
* absent — the only script in the whole document. */
|
|
152
|
+
const THEME_SCRIPT = `
|
|
153
|
+
(function () {
|
|
154
|
+
var params = new URLSearchParams(location.search);
|
|
155
|
+
var theme = params.get("theme");
|
|
156
|
+
if (theme === "light" || theme === "dark") {
|
|
157
|
+
document.documentElement.setAttribute("data-canvas-theme", theme);
|
|
158
|
+
}
|
|
159
|
+
})();
|
|
160
|
+
`.trim();
|
|
161
|
+
/**
|
|
162
|
+
* One example of every markup building block, inside an inert `<template>`
|
|
163
|
+
* (never rendered, but real parsed DOM — not an HTML comment, so class names
|
|
164
|
+
* with `--` in them are never at risk of being mistaken for a comment
|
|
165
|
+
* terminator). An agent reads this once to learn the exact shape of a node,
|
|
166
|
+
* an edge of each kind, a stat, a badge, a legend item, and an
|
|
167
|
+
* interconnection row, then writes its own real ones using the same classes.
|
|
168
|
+
*/
|
|
169
|
+
const PATTERNS_TEMPLATE = `
|
|
170
|
+
<template id="canvas-patterns">
|
|
171
|
+
<!-- copy the pattern you need; this whole block is never rendered -->
|
|
172
|
+
<span class="canvas-badge">standalone workflow</span>
|
|
173
|
+
<div class="canvas-stat"><span class="canvas-stat-value">5</span><span class="canvas-stat-label">steps</span></div>
|
|
174
|
+
<svg>
|
|
175
|
+
<g class="canvas-node node--entry" filter="url(#canvas-glow)" transform="translate(392,40)">
|
|
176
|
+
<rect class="canvas-node-rect" width="176" height="56" rx="14" />
|
|
177
|
+
<text class="canvas-node-title" x="88" y="24">step_name</text>
|
|
178
|
+
<text class="canvas-node-sub" x="88" y="40">short description</text>
|
|
179
|
+
</g>
|
|
180
|
+
<g class="canvas-node node--step" filter="url(#canvas-glow)" transform="translate(392,150)">
|
|
181
|
+
<rect class="canvas-node-rect" width="176" height="56" rx="14" />
|
|
182
|
+
<text class="canvas-node-title" x="88" y="28">step_name</text>
|
|
183
|
+
</g>
|
|
184
|
+
<g class="canvas-node node--pause" filter="url(#canvas-glow)" transform="translate(392,260)">
|
|
185
|
+
<rect class="canvas-node-rect" width="176" height="56" rx="14" />
|
|
186
|
+
<text class="canvas-node-title" x="88" y="24">step_name</text>
|
|
187
|
+
<text class="canvas-node-sub" x="88" y="40">waits for a human / event</text>
|
|
188
|
+
</g>
|
|
189
|
+
<g class="canvas-node node--terminal-success" filter="url(#canvas-glow)" transform="translate(200,410)">
|
|
190
|
+
<rect class="canvas-node-rect" width="176" height="56" rx="14" />
|
|
191
|
+
<text class="canvas-node-title" x="88" y="24">step_name</text>
|
|
192
|
+
<text class="canvas-node-sub" x="88" y="40">terminate({ resolved: true })</text>
|
|
193
|
+
</g>
|
|
194
|
+
<g class="canvas-node node--terminal-warn" filter="url(#canvas-glow)" transform="translate(600,410)">
|
|
195
|
+
<rect class="canvas-node-rect" width="176" height="56" rx="14" />
|
|
196
|
+
<text class="canvas-node-title" x="88" y="24">step_name</text>
|
|
197
|
+
<text class="canvas-node-sub" x="88" y="40">terminate({ escalated: true })</text>
|
|
198
|
+
</g>
|
|
199
|
+
<!-- sequential: a step with exactly one successor -> straight line -->
|
|
200
|
+
<path class="canvas-edge" d="M480,96 L480,150" marker-end="url(#canvas-arrow)" />
|
|
201
|
+
<!-- branching: a step with multiple successors -> curved, colored by the destination's outcome -->
|
|
202
|
+
<path class="canvas-edge canvas-edge--success" d="M480,320 C480,365 288,365 288,410" marker-end="url(#canvas-arrow-success)" />
|
|
203
|
+
<path class="canvas-edge canvas-edge--warn" d="M480,320 C480,365 688,365 688,410" marker-end="url(#canvas-arrow-warn)" />
|
|
204
|
+
<!-- cross-workflow signal/handoff: dashed, always neutral -->
|
|
205
|
+
<path class="canvas-edge canvas-edge--cross" d="M480,320 L480,410" marker-end="url(#canvas-arrow)" />
|
|
206
|
+
<!-- edge label, positioned near the branch point, anchored toward its own destination -->
|
|
207
|
+
<text class="canvas-edge-label" x="440" y="345" text-anchor="end">category == x</text>
|
|
208
|
+
</svg>
|
|
209
|
+
<div class="canvas-interconnection-row">
|
|
210
|
+
<span class="canvas-legend-marker canvas-legend-marker--entry"></span>
|
|
211
|
+
<span class="canvas-interconnection-title">external -> intake</span>
|
|
212
|
+
<span class="canvas-interconnection-tag">signal</span>
|
|
213
|
+
<p class="canvas-interconnection-desc">an order object enters here</p>
|
|
214
|
+
</div>
|
|
215
|
+
<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--entry"></span>entry / active step</span>
|
|
216
|
+
<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--step"></span>step</span>
|
|
217
|
+
<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--pause"></span>pause / waits for input</span>
|
|
218
|
+
<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--terminal-success"></span>terminal · success</span>
|
|
219
|
+
<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--terminal-warn"></span>terminal · escalation</span>
|
|
220
|
+
<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--cross"></span>cross-workflow signal/handoff</span>
|
|
221
|
+
</template>
|
|
222
|
+
`.trim();
|
|
223
|
+
/** The `<defs>` every SVG graph needs — same glow filter and arrow markers
|
|
224
|
+
* (default/success/warn) referenced by every node/edge pattern above. */
|
|
225
|
+
const SVG_DEFS = `
|
|
226
|
+
<svg width="0" height="0" style="position: absolute;">
|
|
227
|
+
<defs>
|
|
228
|
+
<marker id="canvas-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
229
|
+
<path class="canvas-arrow-fill" d="M0,0 L10,5 L0,10 z" />
|
|
230
|
+
</marker>
|
|
231
|
+
<marker id="canvas-arrow-success" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
232
|
+
<path class="canvas-arrow-fill canvas-arrow-fill--success" d="M0,0 L10,5 L0,10 z" />
|
|
233
|
+
</marker>
|
|
234
|
+
<marker id="canvas-arrow-warn" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
235
|
+
<path class="canvas-arrow-fill canvas-arrow-fill--warn" d="M0,0 L10,5 L0,10 z" />
|
|
236
|
+
</marker>
|
|
237
|
+
<filter id="canvas-glow" x="-40%" y="-40%" width="180%" height="180%">
|
|
238
|
+
<feDropShadow dx="0" dy="0" stdDeviation="3" flood-color="var(--canvas-accent)" flood-opacity="0.18" />
|
|
239
|
+
</filter>
|
|
240
|
+
</defs>
|
|
241
|
+
</svg>
|
|
242
|
+
`.trim();
|
|
243
|
+
/**
|
|
244
|
+
* Wraps `bodyHtml` in the shared canvas document shell: doctype, the theme
|
|
245
|
+
* switch script, and every CSS class an agent's markup can use. This is the
|
|
246
|
+
* single source both the pristine template and `scripts/seed-example.mjs`'s
|
|
247
|
+
* prefilled instance render through, so they can't drift from each other.
|
|
248
|
+
*/
|
|
249
|
+
export function renderCanvasDocument(bodyHtml) {
|
|
250
|
+
return `<!DOCTYPE html>
|
|
251
|
+
<html lang="en">
|
|
252
|
+
<head>
|
|
253
|
+
<meta charset="UTF-8" />
|
|
254
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
255
|
+
<title>Sapiom workflow canvas</title>
|
|
256
|
+
<script>
|
|
257
|
+
${THEME_SCRIPT}
|
|
258
|
+
</script>
|
|
259
|
+
<style>
|
|
260
|
+
${themeStyleBlock()}
|
|
261
|
+
</style>
|
|
262
|
+
</head>
|
|
263
|
+
<body>
|
|
264
|
+
${SVG_DEFS}
|
|
265
|
+
<div id="canvas-root">
|
|
266
|
+
${bodyHtml}
|
|
267
|
+
</div>
|
|
268
|
+
</body>
|
|
269
|
+
</html>
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
const TEMPLATE_BODY = `
|
|
273
|
+
<!--
|
|
274
|
+
═══════════════════════════════════════════════════════════════════════
|
|
275
|
+
SAPIOM CANVAS TEMPLATE — fill in the sections below to visualize a
|
|
276
|
+
workflow. Keep the <style> block in <head> and the structural classes
|
|
277
|
+
you see here untouched; only add markup using the patterns in the
|
|
278
|
+
<template id="canvas-patterns"> block near the end of <body> (it is
|
|
279
|
+
never rendered — read it, copy from it, don't edit it). Delete the
|
|
280
|
+
empty-state note below and the patterns template once you're done
|
|
281
|
+
authoring. For a single bound workflow, one canvas-panel is enough; for
|
|
282
|
+
a workspace overview, add one canvas-panel per workflow plus an
|
|
283
|
+
Interconnections panel (see the pattern for its row shape).
|
|
284
|
+
═══════════════════════════════════════════════════════════════════════
|
|
285
|
+
-->
|
|
286
|
+
<section class="canvas-panel">
|
|
287
|
+
<header class="canvas-header">
|
|
288
|
+
<div class="canvas-title-row">
|
|
289
|
+
<h1 class="canvas-title">Untitled workflow</h1>
|
|
290
|
+
</div>
|
|
291
|
+
<p class="canvas-subtitle">One-line description of what this workflow does.</p>
|
|
292
|
+
<div class="canvas-stats"></div>
|
|
293
|
+
</header>
|
|
294
|
+
<div class="canvas-diagram-panel">
|
|
295
|
+
<p class="canvas-empty-note">Nothing visualized yet — run Visualize on a workflow.</p>
|
|
296
|
+
</div>
|
|
297
|
+
</section>
|
|
298
|
+
|
|
299
|
+
${PATTERNS_TEMPLATE}
|
|
300
|
+
`.trim();
|
|
301
|
+
/** The exact document written to both `_template.html` and the initial
|
|
302
|
+
* `index.html` — a friendly empty state plus the patterns reference. */
|
|
303
|
+
export const TEMPLATE_HTML = renderCanvasDocument(TEMPLATE_BODY);
|
|
304
|
+
async function writeIfMissing(filePath, content) {
|
|
305
|
+
try {
|
|
306
|
+
await fs.access(filePath);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Doesn't exist yet — fall through and write it.
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
314
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
console.error(`[harness] failed to write canvas file ${filePath}:`, err);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Backfill-only: ensures both `<cwd>/.sapiom/canvas/_template.html` (a
|
|
322
|
+
* pristine copy, so "clone the template" always has a clean source — see
|
|
323
|
+
* the visualize macro's prompt) and `<cwd>/.sapiom/canvas/index.html` (the
|
|
324
|
+
* live canvas, seeded with the same empty-state content) exist. Never
|
|
325
|
+
* clobbers either file if something's already there — an earlier session,
|
|
326
|
+
* or the agent's own edits. Called from SessionManager's create()/resume()
|
|
327
|
+
* (see its `ensureCanvasTemplate` option) so the canvas pane never opens to
|
|
328
|
+
* a completely empty iframe. Best-effort, like the sibling
|
|
329
|
+
* `workspace-context.ts` writer: a session's cwd could be unwritable, and
|
|
330
|
+
* that must never fail session creation itself.
|
|
331
|
+
*/
|
|
332
|
+
export async function ensureCanvasTemplate(cwd) {
|
|
333
|
+
await removeLegacyOverviewIndex(path.join(cwd, CANVAS_INDEX));
|
|
334
|
+
await writeIfMissing(path.join(cwd, CANVAS_TEMPLATE_FILE), TEMPLATE_HTML);
|
|
335
|
+
await writeIfMissing(path.join(cwd, CANVAS_INDEX), TEMPLATE_HTML);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Deletes `index.html` iff it's a legacy deterministic overview a pre-split
|
|
339
|
+
* server wrote there (detected by signature — see canvas-index-classify.ts).
|
|
340
|
+
* Such a file is a stale stacked all-workflows page with baked-in "render
|
|
341
|
+
* failed" panels; left in place it keeps getting served (or, at minimum,
|
|
342
|
+
* skipped by `writeIfMissing` so a fresh seed never replaces it). An
|
|
343
|
+
* agent-authored custom canvas and the seeded template are both left
|
|
344
|
+
* untouched. Best-effort — a read/unlink failure must never fail session
|
|
345
|
+
* creation. Once removed, the `writeIfMissing` below reseeds the clean
|
|
346
|
+
* template.
|
|
347
|
+
*/
|
|
348
|
+
async function removeLegacyOverviewIndex(indexPath) {
|
|
349
|
+
let content;
|
|
350
|
+
try {
|
|
351
|
+
content = await fs.readFile(indexPath, "utf8");
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return; // Missing (the common case) or unreadable — nothing to clean up.
|
|
355
|
+
}
|
|
356
|
+
if (!isLegacyDeterministicCanvas(content))
|
|
357
|
+
return;
|
|
358
|
+
try {
|
|
359
|
+
await fs.rm(indexPath, { force: true });
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
console.error(`[harness] failed to remove legacy canvas overview ${indexPath}:`, err);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=canvas-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-template.js","sourceRoot":"","sources":["../../src/core/canvas-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE;;uEAEuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,UAAU,iBAAiB,CAAC;AAEnE,SAAS,eAAe;IACtB,yEAAyE;IACzE,0EAA0E;IAC1E,mEAAmE;IACnE,yEAAyE;IACzE,wEAAwE;IACxE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqHR,CAAC,IAAI,EAAE,CAAC;AACT,CAAC;AAED;;sDAEsD;AACtD,MAAM,YAAY,GAAG;;;;;;;;CAQpB,CAAC,IAAI,EAAE,CAAC;AAET;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDzB,CAAC,IAAI,EAAE,CAAC;AAET;0EAC0E;AAC1E,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;CAiBhB,CAAC,IAAI,EAAE,CAAC;AAET;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,OAAO;;;;;;;EAOP,YAAY;;;EAGZ,eAAe,EAAE;;;;EAIjB,QAAQ;;EAER,QAAQ;;;;CAIT,CAAC;AACF,CAAC;AAED,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BpB,iBAAiB;CAClB,CAAC,IAAI,EAAE,CAAC;AAET;yEACyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;AAEjE,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,OAAe;IAC7D,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO;IACT,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,yCAAyC,QAAQ,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAW;IACpD,MAAM,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;IAC9D,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC,EAAE,aAAa,CAAC,CAAC;IAC1E,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,yBAAyB,CAAC,SAAiB;IACxD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,iEAAiE;IAC3E,CAAC;IACD,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC;QAAE,OAAO;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,qDAAqD,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;IACxF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cheap change fingerprint for a directory tree: sorted `path:mtime:size`
|
|
3
|
+
* entries. Used by the polling fallback (and directly testable on its own)
|
|
4
|
+
* — not a content hash, just enough to notice something moved.
|
|
5
|
+
*/
|
|
6
|
+
export declare function snapshotCanvasDir(canvasDir: string): string;
|
|
7
|
+
export interface CanvasWatcherManagerDeps {
|
|
8
|
+
/** Debounced per-session change notification — the integrator broadcasts
|
|
9
|
+
* `{type: "canvas.reload", harnessSessionId}` on /ws/events from this. */
|
|
10
|
+
onChange(harnessSessionId: string): void;
|
|
11
|
+
}
|
|
12
|
+
/** Registry of one SessionCanvasWatcher per active harness session. */
|
|
13
|
+
export declare class CanvasWatcherManager {
|
|
14
|
+
private readonly deps;
|
|
15
|
+
private readonly watchers;
|
|
16
|
+
constructor(deps: CanvasWatcherManagerDeps);
|
|
17
|
+
/** Idempotent: replaces any existing watcher for this session (e.g. a resume
|
|
18
|
+
* into a different cwd). */
|
|
19
|
+
start(harnessSessionId: string, cwd: string): void;
|
|
20
|
+
stop(harnessSessionId: string): void;
|
|
21
|
+
stopAll(): void;
|
|
22
|
+
/** Test/debug helper — how many sessions currently have an active watcher. */
|
|
23
|
+
get size(): number;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=canvas-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-watcher.d.ts","sourceRoot":"","sources":["../../src/core/canvas-watcher.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAiB3D;AA6FD,MAAM,WAAW,wBAAwB;IACvC;+EAC2E;IAC3E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,uEAAuE;AACvE,qBAAa,oBAAoB;IAGnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2C;gBAEvC,IAAI,EAAE,wBAAwB;IAE3D;iCAC6B;IAC7B,KAAK,CAAC,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAQlD,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAKpC,OAAO,IAAI,IAAI;IAIf,8EAA8E;IAC9E,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { CANVAS_DIR } from "../shared/types.js";
|
|
4
|
+
const DEBOUNCE_MS = 150;
|
|
5
|
+
const POLL_INTERVAL_MS = 500;
|
|
6
|
+
/**
|
|
7
|
+
* Cheap change fingerprint for a directory tree: sorted `path:mtime:size`
|
|
8
|
+
* entries. Used by the polling fallback (and directly testable on its own)
|
|
9
|
+
* — not a content hash, just enough to notice something moved.
|
|
10
|
+
*/
|
|
11
|
+
export function snapshotCanvasDir(canvasDir) {
|
|
12
|
+
let entries;
|
|
13
|
+
try {
|
|
14
|
+
entries = fs.readdirSync(canvasDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
const parts = entries.map((entry) => {
|
|
20
|
+
try {
|
|
21
|
+
const stat = fs.statSync(path.join(canvasDir, entry));
|
|
22
|
+
return `${entry}:${stat.mtimeMs}:${stat.size}`;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return `${entry}:gone`;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return parts.sort().join("|");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One session's watcher. Watches the whole project root (not the canvas dir
|
|
32
|
+
* directly) recursively and filters to CANVAS_DIR-prefixed changes — so
|
|
33
|
+
* there's no separate "wait for the dir to appear" phase, and it naturally
|
|
34
|
+
* survives the canvas dir itself being deleted/recreated. Falls back to
|
|
35
|
+
* polling a directory-tree fingerprint when recursive `fs.watch` isn't
|
|
36
|
+
* available (notably Linux) or the watcher errors out at runtime.
|
|
37
|
+
*/
|
|
38
|
+
class SessionCanvasWatcher {
|
|
39
|
+
constructor(cwd, harnessSessionId, onChange) {
|
|
40
|
+
this.cwd = cwd;
|
|
41
|
+
this.harnessSessionId = harnessSessionId;
|
|
42
|
+
this.onChange = onChange;
|
|
43
|
+
this.watcher = null;
|
|
44
|
+
this.pollTimer = null;
|
|
45
|
+
this.debounceTimer = null;
|
|
46
|
+
this.closed = false;
|
|
47
|
+
this.lastSnapshot = "";
|
|
48
|
+
this.canvasDir = path.join(cwd, CANVAS_DIR);
|
|
49
|
+
this.canvasPrefix = CANVAS_DIR + path.sep;
|
|
50
|
+
this.arm();
|
|
51
|
+
}
|
|
52
|
+
scheduleChange() {
|
|
53
|
+
if (this.closed)
|
|
54
|
+
return;
|
|
55
|
+
if (this.debounceTimer)
|
|
56
|
+
clearTimeout(this.debounceTimer);
|
|
57
|
+
this.debounceTimer = setTimeout(() => this.onChange(this.harnessSessionId), DEBOUNCE_MS);
|
|
58
|
+
}
|
|
59
|
+
isCanvasPath(filename) {
|
|
60
|
+
// A `null` filename means the platform couldn't report which path
|
|
61
|
+
// changed — reload to be safe rather than miss an update.
|
|
62
|
+
if (!filename)
|
|
63
|
+
return true;
|
|
64
|
+
return filename === CANVAS_DIR || filename.startsWith(this.canvasPrefix);
|
|
65
|
+
}
|
|
66
|
+
arm() {
|
|
67
|
+
if (this.closed)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
this.watcher = fs.watch(this.cwd, { recursive: true }, (event, filename) => {
|
|
71
|
+
if (!this.isCanvasPath(filename))
|
|
72
|
+
return;
|
|
73
|
+
this.scheduleChange();
|
|
74
|
+
// A rename at (or above) the canvas dir — e.g. an editor's atomic
|
|
75
|
+
// write-then-rename, or the agent `mkdir -p`'ing it for the first
|
|
76
|
+
// time — can leave a recursive watcher no longer covering the new
|
|
77
|
+
// inode on some platforms. Re-arm defensively rather than risk
|
|
78
|
+
// silently going deaf.
|
|
79
|
+
if (event === "rename" && (filename === CANVAS_DIR || filename === path.dirname(CANVAS_DIR))) {
|
|
80
|
+
this.rearm();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
this.watcher.on("error", () => this.fallBackToPolling());
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// `recursive` isn't supported on this platform (notably Linux).
|
|
87
|
+
this.fallBackToPolling();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
rearm() {
|
|
91
|
+
this.watcher?.close();
|
|
92
|
+
this.watcher = null;
|
|
93
|
+
this.arm();
|
|
94
|
+
}
|
|
95
|
+
fallBackToPolling() {
|
|
96
|
+
if (this.closed || this.pollTimer)
|
|
97
|
+
return;
|
|
98
|
+
this.watcher?.close();
|
|
99
|
+
this.watcher = null;
|
|
100
|
+
this.lastSnapshot = snapshotCanvasDir(this.canvasDir);
|
|
101
|
+
this.pollTimer = setInterval(() => {
|
|
102
|
+
const snapshot = snapshotCanvasDir(this.canvasDir);
|
|
103
|
+
if (snapshot !== this.lastSnapshot) {
|
|
104
|
+
this.lastSnapshot = snapshot;
|
|
105
|
+
this.scheduleChange();
|
|
106
|
+
}
|
|
107
|
+
}, POLL_INTERVAL_MS);
|
|
108
|
+
}
|
|
109
|
+
close() {
|
|
110
|
+
this.closed = true;
|
|
111
|
+
if (this.debounceTimer)
|
|
112
|
+
clearTimeout(this.debounceTimer);
|
|
113
|
+
if (this.pollTimer)
|
|
114
|
+
clearInterval(this.pollTimer);
|
|
115
|
+
this.watcher?.close();
|
|
116
|
+
this.watcher = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Registry of one SessionCanvasWatcher per active harness session. */
|
|
120
|
+
export class CanvasWatcherManager {
|
|
121
|
+
constructor(deps) {
|
|
122
|
+
this.deps = deps;
|
|
123
|
+
this.watchers = new Map();
|
|
124
|
+
}
|
|
125
|
+
/** Idempotent: replaces any existing watcher for this session (e.g. a resume
|
|
126
|
+
* into a different cwd). */
|
|
127
|
+
start(harnessSessionId, cwd) {
|
|
128
|
+
this.stop(harnessSessionId);
|
|
129
|
+
this.watchers.set(harnessSessionId, new SessionCanvasWatcher(cwd, harnessSessionId, (id) => this.deps.onChange(id)));
|
|
130
|
+
}
|
|
131
|
+
stop(harnessSessionId) {
|
|
132
|
+
this.watchers.get(harnessSessionId)?.close();
|
|
133
|
+
this.watchers.delete(harnessSessionId);
|
|
134
|
+
}
|
|
135
|
+
stopAll() {
|
|
136
|
+
for (const harnessSessionId of [...this.watchers.keys()])
|
|
137
|
+
this.stop(harnessSessionId);
|
|
138
|
+
}
|
|
139
|
+
/** Test/debug helper — how many sessions currently have an active watcher. */
|
|
140
|
+
get size() {
|
|
141
|
+
return this.watchers.size;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=canvas-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-watcher.js","sourceRoot":"","sources":["../../src/core/canvas-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory queue -> batched POST to the remote collector, at
|
|
3
|
+
* `${SAPIOM_COLLECTOR_URL}/v1/harness/events`.
|
|
4
|
+
*
|
|
5
|
+
* Gated by telemetry opt-in: when off (or when no collector URL is
|
|
6
|
+
* configured at all), `enqueue` is a no-op — events still land in the local
|
|
7
|
+
* ndjson store via a separate path, this is only the remote fan-out.
|
|
8
|
+
*
|
|
9
|
+
* Response contract: any 2xx = accepted, drop the batch from the queue.
|
|
10
|
+
* 4xx = permanent failure, drop without retry (a malformed batch will never
|
|
11
|
+
* succeed by resending it). 5xx or a network error = transient, retry with
|
|
12
|
+
* exponential backoff, then drop.
|
|
13
|
+
*/
|
|
14
|
+
import { type AnalyticsEvent, type CollectorContext } from "../../shared/types.js";
|
|
15
|
+
type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
|
|
16
|
+
export interface CollectorBatcherOptions {
|
|
17
|
+
machineId: string;
|
|
18
|
+
telemetryOptIn: boolean;
|
|
19
|
+
/** Static per-install/boot context sent at the batch level. */
|
|
20
|
+
context: CollectorContext;
|
|
21
|
+
/** Base URL; `/v1/harness/events` is appended. Defaults to `process.env[ENV.collectorUrl]`. */
|
|
22
|
+
collectorUrl?: string;
|
|
23
|
+
/** Sapiom API key; sent as `Authorization: Bearer <key>`. Omitted (anonymous) when null/unset. */
|
|
24
|
+
apiKey?: string | null;
|
|
25
|
+
flushIntervalMs?: number;
|
|
26
|
+
maxBatchSize?: number;
|
|
27
|
+
/** Injectable for tests. Defaults to the global `fetch`. */
|
|
28
|
+
fetchImpl?: FetchLike;
|
|
29
|
+
onDebug?: (message: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare class CollectorBatcher {
|
|
32
|
+
private readonly machineId;
|
|
33
|
+
private readonly context;
|
|
34
|
+
private readonly eventsUrl;
|
|
35
|
+
private readonly maxBatchSize;
|
|
36
|
+
private readonly fetchImpl;
|
|
37
|
+
private readonly onDebug;
|
|
38
|
+
private readonly timer;
|
|
39
|
+
private queue;
|
|
40
|
+
private telemetryOptIn;
|
|
41
|
+
private apiKey;
|
|
42
|
+
private flushing;
|
|
43
|
+
private warnedNoCollectorUrl;
|
|
44
|
+
constructor(options: CollectorBatcherOptions);
|
|
45
|
+
setTelemetryOptIn(optIn: boolean): void;
|
|
46
|
+
/** Update the auth key used on subsequent flushes (e.g. after login/logout). */
|
|
47
|
+
setApiKey(apiKey: string | null): void;
|
|
48
|
+
/** Queue an event for the next batch. No-op unless opted in and configured. */
|
|
49
|
+
enqueue(event: AnalyticsEvent): void;
|
|
50
|
+
/** Send whatever is queued now, with retry/backoff, dropping on final failure. */
|
|
51
|
+
flush(): Promise<void>;
|
|
52
|
+
private sendWithRetry;
|
|
53
|
+
/** Flush everything queued, then stop the periodic timer. */
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=batcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../../../src/core/collector/batcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAGL,KAAK,cAAc,EAEnB,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAS/B,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,OAAO,EAAE,gBAAgB,CAAC;IAC1B,+FAA+F;IAC/F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAMD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,oBAAoB,CAAS;gBAEzB,OAAO,EAAE,uBAAuB;IAsB5C,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKvC,gFAAgF;IAChF,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAItC,+EAA+E;IAC/E,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAsBpC,kFAAkF;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBd,aAAa;IAuC3B,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B"}
|