@sapiom/harness 0.1.5 → 0.1.6
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 +61 -0
- package/dist/cli/bin.js +24 -7
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/consent.js +1 -1
- package/dist/core/adapters/adapter.d.ts +2 -2
- 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-run-state.d.ts +9 -5
- package/dist/core/canvas-run-state.d.ts.map +1 -1
- package/dist/core/canvas-run-state.js +42 -13
- package/dist/core/canvas-run-state.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/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/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/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/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/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/rest.d.ts.map +1 -1
- package/dist/server/rest.js +0 -2
- 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/shared/types.d.ts +79 -35
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/web/assets/index-DCtyI4VW.css +32 -0
- package/dist/web/assets/index-WSw5RvYR.js +445 -0
- package/dist/web/canvas/sess-boot/index.html +305 -0
- package/dist/web/index.html +4 -9
- package/dist/web/welcome-hero-dark.png +0 -0
- package/dist/web/welcome-hero-light.png +0 -0
- package/package.json +6 -3
- 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/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,305 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>leasing — demo canvas</title>
|
|
7
|
+
<!--
|
|
8
|
+
Static demo canvas for the GitHub Pages build of Sapiom Harness.
|
|
9
|
+
In the real product this document is generated by the harness server's
|
|
10
|
+
deterministic canvas renderer into .sapiom/canvas/renders/<slug>.html and
|
|
11
|
+
served at /canvas/<sessionId>/. This copy reproduces that renderer's
|
|
12
|
+
CASCADING layout (vertical flow, group bands, labeled branch fork) for the
|
|
13
|
+
demo workspace only — it is not agent output.
|
|
14
|
+
|
|
15
|
+
The document is VISUAL ONLY: workflow name/description/steps belong to the
|
|
16
|
+
app chrome around the iframe. Connector system: 1px hairline verticals
|
|
17
|
+
with one small arrowhead; branch labels are mono chips on the fork.
|
|
18
|
+
-->
|
|
19
|
+
<style>
|
|
20
|
+
:root {
|
|
21
|
+
--bg: #fafafa; --panel: #ffffff; --ink: #141417; --ink-muted: #54545e;
|
|
22
|
+
--ink-faint: #6a6a74; --line: rgba(17,17,20,.08); --line-2: rgba(17,17,20,.15);
|
|
23
|
+
--brand: #167e3a; --brand-wash: rgba(22,126,58,.12); --node: #f2f2f3;
|
|
24
|
+
--dot: rgba(17,17,20,.12);
|
|
25
|
+
}
|
|
26
|
+
[data-theme="dark"] {
|
|
27
|
+
--bg: #0a0a0c; --panel: #101013; --ink: #f4f4f5; --ink-muted: #a7a7b0;
|
|
28
|
+
--ink-faint: #85858f; --line: rgba(255,255,255,.07); --line-2: rgba(255,255,255,.13);
|
|
29
|
+
--brand: #6be195; --brand-wash: rgba(107,225,149,.14); --node: #17171b;
|
|
30
|
+
--dot: rgba(255,255,255,.10);
|
|
31
|
+
}
|
|
32
|
+
* { box-sizing: border-box; }
|
|
33
|
+
html, body { height: 100%; }
|
|
34
|
+
body {
|
|
35
|
+
margin: 0; padding: 0; background: var(--bg); color: var(--ink);
|
|
36
|
+
font: 13px/1.5 "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
37
|
+
}
|
|
38
|
+
/* Full-bleed card: the surrounding pane provides the card chrome, so the
|
|
39
|
+
dotted grid runs edge to edge with no inner frame of its own. The
|
|
40
|
+
bottom padding reserves the strip where the app docks its zoom
|
|
41
|
+
controls, so the graph never renders underneath them; the same inset
|
|
42
|
+
is posted to the app as the fit-bounds exclusion (see the size post). */
|
|
43
|
+
.canvas-panel {
|
|
44
|
+
position: relative; min-height: 100%;
|
|
45
|
+
padding: 40px 20px 72px;
|
|
46
|
+
background-image: radial-gradient(var(--dot) 1px, transparent 1px);
|
|
47
|
+
background-size: 16px 16px;
|
|
48
|
+
}
|
|
49
|
+
/* Kept for the markup contract; header text belongs to the app chrome
|
|
50
|
+
around this iframe, so it renders visually hidden here. */
|
|
51
|
+
.canvas-title, .canvas-subtitle {
|
|
52
|
+
position: absolute; width: 1px; height: 1px; overflow: hidden;
|
|
53
|
+
clip: rect(0 0 0 0); margin: 0;
|
|
54
|
+
}
|
|
55
|
+
.canvas-badge {
|
|
56
|
+
/* Top-left: the app's view controls own the frame's top-right corner. */
|
|
57
|
+
position: absolute; top: 10px; left: 10px;
|
|
58
|
+
font: 500 9px/1 "Geist Mono", ui-monospace, monospace;
|
|
59
|
+
padding: 4px 8px; border-radius: 8px;
|
|
60
|
+
background: var(--brand-wash); color: var(--brand); border: 1px solid var(--line);
|
|
61
|
+
}
|
|
62
|
+
/* The graph is the pan/zoom subject: the app posts view messages and this
|
|
63
|
+
wrapper transforms, while the dotted board behind it stays put filling
|
|
64
|
+
the pane (see the view-contract script at the end of body). */
|
|
65
|
+
.cascade { display: flex; flex-direction: column; align-items: center; transform-origin: 50% 0; }
|
|
66
|
+
.group {
|
|
67
|
+
display: flex; flex-direction: column; align-items: center;
|
|
68
|
+
border: 1px dashed var(--line-2); border-radius: 10px;
|
|
69
|
+
padding: 20px 24px 16px; position: relative; min-width: 200px;
|
|
70
|
+
}
|
|
71
|
+
.group + .group { margin-top: 0; }
|
|
72
|
+
.group-label {
|
|
73
|
+
position: absolute; top: -7px; left: 12px;
|
|
74
|
+
font: 500 9px/1 "Geist Mono", ui-monospace, monospace; letter-spacing: .04em;
|
|
75
|
+
color: var(--ink-faint); background: var(--panel); padding: 2px 6px;
|
|
76
|
+
}
|
|
77
|
+
.node {
|
|
78
|
+
background: var(--node); border: 1px solid var(--line); border-radius: 8px;
|
|
79
|
+
padding: 10px 18px; min-width: 128px; text-align: center;
|
|
80
|
+
}
|
|
81
|
+
.node .kind {
|
|
82
|
+
display: block; color: var(--ink-faint);
|
|
83
|
+
font: 500 9px/1.6 "Geist Mono", ui-monospace, monospace; letter-spacing: .02em;
|
|
84
|
+
}
|
|
85
|
+
.node .label { font-weight: 590; font-size: 12px; }
|
|
86
|
+
.node .meta {
|
|
87
|
+
display: block; color: var(--ink-muted);
|
|
88
|
+
font: 500 9px/1.7 "Geist Mono", ui-monospace, monospace;
|
|
89
|
+
}
|
|
90
|
+
.node--entry { border-color: var(--brand); }
|
|
91
|
+
/* Interaction states, driven by app messages (the app's gesture layer sits
|
|
92
|
+
over this iframe, so native :hover can never fire here): hover lifts and
|
|
93
|
+
tints, press settles, select rings while the app's drill-down is open. */
|
|
94
|
+
.node {
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
transition: border-color 130ms ease-out, background 130ms ease-out,
|
|
97
|
+
transform 130ms ease-out, box-shadow 130ms ease-out;
|
|
98
|
+
}
|
|
99
|
+
.node.is-hover, .node:hover {
|
|
100
|
+
border-color: var(--brand);
|
|
101
|
+
background: color-mix(in srgb, var(--brand) 6%, var(--node));
|
|
102
|
+
transform: translateY(-1px);
|
|
103
|
+
}
|
|
104
|
+
.node.is-pressed {
|
|
105
|
+
transform: translateY(0);
|
|
106
|
+
background: color-mix(in srgb, var(--brand) 12%, var(--node));
|
|
107
|
+
}
|
|
108
|
+
/* Selected: keep the ring, and add a soft brand-tinted fill so the picked
|
|
109
|
+
node reads at a glance (between hover 6% and press 12%). Uses the theme's
|
|
110
|
+
own node surface, so contrast holds in light and dark. */
|
|
111
|
+
.node.is-selected {
|
|
112
|
+
border-color: var(--brand);
|
|
113
|
+
box-shadow: 0 0 0 1px var(--brand);
|
|
114
|
+
background: color-mix(in srgb, var(--brand) 10%, var(--node));
|
|
115
|
+
}
|
|
116
|
+
@media (prefers-reduced-motion: reduce) {
|
|
117
|
+
.node { transition: none; }
|
|
118
|
+
.node.is-hover, .node:hover { transform: none; }
|
|
119
|
+
}
|
|
120
|
+
/* Vertical connector: 1px hairline, one small arrowhead. */
|
|
121
|
+
.edge {
|
|
122
|
+
width: 1px; height: 28px; background: var(--line-2);
|
|
123
|
+
position: relative; flex-shrink: 0;
|
|
124
|
+
}
|
|
125
|
+
.edge::after {
|
|
126
|
+
content: ""; position: absolute; bottom: 0; left: -2.5px;
|
|
127
|
+
border: 3px solid transparent; border-top: 4px solid var(--ink-faint);
|
|
128
|
+
}
|
|
129
|
+
/* Branch fork: two curved hairline paths with mono label chips. */
|
|
130
|
+
.fork { position: relative; width: 300px; height: 56px; flex-shrink: 0; }
|
|
131
|
+
.fork svg { position: absolute; inset: 0; width: 100%; height: 100%; }
|
|
132
|
+
.fork path { fill: none; stroke: var(--line-2); stroke-width: 1; }
|
|
133
|
+
.fork-label {
|
|
134
|
+
position: absolute; top: 12px; transform: translateX(-50%);
|
|
135
|
+
font: 500 9px/1 "Geist Mono", ui-monospace, monospace;
|
|
136
|
+
color: var(--ink-faint); background: var(--panel);
|
|
137
|
+
border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
|
|
138
|
+
white-space: nowrap;
|
|
139
|
+
}
|
|
140
|
+
.outcomes { display: flex; gap: 24px; }
|
|
141
|
+
</style>
|
|
142
|
+
</head>
|
|
143
|
+
<body>
|
|
144
|
+
<script>
|
|
145
|
+
var theme = new URLSearchParams(location.search).get("theme");
|
|
146
|
+
if (theme === "dark" || theme === "light") document.documentElement.dataset.theme = theme;
|
|
147
|
+
</script>
|
|
148
|
+
<div class="canvas-panel">
|
|
149
|
+
<h1 class="canvas-title">leasing</h1>
|
|
150
|
+
<p class="canvas-subtitle">Deterministic workflow diagram — generated from the project's sapiom.json.</p>
|
|
151
|
+
<span class="canvas-badge">demo render</span>
|
|
152
|
+
<div class="cascade">
|
|
153
|
+
<div class="group">
|
|
154
|
+
<span class="group-label">intake & screening</span>
|
|
155
|
+
<div class="node node--entry" data-node-id="intake"><span class="kind">entry</span><span class="label">intake</span></div>
|
|
156
|
+
<div class="edge"></div>
|
|
157
|
+
<div class="node" data-node-id="screen"><span class="kind">step</span><span class="label">screen</span><span class="meta">1 input</span></div>
|
|
158
|
+
<div class="edge"></div>
|
|
159
|
+
<div class="node" data-node-id="credit-check"><span class="kind">step</span><span class="label">credit-check</span><span class="meta">2 inputs · 30s limit</span></div>
|
|
160
|
+
</div>
|
|
161
|
+
<div class="edge"></div>
|
|
162
|
+
<div class="node" data-node-id="approve"><span class="kind">gate</span><span class="label">approve?</span><span class="meta">1 input · 2 branches</span></div>
|
|
163
|
+
<div class="fork" aria-hidden="true">
|
|
164
|
+
<svg viewBox="0 0 300 56" preserveAspectRatio="none">
|
|
165
|
+
<path d="M150 0 C 150 28, 75 28, 75 56" />
|
|
166
|
+
<path d="M150 0 C 150 28, 225 28, 225 56" />
|
|
167
|
+
</svg>
|
|
168
|
+
<span class="fork-label" style="left: 25%">score ≥ 620</span>
|
|
169
|
+
<span class="fork-label" style="left: 75%">declined</span>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="outcomes">
|
|
172
|
+
<div class="node" data-node-id="draft-lease"><span class="kind">exit</span><span class="label">draft-lease</span><span class="meta">2 inputs</span></div>
|
|
173
|
+
<div class="node" data-node-id="manual-review"><span class="kind">exit</span><span class="label">manual-review</span><span class="meta">2 inputs</span></div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<script type="application/json" id="sapiom-graph">
|
|
179
|
+
{
|
|
180
|
+
"name": "leasing",
|
|
181
|
+
"entry": "intake",
|
|
182
|
+
"nodes": [
|
|
183
|
+
{ "id": "intake", "kind": "entry", "label": "intake", "role": "entry", "description": "Logs the incoming order and stamps the time it arrived.", "timeoutMs": null, "inputSchema": null, "capabilities": ["records.read"] },
|
|
184
|
+
{ "id": "screen", "kind": "step", "label": "screen", "role": "step", "description": "Runs the applicant against the screening rules.", "timeoutMs": null, "inputSchema": { "type": "object", "properties": { "application": { "type": "object" } }, "required": ["application"] }, "capabilities": ["records.read"] },
|
|
185
|
+
{ "id": "credit-check", "kind": "step", "label": "credit-check", "role": "step", "description": "Pulls the credit score used by the approval gate.", "timeoutMs": 30000, "inputSchema": { "type": "object", "properties": { "application": { "type": "object" }, "bureau": { "type": "string" } }, "required": ["application"] }, "capabilities": ["credit.check"] },
|
|
186
|
+
{ "id": "approve", "kind": "step", "label": "approve?", "role": "step · branches", "description": "Branches on the score: 620 and above auto-drafts a lease; everything else escalates.", "timeoutMs": null, "inputSchema": { "type": "object", "properties": { "score": { "type": "number" } }, "required": ["score"] }, "capabilities": ["rules.evaluate"] },
|
|
187
|
+
{ "id": "draft-lease", "kind": "terminal-success", "label": "draft-lease", "role": "terminal · success", "description": "Auto-drafts the lease and completes.", "timeoutMs": null, "inputSchema": { "type": "object", "properties": { "application": { "type": "object" }, "score": { "type": "number" } }, "required": ["application", "score"] }, "capabilities": ["records.write"] },
|
|
188
|
+
{ "id": "manual-review", "kind": "terminal-warn", "label": "manual-review", "role": "terminal · needs attention", "description": "Escalates to a human for manual review.", "timeoutMs": null, "inputSchema": { "type": "object", "properties": { "application": { "type": "object" }, "score": { "type": "number" } }, "required": ["application", "score"] }, "capabilities": ["records.write"] }
|
|
189
|
+
],
|
|
190
|
+
"edges": [
|
|
191
|
+
{ "from": "intake", "to": "screen", "kind": "sequential", "label": "" },
|
|
192
|
+
{ "from": "screen", "to": "credit-check", "kind": "sequential", "label": "" },
|
|
193
|
+
{ "from": "credit-check", "to": "approve", "kind": "sequential", "label": "" },
|
|
194
|
+
{ "from": "approve", "to": "draft-lease", "kind": "branching", "label": "score ≥ 620" },
|
|
195
|
+
{ "from": "approve", "to": "manual-review", "kind": "branching", "label": "declined" }
|
|
196
|
+
],
|
|
197
|
+
"groups": [{ "label": "intake & screening", "nodeIds": ["intake", "screen", "credit-check"] }],
|
|
198
|
+
"warnings": []
|
|
199
|
+
}
|
|
200
|
+
</script>
|
|
201
|
+
<script>
|
|
202
|
+
// View contract: the harness posts {type:"sapiom-canvas:view", zoom, x, y}
|
|
203
|
+
// and the GRAPH transforms in place — the dotted board never moves, so
|
|
204
|
+
// panning reads as moving the diagram across an anchored surface. The
|
|
205
|
+
// graph payload above is posted once so the app's step drill-down works.
|
|
206
|
+
(function () {
|
|
207
|
+
var graphEl = document.getElementById("sapiom-graph");
|
|
208
|
+
if (graphEl) {
|
|
209
|
+
try {
|
|
210
|
+
parent.postMessage({ type: "sapiom-canvas:graph", graph: JSON.parse(graphEl.textContent || "{}") }, "*");
|
|
211
|
+
} catch (err) { /* skip drill-down */ }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Fit contract: post the graph's natural size plus the chrome insets
|
|
215
|
+
// the app must keep clear (the panel's own padding — the bottom strip
|
|
216
|
+
// is where the app docks its zoom controls). The app computes a
|
|
217
|
+
// fit-to-view from these bounds on first render and on pane resizes.
|
|
218
|
+
function postSize() {
|
|
219
|
+
var cascade = document.querySelector(".cascade");
|
|
220
|
+
if (!cascade) return;
|
|
221
|
+
try {
|
|
222
|
+
parent.postMessage({
|
|
223
|
+
type: "sapiom-canvas:size",
|
|
224
|
+
width: cascade.offsetWidth,
|
|
225
|
+
height: cascade.offsetHeight,
|
|
226
|
+
insetTop: 40,
|
|
227
|
+
insetBottom: 72,
|
|
228
|
+
insetX: 20
|
|
229
|
+
}, "*");
|
|
230
|
+
} catch (err) { /* no parent */ }
|
|
231
|
+
}
|
|
232
|
+
// After layout: rAF covers first paint; load covers font swaps that can
|
|
233
|
+
// still nudge the cascade's measured height.
|
|
234
|
+
requestAnimationFrame(postSize);
|
|
235
|
+
window.addEventListener("load", postSize);
|
|
236
|
+
|
|
237
|
+
// Pointer contract: the app's gesture layer owns raw pointer events, so
|
|
238
|
+
// clicks, hovers, and presses all arrive as messages with iframe-relative
|
|
239
|
+
// coordinates. This document hit-tests its own nodes, applies the state
|
|
240
|
+
// classes, and answers picks with the node id (drill-down) and hovers
|
|
241
|
+
// with {type:"sapiom-canvas:hit"} (the app's cursor affordance).
|
|
242
|
+
function nodeAt(x, y) {
|
|
243
|
+
if (typeof x !== "number" || typeof y !== "number" || x < 0 || y < 0) return null;
|
|
244
|
+
var el = document.elementFromPoint(x, y);
|
|
245
|
+
return el && el.closest ? el.closest("[data-node-id]") : null;
|
|
246
|
+
}
|
|
247
|
+
var hoverEl = null;
|
|
248
|
+
var pressEl = null;
|
|
249
|
+
var selectedEl = null;
|
|
250
|
+
window.addEventListener("message", function (event) {
|
|
251
|
+
var data = event.data;
|
|
252
|
+
if (!data) return;
|
|
253
|
+
if (data.type === "sapiom-canvas:pick") {
|
|
254
|
+
var node = nodeAt(data.x, data.y);
|
|
255
|
+
if (node) {
|
|
256
|
+
try {
|
|
257
|
+
parent.postMessage({ type: "sapiom-canvas:node", id: node.getAttribute("data-node-id") }, "*");
|
|
258
|
+
} catch (err) { /* no parent */ }
|
|
259
|
+
}
|
|
260
|
+
} else if (data.type === "sapiom-canvas:hover") {
|
|
261
|
+
var over = nodeAt(data.x, data.y);
|
|
262
|
+
if (over !== hoverEl) {
|
|
263
|
+
if (hoverEl) hoverEl.classList.remove("is-hover");
|
|
264
|
+
if (over) over.classList.add("is-hover");
|
|
265
|
+
hoverEl = over;
|
|
266
|
+
try {
|
|
267
|
+
parent.postMessage({ type: "sapiom-canvas:hit", id: over ? over.getAttribute("data-node-id") : null }, "*");
|
|
268
|
+
} catch (err) { /* no parent */ }
|
|
269
|
+
}
|
|
270
|
+
} else if (data.type === "sapiom-canvas:press") {
|
|
271
|
+
if (pressEl) { pressEl.classList.remove("is-pressed"); pressEl = null; }
|
|
272
|
+
if (data.down) {
|
|
273
|
+
var pressed = nodeAt(data.x, data.y);
|
|
274
|
+
if (pressed) { pressed.classList.add("is-pressed"); pressEl = pressed; }
|
|
275
|
+
}
|
|
276
|
+
} else if (data.type === "sapiom-canvas:select") {
|
|
277
|
+
if (selectedEl) { selectedEl.classList.remove("is-selected"); selectedEl = null; }
|
|
278
|
+
if (typeof data.id === "string") {
|
|
279
|
+
var nodes = document.querySelectorAll("[data-node-id]");
|
|
280
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
281
|
+
if (nodes[i].getAttribute("data-node-id") === data.id) {
|
|
282
|
+
nodes[i].classList.add("is-selected");
|
|
283
|
+
selectedEl = nodes[i];
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
var graph = document.querySelector(".cascade");
|
|
292
|
+
if (!graph) return;
|
|
293
|
+
window.addEventListener("message", function (event) {
|
|
294
|
+
var data = event.data;
|
|
295
|
+
if (!data || data.type !== "sapiom-canvas:view") return;
|
|
296
|
+
var zoom = typeof data.zoom === "number" ? data.zoom : 1;
|
|
297
|
+
var x = typeof data.x === "number" ? data.x : 0;
|
|
298
|
+
var y = typeof data.y === "number" ? data.y : 0;
|
|
299
|
+
graph.style.transform =
|
|
300
|
+
zoom === 1 && x === 0 && y === 0 ? "" : "translate(" + x + "px, " + y + "px) scale(" + zoom + ")";
|
|
301
|
+
});
|
|
302
|
+
})();
|
|
303
|
+
</script>
|
|
304
|
+
</body>
|
|
305
|
+
</html>
|
package/dist/web/index.html
CHANGED
|
@@ -3,23 +3,18 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Sapiom
|
|
6
|
+
<title>Sapiom Studio</title>
|
|
7
7
|
<script>
|
|
8
8
|
// Set the theme before first paint to avoid a flash of the wrong one.
|
|
9
9
|
// Storage key must match web/src/lib/theme.ts (this can't import it).
|
|
10
10
|
(function () {
|
|
11
11
|
var stored = localStorage.getItem("sapiom-harness-theme");
|
|
12
|
-
var theme =
|
|
13
|
-
stored === "light" || stored === "dark"
|
|
14
|
-
? stored
|
|
15
|
-
: window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
16
|
-
? "dark"
|
|
17
|
-
: "light";
|
|
12
|
+
var theme = stored === "light" || stored === "dark" ? stored : "dark";
|
|
18
13
|
document.documentElement.dataset.theme = theme;
|
|
19
14
|
})();
|
|
20
15
|
</script>
|
|
21
|
-
<script type="module" crossorigin src="/assets/index-
|
|
22
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-WSw5RvYR.js"></script>
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DCtyI4VW.css">
|
|
23
18
|
</head>
|
|
24
19
|
<body>
|
|
25
20
|
<div id="root"></div>
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sapiom/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Sapiom Harness — a CLI-launched local web app that runs your coding agent (Claude Code, Codex) in a Sapiom-configured environment: MCP pre-wired, workflows tracked, one-click deploy/run, and a live canvas for previews.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sapiom",
|
|
@@ -47,13 +47,15 @@
|
|
|
47
47
|
"open": "^10.1.0",
|
|
48
48
|
"ws": "^8.18.0",
|
|
49
49
|
"zod": "^3.25.0",
|
|
50
|
-
"@sapiom/agent": "^0.6.
|
|
51
|
-
"@sapiom/agent-core": "^0.9.
|
|
50
|
+
"@sapiom/agent": "^0.6.7",
|
|
51
|
+
"@sapiom/agent-core": "^0.9.9",
|
|
52
52
|
"@sapiom/analytics-core": "^0.2.1",
|
|
53
53
|
"@sapiom/mcp": "^0.11.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@playwright/test": "^1.61.0",
|
|
57
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
58
|
+
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
57
59
|
"@types/express": "^4.17.21",
|
|
58
60
|
"@types/node": "^20.11.30",
|
|
59
61
|
"@types/react": "^19.0.0",
|
|
@@ -87,6 +89,7 @@
|
|
|
87
89
|
"start": "node dist/cli/bin.js",
|
|
88
90
|
"test": "vitest run",
|
|
89
91
|
"test:watch": "vitest",
|
|
92
|
+
"test:mutation": "stryker run",
|
|
90
93
|
"test:ui": "playwright test --config web/e2e/playwright.config.ts",
|
|
91
94
|
"test:canvas": "playwright test --config e2e/playwright.config.ts",
|
|
92
95
|
"typecheck": "tsc --noEmit && tsc --noEmit -p web/tsconfig.json",
|
package/dist/core/run-spend.d.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* run-spend — fetches the cost/spend summary for a prod execution from the
|
|
3
|
-
* core surface and maps it to a {@link RunSpend} the canvas can poll.
|
|
4
|
-
*
|
|
5
|
-
* WHY the core surface: the spend endpoint lives at
|
|
6
|
-
* `GET /v1/workflows/executions/:id/spend` on api.sapiom.ai (the CORE
|
|
7
|
-
* capability surface), which is distinct from the agents surface
|
|
8
|
-
* (tools.sapiom.ai) that run-state.ts uses. The same caveat applies:
|
|
9
|
-
* env-consistency with the run's environment matters — both surfaces must be
|
|
10
|
-
* overridden together when pointing at non-prod.
|
|
11
|
-
*
|
|
12
|
-
* WHY the key stays server-side: same rationale as run-state.ts — the Sapiom
|
|
13
|
-
* API key is a harness credential that must never reach the browser. The
|
|
14
|
-
* harness server fetches on behalf of the canvas, so the SPA polls a local
|
|
15
|
-
* `/api/runs/:id/spend` endpoint (no key in the request) rather than calling
|
|
16
|
-
* the upstream surface directly.
|
|
17
|
-
*/
|
|
18
|
-
import type { RunSpend } from "../shared/types.js";
|
|
19
|
-
/**
|
|
20
|
-
* Resolve the CORE surface base URL for the spend endpoint.
|
|
21
|
-
*
|
|
22
|
-
* The spend endpoint lives on the CORE host (`api.<env>`), distinct from the
|
|
23
|
-
* agents host run-state.ts uses (`tools.<env>`). Crucially, a run lives in the
|
|
24
|
-
* agents env, and its spend must be read from the MATCHING core env — otherwise
|
|
25
|
-
* a prod run's spend queried against dev 401s. So we DERIVE the core host from
|
|
26
|
-
* the agents host (`tools.<env>` → `api.<env>`) rather than reading
|
|
27
|
-
* `SAPIOM_API_URL`, which in some setups points at a different env than the
|
|
28
|
-
* agents surface. An explicit `SAPIOM_CORE_URL` still wins for full control.
|
|
29
|
-
*/
|
|
30
|
-
export declare function resolveCoreBaseUrl(): string;
|
|
31
|
-
export type RunSpendResult = {
|
|
32
|
-
ok: true;
|
|
33
|
-
spend: RunSpend;
|
|
34
|
-
} | {
|
|
35
|
-
ok: false;
|
|
36
|
-
status: number;
|
|
37
|
-
error: string;
|
|
38
|
-
};
|
|
39
|
-
export interface RunSpendFetcherOpts {
|
|
40
|
-
apiKey: string | null;
|
|
41
|
-
baseUrl?: string;
|
|
42
|
-
/** Injectable fetch implementation — defaults to global fetch. Test seam. */
|
|
43
|
-
fetchImpl?: typeof fetch;
|
|
44
|
-
}
|
|
45
|
-
export interface RunSpendFetcher {
|
|
46
|
-
fetch(executionId: string): Promise<RunSpendResult>;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Create a fetcher that resolves an execution's spend/cost from the core
|
|
50
|
-
* surface. The fetcher is intentionally non-throwing: all error paths return
|
|
51
|
-
* a typed {@link RunSpendResult} with `ok: false` so the router can forward
|
|
52
|
-
* the appropriate HTTP status without a try/catch at the call site.
|
|
53
|
-
*/
|
|
54
|
-
export declare function createRunSpendFetcher(opts: RunSpendFetcherOpts): RunSpendFetcher;
|
|
55
|
-
//# sourceMappingURL=run-spend.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-spend.d.ts","sourceRoot":"","sources":["../../src/core/run-spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAc3C;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACrD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,mBAAmB,GACxB,eAAe,CAsEjB"}
|
package/dist/core/run-spend.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* run-spend — fetches the cost/spend summary for a prod execution from the
|
|
3
|
-
* core surface and maps it to a {@link RunSpend} the canvas can poll.
|
|
4
|
-
*
|
|
5
|
-
* WHY the core surface: the spend endpoint lives at
|
|
6
|
-
* `GET /v1/workflows/executions/:id/spend` on api.sapiom.ai (the CORE
|
|
7
|
-
* capability surface), which is distinct from the agents surface
|
|
8
|
-
* (tools.sapiom.ai) that run-state.ts uses. The same caveat applies:
|
|
9
|
-
* env-consistency with the run's environment matters — both surfaces must be
|
|
10
|
-
* overridden together when pointing at non-prod.
|
|
11
|
-
*
|
|
12
|
-
* WHY the key stays server-side: same rationale as run-state.ts — the Sapiom
|
|
13
|
-
* API key is a harness credential that must never reach the browser. The
|
|
14
|
-
* harness server fetches on behalf of the canvas, so the SPA polls a local
|
|
15
|
-
* `/api/runs/:id/spend` endpoint (no key in the request) rather than calling
|
|
16
|
-
* the upstream surface directly.
|
|
17
|
-
*/
|
|
18
|
-
import { resolveAgentsBaseUrl } from "./run-state.js";
|
|
19
|
-
/**
|
|
20
|
-
* Resolve the CORE surface base URL for the spend endpoint.
|
|
21
|
-
*
|
|
22
|
-
* The spend endpoint lives on the CORE host (`api.<env>`), distinct from the
|
|
23
|
-
* agents host run-state.ts uses (`tools.<env>`). Crucially, a run lives in the
|
|
24
|
-
* agents env, and its spend must be read from the MATCHING core env — otherwise
|
|
25
|
-
* a prod run's spend queried against dev 401s. So we DERIVE the core host from
|
|
26
|
-
* the agents host (`tools.<env>` → `api.<env>`) rather than reading
|
|
27
|
-
* `SAPIOM_API_URL`, which in some setups points at a different env than the
|
|
28
|
-
* agents surface. An explicit `SAPIOM_CORE_URL` still wins for full control.
|
|
29
|
-
*/
|
|
30
|
-
export function resolveCoreBaseUrl() {
|
|
31
|
-
const override = process.env.SAPIOM_CORE_URL;
|
|
32
|
-
if (override)
|
|
33
|
-
return override;
|
|
34
|
-
const agents = resolveAgentsBaseUrl();
|
|
35
|
-
try {
|
|
36
|
-
const url = new URL(agents);
|
|
37
|
-
if (url.hostname.startsWith("tools.")) {
|
|
38
|
-
url.hostname = `api.${url.hostname.slice("tools.".length)}`;
|
|
39
|
-
return url.origin;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// Unparseable agents URL — fall through to the prod default.
|
|
44
|
-
}
|
|
45
|
-
return "https://api.sapiom.ai";
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Create a fetcher that resolves an execution's spend/cost from the core
|
|
49
|
-
* surface. The fetcher is intentionally non-throwing: all error paths return
|
|
50
|
-
* a typed {@link RunSpendResult} with `ok: false` so the router can forward
|
|
51
|
-
* the appropriate HTTP status without a try/catch at the call site.
|
|
52
|
-
*/
|
|
53
|
-
export function createRunSpendFetcher(opts) {
|
|
54
|
-
const { apiKey, baseUrl = resolveCoreBaseUrl(), fetchImpl = fetch } = opts;
|
|
55
|
-
return {
|
|
56
|
-
async fetch(executionId) {
|
|
57
|
-
// No API key — do not touch the network; the harness is not signed in.
|
|
58
|
-
if (!apiKey) {
|
|
59
|
-
return {
|
|
60
|
-
ok: false,
|
|
61
|
-
status: 503,
|
|
62
|
-
error: "harness is not signed in to Sapiom",
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
let res;
|
|
66
|
-
try {
|
|
67
|
-
res = await fetchImpl(`${baseUrl}/v1/workflows/executions/${encodeURIComponent(executionId)}/spend`, { headers: { "x-api-key": apiKey } });
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return { ok: false, status: 502, error: "gateway unreachable" };
|
|
71
|
-
}
|
|
72
|
-
if (res.status === 404) {
|
|
73
|
-
return { ok: false, status: 404, error: "spend not found" };
|
|
74
|
-
}
|
|
75
|
-
if (!res.ok) {
|
|
76
|
-
return {
|
|
77
|
-
ok: false,
|
|
78
|
-
status: 502,
|
|
79
|
-
error: `gateway responded ${res.status}`,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
const raw = (await res.json());
|
|
84
|
-
// The response is ExecutionSpendDto — we map the capability sub-object
|
|
85
|
-
// fields to RunSpend, falling back to top-level totalUsd when needed.
|
|
86
|
-
const capability = raw.capability;
|
|
87
|
-
const totalUsd = capability?.totalUsd ??
|
|
88
|
-
raw.totalUsd ??
|
|
89
|
-
"0";
|
|
90
|
-
const settleState = capability?.settleState ?? "pending";
|
|
91
|
-
const rawByStep = capability?.byStep ?? [];
|
|
92
|
-
const byStep = rawByStep.map((s) => {
|
|
93
|
-
const step = s;
|
|
94
|
-
return {
|
|
95
|
-
name: step.stepName,
|
|
96
|
-
totalUsd: step.totalUsd,
|
|
97
|
-
entryCount: step.entryCount,
|
|
98
|
-
};
|
|
99
|
-
});
|
|
100
|
-
const spend = { executionId, totalUsd, settleState, byStep };
|
|
101
|
-
return { ok: true, spend };
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
return { ok: false, status: 502, error: "could not decode spend" };
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
//# sourceMappingURL=run-spend.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-spend.js","sourceRoot":"","sources":["../../src/core/run-spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtC,GAAG,CAAC,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5D,OAAO,GAAG,CAAC,MAAM,CAAC;QACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IACD,OAAO,uBAAuB,CAAC;AACjC,CAAC;AAiBD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAyB;IAEzB,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,kBAAkB,EAAE,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;IAE3E,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,WAAmB;YAC7B,uEAAuE;YACvE,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,GAAG;oBACX,KAAK,EAAE,oCAAoC;iBAC5C,CAAC;YACJ,CAAC;YAED,IAAI,GAAa,CAAC;YAClB,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,SAAS,CACnB,GAAG,OAAO,4BAA4B,kBAAkB,CAAC,WAAW,CAAC,QAAQ,EAC7E,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CACrC,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;YAClE,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;YAC9D,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,GAAG;oBACX,KAAK,EAAE,qBAAqB,GAAG,CAAC,MAAM,EAAE;iBACzC,CAAC;YACJ,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;gBAE1D,uEAAuE;gBACvE,sEAAsE;gBACtE,MAAM,UAAU,GAAG,GAAG,CAAC,UAEV,CAAC;gBAEd,MAAM,QAAQ,GACX,UAAU,EAAE,QAA+B;oBAC3C,GAAG,CAAC,QAA+B;oBACpC,GAAG,CAAC;gBAEN,MAAM,WAAW,GACd,UAAU,EAAE,WAAkC,IAAI,SAAS,CAAC;gBAE/D,MAAM,SAAS,GAAI,UAAU,EAAE,MAAgC,IAAI,EAAE,CAAC;gBACtE,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBACjC,MAAM,IAAI,GAAG,CAA4B,CAAC;oBAC1C,OAAO;wBACL,IAAI,EAAE,IAAI,CAAC,QAAkB;wBAC7B,QAAQ,EAAE,IAAI,CAAC,QAAkB;wBACjC,UAAU,EAAE,IAAI,CAAC,UAAoB;qBACtC,CAAC;gBACJ,CAAC,CAAC,CAAC;gBAEH,MAAM,KAAK,GAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;gBACvE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;YACrE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* run-transactions — fetches the per-call cost drill-down for an execution
|
|
3
|
-
* from the core surface and maps it to a {@link RunCall}[] the canvas shows
|
|
4
|
-
* when a user clicks a step and asks "why is this costly".
|
|
5
|
-
*
|
|
6
|
-
* WHY this exists alongside run-spend: `/spend` gives per-STEP aggregates
|
|
7
|
-
* (totalUsd + entryCount). This endpoint itemises the individual billable
|
|
8
|
-
* capability calls that make up that total (e.g. "renderPdfs $7.95 = 1 sandbox
|
|
9
|
-
* call"), which is what makes the debug macros actionable.
|
|
10
|
-
*
|
|
11
|
-
* WHY provider-agnostic: the raw upstream service name (the provider) is mapped
|
|
12
|
-
* to a generic capability label here, server-side, so the browser never
|
|
13
|
-
* receives — and this open-source file never hard-codes — a provider or model
|
|
14
|
-
* name. See the capability-label heuristic below (keyed on generic op/resource
|
|
15
|
-
* tokens, never a brand).
|
|
16
|
-
*
|
|
17
|
-
* WHY the key stays server-side: identical rationale to run-state / run-spend —
|
|
18
|
-
* the Sapiom API key is a harness credential that must never reach the browser.
|
|
19
|
-
*/
|
|
20
|
-
import type { RunCall } from "../shared/types.js";
|
|
21
|
-
export type RunCallsResult = {
|
|
22
|
-
ok: true;
|
|
23
|
-
calls: RunCall[];
|
|
24
|
-
} | {
|
|
25
|
-
ok: false;
|
|
26
|
-
status: number;
|
|
27
|
-
error: string;
|
|
28
|
-
};
|
|
29
|
-
export interface RunTransactionsFetcherOpts {
|
|
30
|
-
apiKey: string | null;
|
|
31
|
-
/** Override the core base URL (resolved from env by default). Test seam. */
|
|
32
|
-
baseUrl?: string;
|
|
33
|
-
/** Injectable fetch implementation — defaults to global fetch. Test seam. */
|
|
34
|
-
fetchImpl?: typeof fetch;
|
|
35
|
-
}
|
|
36
|
-
export interface RunTransactionsFetcher {
|
|
37
|
-
fetch(executionId: string): Promise<RunCallsResult>;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Map a raw upstream operation/resource to a provider-agnostic capability
|
|
41
|
-
* label. Keyed ONLY on generic tokens (message / sandbox / search) and the
|
|
42
|
-
* operation verb — deliberately never on a provider/brand name, so this file
|
|
43
|
-
* stays clean of internal service names and the label is stable if the
|
|
44
|
-
* underlying provider is swapped. Unknown shapes fall back to the operation
|
|
45
|
-
* verb, which is itself generic ("create", "execute", …).
|
|
46
|
-
*/
|
|
47
|
-
export declare function capabilityLabel(op: string, resource: string): string;
|
|
48
|
-
/**
|
|
49
|
-
* Create a fetcher that resolves an execution's per-call cost breakdown from
|
|
50
|
-
* the core transactions endpoint. Like the spend fetcher, it is intentionally
|
|
51
|
-
* non-throwing: every error path returns a typed {@link RunCallsResult} with
|
|
52
|
-
* `ok: false` so the router forwards the status without a try/catch.
|
|
53
|
-
*
|
|
54
|
-
* Only BILLABLE calls are returned (active captured USD > 0) — the free
|
|
55
|
-
* `workflows` orchestration rows are dropped as noise. Each call is attributed
|
|
56
|
-
* to `metadata.workflowStepName ?? actionName` so the canvas can match it to a
|
|
57
|
-
* step node.
|
|
58
|
-
*/
|
|
59
|
-
export declare function createRunTransactionsFetcher(opts: RunTransactionsFetcherOpts): RunTransactionsFetcher;
|
|
60
|
-
//# sourceMappingURL=run-transactions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-transactions.d.ts","sourceRoot":"","sources":["../../src/core/run-transactions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,EAAE,CAAA;CAAE,GAC9B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACrD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQpE;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,0BAA0B,GAC/B,sBAAsB,CAyExB"}
|