@sapiom/harness 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -43
  3. package/dist/cli/auth.d.ts +26 -0
  4. package/dist/cli/auth.d.ts.map +1 -0
  5. package/dist/cli/auth.js +39 -0
  6. package/dist/cli/auth.js.map +1 -0
  7. package/dist/cli/bin.d.ts +3 -0
  8. package/dist/cli/bin.d.ts.map +1 -0
  9. package/dist/cli/bin.js +163 -0
  10. package/dist/cli/bin.js.map +1 -0
  11. package/dist/cli/consent.d.ts +15 -0
  12. package/dist/cli/consent.d.ts.map +1 -0
  13. package/dist/cli/consent.js +81 -0
  14. package/dist/cli/consent.js.map +1 -0
  15. package/dist/cli/doctor.d.ts +27 -0
  16. package/dist/cli/doctor.d.ts.map +1 -0
  17. package/dist/cli/doctor.js +74 -0
  18. package/dist/cli/doctor.js.map +1 -0
  19. package/dist/cli/machine-id.d.ts +19 -0
  20. package/dist/cli/machine-id.d.ts.map +1 -0
  21. package/dist/cli/machine-id.js +75 -0
  22. package/dist/cli/machine-id.js.map +1 -0
  23. package/dist/cli/paths.d.ts +3 -0
  24. package/dist/cli/paths.d.ts.map +1 -0
  25. package/dist/cli/paths.js +11 -0
  26. package/dist/cli/paths.js.map +1 -0
  27. package/dist/cli/settings.d.ts +26 -0
  28. package/dist/cli/settings.d.ts.map +1 -0
  29. package/dist/cli/settings.js +123 -0
  30. package/dist/cli/settings.js.map +1 -0
  31. package/dist/core/adapters/claude-code.d.ts +41 -0
  32. package/dist/core/adapters/claude-code.d.ts.map +1 -0
  33. package/dist/core/adapters/claude-code.js +238 -0
  34. package/dist/core/adapters/claude-code.js.map +1 -0
  35. package/dist/core/adapters/codex.d.ts +59 -0
  36. package/dist/core/adapters/codex.d.ts.map +1 -0
  37. package/dist/core/adapters/codex.js +319 -0
  38. package/dist/core/adapters/codex.js.map +1 -0
  39. package/dist/core/canvas-body.d.ts +45 -0
  40. package/dist/core/canvas-body.d.ts.map +1 -0
  41. package/dist/core/canvas-body.js +94 -0
  42. package/dist/core/canvas-body.js.map +1 -0
  43. package/dist/core/canvas-cache.d.ts +23 -0
  44. package/dist/core/canvas-cache.d.ts.map +1 -0
  45. package/dist/core/canvas-cache.js +64 -0
  46. package/dist/core/canvas-cache.js.map +1 -0
  47. package/dist/core/canvas-enrich.d.ts +106 -0
  48. package/dist/core/canvas-enrich.d.ts.map +1 -0
  49. package/dist/core/canvas-enrich.js +178 -0
  50. package/dist/core/canvas-enrich.js.map +1 -0
  51. package/dist/core/canvas-enrichment.d.ts +144 -0
  52. package/dist/core/canvas-enrichment.d.ts.map +1 -0
  53. package/dist/core/canvas-enrichment.js +192 -0
  54. package/dist/core/canvas-enrichment.js.map +1 -0
  55. package/dist/core/canvas-graph.d.ts +71 -0
  56. package/dist/core/canvas-graph.d.ts.map +1 -0
  57. package/dist/core/canvas-graph.js +111 -0
  58. package/dist/core/canvas-graph.js.map +1 -0
  59. package/dist/core/canvas-index-classify.d.ts +41 -0
  60. package/dist/core/canvas-index-classify.d.ts.map +1 -0
  61. package/dist/core/canvas-index-classify.js +63 -0
  62. package/dist/core/canvas-index-classify.js.map +1 -0
  63. package/dist/core/canvas-interconnections.d.ts +24 -0
  64. package/dist/core/canvas-interconnections.d.ts.map +1 -0
  65. package/dist/core/canvas-interconnections.js +96 -0
  66. package/dist/core/canvas-interconnections.js.map +1 -0
  67. package/dist/core/canvas-manifest-check.d.ts +15 -0
  68. package/dist/core/canvas-manifest-check.d.ts.map +1 -0
  69. package/dist/core/canvas-manifest-check.js +90 -0
  70. package/dist/core/canvas-manifest-check.js.map +1 -0
  71. package/dist/core/canvas-render.d.ts +73 -0
  72. package/dist/core/canvas-render.d.ts.map +1 -0
  73. package/dist/core/canvas-render.js +145 -0
  74. package/dist/core/canvas-render.js.map +1 -0
  75. package/dist/core/canvas-svg.d.ts +50 -0
  76. package/dist/core/canvas-svg.d.ts.map +1 -0
  77. package/dist/core/canvas-svg.js +240 -0
  78. package/dist/core/canvas-svg.js.map +1 -0
  79. package/dist/core/canvas-template.d.ts +43 -0
  80. package/dist/core/canvas-template.d.ts.map +1 -0
  81. package/dist/core/canvas-template.js +365 -0
  82. package/dist/core/canvas-template.js.map +1 -0
  83. package/dist/core/canvas-watcher.d.ts +25 -0
  84. package/dist/core/canvas-watcher.d.ts.map +1 -0
  85. package/dist/core/canvas-watcher.js +144 -0
  86. package/dist/core/canvas-watcher.js.map +1 -0
  87. package/dist/core/collector/batcher.d.ts +57 -0
  88. package/dist/core/collector/batcher.d.ts.map +1 -0
  89. package/dist/core/collector/batcher.js +143 -0
  90. package/dist/core/collector/batcher.js.map +1 -0
  91. package/dist/core/collector/codex-tailer.d.ts +85 -0
  92. package/dist/core/collector/codex-tailer.d.ts.map +1 -0
  93. package/dist/core/collector/codex-tailer.js +270 -0
  94. package/dist/core/collector/codex-tailer.js.map +1 -0
  95. package/dist/core/collector/normalizer.d.ts +33 -0
  96. package/dist/core/collector/normalizer.d.ts.map +1 -0
  97. package/dist/core/collector/normalizer.js +94 -0
  98. package/dist/core/collector/normalizer.js.map +1 -0
  99. package/dist/core/collector/seq.d.ts +14 -0
  100. package/dist/core/collector/seq.d.ts.map +1 -0
  101. package/dist/core/collector/seq.js +20 -0
  102. package/dist/core/collector/seq.js.map +1 -0
  103. package/dist/core/collector/store.d.ts +16 -0
  104. package/dist/core/collector/store.d.ts.map +1 -0
  105. package/dist/core/collector/store.js +30 -0
  106. package/dist/core/collector/store.js.map +1 -0
  107. package/dist/core/collector/transcript.d.ts +38 -0
  108. package/dist/core/collector/transcript.d.ts.map +1 -0
  109. package/dist/core/collector/transcript.js +110 -0
  110. package/dist/core/collector/transcript.js.map +1 -0
  111. package/dist/core/event-bus.d.ts +9 -0
  112. package/dist/core/event-bus.d.ts.map +1 -0
  113. package/dist/core/event-bus.js +25 -0
  114. package/dist/core/event-bus.js.map +1 -0
  115. package/dist/core/example-seed.d.ts +34 -0
  116. package/dist/core/example-seed.d.ts.map +1 -0
  117. package/dist/core/example-seed.js +277 -0
  118. package/dist/core/example-seed.js.map +1 -0
  119. package/dist/core/inject/claude-settings.d.ts +34 -0
  120. package/dist/core/inject/claude-settings.d.ts.map +1 -0
  121. package/dist/core/inject/claude-settings.js +152 -0
  122. package/dist/core/inject/claude-settings.js.map +1 -0
  123. package/dist/core/inject/mcp-config.d.ts +26 -0
  124. package/dist/core/inject/mcp-config.d.ts.map +1 -0
  125. package/dist/core/inject/mcp-config.js +39 -0
  126. package/dist/core/inject/mcp-config.js.map +1 -0
  127. package/dist/core/inject/retention.d.ts +66 -0
  128. package/dist/core/inject/retention.d.ts.map +1 -0
  129. package/dist/core/inject/retention.js +119 -0
  130. package/dist/core/inject/retention.js.map +1 -0
  131. package/dist/core/inject/system-prompt.d.ts +11 -0
  132. package/dist/core/inject/system-prompt.d.ts.map +1 -0
  133. package/dist/core/inject/system-prompt.js +22 -0
  134. package/dist/core/inject/system-prompt.js.map +1 -0
  135. package/dist/core/macro-runner.d.ts +36 -0
  136. package/dist/core/macro-runner.d.ts.map +1 -0
  137. package/dist/core/macro-runner.js +66 -0
  138. package/dist/core/macro-runner.js.map +1 -0
  139. package/dist/core/macros.d.ts +10 -0
  140. package/dist/core/macros.d.ts.map +1 -0
  141. package/dist/core/macros.js +59 -0
  142. package/dist/core/macros.js.map +1 -0
  143. package/dist/core/path-safety.d.ts +33 -0
  144. package/dist/core/path-safety.d.ts.map +1 -0
  145. package/dist/core/path-safety.js +63 -0
  146. package/dist/core/path-safety.js.map +1 -0
  147. package/dist/core/paths.d.ts +27 -0
  148. package/dist/core/paths.d.ts.map +1 -0
  149. package/dist/core/paths.js +43 -0
  150. package/dist/core/paths.js.map +1 -0
  151. package/dist/core/port-detector.d.ts +69 -0
  152. package/dist/core/port-detector.d.ts.map +1 -0
  153. package/dist/core/port-detector.js +121 -0
  154. package/dist/core/port-detector.js.map +1 -0
  155. package/dist/core/session-manager.d.ts +275 -0
  156. package/dist/core/session-manager.d.ts.map +1 -0
  157. package/dist/core/session-manager.js +729 -0
  158. package/dist/core/session-manager.js.map +1 -0
  159. package/dist/core/task-manager.d.ts +138 -0
  160. package/dist/core/task-manager.d.ts.map +1 -0
  161. package/dist/core/task-manager.js +286 -0
  162. package/dist/core/task-manager.js.map +1 -0
  163. package/dist/core/task-stream.d.ts +32 -0
  164. package/dist/core/task-stream.d.ts.map +1 -0
  165. package/dist/core/task-stream.js +87 -0
  166. package/dist/core/task-stream.js.map +1 -0
  167. package/dist/core/workflow-registry.d.ts +36 -0
  168. package/dist/core/workflow-registry.d.ts.map +1 -0
  169. package/dist/core/workflow-registry.js +220 -0
  170. package/dist/core/workflow-registry.js.map +1 -0
  171. package/dist/core/workspace-context.d.ts +43 -0
  172. package/dist/core/workspace-context.d.ts.map +1 -0
  173. package/dist/core/workspace-context.js +108 -0
  174. package/dist/core/workspace-context.js.map +1 -0
  175. package/dist/core/workspace-watcher.d.ts +26 -0
  176. package/dist/core/workspace-watcher.d.ts.map +1 -0
  177. package/dist/core/workspace-watcher.js +174 -0
  178. package/dist/core/workspace-watcher.js.map +1 -0
  179. package/dist/index.d.ts +6 -0
  180. package/dist/index.d.ts.map +1 -0
  181. package/dist/index.js +6 -0
  182. package/dist/index.js.map +1 -0
  183. package/dist/profiles/canvas-guidelines.d.ts +20 -0
  184. package/dist/profiles/canvas-guidelines.d.ts.map +1 -0
  185. package/dist/profiles/canvas-guidelines.js +48 -0
  186. package/dist/profiles/canvas-guidelines.js.map +1 -0
  187. package/dist/profiles/default.d.ts +10 -0
  188. package/dist/profiles/default.d.ts.map +1 -0
  189. package/dist/profiles/default.js +70 -0
  190. package/dist/profiles/default.js.map +1 -0
  191. package/dist/server/auth.d.ts +9 -0
  192. package/dist/server/auth.d.ts.map +1 -0
  193. package/dist/server/auth.js +28 -0
  194. package/dist/server/auth.js.map +1 -0
  195. package/dist/server/canvas-render.d.ts +18 -0
  196. package/dist/server/canvas-render.d.ts.map +1 -0
  197. package/dist/server/canvas-render.js +24 -0
  198. package/dist/server/canvas-render.js.map +1 -0
  199. package/dist/server/canvas.d.ts +12 -0
  200. package/dist/server/canvas.d.ts.map +1 -0
  201. package/dist/server/canvas.js +174 -0
  202. package/dist/server/canvas.js.map +1 -0
  203. package/dist/server/events-ws.d.ts +11 -0
  204. package/dist/server/events-ws.d.ts.map +1 -0
  205. package/dist/server/events-ws.js +24 -0
  206. package/dist/server/events-ws.js.map +1 -0
  207. package/dist/server/fs.d.ts +4 -0
  208. package/dist/server/fs.d.ts.map +1 -0
  209. package/dist/server/fs.js +88 -0
  210. package/dist/server/fs.js.map +1 -0
  211. package/dist/server/index.d.ts +83 -0
  212. package/dist/server/index.d.ts.map +1 -0
  213. package/dist/server/index.js +0 -0
  214. package/dist/server/index.js.map +1 -0
  215. package/dist/server/ingest.d.ts +74 -0
  216. package/dist/server/ingest.d.ts.map +1 -0
  217. package/dist/server/ingest.js +74 -0
  218. package/dist/server/ingest.js.map +1 -0
  219. package/dist/server/macros.d.ts +32 -0
  220. package/dist/server/macros.d.ts.map +1 -0
  221. package/dist/server/macros.js +76 -0
  222. package/dist/server/macros.js.map +1 -0
  223. package/dist/server/rest.d.ts +75 -0
  224. package/dist/server/rest.d.ts.map +1 -0
  225. package/dist/server/rest.js +235 -0
  226. package/dist/server/rest.js.map +1 -0
  227. package/dist/server/static.d.ts +8 -0
  228. package/dist/server/static.d.ts.map +1 -0
  229. package/dist/server/static.js +36 -0
  230. package/dist/server/static.js.map +1 -0
  231. package/dist/server/terminal-ws.d.ts +9 -0
  232. package/dist/server/terminal-ws.d.ts.map +1 -0
  233. package/dist/server/terminal-ws.js +59 -0
  234. package/dist/server/terminal-ws.js.map +1 -0
  235. package/dist/server/ws-router.d.ts +16 -0
  236. package/dist/server/ws-router.d.ts.map +1 -0
  237. package/dist/server/ws-router.js +26 -0
  238. package/dist/server/ws-router.js.map +1 -0
  239. package/dist/shared/types.d.ts +485 -0
  240. package/dist/shared/types.d.ts.map +1 -0
  241. package/dist/shared/types.js +80 -0
  242. package/dist/shared/types.js.map +1 -0
  243. package/dist/web/assets/index-B9R4TzH7.css +32 -0
  244. package/dist/web/assets/index-Dh97QAgq.js +192 -0
  245. package/dist/web/index.html +27 -0
  246. package/package.json +84 -7
