plan-assistant 1.3.4 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/_app/immutable/chunks/{BJs6I7qC.js → 10ZXP06Q.js} +1 -1
- package/build/client/_app/immutable/chunks/{ChJyFfSj.js → 1eL0zcBS.js} +1 -1
- package/build/client/_app/immutable/chunks/74GYr5Jz.js +3 -0
- package/build/client/_app/immutable/chunks/{B2fFwvWw.js → B-HTmPy1.js} +1 -1
- package/build/client/_app/immutable/chunks/{RIGBV7Ip.js → B2hVYoEJ.js} +1 -1
- package/build/client/_app/immutable/chunks/{DVXd8vwC.js → B628eJGB.js} +1 -1
- package/build/client/_app/immutable/chunks/{CpucjHJL.js → B80ongyB.js} +1 -1
- package/build/client/_app/immutable/chunks/{BXCtgyOT.js → B8FhIQG1.js} +1 -1
- package/build/client/_app/immutable/chunks/{DoIz7SoC.js → BCgSWfby.js} +1 -1
- package/build/client/_app/immutable/chunks/{B-25Ue8o.js → BFl4ofgi.js} +4 -4
- package/build/client/_app/immutable/chunks/{DK1zqZrn.js → BIKGXasq.js} +1 -1
- package/build/client/_app/immutable/chunks/{DlbZX2EZ.js → BNv9nR-i.js} +1 -1
- package/build/client/_app/immutable/chunks/{CeIvvAVV.js → BOjlNXC_.js} +1 -1
- package/build/client/_app/immutable/chunks/{a5eoTq-5.js → BRBMkxQw.js} +1 -1
- package/build/client/_app/immutable/chunks/{_2rlQOMN.js → BT3ImTYg.js} +1 -1
- package/build/client/_app/immutable/chunks/{BJxAjYQA.js → BUu_XsOv.js} +1 -1
- package/build/client/_app/immutable/chunks/{B9WViv7H.js → BVq6bo_n.js} +1 -1
- package/build/client/_app/immutable/chunks/{DteI5BLO.js → BZQqs639.js} +1 -1
- package/build/client/_app/immutable/chunks/{nFhgrnCT.js → BZTb6H6F.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dl-inTne.js → BrjAEg64.js} +1 -1
- package/build/client/_app/immutable/chunks/{DSIQf7-a.js → Bt3MWn7w.js} +26 -26
- package/build/client/_app/immutable/chunks/{Dnz2r_pS.js → BvzznBFq.js} +1 -1
- package/build/client/_app/immutable/chunks/{DshYkidx.js → C4gCk_ik.js} +1 -1
- package/build/client/_app/immutable/chunks/{2wZlHG5k.js → C5wtj4WQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dvli4fnO.js → CEqaJ7Yk.js} +1 -1
- package/build/client/_app/immutable/chunks/{3E04qp8C.js → CIGAXayx.js} +1 -1
- package/build/client/_app/immutable/chunks/COggdvXA.js +1 -0
- package/build/client/_app/immutable/chunks/{C47KpwkC.js → CYQe9sJG.js} +1 -1
- package/build/client/_app/immutable/chunks/{BrX0OiHa.js → CYxrEvMN.js} +1 -1
- package/build/client/_app/immutable/chunks/{CUqNPCjK.js → CaLUaLKT.js} +1 -1
- package/build/client/_app/immutable/chunks/{Cwd5DdSa.js → CdHvJ4_w.js} +1 -1
- package/build/client/_app/immutable/chunks/{CJ-lpX9h.js → Ckyou5Od.js} +1 -1
- package/build/client/_app/immutable/chunks/{C1YAjCKh.js → CmrXQ7MQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{BWYga5zO.js → Cn53TYFN.js} +1 -1
- package/build/client/_app/immutable/chunks/{CD_kQCcy.js → CobJHAAW.js} +1 -1
- package/build/client/_app/immutable/chunks/{B51GwAfL.js → CpHaxpYy.js} +1 -1
- package/build/client/_app/immutable/chunks/{BdoKnygS.js → CuD0M7ml.js} +2 -2
- package/build/client/_app/immutable/chunks/{BfABZbDO.js → DGn8gxMH.js} +1 -1
- package/build/client/_app/immutable/chunks/{CbTVVa30.js → DJ4TnckQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{C_qF9Ll1.js → DL-S4jBj.js} +1 -1
- package/build/client/_app/immutable/chunks/{ioQlYLW1.js → DLTGIy2S.js} +1 -1
- package/build/client/_app/immutable/chunks/{EodvFm9d.js → DcaEnsuI.js} +1 -1
- package/build/client/_app/immutable/chunks/{CH1RPLU9.js → DhPQR3Px.js} +1 -1
- package/build/client/_app/immutable/chunks/{gzN3DgKu.js → Dik5m9U4.js} +1 -1
- package/build/client/_app/immutable/chunks/{DSQnKfn-.js → Dj-iuGW3.js} +1 -1
- package/build/client/_app/immutable/chunks/{DePvhy8M.js → MQLxN4-w.js} +1 -1
- package/build/client/_app/immutable/chunks/{SbhBQTvy.js → N5edR_wk.js} +1 -1
- package/build/client/_app/immutable/chunks/{k7j8NQjk.js → NMQ8yb5B.js} +1 -1
- package/build/client/_app/immutable/chunks/{DJe5QTzh.js → S5LArjpJ.js} +1 -1
- package/build/client/_app/immutable/chunks/{CgJVu5ra.js → SsCjYNSL.js} +1 -1
- package/build/client/_app/immutable/chunks/UguYSyR_.js +1 -0
- package/build/client/_app/immutable/chunks/{CkIr3n4I.js → aOKoOpNt.js} +1 -1
- package/build/client/_app/immutable/chunks/{wOfVBGZS.js → dRJp6O4P.js} +1 -1
- package/build/client/_app/immutable/chunks/{CJ4vuw4Q.js → nlEqX7_u.js} +1 -1
- package/build/client/_app/immutable/chunks/{lFo8pWTB.js → q-tJjDlp.js} +1 -1
- package/build/client/_app/immutable/entry/{app.xXlXisbW.js → app.BR7HPYnZ.js} +2 -2
- package/build/client/_app/immutable/entry/start.DbhlkFfd.js +1 -0
- package/build/client/_app/immutable/nodes/0.BDKx-zUI.js +1 -0
- package/build/client/_app/immutable/nodes/{1.CduF9YQo.js → 1.B_9JaBUL.js} +1 -1
- package/build/client/_app/immutable/nodes/{2.ZFWOqsMm.js → 2.D_8Y7w6T.js} +1 -1
- package/build/client/_app/immutable/nodes/3.sgWOIIcg.js +1 -0
- package/build/client/_app/version.json +1 -1
- package/build/server/chunks/{0-F8UDIQlw.js → 0-C94QRPbG.js} +2 -2
- package/build/server/chunks/{0-F8UDIQlw.js.map → 0-C94QRPbG.js.map} +1 -1
- package/build/server/chunks/1-G6vwI4Kc.js +9 -0
- package/build/server/chunks/{1-YUd-zKqx.js.map → 1-G6vwI4Kc.js.map} +1 -1
- package/build/server/chunks/{2-tND4IT0j.js → 2-Cy9PX2zK.js} +2 -2
- package/build/server/chunks/{2-tND4IT0j.js.map → 2-Cy9PX2zK.js.map} +1 -1
- package/build/server/chunks/{3-DzwYMPfw.js → 3-CxxgkGJa.js} +3 -3
- package/build/server/chunks/{3-DzwYMPfw.js.map → 3-CxxgkGJa.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-BXgDoamA.js → _page.svelte-DdQY1QqH.js} +62 -3
- package/build/server/chunks/_page.svelte-DdQY1QqH.js.map +1 -0
- package/build/server/chunks/_server.ts-CEtvrNT1.js +13 -0
- package/build/server/chunks/_server.ts-CEtvrNT1.js.map +1 -0
- package/build/server/chunks/{_server.ts-p_sHg72N.js → _server.ts-sRxHDQa9.js} +2 -2
- package/build/server/chunks/{_server.ts-p_sHg72N.js.map → _server.ts-sRxHDQa9.js.map} +1 -1
- package/build/server/chunks/{hooks.server-CeNxBnIX.js → hooks.server-sBuk2932.js} +12 -2
- package/build/server/chunks/hooks.server-sBuk2932.js.map +1 -0
- package/build/server/chunks/idle-timer-CouFy_WF.js +49 -0
- package/build/server/chunks/idle-timer-CouFy_WF.js.map +1 -0
- package/build/server/chunks/{sse-manager-DpUiK2jt.js → sse-manager-C9ZLI_Nx.js} +13 -2
- package/build/server/chunks/sse-manager-C9ZLI_Nx.js.map +1 -0
- package/build/server/index.js +22 -3
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +13 -6
- package/build/server/manifest.js.map +1 -1
- package/dist/cli/commands/review.js +34 -233
- package/dist/cli/commands/stop.js +64 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/server-client.js +209 -0
- package/dist/cli/session-reader.js +70 -1
- package/package.json +1 -1
- package/build/client/_app/immutable/chunks/BHAhR-ZH.js +0 -3
- package/build/client/_app/immutable/chunks/BPkIOP5x.js +0 -1
- package/build/client/_app/immutable/chunks/QMxLvN9k.js +0 -1
- package/build/client/_app/immutable/entry/start.C6hZEWOp.js +0 -1
- package/build/client/_app/immutable/nodes/0.BZ6p9IRV.js +0 -1
- package/build/client/_app/immutable/nodes/3.CfL49yTp.js +0 -1
- package/build/server/chunks/1-YUd-zKqx.js +0 -9
- package/build/server/chunks/_page.svelte-BXgDoamA.js.map +0 -1
- package/build/server/chunks/hooks.server-CeNxBnIX.js.map +0 -1
- package/build/server/chunks/sse-manager-DpUiK2jt.js.map +0 -1
package/build/server/manifest.js
CHANGED
|
@@ -10,12 +10,12 @@ return {
|
|
|
10
10
|
assets: new Set(["favicon.png"]),
|
|
11
11
|
mimeTypes: {".png":"image/png"},
|
|
12
12
|
_: {
|
|
13
|
-
client: {start:"_app/immutable/entry/start.
|
|
13
|
+
client: {start:"_app/immutable/entry/start.DbhlkFfd.js",app:"_app/immutable/entry/app.BR7HPYnZ.js",imports:["_app/immutable/entry/start.DbhlkFfd.js","_app/immutable/chunks/74GYr5Jz.js","_app/immutable/chunks/CobJHAAW.js","_app/immutable/chunks/DGn8gxMH.js","_app/immutable/entry/app.BR7HPYnZ.js","_app/immutable/chunks/Dj-iuGW3.js","_app/immutable/chunks/CobJHAAW.js","_app/immutable/chunks/dRJp6O4P.js","_app/immutable/chunks/BrjAEg64.js","_app/immutable/chunks/DGn8gxMH.js","_app/immutable/chunks/BZQqs639.js","_app/immutable/chunks/C5wtj4WQ.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
|
14
14
|
nodes: [
|
|
15
|
-
__memo(() => import('./chunks/0-
|
|
16
|
-
__memo(() => import('./chunks/1-
|
|
17
|
-
__memo(() => import('./chunks/2-
|
|
18
|
-
__memo(() => import('./chunks/3-
|
|
15
|
+
__memo(() => import('./chunks/0-C94QRPbG.js')),
|
|
16
|
+
__memo(() => import('./chunks/1-G6vwI4Kc.js')),
|
|
17
|
+
__memo(() => import('./chunks/2-Cy9PX2zK.js')),
|
|
18
|
+
__memo(() => import('./chunks/3-CxxgkGJa.js'))
|
|
19
19
|
],
|
|
20
20
|
remotes: {
|
|
21
21
|
|
|
@@ -77,12 +77,19 @@ return {
|
|
|
77
77
|
page: null,
|
|
78
78
|
endpoint: __memo(() => import('./chunks/_server.ts-b4ynvdd4.js'))
|
|
79
79
|
},
|
|
80
|
+
{
|
|
81
|
+
id: "/api/shutdown",
|
|
82
|
+
pattern: /^\/api\/shutdown\/?$/,
|
|
83
|
+
params: [],
|
|
84
|
+
page: null,
|
|
85
|
+
endpoint: __memo(() => import('./chunks/_server.ts-CEtvrNT1.js'))
|
|
86
|
+
},
|
|
80
87
|
{
|
|
81
88
|
id: "/api/sse/[sessionId]",
|
|
82
89
|
pattern: /^\/api\/sse\/([^/]+?)\/?$/,
|
|
83
90
|
params: [{"name":"sessionId","optional":false,"rest":false,"chained":false}],
|
|
84
91
|
page: null,
|
|
85
|
-
endpoint: __memo(() => import('./chunks/_server.ts-
|
|
92
|
+
endpoint: __memo(() => import('./chunks/_server.ts-sRxHDQa9.js'))
|
|
86
93
|
},
|
|
87
94
|
{
|
|
88
95
|
id: "/plan/[sessionId]",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sources":["../../.svelte-kit/adapter-node/manifest.js"],"sourcesContent":["export const manifest = (() => {\nfunction __memo(fn) {\n\tlet value;\n\treturn () => value ??= (value = fn());\n}\n\nreturn {\n\tappDir: \"_app\",\n\tappPath: \"_app\",\n\tassets: new Set([\"favicon.png\"]),\n\tmimeTypes: {\".png\":\"image/png\"},\n\t_: {\n\t\tclient: {start:\"_app/immutable/entry/start.
|
|
1
|
+
{"version":3,"file":"manifest.js","sources":["../../.svelte-kit/adapter-node/manifest.js"],"sourcesContent":["export const manifest = (() => {\nfunction __memo(fn) {\n\tlet value;\n\treturn () => value ??= (value = fn());\n}\n\nreturn {\n\tappDir: \"_app\",\n\tappPath: \"_app\",\n\tassets: new Set([\"favicon.png\"]),\n\tmimeTypes: {\".png\":\"image/png\"},\n\t_: {\n\t\tclient: {start:\"_app/immutable/entry/start.DbhlkFfd.js\",app:\"_app/immutable/entry/app.BR7HPYnZ.js\",imports:[\"_app/immutable/entry/start.DbhlkFfd.js\",\"_app/immutable/chunks/74GYr5Jz.js\",\"_app/immutable/chunks/CobJHAAW.js\",\"_app/immutable/chunks/DGn8gxMH.js\",\"_app/immutable/entry/app.BR7HPYnZ.js\",\"_app/immutable/chunks/Dj-iuGW3.js\",\"_app/immutable/chunks/CobJHAAW.js\",\"_app/immutable/chunks/dRJp6O4P.js\",\"_app/immutable/chunks/BrjAEg64.js\",\"_app/immutable/chunks/DGn8gxMH.js\",\"_app/immutable/chunks/BZQqs639.js\",\"_app/immutable/chunks/C5wtj4WQ.js\"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},\n\t\tnodes: [\n\t\t\t__memo(() => import('./nodes/0.js')),\n\t\t\t__memo(() => import('./nodes/1.js')),\n\t\t\t__memo(() => import('./nodes/2.js')),\n\t\t\t__memo(() => import('./nodes/3.js'))\n\t\t],\n\t\tremotes: {\n\t\t\t\n\t\t},\n\t\troutes: [\n\t\t\t{\n\t\t\t\tid: \"/\",\n\t\t\t\tpattern: /^\\/$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: { layouts: [0,], errors: [1,], leaf: 2 },\n\t\t\t\tendpoint: null\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/health\",\n\t\t\t\tpattern: /^\\/api\\/health\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/health/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/approve\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/approve\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/approve/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/feedback\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/feedback\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/feedback/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/versions\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/versions\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/versions/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sessions/[sessionId]/versions/[version]\",\n\t\t\t\tpattern: /^\\/api\\/sessions\\/([^/]+?)\\/versions\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false},{\"name\":\"version\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sessions/_sessionId_/versions/_version_/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/shutdown\",\n\t\t\t\tpattern: /^\\/api\\/shutdown\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/shutdown/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/sse/[sessionId]\",\n\t\t\t\tpattern: /^\\/api\\/sse\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/sse/_sessionId_/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/plan/[sessionId]\",\n\t\t\t\tpattern: /^\\/plan\\/([^/]+?)\\/?$/,\n\t\t\t\tparams: [{\"name\":\"sessionId\",\"optional\":false,\"rest\":false,\"chained\":false}],\n\t\t\t\tpage: { layouts: [0,], errors: [1,], leaf: 3 },\n\t\t\t\tendpoint: null\n\t\t\t}\n\t\t],\n\t\tprerendered_routes: new Set([]),\n\t\tmatchers: async () => {\n\t\t\t\n\t\t\treturn { };\n\t\t},\n\t\tserver_assets: {}\n\t}\n}\n})();\n\nexport const prerendered = new Set([]);\n\nexport const base = \"\";"],"names":[],"mappings":"AAAY,MAAC,QAAQ,GAAG,CAAC,MAAM;AAC/B,SAAS,MAAM,CAAC,EAAE,EAAE;AACpB,CAAC,IAAI,KAAK;AACV,CAAC,OAAO,MAAM,KAAK,MAAM,KAAK,GAAG,EAAE,EAAE,CAAC;AACtC;;AAEA,OAAO;AACP,CAAC,MAAM,EAAE,MAAM;AACf,CAAC,OAAO,EAAE,MAAM;AAChB,CAAC,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC;AACjC,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;AAChC,CAAC,CAAC,EAAE;AACJ,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,wCAAwC,CAAC,GAAG,CAAC,sCAAsC,CAAC,OAAO,CAAC,CAAC,wCAAwC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,sCAAsC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAAC,KAAK,CAAC;AAC7lB,EAAE,KAAK,EAAE;AACT,GAAG,MAAM,CAAC,MAAM,OAAO,wBAAc,CAAC,CAAC;AACvC,GAAG,MAAM,CAAC,MAAM,OAAO,wBAAc,CAAC,CAAC;AACvC,GAAG,MAAM,CAAC,MAAM,OAAO,wBAAc,CAAC,CAAC;AACvC,GAAG,MAAM,CAAC,MAAM,OAAO,wBAAc,CAAC;AACtC,GAAG;AACH,EAAE,OAAO,EAAE;AACX;AACA,GAAG;AACH,EAAE,MAAM,EAAE;AACV,GAAG;AACH,IAAI,EAAE,EAAE,GAAG;AACX,IAAI,OAAO,EAAE,MAAM;AACnB,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;AAClD,IAAI,QAAQ,EAAE;AACd,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,aAAa;AACrB,IAAI,OAAO,EAAE,oBAAoB;AACjC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAA8C,CAAC;AACjF,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,eAAe;AACvB,IAAI,OAAO,EAAE,sBAAsB;AACnC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAAgD,CAAC;AACnF,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,2BAA2B;AACnC,IAAI,OAAO,EAAE,gCAAgC;AAC7C,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChF,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAA4D,CAAC;AAC/F,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,mCAAmC;AAC3C,IAAI,OAAO,EAAE,yCAAyC;AACtD,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChF,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAAoE,CAAC;AACvG,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,oCAAoC;AAC5C,IAAI,OAAO,EAAE,0CAA0C;AACvD,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChF,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAAqE,CAAC;AACxG,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,oCAAoC;AAC5C,IAAI,OAAO,EAAE,0CAA0C;AACvD,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChF,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAAqE,CAAC;AACxG,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,8CAA8C;AACtD,IAAI,OAAO,EAAE,oDAAoD;AACjE,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACjJ,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAA+E,CAAC;AAClH,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,eAAe;AACvB,IAAI,OAAO,EAAE,sBAAsB;AACnC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAAgD,CAAC;AACnF,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,sBAAsB;AAC9B,IAAI,OAAO,EAAE,2BAA2B;AACxC,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChF,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,iCAAuD,CAAC;AAC1F,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,mBAAmB;AAC3B,IAAI,OAAO,EAAE,uBAAuB;AACpC,IAAI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAChF,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;AAClD,IAAI,QAAQ,EAAE;AACd;AACA,GAAG;AACH,EAAE,kBAAkB,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;AACjC,EAAE,QAAQ,EAAE,YAAY;AACxB;AACA,GAAG,OAAO,IAAI;AACd,EAAE,CAAC;AACH,EAAE,aAAa,EAAE;AACjB;AACA;AACA,CAAC;;AAEW,MAAC,WAAW,GAAG,IAAI,GAAG,CAAC,EAAE;;AAEzB,MAAC,IAAI,GAAG;;;;"}
|
|
@@ -1,179 +1,10 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, } from "node:fs";
|
|
2
2
|
import { resolve, dirname, join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { spawn, execSync } from "node:child_process";
|
|
5
|
-
import { createServer } from "node:net";
|
|
6
3
|
import { watch } from "chokidar";
|
|
7
4
|
import { parseMarkdownToPlan, sessionIdFromPath } from "../markdown-to-plan.js";
|
|
8
5
|
import { outputJson } from "../output.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const DEFAULT_BASE_PORT = 5181;
|
|
12
|
-
const MAX_PORT = 5199;
|
|
13
|
-
function getPackageDir() {
|
|
14
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
15
|
-
// dist/cli/commands/review.js -> package root
|
|
16
|
-
return resolve(dirname(thisFile), "../../..");
|
|
17
|
-
}
|
|
18
|
-
async function checkHealth(port) {
|
|
19
|
-
try {
|
|
20
|
-
const controller = new AbortController();
|
|
21
|
-
const timeout = setTimeout(() => controller.abort(), 500);
|
|
22
|
-
const res = await fetch(`http://localhost:${port}/api/health`, {
|
|
23
|
-
signal: controller.signal,
|
|
24
|
-
});
|
|
25
|
-
clearTimeout(timeout);
|
|
26
|
-
if (!res.ok)
|
|
27
|
-
return null;
|
|
28
|
-
return (await res.json());
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function lockFilePath(sessionDir) {
|
|
35
|
-
return join(sessionDir, ".server-lock.json");
|
|
36
|
-
}
|
|
37
|
-
function readLock(sessionDir) {
|
|
38
|
-
const lockPath = lockFilePath(sessionDir);
|
|
39
|
-
if (!existsSync(lockPath))
|
|
40
|
-
return null;
|
|
41
|
-
try {
|
|
42
|
-
return JSON.parse(readFileSync(lockPath, "utf-8"));
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function writeLock(sessionDir, port, pid) {
|
|
49
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
50
|
-
writeFileSync(lockFilePath(sessionDir), JSON.stringify({ port, pid }));
|
|
51
|
-
}
|
|
52
|
-
function clearLock(sessionDir) {
|
|
53
|
-
try {
|
|
54
|
-
unlinkSync(lockFilePath(sessionDir));
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// ignore
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function isPidAlive(pid) {
|
|
61
|
-
try {
|
|
62
|
-
process.kill(pid, 0);
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
async function findExistingServer(sessionDir, basePort) {
|
|
70
|
-
// Check lock file first (fast path, avoids port scan race)
|
|
71
|
-
const lock = readLock(sessionDir);
|
|
72
|
-
if (lock) {
|
|
73
|
-
if (isPidAlive(lock.pid)) {
|
|
74
|
-
const health = await checkHealth(lock.port);
|
|
75
|
-
if (health && health.sessionDir === sessionDir) {
|
|
76
|
-
return lock.port;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// Stale lock — remove it
|
|
80
|
-
clearLock(sessionDir);
|
|
81
|
-
}
|
|
82
|
-
// Fallback: scan ports (handles lock-less legacy servers)
|
|
83
|
-
for (let port = basePort; port <= MAX_PORT; port++) {
|
|
84
|
-
const health = await checkHealth(port);
|
|
85
|
-
if (health && health.sessionDir === sessionDir) {
|
|
86
|
-
return port;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
function isPortFree(port) {
|
|
92
|
-
return new Promise((resolve) => {
|
|
93
|
-
const server = createServer();
|
|
94
|
-
server.once("error", () => resolve(false));
|
|
95
|
-
server.once("listening", () => {
|
|
96
|
-
server.close(() => resolve(true));
|
|
97
|
-
});
|
|
98
|
-
server.listen(port, "127.0.0.1");
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
async function findFreePort(basePort) {
|
|
102
|
-
for (let port = basePort; port <= MAX_PORT; port++) {
|
|
103
|
-
if (await isPortFree(port))
|
|
104
|
-
return port;
|
|
105
|
-
}
|
|
106
|
-
throw new Error(`No free port found in range ${basePort}-${MAX_PORT}`);
|
|
107
|
-
}
|
|
108
|
-
function startServer(sessionDir, port) {
|
|
109
|
-
const packageDir = getPackageDir();
|
|
110
|
-
const buildEntry = join(packageDir, "build", "index.js");
|
|
111
|
-
if (!existsSync(buildEntry)) {
|
|
112
|
-
console.error(`Error: Server build not found at ${buildEntry}\nRun 'npm run build:server' in the plan-assistant package first.`);
|
|
113
|
-
process.exit(1);
|
|
114
|
-
}
|
|
115
|
-
return new Promise((resolvePromise, reject) => {
|
|
116
|
-
const child = spawn("node", [buildEntry], {
|
|
117
|
-
env: {
|
|
118
|
-
...process.env,
|
|
119
|
-
SESSION_DIR: sessionDir,
|
|
120
|
-
PORT: String(port),
|
|
121
|
-
},
|
|
122
|
-
stdio: "ignore",
|
|
123
|
-
detached: true,
|
|
124
|
-
});
|
|
125
|
-
child.unref();
|
|
126
|
-
const pid = child.pid;
|
|
127
|
-
let attempts = 0;
|
|
128
|
-
const maxAttempts = 30;
|
|
129
|
-
const interval = setInterval(async () => {
|
|
130
|
-
attempts++;
|
|
131
|
-
try {
|
|
132
|
-
const controller = new AbortController();
|
|
133
|
-
const timeout = setTimeout(() => controller.abort(), 1000);
|
|
134
|
-
const res = await fetch(`http://localhost:${port}/api/health`, {
|
|
135
|
-
signal: controller.signal,
|
|
136
|
-
});
|
|
137
|
-
clearTimeout(timeout);
|
|
138
|
-
if (res.ok) {
|
|
139
|
-
clearInterval(interval);
|
|
140
|
-
resolvePromise(pid);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
if (attempts >= maxAttempts) {
|
|
145
|
-
clearInterval(interval);
|
|
146
|
-
reject(new Error("Server failed to start within 15 seconds"));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}, 500);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
async function launchServer(sessionDir, port) {
|
|
153
|
-
process.stdout.write(`Starting Plan Assistant server on port ${port}...`);
|
|
154
|
-
try {
|
|
155
|
-
const pid = await startServer(sessionDir, port);
|
|
156
|
-
writeLock(sessionDir, port, pid);
|
|
157
|
-
console.log(" ready.");
|
|
158
|
-
}
|
|
159
|
-
catch (err) {
|
|
160
|
-
console.error(` failed: ${err}`);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
function openBrowser(url) {
|
|
165
|
-
try {
|
|
166
|
-
const cmd = process.platform === "darwin"
|
|
167
|
-
? "open"
|
|
168
|
-
: process.platform === "win32"
|
|
169
|
-
? "start"
|
|
170
|
-
: "xdg-open";
|
|
171
|
-
execSync(`${cmd} "${url}"`, { stdio: "ignore" });
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
console.log(`Open in browser: ${url}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
6
|
+
import { waitForFeedback } from "../session-reader.js";
|
|
7
|
+
import { DEFAULT_BASE_PORT, MAX_PORT, findExistingServer, checkHealth, isPortFree, findFreePort, launchServer, openBrowser, } from "../server-client.js";
|
|
177
8
|
export async function review(args) {
|
|
178
9
|
const markdownFile = args.positional[0];
|
|
179
10
|
if (!markdownFile) {
|
|
@@ -181,6 +12,11 @@ export async function review(args) {
|
|
|
181
12
|
console.error("Usage: plan-assistant review <markdown-file>");
|
|
182
13
|
process.exit(1);
|
|
183
14
|
}
|
|
15
|
+
// Parse host configuration (for Docker/remote sandbox environments)
|
|
16
|
+
const hostFlag = args.flags.host;
|
|
17
|
+
const displayHost = (typeof hostFlag === "string" ? hostFlag : null) ??
|
|
18
|
+
process.env.PLAN_ASSISTANT_HOST ??
|
|
19
|
+
"localhost";
|
|
184
20
|
// Parse port configuration
|
|
185
21
|
const portFlag = args.flags.port;
|
|
186
22
|
const envPort = process.env.PLAN_ASSISTANT_PORT;
|
|
@@ -274,12 +110,32 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
274
110
|
writeFileSync(join(sessionPath, "plan.json"), JSON.stringify(plan, null, 2));
|
|
275
111
|
writeFileSync(join(sessionPath, "versions", `v${version}.json`), JSON.stringify(plan, null, 2));
|
|
276
112
|
// Find existing server for this session dir or start a new one
|
|
113
|
+
const reuse = args.flags.reuse === true;
|
|
277
114
|
const basePort = requestedPort ?? DEFAULT_BASE_PORT;
|
|
278
115
|
let port = await findExistingServer(sessionDir, basePort);
|
|
116
|
+
if (!port && reuse) {
|
|
117
|
+
// --reuse: find any running plan-assistant server and reuse it
|
|
118
|
+
for (let p = basePort; p <= MAX_PORT; p++) {
|
|
119
|
+
const health = await checkHealth(p);
|
|
120
|
+
if (health) {
|
|
121
|
+
port = p;
|
|
122
|
+
console.error(`Reusing existing server on port ${p}.`);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
279
127
|
if (!port) {
|
|
280
128
|
if (requestedPort) {
|
|
281
129
|
if (!(await isPortFree(requestedPort))) {
|
|
282
|
-
|
|
130
|
+
// Check if it's a plan-assistant server for a better error message
|
|
131
|
+
const health = await checkHealth(requestedPort);
|
|
132
|
+
if (health) {
|
|
133
|
+
console.error(`Error: Port ${requestedPort} is already used by Plan Assistant (session dir: ${health.sessionDir}).` +
|
|
134
|
+
`\nUse \`plan-assistant stop\` to stop it, or add --reuse to share the server.`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.error(`Error: Port ${requestedPort} is already in use by another process.`);
|
|
138
|
+
}
|
|
283
139
|
process.exit(1);
|
|
284
140
|
}
|
|
285
141
|
port = requestedPort;
|
|
@@ -289,7 +145,7 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
289
145
|
}
|
|
290
146
|
await launchServer(sessionDir, port);
|
|
291
147
|
}
|
|
292
|
-
const url = `http
|
|
148
|
+
const url = `http://${displayHost}:${port}/plan/${sessionId}`;
|
|
293
149
|
const feedbackPath = join(sessionPath, "feedback.json");
|
|
294
150
|
// Machine-readable ready event on first line
|
|
295
151
|
outputJson({
|
|
@@ -300,7 +156,9 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
300
156
|
url,
|
|
301
157
|
feedbackPath,
|
|
302
158
|
});
|
|
303
|
-
|
|
159
|
+
if (displayHost === "localhost") {
|
|
160
|
+
openBrowser(url);
|
|
161
|
+
}
|
|
304
162
|
const noWait = args.flags["no-wait"] === true;
|
|
305
163
|
console.error(`\nPlan Assistant`);
|
|
306
164
|
console.error(` Review: ${url}`);
|
|
@@ -340,7 +198,7 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
340
198
|
});
|
|
341
199
|
// Wait for feedback unless --no-wait
|
|
342
200
|
if (!noWait) {
|
|
343
|
-
await waitForFeedback(feedbackPath, sessionId, plan.meta.title, mdWatcher);
|
|
201
|
+
await waitForFeedback(feedbackPath, sessionId, plan.meta.title, mdWatcher, sessionPath);
|
|
344
202
|
return;
|
|
345
203
|
}
|
|
346
204
|
// Keep process alive (--no-wait mode)
|
|
@@ -350,60 +208,3 @@ Run \`npx plan-assistant init --output <file>\` to generate a correctly-formatte
|
|
|
350
208
|
process.exit(0);
|
|
351
209
|
});
|
|
352
210
|
}
|
|
353
|
-
async function waitForFeedback(feedbackPath, sessionId, planTitle, mdWatcher) {
|
|
354
|
-
// Check if feedback already submitted
|
|
355
|
-
if (existsSync(feedbackPath)) {
|
|
356
|
-
try {
|
|
357
|
-
const existing = JSON.parse(readFileSync(feedbackPath, "utf-8"));
|
|
358
|
-
if (existing.status === "approved" || existing.status === "needs-work") {
|
|
359
|
-
mdWatcher.close();
|
|
360
|
-
outputFeedbackResult(existing, sessionId, planTitle);
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
catch {
|
|
365
|
-
/* ignore */
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return new Promise((resolve) => {
|
|
369
|
-
const fbWatcher = watch(feedbackPath, {
|
|
370
|
-
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 100 },
|
|
371
|
-
});
|
|
372
|
-
const check = () => {
|
|
373
|
-
try {
|
|
374
|
-
if (!existsSync(feedbackPath))
|
|
375
|
-
return;
|
|
376
|
-
const data = JSON.parse(readFileSync(feedbackPath, "utf-8"));
|
|
377
|
-
if (data.status === "approved" || data.status === "needs-work") {
|
|
378
|
-
fbWatcher.close();
|
|
379
|
-
mdWatcher.close();
|
|
380
|
-
outputFeedbackResult(data, sessionId, planTitle);
|
|
381
|
-
resolve();
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
catch {
|
|
385
|
-
/* ignore parse errors during writes */
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
fbWatcher.on("change", check);
|
|
389
|
-
fbWatcher.on("add", check);
|
|
390
|
-
process.on("SIGINT", () => {
|
|
391
|
-
fbWatcher.close();
|
|
392
|
-
mdWatcher.close();
|
|
393
|
-
console.error("\nStopped watching.");
|
|
394
|
-
process.exit(0);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
function outputFeedbackResult(feedback, sessionId, planTitle) {
|
|
399
|
-
const unresolvedComments = feedback.comments.filter((c) => !c.resolved);
|
|
400
|
-
outputJson({
|
|
401
|
-
event: "feedback",
|
|
402
|
-
sessionId,
|
|
403
|
-
planTitle,
|
|
404
|
-
status: feedback.status,
|
|
405
|
-
comments: unresolvedComments,
|
|
406
|
-
commentCount: unresolvedComments.length,
|
|
407
|
-
});
|
|
408
|
-
process.exit(feedback.status === "approved" ? EXIT_APPROVED : EXIT_NEEDS_WORK);
|
|
409
|
-
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
import { resolveSession } from "../session-resolver.js";
|
|
3
|
+
import { stopServer, checkHealth, DEFAULT_BASE_PORT, MAX_PORT, } from "../server-client.js";
|
|
4
|
+
import { outputJson, outputError } from "../output.js";
|
|
5
|
+
export async function stop(args) {
|
|
6
|
+
const target = args.positional[0];
|
|
7
|
+
if (target) {
|
|
8
|
+
// Stop server for a specific session
|
|
9
|
+
const session = resolveSession(target);
|
|
10
|
+
if (!session) {
|
|
11
|
+
outputError(`Session not found: ${target}`, "NOT_FOUND");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const parentDir = dirname(session.sessionDir);
|
|
15
|
+
const stopped = await stopServer(parentDir);
|
|
16
|
+
if (stopped) {
|
|
17
|
+
outputJson({ event: "server-stopped", sessionDir: parentDir });
|
|
18
|
+
console.error(`Server stopped for session ${session.sessionId}.`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
outputJson({ event: "no-server", sessionDir: parentDir });
|
|
22
|
+
console.error(`No running server found for session ${session.sessionId}.`);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// No argument: find and stop all plan-assistant servers in port range
|
|
27
|
+
let stopped = 0;
|
|
28
|
+
const results = [];
|
|
29
|
+
for (let port = DEFAULT_BASE_PORT; port <= MAX_PORT; port++) {
|
|
30
|
+
const health = await checkHealth(port);
|
|
31
|
+
if (health) {
|
|
32
|
+
try {
|
|
33
|
+
const controller = new AbortController();
|
|
34
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
35
|
+
await fetch(`http://localhost:${port}/api/shutdown`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
signal: controller.signal,
|
|
38
|
+
});
|
|
39
|
+
clearTimeout(timeout);
|
|
40
|
+
results.push({ port, sessionDir: health.sessionDir });
|
|
41
|
+
stopped++;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Try SIGTERM
|
|
45
|
+
try {
|
|
46
|
+
process.kill(health.pid, "SIGTERM");
|
|
47
|
+
results.push({ port, sessionDir: health.sessionDir });
|
|
48
|
+
stopped++;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// ignore
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (stopped === 0) {
|
|
57
|
+
outputJson({ event: "no-servers", stopped: 0 });
|
|
58
|
+
console.error("No running Plan Assistant servers found.");
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
outputJson({ event: "servers-stopped", stopped, servers: results });
|
|
62
|
+
console.error(`Stopped ${stopped} server(s).`);
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -54,6 +54,7 @@ Commands:
|
|
|
54
54
|
plan-assistant status <session-id-or-file> Check review status
|
|
55
55
|
plan-assistant feedback <session-id-or-file> Read feedback JSON
|
|
56
56
|
plan-assistant list [--dir <path>] List all sessions
|
|
57
|
+
plan-assistant stop [<session-id-or-file>] Stop running server(s)
|
|
57
58
|
plan-assistant clean [--all] [--older-than <dur>] Remove old sessions
|
|
58
59
|
plan-assistant export <session-id-or-file> Export as HTML
|
|
59
60
|
plan-assistant help format Show the required plan format
|
|
@@ -62,7 +63,9 @@ Commands:
|
|
|
62
63
|
Flags:
|
|
63
64
|
--pretty Human-readable output (default: JSON)
|
|
64
65
|
--port <N> Port for review server (review command)
|
|
66
|
+
--host <H> Hostname for browser URL, e.g. host IP when running in Docker (review command)
|
|
65
67
|
--no-wait Don't wait for feedback, just start server (review command)
|
|
68
|
+
--reuse Reuse an already-running server on another session (review command)
|
|
66
69
|
--wait Block until feedback is submitted (status command)
|
|
67
70
|
|
|
68
71
|
TIP: Always start with \`plan-assistant init\` to get a correctly-formatted template.
|
|
@@ -150,6 +153,10 @@ export async function main(args) {
|
|
|
150
153
|
const { init } = await import("./commands/init.js");
|
|
151
154
|
return init(parsed);
|
|
152
155
|
}
|
|
156
|
+
case "stop": {
|
|
157
|
+
const { stop } = await import("./commands/stop.js");
|
|
158
|
+
return stop(parsed);
|
|
159
|
+
}
|
|
153
160
|
case "clean": {
|
|
154
161
|
const { clean } = await import("./commands/clean.js");
|
|
155
162
|
return clean(parsed);
|