@launchsecure/launch-kit 0.0.34 → 0.0.35
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/server/cli.js +37 -9
- package/dist/server/council-entry.js +0 -0
- package/dist/server/fb-wizard.js +0 -0
- package/dist/server/init-entry.js +128 -20
- package/dist/server/launch-bot-entry.js +4078 -0
- package/dist/server/orbit-entry.js +123 -26
- package/dist/server/radar-docker-init-entry.js +55 -3
- package/dist/server/radar-entrypoint-entry.js +0 -0
- package/dist/server/radar-teardown-entry.js +0 -0
- package/dist/server/rover-entry.js +547 -114
- package/package.json +24 -24
- package/scaffolds/ls-marketplace/plugins/kit/skills/brief/SKILL.md +53 -22
- package/scaffolds/ls-marketplace/plugins/kit/skills/brief/briefs.mjs +152 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/kickoff/SKILL.md +151 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/orbit/SKILL.md +14 -2
- package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
|
@@ -1530,6 +1530,73 @@ var init_manifest = __esm({
|
|
|
1530
1530
|
}
|
|
1531
1531
|
});
|
|
1532
1532
|
|
|
1533
|
+
// src/server/orbit/active.ts
|
|
1534
|
+
function anchorPaths(projectRoot) {
|
|
1535
|
+
const dir = (0, import_node_path7.join)(projectRoot, LAUNCHSECURE_DIR, "orbit");
|
|
1536
|
+
return { dir, current: (0, import_node_path7.join)(dir, "current"), activeJson: (0, import_node_path7.join)(dir, "active.json") };
|
|
1537
|
+
}
|
|
1538
|
+
function present(p) {
|
|
1539
|
+
try {
|
|
1540
|
+
(0, import_node_fs10.lstatSync)(p);
|
|
1541
|
+
return true;
|
|
1542
|
+
} catch {
|
|
1543
|
+
return false;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
function setActiveOrbit(entry, envFileName) {
|
|
1547
|
+
const paths = anchorPaths(entry.projectRoot);
|
|
1548
|
+
(0, import_node_fs10.mkdirSync)(paths.dir, { recursive: true });
|
|
1549
|
+
const tmpLink = `${paths.current}.tmp.${process.pid}`;
|
|
1550
|
+
if (present(tmpLink)) (0, import_node_fs10.rmSync)(tmpLink, { force: true });
|
|
1551
|
+
(0, import_node_fs10.symlinkSync)(entry.path, tmpLink);
|
|
1552
|
+
(0, import_node_fs10.renameSync)(tmpLink, paths.current);
|
|
1553
|
+
const active = {
|
|
1554
|
+
slug: entry.slug,
|
|
1555
|
+
branch: entry.branch,
|
|
1556
|
+
path: entry.path,
|
|
1557
|
+
envFile: (0, import_node_path7.join)(entry.path, envFileName),
|
|
1558
|
+
projectRoot: entry.projectRoot,
|
|
1559
|
+
anchor: paths.current,
|
|
1560
|
+
switchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1561
|
+
};
|
|
1562
|
+
const tmpJson = `${paths.activeJson}.tmp.${process.pid}`;
|
|
1563
|
+
(0, import_node_fs10.writeFileSync)(tmpJson, JSON.stringify(active, null, 2) + "\n", "utf-8");
|
|
1564
|
+
(0, import_node_fs10.renameSync)(tmpJson, paths.activeJson);
|
|
1565
|
+
return active;
|
|
1566
|
+
}
|
|
1567
|
+
function clearActiveOrbit(projectRoot) {
|
|
1568
|
+
const paths = anchorPaths(projectRoot);
|
|
1569
|
+
let cleared = false;
|
|
1570
|
+
for (const p of [paths.current, paths.activeJson]) {
|
|
1571
|
+
if (present(p)) {
|
|
1572
|
+
try {
|
|
1573
|
+
(0, import_node_fs10.rmSync)(p, { force: true });
|
|
1574
|
+
cleared = true;
|
|
1575
|
+
} catch {
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return cleared;
|
|
1580
|
+
}
|
|
1581
|
+
function readActiveOrbit(projectRoot) {
|
|
1582
|
+
const { activeJson } = anchorPaths(projectRoot);
|
|
1583
|
+
if (!present(activeJson)) return null;
|
|
1584
|
+
try {
|
|
1585
|
+
return JSON.parse((0, import_node_fs10.readFileSync)(activeJson, "utf-8"));
|
|
1586
|
+
} catch {
|
|
1587
|
+
return null;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
var import_node_fs10, import_node_path7;
|
|
1591
|
+
var init_active = __esm({
|
|
1592
|
+
"src/server/orbit/active.ts"() {
|
|
1593
|
+
"use strict";
|
|
1594
|
+
import_node_fs10 = require("node:fs");
|
|
1595
|
+
import_node_path7 = require("node:path");
|
|
1596
|
+
init_launch_kit_paths();
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1533
1600
|
// src/server/orbit/slug.ts
|
|
1534
1601
|
function slugify(branch) {
|
|
1535
1602
|
let s = branch.toLowerCase();
|
|
@@ -1556,7 +1623,7 @@ async function create(opts) {
|
|
|
1556
1623
|
const envFileName = opts.envFileName ?? manifest.envFile ?? DEFAULT_ENV_FILE;
|
|
1557
1624
|
const ctx = {
|
|
1558
1625
|
projectRoot: opts.projectRoot,
|
|
1559
|
-
manifestPath: (0,
|
|
1626
|
+
manifestPath: (0, import_node_path8.join)(opts.projectRoot, "orbit.json"),
|
|
1560
1627
|
manifest,
|
|
1561
1628
|
logger: opts.logger,
|
|
1562
1629
|
envFileName
|
|
@@ -1610,13 +1677,13 @@ async function create(opts) {
|
|
|
1610
1677
|
Object.assign(rewriteFns, adapter.envRewrites(state, ctx));
|
|
1611
1678
|
}
|
|
1612
1679
|
}
|
|
1613
|
-
const srcEnv = (0,
|
|
1614
|
-
const dstEnv = (0,
|
|
1615
|
-
if ((0,
|
|
1680
|
+
const srcEnv = (0, import_node_path8.join)(opts.projectRoot, envFileName);
|
|
1681
|
+
const dstEnv = (0, import_node_path8.join)(worktreeState.path, envFileName);
|
|
1682
|
+
if ((0, import_node_fs11.existsSync)(srcEnv) && !(0, import_node_fs11.existsSync)(dstEnv)) {
|
|
1616
1683
|
opts.logger.step(`copy ${envFileName} into worktree`);
|
|
1617
1684
|
copyEnvFile(srcEnv, dstEnv);
|
|
1618
1685
|
}
|
|
1619
|
-
if ((0,
|
|
1686
|
+
if ((0, import_node_fs11.existsSync)(dstEnv) && Object.keys(rewriteFns).length > 0) {
|
|
1620
1687
|
opts.logger.step(`rewrite ${envFileName} (${Object.keys(rewriteFns).join(", ")})`);
|
|
1621
1688
|
const r = rewriteEnvFile(dstEnv, rewriteFns);
|
|
1622
1689
|
if (r.missing.length > 0) {
|
|
@@ -1636,7 +1703,7 @@ async function create(opts) {
|
|
|
1636
1703
|
opts.logger.ok(`registered ${slug}`);
|
|
1637
1704
|
try {
|
|
1638
1705
|
opts.logger.step(`generate chart graph for worktree`);
|
|
1639
|
-
const chartEntry = (0,
|
|
1706
|
+
const chartEntry = (0, import_node_path8.join)(__dirname, "graph-mcp-entry.js");
|
|
1640
1707
|
const result = (0, import_node_child_process8.spawnSync)(
|
|
1641
1708
|
process.execPath,
|
|
1642
1709
|
[chartEntry, "generate"],
|
|
@@ -1688,7 +1755,7 @@ async function drop(opts) {
|
|
|
1688
1755
|
const manifest = loadManifestOrThrow(opts.projectRoot);
|
|
1689
1756
|
const ctx = {
|
|
1690
1757
|
projectRoot: opts.projectRoot,
|
|
1691
|
-
manifestPath: (0,
|
|
1758
|
+
manifestPath: (0, import_node_path8.join)(opts.projectRoot, "orbit.json"),
|
|
1692
1759
|
manifest,
|
|
1693
1760
|
logger: opts.logger,
|
|
1694
1761
|
envFileName: manifest.envFile ?? DEFAULT_ENV_FILE
|
|
@@ -1709,7 +1776,7 @@ async function drop(opts) {
|
|
|
1709
1776
|
if (result.backupPath) backups[ref.name] = result.backupPath;
|
|
1710
1777
|
freed[ref.name] = state.state;
|
|
1711
1778
|
}
|
|
1712
|
-
if ((0,
|
|
1779
|
+
if ((0, import_node_fs11.existsSync)(entry.path)) {
|
|
1713
1780
|
const blockers = findBlockingPids(entry.path);
|
|
1714
1781
|
if (blockers.length > 0) {
|
|
1715
1782
|
opts.logger.warn(`${blockers.length} process(es) holding files in worktree \u2014 terminating:`);
|
|
@@ -1728,15 +1795,19 @@ async function drop(opts) {
|
|
|
1728
1795
|
} catch (e) {
|
|
1729
1796
|
opts.logger.warn(`worktree remove failed (continuing anyway): ${e.message}`);
|
|
1730
1797
|
}
|
|
1731
|
-
if ((0,
|
|
1798
|
+
if ((0, import_node_fs11.existsSync)(entry.path)) {
|
|
1732
1799
|
opts.logger.step(`remove leftover dir at ${entry.path}`);
|
|
1733
1800
|
try {
|
|
1734
|
-
(0,
|
|
1801
|
+
(0, import_node_fs11.rmSync)(entry.path, { recursive: true, force: true });
|
|
1735
1802
|
} catch (e) {
|
|
1736
1803
|
opts.logger.warn(`leftover dir cleanup failed: ${e.message}`);
|
|
1737
1804
|
}
|
|
1738
1805
|
}
|
|
1739
1806
|
await deregisterWorktree(slug);
|
|
1807
|
+
if (readActiveOrbit(opts.projectRoot)?.slug === slug) {
|
|
1808
|
+
clearActiveOrbit(opts.projectRoot);
|
|
1809
|
+
opts.logger.step("cleared active-orbit anchor (current)");
|
|
1810
|
+
}
|
|
1740
1811
|
opts.logger.ok(`dropped ${slug}`);
|
|
1741
1812
|
return { slug, backups, freed };
|
|
1742
1813
|
}
|
|
@@ -1753,12 +1824,19 @@ function switchTo(opts) {
|
|
|
1753
1824
|
const slug = slugify(opts.branch);
|
|
1754
1825
|
const entry = lookupWorktree(slug);
|
|
1755
1826
|
if (!entry) throw new Error(`no orbit registered for branch "${opts.branch}" (slug ${slug})`);
|
|
1827
|
+
const active = setActiveOrbit(entry, DEFAULT_ENV_FILE);
|
|
1756
1828
|
return {
|
|
1757
1829
|
path: entry.path,
|
|
1758
|
-
envFile:
|
|
1830
|
+
envFile: active.envFile,
|
|
1831
|
+
anchor: active.anchor,
|
|
1832
|
+
activeJson: anchorPaths(entry.projectRoot).activeJson,
|
|
1833
|
+
nextAction: `cd "${active.anchor}"`,
|
|
1759
1834
|
entry
|
|
1760
1835
|
};
|
|
1761
1836
|
}
|
|
1837
|
+
function deactivate(opts) {
|
|
1838
|
+
return { cleared: clearActiveOrbit(opts.projectRoot), projectRoot: opts.projectRoot };
|
|
1839
|
+
}
|
|
1762
1840
|
function doctor() {
|
|
1763
1841
|
const issues = [];
|
|
1764
1842
|
const trackedByRoot = /* @__PURE__ */ new Map();
|
|
@@ -1781,7 +1859,7 @@ function doctor() {
|
|
|
1781
1859
|
return s;
|
|
1782
1860
|
};
|
|
1783
1861
|
for (const w of listWorktrees()) {
|
|
1784
|
-
if (!(0,
|
|
1862
|
+
if (!(0, import_node_fs11.existsSync)(w.path)) {
|
|
1785
1863
|
issues.push({ kind: "missing-worktree", slug: w.slug, detail: `path ${w.path} does not exist` });
|
|
1786
1864
|
continue;
|
|
1787
1865
|
}
|
|
@@ -2018,18 +2096,19 @@ function formatAge(iso) {
|
|
|
2018
2096
|
const d = Math.floor(h / 24);
|
|
2019
2097
|
return `${d}d`;
|
|
2020
2098
|
}
|
|
2021
|
-
var import_node_child_process8,
|
|
2099
|
+
var import_node_child_process8, import_node_fs11, import_node_path8, DEFAULT_ENV_FILE;
|
|
2022
2100
|
var init_orchestrator = __esm({
|
|
2023
2101
|
"src/server/orbit/orchestrator.ts"() {
|
|
2024
2102
|
"use strict";
|
|
2025
2103
|
import_node_child_process8 = require("node:child_process");
|
|
2026
|
-
|
|
2027
|
-
|
|
2104
|
+
import_node_fs11 = require("node:fs");
|
|
2105
|
+
import_node_path8 = require("node:path");
|
|
2028
2106
|
init_adapter_registry();
|
|
2029
2107
|
init_env_rewriter();
|
|
2030
2108
|
init_gate_runner();
|
|
2031
2109
|
init_manifest();
|
|
2032
2110
|
init_registry();
|
|
2111
|
+
init_active();
|
|
2033
2112
|
init_slug();
|
|
2034
2113
|
DEFAULT_ENV_FILE = ".env.local";
|
|
2035
2114
|
}
|
|
@@ -2093,11 +2172,16 @@ async function handleTool(name, args) {
|
|
|
2093
2172
|
const result = switchTo({ branch, projectRoot });
|
|
2094
2173
|
return text({
|
|
2095
2174
|
slug: result.entry.slug,
|
|
2175
|
+
branch: result.entry.branch,
|
|
2176
|
+
anchor: result.anchor,
|
|
2096
2177
|
path: result.path,
|
|
2097
2178
|
envFile: result.envFile,
|
|
2098
|
-
nextAction:
|
|
2179
|
+
nextAction: result.nextAction
|
|
2099
2180
|
});
|
|
2100
2181
|
}
|
|
2182
|
+
case "orbit.deactivate": {
|
|
2183
|
+
return text(deactivate({ projectRoot }));
|
|
2184
|
+
}
|
|
2101
2185
|
case "orbit.drop": {
|
|
2102
2186
|
const branch = String(args.branch ?? "");
|
|
2103
2187
|
if (!branch) return text({ error: "branch is required" });
|
|
@@ -2256,15 +2340,20 @@ var init_orbit_mcp = __esm({
|
|
|
2256
2340
|
},
|
|
2257
2341
|
{
|
|
2258
2342
|
name: "orbit.switch",
|
|
2259
|
-
description:
|
|
2343
|
+
description: 'Activate an orbit by branch name. Atomically repoints the stable anchor <project>/.launchsecure/orbit/current at the orbit\'s worktree and records active.json, then returns `anchor` (the current/ path) + `nextAction`. AFTER calling this, operate on the anchor: Read/Edit/Write under `.launchsecure/orbit/current/<path>`, pass `project_root: ".launchsecure/orbit/current"` to chart, and run the returned `nextAction` (cd into current) so commands execute in the orbit. Switching to a different orbit just repoints the anchor; re-run nextAction afterwards.',
|
|
2260
2344
|
inputSchema: {
|
|
2261
2345
|
type: "object",
|
|
2262
2346
|
properties: {
|
|
2263
|
-
branch: { type: "string", description: "Branch name to
|
|
2347
|
+
branch: { type: "string", description: "Branch name of the orbit to activate." }
|
|
2264
2348
|
},
|
|
2265
2349
|
required: ["branch"]
|
|
2266
2350
|
}
|
|
2267
2351
|
},
|
|
2352
|
+
{
|
|
2353
|
+
name: "orbit.deactivate",
|
|
2354
|
+
description: "Clear the active-orbit anchor for this project \u2014 removes <project>/.launchsecure/orbit/current and active.json so no orbit is active. Use when you're done working in an orbit and want subsequent work to target the main checkout. No-op if nothing is active.",
|
|
2355
|
+
inputSchema: { type: "object", properties: {} }
|
|
2356
|
+
},
|
|
2268
2357
|
{
|
|
2269
2358
|
name: "orbit.drop",
|
|
2270
2359
|
description: "Tear down an orbit: backup the database (default true), drop the database, terminate any process holding files inside the worktree (chart/beacon MCPs, dev servers \u2014 via lsof+SIGTERM), remove the worktree, sweep any leftover on-disk dir, deregister. Idempotent: safe to retry on a partial state (e.g. git already removed the worktree but the registry still claims it). Returns { slug, backups, freed }.",
|
|
@@ -2326,8 +2415,8 @@ var init_orbit_mcp = __esm({
|
|
|
2326
2415
|
});
|
|
2327
2416
|
|
|
2328
2417
|
// src/server/orbit-entry.ts
|
|
2329
|
-
var
|
|
2330
|
-
var
|
|
2418
|
+
var import_node_fs12 = require("node:fs");
|
|
2419
|
+
var import_node_path9 = require("node:path");
|
|
2331
2420
|
init_orchestrator();
|
|
2332
2421
|
init_logger();
|
|
2333
2422
|
function parseFlags(argv) {
|
|
@@ -2413,14 +2502,22 @@ ${JSON.stringify(result, null, 2)}
|
|
|
2413
2502
|
if (!branch) die("usage: launch-orbit switch <branch> [--emit-cd]");
|
|
2414
2503
|
const result = switchTo({ branch, projectRoot });
|
|
2415
2504
|
if (flags.emitCd) {
|
|
2416
|
-
process.stdout.write(`cd ${result.
|
|
2505
|
+
process.stdout.write(`cd ${result.anchor}
|
|
2417
2506
|
`);
|
|
2418
2507
|
} else {
|
|
2419
|
-
process.stdout.write(
|
|
2420
|
-
|
|
2508
|
+
process.stdout.write(
|
|
2509
|
+
`${JSON.stringify({ anchor: result.anchor, path: result.path, envFile: result.envFile, nextAction: result.nextAction }, null, 2)}
|
|
2510
|
+
`
|
|
2511
|
+
);
|
|
2421
2512
|
}
|
|
2422
2513
|
return;
|
|
2423
2514
|
}
|
|
2515
|
+
case "deactivate": {
|
|
2516
|
+
const result = deactivate({ projectRoot });
|
|
2517
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
2518
|
+
`);
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2424
2521
|
case "drop": {
|
|
2425
2522
|
const branch = positional[0];
|
|
2426
2523
|
if (!branch) die("usage: launch-orbit drop <branch> [--no-backup]");
|
|
@@ -2495,8 +2592,8 @@ ${JSON.stringify(result, null, 2)}
|
|
|
2495
2592
|
}
|
|
2496
2593
|
}
|
|
2497
2594
|
function runInit(projectRoot) {
|
|
2498
|
-
const path = (0,
|
|
2499
|
-
if ((0,
|
|
2595
|
+
const path = (0, import_node_path9.join)(projectRoot, "orbit.json");
|
|
2596
|
+
if ((0, import_node_fs12.existsSync)(path)) {
|
|
2500
2597
|
process.stderr.write(`[launch-orbit] orbit.json already exists at ${path}
|
|
2501
2598
|
`);
|
|
2502
2599
|
process.exit(1);
|
|
@@ -2551,7 +2648,7 @@ function runInit(projectRoot) {
|
|
|
2551
2648
|
full: { resources: ["db", "ports"] }
|
|
2552
2649
|
}
|
|
2553
2650
|
};
|
|
2554
|
-
(0,
|
|
2651
|
+
(0, import_node_fs12.writeFileSync)(path, JSON.stringify(starter, null, 2) + "\n", "utf-8");
|
|
2555
2652
|
process.stdout.write(`\u2713 wrote ${path}
|
|
2556
2653
|
`);
|
|
2557
2654
|
}
|
|
@@ -152,17 +152,26 @@ var SHORTHANDS = {
|
|
|
152
152
|
sequencer: { port: 3517, bin: "launch-sequencer", args: [] },
|
|
153
153
|
chart: { port: 52819, bin: "launch-chart", args: ["serve"] },
|
|
154
154
|
deck: { port: 52829, bin: "launch-deck", args: ["serve"] },
|
|
155
|
-
council: { port: 52839, bin: "launch-council", args: ["serve"] }
|
|
155
|
+
council: { port: 52839, bin: "launch-council", args: ["serve"] },
|
|
156
|
+
// Claude web terminal — exposes a viewable/drivable `claude` session at
|
|
157
|
+
// `bot.<baseDomain>`. NOTE: no auth gate yet (tracked as a separate
|
|
158
|
+
// high-priority rover-security work item); ships behind a plain link first.
|
|
159
|
+
bot: { port: 52849, bin: "launch-bot", args: ["serve"] }
|
|
156
160
|
};
|
|
157
161
|
var DNS_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
158
162
|
function defaultServices() {
|
|
159
163
|
return [expandShorthand("radar")];
|
|
160
164
|
}
|
|
161
165
|
function expandShorthand(name) {
|
|
166
|
+
if (name === "preview") {
|
|
167
|
+
const raw = process.env.PREVIEW_PORT;
|
|
168
|
+
const port = raw && Number.isFinite(Number.parseInt(raw, 10)) ? Number.parseInt(raw, 10) : 3e3;
|
|
169
|
+
return { name: "preview", port, bin: "", args: [], skipSpawn: true };
|
|
170
|
+
}
|
|
162
171
|
const def = SHORTHANDS[name];
|
|
163
172
|
if (!def) {
|
|
164
173
|
throw new Error(
|
|
165
|
-
`[launch-kit-services] unknown shorthand "${name}" \u2014 known: ${Object.keys(SHORTHANDS).join(", ")}. Use an object entry { name, port, bin, args } for custom services.`
|
|
174
|
+
`[launch-kit-services] unknown shorthand "${name}" \u2014 known: ${[...Object.keys(SHORTHANDS), "preview"].join(", ")}. Use an object entry { name, port, bin, args } for custom services.`
|
|
166
175
|
);
|
|
167
176
|
}
|
|
168
177
|
return { name, port: def.port, bin: def.bin, args: [...def.args] };
|
|
@@ -243,7 +252,7 @@ function resolveServices(opts = {}) {
|
|
|
243
252
|
}
|
|
244
253
|
return validate(defaultServices());
|
|
245
254
|
}
|
|
246
|
-
var SHORTHAND_NAMES = Object.keys(SHORTHANDS);
|
|
255
|
+
var SHORTHAND_NAMES = [...Object.keys(SHORTHANDS), "preview"];
|
|
247
256
|
|
|
248
257
|
// src/server/cf-ingress.ts
|
|
249
258
|
var import_node_fs2 = require("node:fs");
|
|
@@ -455,6 +464,23 @@ function setupClaudeCredentials() {
|
|
|
455
464
|
cfg.lastOnboardingVersion = cfg.lastOnboardingVersion ?? "2.1.159";
|
|
456
465
|
cfg.numStartups = (cfg.numStartups ?? 0) + 1;
|
|
457
466
|
cfg.installMethod = cfg.installMethod ?? "global";
|
|
467
|
+
const PREAPPROVED_MCPS = [
|
|
468
|
+
"launch-secure",
|
|
469
|
+
"launch-chart",
|
|
470
|
+
"launch-deck",
|
|
471
|
+
"launch-orbit",
|
|
472
|
+
"launch-recall",
|
|
473
|
+
"launch-beacon",
|
|
474
|
+
"launch-sequencer"
|
|
475
|
+
];
|
|
476
|
+
const projects = cfg.projects ?? {};
|
|
477
|
+
const wsKey = "/workspace";
|
|
478
|
+
const wsProject = projects[wsKey] ?? {};
|
|
479
|
+
const existingEnabled = Array.isArray(wsProject.enabledMcpjsonServers) ? wsProject.enabledMcpjsonServers : [];
|
|
480
|
+
const mergedEnabled = Array.from(/* @__PURE__ */ new Set([...existingEnabled, ...PREAPPROVED_MCPS]));
|
|
481
|
+
wsProject.enabledMcpjsonServers = mergedEnabled;
|
|
482
|
+
projects[wsKey] = wsProject;
|
|
483
|
+
cfg.projects = projects;
|
|
458
484
|
(0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(cfg, null, 2));
|
|
459
485
|
(0, import_node_fs3.chmodSync)(configPath, 384);
|
|
460
486
|
}
|
|
@@ -464,6 +490,27 @@ function setupGitAndGh() {
|
|
|
464
490
|
const status = run("launch-kit", ["setup-git", `--identity=${name} <${email}>`]);
|
|
465
491
|
if (status !== 0) fail(`[entrypoint] launch-kit setup-git failed (status ${status})`);
|
|
466
492
|
}
|
|
493
|
+
function detectAndSetPreviewPort() {
|
|
494
|
+
if (process.env.PREVIEW_PORT) return;
|
|
495
|
+
try {
|
|
496
|
+
const pkgPath = "/workspace/package.json";
|
|
497
|
+
if (!(0, import_node_fs3.existsSync)(pkgPath)) return;
|
|
498
|
+
const pkg = JSON.parse((0, import_node_fs3.readFileSync)(pkgPath, "utf-8"));
|
|
499
|
+
const scripts = pkg.scripts ?? {};
|
|
500
|
+
const portRe = /(?:--port[= ]|-p\s+|\bPORT=)(\d{2,5})\b/;
|
|
501
|
+
for (const name of ["dev", "start", "serve"]) {
|
|
502
|
+
const script = scripts[name];
|
|
503
|
+
if (typeof script !== "string") continue;
|
|
504
|
+
const m = script.match(portRe);
|
|
505
|
+
if (m) {
|
|
506
|
+
process.env.PREVIEW_PORT = m[1];
|
|
507
|
+
console.log(`[entrypoint] preview port detected from package.json scripts.${name}: ${m[1]}`);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
}
|
|
467
514
|
function initWorkspaceIfEmpty() {
|
|
468
515
|
process.chdir("/workspace");
|
|
469
516
|
if ((0, import_node_fs3.existsSync)(".git")) {
|
|
@@ -563,6 +610,10 @@ function spawnServiceGroup(services) {
|
|
|
563
610
|
let exitedCount = 0;
|
|
564
611
|
let firstFailure = null;
|
|
565
612
|
for (const spec of services) {
|
|
613
|
+
if (spec.skipSpawn) {
|
|
614
|
+
console.log(`[entrypoint] ${spec.name} \u2192 ingress-only on port ${spec.port} (no spawn; user starts dev server here)`);
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
566
617
|
const args = [...spec.args, "--port", String(spec.port)];
|
|
567
618
|
console.log(`[entrypoint] starting ${spec.name}: ${spec.bin} ${args.join(" ")}`);
|
|
568
619
|
const proc = (0, import_node_child_process.spawn)(spec.bin, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -599,6 +650,7 @@ async function main() {
|
|
|
599
650
|
setupClaudeCredentials();
|
|
600
651
|
setupGitAndGh();
|
|
601
652
|
initWorkspaceIfEmpty();
|
|
653
|
+
detectAndSetPreviewPort();
|
|
602
654
|
let services;
|
|
603
655
|
try {
|
|
604
656
|
services = resolveServices();
|
|
File without changes
|
|
File without changes
|