@@ -0,0 +1,59 @@
1
+ export const DEFAULT_MACROS = [
2
+ {
3
+ id: "run_local",
4
+ label: "Run local",
5
+ icon: "Play",
6
+ requiresWorkflow: true,
7
+ action: {
8
+ kind: "inject",
9
+ submit: true,
10
+ text: "cd {{workflow.path}} && sapiom agents run --target local",
11
+ },
12
+ },
13
+ {
14
+ id: "deploy",
15
+ label: "Deploy",
16
+ icon: "Cloud",
17
+ requiresWorkflow: true,
18
+ action: {
19
+ kind: "inject",
20
+ submit: true,
21
+ text: "cd {{workflow.path}} && sapiom agents deploy",
22
+ },
23
+ },
24
+ {
25
+ id: "prod_run",
26
+ label: "Prod run",
27
+ icon: "Zap",
28
+ requiresWorkflow: true,
29
+ action: {
30
+ kind: "inject",
31
+ submit: true,
32
+ text: "cd {{workflow.path}} && sapiom agents run --target prod",
33
+ },
34
+ },
35
+ {
36
+ id: "open_prod",
37
+ label: "Open prod",
38
+ icon: "ExternalLink",
39
+ requiresWorkflow: true,
40
+ action: {
41
+ kind: "open-url",
42
+ url: "https://app.sapiom.ai/workflows/{{workflow.definitionId}}",
43
+ },
44
+ },
45
+ {
46
+ // One-click force refresh of the bound workflow's canvas: re-runs the
47
+ // deterministic, zero-LLM structure render (core/canvas-render.ts —
48
+ // instant, cache-invalidated) AND re-spawns the bounded AI enrichment
49
+ // task (core/canvas-enrich.ts, a headless background run that returns
50
+ // validated JSON annotations, never HTML) — all server-side, without
51
+ // touching the session's pty. A cheap no-op when the session is unbound.
52
+ id: "visualize",
53
+ label: "Visualize",
54
+ icon: "Sparkles",
55
+ requiresWorkflow: false,
56
+ action: { kind: "render-canvas" },
57
+ },
58
+ ];
59
+ //# sourceMappingURL=macros.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macros.js","sourceRoot":"","sources":["../../src/core/macros.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC;QACE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,MAAM;QACZ,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,0DAA0D;SACjE;KACF;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,OAAO;QACb,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,8CAA8C;SACrD;KACF;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,UAAU;QACjB,IAAI,EAAE,KAAK;QACX,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,yDAAyD;SAChE;KACF;IACD;QACE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,cAAc;QACpB,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE;YACN,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,2DAA2D;SACjE;KACF;IACD;QACE,sEAAsE;QACtE,oEAAoE;QACpE,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,yEAAyE;QACzE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,UAAU;QAChB,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE;KAClC;CACF,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The absolute path of `name` as a direct child of `root`, or null when
3
+ * `name` is anything other than one plain path segment — separators, ".",
4
+ * "..", absolute paths, or anything else that could resolve outside `root`.
5
+ * (Extracted from the generated-dir retention policy, which relies on it to
6
+ * refuse deleting anything but a direct child of its own root.)
7
+ */
8
+ export declare function childPath(root: string, name: string): string | null;
9
+ /**
10
+ * Resolves `candidate` (relative to `root`, or absolute) and confirms the
11
+ * result stays within `root`. Returns the resolved absolute path, or null if
12
+ * it escapes — whether via a `..` climb, an absolute path, or a different
13
+ * drive. `root` itself counts as inside.
14
+ *
15
+ * Containment is decided with `path.relative`: the relative path from the
16
+ * resolved root to the resolved candidate begins with `..` (or is itself
17
+ * absolute) exactly when the candidate lies outside the root — the standard,
18
+ * analyzer-recognized shape for a path-traversal barrier.
19
+ */
20
+ export declare function resolveWithinRoot(root: string, candidate: string): string | null;
21
+ /**
22
+ * True when `p` contains an upward-traversal (`..`) path segment. Segment
23
+ * aware: a name that merely *contains* ".." (e.g. "a..b") is not a match.
24
+ *
25
+ * Use as a defense-in-depth assertion on an already-resolved absolute path
26
+ * for endpoints that legitimately accept an arbitrary absolute path (a
27
+ * filesystem browser, an explicit "connect this directory") and so have no
28
+ * single root to confine to. A fully resolved path never retains a `..`
29
+ * segment, so this rejects nothing legitimate — it makes the "normalization
30
+ * left no traversal behind" guarantee explicit and local to the sink.
31
+ */
32
+ export declare function hasTraversalSegment(p: string): boolean;
33
+ //# sourceMappingURL=path-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../../src/core/path-safety.ts"],"names":[],"mappings":"AAeA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEtD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shared filesystem path-safety guards.
3
+ *
4
+ * The harness runs on the user's own machine on the user's own inputs
5
+ * (workflow directories, a session cwd, a canvas sub-path), so the threat
6
+ * here isn't a remote attacker crossing a privilege boundary — it's ordinary
7
+ * footguns: a `..` segment, an absolute path where a relative one was meant,
8
+ * or a symlink that quietly resolves a user-derived value outside the
9
+ * directory a feature intended to confine it to. Each helper turns "a path
10
+ * that might escape" into "a path proven to stay put, or null", so callers
11
+ * reject rather than silently reach outside — and each guard is written in a
12
+ * form static analysis recognizes as neutralizing path-traversal taint.
13
+ */
14
+ import * as path from "node:path";
15
+ /**
16
+ * The absolute path of `name` as a direct child of `root`, or null when
17
+ * `name` is anything other than one plain path segment — separators, ".",
18
+ * "..", absolute paths, or anything else that could resolve outside `root`.
19
+ * (Extracted from the generated-dir retention policy, which relies on it to
20
+ * refuse deleting anything but a direct child of its own root.)
21
+ */
22
+ export function childPath(root, name) {
23
+ if (!name || name === "." || name === "..")
24
+ return null;
25
+ const resolved = path.resolve(root, name);
26
+ if (path.dirname(resolved) !== root || path.basename(resolved) !== name)
27
+ return null;
28
+ return resolved;
29
+ }
30
+ /**
31
+ * Resolves `candidate` (relative to `root`, or absolute) and confirms the
32
+ * result stays within `root`. Returns the resolved absolute path, or null if
33
+ * it escapes — whether via a `..` climb, an absolute path, or a different
34
+ * drive. `root` itself counts as inside.
35
+ *
36
+ * Containment is decided with `path.relative`: the relative path from the
37
+ * resolved root to the resolved candidate begins with `..` (or is itself
38
+ * absolute) exactly when the candidate lies outside the root — the standard,
39
+ * analyzer-recognized shape for a path-traversal barrier.
40
+ */
41
+ export function resolveWithinRoot(root, candidate) {
42
+ const resolvedRoot = path.resolve(root);
43
+ const resolved = path.resolve(resolvedRoot, candidate);
44
+ const relative = path.relative(resolvedRoot, resolved);
45
+ if (relative.startsWith("..") || path.isAbsolute(relative))
46
+ return null;
47
+ return resolved;
48
+ }
49
+ /**
50
+ * True when `p` contains an upward-traversal (`..`) path segment. Segment
51
+ * aware: a name that merely *contains* ".." (e.g. "a..b") is not a match.
52
+ *
53
+ * Use as a defense-in-depth assertion on an already-resolved absolute path
54
+ * for endpoints that legitimately accept an arbitrary absolute path (a
55
+ * filesystem browser, an explicit "connect this directory") and so have no
56
+ * single root to confine to. A fully resolved path never retains a `..`
57
+ * segment, so this rejects nothing legitimate — it makes the "normalization
58
+ * left no traversal behind" guarantee explicit and local to the sink.
59
+ */
60
+ export function hasTraversalSegment(p) {
61
+ return /(?:^|[/\\])\.\.(?:[/\\]|$)/.test(p);
62
+ }
63
+ //# sourceMappingURL=path-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-safety.js","sourceRoot":"","sources":["../../src/core/path-safety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IAClD,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACrF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,SAAiB;IAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,OAAO,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Small path helper shared across the harness's core modules (session
3
+ * manager, adapters, analytics store): the contract (`src/shared/types.ts`)
4
+ * expresses well-known paths with a leading `~`.
5
+ */
6
+ /** Expand a leading `~` (home directory) in a path, then resolve to absolute. */
7
+ export declare function expandHome(path: string): string;
8
+ /** Absolute locations of every piece of persistent harness state, all rooted
9
+ * under one directory. See resolveStatePaths(). */
10
+ export interface HarnessStatePaths {
11
+ root: string;
12
+ machineId: string;
13
+ sessions: string;
14
+ workflows: string;
15
+ events: string;
16
+ settings: string;
17
+ generated: string;
18
+ sampleProject: string;
19
+ }
20
+ /**
21
+ * Resolves the full set of persistent-state locations under a single root.
22
+ * Defaults to the real HARNESS_HOME (`~/.sapiom/harness`) — the CLI's
23
+ * behavior is unchanged — while tests and scripted checks pass a scratch
24
+ * directory so a server boot can never touch the developer's real state.
25
+ */
26
+ export declare function resolveStatePaths(stateRoot?: string): HarnessStatePaths;
27
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;oDACoD;AACpD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAUD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAYvE"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Small path helper shared across the harness's core modules (session
3
+ * manager, adapters, analytics store): the contract (`src/shared/types.ts`)
4
+ * expresses well-known paths with a leading `~`.
5
+ */
6
+ import { homedir } from "node:os";
7
+ import { join, resolve } from "node:path";
8
+ import { HARNESS_HOME, HARNESS_PATHS } from "../shared/types.js";
9
+ /** Expand a leading `~` (home directory) in a path, then resolve to absolute. */
10
+ export function expandHome(path) {
11
+ if (path === "~")
12
+ return homedir();
13
+ if (path.startsWith("~/"))
14
+ return resolve(homedir(), path.slice(2));
15
+ return resolve(path);
16
+ }
17
+ /** The path of `entry` relative to HARNESS_HOME — HARNESS_PATHS expresses
18
+ * every well-known file as `${HARNESS_HOME}/<name>`, and deriving the name
19
+ * here (instead of repeating it) keeps HARNESS_PATHS the single source of
20
+ * truth for what the files are called. */
21
+ function relativeToHome(entry) {
22
+ return entry.slice(HARNESS_HOME.length + 1);
23
+ }
24
+ /**
25
+ * Resolves the full set of persistent-state locations under a single root.
26
+ * Defaults to the real HARNESS_HOME (`~/.sapiom/harness`) — the CLI's
27
+ * behavior is unchanged — while tests and scripted checks pass a scratch
28
+ * directory so a server boot can never touch the developer's real state.
29
+ */
30
+ export function resolveStatePaths(stateRoot) {
31
+ const root = expandHome(stateRoot ?? HARNESS_HOME);
32
+ return {
33
+ root,
34
+ machineId: join(root, relativeToHome(HARNESS_PATHS.machineId)),
35
+ sessions: join(root, relativeToHome(HARNESS_PATHS.sessions)),
36
+ workflows: join(root, relativeToHome(HARNESS_PATHS.workflows)),
37
+ events: join(root, relativeToHome(HARNESS_PATHS.events)),
38
+ settings: join(root, relativeToHome(HARNESS_PATHS.settings)),
39
+ generated: join(root, relativeToHome(HARNESS_PATHS.generated)),
40
+ sampleProject: join(root, relativeToHome(HARNESS_PATHS.sampleProject)),
41
+ };
42
+ }
43
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEjE,iFAAiF;AACjF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,OAAO,EAAE,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAeD;;;2CAG2C;AAC3C,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAkB;IAClD,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,IAAI,YAAY,CAAC,CAAC;IACnD,OAAO;QACL,IAAI;QACJ,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9D,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC5D,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9D,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACxD,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC5D,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9D,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;KACvE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Dev-server port detection (workstream W5's backend slice).
3
+ *
4
+ * The Preview pane's "Preview :PORT" chip (CanvasPane.tsx) lights up from a
5
+ * `port.detected` bus message. This is the detector behind that signal: feed
6
+ * it raw text — pty output chunks as they arrive, or a `tool.call` event's
7
+ * command/output — per session, and it calls `onPort` once per distinct
8
+ * (session, port) `localhost:<port>` reference it finds. The integrator
9
+ * wires `feed()` to both the pty output stream and the analytics ingest
10
+ * pipeline, and broadcasts `{type: "port.detected", harnessSessionId, port,
11
+ * url}` on /ws/events from `onPort`.
12
+ */
13
+ export interface PortDetectorDeps {
14
+ onPort(harnessSessionId: string, port: number, url: string): void;
15
+ /** Ports considered the harness's own infrastructure — a match against one
16
+ * of these is silently dropped (not even recorded as "seen") rather than
17
+ * broadcast as a discovery. Known ports at construction time; more can be
18
+ * added later via `addExcludedPort` (e.g. an ephemeral `port: 0` only
19
+ * resolves to its real bound port after the server starts listening,
20
+ * which happens after this detector is already constructed). */
21
+ excludedPorts?: Iterable<number>;
22
+ }
23
+ /**
24
+ * Extracts the port a URL string actually resolves to, including the
25
+ * protocol's implicit default when none is written out explicitly (`new
26
+ * URL(...).port` is `""` in that case, not the real port) — used to exclude
27
+ * the analytics collector's own port from detection.
28
+ */
29
+ export declare function portFromUrl(rawUrl: string): number | null;
30
+ /**
31
+ * Stateful, streaming-safe: a match that touches the very end of the
32
+ * buffered text is held back rather than finalized, in case more digits are
33
+ * still arriving in the next chunk (so this is safe to feed byte-for-byte
34
+ * pty output, not just complete lines). Call `flush()` after feeding a
35
+ * discrete, already-complete string (e.g. one tool.call event's output) —
36
+ * see its doc comment for why that's necessary. Dedupes per (session, port)
37
+ * so a port that keeps appearing in output only ever fires `onPort` once —
38
+ * permanently for the session's lifetime (not a TTL): `reset()` is what
39
+ * clears it, on session exit, so a reused session id starts clean.
40
+ */
41
+ export declare class PortDetector {
42
+ private readonly deps;
43
+ private readonly buffers;
44
+ private readonly seenPorts;
45
+ private readonly excludedPorts;
46
+ constructor(deps: PortDetectorDeps);
47
+ /** Exclude a port discovered after construction — e.g. the harness's own
48
+ * actual bound port, which is only known once the HTTP server resolves
49
+ * its `listen()` call (relevant for an ephemeral `port: 0`). */
50
+ addExcludedPort(port: number): void;
51
+ feed(chunk: string, harnessSessionId: string): void;
52
+ private tryEmit;
53
+ /**
54
+ * Finalizes whatever's currently held back for a session, immediately.
55
+ * `feed()` deliberately withholds a match touching the end of the buffered
56
+ * text in case more digits are still arriving — correct for a live pty
57
+ * byte stream, but wrong for a single discrete, complete string (e.g. one
58
+ * `tool.call` event's already-finished output) where no "next chunk" is
59
+ * ever coming: without this, a port at the very end of that string (a
60
+ * common shape — "...started server on http://localhost:5544") would be
61
+ * held pending forever and `onPort` would never fire. Call this right
62
+ * after `feed()` whenever the fed text is known to be complete.
63
+ */
64
+ flush(harnessSessionId: string): void;
65
+ /** Drops buffered/dedupe state for a session — call on session exit/kill so
66
+ * a reused session id (e.g. a fresh session in the same slot) starts clean. */
67
+ reset(harnessSessionId: string): void;
68
+ }
69
+ //# sourceMappingURL=port-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port-detector.d.ts","sourceRoot":"","sources":["../../src/core/port-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAClE;;;;;qEAKiE;IACjE,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQzD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IAKX,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;gBAEf,IAAI,EAAE,gBAAgB;IAInD;;qEAEiE;IACjE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAInC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAqBnD,OAAO,CAAC,OAAO;IAiBf;;;;;;;;;;OAUG;IACH,KAAK,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAYrC;oFACgF;IAChF,KAAK,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;CAItC"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Dev-server port detection (workstream W5's backend slice).
3
+ *
4
+ * The Preview pane's "Preview :PORT" chip (CanvasPane.tsx) lights up from a
5
+ * `port.detected` bus message. This is the detector behind that signal: feed
6
+ * it raw text — pty output chunks as they arrive, or a `tool.call` event's
7
+ * command/output — per session, and it calls `onPort` once per distinct
8
+ * (session, port) `localhost:<port>` reference it finds. The integrator
9
+ * wires `feed()` to both the pty output stream and the analytics ingest
10
+ * pipeline, and broadcasts `{type: "port.detected", harnessSessionId, port,
11
+ * url}` on /ws/events from `onPort`.
12
+ */
13
+ const PORT_PATTERN_SOURCE = String.raw `(?:https?://)?localhost:(\d{2,5})\b`;
14
+ /** Long enough to hold the literal "localhost:" anchor split across a chunk
15
+ * boundary, plus a few digits either side. */
16
+ const TAIL_SAFETY = 24;
17
+ /**
18
+ * Extracts the port a URL string actually resolves to, including the
19
+ * protocol's implicit default when none is written out explicitly (`new
20
+ * URL(...).port` is `""` in that case, not the real port) — used to exclude
21
+ * the analytics collector's own port from detection.
22
+ */
23
+ export function portFromUrl(rawUrl) {
24
+ try {
25
+ const url = new URL(rawUrl);
26
+ if (url.port)
27
+ return Number(url.port);
28
+ return url.protocol === "https:" ? 443 : 80;
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ /**
35
+ * Stateful, streaming-safe: a match that touches the very end of the
36
+ * buffered text is held back rather than finalized, in case more digits are
37
+ * still arriving in the next chunk (so this is safe to feed byte-for-byte
38
+ * pty output, not just complete lines). Call `flush()` after feeding a
39
+ * discrete, already-complete string (e.g. one tool.call event's output) —
40
+ * see its doc comment for why that's necessary. Dedupes per (session, port)
41
+ * so a port that keeps appearing in output only ever fires `onPort` once —
42
+ * permanently for the session's lifetime (not a TTL): `reset()` is what
43
+ * clears it, on session exit, so a reused session id starts clean.
44
+ */
45
+ export class PortDetector {
46
+ constructor(deps) {
47
+ this.deps = deps;
48
+ this.buffers = new Map();
49
+ this.seenPorts = new Map();
50
+ this.excludedPorts = new Set(deps.excludedPorts ?? []);
51
+ }
52
+ /** Exclude a port discovered after construction — e.g. the harness's own
53
+ * actual bound port, which is only known once the HTTP server resolves
54
+ * its `listen()` call (relevant for an ephemeral `port: 0`). */
55
+ addExcludedPort(port) {
56
+ this.excludedPorts.add(port);
57
+ }
58
+ feed(chunk, harnessSessionId) {
59
+ const combined = (this.buffers.get(harnessSessionId) ?? "") + chunk;
60
+ const regex = new RegExp(PORT_PATTERN_SOURCE, "g");
61
+ let match;
62
+ let pendingFrom = null;
63
+ while ((match = regex.exec(combined))) {
64
+ const end = match.index + match[0].length;
65
+ if (end === combined.length) {
66
+ // Could still be growing (more digits due next chunk) — hold it back.
67
+ pendingFrom = match.index;
68
+ break;
69
+ }
70
+ this.tryEmit(harnessSessionId, Number(match[1]));
71
+ }
72
+ const retainFrom = pendingFrom ?? Math.max(0, combined.length - TAIL_SAFETY);
73
+ this.buffers.set(harnessSessionId, combined.slice(retainFrom));
74
+ }
75
+ tryEmit(harnessSessionId, port) {
76
+ if (!Number.isInteger(port) || port <= 0 || port > 65535)
77
+ return;
78
+ // Never even recorded as "seen" — an excluded port isn't a real
79
+ // discovery, so it shouldn't consume per-session dedupe state either.
80
+ if (this.excludedPorts.has(port))
81
+ return;
82
+ let ports = this.seenPorts.get(harnessSessionId);
83
+ if (!ports) {
84
+ ports = new Set();
85
+ this.seenPorts.set(harnessSessionId, ports);
86
+ }
87
+ if (ports.has(port))
88
+ return;
89
+ ports.add(port);
90
+ this.deps.onPort(harnessSessionId, port, `http://localhost:${port}`);
91
+ }
92
+ /**
93
+ * Finalizes whatever's currently held back for a session, immediately.
94
+ * `feed()` deliberately withholds a match touching the end of the buffered
95
+ * text in case more digits are still arriving — correct for a live pty
96
+ * byte stream, but wrong for a single discrete, complete string (e.g. one
97
+ * `tool.call` event's already-finished output) where no "next chunk" is
98
+ * ever coming: without this, a port at the very end of that string (a
99
+ * common shape — "...started server on http://localhost:5544") would be
100
+ * held pending forever and `onPort` would never fire. Call this right
101
+ * after `feed()` whenever the fed text is known to be complete.
102
+ */
103
+ flush(harnessSessionId) {
104
+ const pending = this.buffers.get(harnessSessionId);
105
+ if (!pending)
106
+ return;
107
+ const regex = new RegExp(PORT_PATTERN_SOURCE, "g");
108
+ let match;
109
+ while ((match = regex.exec(pending))) {
110
+ this.tryEmit(harnessSessionId, Number(match[1]));
111
+ }
112
+ this.buffers.delete(harnessSessionId);
113
+ }
114
+ /** Drops buffered/dedupe state for a session — call on session exit/kill so
115
+ * a reused session id (e.g. a fresh session in the same slot) starts clean. */
116
+ reset(harnessSessionId) {
117
+ this.buffers.delete(harnessSessionId);
118
+ this.seenPorts.delete(harnessSessionId);
119
+ }
120
+ }
121
+ //# sourceMappingURL=port-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port-detector.js","sourceRoot":"","sources":["../../src/core/port-detector.ts"],"names":[],"mappings":"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"}