@sapiom/harness 0.1.4 → 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.
Files changed (98) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/cli/bin.js +24 -7
  3. package/dist/cli/bin.js.map +1 -1
  4. package/dist/cli/consent.js +1 -1
  5. package/dist/core/adapters/adapter.d.ts +2 -2
  6. package/dist/core/api-key-provider.d.ts +79 -0
  7. package/dist/core/api-key-provider.d.ts.map +1 -0
  8. package/dist/core/api-key-provider.js +91 -0
  9. package/dist/core/api-key-provider.js.map +1 -0
  10. package/dist/core/canvas-run-state.d.ts +99 -0
  11. package/dist/core/canvas-run-state.d.ts.map +1 -0
  12. package/dist/core/canvas-run-state.js +191 -0
  13. package/dist/core/canvas-run-state.js.map +1 -0
  14. package/dist/core/canvas-svg.d.ts.map +1 -1
  15. package/dist/core/canvas-svg.js +28 -8
  16. package/dist/core/canvas-svg.js.map +1 -1
  17. package/dist/core/canvas-template.d.ts +4 -3
  18. package/dist/core/canvas-template.d.ts.map +1 -1
  19. package/dist/core/canvas-template.js +75 -3
  20. package/dist/core/canvas-template.js.map +1 -1
  21. package/dist/core/definition-slug-resolver.d.ts +46 -0
  22. package/dist/core/definition-slug-resolver.d.ts.map +1 -0
  23. package/dist/core/definition-slug-resolver.js +110 -0
  24. package/dist/core/definition-slug-resolver.js.map +1 -0
  25. package/dist/core/execution-detector.d.ts +59 -0
  26. package/dist/core/execution-detector.d.ts.map +1 -0
  27. package/dist/core/execution-detector.js +110 -0
  28. package/dist/core/execution-detector.js.map +1 -0
  29. package/dist/core/inject/mcp-config.d.ts.map +1 -1
  30. package/dist/core/inject/mcp-config.js +9 -1
  31. package/dist/core/inject/mcp-config.js.map +1 -1
  32. package/dist/core/render-local-run.d.ts +82 -0
  33. package/dist/core/render-local-run.d.ts.map +1 -0
  34. package/dist/core/render-local-run.js +105 -0
  35. package/dist/core/render-local-run.js.map +1 -0
  36. package/dist/core/render-log-slice.d.ts +30 -0
  37. package/dist/core/render-log-slice.d.ts.map +1 -0
  38. package/dist/core/render-log-slice.js +49 -0
  39. package/dist/core/render-log-slice.js.map +1 -0
  40. package/dist/core/render-run-state.d.ts +9 -0
  41. package/dist/core/render-run-state.d.ts.map +1 -0
  42. package/dist/core/render-run-state.js +120 -0
  43. package/dist/core/render-run-state.js.map +1 -0
  44. package/dist/core/run-local-bootstrap.d.ts +93 -0
  45. package/dist/core/run-local-bootstrap.d.ts.map +1 -0
  46. package/dist/core/run-local-bootstrap.js +155 -0
  47. package/dist/core/run-local-bootstrap.js.map +1 -0
  48. package/dist/core/run-state.d.ts +60 -0
  49. package/dist/core/run-state.d.ts.map +1 -0
  50. package/dist/core/run-state.js +122 -0
  51. package/dist/core/run-state.js.map +1 -0
  52. package/dist/core/stub-feedback.d.ts +52 -0
  53. package/dist/core/stub-feedback.d.ts.map +1 -0
  54. package/dist/core/stub-feedback.js +33 -0
  55. package/dist/core/stub-feedback.js.map +1 -0
  56. package/dist/core/workflow-registry.d.ts +12 -1
  57. package/dist/core/workflow-registry.d.ts.map +1 -1
  58. package/dist/core/workflow-registry.js +2 -0
  59. package/dist/core/workflow-registry.js.map +1 -1
  60. package/dist/server/actions.d.ts +139 -0
  61. package/dist/server/actions.d.ts.map +1 -0
  62. package/dist/server/actions.js +430 -0
  63. package/dist/server/actions.js.map +1 -0
  64. package/dist/server/auth-routes.d.ts +92 -0
  65. package/dist/server/auth-routes.d.ts.map +1 -0
  66. package/dist/server/auth-routes.js +151 -0
  67. package/dist/server/auth-routes.js.map +1 -0
  68. package/dist/server/index.d.ts.map +1 -1
  69. package/dist/server/index.js +0 -0
  70. package/dist/server/index.js.map +1 -1
  71. package/dist/server/rest.d.ts +5 -0
  72. package/dist/server/rest.d.ts.map +1 -1
  73. package/dist/server/rest.js +36 -12
  74. package/dist/server/rest.js.map +1 -1
  75. package/dist/server/runs.d.ts +34 -0
  76. package/dist/server/runs.d.ts.map +1 -0
  77. package/dist/server/runs.js +59 -0
  78. package/dist/server/runs.js.map +1 -0
  79. package/dist/server/static.d.ts +15 -1
  80. package/dist/server/static.d.ts.map +1 -1
  81. package/dist/server/static.js +54 -4
  82. package/dist/server/static.js.map +1 -1
  83. package/dist/shared/types.d.ts +136 -2
  84. package/dist/shared/types.d.ts.map +1 -1
  85. package/dist/shared/types.js.map +1 -1
  86. package/dist/web/assets/index-DCtyI4VW.css +32 -0
  87. package/dist/web/assets/index-WSw5RvYR.js +445 -0
  88. package/dist/web/canvas/sess-boot/index.html +305 -0
  89. package/dist/web/index.html +4 -9
  90. package/dist/web/welcome-hero-dark.png +0 -0
  91. package/dist/web/welcome-hero-light.png +0 -0
  92. package/package.json +6 -3
  93. package/dist/server/skills.d.ts +0 -51
  94. package/dist/server/skills.d.ts.map +0 -1
  95. package/dist/server/skills.js +0 -312
  96. package/dist/server/skills.js.map +0 -1
  97. package/dist/web/assets/index-BhKGSTTT.js +0 -214
  98. package/dist/web/assets/index-DRTrwTku.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 &amp; 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>
