@launchsecure/launch-kit 0.0.32 → 0.0.34

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 (93) hide show
  1. package/dist/chart-client/assets/{index-B__ARB8k.js → index-DFu2xIrM.js} +2 -2
  2. package/dist/chart-client/assets/index-DpKO9p0s.css +1 -0
  3. package/dist/chart-client/index.html +2 -2
  4. package/dist/client/assets/{index-h8kMzVtG.js → index-Cbw6bVdx.js} +2 -2
  5. package/dist/client/assets/index-Dv6dD2zY.css +32 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/council-client/assets/index-AqQ9Sei6.css +1 -0
  8. package/dist/council-client/assets/{index-CWaDcsFR.js → index-CAsmGTzg.js} +2 -2
  9. package/dist/council-client/index.html +2 -2
  10. package/dist/deck-client/assets/{_baseUniq-C7GsHvgg.js → _baseUniq-BiVx0WO_.js} +1 -1
  11. package/dist/deck-client/assets/{arc-CSrZRINY.js → arc-DGMkiEzS.js} +1 -1
  12. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-zoB-G17J.js → architectureDiagram-Q4EWVU46-Y2WRmHtk.js} +1 -1
  13. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BRjjtYH6.js → blockDiagram-DXYQGD6D-_Lbfu5BQ.js} +1 -1
  14. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-C3D3sd2U.js → c4Diagram-AHTNJAMY-CTqpYTBX.js} +1 -1
  15. package/dist/deck-client/assets/channel-DB6LxW_l.js +1 -0
  16. package/dist/deck-client/assets/{chunk-4BX2VUAB-DhpDMOPO.js → chunk-4BX2VUAB-liEIbPHs.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-4TB4RGXK-BIRgPXRl.js → chunk-4TB4RGXK-CCc6lYvL.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-55IACEB6-BF24dwDZ.js → chunk-55IACEB6-D02jJUR2.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-EDXVE4YY-CW75Y61B.js → chunk-EDXVE4YY-BFmGMbLD.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-FMBD7UC4-B5-oyL79.js → chunk-FMBD7UC4-6wFLOVcJ.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-OYMX7WX6-BB2bHe_Q.js → chunk-OYMX7WX6-Bnr8RiBf.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-QZHKN3VN-D80eZO4B.js → chunk-QZHKN3VN-Ct82MksJ.js} +1 -1
  23. package/dist/deck-client/assets/{chunk-YZCP3GAM-Dz9787p_.js → chunk-YZCP3GAM-BXmN1diQ.js} +1 -1
  24. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +1 -0
  25. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +1 -0
  26. package/dist/deck-client/assets/clone-DiIRH1pI.js +1 -0
  27. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-MQjiZLcL.js → cose-bilkent-S5V4N54A-CmQCT-mH.js} +1 -1
  28. package/dist/deck-client/assets/{dagre-KV5264BT-DG4EcLpJ.js → dagre-KV5264BT-DDdSa9EX.js} +1 -1
  29. package/dist/deck-client/assets/{diagram-5BDNPKRD-1n7hM3Gc.js → diagram-5BDNPKRD-Bccks2xJ.js} +1 -1
  30. package/dist/deck-client/assets/{diagram-G4DWMVQ6-CYMarncV.js → diagram-G4DWMVQ6-CPPNgxmQ.js} +1 -1
  31. package/dist/deck-client/assets/{diagram-MMDJMWI5-DSisoipe.js → diagram-MMDJMWI5-KrD300pS.js} +1 -1
  32. package/dist/deck-client/assets/{diagram-TYMM5635-Btnq49OJ.js → diagram-TYMM5635-DefnLuQf.js} +1 -1
  33. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-Cu2Hb_Tz.js → erDiagram-SMLLAGMA-DI9FfnFP.js} +1 -1
  34. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-CGJzUzsO.js → flowDiagram-DWJPFMVM-twKyd3Fx.js} +1 -1
  35. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-D9sqGUBT.js → ganttDiagram-T4ZO3ILL-Wau3jhBr.js} +1 -1
  36. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-C0QwX2od.js → gitGraphDiagram-UUTBAWPF-D9GgYXwb.js} +1 -1
  37. package/dist/deck-client/assets/{graph-CcBjOQCl.js → graph-BhNLzyXS.js} +1 -1
  38. package/dist/deck-client/assets/index-B-YQq5b5.css +1 -0
  39. package/dist/deck-client/assets/{index-0arwoc0z.js → index-BtQBaQ7s.js} +3 -3
  40. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-DTimhhhS.js → infoDiagram-42DDH7IO-TylGlSG-.js} +1 -1
  41. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DxOxg_B4.js → ishikawaDiagram-UXIWVN3A-DAT8icpg.js} +1 -1
  42. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-Bpq0qa4j.js → journeyDiagram-VCZTEJTY-D3v_XL72.js} +1 -1
  43. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-aTIrpcVO.js → kanban-definition-6JOO6SKY-DNUOBiNr.js} +1 -1
  44. package/dist/deck-client/assets/{layout-DqglLR2E.js → layout-COfodgwF.js} +1 -1
  45. package/dist/deck-client/assets/{linear-D5GxehPc.js → linear-DmTsuIvK.js} +1 -1
  46. package/dist/deck-client/assets/{min-DXLfSREq.js → min-BW1F7i1D.js} +1 -1
  47. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-mO5Vys7I.js → mindmap-definition-QFDTVHPH-CErFzKWl.js} +1 -1
  48. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-Dm0gzdAr.js → pieDiagram-DEJITSTG-DW5F757o.js} +1 -1
  49. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-Daq7j3qD.js → quadrantDiagram-34T5L4WZ-B1S2-TfI.js} +1 -1
  50. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-CmwV95um.js → requirementDiagram-MS252O5E-BY5BAR-5.js} +1 -1
  51. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BOYl3Nkf.js → sankeyDiagram-XADWPNL6-CE1Cp9HS.js} +1 -1
  52. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-BuUjhIcW.js → sequenceDiagram-FGHM5R23-IaHnbKye.js} +1 -1
  53. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-LUZ_uwio.js → stateDiagram-FHFEXIEX-CwPJm9hU.js} +1 -1
  54. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +1 -0
  55. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-CDUxCCAW.js → timeline-definition-GMOUNBTQ-DVFGGSgN.js} +1 -1
  56. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-BRb24Tf7.js → vennDiagram-DHZGUBPP-C1194MJi.js} +1 -1
  57. package/dist/deck-client/assets/wardley-RL74JXVD-CHZiUbBa.js +162 -0
  58. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BLGlYrQz.js → wardleyDiagram-NUSXRM2D-hpwdFfGj.js} +1 -1
  59. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-De31MSnk.js → xychartDiagram-5P7HB3ND-DYkotwy8.js} +1 -1
  60. package/dist/deck-client/index.html +2 -2
  61. package/dist/server/chart-serve.js +167 -2
  62. package/dist/server/cli.js +328 -42
  63. package/dist/server/course-entry.js +1 -1
  64. package/dist/server/graph-mcp-entry.js +180 -4
  65. package/dist/server/init-entry.js +1133 -219
  66. package/dist/server/launch-radar-entry.js +45 -0
  67. package/dist/server/parse-worker-entry.js +167 -2
  68. package/dist/server/radar-docker-init-entry.js +644 -0
  69. package/dist/server/radar-entrypoint-entry.js +99 -0
  70. package/dist/server/radar-teardown-entry.js +478 -0
  71. package/dist/server/recall-entry.js +4 -1
  72. package/dist/server/rover-entry.js +20122 -0
  73. package/package.json +7 -5
  74. package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +5 -5
  75. package/scaffolds/ls-marketplace/plugins/kit/commands/standup.md +6 -6
  76. package/scaffolds/ls-marketplace/plugins/kit/skills/analyse/SKILL.md +6 -0
  77. package/scaffolds/ls-marketplace/plugins/kit/skills/brief/SKILL.md +40 -48
  78. package/scaffolds/ls-marketplace/plugins/kit/skills/debug/SKILL.md +45 -20
  79. package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +76 -67
  80. package/scaffolds/ls-marketplace/plugins/kit/skills/handoff/SKILL.md +132 -0
  81. package/scaffolds/ls-marketplace/plugins/kit/skills/ship/SKILL.md +290 -0
  82. package/scaffolds/statusline/statusline-mcp.sh +82 -2
  83. package/scaffolds/statusline/statusline-wrapper.sh +8 -1
  84. package/dist/chart-client/assets/index-CDIhdgWg.css +0 -1
  85. package/dist/client/assets/index-CfW4n40I.css +0 -32
  86. package/dist/council-client/assets/index-CZim6x1u.css +0 -1
  87. package/dist/deck-client/assets/channel-8ReQnQfH.js +0 -1
  88. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-cRxTeGkK.js +0 -1
  89. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-cRxTeGkK.js +0 -1
  90. package/dist/deck-client/assets/clone-LSHZ3K6R.js +0 -1
  91. package/dist/deck-client/assets/index-BlTlhxFW.css +0 -1
  92. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CnnRwE5D.js +0 -1
  93. package/dist/deck-client/assets/wardley-RL74JXVD-B0BYyVBY.js +0 -162
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/server/radar-entrypoint-entry.ts
5
+ var import_node_child_process = require("node:child_process");
6
+ var import_node_fs = require("node:fs");
7
+ var import_node_path = require("node:path");
8
+ var REQUIRED_ENV = [
9
+ "CLAUDE_CREDENTIALS_B64",
10
+ "LS_PAT",
11
+ "LS_ORG_SLUG",
12
+ "LS_PROJECT_SLUG",
13
+ "GH_TOKEN"
14
+ ];
15
+ function fail(message) {
16
+ console.error(message);
17
+ process.exit(1);
18
+ }
19
+ function requireEnv(name) {
20
+ const v = process.env[name];
21
+ if (!v) fail(`ERROR: ${name} is required but not set`);
22
+ return v;
23
+ }
24
+ function run(cmd, args, stdio = "inherit") {
25
+ const r = (0, import_node_child_process.spawnSync)(cmd, args, { stdio });
26
+ return r.status ?? 1;
27
+ }
28
+ function setupClaudeCredentials() {
29
+ const home = process.env.HOME ?? "/home/launchpod";
30
+ const claudeDir = (0, import_node_path.join)(home, ".claude");
31
+ (0, import_node_fs.mkdirSync)(claudeDir, { recursive: true });
32
+ const decoded = Buffer.from(requireEnv("CLAUDE_CREDENTIALS_B64"), "base64").toString("utf8");
33
+ const credsPath = (0, import_node_path.join)(claudeDir, ".credentials.json");
34
+ (0, import_node_fs.writeFileSync)(credsPath, decoded);
35
+ (0, import_node_fs.chmodSync)(credsPath, 384);
36
+ const configPath = (0, import_node_path.join)(home, ".claude.json");
37
+ let cfg = {};
38
+ if ((0, import_node_fs.existsSync)(configPath)) {
39
+ try {
40
+ cfg = JSON.parse((0, import_node_fs.readFileSync)(configPath, "utf8"));
41
+ } catch {
42
+ cfg = {};
43
+ }
44
+ }
45
+ cfg.hasCompletedOnboarding = true;
46
+ cfg.lastOnboardingVersion = cfg.lastOnboardingVersion ?? "2.1.159";
47
+ cfg.numStartups = (cfg.numStartups ?? 0) + 1;
48
+ cfg.installMethod = cfg.installMethod ?? "global";
49
+ (0, import_node_fs.writeFileSync)(configPath, JSON.stringify(cfg, null, 2));
50
+ (0, import_node_fs.chmodSync)(configPath, 384);
51
+ }
52
+ function setupGitAndGh() {
53
+ const name = process.env.GIT_USER_NAME ?? "Radar Bot";
54
+ const email = process.env.GIT_USER_EMAIL ?? "radar@launchpod.local";
55
+ const status = run("launch-kit", ["setup-git", `--identity=${name} <${email}>`]);
56
+ if (status !== 0) fail(`[entrypoint] launch-kit setup-git failed (status ${status})`);
57
+ }
58
+ function initWorkspaceIfEmpty() {
59
+ process.chdir("/workspace");
60
+ if ((0, import_node_fs.existsSync)(".git")) {
61
+ console.log("[entrypoint] /workspace already initialized \u2014 skipping init");
62
+ return;
63
+ }
64
+ console.log("[entrypoint] /workspace is empty \u2014 running launch-kit init");
65
+ const status = run("launch-kit", [
66
+ "init",
67
+ `--token=${requireEnv("LS_PAT")}`,
68
+ `--org=${requireEnv("LS_ORG_SLUG")}`,
69
+ `--project=${requireEnv("LS_PROJECT_SLUG")}`,
70
+ `--url=${process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app"}`,
71
+ `--dir=/workspace`
72
+ ]);
73
+ if (status !== 0) fail(`[entrypoint] launch-kit init failed (status ${status})`);
74
+ }
75
+ function execLaunchPodRadar() {
76
+ console.log("[entrypoint] starting launch-pod radar");
77
+ const child = (0, import_node_child_process.spawn)("launch-pod", ["radar"], { stdio: "inherit" });
78
+ const forward = (sig) => () => {
79
+ try {
80
+ child.kill(sig);
81
+ } catch {
82
+ }
83
+ };
84
+ process.on("SIGTERM", forward("SIGTERM"));
85
+ process.on("SIGINT", forward("SIGINT"));
86
+ process.on("SIGHUP", forward("SIGHUP"));
87
+ child.on("exit", (code, signal) => {
88
+ if (signal) process.kill(process.pid, signal);
89
+ else process.exit(code ?? 0);
90
+ });
91
+ }
92
+ function main() {
93
+ for (const k of REQUIRED_ENV) requireEnv(k);
94
+ setupClaudeCredentials();
95
+ setupGitAndGh();
96
+ initWorkspaceIfEmpty();
97
+ execLaunchPodRadar();
98
+ }
99
+ main();
@@ -0,0 +1,478 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/server/radar-teardown-entry.ts
5
+ var import_node_child_process = require("node:child_process");
6
+ var import_node_fs = require("node:fs");
7
+ var import_node_path = require("node:path");
8
+
9
+ // src/server/recall/config.ts
10
+ var DEFAULT_CONFIG = {
11
+ debounce: 3e3,
12
+ ignore: [
13
+ "node_modules",
14
+ ".next",
15
+ "dist",
16
+ "build",
17
+ ".turbo",
18
+ ".git",
19
+ ".recall",
20
+ "coverage",
21
+ ".pnpm-store",
22
+ ".launchsecure/graphs",
23
+ ".claude/settings.local.json"
24
+ ],
25
+ retention: {
26
+ keepLast: 5e3,
27
+ maxAgeDays: 30
28
+ }
29
+ };
30
+
31
+ // src/server/radar/mcp.ts
32
+ var import_node_https = require("node:https");
33
+ var import_node_http = require("node:http");
34
+ var ProjectMcpClient = class {
35
+ constructor(opts) {
36
+ this.initialized = false;
37
+ this.callId = 1;
38
+ this.mcpUrl = new URL("/api/mcp/project", opts.serverUrl);
39
+ this.headers = {
40
+ "Content-Type": "application/json",
41
+ "Accept": "application/json, text/event-stream",
42
+ "Authorization": `Bearer ${opts.pat}`
43
+ };
44
+ if (opts.orgSlug) this.headers["X-Org-Slug"] = opts.orgSlug;
45
+ if (opts.projectSlug) this.headers["X-Project-Slug"] = opts.projectSlug;
46
+ }
47
+ async call(toolName, args) {
48
+ await this.ensureInitialized();
49
+ const body = JSON.stringify({
50
+ jsonrpc: "2.0",
51
+ id: this.callId++,
52
+ method: "tools/call",
53
+ params: { name: toolName, arguments: args }
54
+ });
55
+ const resp = await this.send(body);
56
+ const parsed = parseBody(resp.body);
57
+ if (parsed.error) {
58
+ throw new Error(`MCP ${toolName} failed: ${parsed.error.message ?? JSON.stringify(parsed.error)}`);
59
+ }
60
+ const text = parsed.result?.content?.[0]?.text;
61
+ if (!text) return parsed.result;
62
+ if (text.startsWith("\u2500\u2500 Error")) {
63
+ const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
64
+ const reason = lines.find((l) => !l.startsWith("\u2500\u2500")) ?? text;
65
+ throw new Error(`MCP ${toolName} failed: ${reason}`);
66
+ }
67
+ try {
68
+ return JSON.parse(text);
69
+ } catch {
70
+ return text;
71
+ }
72
+ }
73
+ async ensureInitialized() {
74
+ if (this.initialized) return;
75
+ const initBody = JSON.stringify({
76
+ jsonrpc: "2.0",
77
+ id: this.callId++,
78
+ method: "initialize",
79
+ params: {
80
+ protocolVersion: "2025-03-26",
81
+ capabilities: {},
82
+ clientInfo: { name: "launchpod-feedback-agent", version: "0.0.1" }
83
+ }
84
+ });
85
+ const initResp = await this.send(initBody);
86
+ if (initResp.sessionId) this.sessionId = initResp.sessionId;
87
+ const notifBody = JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" });
88
+ await this.send(notifBody);
89
+ this.initialized = true;
90
+ }
91
+ send(body) {
92
+ return new Promise((resolve2, reject) => {
93
+ const headers = {
94
+ ...this.headers,
95
+ "Content-Length": String(Buffer.byteLength(body))
96
+ };
97
+ if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
98
+ const requester = this.mcpUrl.protocol === "https:" ? import_node_https.request : import_node_http.request;
99
+ const req = requester(
100
+ {
101
+ host: this.mcpUrl.hostname,
102
+ port: this.mcpUrl.port || (this.mcpUrl.protocol === "https:" ? 443 : 80),
103
+ path: this.mcpUrl.pathname + this.mcpUrl.search,
104
+ method: "POST",
105
+ headers
106
+ },
107
+ (res) => {
108
+ const chunks = [];
109
+ res.on("data", (c) => chunks.push(c));
110
+ res.on("end", () => {
111
+ const text = Buffer.concat(chunks).toString("utf-8");
112
+ if (res.statusCode && res.statusCode >= 400) {
113
+ reject(new Error(`MCP HTTP ${res.statusCode}: ${text}`));
114
+ return;
115
+ }
116
+ const sid = res.headers["mcp-session-id"];
117
+ resolve2({ body: text, sessionId: typeof sid === "string" ? sid : void 0 });
118
+ });
119
+ }
120
+ );
121
+ req.on("error", reject);
122
+ req.write(body);
123
+ req.end();
124
+ });
125
+ }
126
+ };
127
+ function parseBody(text) {
128
+ if (!text) return {};
129
+ if (text.startsWith("event:") || text.includes("\ndata: ")) {
130
+ for (const line of text.split("\n")) {
131
+ if (line.startsWith("data: ")) {
132
+ try {
133
+ return JSON.parse(line.slice(6));
134
+ } catch {
135
+ }
136
+ }
137
+ }
138
+ }
139
+ try {
140
+ return JSON.parse(text);
141
+ } catch {
142
+ return {};
143
+ }
144
+ }
145
+
146
+ // src/server/radar-teardown-entry.ts
147
+ var COMPOSE_FILE = (0, import_node_path.resolve)(process.cwd(), "docker-compose.yml");
148
+ var LAUNCH_POD_DIR = (0, import_node_path.dirname)(COMPOSE_FILE);
149
+ var COMPOSE_BASE = ["compose", "-f", COMPOSE_FILE];
150
+ var COMPOSE_SERVICE = "launch-pod";
151
+ var ENV_PATH = (0, import_node_path.join)(LAUNCH_POD_DIR, ".env");
152
+ function ensureLaunchPodCompose() {
153
+ if (!(0, import_node_fs.existsSync)(COMPOSE_FILE)) {
154
+ console.error(`[teardown] aborting \u2014 no docker-compose.yml at ${COMPOSE_FILE}`);
155
+ console.error(`[teardown] run from packages/cli/docker/launch-pod/ (the directory holding the launch-pod compose).`);
156
+ process.exit(1);
157
+ }
158
+ const text = (0, import_node_fs.readFileSync)(COMPOSE_FILE, "utf-8");
159
+ const hasServices = /(^|\n)services:\s*(\n|$)/.test(text);
160
+ const hasLaunchPod = /\n[ \t]+launch-pod:/.test(text);
161
+ if (!hasServices || !hasLaunchPod) {
162
+ console.error(`[teardown] aborting \u2014 ${COMPOSE_FILE} does not define a 'launch-pod' service.`);
163
+ console.error(`[teardown] this command is only for the launch-pod container; refusing to touch this compose project.`);
164
+ process.exit(1);
165
+ }
166
+ }
167
+ function parseArgs(argv) {
168
+ const out = { force: false, removeEnv: false, removeImage: false, help: false };
169
+ for (const a of argv) {
170
+ switch (a) {
171
+ case "--force":
172
+ out.force = true;
173
+ break;
174
+ case "--remove-env":
175
+ out.removeEnv = true;
176
+ break;
177
+ case "--remove-image":
178
+ out.removeImage = true;
179
+ break;
180
+ case "-h":
181
+ case "--help":
182
+ out.help = true;
183
+ break;
184
+ default:
185
+ console.error(`unknown flag: ${a}`);
186
+ process.exit(1);
187
+ }
188
+ }
189
+ return out;
190
+ }
191
+ function printHelp() {
192
+ console.log("usage: launch-sequencer radar:teardown [--remove-env] [--remove-image] [--force]");
193
+ console.log("");
194
+ console.log(" --remove-env also delete .env (default: keep \u2014 contains live creds)");
195
+ console.log(" --remove-image also remove launch-pod:local image");
196
+ console.log(" --force skip the workspace-safety preflight (discards");
197
+ console.log(" uncommitted changes / unpushed commits / in-flight");
198
+ console.log(" analyzer sessions without warning)");
199
+ }
200
+ function sh(cmd, args, opts = {}) {
201
+ const r = (0, import_node_child_process.spawnSync)(cmd, args, { encoding: "utf8", ...opts });
202
+ return {
203
+ status: r.status ?? 1,
204
+ stdout: typeof r.stdout === "string" ? r.stdout : "",
205
+ stderr: typeof r.stderr === "string" ? r.stderr : ""
206
+ };
207
+ }
208
+ function workspaceSh(command) {
209
+ const psQ = sh("docker", [...COMPOSE_BASE, "ps", "-q", COMPOSE_SERVICE]);
210
+ if (psQ.status === 0 && psQ.stdout.trim()) {
211
+ return sh("docker", [...COMPOSE_BASE, "exec", "-T", COMPOSE_SERVICE, "sh", "-c", command]).stdout;
212
+ }
213
+ return sh("docker", [...COMPOSE_BASE, "run", "--rm", "--no-deps", "--entrypoint", "sh", COMPOSE_SERVICE, "-c", command]).stdout;
214
+ }
215
+ function loadIgnoreSegs() {
216
+ const json = workspaceSh("cat /workspace/.recall/config.json 2>/dev/null");
217
+ if (json.trim()) {
218
+ try {
219
+ const j = JSON.parse(json);
220
+ if (Array.isArray(j.ignore) && j.ignore.every((s) => typeof s === "string")) {
221
+ return j.ignore;
222
+ }
223
+ } catch {
224
+ }
225
+ }
226
+ return [...DEFAULT_CONFIG.ignore];
227
+ }
228
+ function pathIsNoise(p, segs) {
229
+ for (const seg of segs) {
230
+ if (p === seg) return true;
231
+ if (p.startsWith(seg + "/")) return true;
232
+ if (p.endsWith("/" + seg)) return true;
233
+ if (p.includes("/" + seg + "/")) return true;
234
+ }
235
+ return false;
236
+ }
237
+ var SCAFFOLDED_MCP_PATHS = [
238
+ "mcpServers.launch-secure.url"
239
+ ];
240
+ function collectDiffPaths(a, b, prefix, out) {
241
+ if (a === b) return;
242
+ const aIsPrim = a === null || typeof a !== "object";
243
+ const bIsPrim = b === null || typeof b !== "object";
244
+ if (aIsPrim || bIsPrim) {
245
+ if (a !== b) out.push(prefix || "$");
246
+ return;
247
+ }
248
+ const aIsArr = Array.isArray(a);
249
+ const bIsArr = Array.isArray(b);
250
+ if (aIsArr || bIsArr) {
251
+ if (!aIsArr || !bIsArr || a.length !== b.length) {
252
+ out.push(prefix || "$");
253
+ return;
254
+ }
255
+ const aa = a;
256
+ const bb = b;
257
+ for (let i = 0; i < aa.length; i++) {
258
+ collectDiffPaths(aa[i], bb[i], prefix ? `${prefix}[${i}]` : `[${i}]`, out);
259
+ }
260
+ return;
261
+ }
262
+ const ao = a;
263
+ const bo = b;
264
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(ao), ...Object.keys(bo)])) {
265
+ const child = prefix ? `${prefix}.${k}` : k;
266
+ if (!(k in ao) || !(k in bo)) {
267
+ out.push(child);
268
+ continue;
269
+ }
270
+ collectDiffPaths(ao[k], bo[k], child, out);
271
+ }
272
+ }
273
+ function mcpJsonOnlyScaffoldedDiff() {
274
+ const headJson = workspaceSh("git -C /workspace show HEAD:.mcp.json 2>/dev/null");
275
+ const wtJson = workspaceSh("cat /workspace/.mcp.json 2>/dev/null");
276
+ if (!headJson.trim() || !wtJson.trim()) return false;
277
+ let head, wt;
278
+ try {
279
+ head = JSON.parse(headJson);
280
+ wt = JSON.parse(wtJson);
281
+ } catch {
282
+ return false;
283
+ }
284
+ const diffs = [];
285
+ collectDiffPaths(head, wt, "", diffs);
286
+ if (diffs.length === 0) return false;
287
+ return diffs.every((d) => SCAFFOLDED_MCP_PATHS.some((s) => d === s || d.startsWith(s + ".")));
288
+ }
289
+ function filterStatusSignal(raw, modeOnly, segs, scaffoldClean) {
290
+ const out = [];
291
+ for (const rawLine of raw.split("\n")) {
292
+ const line = rawLine.replace(/\r$/, "");
293
+ if (line.length < 3) continue;
294
+ let p = line.slice(3);
295
+ if (line.startsWith("R") || line.startsWith("C")) {
296
+ const arrow = p.indexOf(" -> ");
297
+ if (arrow >= 0) p = p.slice(arrow + 4);
298
+ }
299
+ if (pathIsNoise(p, segs)) continue;
300
+ if (modeOnly.has(p)) continue;
301
+ if (scaffoldClean.has(p)) continue;
302
+ out.push(line);
303
+ }
304
+ return out;
305
+ }
306
+ function probeDocker() {
307
+ const containerRunning = sh("docker", [...COMPOSE_BASE, "ps", "-q", COMPOSE_SERVICE]).stdout.trim().length > 0;
308
+ const projectName = (sh("docker", [...COMPOSE_BASE, "config", "--format", "json"]).stdout.match(/"name"\s*:\s*"([^"]+)"/) ?? [])[1] ?? "";
309
+ let workspaceVolumeExists = false;
310
+ if (projectName) {
311
+ const vols = sh("docker", ["volume", "ls", "-q", "--filter", `name=^${projectName}_workspace$`]);
312
+ workspaceVolumeExists = vols.status === 0 && vols.stdout.trim().length > 0;
313
+ }
314
+ return { containerRunning, workspaceVolumeExists };
315
+ }
316
+ function preflightWorkspace(args) {
317
+ if (args.force) {
318
+ console.log("[teardown] --force given \u2014 skipping workspace-safety preflight");
319
+ return;
320
+ }
321
+ const state = probeDocker();
322
+ if (!state.containerRunning && !state.workspaceVolumeExists) {
323
+ console.log("[teardown] no launch-pod container or workspace volume present \u2014 nothing to check");
324
+ return;
325
+ }
326
+ console.log("[teardown] checking workspace for unsaved work\u2026");
327
+ const segs = loadIgnoreSegs();
328
+ const numstatRaw = workspaceSh("git -C /workspace diff --numstat 2>/dev/null");
329
+ const modeOnly = /* @__PURE__ */ new Set();
330
+ for (const line of numstatRaw.split("\n")) {
331
+ const parts = line.split(" ");
332
+ if (parts.length >= 3 && parts[0] === "0" && parts[1] === "0") {
333
+ modeOnly.add(parts[2]);
334
+ }
335
+ }
336
+ const rawDirty = workspaceSh("git -C /workspace status --porcelain 2>/dev/null | head -200");
337
+ const scaffoldClean = /* @__PURE__ */ new Set();
338
+ if (mcpJsonOnlyScaffoldedDiff()) scaffoldClean.add(".mcp.json");
339
+ const dirty = filterStatusSignal(rawDirty, modeOnly, segs, scaffoldClean);
340
+ const unpushedRaw = workspaceSh("git -C /workspace rev-list --count @{u}..HEAD 2>/dev/null || echo 0").trim();
341
+ const unpushed = parseInt(unpushedRaw || "0", 10);
342
+ const inflightRaw = workspaceSh(`grep -oE '"state":[[:space:]]*"(analyzing|queued)"' /workspace/.launchpod/radar-pings.json 2>/dev/null | wc -l | tr -d ' '`).trim();
343
+ const inflight = parseInt(inflightRaw || "0", 10);
344
+ let blocked = false;
345
+ if (dirty.length > 0) {
346
+ console.log("[teardown] \u2717 uncommitted changes in /workspace (after recall-ignore filter):");
347
+ for (const line of dirty) console.log(" " + line);
348
+ blocked = true;
349
+ }
350
+ if (unpushed > 0) {
351
+ console.log(`[teardown] \u2717 ${unpushed} unpushed commit(s) ahead of upstream`);
352
+ blocked = true;
353
+ }
354
+ if (inflight > 0) {
355
+ console.log(`[teardown] \u2717 ${inflight} in-flight or queued analyzer session(s) \u2014 output will be lost`);
356
+ blocked = true;
357
+ }
358
+ if (blocked) {
359
+ console.log("");
360
+ console.log("[teardown] aborting \u2014 workspace has unsaved work.");
361
+ console.log(" \u2022 commit + push from inside the container (docker compose exec launch-pod sh), then re-run, OR");
362
+ console.log(" \u2022 re-run with --force to discard the work and tear down anyway.");
363
+ process.exit(2);
364
+ }
365
+ console.log("[teardown] \u2713 workspace is clean (only recall-ignored noise present) \u2014 safe to tear down");
366
+ }
367
+ function parseEnvFile(path) {
368
+ const env = {};
369
+ for (const raw of (0, import_node_fs.readFileSync)(path, "utf8").split("\n")) {
370
+ const line = raw.trim();
371
+ if (!line || line.startsWith("#")) continue;
372
+ const eq = line.indexOf("=");
373
+ if (eq < 0) continue;
374
+ const key = line.slice(0, eq).trim();
375
+ let value = line.slice(eq + 1).trim();
376
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
377
+ value = value.slice(1, -1);
378
+ }
379
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) env[key] = value;
380
+ }
381
+ return env;
382
+ }
383
+ async function releaseWebhook() {
384
+ const psQ = sh("docker", [...COMPOSE_BASE, "ps", "-q", COMPOSE_SERVICE]);
385
+ if (psQ.status !== 0 || !psQ.stdout.trim()) {
386
+ console.log("[teardown] container not running \u2014 skipping webhook release");
387
+ console.log("[teardown] (any prior registration is now orphaned \u2014 clean up via cloud LS settings UI)");
388
+ return;
389
+ }
390
+ const radarJson = workspaceSh("cat /workspace/.launchpod/radar.json 2>/dev/null");
391
+ let endpointId = "";
392
+ try {
393
+ const j = JSON.parse(radarJson);
394
+ endpointId = j.endpointId ?? j.registration?.endpointId ?? j.webhook?.endpointId ?? "";
395
+ } catch {
396
+ }
397
+ if (!endpointId) {
398
+ console.log("[teardown] no endpoint_id found in /workspace/.launchpod/radar.json \u2014 skipping release");
399
+ return;
400
+ }
401
+ if (!(0, import_node_fs.existsSync)(ENV_PATH)) {
402
+ console.log(`[teardown] no .env found at ${ENV_PATH} \u2014 can't release webhook (manual cleanup needed)`);
403
+ return;
404
+ }
405
+ const env = parseEnvFile(ENV_PATH);
406
+ const pat = env.LS_PAT;
407
+ const orgSlug = env.LS_ORG_SLUG;
408
+ const projectSlug = env.LS_PROJECT_SLUG;
409
+ const serverUrl = env.LS_SERVER_URL || "https://launchsecure-v2.vercel.app";
410
+ if (!pat || !orgSlug || !projectSlug) {
411
+ console.log("[teardown] .env is missing LS_PAT / LS_ORG_SLUG / LS_PROJECT_SLUG \u2014 skipping release");
412
+ return;
413
+ }
414
+ console.log(`[teardown] releasing endpoint ${endpointId} from ${serverUrl}`);
415
+ try {
416
+ const mcp = new ProjectMcpClient({ serverUrl, pat, orgSlug, projectSlug });
417
+ await mcp.call("launchpod_webhook_release", { endpoint_id: endpointId });
418
+ console.log("[teardown] \u2713 webhook released");
419
+ } catch (err) {
420
+ const msg = err instanceof Error ? err.message : String(err);
421
+ console.log(`[teardown] \u26A0 webhook release failed (${msg}) \u2014 delete manually at:`);
422
+ console.log(` ${serverUrl}/${orgSlug}/projects/${projectSlug}/settings`);
423
+ }
424
+ }
425
+ function dockerComposeDown() {
426
+ console.log(`[teardown] docker compose -f ${COMPOSE_FILE} down -v`);
427
+ const down = (0, import_node_child_process.spawnSync)("docker", [...COMPOSE_BASE, "down", "-v"], { encoding: "utf8" });
428
+ const combined = (down.stdout || "") + (down.stderr || "");
429
+ for (const line of combined.split("\n")) {
430
+ if (/Removing|Removed|Stopping|Stopped/.test(line)) console.log(" " + line.trim());
431
+ }
432
+ if (down.status !== 0) {
433
+ console.error(`[teardown] docker compose down -v failed (status ${down.status})`);
434
+ process.exit(down.status ?? 1);
435
+ }
436
+ }
437
+ function cleanupLocalArtifacts(args) {
438
+ if (args.removeImage) {
439
+ console.log("[teardown] removing image launch-pod:local");
440
+ const rmi = sh("docker", ["rmi", "launch-pod:local"]);
441
+ console.log(rmi.status === 0 ? " removed" : " not present");
442
+ }
443
+ for (const f of (0, import_node_fs.readdirSync)(LAUNCH_POD_DIR)) {
444
+ if (f.startsWith("launchsecure-launch-kit-") && f.endsWith(".tgz")) {
445
+ console.log("[teardown] removing launch-kit tarball(s)");
446
+ try {
447
+ (0, import_node_fs.unlinkSync)((0, import_node_path.join)(LAUNCH_POD_DIR, f));
448
+ } catch {
449
+ }
450
+ }
451
+ }
452
+ if (args.removeEnv && (0, import_node_fs.existsSync)(ENV_PATH)) {
453
+ console.log("[teardown] removing .env (--remove-env)");
454
+ try {
455
+ (0, import_node_fs.unlinkSync)(ENV_PATH);
456
+ } catch {
457
+ }
458
+ } else if ((0, import_node_fs.existsSync)(ENV_PATH)) {
459
+ console.log("[teardown] keeping .env (use --remove-env to delete it next time)");
460
+ }
461
+ }
462
+ async function main() {
463
+ const args = parseArgs(process.argv.slice(2));
464
+ if (args.help) {
465
+ printHelp();
466
+ return;
467
+ }
468
+ ensureLaunchPodCompose();
469
+ preflightWorkspace(args);
470
+ await releaseWebhook();
471
+ dockerComposeDown();
472
+ cleanupLocalArtifacts(args);
473
+ console.log("[teardown] done.");
474
+ }
475
+ main().catch((err) => {
476
+ console.error("[teardown] fatal:", err);
477
+ process.exit(1);
478
+ });
@@ -167,7 +167,10 @@ var init_config = __esm({
167
167
  ".turbo",
168
168
  ".git",
169
169
  ".recall",
170
- "coverage"
170
+ "coverage",
171
+ ".pnpm-store",
172
+ ".launchsecure/graphs",
173
+ ".claude/settings.local.json"
171
174
  ],
172
175
  retention: {
173
176
  keepLast: 5e3,