@sapiom/harness 0.1.5 → 0.2.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/CHANGELOG.md +151 -0
- package/dist/cli/args.d.ts +16 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +80 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.js +30 -66
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/consent.js +1 -1
- package/dist/cli/settings.d.ts.map +1 -1
- package/dist/cli/settings.js +33 -1
- package/dist/cli/settings.js.map +1 -1
- package/dist/core/adapters/adapter.d.ts +2 -2
- package/dist/core/adapters/claude-code.d.ts +60 -2
- package/dist/core/adapters/claude-code.d.ts.map +1 -1
- package/dist/core/adapters/claude-code.js +119 -32
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/adapters/codex.d.ts +21 -2
- package/dist/core/adapters/codex.d.ts.map +1 -1
- package/dist/core/adapters/codex.js +53 -2
- package/dist/core/adapters/codex.js.map +1 -1
- package/dist/core/adapters/detect.d.ts +9 -0
- package/dist/core/adapters/detect.d.ts.map +1 -1
- package/dist/core/adapters/detect.js +9 -0
- package/dist/core/adapters/detect.js.map +1 -1
- package/dist/core/api-key-provider.d.ts +79 -0
- package/dist/core/api-key-provider.d.ts.map +1 -0
- package/dist/core/api-key-provider.js +91 -0
- package/dist/core/api-key-provider.js.map +1 -0
- package/dist/core/canvas-body.d.ts +28 -28
- package/dist/core/canvas-body.d.ts.map +1 -1
- package/dist/core/canvas-body.js +117 -55
- package/dist/core/canvas-body.js.map +1 -1
- package/dist/core/canvas-derive.d.ts +43 -0
- package/dist/core/canvas-derive.d.ts.map +1 -0
- package/dist/core/canvas-derive.js +95 -0
- package/dist/core/canvas-derive.js.map +1 -0
- package/dist/core/canvas-enrichment.d.ts +52 -127
- package/dist/core/canvas-enrichment.d.ts.map +1 -1
- package/dist/core/canvas-enrichment.js +17 -178
- package/dist/core/canvas-enrichment.js.map +1 -1
- package/dist/core/canvas-graph.d.ts +40 -1
- package/dist/core/canvas-graph.d.ts.map +1 -1
- package/dist/core/canvas-graph.js +71 -9
- package/dist/core/canvas-graph.js.map +1 -1
- package/dist/core/canvas-interconnections.d.ts +26 -8
- package/dist/core/canvas-interconnections.d.ts.map +1 -1
- package/dist/core/canvas-interconnections.js +132 -24
- package/dist/core/canvas-interconnections.js.map +1 -1
- package/dist/core/canvas-manifest-check.d.ts.map +1 -1
- package/dist/core/canvas-manifest-check.js +15 -2
- package/dist/core/canvas-manifest-check.js.map +1 -1
- package/dist/core/canvas-render.d.ts +8 -15
- package/dist/core/canvas-render.d.ts.map +1 -1
- package/dist/core/canvas-render.js +29 -41
- package/dist/core/canvas-render.js.map +1 -1
- package/dist/core/canvas-run-state.d.ts +50 -5
- package/dist/core/canvas-run-state.d.ts.map +1 -1
- package/dist/core/canvas-run-state.js +176 -11
- package/dist/core/canvas-run-state.js.map +1 -1
- package/dist/core/canvas-svg.d.ts +9 -4
- package/dist/core/canvas-svg.d.ts.map +1 -1
- package/dist/core/canvas-svg.js +84 -13
- package/dist/core/canvas-svg.js.map +1 -1
- package/dist/core/canvas-template.d.ts +9 -10
- package/dist/core/canvas-template.d.ts.map +1 -1
- package/dist/core/canvas-template.js +57 -20
- package/dist/core/canvas-template.js.map +1 -1
- package/dist/core/canvas-watcher.d.ts +22 -1
- package/dist/core/canvas-watcher.d.ts.map +1 -1
- package/dist/core/canvas-watcher.js +111 -30
- package/dist/core/canvas-watcher.js.map +1 -1
- package/dist/core/collector/normalizer.d.ts +8 -0
- package/dist/core/collector/normalizer.d.ts.map +1 -1
- package/dist/core/collector/normalizer.js +8 -0
- package/dist/core/collector/normalizer.js.map +1 -1
- package/dist/core/collector/store.d.ts +75 -1
- package/dist/core/collector/store.d.ts.map +1 -1
- package/dist/core/collector/store.js +334 -2
- package/dist/core/collector/store.js.map +1 -1
- package/dist/core/definition-slug-resolver.d.ts +13 -0
- package/dist/core/definition-slug-resolver.d.ts.map +1 -1
- package/dist/core/definition-slug-resolver.js +29 -0
- package/dist/core/definition-slug-resolver.js.map +1 -1
- package/dist/core/errors.d.ts +10 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +11 -5
- package/dist/core/errors.js.map +1 -1
- package/dist/core/example-seed.d.ts +8 -0
- package/dist/core/example-seed.d.ts.map +1 -1
- package/dist/core/example-seed.js +47 -10
- package/dist/core/example-seed.js.map +1 -1
- package/dist/core/inject/system-prompt.d.ts +8 -0
- package/dist/core/inject/system-prompt.d.ts.map +1 -1
- package/dist/core/inject/system-prompt.js +3 -1
- package/dist/core/inject/system-prompt.js.map +1 -1
- package/dist/core/macros.d.ts.map +1 -1
- package/dist/core/macros.js +5 -6
- package/dist/core/macros.js.map +1 -1
- package/dist/core/paths.d.ts +1 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +1 -0
- package/dist/core/paths.js.map +1 -1
- package/dist/core/record-archive.d.ts +187 -0
- package/dist/core/record-archive.d.ts.map +1 -0
- package/dist/core/record-archive.js +465 -0
- package/dist/core/record-archive.js.map +1 -0
- package/dist/core/rehydration.d.ts +72 -0
- package/dist/core/rehydration.d.ts.map +1 -0
- package/dist/core/rehydration.js +72 -0
- package/dist/core/rehydration.js.map +1 -0
- package/dist/core/render-local-run.d.ts +82 -0
- package/dist/core/render-local-run.d.ts.map +1 -0
- package/dist/core/render-local-run.js +105 -0
- package/dist/core/render-local-run.js.map +1 -0
- package/dist/core/render-log-slice.d.ts +30 -0
- package/dist/core/render-log-slice.d.ts.map +1 -0
- package/dist/core/render-log-slice.js +49 -0
- package/dist/core/render-log-slice.js.map +1 -0
- package/dist/core/render-run-state.d.ts.map +1 -1
- package/dist/core/render-run-state.js +30 -49
- package/dist/core/render-run-state.js.map +1 -1
- package/dist/core/resume-brief.d.ts +130 -0
- package/dist/core/resume-brief.d.ts.map +1 -0
- package/dist/core/resume-brief.js +404 -0
- package/dist/core/resume-brief.js.map +1 -0
- package/dist/core/rolling-summary.d.ts +130 -0
- package/dist/core/rolling-summary.d.ts.map +1 -0
- package/dist/core/rolling-summary.js +227 -0
- package/dist/core/rolling-summary.js.map +1 -0
- package/dist/core/run-local-bootstrap.d.ts +93 -0
- package/dist/core/run-local-bootstrap.d.ts.map +1 -0
- package/dist/core/run-local-bootstrap.js +155 -0
- package/dist/core/run-local-bootstrap.js.map +1 -0
- package/dist/core/run-state.d.ts +16 -1
- package/dist/core/run-state.d.ts.map +1 -1
- package/dist/core/run-state.js +75 -25
- package/dist/core/run-state.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -3
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +56 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-record.d.ts +165 -0
- package/dist/core/session-record.d.ts.map +1 -0
- package/dist/core/session-record.js +470 -0
- package/dist/core/session-record.js.map +1 -0
- package/dist/core/spawn-target.d.ts +12 -0
- package/dist/core/spawn-target.d.ts.map +1 -0
- package/dist/core/spawn-target.js +187 -0
- package/dist/core/spawn-target.js.map +1 -0
- package/dist/core/stub-feedback.d.ts +52 -0
- package/dist/core/stub-feedback.d.ts.map +1 -0
- package/dist/core/stub-feedback.js +33 -0
- package/dist/core/stub-feedback.js.map +1 -0
- package/dist/core/task-manager.d.ts.map +1 -1
- package/dist/core/task-manager.js +9 -1
- package/dist/core/task-manager.js.map +1 -1
- package/dist/core/template-catalog.d.ts +48 -0
- package/dist/core/template-catalog.d.ts.map +1 -0
- package/dist/core/template-catalog.js +338 -0
- package/dist/core/template-catalog.js.map +1 -0
- package/dist/core/workflow-registry.d.ts.map +1 -1
- package/dist/core/workflow-registry.js +2 -2
- package/dist/core/workflow-registry.js.map +1 -1
- package/dist/core/workspace-watcher.d.ts.map +1 -1
- package/dist/core/workspace-watcher.js +4 -3
- package/dist/core/workspace-watcher.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/profiles/default.d.ts.map +1 -1
- package/dist/profiles/default.js +9 -14
- package/dist/profiles/default.js.map +1 -1
- package/dist/server/actions.d.ts +139 -0
- package/dist/server/actions.d.ts.map +1 -0
- package/dist/server/actions.js +430 -0
- package/dist/server/actions.js.map +1 -0
- package/dist/server/auth-routes.d.ts +92 -0
- package/dist/server/auth-routes.d.ts.map +1 -0
- package/dist/server/auth-routes.js +151 -0
- package/dist/server/auth-routes.js.map +1 -0
- package/dist/server/canvas.d.ts.map +1 -1
- package/dist/server/canvas.js +7 -7
- package/dist/server/canvas.js.map +1 -1
- package/dist/server/fs.d.ts.map +1 -1
- package/dist/server/fs.js +17 -1
- package/dist/server/fs.js.map +1 -1
- package/dist/server/index.d.ts +12 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/ingest.d.ts +11 -0
- package/dist/server/ingest.d.ts.map +1 -1
- package/dist/server/ingest.js +1 -0
- package/dist/server/ingest.js.map +1 -1
- package/dist/server/macros.d.ts +4 -6
- package/dist/server/macros.d.ts.map +1 -1
- package/dist/server/macros.js.map +1 -1
- package/dist/server/rest.d.ts +18 -6
- package/dist/server/rest.d.ts.map +1 -1
- package/dist/server/rest.js +185 -50
- package/dist/server/rest.js.map +1 -1
- package/dist/server/runs.d.ts +16 -15
- package/dist/server/runs.d.ts.map +1 -1
- package/dist/server/runs.js +15 -80
- package/dist/server/runs.js.map +1 -1
- package/dist/server/static.d.ts +15 -1
- package/dist/server/static.d.ts.map +1 -1
- package/dist/server/static.js +54 -4
- package/dist/server/static.js.map +1 -1
- package/dist/server/templates.d.ts +24 -0
- package/dist/server/templates.d.ts.map +1 -0
- package/dist/server/templates.js +54 -0
- package/dist/server/templates.js.map +1 -0
- package/dist/shared/types.d.ts +558 -66
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +19 -11
- package/dist/shared/types.js.map +1 -1
- package/dist/web/assets/index-DC65cskq.js +446 -0
- package/dist/web/assets/index-xtD-3FFa.css +32 -0
- package/dist/web/canvas/sess-boot/index.html +305 -0
- package/dist/web/index.html +4 -9
- package/package.json +8 -4
- package/dist/core/canvas-enrich.d.ts +0 -115
- package/dist/core/canvas-enrich.d.ts.map +0 -1
- package/dist/core/canvas-enrich.js +0 -186
- package/dist/core/canvas-enrich.js.map +0 -1
- package/dist/core/run-spend.d.ts +0 -55
- package/dist/core/run-spend.d.ts.map +0 -1
- package/dist/core/run-spend.js +0 -109
- package/dist/core/run-spend.js.map +0 -1
- package/dist/core/run-transactions.d.ts +0 -60
- package/dist/core/run-transactions.d.ts.map +0 -1
- package/dist/core/run-transactions.js +0 -129
- package/dist/core/run-transactions.js.map +0 -1
- package/dist/profiles/canvas-guidelines.d.ts +0 -20
- package/dist/profiles/canvas-guidelines.d.ts.map +0 -1
- package/dist/profiles/canvas-guidelines.js +0 -48
- package/dist/profiles/canvas-guidelines.js.map +0 -1
- package/dist/server/skills.d.ts +0 -51
- package/dist/server/skills.d.ts.map +0 -1
- package/dist/server/skills.js +0 -312
- package/dist/server/skills.js.map +0 -1
- package/dist/web/assets/index-DM84J83Y.js +0 -234
- package/dist/web/assets/index-DemtWW4L.css +0 -32
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-key-provider — the single source of truth for the Sapiom API key that
|
|
3
|
+
* Studio actions authenticate with server-side.
|
|
4
|
+
*
|
|
5
|
+
* WHY this exists: the key is resolved ONCE at CLI boot (from the cached
|
|
6
|
+
* credential or a fresh browser login) and then handed to the server as a plain
|
|
7
|
+
* string. Two problems fell out of that snapshot:
|
|
8
|
+
*
|
|
9
|
+
* 1. It never refreshes. If the key is rotated/revoked — or the user runs the
|
|
10
|
+
* shared login again in another process, rewriting the credential store —
|
|
11
|
+
* the running Studio server keeps sending the stale key and every upstream
|
|
12
|
+
* call 401s until the whole server is restarted. There was no re-auth path.
|
|
13
|
+
* 2. It conflated two different secrets. The per-boot *boot token*
|
|
14
|
+
* (`X-Harness-Token`) only gates the LOCAL `/api` surface; the *API key*
|
|
15
|
+
* (`sk_…`) is what authenticates upstream Sapiom calls. Studio actions must
|
|
16
|
+
* authenticate with the held API key, never the boot token.
|
|
17
|
+
*
|
|
18
|
+
* This provider holds the current key behind a getter (so consumers read the
|
|
19
|
+
* live value, not a boot-time copy) and can `refresh()` it by re-reading the
|
|
20
|
+
* shared credential store the CLI/MCP login writes to
|
|
21
|
+
* (`~/.sapiom/credentials.json`, via `@sapiom/mcp/auth`). A 401 from an
|
|
22
|
+
* upstream call can therefore recover in place — refresh, then retry — instead
|
|
23
|
+
* of locking the Studio.
|
|
24
|
+
*
|
|
25
|
+
* NOT in scope (deliberately, per the ticket): triggering an interactive
|
|
26
|
+
* browser re-login from the server, or a broader session-auth redesign. Refresh
|
|
27
|
+
* here is a silent re-read of already-cached credentials; if the store has no
|
|
28
|
+
* newer key, the call surfaces the auth error honestly.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Reads the currently-held API key and can refresh it from the shared
|
|
32
|
+
* credential store. Consumers should call {@link getKey} per request (never
|
|
33
|
+
* cache the returned string across requests) and {@link refresh} exactly once
|
|
34
|
+
* when an upstream call returns 401/403, retrying only if refresh yields a
|
|
35
|
+
* different, non-null key.
|
|
36
|
+
*/
|
|
37
|
+
export interface ApiKeyProvider {
|
|
38
|
+
/** The current API key, or null when the harness is not signed in. */
|
|
39
|
+
getKey(): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Re-read the shared credential store and adopt any newer key found there.
|
|
42
|
+
* Returns the (possibly updated) current key, or null if none is available.
|
|
43
|
+
* Never throws — a read failure leaves the current key untouched and is
|
|
44
|
+
* reported by returning the existing value.
|
|
45
|
+
*/
|
|
46
|
+
refresh(): Promise<string | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Unconditionally zero the in-memory key (sets it to null). Called on
|
|
49
|
+
* disconnect so that {@link getKey} returns null immediately — distinct from
|
|
50
|
+
* {@link refresh}, whose `if (latest)` guard deliberately preserves the
|
|
51
|
+
* cached key when the credential store has nothing to offer.
|
|
52
|
+
*/
|
|
53
|
+
clear(): void;
|
|
54
|
+
}
|
|
55
|
+
/** Overridable reads for the credential store — a test seam. Defaults hit the
|
|
56
|
+
* real `@sapiom/mcp/auth` store the CLI login writes to. */
|
|
57
|
+
export interface ApiKeyProviderDeps {
|
|
58
|
+
/** Resolve the active environment name (governs which cached entry to read). */
|
|
59
|
+
resolveEnvironmentName?: () => Promise<string>;
|
|
60
|
+
/** Read the cached API key for a given environment, or null if absent. */
|
|
61
|
+
readApiKeyForEnv?: (envName: string) => Promise<string | null>;
|
|
62
|
+
/** Overrides SAPIOM_ENVIRONMENT for environment resolution. */
|
|
63
|
+
environment?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Build an {@link ApiKeyProvider} seeded with the boot-time key. `refresh()`
|
|
67
|
+
* re-reads the shared credential store for the active environment and adopts a
|
|
68
|
+
* newer key when one is present — the reconciliation point between the key the
|
|
69
|
+
* CLI captured at boot and any key rotated/re-logged-in afterward.
|
|
70
|
+
*/
|
|
71
|
+
export declare function createApiKeyProvider(initialKey: string | null, deps?: ApiKeyProviderDeps): ApiKeyProvider;
|
|
72
|
+
/**
|
|
73
|
+
* Adapt a plain `string | null` API key into an {@link ApiKeyProvider} whose
|
|
74
|
+
* `refresh()` is a no-op and `clear()` is a no-op. Lets call sites that only
|
|
75
|
+
* ever have a static key (tests, callers with no credential store) share the
|
|
76
|
+
* one provider-shaped contract without special-casing.
|
|
77
|
+
*/
|
|
78
|
+
export declare function staticApiKeyProvider(key: string | null): ApiKeyProvider;
|
|
79
|
+
//# sourceMappingURL=api-key-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-provider.d.ts","sourceRoot":"","sources":["../../src/core/api-key-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,MAAM,IAAI,MAAM,GAAG,IAAI,CAAC;IACxB;;;;;OAKG;IACH,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClC;;;;;OAKG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED;6DAC6D;AAC7D,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/D,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAkBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,IAAI,GAAE,kBAAuB,GAC5B,cAAc,CA8BhB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAQvE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-key-provider — the single source of truth for the Sapiom API key that
|
|
3
|
+
* Studio actions authenticate with server-side.
|
|
4
|
+
*
|
|
5
|
+
* WHY this exists: the key is resolved ONCE at CLI boot (from the cached
|
|
6
|
+
* credential or a fresh browser login) and then handed to the server as a plain
|
|
7
|
+
* string. Two problems fell out of that snapshot:
|
|
8
|
+
*
|
|
9
|
+
* 1. It never refreshes. If the key is rotated/revoked — or the user runs the
|
|
10
|
+
* shared login again in another process, rewriting the credential store —
|
|
11
|
+
* the running Studio server keeps sending the stale key and every upstream
|
|
12
|
+
* call 401s until the whole server is restarted. There was no re-auth path.
|
|
13
|
+
* 2. It conflated two different secrets. The per-boot *boot token*
|
|
14
|
+
* (`X-Harness-Token`) only gates the LOCAL `/api` surface; the *API key*
|
|
15
|
+
* (`sk_…`) is what authenticates upstream Sapiom calls. Studio actions must
|
|
16
|
+
* authenticate with the held API key, never the boot token.
|
|
17
|
+
*
|
|
18
|
+
* This provider holds the current key behind a getter (so consumers read the
|
|
19
|
+
* live value, not a boot-time copy) and can `refresh()` it by re-reading the
|
|
20
|
+
* shared credential store the CLI/MCP login writes to
|
|
21
|
+
* (`~/.sapiom/credentials.json`, via `@sapiom/mcp/auth`). A 401 from an
|
|
22
|
+
* upstream call can therefore recover in place — refresh, then retry — instead
|
|
23
|
+
* of locking the Studio.
|
|
24
|
+
*
|
|
25
|
+
* NOT in scope (deliberately, per the ticket): triggering an interactive
|
|
26
|
+
* browser re-login from the server, or a broader session-auth redesign. Refresh
|
|
27
|
+
* here is a silent re-read of already-cached credentials; if the store has no
|
|
28
|
+
* newer key, the call surfaces the auth error honestly.
|
|
29
|
+
*/
|
|
30
|
+
import { readCredentials, resolveEnvironment } from "@sapiom/mcp/auth";
|
|
31
|
+
async function defaultResolveEnvironmentName(environment) {
|
|
32
|
+
const env = await resolveEnvironment(environment ?? process.env.SAPIOM_ENVIRONMENT);
|
|
33
|
+
return env.name;
|
|
34
|
+
}
|
|
35
|
+
async function defaultReadApiKeyForEnv(envName) {
|
|
36
|
+
const entry = await readCredentials(envName);
|
|
37
|
+
return entry?.apiKey ?? null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build an {@link ApiKeyProvider} seeded with the boot-time key. `refresh()`
|
|
41
|
+
* re-reads the shared credential store for the active environment and adopts a
|
|
42
|
+
* newer key when one is present — the reconciliation point between the key the
|
|
43
|
+
* CLI captured at boot and any key rotated/re-logged-in afterward.
|
|
44
|
+
*/
|
|
45
|
+
export function createApiKeyProvider(initialKey, deps = {}) {
|
|
46
|
+
let current = initialKey;
|
|
47
|
+
const resolveEnvName = deps.resolveEnvironmentName ??
|
|
48
|
+
(() => defaultResolveEnvironmentName(deps.environment));
|
|
49
|
+
const readApiKey = deps.readApiKeyForEnv ?? defaultReadApiKeyForEnv;
|
|
50
|
+
return {
|
|
51
|
+
getKey() {
|
|
52
|
+
return current;
|
|
53
|
+
},
|
|
54
|
+
async refresh() {
|
|
55
|
+
try {
|
|
56
|
+
const envName = await resolveEnvName();
|
|
57
|
+
const latest = await readApiKey(envName);
|
|
58
|
+
// Only adopt a real, non-empty key. A missing/emptied credential must
|
|
59
|
+
// not clobber a key that's simply been rotated out from under us but is
|
|
60
|
+
// still the best value we have to report an honest auth error with.
|
|
61
|
+
if (latest)
|
|
62
|
+
current = latest;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Store unreadable (HOME missing, malformed file, …) — keep the current
|
|
66
|
+
// key. The caller's retry will simply hit the same auth error, which is
|
|
67
|
+
// the correct, honest outcome.
|
|
68
|
+
}
|
|
69
|
+
return current;
|
|
70
|
+
},
|
|
71
|
+
clear() {
|
|
72
|
+
current = null;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Adapt a plain `string | null` API key into an {@link ApiKeyProvider} whose
|
|
78
|
+
* `refresh()` is a no-op and `clear()` is a no-op. Lets call sites that only
|
|
79
|
+
* ever have a static key (tests, callers with no credential store) share the
|
|
80
|
+
* one provider-shaped contract without special-casing.
|
|
81
|
+
*/
|
|
82
|
+
export function staticApiKeyProvider(key) {
|
|
83
|
+
return {
|
|
84
|
+
getKey: () => key,
|
|
85
|
+
refresh: () => Promise.resolve(key),
|
|
86
|
+
clear: () => {
|
|
87
|
+
/* static key — clear is a no-op */
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=api-key-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-provider.js","sourceRoot":"","sources":["../../src/core/api-key-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAuCvE,KAAK,UAAU,6BAA6B,CAC1C,WAAoB;IAEpB,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAClC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAC9C,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,OAAe;IAEf,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;IAC7C,OAAO,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAyB,EACzB,OAA2B,EAAE;IAE7B,IAAI,OAAO,GAAG,UAAU,CAAC;IACzB,MAAM,cAAc,GAClB,IAAI,CAAC,sBAAsB;QAC3B,CAAC,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;IAEpE,OAAO;QACL,MAAM;YACJ,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,KAAK,CAAC,OAAO;YACX,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,cAAc,EAAE,CAAC;gBACvC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;gBACzC,sEAAsE;gBACtE,wEAAwE;gBACxE,oEAAoE;gBACpE,IAAI,MAAM;oBAAE,OAAO,GAAG,MAAM,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,wEAAwE;gBACxE,wEAAwE;gBACxE,+BAA+B;YACjC,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,KAAK;YACH,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAkB;IACrD,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG;QACjB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;QACnC,KAAK,EAAE,GAAG,EAAE;YACV,mCAAmC;QACrC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Assembles the `<div id="canvas-root">` body — the bound workflow's
|
|
3
|
-
* `<section class="canvas-panel">` (header +
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* `<section class="canvas-panel">` (title/summary header + SVG diagram),
|
|
4
|
+
* entirely from the classes `core/canvas-template.ts`'s shell already defines.
|
|
5
|
+
* This is the HTML half of the deterministic render; `core/canvas-render.ts`
|
|
6
|
+
* wraps the result through `renderCanvasDocument()` and writes it to the
|
|
7
|
+
* workflow's render file.
|
|
8
|
+
*
|
|
9
|
+
* The panel ends with the color/shape legend (only the node kinds actually
|
|
10
|
+
* used, plus a cross-workflow row when relevant) so the diagram reads on its
|
|
11
|
+
* own. It sits at the bottom of `#canvas-root` INSIDE the iframe — safe from
|
|
12
|
+
* the SPA's floating zoom/fit controls because the pane reserves a strip below
|
|
13
|
+
* the iframe for them (see `.canvas-visual { padding-bottom }` in
|
|
14
|
+
* web/src/styles.css), so the two never sit "l'un sur l'autre". The only thing
|
|
15
|
+
* deliberately dropped is the old prose footer note ("Static preview —
|
|
16
|
+
* regenerate…"), which was pure noise.
|
|
8
17
|
*/
|
|
9
18
|
import type { CanvasEdgeKind, CanvasGraph, CanvasNodeKind } from "./canvas-graph.js";
|
|
10
19
|
import type { CanvasEnrichment } from "./canvas-enrichment.js";
|
|
@@ -12,34 +21,25 @@ export interface WorkflowPanelMeta {
|
|
|
12
21
|
title: string;
|
|
13
22
|
badges?: string[];
|
|
14
23
|
}
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
export declare function buildWorkflowPanelHtml(graph: CanvasGraph, meta: WorkflowPanelMeta,
|
|
24
|
+
/**
|
|
25
|
+
* The color/shape key for one diagram: only the node kinds that actually appear
|
|
26
|
+
* (in stable order), plus a cross-workflow row when the graph has cross edges.
|
|
27
|
+
* Returns "" when nothing is worth keying, so the panel omits the footer.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildLegendHtml(nodeKinds: Set<CanvasNodeKind>, edgeKinds: Set<CanvasEdgeKind>): string;
|
|
30
|
+
/** One workflow's full panel: title/badges + summary header and the SVG
|
|
31
|
+
* diagram, with the deterministic enrichment merged in (summary line and
|
|
32
|
+
* node/edge annotations), plus the embedded step-graph payload the Steps tab
|
|
33
|
+
* reads. */
|
|
34
|
+
export declare function buildWorkflowPanelHtml(graph: CanvasGraph, meta: WorkflowPanelMeta, enrichment?: CanvasEnrichment | null): string;
|
|
26
35
|
/** A degraded panel for a workflow whose graph couldn't be extracted — never
|
|
27
36
|
* a crash, never a silent fallback to the LLM path, just an honest reason
|
|
28
37
|
* styled through the same shell. */
|
|
29
38
|
export declare function buildErrorPanelHtml(title: string, reason: string): string;
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
/** Joins panels + a footer (enrichment notes/cross-workflow tie, legend,
|
|
34
|
-
* note) into the final `#canvas-root` body — the string
|
|
35
|
-
* `renderCanvasDocument()` wraps. */
|
|
39
|
+
/** Joins the workflow panel(s) into the final `#canvas-root` body — the string
|
|
40
|
+
* `renderCanvasDocument()` wraps. Each panel already carries its own legend
|
|
41
|
+
* footer (see `buildWorkflowPanelHtml`); there is no document-level footer. */
|
|
36
42
|
export declare function assembleCanvasBody(input: {
|
|
37
43
|
panels: string[];
|
|
38
|
-
legend: string;
|
|
39
|
-
note: string;
|
|
40
|
-
/** Enrichment footer facts (already schema-bounded), rendered as a list. */
|
|
41
|
-
notes?: string[];
|
|
42
|
-
/** Enrichment's "how this ties into the other workflows" line. */
|
|
43
|
-
crossWorkflow?: string;
|
|
44
44
|
}): string;
|
|
45
45
|
//# sourceMappingURL=canvas-body.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-body.d.ts","sourceRoot":"","sources":["../../src/core/canvas-body.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"canvas-body.d.ts","sourceRoot":"","sources":["../../src/core/canvas-body.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO/D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAoBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,GAAG,MAAM,CAYtG;AAwED;;;aAGa;AACb,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,iBAAiB,EACvB,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI,GACnC,MAAM,CA2BR;AAED;;qCAEqC;AACrC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAYzE;AAED;;gFAEgF;AAChF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,MAAM,CAEtE"}
|
package/dist/core/canvas-body.js
CHANGED
|
@@ -1,36 +1,134 @@
|
|
|
1
|
-
import { renderGraphSvg } from "./canvas-svg.js";
|
|
1
|
+
import { renderGraphSvg, usedKinds } from "./canvas-svg.js";
|
|
2
2
|
function esc(s) {
|
|
3
3
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const NODE_KIND_LABEL = {
|
|
6
|
+
entry: "entry / active step",
|
|
7
|
+
step: "step",
|
|
8
|
+
pause: "pause / waits for input",
|
|
9
|
+
"terminal-success": "terminal · success",
|
|
10
|
+
"terminal-warn": "terminal · escalation",
|
|
11
|
+
"launched-workflow": "launches another workflow",
|
|
12
|
+
};
|
|
13
|
+
const NODE_KIND_ORDER = [
|
|
14
|
+
"entry",
|
|
15
|
+
"step",
|
|
16
|
+
"pause",
|
|
17
|
+
"terminal-success",
|
|
18
|
+
"terminal-warn",
|
|
19
|
+
"launched-workflow",
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* The color/shape key for one diagram: only the node kinds that actually appear
|
|
23
|
+
* (in stable order), plus a cross-workflow row when the graph has cross edges.
|
|
24
|
+
* Returns "" when nothing is worth keying, so the panel omits the footer.
|
|
25
|
+
*/
|
|
26
|
+
export function buildLegendHtml(nodeKinds, edgeKinds) {
|
|
27
|
+
const items = NODE_KIND_ORDER.filter((k) => nodeKinds.has(k)).map((k) => `<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--${k}"></span>${NODE_KIND_LABEL[k]}</span>`);
|
|
28
|
+
if (edgeKinds.has("cross")) {
|
|
29
|
+
items.push(`<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--cross"></span>cross-workflow signal/handoff</span>`);
|
|
30
|
+
}
|
|
31
|
+
if (items.length === 0)
|
|
32
|
+
return "";
|
|
33
|
+
return `<footer class="canvas-legend">${items.join("")}</footer>`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Serializes the graph (enrichment merged) into the JSON payload the app's
|
|
37
|
+
* Steps tab / inspector consumes, embedded as a `<script id="sapiom-graph">`
|
|
38
|
+
* data block that `bootCanvasGraph` (canvas-run-state.ts) posts to the parent.
|
|
39
|
+
*
|
|
40
|
+
* The projection mirrors `renderGraphSvg`'s merge EXACTLY so the Steps tab and
|
|
41
|
+
* the drawn board never disagree: `role` = the node's sublabel (enrichment
|
|
42
|
+
* override first), `description` = the enrichment hover text, and each edge's
|
|
43
|
+
* `label` is keyed `from->to`. Web-only fields the server graph doesn't carry
|
|
44
|
+
* (timeout / input schema / capabilities) are omitted — `parseCanvasGraph`
|
|
45
|
+
* defaults them, so the drill-down degrades to names + roles + branch facts.
|
|
46
|
+
*
|
|
47
|
+
* `<` is escaped to `<` so a label containing `</script>` can't break out
|
|
48
|
+
* of the JSON block; the result is still valid JSON.
|
|
49
|
+
*/
|
|
50
|
+
function buildGraphPayload(graph, enrichment) {
|
|
51
|
+
const payload = {
|
|
52
|
+
name: graph.manifestName,
|
|
53
|
+
entry: graph.entry,
|
|
54
|
+
nodes: graph.nodes.map((n) => {
|
|
55
|
+
const details = enrichment?.nodeDetails?.[n.id];
|
|
56
|
+
return {
|
|
57
|
+
id: n.id,
|
|
58
|
+
kind: n.kind,
|
|
59
|
+
label: n.label,
|
|
60
|
+
role: details?.sublabel ?? n.sublabel ?? "",
|
|
61
|
+
// Manifest-authored description (Option A) wins; enrichment is the
|
|
62
|
+
// legacy fallback (currently always undefined).
|
|
63
|
+
description: details?.description ?? n.description ?? "",
|
|
64
|
+
inputSchema: n.inputSchema ?? null,
|
|
65
|
+
capabilities: n.capabilities ?? [],
|
|
66
|
+
timeoutMs: n.timeoutMs ?? null,
|
|
67
|
+
};
|
|
68
|
+
}),
|
|
69
|
+
edges: graph.edges.map((e) => ({
|
|
70
|
+
from: e.from,
|
|
71
|
+
to: e.to,
|
|
72
|
+
kind: e.kind,
|
|
73
|
+
label: enrichment?.edgeLabels?.[`${e.from}->${e.to}`] ?? e.label ?? "",
|
|
74
|
+
})),
|
|
75
|
+
warnings: graph.warnings,
|
|
76
|
+
};
|
|
77
|
+
return JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
78
|
+
}
|
|
79
|
+
/** The workflow-level overview the Canvas tab's bottom panel shows: a
|
|
80
|
+
* deterministic stats line ("N steps · M exits · <entry> entry"), the shape
|
|
81
|
+
* summary as the description, and the graph's validation notes. Embedded as a
|
|
82
|
+
* JSON data block (read by bootCanvasOverview), the same pattern as the step
|
|
83
|
+
* graph — everything here is derived from the graph, no LLM. */
|
|
84
|
+
function buildOverviewPayload(graph, enrichment) {
|
|
85
|
+
const exits = graph.nodes.filter((n) => n.kind === "terminal-success" || n.kind === "terminal-warn").length;
|
|
86
|
+
const steps = graph.nodes.length - exits;
|
|
87
|
+
const entryLabel = graph.nodes.find((n) => n.id === graph.entry)?.label ?? graph.entry;
|
|
88
|
+
const parts = [`${steps} ${steps === 1 ? "step" : "steps"}`];
|
|
89
|
+
if (exits > 0)
|
|
90
|
+
parts.push(`${exits} ${exits === 1 ? "exit" : "exits"}`);
|
|
91
|
+
if (entryLabel)
|
|
92
|
+
parts.push(`${entryLabel} entry`);
|
|
93
|
+
const overview = {
|
|
94
|
+
// Human-authored workflow description from the manifest (Option A); "" when
|
|
95
|
+
// the workflow declares none (the shape summary still renders on the board).
|
|
96
|
+
description: graph.description ?? "",
|
|
97
|
+
stats: parts.join(" · "),
|
|
98
|
+
notes: enrichment?.notes ?? [],
|
|
99
|
+
};
|
|
100
|
+
// JSON in a <script> block: neutralize any "</script>"/"<" in derived text.
|
|
101
|
+
return JSON.stringify(overview).replace(/</g, "\\u003c");
|
|
102
|
+
}
|
|
103
|
+
/** One workflow's full panel: title/badges + summary header and the SVG
|
|
104
|
+
* diagram, with the deterministic enrichment merged in (summary line and
|
|
105
|
+
* node/edge annotations), plus the embedded step-graph payload the Steps tab
|
|
106
|
+
* reads. */
|
|
107
|
+
export function buildWorkflowPanelHtml(graph, meta, enrichment) {
|
|
9
108
|
const badges = (meta.badges ?? [])
|
|
10
109
|
.map((b) => `<span class="canvas-badge">${esc(b)}</span>`)
|
|
11
110
|
.join("");
|
|
12
111
|
const warningBadge = graph.warnings.length > 0
|
|
13
112
|
? `<span class="canvas-badge">${graph.warnings.length} warning${graph.warnings.length === 1 ? "" : "s"}</span>`
|
|
14
113
|
: "";
|
|
15
|
-
const enrichment = panelEnrichment?.enrichment ?? null;
|
|
16
|
-
const staleBadge = panelEnrichment?.stale
|
|
17
|
-
? `<span class="canvas-badge canvas-badge--stale">stale — Refresh</span>`
|
|
18
|
-
: "";
|
|
19
114
|
const summary = enrichment?.summary ? `\n <p class="canvas-subtitle">${esc(enrichment.summary)}</p>` : "";
|
|
115
|
+
const used = usedKinds(graph);
|
|
116
|
+
const legend = buildLegendHtml(used.nodeKinds, used.edgeKinds);
|
|
117
|
+
const legendHtml = legend ? `\n ${legend}` : "";
|
|
118
|
+
const graphData = buildGraphPayload(graph, enrichment);
|
|
119
|
+
const overviewData = buildOverviewPayload(graph, enrichment);
|
|
20
120
|
return `<section class="canvas-panel">
|
|
21
121
|
<header class="canvas-header">
|
|
22
122
|
<div class="canvas-title-row">
|
|
23
123
|
<h1 class="canvas-title">${esc(meta.title)}</h1>
|
|
24
|
-
${badges}${warningBadge}
|
|
124
|
+
${badges}${warningBadge}
|
|
25
125
|
</div>${summary}
|
|
26
|
-
<div class="canvas-stats">
|
|
27
|
-
<div class="canvas-stat"><span class="canvas-stat-value">${graph.nodes.length}</span><span class="canvas-stat-label">steps</span></div>
|
|
28
|
-
<div class="canvas-stat"><span class="canvas-stat-value">${esc(graph.entry)}</span><span class="canvas-stat-label">entry</span></div>
|
|
29
|
-
</div>
|
|
30
126
|
</header>
|
|
31
127
|
<div class="canvas-diagram-panel">
|
|
32
128
|
${renderGraphSvg(graph, enrichment)}
|
|
33
|
-
</div
|
|
129
|
+
</div>${legendHtml}
|
|
130
|
+
<script type="application/json" id="sapiom-graph">${graphData}</script>
|
|
131
|
+
<script type="application/json" id="sapiom-overview">${overviewData}</script>
|
|
34
132
|
</section>`;
|
|
35
133
|
}
|
|
36
134
|
/** A degraded panel for a workflow whose graph couldn't be extracted — never
|
|
@@ -49,46 +147,10 @@ export function buildErrorPanelHtml(title, reason) {
|
|
|
49
147
|
</div>
|
|
50
148
|
</section>`;
|
|
51
149
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
pause: "pause / waits for input",
|
|
56
|
-
"terminal-success": "terminal · success",
|
|
57
|
-
"terminal-warn": "terminal · escalation",
|
|
58
|
-
"launched-workflow": "launches another workflow",
|
|
59
|
-
};
|
|
60
|
-
const NODE_KIND_ORDER = [
|
|
61
|
-
"entry",
|
|
62
|
-
"step",
|
|
63
|
-
"pause",
|
|
64
|
-
"terminal-success",
|
|
65
|
-
"terminal-warn",
|
|
66
|
-
"launched-workflow",
|
|
67
|
-
];
|
|
68
|
-
/** Legend footer covering every node/edge kind actually used across every
|
|
69
|
-
* rendered panel (aggregate, not per-panel — matches the template's shape). */
|
|
70
|
-
export function buildLegendHtml(nodeKinds, edgeKinds) {
|
|
71
|
-
const items = NODE_KIND_ORDER.filter((k) => nodeKinds.has(k)).map((k) => `<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--${k}"></span>${NODE_KIND_LABEL[k]}</span>`);
|
|
72
|
-
if (edgeKinds.has("cross")) {
|
|
73
|
-
items.push(`<span class="canvas-legend-item"><span class="canvas-legend-marker canvas-legend-marker--cross"></span>cross-workflow signal/handoff</span>`);
|
|
74
|
-
}
|
|
75
|
-
return `<div class="canvas-legend">${items.join("")}</div>`;
|
|
76
|
-
}
|
|
77
|
-
/** Joins panels + a footer (enrichment notes/cross-workflow tie, legend,
|
|
78
|
-
* note) into the final `#canvas-root` body — the string
|
|
79
|
-
* `renderCanvasDocument()` wraps. */
|
|
150
|
+
/** Joins the workflow panel(s) into the final `#canvas-root` body — the string
|
|
151
|
+
* `renderCanvasDocument()` wraps. Each panel already carries its own legend
|
|
152
|
+
* footer (see `buildWorkflowPanelHtml`); there is no document-level footer. */
|
|
80
153
|
export function assembleCanvasBody(input) {
|
|
81
|
-
|
|
82
|
-
const notesHtml = input.notes && input.notes.length > 0
|
|
83
|
-
? `\n <ul class="canvas-notes">${input.notes.map((n) => `<li>${esc(n)}</li>`).join("")}</ul>`
|
|
84
|
-
: "";
|
|
85
|
-
const crossHtml = input.crossWorkflow
|
|
86
|
-
? `\n <p class="canvas-cross-workflow">${esc(input.crossWorkflow)}</p>`
|
|
87
|
-
: "";
|
|
88
|
-
parts.push(`<footer class="canvas-footer">${notesHtml}${crossHtml}
|
|
89
|
-
${input.legend}
|
|
90
|
-
<p class="canvas-note">${esc(input.note)}</p>
|
|
91
|
-
</footer>`);
|
|
92
|
-
return parts.join("\n\n");
|
|
154
|
+
return input.panels.join("\n\n");
|
|
93
155
|
}
|
|
94
156
|
//# sourceMappingURL=canvas-body.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-body.js","sourceRoot":"","sources":["../../src/core/canvas-body.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"canvas-body.js","sourceRoot":"","sources":["../../src/core/canvas-body.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic canvas enrichment (no LLM, no user token, no file reads, no
|
|
3
|
+
* cache). Given the already-extracted CanvasGraph (core/canvas-graph.ts), it
|
|
4
|
+
* computes the same CanvasEnrichment shape the old AI task returned
|
|
5
|
+
* (core/canvas-enrichment.ts) — a summary line, footer notes, and a
|
|
6
|
+
* cross-workflow tie — entirely from the graph's own structure. Pure and
|
|
7
|
+
* total: the same graph in always yields the same enrichment out, and every
|
|
8
|
+
* string is kept within ENRICHMENT_LIMITS so the layout can't break.
|
|
9
|
+
*
|
|
10
|
+
* What it deliberately does NOT reproduce from the retired AI pass: free-prose
|
|
11
|
+
* per-step descriptions and condition-named edge labels, which needed a reading
|
|
12
|
+
* of the step bodies. The base render already conveys role (node border +
|
|
13
|
+
* classify-derived sublabel) and outcome (edge color), so their absence
|
|
14
|
+
* degrades to "less prose", never "less structure".
|
|
15
|
+
*/
|
|
16
|
+
import { type CanvasEnrichment } from "./canvas-enrichment.js";
|
|
17
|
+
import type { CanvasGraph } from "./canvas-graph.js";
|
|
18
|
+
/**
|
|
19
|
+
* A one-line description of the workflow's shape — step count, branch points,
|
|
20
|
+
* and terminal outcomes — the deterministic stand-in for the AI summary. Reads
|
|
21
|
+
* the same graph the diagram is drawn from, so it can never disagree with it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function deriveSummary(graph: CanvasGraph): string;
|
|
24
|
+
/**
|
|
25
|
+
* Footer notes: the graph's own validation warnings (unreachable steps, no path
|
|
26
|
+
* to a terminal) — facts worth surfacing that the diagram can't show on its own.
|
|
27
|
+
* Undefined when the graph is clean, capped to the contract's count/length.
|
|
28
|
+
*/
|
|
29
|
+
export declare function deriveNotes(graph: CanvasGraph): string[] | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The cross-workflow tie, derived from the dashed launched-workflow nodes the
|
|
32
|
+
* extraction already merged in (core/canvas-graph.ts's mergeLaunchesIntoGraph,
|
|
33
|
+
* fed by the grep in core/canvas-interconnections.ts). Undefined when this
|
|
34
|
+
* workflow launches none.
|
|
35
|
+
*/
|
|
36
|
+
export declare function deriveCrossWorkflow(graph: CanvasGraph): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Builds the deterministic enrichment for a graph. Only sets a field when it
|
|
39
|
+
* has real content, so an annotation-light but valid workflow renders its clean
|
|
40
|
+
* base — matching the "every field optional" contract the AI enrichment had.
|
|
41
|
+
*/
|
|
42
|
+
export declare function deriveEnrichment(graph: CanvasGraph): CanvasEnrichment;
|
|
43
|
+
//# sourceMappingURL=canvas-derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-derive.d.ts","sourceRoot":"","sources":["../../src/core/canvas-derive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,mBAAmB,CAAC;AAgBjF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAuBxD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,GAAG,SAAS,CAMpE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAK1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,gBAAgB,CAOrE"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic canvas enrichment (no LLM, no user token, no file reads, no
|
|
3
|
+
* cache). Given the already-extracted CanvasGraph (core/canvas-graph.ts), it
|
|
4
|
+
* computes the same CanvasEnrichment shape the old AI task returned
|
|
5
|
+
* (core/canvas-enrichment.ts) — a summary line, footer notes, and a
|
|
6
|
+
* cross-workflow tie — entirely from the graph's own structure. Pure and
|
|
7
|
+
* total: the same graph in always yields the same enrichment out, and every
|
|
8
|
+
* string is kept within ENRICHMENT_LIMITS so the layout can't break.
|
|
9
|
+
*
|
|
10
|
+
* What it deliberately does NOT reproduce from the retired AI pass: free-prose
|
|
11
|
+
* per-step descriptions and condition-named edge labels, which needed a reading
|
|
12
|
+
* of the step bodies. The base render already conveys role (node border +
|
|
13
|
+
* classify-derived sublabel) and outcome (edge color), so their absence
|
|
14
|
+
* degrades to "less prose", never "less structure".
|
|
15
|
+
*/
|
|
16
|
+
import { ENRICHMENT_LIMITS } from "./canvas-enrichment.js";
|
|
17
|
+
/** Truncates to a hard cap with an ellipsis. The derivations below stay well
|
|
18
|
+
* under their caps; this just makes the same guarantee the contract does. */
|
|
19
|
+
function clamp(value, max) {
|
|
20
|
+
return value.length <= max ? value : `${value.slice(0, max - 1)}…`;
|
|
21
|
+
}
|
|
22
|
+
function countKind(nodes, kind) {
|
|
23
|
+
return nodes.filter((n) => n.kind === kind).length;
|
|
24
|
+
}
|
|
25
|
+
function plural(n, one, many = `${one}s`) {
|
|
26
|
+
return n === 1 ? one : many;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A one-line description of the workflow's shape — step count, branch points,
|
|
30
|
+
* and terminal outcomes — the deterministic stand-in for the AI summary. Reads
|
|
31
|
+
* the same graph the diagram is drawn from, so it can never disagree with it.
|
|
32
|
+
*/
|
|
33
|
+
export function deriveSummary(graph) {
|
|
34
|
+
// Real steps only — exclude the dashed launched-workflow placeholders.
|
|
35
|
+
const stepCount = graph.nodes.filter((n) => n.kind !== "launched-workflow").length;
|
|
36
|
+
// A branch point is a step that fans out to multiple successors; the graph
|
|
37
|
+
// marks exactly those edges "branching" (core/canvas-graph.ts's edgesForStep).
|
|
38
|
+
const branchPoints = new Set(graph.edges.filter((e) => e.kind === "branching").map((e) => e.from)).size;
|
|
39
|
+
const success = countKind(graph.nodes, "terminal-success");
|
|
40
|
+
const warn = countKind(graph.nodes, "terminal-warn");
|
|
41
|
+
const parts = [`${stepCount} ${plural(stepCount, "step")}`];
|
|
42
|
+
if (branchPoints > 0)
|
|
43
|
+
parts.push(`${branchPoints} ${plural(branchPoints, "branch point")}`);
|
|
44
|
+
const outcomes = [];
|
|
45
|
+
if (success > 0)
|
|
46
|
+
outcomes.push(`${success} success`);
|
|
47
|
+
if (warn > 0)
|
|
48
|
+
outcomes.push(`${warn} escalation`);
|
|
49
|
+
if (outcomes.length > 0) {
|
|
50
|
+
parts.push(`${outcomes.join(" / ")} ${plural(success + warn, "outcome")}`);
|
|
51
|
+
}
|
|
52
|
+
return clamp(parts.join(" · "), ENRICHMENT_LIMITS.summary);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Footer notes: the graph's own validation warnings (unreachable steps, no path
|
|
56
|
+
* to a terminal) — facts worth surfacing that the diagram can't show on its own.
|
|
57
|
+
* Undefined when the graph is clean, capped to the contract's count/length.
|
|
58
|
+
*/
|
|
59
|
+
export function deriveNotes(graph) {
|
|
60
|
+
if (graph.warnings.length === 0)
|
|
61
|
+
return undefined;
|
|
62
|
+
const notes = graph.warnings
|
|
63
|
+
.slice(0, ENRICHMENT_LIMITS.noteCount)
|
|
64
|
+
.map((w) => clamp(w, ENRICHMENT_LIMITS.note));
|
|
65
|
+
return notes.length > 0 ? notes : undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The cross-workflow tie, derived from the dashed launched-workflow nodes the
|
|
69
|
+
* extraction already merged in (core/canvas-graph.ts's mergeLaunchesIntoGraph,
|
|
70
|
+
* fed by the grep in core/canvas-interconnections.ts). Undefined when this
|
|
71
|
+
* workflow launches none.
|
|
72
|
+
*/
|
|
73
|
+
export function deriveCrossWorkflow(graph) {
|
|
74
|
+
const launched = graph.nodes.filter((n) => n.kind === "launched-workflow").map((n) => n.label);
|
|
75
|
+
if (launched.length === 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
const unique = [...new Set(launched)].sort();
|
|
78
|
+
return clamp(`Launches ${unique.join(", ")}.`, ENRICHMENT_LIMITS.crossWorkflow);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Builds the deterministic enrichment for a graph. Only sets a field when it
|
|
82
|
+
* has real content, so an annotation-light but valid workflow renders its clean
|
|
83
|
+
* base — matching the "every field optional" contract the AI enrichment had.
|
|
84
|
+
*/
|
|
85
|
+
export function deriveEnrichment(graph) {
|
|
86
|
+
const enrichment = { summary: deriveSummary(graph) };
|
|
87
|
+
const notes = deriveNotes(graph);
|
|
88
|
+
if (notes)
|
|
89
|
+
enrichment.notes = notes;
|
|
90
|
+
const crossWorkflow = deriveCrossWorkflow(graph);
|
|
91
|
+
if (crossWorkflow)
|
|
92
|
+
enrichment.crossWorkflow = crossWorkflow;
|
|
93
|
+
return enrichment;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=canvas-derive.js.map
|