strikethroo 3.0.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +18 -2
  2. package/dist/cli.js +28 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/serve/archive.d.ts +43 -0
  5. package/dist/serve/archive.d.ts.map +1 -0
  6. package/dist/serve/archive.js +124 -0
  7. package/dist/serve/archive.js.map +1 -0
  8. package/dist/serve/derivation.d.ts +78 -0
  9. package/dist/serve/derivation.d.ts.map +1 -0
  10. package/dist/serve/derivation.js +237 -0
  11. package/dist/serve/derivation.js.map +1 -0
  12. package/dist/serve/events.d.ts +41 -0
  13. package/dist/serve/events.d.ts.map +1 -0
  14. package/dist/serve/events.js +91 -0
  15. package/dist/serve/events.js.map +1 -0
  16. package/dist/serve/markdown.d.ts +75 -0
  17. package/dist/serve/markdown.d.ts.map +1 -0
  18. package/dist/serve/markdown.js +221 -0
  19. package/dist/serve/markdown.js.map +1 -0
  20. package/dist/serve/root.d.ts +48 -0
  21. package/dist/serve/root.d.ts.map +1 -0
  22. package/dist/serve/root.js +110 -0
  23. package/dist/serve/root.js.map +1 -0
  24. package/dist/serve/self-review.d.ts +70 -0
  25. package/dist/serve/self-review.d.ts.map +1 -0
  26. package/dist/serve/self-review.js +164 -0
  27. package/dist/serve/self-review.js.map +1 -0
  28. package/dist/serve/server.d.ts +70 -0
  29. package/dist/serve/server.d.ts.map +1 -0
  30. package/dist/serve/server.js +368 -0
  31. package/dist/serve/server.js.map +1 -0
  32. package/dist/serve/watcher.d.ts +32 -0
  33. package/dist/serve/watcher.d.ts.map +1 -0
  34. package/dist/serve/watcher.js +99 -0
  35. package/dist/serve/watcher.js.map +1 -0
  36. package/dist/serve/workspace-model.d.ts +80 -0
  37. package/dist/serve/workspace-model.d.ts.map +1 -0
  38. package/dist/serve/workspace-model.js +176 -0
  39. package/dist/serve/workspace-model.js.map +1 -0
  40. package/dist/skill-scripts/shared/frontmatter.d.ts +2 -0
  41. package/dist/skill-scripts/shared/frontmatter.d.ts.map +1 -0
  42. package/dist/skill-scripts/shared/frontmatter.js +37 -0
  43. package/dist/skill-scripts/shared/frontmatter.js.map +1 -0
  44. package/dist/skill-scripts/shared/plan-scan.d.ts +10 -0
  45. package/dist/skill-scripts/shared/plan-scan.d.ts.map +1 -0
  46. package/dist/skill-scripts/shared/plan-scan.js +91 -0
  47. package/dist/skill-scripts/shared/plan-scan.js.map +1 -0
  48. package/dist/skill-scripts/shared/root.d.ts +2 -0
  49. package/dist/skill-scripts/shared/root.d.ts.map +1 -0
  50. package/dist/skill-scripts/shared/root.js +104 -0
  51. package/dist/skill-scripts/shared/root.js.map +1 -0
  52. package/dist-web/assets/Fraunces-Italic-VariableFont_SOFT_WONK_opsz_wght-CuXX_kIb.ttf +0 -0
  53. package/dist-web/assets/Fraunces-VariableFont_SOFT_WONK_opsz_wght-DLWfSgHL.ttf +0 -0
  54. package/dist-web/assets/Outfit-VariableFont_wght-B6lQlNBM.ttf +0 -0
  55. package/dist-web/assets/SFMonoBold-DXHgGUoo.otf +0 -0
  56. package/dist-web/assets/SFMonoBoldItalic-CdGEImHi.otf +0 -0
  57. package/dist-web/assets/SFMonoHeavy-DL92f7rT.otf +0 -0
  58. package/dist-web/assets/SFMonoHeavyItalic-DnE00LFH.otf +0 -0
  59. package/dist-web/assets/SFMonoLight-CaMC1gg-.otf +0 -0
  60. package/dist-web/assets/SFMonoLightItalic-DG3Owzn7.otf +0 -0
  61. package/dist-web/assets/SFMonoMedium-ypRDCKSo.otf +0 -0
  62. package/dist-web/assets/SFMonoMediumItalic-DkJk-c-x.otf +0 -0
  63. package/dist-web/assets/SFMonoRegular-77Uh1IPk.otf +0 -0
  64. package/dist-web/assets/SFMonoRegularItalic-Cn01bACm.otf +0 -0
  65. package/dist-web/assets/SFMonoSemibold-CvhtzcyM.otf +0 -0
  66. package/dist-web/assets/SFMonoSemiboldItalic-D75NMH13.otf +0 -0
  67. package/dist-web/assets/arc-JkuhZ_PU.js +1 -0
  68. package/dist-web/assets/architectureDiagram-3BPJPVTR-DwCgGxAM.js +36 -0
  69. package/dist-web/assets/blockDiagram-GPEHLZMM-BCwcsKdi.js +132 -0
  70. package/dist-web/assets/c4Diagram-AAUBKEIU-Cm1cMFQT.js +10 -0
  71. package/dist-web/assets/channel-8UZd6AbH.js +1 -0
  72. package/dist-web/assets/chunk-2J33WTMH-BHDreu9q.js +1 -0
  73. package/dist-web/assets/chunk-4BX2VUAB-CvH1iMKB.js +1 -0
  74. package/dist-web/assets/chunk-55IACEB6-C02LfbKU.js +1 -0
  75. package/dist-web/assets/chunk-727SXJPM-BYDaeCJW.js +206 -0
  76. package/dist-web/assets/chunk-AQP2D5EJ-CW-sdA3M.js +231 -0
  77. package/dist-web/assets/chunk-FMBD7UC4-BE3hNKOn.js +15 -0
  78. package/dist-web/assets/chunk-ND2GUHAM-BosLJTdj.js +1 -0
  79. package/dist-web/assets/chunk-QZHKN3VN-Bf44xttm.js +1 -0
  80. package/dist-web/assets/classDiagram-4FO5ZUOK-D7aMNqbH.js +1 -0
  81. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-D7aMNqbH.js +1 -0
  82. package/dist-web/assets/cose-bilkent-S5V4N54A-BZS-xBec.js +1 -0
  83. package/dist-web/assets/cytoscape.esm-BHYC38rz.js +331 -0
  84. package/dist-web/assets/dagre-BM42HDAG-Be_HyeG7.js +4 -0
  85. package/dist-web/assets/defaultLocale-CrowFXzY.js +1 -0
  86. package/dist-web/assets/diagram-2AECGRRQ-BVt23uOL.js +43 -0
  87. package/dist-web/assets/diagram-5GNKFQAL-BF-OZRmP.js +10 -0
  88. package/dist-web/assets/diagram-KO2AKTUF-BvW-uYc2.js +3 -0
  89. package/dist-web/assets/diagram-LMA3HP47-CG2JR86G.js +24 -0
  90. package/dist-web/assets/diagram-OG6HWLK6-PZ5nR1AF.js +24 -0
  91. package/dist-web/assets/erDiagram-TEJ5UH35-D2GJWnIo.js +85 -0
  92. package/dist-web/assets/flowDiagram-I6XJVG4X-DGboZXQm.js +162 -0
  93. package/dist-web/assets/ganttDiagram-6RSMTGT7-IPOlSI7E.js +292 -0
  94. package/dist-web/assets/gitGraphDiagram-PVQCEYII-DO9E2tXK.js +106 -0
  95. package/dist-web/assets/graph-Dqkl27Ch.js +1 -0
  96. package/dist-web/assets/index-B8AJFM1Y.js +125 -0
  97. package/dist-web/assets/index-Bm5I46eC.css +1 -0
  98. package/dist-web/assets/infoDiagram-5YYISTIA-Clg2ubQz.js +2 -0
  99. package/dist-web/assets/init-Gi6I4Gst.js +1 -0
  100. package/dist-web/assets/ishikawaDiagram-YF4QCWOH-CDyYc7AV.js +70 -0
  101. package/dist-web/assets/journeyDiagram-JHISSGLW-CIz55Ff7.js +139 -0
  102. package/dist-web/assets/kanban-definition-UN3LZRKU-OzC_r8lI.js +89 -0
  103. package/dist-web/assets/katex-C5jXJg4s.js +257 -0
  104. package/dist-web/assets/layout-DO9UvE1P.js +1 -0
  105. package/dist-web/assets/linear-rdoP0iOK.js +1 -0
  106. package/dist-web/assets/mermaid.core-DOgS-uvF.js +307 -0
  107. package/dist-web/assets/mindmap-definition-RKZ34NQL-52nfuxxC.js +96 -0
  108. package/dist-web/assets/ordinal-Cboi1Yqb.js +1 -0
  109. package/dist-web/assets/pieDiagram-4H26LBE5-Drwd0WV7.js +30 -0
  110. package/dist-web/assets/quadrantDiagram-W4KKPZXB-DYJRwtFj.js +7 -0
  111. package/dist-web/assets/requirementDiagram-4Y6WPE33-HOL8fCwg.js +84 -0
  112. package/dist-web/assets/sankeyDiagram-5OEKKPKP-CyA-ExUk.js +40 -0
  113. package/dist-web/assets/sequenceDiagram-3UESZ5HK-C6Zr5XVd.js +162 -0
  114. package/dist-web/assets/stateDiagram-AJRCARHV-DSAZMis2.js +1 -0
  115. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-nDV4UH9R.js +1 -0
  116. package/dist-web/assets/timeline-definition-PNZ67QCA-CUPjxRlk.js +120 -0
  117. package/dist-web/assets/vennDiagram-CIIHVFJN-CtjMS4vh.js +34 -0
  118. package/dist-web/assets/wardley-L42UT6IY-Bk6PjBWD.js +173 -0
  119. package/dist-web/assets/wardleyDiagram-YWT4CUSO-Bjb8yHyW.js +78 -0
  120. package/dist-web/assets/xychartDiagram-2RQKCTM6-C3ukcEfW.js +7 -0
  121. package/dist-web/favicon.svg +13 -0
  122. package/dist-web/index.html +30 -0
  123. package/package.json +28 -8
  124. package/templates/harness/skills/st-full-workflow/SKILL.md +2 -2
  125. package/templates/harness/skills/st-generate-tasks/SKILL.md +2 -2
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ /**
3
+ * Self-review launch capability for `npx strikethroo serve`.
4
+ *
5
+ * The serve API is otherwise read-only. This module adds the single, narrowly
6
+ * scoped exception the SPA's "Launch self-review" button needs:
7
+ *
8
+ * - {@link isSelfReviewAvailable} — a PATH scan answering "is the `self-review`
9
+ * binary installed?", exposed to the SPA via `GET /api/capabilities` so the
10
+ * button only renders when launching can actually succeed;
11
+ * - {@link launchSelfReview} — validates a client-supplied plan path stays
12
+ * inside the workspace's `plans/` or `archive/` subtree, then spawns the
13
+ * binary detached (fire-and-forget, like the browser auto-open).
14
+ *
15
+ * Untrusted input (the path string) is never passed to the shell: `spawn` is
16
+ * invoked with an argv array, and the path is rejected before spawning unless it
17
+ * resolves to an existing file under `plans/` or `archive/`. Node built-ins only.
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.launchSelfReview = exports.resolveReviewPath = exports.isSelfReviewAvailable = exports.SELF_REVIEW_URL = exports.SELF_REVIEW_BINARY = void 0;
54
+ const fs = __importStar(require("fs"));
55
+ const path = __importStar(require("path"));
56
+ const child_process_1 = require("child_process");
57
+ /** The binary name looked up on PATH and spawned. */
58
+ exports.SELF_REVIEW_BINARY = 'self-review';
59
+ /** Where to obtain self-review; surfaced by the SPA when it is not installed. */
60
+ exports.SELF_REVIEW_URL = 'https://github.com/e0ipso/self-review';
61
+ /**
62
+ * Returns `true` when the `self-review` binary is found on the process `PATH`.
63
+ *
64
+ * A presence check, matching the literal "binary is in path" contract: each
65
+ * `PATH` entry is probed for a regular file named `self-review` (plus the
66
+ * `PATHEXT` variants on Windows). It deliberately does not execute the binary,
67
+ * so it cannot hang and has no side effects.
68
+ */
69
+ const isSelfReviewAvailable = (env = process.env) => {
70
+ const pathVar = env.PATH ?? env.Path ?? '';
71
+ if (!pathVar)
72
+ return false;
73
+ const exts = process.platform === 'win32'
74
+ ? (env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
75
+ : [''];
76
+ for (const dir of pathVar.split(path.delimiter).filter(Boolean)) {
77
+ for (const ext of exts) {
78
+ const candidate = path.join(dir, exports.SELF_REVIEW_BINARY + ext);
79
+ try {
80
+ if (fs.statSync(candidate).isFile())
81
+ return true;
82
+ }
83
+ catch {
84
+ // Not present in this directory; keep scanning.
85
+ }
86
+ }
87
+ }
88
+ return false;
89
+ };
90
+ exports.isSelfReviewAvailable = isSelfReviewAvailable;
91
+ /** Spawns the binary detached and unref'd so it outlives the request. */
92
+ const spawnDetached = (command, args) => {
93
+ const child = (0, child_process_1.spawn)(command, args, { stdio: 'ignore', detached: true });
94
+ child.on('error', () => {
95
+ /* a failed launch must not crash the server */
96
+ });
97
+ child.unref();
98
+ };
99
+ /**
100
+ * Resolves a client-supplied plan path to an absolute path, requiring it to be
101
+ * an existing file inside `<root>/plans` or `<root>/archive`. Returns the
102
+ * absolute path on success or a user-facing error otherwise.
103
+ *
104
+ * `root` is the absolute `.ai/strikethroo` directory; client paths are the
105
+ * workspace-relative form the SPA shows (e.g. `.ai/strikethroo/plans/NN--s/…`),
106
+ * so they are resolved against the project root (`root/../..`). Absolute client
107
+ * paths are accepted too — the containment check is what enforces safety.
108
+ */
109
+ const resolveReviewPath = (root, clientPath) => {
110
+ if (typeof clientPath !== 'string' || clientPath.trim() === '') {
111
+ return { error: 'A plan path is required.', status: 400 };
112
+ }
113
+ const projectRoot = path.resolve(root, '..', '..');
114
+ const resolved = path.resolve(projectRoot, clientPath);
115
+ const plansDir = path.join(root, 'plans');
116
+ const archiveDir = path.join(root, 'archive');
117
+ const within = (dir) => resolved === dir || resolved.startsWith(dir + path.sep);
118
+ if (!within(plansDir) && !within(archiveDir)) {
119
+ return {
120
+ error: 'Plan path must be inside the workspace plans/ or archive/ directory.',
121
+ status: 400,
122
+ };
123
+ }
124
+ try {
125
+ if (!fs.statSync(resolved).isFile()) {
126
+ return { error: 'Plan path does not point to a file.', status: 404 };
127
+ }
128
+ }
129
+ catch {
130
+ return { error: 'Plan file not found.', status: 404 };
131
+ }
132
+ return { absPath: resolved };
133
+ };
134
+ exports.resolveReviewPath = resolveReviewPath;
135
+ /**
136
+ * Validates `clientPath` and, if the binary is available, launches
137
+ * `self-review <absolutePath>` detached. Pure of HTTP concerns: returns the
138
+ * status/body the endpoint should send so it stays unit-testable.
139
+ */
140
+ const launchSelfReview = (root, clientPath, deps = {}) => {
141
+ const available = deps.available ?? exports.isSelfReviewAvailable;
142
+ if (!available()) {
143
+ return {
144
+ status: 409,
145
+ body: { ok: false, error: 'self-review is not installed on PATH.' },
146
+ };
147
+ }
148
+ const resolved = (0, exports.resolveReviewPath)(root, clientPath);
149
+ if ('error' in resolved) {
150
+ return { status: resolved.status, body: { ok: false, error: resolved.error } };
151
+ }
152
+ try {
153
+ (deps.spawnDetached ?? spawnDetached)(exports.SELF_REVIEW_BINARY, [resolved.absPath]);
154
+ }
155
+ catch (err) {
156
+ return {
157
+ status: 500,
158
+ body: { ok: false, error: `Failed to launch self-review: ${err.message}` },
159
+ };
160
+ }
161
+ return { status: 200, body: { ok: true } };
162
+ };
163
+ exports.launchSelfReview = launchSelfReview;
164
+ //# sourceMappingURL=self-review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-review.js","sourceRoot":"","sources":["../../src/serve/self-review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Core HTTP server for `npx strikethroo serve`.
3
+ *
4
+ * Composes three concerns over the workspace model (`workspace-model.ts`):
5
+ * - static hosting of the prebuilt SPA, with directory-traversal protection
6
+ * and an `index.html` fallback so client-side routing resolves;
7
+ * - a read-only JSON API (`/api/plans`, `/api/plans/:id`, `/api/config`,
8
+ * `/api/capabilities`) read fresh per request so responses reflect current
9
+ * disk state;
10
+ * - `POST /api/plans/:id/archive`, the one sanctioned workspace mutation:
11
+ * it moves a `done` plan's directory from `plans/` to `archive/`;
12
+ * - the non-read endpoint `POST /api/self-review`, which launches the
13
+ * external self-review binary for a validated in-workspace plan path;
14
+ * - platform-aware browser auto-open on startup.
15
+ *
16
+ * The SSE change stream (`GET /api/events`) is added by a separate module that
17
+ * hooks into the `apiHandlers` extension point below, so this module stays free
18
+ * of file-watching concerns. Node built-ins only — no runtime dependency, no
19
+ * Vite/React/Tailwind imports.
20
+ */
21
+ import * as http from 'http';
22
+ import { EventsHub } from './events';
23
+ /** Options for {@link startServer}. */
24
+ export interface ServeOptions {
25
+ /** Absolute path of the `.ai/strikethroo` workspace directory to serve. */
26
+ root: string;
27
+ /** Port to bind. `0` selects an ephemeral free port (used by tests). */
28
+ port: number;
29
+ /** When `false`, do not open the browser on startup. */
30
+ open: boolean;
31
+ /** Absolute path of the prebuilt SPA assets directory. */
32
+ assetsDir: string;
33
+ /**
34
+ * Optional extra API route handlers, tried before the built-in read endpoints
35
+ * and the built-in SSE stream. A handler returns `true` once it has taken
36
+ * ownership of the response, `false` to fall through.
37
+ */
38
+ apiHandlers?: ApiHandler[];
39
+ /**
40
+ * Debounce quiet window (ms) for the change watcher. Defaults to the watcher
41
+ * module's default; exposed mainly so tests can tighten it.
42
+ */
43
+ debounceMs?: number;
44
+ }
45
+ /** A pluggable `/api/*` handler. Returns `true` if it handled the request. */
46
+ export type ApiHandler = (req: http.IncomingMessage, res: http.ServerResponse, ctx: {
47
+ root: string;
48
+ pathname: string;
49
+ }) => boolean;
50
+ /** Result of a successful {@link startServer} call. */
51
+ export interface ServeHandle {
52
+ url: string;
53
+ server: http.Server;
54
+ port: number;
55
+ /** The SSE change-stream hub backing `GET /api/events`. */
56
+ events: EventsHub;
57
+ }
58
+ /**
59
+ * Resolves the default prebuilt SPA assets directory relative to the installed
60
+ * package. The compiled server lives at `<pkg>/dist/serve/server.js`, so the
61
+ * package root is two levels up and the Plan 82 Vite output is `<pkg>/dist-web`.
62
+ * Plan 94 finalizes the shipped asset path; this is the local-dev default.
63
+ */
64
+ export declare const defaultAssetsDir: () => string;
65
+ /**
66
+ * Creates and starts the HTTP server, resolving with the bound URL once it is
67
+ * listening. Rejects only on a genuine listen/bind error.
68
+ */
69
+ export declare const startServer: (opts: ServeOptions) => Promise<ServeHandle>;
70
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/serve/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAM7B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAIrC,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,OAAO,CAAC;IACd,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,CACvB,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KACpC,OAAO,CAAC;AAEb,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,EAAE,SAAS,CAAC;CACnB;AA2BD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAO,MAAyD,CAAC;AAgP9F;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,YAAY,KAAG,OAAO,CAAC,WAAW,CA4CnE,CAAC"}
@@ -0,0 +1,368 @@
1
+ "use strict";
2
+ /**
3
+ * Core HTTP server for `npx strikethroo serve`.
4
+ *
5
+ * Composes three concerns over the workspace model (`workspace-model.ts`):
6
+ * - static hosting of the prebuilt SPA, with directory-traversal protection
7
+ * and an `index.html` fallback so client-side routing resolves;
8
+ * - a read-only JSON API (`/api/plans`, `/api/plans/:id`, `/api/config`,
9
+ * `/api/capabilities`) read fresh per request so responses reflect current
10
+ * disk state;
11
+ * - `POST /api/plans/:id/archive`, the one sanctioned workspace mutation:
12
+ * it moves a `done` plan's directory from `plans/` to `archive/`;
13
+ * - the non-read endpoint `POST /api/self-review`, which launches the
14
+ * external self-review binary for a validated in-workspace plan path;
15
+ * - platform-aware browser auto-open on startup.
16
+ *
17
+ * The SSE change stream (`GET /api/events`) is added by a separate module that
18
+ * hooks into the `apiHandlers` extension point below, so this module stays free
19
+ * of file-watching concerns. Node built-ins only — no runtime dependency, no
20
+ * Vite/React/Tailwind imports.
21
+ */
22
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ var desc = Object.getOwnPropertyDescriptor(m, k);
25
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
26
+ desc = { enumerable: true, get: function() { return m[k]; } };
27
+ }
28
+ Object.defineProperty(o, k2, desc);
29
+ }) : (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ o[k2] = m[k];
32
+ }));
33
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
34
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
35
+ }) : function(o, v) {
36
+ o["default"] = v;
37
+ });
38
+ var __importStar = (this && this.__importStar) || (function () {
39
+ var ownKeys = function(o) {
40
+ ownKeys = Object.getOwnPropertyNames || function (o) {
41
+ var ar = [];
42
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
43
+ return ar;
44
+ };
45
+ return ownKeys(o);
46
+ };
47
+ return function (mod) {
48
+ if (mod && mod.__esModule) return mod;
49
+ var result = {};
50
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
51
+ __setModuleDefault(result, mod);
52
+ return result;
53
+ };
54
+ })();
55
+ Object.defineProperty(exports, "__esModule", { value: true });
56
+ exports.startServer = exports.defaultAssetsDir = void 0;
57
+ const http = __importStar(require("http"));
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const child_process_1 = require("child_process");
61
+ const url_1 = require("url");
62
+ const workspace_model_1 = require("./workspace-model");
63
+ const events_1 = require("./events");
64
+ const self_review_1 = require("./self-review");
65
+ const archive_1 = require("./archive");
66
+ const MIME_TYPES = {
67
+ '.html': 'text/html; charset=utf-8',
68
+ '.js': 'text/javascript; charset=utf-8',
69
+ '.mjs': 'text/javascript; charset=utf-8',
70
+ '.css': 'text/css; charset=utf-8',
71
+ '.json': 'application/json; charset=utf-8',
72
+ '.svg': 'image/svg+xml',
73
+ '.png': 'image/png',
74
+ '.jpg': 'image/jpeg',
75
+ '.jpeg': 'image/jpeg',
76
+ '.gif': 'image/gif',
77
+ '.ico': 'image/x-icon',
78
+ '.webp': 'image/webp',
79
+ '.woff': 'font/woff',
80
+ '.woff2': 'font/woff2',
81
+ '.ttf': 'font/ttf',
82
+ '.otf': 'font/otf',
83
+ '.map': 'application/json; charset=utf-8',
84
+ '.txt': 'text/plain; charset=utf-8',
85
+ '.wasm': 'application/wasm',
86
+ };
87
+ const mimeFor = (filePath) => MIME_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream';
88
+ /**
89
+ * Resolves the default prebuilt SPA assets directory relative to the installed
90
+ * package. The compiled server lives at `<pkg>/dist/serve/server.js`, so the
91
+ * package root is two levels up and the Plan 82 Vite output is `<pkg>/dist-web`.
92
+ * Plan 94 finalizes the shipped asset path; this is the local-dev default.
93
+ */
94
+ const defaultAssetsDir = () => path.resolve(__dirname, '..', '..', 'dist-web');
95
+ exports.defaultAssetsDir = defaultAssetsDir;
96
+ const sendJson = (res, status, body) => {
97
+ const payload = JSON.stringify(body);
98
+ res.writeHead(status, {
99
+ 'Content-Type': 'application/json; charset=utf-8',
100
+ 'Content-Length': Buffer.byteLength(payload),
101
+ });
102
+ res.end(payload);
103
+ };
104
+ /** Parses `/api/plans/:id` -> numeric id, or `null` if the segment is not numeric. */
105
+ const parsePlanId = (pathname) => {
106
+ const match = /^\/api\/plans\/([^/]+)\/?$/.exec(pathname);
107
+ if (!match || match[1] === undefined)
108
+ return null;
109
+ const id = Number(decodeURIComponent(match[1]));
110
+ return Number.isInteger(id) && id >= 0 ? id : null;
111
+ };
112
+ /** Parses `/api/plans/:id/archive` -> numeric id, or `null` if not numeric. */
113
+ const parseArchivePlanId = (pathname) => {
114
+ const match = /^\/api\/plans\/([^/]+)\/archive\/?$/.exec(pathname);
115
+ if (!match || match[1] === undefined)
116
+ return null;
117
+ const id = Number(decodeURIComponent(match[1]));
118
+ return Number.isInteger(id) && id >= 0 ? id : null;
119
+ };
120
+ /**
121
+ * Handles `POST /api/plans/:id/archive`: delegates entirely to the {@link
122
+ * archivePlan} operation and maps its discriminated result to status codes
123
+ * without duplicating validation. Returns `true` once it owns the response.
124
+ */
125
+ const handleArchive = (res, root, pathname) => {
126
+ const id = parseArchivePlanId(pathname);
127
+ if (id === null) {
128
+ sendJson(res, 400, { error: 'Invalid plan id.' });
129
+ return true;
130
+ }
131
+ (0, archive_1.archivePlan)(root, id)
132
+ .then(result => {
133
+ if (result.ok) {
134
+ sendJson(res, 200, result.plan);
135
+ return;
136
+ }
137
+ switch (result.reason) {
138
+ case 'not-found':
139
+ sendJson(res, 404, { error: result.message });
140
+ return;
141
+ case 'not-done':
142
+ case 'already-archived':
143
+ case 'destination-exists':
144
+ sendJson(res, 409, { error: result.message });
145
+ return;
146
+ default:
147
+ // fs-error: a safe, fixed message — never leak internals.
148
+ sendJson(res, 500, { error: 'Failed to archive plan.' });
149
+ }
150
+ })
151
+ .catch(() => {
152
+ sendJson(res, 500, { error: 'Failed to archive plan.' });
153
+ });
154
+ return true;
155
+ };
156
+ /** Handles the built-in read-only API. Returns `true` when the request matched. */
157
+ const handleApi = (res, root, pathname) => {
158
+ try {
159
+ if (pathname === '/api/plans' || pathname === '/api/plans/') {
160
+ sendJson(res, 200, (0, workspace_model_1.getWorkspaceModel)(root).plans);
161
+ return true;
162
+ }
163
+ if (pathname === '/api/config' || pathname === '/api/config/') {
164
+ sendJson(res, 200, (0, workspace_model_1.getConfig)(root));
165
+ return true;
166
+ }
167
+ if (pathname === '/api/capabilities' || pathname === '/api/capabilities/') {
168
+ sendJson(res, 200, { selfReview: (0, self_review_1.isSelfReviewAvailable)() });
169
+ return true;
170
+ }
171
+ if (/^\/api\/plans\/[^/]+\/?$/.test(pathname)) {
172
+ const id = parsePlanId(pathname);
173
+ if (id === null) {
174
+ sendJson(res, 404, { error: 'Invalid plan id' });
175
+ return true;
176
+ }
177
+ const detail = (0, workspace_model_1.getPlanDetail)(root, id);
178
+ if (!detail) {
179
+ sendJson(res, 404, { error: `Plan ${id} not found` });
180
+ return true;
181
+ }
182
+ sendJson(res, 200, detail);
183
+ return true;
184
+ }
185
+ return false;
186
+ }
187
+ catch (err) {
188
+ // Malformed workspace data: concise message, never a raw stack trace.
189
+ sendJson(res, 500, {
190
+ error: `Failed to read workspace: ${err instanceof Error ? err.message : String(err)}`,
191
+ });
192
+ return true;
193
+ }
194
+ };
195
+ /** Max accepted request-body size (bytes). The self-review body is tiny JSON. */
196
+ const MAX_BODY_BYTES = 64 * 1024;
197
+ /** Reads and JSON-parses a request body, rejecting oversized or malformed input. */
198
+ const readJsonBody = (req) => new Promise((resolve, reject) => {
199
+ let size = 0;
200
+ const chunks = [];
201
+ req.on('data', (chunk) => {
202
+ size += chunk.length;
203
+ if (size > MAX_BODY_BYTES) {
204
+ reject(new Error('Request body too large'));
205
+ req.destroy();
206
+ return;
207
+ }
208
+ chunks.push(chunk);
209
+ });
210
+ req.on('end', () => {
211
+ const raw = Buffer.concat(chunks).toString('utf8').trim();
212
+ if (raw === '') {
213
+ resolve({});
214
+ return;
215
+ }
216
+ try {
217
+ resolve(JSON.parse(raw));
218
+ }
219
+ catch {
220
+ reject(new Error('Invalid JSON body'));
221
+ }
222
+ });
223
+ req.on('error', reject);
224
+ });
225
+ /**
226
+ * Handles `POST /api/self-review`: launches the external self-review binary for
227
+ * the requested plan path. Returns `true` once it has owned the response.
228
+ */
229
+ const handleSelfReview = (req, res, root) => {
230
+ readJsonBody(req)
231
+ .then(body => {
232
+ const clientPath = body.path;
233
+ const result = (0, self_review_1.launchSelfReview)(root, typeof clientPath === 'string' ? clientPath : '');
234
+ sendJson(res, result.status, result.body);
235
+ })
236
+ .catch((err) => {
237
+ sendJson(res, 400, { ok: false, error: err.message });
238
+ });
239
+ return true;
240
+ };
241
+ /** Streams a static file with an appropriate content type. */
242
+ const sendFile = (res, filePath) => {
243
+ res.writeHead(200, { 'Content-Type': mimeFor(filePath) });
244
+ const stream = fs.createReadStream(filePath);
245
+ stream.on('error', () => {
246
+ if (!res.headersSent)
247
+ res.writeHead(500);
248
+ res.end();
249
+ });
250
+ stream.pipe(res);
251
+ };
252
+ const ASSETS_MISSING_MESSAGE = 'Web assets not found. Build the web app first (e.g. `npm run build:web`).';
253
+ /** Serves the SPA: a real file under the assets root, else the index fallback. */
254
+ const handleStatic = (res, assetsDir, pathname) => {
255
+ const indexFile = path.join(assetsDir, 'index.html');
256
+ // Resolve the request path under assetsDir and guard against traversal.
257
+ const relative = decodeURIComponent(pathname).replace(/^\/+/, '');
258
+ const resolved = path.resolve(assetsDir, relative);
259
+ const assetsRoot = path.resolve(assetsDir);
260
+ const withinRoot = resolved === assetsRoot || resolved.startsWith(assetsRoot + path.sep);
261
+ if (!withinRoot) {
262
+ res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' });
263
+ res.end('Forbidden');
264
+ return;
265
+ }
266
+ // A real, non-directory file under the assets root: serve it directly.
267
+ try {
268
+ if (relative !== '' && fs.statSync(resolved).isFile()) {
269
+ sendFile(res, resolved);
270
+ return;
271
+ }
272
+ }
273
+ catch {
274
+ // Not a file; fall through to the SPA index fallback.
275
+ }
276
+ // SPA fallback: serve index.html for any non-file route so client routing works.
277
+ try {
278
+ if (fs.statSync(indexFile).isFile()) {
279
+ sendFile(res, indexFile);
280
+ return;
281
+ }
282
+ }
283
+ catch {
284
+ // index.html missing -> assets not built.
285
+ }
286
+ res.writeHead(503, { 'Content-Type': 'text/plain; charset=utf-8' });
287
+ res.end(ASSETS_MISSING_MESSAGE);
288
+ };
289
+ /** Opens the default browser at `url`. Failures are logged, never fatal. */
290
+ const openBrowser = (url) => {
291
+ try {
292
+ const platform = process.platform;
293
+ let command;
294
+ let args;
295
+ if (platform === 'darwin') {
296
+ command = 'open';
297
+ args = [url];
298
+ }
299
+ else if (platform === 'win32') {
300
+ command = 'cmd';
301
+ args = ['/c', 'start', '', url];
302
+ }
303
+ else {
304
+ command = 'xdg-open';
305
+ args = [url];
306
+ }
307
+ const child = (0, child_process_1.spawn)(command, args, { stdio: 'ignore', detached: true });
308
+ child.on('error', () => {
309
+ /* a failed open must not stop the server */
310
+ });
311
+ child.unref();
312
+ }
313
+ catch {
314
+ /* non-fatal */
315
+ }
316
+ };
317
+ /**
318
+ * Creates and starts the HTTP server, resolving with the bound URL once it is
319
+ * listening. Rejects only on a genuine listen/bind error.
320
+ */
321
+ const startServer = (opts) => {
322
+ const extraHandlers = opts.apiHandlers ?? [];
323
+ const events = new events_1.EventsHub(opts.root, opts.debounceMs);
324
+ const server = http.createServer((req, res) => {
325
+ const pathname = new url_1.URL(req.url ?? '/', 'http://localhost').pathname;
326
+ if (pathname.startsWith('/api/')) {
327
+ for (const handler of extraHandlers) {
328
+ if (handler(req, res, { root: opts.root, pathname }))
329
+ return;
330
+ }
331
+ // The one sanctioned workspace mutation: archive a done plan.
332
+ if (req.method === 'POST' && /^\/api\/plans\/[^/]+\/archive\/?$/.test(pathname)) {
333
+ if (handleArchive(res, opts.root, pathname))
334
+ return;
335
+ }
336
+ // The one write-ish endpoint: launch the external self-review binary.
337
+ if (req.method === 'POST' && /^\/api\/self-review\/?$/.test(pathname)) {
338
+ if (handleSelfReview(req, res, opts.root))
339
+ return;
340
+ }
341
+ // Built-in SSE change stream.
342
+ if (events.apiHandler(req, res, { pathname }))
343
+ return;
344
+ if (handleApi(res, opts.root, pathname))
345
+ return;
346
+ sendJson(res, 404, { error: `Unknown API route: ${pathname}` });
347
+ return;
348
+ }
349
+ handleStatic(res, opts.assetsDir, pathname);
350
+ });
351
+ // Tear the watcher and open client streams down with the server.
352
+ server.on('close', () => events.close());
353
+ return new Promise((resolve, reject) => {
354
+ server.once('error', reject);
355
+ server.listen(opts.port, () => {
356
+ server.removeListener('error', reject);
357
+ const address = server.address();
358
+ const boundPort = typeof address === 'object' && address ? address.port : opts.port;
359
+ const url = `http://localhost:${boundPort}`;
360
+ events.start();
361
+ if (opts.open)
362
+ openBrowser(url);
363
+ resolve({ url, server, port: boundPort, events });
364
+ });
365
+ });
366
+ };
367
+ exports.startServer = startServer;
368
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/serve/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Workspace file watcher for `npx strikethroo serve`.
3
+ *
4
+ * Centralizes change detection behind one small interface so the underlying
5
+ * mechanism can be swapped later without touching call sites. Today it uses a
6
+ * single recursive `fs.watch` over the workspace, feeding a debounce so a burst
7
+ * of rapid writes (editor saves, assistant edits) collapses into one `onChange`
8
+ * call after a short quiet window.
9
+ *
10
+ * Per the plan's portability risk, recursive `fs.watch` may be unreliable on
11
+ * some Linux configurations. If that proves true on a target platform, a tiny
12
+ * watcher dependency can be substituted here as a documented last resort without
13
+ * changing how the server consumes this module. No such dependency is added now.
14
+ *
15
+ * Node built-ins only.
16
+ */
17
+ /** A running watcher. Call `close()` to stop watching and clear timers. */
18
+ export interface WorkspaceWatcher {
19
+ close(): void;
20
+ }
21
+ /** Default debounce quiet window, in milliseconds. */
22
+ export declare const DEFAULT_DEBOUNCE_MS = 300;
23
+ /**
24
+ * Starts watching `workspaceDir` recursively. `onChange` is invoked once per
25
+ * burst, after `debounceMs` of quiet. Returns a handle whose `close()` stops the
26
+ * watch and clears any pending debounce timer.
27
+ *
28
+ * Watch errors are swallowed (the stream stays usable); they must not crash the
29
+ * server. If `fs.watch` cannot start at all, a no-op watcher is returned.
30
+ */
31
+ export declare const createWorkspaceWatcher: (workspaceDir: string, onChange: () => void, debounceMs?: number) => WorkspaceWatcher;
32
+ //# sourceMappingURL=watcher.d.ts.map