@@ -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 Harness</title>
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-BhKGSTTT.js"></script>
22
- <link rel="stylesheet" crossorigin href="/assets/index-DRTrwTku.css">
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.4",
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.4",
51
- "@sapiom/agent-core": "^0.9.5",
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",
@@ -1,51 +0,0 @@
1
- /**
2
- * Skills listing server — backs GET /api/skills and GET /api/skills/:id.
3
- *
4
- * Two skill sources are merged:
5
- * 1. Installed @sapiom packages: scans node_modules/@sapiom/{pkg}/skills/SKILL.md
6
- * (gracefully empty when no package ships skills yet).
7
- * 2. User's own ~/.claude/skills/{id}/SKILL.md (same convention Claude Code
8
- * uses for project-specific skills).
9
- *
10
- * Path-traversal safety: skill ids are slug-shaped identifiers (letters,
11
- * digits, hyphens, underscores) — any id that fails the SAFE_SLUG regex is
12
- * rejected with a 404 before any filesystem access.
13
- *
14
- * The list endpoint (GET /api/skills) does a full directory walk across both
15
- * roots. The detail endpoint (GET /api/skills/:id) resolves in O(1) by
16
- * constructing candidate paths directly from the slug (= directory name),
17
- * without re-walking the tree.
18
- *
19
- * Markdown is served as-is — the SPA renders it client-side (no HTML
20
- * injection here). The frontmatter block is stripped before delivery so the
21
- * rendered view shows only the body.
22
- */
23
- import { Router } from "express";
24
- export interface SkillMeta {
25
- /** Stable identifier: directory name of the skill folder. */
26
- id: string;
27
- /** Human name from the `name:` frontmatter field. Falls back to the id. */
28
- name: string;
29
- /** One-line description from the `description:` frontmatter field. */
30
- description: string;
31
- /** Logical source group — shown in the panel's section header. */
32
- source: "package" | "user";
33
- }
34
- export interface SkillDetail extends SkillMeta {
35
- /** The full SKILL.md body with the frontmatter block stripped. */
36
- body: string;
37
- }
38
- export interface SkillsRouterOptions {
39
- /** Override the node_modules root for testing. */
40
- nodeModulesRoot?: string;
41
- /** Override the user skills root (default: ~/.claude/skills). */
42
- userSkillsRoot?: string;
43
- /**
44
- * Include the user's personal ~/.claude/skills in the LIST response.
45
- * Off by default — the panel shows only Sapiom package skills so a
46
- * developer's own skills don't clutter the product's skill list.
47
- */
48
- showUserSkills?: boolean;
49
- }
50
- export declare function createSkillsRouter(options?: SkillsRouterOptions): Router;
51
- //# sourceMappingURL=skills.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/server/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAOH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,MAAM,WAAW,SAAS;IACxB,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;CACd;AAoKD,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAkI5E"}