@launchsecure/launch-kit 0.0.37 → 0.0.39
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/dist/beacon/beacon.mjs +308 -298
- package/dist/beacon/beacon.mjs.map +1 -1
- package/dist/beacon/beacon.umd.js +6 -6
- package/dist/beacon/beacon.umd.js.map +1 -1
- package/dist/beacon/types/internal/screenshot.d.ts.map +1 -1
- package/dist/chart-client/assets/index-ysGpLeOW.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-CMN3tlGP.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-ChmNX6bZ.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-Cn5TyL9s.js → _baseUniq-DOrnEQMI.js} +1 -1
- package/dist/deck-client/assets/{arc-D61amKYu.js → arc-DOWK7V3m.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-CpKrvC2W.js → architectureDiagram-Q4EWVU46-DPhzvk7q.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-Yj5OjxvG.js → blockDiagram-DXYQGD6D-CwAGy9lU.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-BIR810Tv.js → c4Diagram-AHTNJAMY-L_g_SS21.js} +1 -1
- package/dist/deck-client/assets/channel-DqiACUUq.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-BeSHwGvx.js → chunk-4BX2VUAB-RKm0LXpu.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-CCqzsLpg.js → chunk-4TB4RGXK-Bk0FUbxU.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-CuW_aq4-.js → chunk-55IACEB6-Cl3hja-M.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-Dl35ixYh.js → chunk-EDXVE4YY-CNIMQCV2.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-TwreZQTv.js → chunk-FMBD7UC4-DqOvWr1k.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-Ahfw8EUo.js → chunk-OYMX7WX6-1Kd7yK5u.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-DlE_zlU-.js → chunk-QZHKN3VN-6_kraYpP.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-Dj6QWzSg.js → chunk-YZCP3GAM-FgAwIWlo.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-D23cq2C3.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-D23cq2C3.js +1 -0
- package/dist/deck-client/assets/clone-C7jSigGq.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-BO1z5aOM.js → cose-bilkent-S5V4N54A-CigVnnPr.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-DVsw17fE.js → dagre-KV5264BT-DHZXTktX.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-6jYs7oZk.js → diagram-5BDNPKRD-H5k0eauU.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-6DbggeGE.js → diagram-G4DWMVQ6-Bg3dFhSY.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-CQtk1cSU.js → diagram-MMDJMWI5-CQLC410N.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-BR-gt75b.js → diagram-TYMM5635-DFTCHVkP.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-C9qMtjdY.js → erDiagram-SMLLAGMA-aiv9GZnL.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-CdaPhPYb.js → flowDiagram-DWJPFMVM-C6Fhvtsy.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-BRsZWUy4.js → ganttDiagram-T4ZO3ILL-DSaGMPM4.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-B8Z90jCj.js → gitGraphDiagram-UUTBAWPF-DMjL2Vix.js} +1 -1
- package/dist/deck-client/assets/{graph-my2Zphm4.js → graph-B7Vn5lkK.js} +1 -1
- package/dist/deck-client/assets/{index-DqAoYZwV.js → index-BD36e-tD.js} +64 -64
- package/dist/deck-client/assets/index-CGbNOpk9.css +1 -0
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-Csr9loin.js → infoDiagram-42DDH7IO-mNi4iygG.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-HWdvUNFi.js → ishikawaDiagram-UXIWVN3A-BwCUmUVt.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-CjYHG6EM.js → journeyDiagram-VCZTEJTY-C6qoqJmJ.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-CX3JdUu7.js → kanban-definition-6JOO6SKY-Dz1Tt3sA.js} +1 -1
- package/dist/deck-client/assets/{layout-Bcucv5Gi.js → layout-CZTyRhOG.js} +1 -1
- package/dist/deck-client/assets/{linear-CUGM5FJZ.js → linear--7n7iEvd.js} +1 -1
- package/dist/deck-client/assets/{min-Dw4g5w9z.js → min-Bh130DN8.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-C8oo61fg.js → mindmap-definition-QFDTVHPH-CfXcK1qH.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-D2WYGkq8.js → pieDiagram-DEJITSTG-DjVHLAVw.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-Vh00GISt.js → quadrantDiagram-34T5L4WZ-CXwvZ1i1.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-DxI-DFrN.js → requirementDiagram-MS252O5E-Cl6xm0fR.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-QgwyjasI.js → sankeyDiagram-XADWPNL6-BOH9sLyh.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-DmOmD5Ni.js → sequenceDiagram-FGHM5R23-BC1MYBn6.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-CRwglGg_.js → stateDiagram-FHFEXIEX-kNp9bv8K.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-hRsAFc2t.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-Dj9YGKOh.js → timeline-definition-GMOUNBTQ-DKnITsD4.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-xzIaOzEU.js → vennDiagram-DHZGUBPP-BdajXRrh.js} +1 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-BL802-su.js +162 -0
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BIYYh-JZ.js → wardleyDiagram-NUSXRM2D-B2hDCDl2.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-Cy9EoJCh.js → xychartDiagram-5P7HB3ND-CvnYFs51.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/cli.js +4 -0
- package/dist/server/council-entry.js +0 -0
- package/dist/server/deck-mcp-entry.js +3 -1
- package/dist/server/deck-serve.js +3 -1
- package/dist/server/fb-wizard.js +0 -0
- package/dist/server/init-entry.js +203 -39
- package/dist/server/radar-docker-init-entry.js +44 -14
- package/dist/server/radar-entrypoint-entry.js +0 -0
- package/dist/server/radar-teardown-entry.js +0 -0
- package/dist/server/rover-entry.js +44 -1
- package/package.json +23 -22
- package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +8 -7
- package/scaffolds/ls-marketplace/plugins/kit/commands/deactivate-statusline.md +2 -2
- package/scaffolds/ls-marketplace/plugins/kit/skills/comms/SKILL.md +88 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/project-info/SKILL.md +88 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/slides/SKILL.md +118 -0
- package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
- package/scaffolds/statusline/statusline-base.sh +20 -0
- package/dist/chart-client/assets/index-DJrjyXbN.css +0 -1
- package/dist/client/assets/index-8eSXr3Ez.css +0 -32
- package/dist/council-client/assets/index-4K0t2WrZ.css +0 -1
- package/dist/deck-client/assets/channel-DrJz2x-n.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-a3tg9w7z.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-a3tg9w7z.js +0 -1
- package/dist/deck-client/assets/clone-Dd7JBCL5.js +0 -1
- package/dist/deck-client/assets/index-ByqxPEgU.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BvZLEWAA.js +0 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-CEAay09T.js +0 -162
- /package/dist/chart-client/assets/{index-BgUxHxwE.js → index-BlsuXuQ1.js} +0 -0
- /package/dist/client/assets/{index-CUivaQnN.js → index-BA7BHBWT.js} +0 -0
- /package/dist/council-client/assets/{index-DN8HN_5K.js → index-jjBWyhry.js} +0 -0
|
@@ -274,6 +274,11 @@ function writeScripts() {
|
|
|
274
274
|
fs4.writeFileSync(WRAPPER_PATH, readScaffold("statusline-wrapper.sh"), { mode: 493 });
|
|
275
275
|
fs4.writeFileSync(CHIP_PATH, readScaffold("statusline-mcp.sh"), { mode: 493 });
|
|
276
276
|
}
|
|
277
|
+
function writeBaseScript() {
|
|
278
|
+
fs4.mkdirSync(LK_DIR, { recursive: true });
|
|
279
|
+
fs4.writeFileSync(BASE_PATH, readScaffold("statusline-base.sh"), { mode: 493 });
|
|
280
|
+
return `bash ${BASE_PATH}`;
|
|
281
|
+
}
|
|
277
282
|
function wrapperCommand(opts) {
|
|
278
283
|
const env = [];
|
|
279
284
|
if (opts.show) env.push(`LK_STATUSLINE_SHOW=${opts.show}`);
|
|
@@ -282,10 +287,7 @@ function wrapperCommand(opts) {
|
|
|
282
287
|
return `${prefix}bash ${WRAPPER_PATH}`;
|
|
283
288
|
}
|
|
284
289
|
function activateStatusline(opts = {}) {
|
|
285
|
-
const settings = readSettings();
|
|
286
|
-
if (!settings) {
|
|
287
|
-
return { ok: false, outcome: "no-settings", message: `no ~/.claude/settings.json \u2014 nothing to wrap` };
|
|
288
|
-
}
|
|
290
|
+
const settings = readSettings() ?? {};
|
|
289
291
|
const currentCmd = settings.statusLine?.command;
|
|
290
292
|
const alreadyWrapped = typeof currentCmd === "string" && currentCmd.includes(WRAPPER_PATH);
|
|
291
293
|
if (alreadyWrapped) {
|
|
@@ -304,20 +306,22 @@ function activateStatusline(opts = {}) {
|
|
|
304
306
|
}
|
|
305
307
|
return { ok: true, outcome: "refreshed", message: "statusline already wrapped \u2014 refreshed chip scripts only" };
|
|
306
308
|
}
|
|
307
|
-
if (!currentCmd) {
|
|
308
|
-
return { ok: false, outcome: "no-statusline", message: "no statusLine.command in ~/.claude/settings.json \u2014 launch-kit only extends an existing statusline" };
|
|
309
|
-
}
|
|
310
309
|
writeScripts();
|
|
310
|
+
const scaffolded = !currentCmd;
|
|
311
|
+
const original = scaffolded ? { type: "command", command: writeBaseScript() } : settings.statusLine;
|
|
311
312
|
const wrapped = {
|
|
312
313
|
...settings,
|
|
313
314
|
statusLine: { type: "command", command: wrapperCommand(opts) },
|
|
314
|
-
[ORIGINAL_KEY]:
|
|
315
|
+
[ORIGINAL_KEY]: original
|
|
315
316
|
};
|
|
316
317
|
writeSettings(wrapped);
|
|
317
318
|
const modeParts = [];
|
|
318
319
|
if (opts.show) modeParts.push(`chips: ${opts.show}`);
|
|
319
320
|
if (opts.compact) modeParts.push("compact mode");
|
|
320
321
|
const modeDesc = modeParts.length > 0 ? ` (${modeParts.join(", ")})` : "";
|
|
322
|
+
if (scaffolded) {
|
|
323
|
+
return { ok: true, outcome: "scaffolded", message: `no statusline found \u2014 scaffolded Claude Code's default base line and wrapped it with chips${modeDesc}; base stashed under ${ORIGINAL_KEY}` };
|
|
324
|
+
}
|
|
321
325
|
return { ok: true, outcome: "activated", message: `wrapped statusLine.command${modeDesc}; original stashed under ${ORIGINAL_KEY}` };
|
|
322
326
|
}
|
|
323
327
|
function deactivateStatusline() {
|
|
@@ -327,18 +331,26 @@ function deactivateStatusline() {
|
|
|
327
331
|
if (!original) {
|
|
328
332
|
return { ok: false, outcome: "not-active", message: `no ${ORIGINAL_KEY} in settings.json \u2014 statusline isn't wrapped by launch-kit` };
|
|
329
333
|
}
|
|
330
|
-
const
|
|
334
|
+
const wasScaffolded = original.command?.includes(BASE_PATH) ?? false;
|
|
335
|
+
const restored = { ...settings };
|
|
336
|
+
if (wasScaffolded) delete restored.statusLine;
|
|
337
|
+
else restored.statusLine = original;
|
|
331
338
|
delete restored[ORIGINAL_KEY];
|
|
332
339
|
writeSettings(restored);
|
|
333
|
-
|
|
340
|
+
const toRemove = wasScaffolded ? [WRAPPER_PATH, CHIP_PATH, BASE_PATH] : [WRAPPER_PATH, CHIP_PATH];
|
|
341
|
+
for (const p of toRemove) {
|
|
334
342
|
try {
|
|
335
343
|
fs4.unlinkSync(p);
|
|
336
344
|
} catch {
|
|
337
345
|
}
|
|
338
346
|
}
|
|
339
|
-
return {
|
|
347
|
+
return {
|
|
348
|
+
ok: true,
|
|
349
|
+
outcome: "deactivated",
|
|
350
|
+
message: wasScaffolded ? "removed launch-kit statusline and scaffolded base line \u2014 back to no statusline" : "restored original statusLine.command"
|
|
351
|
+
};
|
|
340
352
|
}
|
|
341
|
-
var fs4, path4, import_node_os, LK_DIR, WRAPPER_PATH, CHIP_PATH, SETTINGS_PATH, ORIGINAL_KEY;
|
|
353
|
+
var fs4, path4, import_node_os, LK_DIR, WRAPPER_PATH, CHIP_PATH, BASE_PATH, SETTINGS_PATH, ORIGINAL_KEY;
|
|
342
354
|
var init_statusline_install = __esm({
|
|
343
355
|
"src/server/statusline-install.ts"() {
|
|
344
356
|
"use strict";
|
|
@@ -348,6 +360,7 @@ var init_statusline_install = __esm({
|
|
|
348
360
|
LK_DIR = path4.join((0, import_node_os.homedir)(), ".launchsecure");
|
|
349
361
|
WRAPPER_PATH = path4.join(LK_DIR, "statusline-wrapper.sh");
|
|
350
362
|
CHIP_PATH = path4.join(LK_DIR, "statusline-mcp.sh");
|
|
363
|
+
BASE_PATH = path4.join(LK_DIR, "statusline-base.sh");
|
|
351
364
|
SETTINGS_PATH = path4.join((0, import_node_os.homedir)(), ".claude", "settings.json");
|
|
352
365
|
ORIGINAL_KEY = "_launchKitStatuslineOriginal";
|
|
353
366
|
}
|
|
@@ -857,7 +870,13 @@ async function ensureAccessIdp(input) {
|
|
|
857
870
|
return created.result.id;
|
|
858
871
|
}
|
|
859
872
|
async function ensureAccessApp(input) {
|
|
860
|
-
const
|
|
873
|
+
const { service } = input;
|
|
874
|
+
const appDomain = service.path ? `${service.hostname}${service.path}` : service.hostname;
|
|
875
|
+
const policy = service.bypass ? {
|
|
876
|
+
name: "launch-kit-public-bypass",
|
|
877
|
+
decision: "bypass",
|
|
878
|
+
include: [{ everyone: {} }]
|
|
879
|
+
} : {
|
|
861
880
|
name: "launch-kit-org-allow",
|
|
862
881
|
decision: "allow",
|
|
863
882
|
include: [
|
|
@@ -870,12 +889,17 @@ async function ensureAccessApp(input) {
|
|
|
870
889
|
}
|
|
871
890
|
]
|
|
872
891
|
};
|
|
873
|
-
const body = {
|
|
874
|
-
name: `launch-kit ${
|
|
875
|
-
domain:
|
|
892
|
+
const body = service.bypass ? {
|
|
893
|
+
name: `launch-kit ${appDomain} (public)`,
|
|
894
|
+
domain: appDomain,
|
|
895
|
+
type: "self_hosted",
|
|
896
|
+
policies: [policy]
|
|
897
|
+
} : {
|
|
898
|
+
name: `launch-kit ${appDomain}`,
|
|
899
|
+
domain: appDomain,
|
|
876
900
|
type: "self_hosted",
|
|
877
901
|
// Bot terminal = RCE surface → short session. Read portals = a workday.
|
|
878
|
-
session_duration:
|
|
902
|
+
session_duration: service.strict ? "30m" : "24h",
|
|
879
903
|
allowed_idps: [input.idpId],
|
|
880
904
|
auto_redirect_to_identity: true,
|
|
881
905
|
policies: [policy]
|
|
@@ -886,7 +910,7 @@ async function ensureAccessApp(input) {
|
|
|
886
910
|
path: `/accounts/${input.accountId}/access/apps`
|
|
887
911
|
});
|
|
888
912
|
if (!list.success) fail(list, "list access apps");
|
|
889
|
-
const existing = (list.result ?? []).find((a) => a.domain ===
|
|
913
|
+
const existing = (list.result ?? []).find((a) => a.domain === appDomain);
|
|
890
914
|
if (existing) {
|
|
891
915
|
const upd = await cf2({
|
|
892
916
|
apiToken: input.apiToken,
|
|
@@ -894,7 +918,7 @@ async function ensureAccessApp(input) {
|
|
|
894
918
|
path: `/accounts/${input.accountId}/access/apps/${existing.id}`,
|
|
895
919
|
body
|
|
896
920
|
});
|
|
897
|
-
if (!upd.success || !upd.result) fail(upd, `update access app ${
|
|
921
|
+
if (!upd.success || !upd.result) fail(upd, `update access app ${appDomain}`);
|
|
898
922
|
return upd.result.id;
|
|
899
923
|
}
|
|
900
924
|
const created = await cf2({
|
|
@@ -903,7 +927,7 @@ async function ensureAccessApp(input) {
|
|
|
903
927
|
path: `/accounts/${input.accountId}/access/apps`,
|
|
904
928
|
body
|
|
905
929
|
});
|
|
906
|
-
if (!created.success || !created.result) fail(created, `create access app ${
|
|
930
|
+
if (!created.success || !created.result) fail(created, `create access app ${appDomain}`);
|
|
907
931
|
return created.result.id;
|
|
908
932
|
}
|
|
909
933
|
async function provisionAccess(input) {
|
|
@@ -922,7 +946,8 @@ async function provisionAccess(input) {
|
|
|
922
946
|
saveState2(input.stateFile, { idpId, accountId: input.accountId });
|
|
923
947
|
const appIds = {};
|
|
924
948
|
for (const service of input.services) {
|
|
925
|
-
|
|
949
|
+
const appDomain = service.path ? `${service.hostname}${service.path}` : service.hostname;
|
|
950
|
+
appIds[appDomain] = await ensureAccessApp({
|
|
926
951
|
apiToken: input.apiToken,
|
|
927
952
|
accountId: input.accountId,
|
|
928
953
|
idpId,
|
|
@@ -943,6 +968,15 @@ var init_cf_access = __esm({
|
|
|
943
968
|
}
|
|
944
969
|
});
|
|
945
970
|
|
|
971
|
+
// src/server/radar/registration.ts
|
|
972
|
+
var RECEIVER_PATH;
|
|
973
|
+
var init_registration = __esm({
|
|
974
|
+
"src/server/radar/registration.ts"() {
|
|
975
|
+
"use strict";
|
|
976
|
+
RECEIVER_PATH = "/api/radar/ingest";
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
|
|
946
980
|
// src/server/radar-docker-init-entry.ts
|
|
947
981
|
var radar_docker_init_entry_exports = {};
|
|
948
982
|
__export(radar_docker_init_entry_exports, {
|
|
@@ -1195,20 +1229,31 @@ async function maybeProvisionAccess(bundle, ingress) {
|
|
|
1195
1229
|
const skipped = [];
|
|
1196
1230
|
for (const [name, hostname] of Object.entries(ingress.hostnames)) {
|
|
1197
1231
|
const cfg = GATED_SERVICES[name];
|
|
1198
|
-
if (cfg)
|
|
1199
|
-
|
|
1232
|
+
if (!cfg) {
|
|
1233
|
+
skipped.push(name);
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1236
|
+
services.push({ hostname, strict: cfg.strict });
|
|
1237
|
+
for (const path6 of cfg.publicPaths ?? []) {
|
|
1238
|
+
services.push({ hostname, path: path6, bypass: true });
|
|
1239
|
+
}
|
|
1200
1240
|
}
|
|
1201
1241
|
if (skipped.length > 0) {
|
|
1202
1242
|
console.log(`[entrypoint] CF Access: leaving machine surface(s) ungated: ${skipped.join(", ")}`);
|
|
1203
1243
|
}
|
|
1204
1244
|
if (services.length === 0) {
|
|
1205
|
-
console.log("[entrypoint] CF Access: no human-facing service to gate (bot/preview not provisioned)");
|
|
1245
|
+
console.log("[entrypoint] CF Access: no human-facing service to gate (bot/preview/radar/deck not provisioned)");
|
|
1206
1246
|
return;
|
|
1207
1247
|
}
|
|
1208
1248
|
const serverUrl = process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app";
|
|
1209
1249
|
const pat = requireEnv("LS_PAT");
|
|
1210
1250
|
const stateFile = "/workspace/.launchpod/launch-kit-access.json";
|
|
1211
|
-
|
|
1251
|
+
const gatedHosts = services.filter((s) => !s.bypass).map((s) => s.hostname);
|
|
1252
|
+
const bypassed = services.filter((s) => s.bypass).map((s) => `${s.hostname}${s.path ?? ""}`);
|
|
1253
|
+
console.log(`[entrypoint] gating ${gatedHosts.join(", ")} behind CF Access (IdP: ${serverUrl})`);
|
|
1254
|
+
if (bypassed.length > 0) {
|
|
1255
|
+
console.log(`[entrypoint] CF Access: public bypass for ${bypassed.join(", ")}`);
|
|
1256
|
+
}
|
|
1212
1257
|
const result = await provisionAccess({
|
|
1213
1258
|
apiToken: token,
|
|
1214
1259
|
accountId,
|
|
@@ -1364,6 +1409,7 @@ var init_radar_docker_init_entry = __esm({
|
|
|
1364
1409
|
init_launch_kit_services();
|
|
1365
1410
|
init_cf_ingress();
|
|
1366
1411
|
init_cf_access();
|
|
1412
|
+
init_registration();
|
|
1367
1413
|
REQUIRED_ENV = [
|
|
1368
1414
|
"CLAUDE_CREDENTIALS_B64",
|
|
1369
1415
|
"LS_PAT",
|
|
@@ -1378,7 +1424,11 @@ var init_radar_docker_init_entry = __esm({
|
|
|
1378
1424
|
// Claude web terminal — live drivable shell ⇒ RCE surface ⇒ short session.
|
|
1379
1425
|
bot: { strict: true },
|
|
1380
1426
|
// The user's own dev/preview server — a workday-length session is fine.
|
|
1381
|
-
preview: { strict: false }
|
|
1427
|
+
preview: { strict: false },
|
|
1428
|
+
// Radar: gate the UI, bypass the (HMAC-verified) webhook receiver path.
|
|
1429
|
+
radar: { strict: false, publicPaths: [RECEIVER_PATH] },
|
|
1430
|
+
// Deck: gate the whole host — its push is localhost-only, never hits the edge.
|
|
1431
|
+
deck: { strict: false }
|
|
1382
1432
|
};
|
|
1383
1433
|
if (!process.env.VITEST) {
|
|
1384
1434
|
main().catch((err) => {
|
|
@@ -1584,6 +1634,7 @@ var ALL_STEP_IDS = [
|
|
|
1584
1634
|
"migrate-safety",
|
|
1585
1635
|
"ls-marketplace",
|
|
1586
1636
|
"recall-hook",
|
|
1637
|
+
"project-info",
|
|
1587
1638
|
"statusline"
|
|
1588
1639
|
];
|
|
1589
1640
|
var PRESETS = {
|
|
@@ -1591,6 +1642,7 @@ var PRESETS = {
|
|
|
1591
1642
|
refresh: ["resolve", "clone", "install", "onboard", "recall"]
|
|
1592
1643
|
};
|
|
1593
1644
|
var LAUNCH_KIT_PKG = "@launchsecure/launch-kit";
|
|
1645
|
+
var LAUNCH_KIT_PKG_LATEST = `${LAUNCH_KIT_PKG}@latest`;
|
|
1594
1646
|
var LAUNCH_KIT_TOOLS_GUIDE_STATIC_HEAD = `
|
|
1595
1647
|
Wired in Claude Code (.mcp.json):
|
|
1596
1648
|
launch-secure \u2014 LS API: work items, comms, secrets, members, board
|
|
@@ -1661,6 +1713,7 @@ var KNOWN_BOOL_FLAGS = /* @__PURE__ */ new Set([
|
|
|
1661
1713
|
"--no-migrate-safety",
|
|
1662
1714
|
"--no-ls-marketplace",
|
|
1663
1715
|
"--no-recall-hook",
|
|
1716
|
+
"--no-project-info",
|
|
1664
1717
|
"--refresh-scaffolds",
|
|
1665
1718
|
"--quiet",
|
|
1666
1719
|
"--force",
|
|
@@ -1694,6 +1747,7 @@ function parseArgs(argv) {
|
|
|
1694
1747
|
noMigrateSafety: false,
|
|
1695
1748
|
noLsMarketplace: false,
|
|
1696
1749
|
noRecallHook: false,
|
|
1750
|
+
noProjectInfo: false,
|
|
1697
1751
|
refreshScaffolds: false,
|
|
1698
1752
|
quiet: false,
|
|
1699
1753
|
force: false,
|
|
@@ -1738,6 +1792,10 @@ function parseArgs(argv) {
|
|
|
1738
1792
|
args.noRecallHook = true;
|
|
1739
1793
|
continue;
|
|
1740
1794
|
}
|
|
1795
|
+
if (raw === "--no-project-info") {
|
|
1796
|
+
args.noProjectInfo = true;
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1741
1799
|
if (raw === "--refresh-scaffolds") {
|
|
1742
1800
|
args.refreshScaffolds = true;
|
|
1743
1801
|
continue;
|
|
@@ -1872,6 +1930,8 @@ Options:
|
|
|
1872
1930
|
--no-migrate-safety Skip refreshing the migrate-safety scaffold.
|
|
1873
1931
|
--no-ls-marketplace Skip refreshing the launch-secure marketplace.
|
|
1874
1932
|
--no-recall-hook Skip refreshing the recall-hook scaffold.
|
|
1933
|
+
--no-project-info Skip wiring the gitignored CLAUDE.md-imported
|
|
1934
|
+
project-info snapshot (/kit:project-info refreshes it).
|
|
1875
1935
|
--refresh-scaffolds Force-overwrite migrate-safety files (default is to
|
|
1876
1936
|
preserve user edits). Use this to pull updates
|
|
1877
1937
|
published to @launchsecure/launch-kit.
|
|
@@ -1907,8 +1967,8 @@ Subcommands:
|
|
|
1907
1967
|
Env resolved from --env \u2192 $LS_ENV \u2192 server-side project default \u2192
|
|
1908
1968
|
single-env auto-pick. See \`launch-kit secrets --help\`.
|
|
1909
1969
|
statusline activate Wrap ~/.claude/settings.json's statusLine.command so MCP daemon
|
|
1910
|
-
chips (recall, chart, deck, council) get appended.
|
|
1911
|
-
|
|
1970
|
+
chips (recall, chart, deck, council) get appended. If no
|
|
1971
|
+
statusline exists, scaffolds Claude Code's default base line first.
|
|
1912
1972
|
statusline deactivate Restore the original statusLine.command and remove kit scripts.
|
|
1913
1973
|
|
|
1914
1974
|
Usage:
|
|
@@ -1979,7 +2039,7 @@ Options:
|
|
|
1979
2039
|
|
|
1980
2040
|
Step selection (one-command flow \u2014 compose over the preset):
|
|
1981
2041
|
Steps: resolve, clone, cred, mcp, gitignore, install, onboard, recall,
|
|
1982
|
-
migrate-safety, ls-marketplace, recall-hook, statusline
|
|
2042
|
+
migrate-safety, ls-marketplace, recall-hook, project-info, statusline
|
|
1983
2043
|
--preset=<name> init (everything) or refresh (skips resolve/clone/
|
|
1984
2044
|
install/onboard/recall). Default: init. \`init\` and
|
|
1985
2045
|
\`refresh\` subcommands just select this.
|
|
@@ -2285,19 +2345,19 @@ function buildLaunchKitMcpEntries(cfg) {
|
|
|
2285
2345
|
},
|
|
2286
2346
|
"launch-chart": {
|
|
2287
2347
|
command: "npx",
|
|
2288
|
-
args: ["-y", "-p",
|
|
2348
|
+
args: ["-y", "-p", LAUNCH_KIT_PKG_LATEST, "launch-chart"]
|
|
2289
2349
|
},
|
|
2290
2350
|
"launch-deck": {
|
|
2291
2351
|
command: "npx",
|
|
2292
|
-
args: ["-y", "-p",
|
|
2352
|
+
args: ["-y", "-p", LAUNCH_KIT_PKG_LATEST, "launch-deck"]
|
|
2293
2353
|
},
|
|
2294
2354
|
"launch-orbit": {
|
|
2295
2355
|
command: "npx",
|
|
2296
|
-
args: ["-y", "-p",
|
|
2356
|
+
args: ["-y", "-p", LAUNCH_KIT_PKG_LATEST, "launch-orbit", "mcp"]
|
|
2297
2357
|
},
|
|
2298
2358
|
"launch-recall": {
|
|
2299
2359
|
command: "npx",
|
|
2300
|
-
args: ["-y", "-p",
|
|
2360
|
+
args: ["-y", "-p", LAUNCH_KIT_PKG_LATEST, "launch-recall", "mcp"]
|
|
2301
2361
|
}
|
|
2302
2362
|
};
|
|
2303
2363
|
}
|
|
@@ -2317,6 +2377,18 @@ function mergeMcpEntry(existing, ours) {
|
|
|
2317
2377
|
}
|
|
2318
2378
|
return merged;
|
|
2319
2379
|
}
|
|
2380
|
+
function pinLaunchKitLatest(servers) {
|
|
2381
|
+
const fixed = [];
|
|
2382
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
2383
|
+
if (entry.command !== "npx" || !Array.isArray(entry.args)) continue;
|
|
2384
|
+
const i = entry.args.indexOf(LAUNCH_KIT_PKG);
|
|
2385
|
+
if (i !== -1) {
|
|
2386
|
+
entry.args[i] = LAUNCH_KIT_PKG_LATEST;
|
|
2387
|
+
fixed.push(name);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
return fixed;
|
|
2391
|
+
}
|
|
2320
2392
|
function mergeMcpFile(targetDir, launchKitEntries) {
|
|
2321
2393
|
const p = path5.join(targetDir, ".mcp.json");
|
|
2322
2394
|
const hadExisting = fs5.existsSync(p);
|
|
@@ -2342,12 +2414,14 @@ function mergeMcpFile(targetDir, launchKitEntries) {
|
|
|
2342
2414
|
merged.mcpServers[name] = entry;
|
|
2343
2415
|
}
|
|
2344
2416
|
}
|
|
2417
|
+
const pinned = pinLaunchKitLatest(merged.mcpServers);
|
|
2345
2418
|
if (DRY_RUN) {
|
|
2346
2419
|
const action = hadExisting && existingServerCount > 0 ? "would merge into" : "would write";
|
|
2347
|
-
dryNote(`${action} .mcp.json \u2014 overwriting [${overwrites.join(", ") || "none"}], adding [${additions.join(", ") || "none"}]`);
|
|
2420
|
+
dryNote(`${action} .mcp.json \u2014 overwriting [${overwrites.join(", ") || "none"}], adding [${additions.join(", ") || "none"}], pinning @latest on [${pinned.join(", ") || "none"}]`);
|
|
2348
2421
|
return { status: "skipped", summary: "(dry-run)" };
|
|
2349
2422
|
}
|
|
2350
2423
|
fs5.writeFileSync(p, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
2424
|
+
if (pinned.length > 0) info(`pinned @latest on ${pinned.length} launch-kit MCP entr${pinned.length === 1 ? "y" : "ies"}: ${pinned.join(", ")}`);
|
|
2351
2425
|
const verb = hadExisting && existingServerCount > 0 ? "merged" : "wrote";
|
|
2352
2426
|
ok(`${verb === "merged" ? "merged into" : "wrote"} .mcp.json (${Object.keys(launchKitEntries).length} launch-kit entries)`);
|
|
2353
2427
|
const total = Object.keys(launchKitEntries).length;
|
|
@@ -2433,14 +2507,14 @@ function runRecallInit(repoDir) {
|
|
|
2433
2507
|
const recallEntry = path5.resolve(__dirname, "recall-entry.js");
|
|
2434
2508
|
const useSibling = fs5.existsSync(recallEntry);
|
|
2435
2509
|
const cmd = useSibling ? process.execPath : "npx";
|
|
2436
|
-
const args = useSibling ? [recallEntry, "init"] : ["-y", "-p",
|
|
2510
|
+
const args = useSibling ? [recallEntry, "init"] : ["-y", "-p", LAUNCH_KIT_PKG_LATEST, "launch-recall", "init"];
|
|
2437
2511
|
if (DRY_RUN) {
|
|
2438
2512
|
dryNote(`would run launch-recall init: ${cmd} ${args.join(" ")} (cwd: ${repoDir})`);
|
|
2439
2513
|
return;
|
|
2440
2514
|
}
|
|
2441
2515
|
const res = (0, import_node_child_process3.spawnSync)(cmd, args, { cwd: repoDir, stdio: "inherit" });
|
|
2442
2516
|
if (res.status !== 0) {
|
|
2443
|
-
info(`\u26A0 launch-recall init failed (exit ${res.status}). Main onboarding is complete \u2014 you can retry later: cd ${path5.basename(repoDir)} && npx -y -p ${
|
|
2517
|
+
info(`\u26A0 launch-recall init failed (exit ${res.status}). Main onboarding is complete \u2014 you can retry later: cd ${path5.basename(repoDir)} && npx -y -p ${LAUNCH_KIT_PKG_LATEST} launch-recall init`);
|
|
2444
2518
|
return;
|
|
2445
2519
|
}
|
|
2446
2520
|
ok(`launch-recall ready (shadow git initialized)`);
|
|
@@ -2731,7 +2805,7 @@ function wireRecallHook(targetDir) {
|
|
|
2731
2805
|
}
|
|
2732
2806
|
function tryActivateStatusline() {
|
|
2733
2807
|
if (DRY_RUN) {
|
|
2734
|
-
dryNote(`would wrap ~/.claude/settings.json statusLine.command with launch-kit's MCP chip wrapper (
|
|
2808
|
+
dryNote(`would wrap ~/.claude/settings.json statusLine.command with launch-kit's MCP chip wrapper (scaffolds Claude Code's default base line first if none is configured)`);
|
|
2735
2809
|
return { status: "skipped", summary: "(dry-run)" };
|
|
2736
2810
|
}
|
|
2737
2811
|
const res = activateStatusline();
|
|
@@ -2739,6 +2813,10 @@ function tryActivateStatusline() {
|
|
|
2739
2813
|
ok(`statusline wrapped \u2014 MCP chips will render alongside your existing statusline next session`);
|
|
2740
2814
|
return { status: "ok", summary: "wrapped \u2014 MCP chips render next session" };
|
|
2741
2815
|
}
|
|
2816
|
+
if (res.ok && res.outcome === "scaffolded") {
|
|
2817
|
+
ok(`no statusline found \u2014 scaffolded Claude Code's default base line; MCP chips render alongside it next session`);
|
|
2818
|
+
return { status: "ok", summary: "scaffolded default base line + chips" };
|
|
2819
|
+
}
|
|
2742
2820
|
if (res.ok && res.outcome === "refreshed") {
|
|
2743
2821
|
info(`statusline already wrapped \u2014 refreshed chip scripts`);
|
|
2744
2822
|
return { status: "ok", summary: "chip scripts refreshed" };
|
|
@@ -2797,8 +2875,8 @@ async function main2() {
|
|
|
2797
2875
|
if (action === "activate") res = activateStatusline2({ show: showArg, compact: compactArg });
|
|
2798
2876
|
else if (action === "deactivate") res = deactivateStatusline2();
|
|
2799
2877
|
else fail3(`Unknown statusline action: "${action}". Supported: activate, deactivate.`);
|
|
2800
|
-
|
|
2801
|
-
|
|
2878
|
+
const mark = res.ok ? "\u2713" : "\u2717";
|
|
2879
|
+
console.log(`[launch-kit] ${mark} statusline ${res.outcome} \u2014 ${res.message}`);
|
|
2802
2880
|
return;
|
|
2803
2881
|
}
|
|
2804
2882
|
if (subcommand === "secrets") {
|
|
@@ -2876,6 +2954,7 @@ function resolveEnabledSteps(args) {
|
|
|
2876
2954
|
if (args.noMigrateSafety) enabled.delete("migrate-safety");
|
|
2877
2955
|
if (args.noLsMarketplace) enabled.delete("ls-marketplace");
|
|
2878
2956
|
if (args.noRecallHook) enabled.delete("recall-hook");
|
|
2957
|
+
if (args.noProjectInfo) enabled.delete("project-info");
|
|
2879
2958
|
return enabled;
|
|
2880
2959
|
}
|
|
2881
2960
|
async function stepResolve(ctx) {
|
|
@@ -3105,6 +3184,90 @@ function stepStatusline(_ctx) {
|
|
|
3105
3184
|
const slR = tryActivateStatusline();
|
|
3106
3185
|
if (slR) phase("statusline", slR);
|
|
3107
3186
|
}
|
|
3187
|
+
var PROJECT_INFO_REL = ".claude/launch-kit/project-info.md";
|
|
3188
|
+
function ensureClaudeMdImport(targetDir) {
|
|
3189
|
+
const p = path5.join(targetDir, "CLAUDE.md");
|
|
3190
|
+
const importLine = `@${PROJECT_INFO_REL}`;
|
|
3191
|
+
const block = `<!-- launch-kit:project-info \u2014 generated import; safe to move, keep the line -->
|
|
3192
|
+
${importLine}
|
|
3193
|
+
<!-- /launch-kit:project-info -->
|
|
3194
|
+
`;
|
|
3195
|
+
if (fs5.existsSync(p)) {
|
|
3196
|
+
const content = fs5.readFileSync(p, "utf-8");
|
|
3197
|
+
if (content.includes(importLine)) return "in-sync";
|
|
3198
|
+
if (DRY_RUN) {
|
|
3199
|
+
dryNote(`would add ${importLine} import to CLAUDE.md`);
|
|
3200
|
+
return "added";
|
|
3201
|
+
}
|
|
3202
|
+
fs5.writeFileSync(p, content + (content.endsWith("\n") ? "" : "\n") + "\n" + block, "utf-8");
|
|
3203
|
+
return "added";
|
|
3204
|
+
}
|
|
3205
|
+
if (DRY_RUN) {
|
|
3206
|
+
dryNote(`would create CLAUDE.md with ${importLine} import`);
|
|
3207
|
+
return "created";
|
|
3208
|
+
}
|
|
3209
|
+
fs5.writeFileSync(p, `# CLAUDE.md
|
|
3210
|
+
|
|
3211
|
+
${block}`, "utf-8");
|
|
3212
|
+
return "created";
|
|
3213
|
+
}
|
|
3214
|
+
function renderProjectInfoSkeleton(ctx) {
|
|
3215
|
+
const cfg = ctx.cfg;
|
|
3216
|
+
const projectName = ctx.resolved?.projectName ?? cfg.projectSlug;
|
|
3217
|
+
const repo = ctx.resolved?.repositoryUrl ?? "\u2014";
|
|
3218
|
+
const todo = "_Not populated yet \u2014 run `/kit:project-info` in Claude Code._";
|
|
3219
|
+
return [
|
|
3220
|
+
`# Project Info \u2014 ${projectName}`,
|
|
3221
|
+
``,
|
|
3222
|
+
`<!-- launch-kit:generated \u2014 local, git-ignored snapshot. NOT a source of truth. -->`,
|
|
3223
|
+
`> Wired by \`launch-kit\` init. This is a **local, git-ignored snapshot** of`,
|
|
3224
|
+
`> project facts so Claude doesn't re-query them every session. Members,`,
|
|
3225
|
+
`> providers, and environments are filled from LaunchSecure by the`,
|
|
3226
|
+
`> \`/kit:project-info\` skill \u2014 run it to populate or refresh. Treat as a`,
|
|
3227
|
+
`> cache: it can go stale, so re-run after team/provider/env changes.`,
|
|
3228
|
+
``,
|
|
3229
|
+
`## Identity`,
|
|
3230
|
+
`- **Org:** ${cfg.orgSlug}`,
|
|
3231
|
+
`- **Project:** ${projectName} (\`${cfg.projectSlug}\`)`,
|
|
3232
|
+
`- **Server:** ${cfg.serverUrl}`,
|
|
3233
|
+
`- **Course:** ${ctx.courseName ?? "\u2014"}`,
|
|
3234
|
+
`- **Repo:** ${repo}`,
|
|
3235
|
+
``,
|
|
3236
|
+
`## Members`,
|
|
3237
|
+
todo,
|
|
3238
|
+
``,
|
|
3239
|
+
`## Providers / integrations`,
|
|
3240
|
+
todo,
|
|
3241
|
+
``,
|
|
3242
|
+
`## Environments`,
|
|
3243
|
+
todo,
|
|
3244
|
+
``
|
|
3245
|
+
].join("\n");
|
|
3246
|
+
}
|
|
3247
|
+
function stepProjectInfo(ctx) {
|
|
3248
|
+
if (!ctx.cfg) {
|
|
3249
|
+
phase("project-info", { status: "skipped", summary: "no cred resolved" });
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
ensureGitignoreLine(ctx.targetDir, PROJECT_INFO_REL);
|
|
3253
|
+
const importResult = ensureClaudeMdImport(ctx.targetDir);
|
|
3254
|
+
const dest = path5.join(ctx.targetDir, PROJECT_INFO_REL);
|
|
3255
|
+
const existed = fs5.existsSync(dest);
|
|
3256
|
+
if (!existed) {
|
|
3257
|
+
if (DRY_RUN) {
|
|
3258
|
+
dryNote(`would write ${PROJECT_INFO_REL} identity skeleton`);
|
|
3259
|
+
} else {
|
|
3260
|
+
fs5.mkdirSync(path5.dirname(dest), { recursive: true });
|
|
3261
|
+
fs5.writeFileSync(dest, renderProjectInfoSkeleton(ctx), "utf-8");
|
|
3262
|
+
ok(`wrote ${PROJECT_INFO_REL}`);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
const summary = existed ? `kept file \xB7 CLAUDE.md ${importResult} \xB7 /kit:project-info to refresh` : `wrote skeleton \xB7 CLAUDE.md ${importResult} \xB7 /kit:project-info to populate`;
|
|
3266
|
+
phase("project-info", {
|
|
3267
|
+
status: existed && importResult === "in-sync" ? "in-sync" : "ok",
|
|
3268
|
+
summary
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3108
3271
|
var STEPS = [
|
|
3109
3272
|
{ id: "resolve", run: stepResolve },
|
|
3110
3273
|
{ id: "clone", requires: ["resolve"], run: stepClone },
|
|
@@ -3117,6 +3280,7 @@ var STEPS = [
|
|
|
3117
3280
|
{ id: "migrate-safety", run: stepMigrateSafety },
|
|
3118
3281
|
{ id: "ls-marketplace", run: stepLsMarketplace },
|
|
3119
3282
|
{ id: "recall-hook", run: stepRecallHook },
|
|
3283
|
+
{ id: "project-info", requires: ["cred"], run: stepProjectInfo },
|
|
3120
3284
|
{ id: "statusline", run: stepStatusline }
|
|
3121
3285
|
];
|
|
3122
3286
|
function buildCtx(args, enabled) {
|
|
@@ -520,7 +520,13 @@ async function ensureAccessIdp(input) {
|
|
|
520
520
|
return created.result.id;
|
|
521
521
|
}
|
|
522
522
|
async function ensureAccessApp(input) {
|
|
523
|
-
const
|
|
523
|
+
const { service } = input;
|
|
524
|
+
const appDomain = service.path ? `${service.hostname}${service.path}` : service.hostname;
|
|
525
|
+
const policy = service.bypass ? {
|
|
526
|
+
name: "launch-kit-public-bypass",
|
|
527
|
+
decision: "bypass",
|
|
528
|
+
include: [{ everyone: {} }]
|
|
529
|
+
} : {
|
|
524
530
|
name: "launch-kit-org-allow",
|
|
525
531
|
decision: "allow",
|
|
526
532
|
include: [
|
|
@@ -533,12 +539,17 @@ async function ensureAccessApp(input) {
|
|
|
533
539
|
}
|
|
534
540
|
]
|
|
535
541
|
};
|
|
536
|
-
const body = {
|
|
537
|
-
name: `launch-kit ${
|
|
538
|
-
domain:
|
|
542
|
+
const body = service.bypass ? {
|
|
543
|
+
name: `launch-kit ${appDomain} (public)`,
|
|
544
|
+
domain: appDomain,
|
|
545
|
+
type: "self_hosted",
|
|
546
|
+
policies: [policy]
|
|
547
|
+
} : {
|
|
548
|
+
name: `launch-kit ${appDomain}`,
|
|
549
|
+
domain: appDomain,
|
|
539
550
|
type: "self_hosted",
|
|
540
551
|
// Bot terminal = RCE surface → short session. Read portals = a workday.
|
|
541
|
-
session_duration:
|
|
552
|
+
session_duration: service.strict ? "30m" : "24h",
|
|
542
553
|
allowed_idps: [input.idpId],
|
|
543
554
|
auto_redirect_to_identity: true,
|
|
544
555
|
policies: [policy]
|
|
@@ -549,7 +560,7 @@ async function ensureAccessApp(input) {
|
|
|
549
560
|
path: `/accounts/${input.accountId}/access/apps`
|
|
550
561
|
});
|
|
551
562
|
if (!list.success) fail(list, "list access apps");
|
|
552
|
-
const existing = (list.result ?? []).find((a) => a.domain ===
|
|
563
|
+
const existing = (list.result ?? []).find((a) => a.domain === appDomain);
|
|
553
564
|
if (existing) {
|
|
554
565
|
const upd = await cf2({
|
|
555
566
|
apiToken: input.apiToken,
|
|
@@ -557,7 +568,7 @@ async function ensureAccessApp(input) {
|
|
|
557
568
|
path: `/accounts/${input.accountId}/access/apps/${existing.id}`,
|
|
558
569
|
body
|
|
559
570
|
});
|
|
560
|
-
if (!upd.success || !upd.result) fail(upd, `update access app ${
|
|
571
|
+
if (!upd.success || !upd.result) fail(upd, `update access app ${appDomain}`);
|
|
561
572
|
return upd.result.id;
|
|
562
573
|
}
|
|
563
574
|
const created = await cf2({
|
|
@@ -566,7 +577,7 @@ async function ensureAccessApp(input) {
|
|
|
566
577
|
path: `/accounts/${input.accountId}/access/apps`,
|
|
567
578
|
body
|
|
568
579
|
});
|
|
569
|
-
if (!created.success || !created.result) fail(created, `create access app ${
|
|
580
|
+
if (!created.success || !created.result) fail(created, `create access app ${appDomain}`);
|
|
570
581
|
return created.result.id;
|
|
571
582
|
}
|
|
572
583
|
async function provisionAccess(input) {
|
|
@@ -585,7 +596,8 @@ async function provisionAccess(input) {
|
|
|
585
596
|
saveState2(input.stateFile, { idpId, accountId: input.accountId });
|
|
586
597
|
const appIds = {};
|
|
587
598
|
for (const service of input.services) {
|
|
588
|
-
|
|
599
|
+
const appDomain = service.path ? `${service.hostname}${service.path}` : service.hostname;
|
|
600
|
+
appIds[appDomain] = await ensureAccessApp({
|
|
589
601
|
apiToken: input.apiToken,
|
|
590
602
|
accountId: input.accountId,
|
|
591
603
|
idpId,
|
|
@@ -596,6 +608,9 @@ async function provisionAccess(input) {
|
|
|
596
608
|
return { idpId, authDomain, appIds };
|
|
597
609
|
}
|
|
598
610
|
|
|
611
|
+
// src/server/radar/registration.ts
|
|
612
|
+
var RECEIVER_PATH = "/api/radar/ingest";
|
|
613
|
+
|
|
599
614
|
// src/server/radar-docker-init-entry.ts
|
|
600
615
|
var REQUIRED_ENV = [
|
|
601
616
|
"CLAUDE_CREDENTIALS_B64",
|
|
@@ -831,7 +846,11 @@ var GATED_SERVICES = {
|
|
|
831
846
|
// Claude web terminal — live drivable shell ⇒ RCE surface ⇒ short session.
|
|
832
847
|
bot: { strict: true },
|
|
833
848
|
// The user's own dev/preview server — a workday-length session is fine.
|
|
834
|
-
preview: { strict: false }
|
|
849
|
+
preview: { strict: false },
|
|
850
|
+
// Radar: gate the UI, bypass the (HMAC-verified) webhook receiver path.
|
|
851
|
+
radar: { strict: false, publicPaths: [RECEIVER_PATH] },
|
|
852
|
+
// Deck: gate the whole host — its push is localhost-only, never hits the edge.
|
|
853
|
+
deck: { strict: false }
|
|
835
854
|
};
|
|
836
855
|
async function registerOidcClient(serverUrl, pat, redirectUris) {
|
|
837
856
|
const res = await fetch(new URL("/api/rover/oidc-client", serverUrl), {
|
|
@@ -858,20 +877,31 @@ async function maybeProvisionAccess(bundle, ingress) {
|
|
|
858
877
|
const skipped = [];
|
|
859
878
|
for (const [name, hostname] of Object.entries(ingress.hostnames)) {
|
|
860
879
|
const cfg = GATED_SERVICES[name];
|
|
861
|
-
if (cfg)
|
|
862
|
-
|
|
880
|
+
if (!cfg) {
|
|
881
|
+
skipped.push(name);
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
services.push({ hostname, strict: cfg.strict });
|
|
885
|
+
for (const path of cfg.publicPaths ?? []) {
|
|
886
|
+
services.push({ hostname, path, bypass: true });
|
|
887
|
+
}
|
|
863
888
|
}
|
|
864
889
|
if (skipped.length > 0) {
|
|
865
890
|
console.log(`[entrypoint] CF Access: leaving machine surface(s) ungated: ${skipped.join(", ")}`);
|
|
866
891
|
}
|
|
867
892
|
if (services.length === 0) {
|
|
868
|
-
console.log("[entrypoint] CF Access: no human-facing service to gate (bot/preview not provisioned)");
|
|
893
|
+
console.log("[entrypoint] CF Access: no human-facing service to gate (bot/preview/radar/deck not provisioned)");
|
|
869
894
|
return;
|
|
870
895
|
}
|
|
871
896
|
const serverUrl = process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app";
|
|
872
897
|
const pat = requireEnv("LS_PAT");
|
|
873
898
|
const stateFile = "/workspace/.launchpod/launch-kit-access.json";
|
|
874
|
-
|
|
899
|
+
const gatedHosts = services.filter((s) => !s.bypass).map((s) => s.hostname);
|
|
900
|
+
const bypassed = services.filter((s) => s.bypass).map((s) => `${s.hostname}${s.path ?? ""}`);
|
|
901
|
+
console.log(`[entrypoint] gating ${gatedHosts.join(", ")} behind CF Access (IdP: ${serverUrl})`);
|
|
902
|
+
if (bypassed.length > 0) {
|
|
903
|
+
console.log(`[entrypoint] CF Access: public bypass for ${bypassed.join(", ")}`);
|
|
904
|
+
}
|
|
875
905
|
const result = await provisionAccess({
|
|
876
906
|
apiToken: token,
|
|
877
907
|
accountId,
|
|
File without changes
|
|
File without changes
|