@m-kopa/launchpad-cli 0.61.1 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/dist/bundle/upload.d.ts +2 -0
- package/dist/bundle/upload.d.ts.map +1 -1
- package/dist/cli.js +420 -153
- package/dist/commands/deploy.d.ts +10 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/migrate.d.ts +29 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/http/api-client.d.ts.map +1 -1
- package/dist/http/errors.d.ts +3 -0
- package/dist/http/errors.d.ts.map +1 -1
- package/dist/manifest/migrate-dispatch.d.ts +4 -0
- package/dist/manifest/migrate-dispatch.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/skills/launchpad-content-pr/SKILL.md +6 -5
- package/skills/launchpad-deploy/SKILL.md +19 -7
- package/skills/launchpad-deploy-status/SKILL.md +1 -1
- package/skills/launchpad-destroy/SKILL.md +1 -1
- package/skills/launchpad-identity/SKILL.md +1 -1
- package/skills/launchpad-onboard/SKILL.md +3 -2
- package/skills/launchpad-report/SKILL.md +1 -1
- package/skills/launchpad-status/SKILL.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
19
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
20
|
|
|
21
21
|
// src/version.ts
|
|
22
|
-
var CLI_VERSION = "0.
|
|
22
|
+
var CLI_VERSION = "0.63.0";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import { readFileSync } from "node:fs";
|
|
@@ -226,7 +226,12 @@ class ForbiddenError extends Error {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
class NotFoundError extends Error {
|
|
229
|
+
serverCode;
|
|
229
230
|
code = "not_found";
|
|
231
|
+
constructor(message, serverCode) {
|
|
232
|
+
super(message);
|
|
233
|
+
this.serverCode = serverCode;
|
|
234
|
+
}
|
|
230
235
|
}
|
|
231
236
|
var EXIT_CLI_UPGRADE_REQUIRED = 42;
|
|
232
237
|
|
|
@@ -1148,8 +1153,9 @@ async function apiRaw(cfg, opts, fetcher = fetch) {
|
|
|
1148
1153
|
throw recordedFault(new ForbiddenError(`bot returned 403 for ${opts.path}: ${detail}`));
|
|
1149
1154
|
}
|
|
1150
1155
|
if (res.status === 404) {
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1156
|
+
const envelope = await readErrorEnvelope(res, opts.path);
|
|
1157
|
+
const detail = formatErrorEnvelope(envelope);
|
|
1158
|
+
throw recordedFault(new NotFoundError(`bot returned 404 for ${opts.path}: ${detail}`, envelope.code));
|
|
1153
1159
|
}
|
|
1154
1160
|
if (!res.ok) {
|
|
1155
1161
|
if (opts.nonThrowingStatuses?.includes(res.status) === true) {
|
|
@@ -1882,8 +1888,8 @@ function describe10(e) {
|
|
|
1882
1888
|
}
|
|
1883
1889
|
|
|
1884
1890
|
// src/commands/deploy.ts
|
|
1885
|
-
import { existsSync as
|
|
1886
|
-
import * as
|
|
1891
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
1892
|
+
import * as path9 from "node:path";
|
|
1887
1893
|
|
|
1888
1894
|
// src/bundle/orchestrate.ts
|
|
1889
1895
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -2200,8 +2206,14 @@ function deployBundleError(envelope, body) {
|
|
|
2200
2206
|
...typeof body.head_sha === "string" ? { head_sha: body.head_sha } : {},
|
|
2201
2207
|
...paths === undefined ? {} : { paths },
|
|
2202
2208
|
...typeof body.reason === "string" ? { reason: body.reason } : {},
|
|
2209
|
+
...readDeployGuidance(body)
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
function readDeployGuidance(body) {
|
|
2213
|
+
return {
|
|
2203
2214
|
...typeof body.minimum === "string" ? { minimum: body.minimum } : {},
|
|
2204
2215
|
...typeof body.yours === "string" ? { yours: body.yours } : {},
|
|
2216
|
+
...typeof body.requires_admin_approval === "boolean" ? { requires_admin_approval: body.requires_admin_approval } : {},
|
|
2205
2217
|
...typeof body.statusPageUrl === "string" ? { statusPageUrl: body.statusPageUrl } : {}
|
|
2206
2218
|
};
|
|
2207
2219
|
}
|
|
@@ -6999,7 +7011,7 @@ async function verifyFirstDeployServed(args, liveUrl, io, deps) {
|
|
|
6999
7011
|
|
|
7000
7012
|
// src/commands/deploy.ts
|
|
7001
7013
|
import { parse as parseYaml6 } from "yaml";
|
|
7002
|
-
import { readFileSync as
|
|
7014
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
7003
7015
|
|
|
7004
7016
|
// src/deploy/git-files.ts
|
|
7005
7017
|
import { spawn as spawn3 } from "node:child_process";
|
|
@@ -8607,6 +8619,211 @@ function rejectUnsupportedCliRuntime(args) {
|
|
|
8607
8619
|
return 1;
|
|
8608
8620
|
}
|
|
8609
8621
|
|
|
8622
|
+
// src/commands/migrate.ts
|
|
8623
|
+
import { closeSync, existsSync as existsSync7, lstatSync as lstatSync4, mkdirSync as mkdirSync2, mkdtempSync, openSync, readFileSync as readFileSync11, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8624
|
+
import path8 from "node:path";
|
|
8625
|
+
import { stringify } from "yaml";
|
|
8626
|
+
|
|
8627
|
+
// src/manifest/migrate-dispatch.ts
|
|
8628
|
+
function dispatchManifest(manifest) {
|
|
8629
|
+
const v2 = manifest.apiVersion === "launchpad.m-kopa.us/v2";
|
|
8630
|
+
const source = v2 ? manifest.spec : manifest;
|
|
8631
|
+
const appType = v2 ? manifest.spec.appType : manifest.deployment.type;
|
|
8632
|
+
if (appType !== "static" && appType !== "react") {
|
|
8633
|
+
throw new Error("Only static and React apps can use this converter. Pages Functions (react+api) need a compiled Worker build before switching to dispatch; containers use their own runtime.");
|
|
8634
|
+
}
|
|
8635
|
+
if (source.secrets !== undefined || source.containers !== undefined) {
|
|
8636
|
+
throw new Error("Secret and container bindings cannot be converted automatically.");
|
|
8637
|
+
}
|
|
8638
|
+
if (!v2)
|
|
8639
|
+
assertConvertibleV1(manifest);
|
|
8640
|
+
const spec = v2 ? { ...manifest.spec } : {
|
|
8641
|
+
appType,
|
|
8642
|
+
display: {
|
|
8643
|
+
name: manifest.metadata.name,
|
|
8644
|
+
team: manifest.metadata.team,
|
|
8645
|
+
...manifest.metadata.description === undefined ? {} : { description: manifest.metadata.description }
|
|
8646
|
+
},
|
|
8647
|
+
owner: { email: manifest.metadata.owner },
|
|
8648
|
+
access: {
|
|
8649
|
+
audience: manifest.access.audience,
|
|
8650
|
+
private_owner: manifest.access.private_owner,
|
|
8651
|
+
allowed_entra_groups: [...allowedEntraGroups(manifest.access)],
|
|
8652
|
+
identity_types: manifest.access.identity_types
|
|
8653
|
+
},
|
|
8654
|
+
build: manifest.build,
|
|
8655
|
+
app: manifest.app,
|
|
8656
|
+
verify: manifest.verify,
|
|
8657
|
+
sensitive: manifest.sensitive,
|
|
8658
|
+
catalogue: manifest.catalogue,
|
|
8659
|
+
...manifest.production_env === undefined ? {} : {
|
|
8660
|
+
env_vars: Object.fromEntries(Object.entries(manifest.production_env).map(([name, value]) => [name, { type: "plain_text", value }]))
|
|
8661
|
+
}
|
|
8662
|
+
};
|
|
8663
|
+
const candidate = {
|
|
8664
|
+
apiVersion: "launchpad.m-kopa.us/v2",
|
|
8665
|
+
kind: "App",
|
|
8666
|
+
metadata: v2 ? manifest.metadata : { slug: manifest.metadata.name, schemaVersion: 2 },
|
|
8667
|
+
spec: { ...spec, origin: "dispatch", auth: "gateway", confine_origin: undefined }
|
|
8668
|
+
};
|
|
8669
|
+
const parsed = parseManifestV2(candidate);
|
|
8670
|
+
if (parsed.kind !== "ok") {
|
|
8671
|
+
throw new Error(`Cannot convert manifest: ${parsed.issues.map((issue) => `${issue.path}: ${issue.message}`).join("; ")}`);
|
|
8672
|
+
}
|
|
8673
|
+
return parsed.manifest;
|
|
8674
|
+
}
|
|
8675
|
+
function assertConvertibleV1(manifest) {
|
|
8676
|
+
const unsupported2 = [
|
|
8677
|
+
manifest.hostnames !== undefined && "hostnames",
|
|
8678
|
+
manifest.targets !== undefined && "targets",
|
|
8679
|
+
manifest.access.session_duration !== undefined && "access.session_duration",
|
|
8680
|
+
manifest.deployment.runtime !== undefined && "deployment.runtime"
|
|
8681
|
+
].filter(Boolean);
|
|
8682
|
+
if (unsupported2.length > 0) {
|
|
8683
|
+
throw new Error(`Cannot preserve ${unsupported2.join(", ")} on dispatch. Review these settings with the platform team before migrating.`);
|
|
8684
|
+
}
|
|
8685
|
+
}
|
|
8686
|
+
|
|
8687
|
+
// src/commands/migrate.ts
|
|
8688
|
+
var USAGE = "usage: launchpad migrate --origin dispatch [--dry-run]";
|
|
8689
|
+
var migrateCommand = {
|
|
8690
|
+
name: "migrate",
|
|
8691
|
+
summary: "convert an unprovisioned static or React app to Workers dispatch",
|
|
8692
|
+
run: runMigrate
|
|
8693
|
+
};
|
|
8694
|
+
async function runMigrate(args, io) {
|
|
8695
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
8696
|
+
io.out(USAGE);
|
|
8697
|
+
return 0;
|
|
8698
|
+
}
|
|
8699
|
+
const flags = args.filter((arg) => arg !== "--dry-run");
|
|
8700
|
+
if (flags.length !== 2 || flags[0] !== "--origin" || flags[1] !== "dispatch" || args.length > 3) {
|
|
8701
|
+
io.err(USAGE);
|
|
8702
|
+
return 64;
|
|
8703
|
+
}
|
|
8704
|
+
try {
|
|
8705
|
+
const result = await convertToDispatch(process.cwd(), args.includes("--dry-run"));
|
|
8706
|
+
if (result.kind === "already") {
|
|
8707
|
+
io.out("launchpad.yaml already selects Workers dispatch. Run `launchpad deploy`.");
|
|
8708
|
+
} else if (result.kind === "eligible") {
|
|
8709
|
+
io.out(`${result.slug}: eligible for v2 Workers dispatch with gateway authentication. No files changed.`);
|
|
8710
|
+
} else {
|
|
8711
|
+
io.out(`${result.slug}: launchpad.yaml now uses v2 Workers dispatch with gateway authentication.`);
|
|
8712
|
+
io.out(`Audience, owner and application settings are preserved. Original: ${BACKUP_PATH}`);
|
|
8713
|
+
io.out("Run `launchpad deploy`. No Pages project is needed.");
|
|
8714
|
+
}
|
|
8715
|
+
return 0;
|
|
8716
|
+
} catch (error) {
|
|
8717
|
+
io.err(`launchpad migrate: ${error instanceof Error ? error.message : String(error)}`);
|
|
8718
|
+
return 1;
|
|
8719
|
+
}
|
|
8720
|
+
}
|
|
8721
|
+
var BACKUP_PATH = ".launchpad/manifest-before-dispatch.yaml";
|
|
8722
|
+
async function convertToDispatch(cwd, dryRun) {
|
|
8723
|
+
const file = path8.join(cwd, "launchpad.yaml");
|
|
8724
|
+
if (!lstatSync4(file).isFile())
|
|
8725
|
+
throw new Error("launchpad.yaml must be a regular file, not a symlink.");
|
|
8726
|
+
const original = readFileSync11(file, "utf8");
|
|
8727
|
+
const loaded = parseManifestAnyVersion(original, file);
|
|
8728
|
+
if (loaded.kind !== "ok")
|
|
8729
|
+
throw new Error("launchpad.yaml is invalid. Run `launchpad validate` and correct it first.");
|
|
8730
|
+
const manifest = loaded.manifest;
|
|
8731
|
+
if (manifest.apiVersion === "launchpad.m-kopa.us/v2" && manifest.spec.origin === "dispatch")
|
|
8732
|
+
return { kind: "already" };
|
|
8733
|
+
const converted = dispatchManifest(manifest);
|
|
8734
|
+
assertNoCustomRuntime(cwd, converted);
|
|
8735
|
+
const slug = converted.metadata.slug;
|
|
8736
|
+
await assertUnregistered(slug);
|
|
8737
|
+
if (dryRun)
|
|
8738
|
+
return { kind: "eligible", slug };
|
|
8739
|
+
try {
|
|
8740
|
+
saveMigration(file, original, converted);
|
|
8741
|
+
} catch (error) {
|
|
8742
|
+
throw new MigrationSaveError(error);
|
|
8743
|
+
}
|
|
8744
|
+
return { kind: "converted", slug };
|
|
8745
|
+
}
|
|
8746
|
+
async function assertUnregistered(slug) {
|
|
8747
|
+
try {
|
|
8748
|
+
await apiRaw(loadConfig(), { path: `/apps/${encodeURIComponent(slug)}/manifest/state` });
|
|
8749
|
+
} catch (error) {
|
|
8750
|
+
if (error instanceof NotFoundError && error.serverCode === "not_found")
|
|
8751
|
+
return;
|
|
8752
|
+
if (error instanceof UpgradeRequiredError || error instanceof UnauthenticatedError)
|
|
8753
|
+
throw error;
|
|
8754
|
+
throw new RegistryUnavailableError(error);
|
|
8755
|
+
}
|
|
8756
|
+
throw new RegisteredAppError;
|
|
8757
|
+
}
|
|
8758
|
+
|
|
8759
|
+
class MigrationSaveError extends Error {
|
|
8760
|
+
constructor(cause) {
|
|
8761
|
+
super(`Could not save the converted manifest: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
8762
|
+
}
|
|
8763
|
+
}
|
|
8764
|
+
|
|
8765
|
+
class RegistryUnavailableError extends Error {
|
|
8766
|
+
constructor(cause) {
|
|
8767
|
+
super(`Could not confirm whether this app is registered: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
|
|
8771
|
+
class RegisteredAppError extends Error {
|
|
8772
|
+
constructor() {
|
|
8773
|
+
super("This app is already registered. This command only converts apps that have never provisioned; ask the platform team to migrate an existing app.");
|
|
8774
|
+
}
|
|
8775
|
+
}
|
|
8776
|
+
function assertNoCustomRuntime(cwd, manifest) {
|
|
8777
|
+
const appRoot = path8.resolve(cwd, manifest.spec.app?.root ?? ".");
|
|
8778
|
+
const buildRoot = path8.resolve(appRoot, manifest.spec.build?.root_dir ?? ".");
|
|
8779
|
+
const roots = new Set([
|
|
8780
|
+
cwd,
|
|
8781
|
+
appRoot,
|
|
8782
|
+
buildRoot,
|
|
8783
|
+
path8.resolve(cwd, manifest.spec.build?.root_dir ?? "."),
|
|
8784
|
+
path8.resolve(buildRoot, manifest.spec.build?.destination_dir ?? "dist")
|
|
8785
|
+
]);
|
|
8786
|
+
for (const root of roots) {
|
|
8787
|
+
for (const name of ["functions", "_worker.js", "wrangler.toml", "wrangler.json", "wrangler.jsonc"]) {
|
|
8788
|
+
if (existsSync7(path8.join(root, name))) {
|
|
8789
|
+
throw new Error(`${name} needs a runtime review before conversion. Pages Functions and custom Worker bindings must not be dropped. Ask the platform team to review this app.`);
|
|
8790
|
+
}
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
function saveMigration(file, original, converted) {
|
|
8795
|
+
if (readFileSync11(file, "utf8") !== original)
|
|
8796
|
+
throw new Error("launchpad.yaml changed during the check. Retry migration.");
|
|
8797
|
+
const backupDir = path8.join(path8.dirname(file), ".launchpad");
|
|
8798
|
+
if (existsSync7(backupDir) && !lstatSync4(backupDir).isDirectory()) {
|
|
8799
|
+
throw new Error(".launchpad must be a regular directory, not a symlink or file.");
|
|
8800
|
+
}
|
|
8801
|
+
mkdirSync2(backupDir, { recursive: true });
|
|
8802
|
+
const backup = path8.join(backupDir, "manifest-before-dispatch.yaml");
|
|
8803
|
+
const attemptDir = mkdtempSync(path8.join(backupDir, "migration-"));
|
|
8804
|
+
let backupCreated = false;
|
|
8805
|
+
try {
|
|
8806
|
+
const fd = openSync(backup, "wx", 384);
|
|
8807
|
+
backupCreated = true;
|
|
8808
|
+
try {
|
|
8809
|
+
writeFileSync2(fd, original);
|
|
8810
|
+
} finally {
|
|
8811
|
+
closeSync(fd);
|
|
8812
|
+
}
|
|
8813
|
+
const temporary = path8.join(attemptDir, "launchpad.yaml");
|
|
8814
|
+
writeFileSync2(temporary, stringify(converted), { flag: "wx", mode: 384 });
|
|
8815
|
+
if (readFileSync11(file, "utf8") !== original)
|
|
8816
|
+
throw new Error("launchpad.yaml changed during migration. Retry migration.");
|
|
8817
|
+
renameSync(temporary, file);
|
|
8818
|
+
} catch (error) {
|
|
8819
|
+
if (backupCreated)
|
|
8820
|
+
rmSync2(backup);
|
|
8821
|
+
throw error;
|
|
8822
|
+
} finally {
|
|
8823
|
+
rmSync2(attemptDir, { recursive: true, force: true });
|
|
8824
|
+
}
|
|
8825
|
+
}
|
|
8826
|
+
|
|
8610
8827
|
// src/commands/deploy.ts
|
|
8611
8828
|
var deployCommand = {
|
|
8612
8829
|
name: "deploy",
|
|
@@ -8659,8 +8876,8 @@ async function runDeploy(args, io) {
|
|
|
8659
8876
|
}, io);
|
|
8660
8877
|
}
|
|
8661
8878
|
const cwd = process.cwd();
|
|
8662
|
-
const manifestPath =
|
|
8663
|
-
if (
|
|
8879
|
+
const manifestPath = path9.join(cwd, "launchpad.yaml");
|
|
8880
|
+
if (existsSync8(manifestPath)) {
|
|
8664
8881
|
const contentMode = flags.mode.kind === "content" ? flags.mode : {
|
|
8665
8882
|
allowStale: false,
|
|
8666
8883
|
rebaseOntoHead: false,
|
|
@@ -8694,7 +8911,7 @@ async function runDeploy(args, io) {
|
|
|
8694
8911
|
} else {
|
|
8695
8912
|
const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
|
|
8696
8913
|
if (inferred === null) {
|
|
8697
|
-
io.err(`launchpad deploy: could not infer slug from cwd (${
|
|
8914
|
+
io.err(`launchpad deploy: could not infer slug from cwd (${path9.basename(process.cwd())});
|
|
8698
8915
|
` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
|
|
8699
8916
|
return 64;
|
|
8700
8917
|
}
|
|
@@ -8714,10 +8931,10 @@ async function runDeploy(args, io) {
|
|
|
8714
8931
|
return 1;
|
|
8715
8932
|
}
|
|
8716
8933
|
let contentManifestYaml = null;
|
|
8717
|
-
const contentManifestPath =
|
|
8718
|
-
if (
|
|
8934
|
+
const contentManifestPath = path9.join(process.cwd(), "launchpad.yaml");
|
|
8935
|
+
if (existsSync8(contentManifestPath)) {
|
|
8719
8936
|
try {
|
|
8720
|
-
contentManifestYaml =
|
|
8937
|
+
contentManifestYaml = readFileSync12(contentManifestPath, "utf8");
|
|
8721
8938
|
} catch {
|
|
8722
8939
|
contentManifestYaml = null;
|
|
8723
8940
|
}
|
|
@@ -8927,8 +9144,8 @@ function surfaceStaleBlock(body, io) {
|
|
|
8927
9144
|
const head = body.head_sha ?? null;
|
|
8928
9145
|
if (body.error === "stale_overlap" && body.paths !== undefined) {
|
|
8929
9146
|
io.err(`launchpad deploy: BLOCKED — this deploy would roll back ${body.paths.length} file(s) a teammate changed on main since you cloned:`);
|
|
8930
|
-
for (const
|
|
8931
|
-
io.err(` - ${
|
|
9147
|
+
for (const path10 of body.paths.slice(0, 20)) {
|
|
9148
|
+
io.err(` - ${path10}`);
|
|
8932
9149
|
}
|
|
8933
9150
|
if (body.paths.length > 20) {
|
|
8934
9151
|
io.err(` … and ${body.paths.length - 20} more`);
|
|
@@ -8961,7 +9178,7 @@ function withTimeout(p, ms) {
|
|
|
8961
9178
|
async function surfaceManifestDiff(cfg, slug, manifestPath, io) {
|
|
8962
9179
|
let localYaml;
|
|
8963
9180
|
try {
|
|
8964
|
-
localYaml =
|
|
9181
|
+
localYaml = readFileSync12(manifestPath, "utf8");
|
|
8965
9182
|
} catch {
|
|
8966
9183
|
return;
|
|
8967
9184
|
}
|
|
@@ -9010,8 +9227,21 @@ async function runModelADeploy(args) {
|
|
|
9010
9227
|
let slug;
|
|
9011
9228
|
let appType = "static";
|
|
9012
9229
|
let declaredOrigin;
|
|
9230
|
+
let pagesNote;
|
|
9231
|
+
try {
|
|
9232
|
+
pagesNote = await defaultToDispatch(cwd, manifestPath, io);
|
|
9233
|
+
} catch (e) {
|
|
9234
|
+
if (e instanceof UpgradeRequiredError) {
|
|
9235
|
+
io.err(e.message);
|
|
9236
|
+
return EXIT_CLI_UPGRADE_REQUIRED;
|
|
9237
|
+
}
|
|
9238
|
+
io.err(`launchpad deploy: ${describe17(e)}. Nothing was deployed.`);
|
|
9239
|
+
if (e instanceof UnauthenticatedError)
|
|
9240
|
+
io.err(" run `launchpad login` to refresh your session.");
|
|
9241
|
+
return 1;
|
|
9242
|
+
}
|
|
9013
9243
|
try {
|
|
9014
|
-
const manifestObj = parseYaml6(
|
|
9244
|
+
const manifestObj = parseYaml6(readFileSync12(manifestPath, "utf8"));
|
|
9015
9245
|
const metaSlug = resolveManifestSlug(manifestObj);
|
|
9016
9246
|
if (metaSlug === null) {
|
|
9017
9247
|
io.err(`launchpad deploy: launchpad.yaml is missing metadata.slug (v2) / metadata.name (v1). ` + `Run \`launchpad init\` again to regenerate the manifest.`);
|
|
@@ -9076,6 +9306,11 @@ async function runModelADeploy(args) {
|
|
|
9076
9306
|
return 64;
|
|
9077
9307
|
}
|
|
9078
9308
|
io.out(`Bundling ${cwd} for ${slug} (Model A) …`);
|
|
9309
|
+
if (declaredOrigin !== "dispatch" && appType !== "container") {
|
|
9310
|
+
io.out("Deployment target: Cloudflare Pages, selected by launchpad.yaml.");
|
|
9311
|
+
if (pagesNote !== null)
|
|
9312
|
+
io.out(pagesNote);
|
|
9313
|
+
}
|
|
9079
9314
|
let cfg;
|
|
9080
9315
|
try {
|
|
9081
9316
|
cfg = loadConfig();
|
|
@@ -9160,7 +9395,7 @@ async function runModelADeploy(args) {
|
|
|
9160
9395
|
const errorCode = body.error;
|
|
9161
9396
|
if (errorCode === "audience_confirmation_required") {
|
|
9162
9397
|
io.err(String(body.message));
|
|
9163
|
-
io.err("
|
|
9398
|
+
io.err(body.requires_admin_approval === true ? "This sensitive app needs builder confirmation and platform-admin approval. Retry after both are complete." : "The server still requires browser confirmation. Ask the platform team to update it, then retry.");
|
|
9164
9399
|
return 1;
|
|
9165
9400
|
}
|
|
9166
9401
|
if (result.status === 426 && typeof body.minimum === "string" && typeof body.yours === "string") {
|
|
@@ -9363,6 +9598,37 @@ async function runModelADeploy(args) {
|
|
|
9363
9598
|
function surfaceCommittedHandoff(dispatchOrigin, io, pagesStyle = "unicode") {
|
|
9364
9599
|
io.out(committedHandoffMessage(dispatchOrigin, pagesStyle));
|
|
9365
9600
|
}
|
|
9601
|
+
function isIneligible(e) {
|
|
9602
|
+
return !(e instanceof RegistryUnavailableError || e instanceof MigrationSaveError || e instanceof UpgradeRequiredError || e instanceof UnauthenticatedError);
|
|
9603
|
+
}
|
|
9604
|
+
async function defaultToDispatch(cwd, manifestPath, io) {
|
|
9605
|
+
let raw;
|
|
9606
|
+
try {
|
|
9607
|
+
raw = parseYaml6(readFileSync12(manifestPath, "utf8"));
|
|
9608
|
+
} catch {
|
|
9609
|
+
return null;
|
|
9610
|
+
}
|
|
9611
|
+
const isV22 = raw?.apiVersion === "launchpad.m-kopa.us/v2";
|
|
9612
|
+
const appType = isV22 ? raw?.spec?.appType : raw?.deployment?.type;
|
|
9613
|
+
const origin = isV22 ? raw?.spec?.origin : raw?.origin;
|
|
9614
|
+
const auth = isV22 ? raw?.spec?.auth : raw?.auth;
|
|
9615
|
+
if (appType !== "static" && appType !== "react" || origin !== undefined || auth !== "gateway")
|
|
9616
|
+
return null;
|
|
9617
|
+
try {
|
|
9618
|
+
if ((await convertToDispatch(cwd, false)).kind === "converted") {
|
|
9619
|
+
io.out("New apps deploy on Workers dispatch by default, so launchpad.yaml was converted (no Pages project needed).");
|
|
9620
|
+
io.out(` Audience, owner and build settings are preserved. Original: ${BACKUP_PATH}`);
|
|
9621
|
+
io.out(" To stay on Pages instead, set `spec.origin: pages` and deploy again.");
|
|
9622
|
+
}
|
|
9623
|
+
return null;
|
|
9624
|
+
} catch (e) {
|
|
9625
|
+
if (e instanceof RegisteredAppError)
|
|
9626
|
+
return null;
|
|
9627
|
+
if (!(e instanceof Error) || !isIneligible(e))
|
|
9628
|
+
throw e;
|
|
9629
|
+
return `Not converted to Workers dispatch: ${describe17(e)}`;
|
|
9630
|
+
}
|
|
9631
|
+
}
|
|
9366
9632
|
function declaredOriginOf(isV22, origin) {
|
|
9367
9633
|
if (!isV22)
|
|
9368
9634
|
return "pages";
|
|
@@ -9397,7 +9663,7 @@ function pendingReconcileOutcome(body) {
|
|
|
9397
9663
|
}
|
|
9398
9664
|
|
|
9399
9665
|
// src/commands/redeploy.ts
|
|
9400
|
-
import { existsSync as
|
|
9666
|
+
import { existsSync as existsSync9, readFileSync as readFileSync13 } from "node:fs";
|
|
9401
9667
|
import { resolve as resolvePath2 } from "node:path";
|
|
9402
9668
|
import { parse as parseYaml7 } from "yaml";
|
|
9403
9669
|
var SLUG_RE6 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
@@ -9475,10 +9741,10 @@ async function runRedeploy(opts, io, deps = {}) {
|
|
|
9475
9741
|
}
|
|
9476
9742
|
function readLocalAppType(cwd, file) {
|
|
9477
9743
|
const manifestPath = resolvePath2(cwd, file ?? "launchpad.yaml");
|
|
9478
|
-
if (!
|
|
9744
|
+
if (!existsSync9(manifestPath))
|
|
9479
9745
|
return null;
|
|
9480
9746
|
try {
|
|
9481
|
-
const obj = parseYaml7(
|
|
9747
|
+
const obj = parseYaml7(readFileSync13(manifestPath, "utf8"));
|
|
9482
9748
|
return coerceAppType(obj?.deployment?.type);
|
|
9483
9749
|
} catch {
|
|
9484
9750
|
return null;
|
|
@@ -9572,7 +9838,7 @@ function printHelp(io) {
|
|
|
9572
9838
|
}
|
|
9573
9839
|
|
|
9574
9840
|
// src/commands/envvars.ts
|
|
9575
|
-
import * as
|
|
9841
|
+
import * as path10 from "node:path";
|
|
9576
9842
|
var envvarsCommand = {
|
|
9577
9843
|
name: "envvars",
|
|
9578
9844
|
summary: "list / set / remove production env vars (slug-scoped)",
|
|
@@ -9592,7 +9858,7 @@ async function runEnvvars(args, io) {
|
|
|
9592
9858
|
} else {
|
|
9593
9859
|
const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
|
|
9594
9860
|
if (inferred === null) {
|
|
9595
|
-
io.err(`launchpad envvars: could not infer slug from cwd (${
|
|
9861
|
+
io.err(`launchpad envvars: could not infer slug from cwd (${path10.basename(process.cwd())});
|
|
9596
9862
|
` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
|
|
9597
9863
|
return 64;
|
|
9598
9864
|
}
|
|
@@ -9762,7 +10028,7 @@ function describe18(e) {
|
|
|
9762
10028
|
}
|
|
9763
10029
|
|
|
9764
10030
|
// src/commands/generate.ts
|
|
9765
|
-
import { mkdirSync as
|
|
10031
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
|
|
9766
10032
|
import { dirname as dirname7, resolve as resolve10, relative as relative4 } from "node:path";
|
|
9767
10033
|
var generateCommand = {
|
|
9768
10034
|
name: "generate",
|
|
@@ -9812,7 +10078,7 @@ async function runGenerate(args, io) {
|
|
|
9812
10078
|
];
|
|
9813
10079
|
return flags.json ? renderApplyJson(reports, appRoot, io) : renderApplyHuman(reports, appRoot, io);
|
|
9814
10080
|
}
|
|
9815
|
-
function applyOne(artefact,
|
|
10081
|
+
function applyOne(artefact, path11, out, force) {
|
|
9816
10082
|
if (out.kind === "not-applicable" || out.kind === "unsupported") {
|
|
9817
10083
|
return {
|
|
9818
10084
|
artefact,
|
|
@@ -9823,32 +10089,32 @@ function applyOne(artefact, path10, out, force) {
|
|
|
9823
10089
|
warnings: []
|
|
9824
10090
|
};
|
|
9825
10091
|
}
|
|
9826
|
-
const existing = readIfExists(
|
|
10092
|
+
const existing = readIfExists(path11);
|
|
9827
10093
|
if (existing.kind === "read-error") {
|
|
9828
10094
|
return {
|
|
9829
10095
|
artefact,
|
|
9830
|
-
action: { kind: "write-error", path:
|
|
10096
|
+
action: { kind: "write-error", path: path11, message: existing.message },
|
|
9831
10097
|
warnings: out.warnings
|
|
9832
10098
|
};
|
|
9833
10099
|
}
|
|
9834
10100
|
if (existing.kind === "ok" && existing.content === out.content) {
|
|
9835
|
-
return { artefact, action: { kind: "unchanged", path:
|
|
10101
|
+
return { artefact, action: { kind: "unchanged", path: path11 }, warnings: out.warnings };
|
|
9836
10102
|
}
|
|
9837
10103
|
if (existing.kind === "ok" && existing.content !== out.content && !force) {
|
|
9838
10104
|
return {
|
|
9839
10105
|
artefact,
|
|
9840
|
-
action: { kind: "would-overwrite", path:
|
|
10106
|
+
action: { kind: "would-overwrite", path: path11 },
|
|
9841
10107
|
warnings: out.warnings
|
|
9842
10108
|
};
|
|
9843
10109
|
}
|
|
9844
10110
|
try {
|
|
9845
|
-
|
|
9846
|
-
|
|
10111
|
+
mkdirSync3(dirname7(path11), { recursive: true });
|
|
10112
|
+
writeFileSync3(path11, out.content, "utf8");
|
|
9847
10113
|
return {
|
|
9848
10114
|
artefact,
|
|
9849
10115
|
action: {
|
|
9850
10116
|
kind: "written",
|
|
9851
|
-
path:
|
|
10117
|
+
path: path11,
|
|
9852
10118
|
bytes: Buffer.byteLength(out.content, "utf8")
|
|
9853
10119
|
},
|
|
9854
10120
|
warnings: out.warnings
|
|
@@ -9858,16 +10124,16 @@ function applyOne(artefact, path10, out, force) {
|
|
|
9858
10124
|
artefact,
|
|
9859
10125
|
action: {
|
|
9860
10126
|
kind: "write-error",
|
|
9861
|
-
path:
|
|
10127
|
+
path: path11,
|
|
9862
10128
|
message: err.message ?? String(err)
|
|
9863
10129
|
},
|
|
9864
10130
|
warnings: out.warnings
|
|
9865
10131
|
};
|
|
9866
10132
|
}
|
|
9867
10133
|
}
|
|
9868
|
-
function readIfExists(
|
|
10134
|
+
function readIfExists(path11) {
|
|
9869
10135
|
try {
|
|
9870
|
-
return { kind: "ok", content:
|
|
10136
|
+
return { kind: "ok", content: readFileSync14(path11, "utf8") };
|
|
9871
10137
|
} catch (err) {
|
|
9872
10138
|
const e = err;
|
|
9873
10139
|
if (e.code === "ENOENT")
|
|
@@ -10606,17 +10872,17 @@ function describe21(e) {
|
|
|
10606
10872
|
|
|
10607
10873
|
// src/commands/init.ts
|
|
10608
10874
|
import { createInterface } from "node:readline/promises";
|
|
10609
|
-
import { existsSync as
|
|
10875
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync4 } from "node:fs";
|
|
10610
10876
|
import { dirname as dirname8, resolve as resolve11 } from "node:path";
|
|
10611
10877
|
import { stringify as yamlStringify } from "yaml";
|
|
10612
10878
|
|
|
10613
10879
|
// src/detect/index.ts
|
|
10614
|
-
import { existsSync as
|
|
10880
|
+
import { existsSync as existsSync10, readFileSync as readFileSync15, statSync } from "node:fs";
|
|
10615
10881
|
import { join as join13 } from "node:path";
|
|
10616
10882
|
function detectAppShape(cwd) {
|
|
10617
|
-
const hasPackageJson =
|
|
10618
|
-
const hasAnyLockfile = LOCKFILES.some(({ file }) =>
|
|
10619
|
-
const hasViteConfig = VITE_CONFIG_NAMES.some((n) =>
|
|
10883
|
+
const hasPackageJson = existsSync10(join13(cwd, "package.json"));
|
|
10884
|
+
const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync10(join13(cwd, file)));
|
|
10885
|
+
const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync10(join13(cwd, n)));
|
|
10620
10886
|
if (!hasPackageJson && !hasAnyLockfile && !hasViteConfig) {
|
|
10621
10887
|
return {
|
|
10622
10888
|
kind: "not-applicable",
|
|
@@ -10662,7 +10928,7 @@ var LOCKFILES = [
|
|
|
10662
10928
|
{ file: "yarn.lock", pm: "yarn" }
|
|
10663
10929
|
];
|
|
10664
10930
|
function detectPackageManager(cwd) {
|
|
10665
|
-
const present = LOCKFILES.filter(({ file }) =>
|
|
10931
|
+
const present = LOCKFILES.filter(({ file }) => existsSync10(join13(cwd, file)));
|
|
10666
10932
|
if (present.length === 0) {
|
|
10667
10933
|
return {
|
|
10668
10934
|
kind: "ambiguous",
|
|
@@ -10685,7 +10951,7 @@ function detectPackageManager(cwd) {
|
|
|
10685
10951
|
function detectVitePresence(cwd) {
|
|
10686
10952
|
for (const name of VITE_CONFIG_NAMES) {
|
|
10687
10953
|
const p = join13(cwd, name);
|
|
10688
|
-
if (
|
|
10954
|
+
if (existsSync10(p)) {
|
|
10689
10955
|
return { kind: "ok", value: { path: p } };
|
|
10690
10956
|
}
|
|
10691
10957
|
}
|
|
@@ -10697,7 +10963,7 @@ function detectVitePresence(cwd) {
|
|
|
10697
10963
|
function detectAppType(cwd) {
|
|
10698
10964
|
const fnDir = join13(cwd, "functions");
|
|
10699
10965
|
let hasFunctionsDir = false;
|
|
10700
|
-
if (
|
|
10966
|
+
if (existsSync10(fnDir)) {
|
|
10701
10967
|
try {
|
|
10702
10968
|
hasFunctionsDir = statSync(fnDir).isDirectory();
|
|
10703
10969
|
} catch {
|
|
@@ -10708,7 +10974,7 @@ function detectAppType(cwd) {
|
|
|
10708
10974
|
}
|
|
10709
10975
|
function detectBuildCommand(cwd, pm) {
|
|
10710
10976
|
const pkgJsonPath = join13(cwd, "package.json");
|
|
10711
|
-
if (!
|
|
10977
|
+
if (!existsSync10(pkgJsonPath)) {
|
|
10712
10978
|
return {
|
|
10713
10979
|
kind: "ambiguous",
|
|
10714
10980
|
reason: "no package.json at repo root. Run your package manager's `init` first."
|
|
@@ -10716,7 +10982,7 @@ function detectBuildCommand(cwd, pm) {
|
|
|
10716
10982
|
}
|
|
10717
10983
|
let pkgJson;
|
|
10718
10984
|
try {
|
|
10719
|
-
pkgJson = JSON.parse(
|
|
10985
|
+
pkgJson = JSON.parse(readFileSync15(pkgJsonPath, "utf8"));
|
|
10720
10986
|
} catch (e) {
|
|
10721
10987
|
return {
|
|
10722
10988
|
kind: "ambiguous",
|
|
@@ -10751,7 +11017,7 @@ var OUT_DIR_REGEX = /\bbuild\s*:\s*\{[^{}]*?\boutDir\s*:\s*['"]([^'"]+)['"]/s;
|
|
|
10751
11017
|
function detectDestinationDir(cwd, vite) {
|
|
10752
11018
|
let text;
|
|
10753
11019
|
try {
|
|
10754
|
-
text =
|
|
11020
|
+
text = readFileSync15(vite.path, "utf8");
|
|
10755
11021
|
} catch (e) {
|
|
10756
11022
|
return {
|
|
10757
11023
|
kind: "ambiguous",
|
|
@@ -10781,7 +11047,7 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
|
|
|
10781
11047
|
}
|
|
10782
11048
|
const { inputs, options } = parsed;
|
|
10783
11049
|
const outPath = resolve11(process.cwd(), options.out);
|
|
10784
|
-
if (
|
|
11050
|
+
if (existsSync11(outPath) && !options.force) {
|
|
10785
11051
|
io.err(`launchpad init: ${outPath} already exists`);
|
|
10786
11052
|
io.err("Pass --force to overwrite.");
|
|
10787
11053
|
return 64;
|
|
@@ -10837,7 +11103,7 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
|
|
|
10837
11103
|
io.out(`Audience: private. Only you (${privateOwner.email}) and platform administrators can open this app.`);
|
|
10838
11104
|
} else {
|
|
10839
11105
|
io.out(`Audience: selected Entra groups (${fillResult.inputs.groups.join(", ")}), plus platform administrators.`);
|
|
10840
|
-
io.out("
|
|
11106
|
+
io.out("The deploy skill confirms company-wide grants with you. Sensitive apps also require browser confirmation and platform-admin approval.");
|
|
10841
11107
|
}
|
|
10842
11108
|
const manifest = buildManifest({ ...fillResult.inputs, ...privateOwner === undefined ? {} : { privateOwner } }, detected);
|
|
10843
11109
|
const validation = manifest.apiVersion === "launchpad.m-kopa.us/v2" ? ManifestV2Schema.safeParse(manifest) : ManifestSchema.safeParse(manifest);
|
|
@@ -10850,7 +11116,7 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
|
|
|
10850
11116
|
}
|
|
10851
11117
|
const yamlText = renderYaml(manifest);
|
|
10852
11118
|
try {
|
|
10853
|
-
|
|
11119
|
+
writeFileSync4(outPath, yamlText, { encoding: "utf8" });
|
|
10854
11120
|
} catch (err) {
|
|
10855
11121
|
const e = err;
|
|
10856
11122
|
io.err(`launchpad init: failed to write ${outPath}`);
|
|
@@ -10876,10 +11142,10 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
|
|
|
10876
11142
|
if (fillResult.inputs.type === "static") {
|
|
10877
11143
|
const serveDir = resolve11(dirname8(outPath), STATIC_SERVE_DIR);
|
|
10878
11144
|
const indexPath = resolve11(serveDir, "index.html");
|
|
10879
|
-
if (!
|
|
11145
|
+
if (!existsSync11(indexPath)) {
|
|
10880
11146
|
try {
|
|
10881
|
-
|
|
10882
|
-
|
|
11147
|
+
mkdirSync4(serveDir, { recursive: true });
|
|
11148
|
+
writeFileSync4(indexPath, starterIndexHtml(fillResult.inputs.name), {
|
|
10883
11149
|
encoding: "utf8"
|
|
10884
11150
|
});
|
|
10885
11151
|
io.out(`✓ scaffolded ${indexPath}`);
|
|
@@ -11298,10 +11564,10 @@ function starterIndexHtml(slug) {
|
|
|
11298
11564
|
function renderYaml(manifest) {
|
|
11299
11565
|
return yamlStringify(manifest, { lineWidth: 0 });
|
|
11300
11566
|
}
|
|
11301
|
-
function ensureGitignoreEntries(
|
|
11567
|
+
function ensureGitignoreEntries(path11, entries) {
|
|
11302
11568
|
let current = "";
|
|
11303
|
-
if (
|
|
11304
|
-
current =
|
|
11569
|
+
if (existsSync11(path11)) {
|
|
11570
|
+
current = readFileSync16(path11, "utf8");
|
|
11305
11571
|
}
|
|
11306
11572
|
const lines = current.split(/\r?\n/);
|
|
11307
11573
|
const present = new Set(lines.map((l) => l.trim()));
|
|
@@ -11319,7 +11585,7 @@ function ensureGitignoreEntries(path10, entries) {
|
|
|
11319
11585
|
}
|
|
11320
11586
|
}
|
|
11321
11587
|
if (added.length > 0) {
|
|
11322
|
-
|
|
11588
|
+
writeFileSync4(path11, out, { encoding: "utf8" });
|
|
11323
11589
|
}
|
|
11324
11590
|
return added;
|
|
11325
11591
|
}
|
|
@@ -11491,7 +11757,7 @@ function describe23(e) {
|
|
|
11491
11757
|
}
|
|
11492
11758
|
|
|
11493
11759
|
// src/commands/logs.ts
|
|
11494
|
-
import * as
|
|
11760
|
+
import * as path11 from "node:path";
|
|
11495
11761
|
var logsCommand = {
|
|
11496
11762
|
name: "logs",
|
|
11497
11763
|
summary: "show recent Pages deployment history (slug-scoped)",
|
|
@@ -11513,7 +11779,7 @@ async function runLogs(args, io) {
|
|
|
11513
11779
|
} else {
|
|
11514
11780
|
const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
|
|
11515
11781
|
if (inferred === null) {
|
|
11516
|
-
io.err(`launchpad logs: could not infer slug from cwd (${
|
|
11782
|
+
io.err(`launchpad logs: could not infer slug from cwd (${path11.basename(process.cwd())});
|
|
11517
11783
|
` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
|
|
11518
11784
|
return 64;
|
|
11519
11785
|
}
|
|
@@ -11641,7 +11907,7 @@ function describe24(e) {
|
|
|
11641
11907
|
}
|
|
11642
11908
|
|
|
11643
11909
|
// src/commands/merge.ts
|
|
11644
|
-
import * as
|
|
11910
|
+
import * as path12 from "node:path";
|
|
11645
11911
|
var mergeCommand = {
|
|
11646
11912
|
name: "merge",
|
|
11647
11913
|
summary: "squash-merge a review-passed PR (slug-scoped)",
|
|
@@ -11662,7 +11928,7 @@ async function runMerge(args, io) {
|
|
|
11662
11928
|
} else {
|
|
11663
11929
|
const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
|
|
11664
11930
|
if (inferred === null) {
|
|
11665
|
-
io.err(`launchpad merge: could not infer slug from cwd (${
|
|
11931
|
+
io.err(`launchpad merge: could not infer slug from cwd (${path12.basename(process.cwd())});
|
|
11666
11932
|
` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
|
|
11667
11933
|
return 64;
|
|
11668
11934
|
}
|
|
@@ -12292,18 +12558,18 @@ function describe26(e) {
|
|
|
12292
12558
|
}
|
|
12293
12559
|
|
|
12294
12560
|
// src/commands/pull.ts
|
|
12295
|
-
import { writeFileSync as
|
|
12561
|
+
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
12296
12562
|
import { stringify as stringifyYaml } from "yaml";
|
|
12297
12563
|
|
|
12298
12564
|
// src/deploy/manifest-status.ts
|
|
12299
12565
|
async function fetchManifestStatus(cfg, slug, fetcher = fetch) {
|
|
12300
|
-
const
|
|
12301
|
-
return apiJson(cfg, { path:
|
|
12566
|
+
const path13 = `/apps/${encodeURIComponent(slug)}/manifest/status`;
|
|
12567
|
+
return apiJson(cfg, { path: path13 }, fetcher);
|
|
12302
12568
|
}
|
|
12303
12569
|
|
|
12304
12570
|
// src/pull/source-sync.ts
|
|
12305
12571
|
import * as fs6 from "node:fs/promises";
|
|
12306
|
-
import * as
|
|
12572
|
+
import * as path13 from "node:path";
|
|
12307
12573
|
import { constants as fsConstants } from "node:fs";
|
|
12308
12574
|
import { createHash as createHash3 } from "node:crypto";
|
|
12309
12575
|
import { execFile } from "node:child_process";
|
|
@@ -12342,9 +12608,9 @@ async function runSourceSync(args) {
|
|
|
12342
12608
|
args.io.out(`Already current at ${compare.headSha.slice(0, 12)}.`);
|
|
12343
12609
|
return 0;
|
|
12344
12610
|
}
|
|
12345
|
-
const stageRoot =
|
|
12346
|
-
const baseDir =
|
|
12347
|
-
const headDir =
|
|
12611
|
+
const stageRoot = path13.resolve(args.cwd, MARKER_DIR, `pull-stage-${process.pid}-${crypto.randomUUID()}`);
|
|
12612
|
+
const baseDir = path13.join(stageRoot, "base");
|
|
12613
|
+
const headDir = path13.join(stageRoot, "head");
|
|
12348
12614
|
await fs6.mkdir(baseDir, { recursive: true });
|
|
12349
12615
|
await fs6.mkdir(headDir, { recursive: true });
|
|
12350
12616
|
try {
|
|
@@ -12396,7 +12662,7 @@ async function bootstrapProvenance(args) {
|
|
|
12396
12662
|
args.io.err(" Preserve this directory and obtain a separate current clone for comparison.");
|
|
12397
12663
|
return 1;
|
|
12398
12664
|
}
|
|
12399
|
-
const stageRoot =
|
|
12665
|
+
const stageRoot = path13.resolve(args.cwd, MARKER_DIR, `pull-bootstrap-${process.pid}-${crypto.randomUUID()}`);
|
|
12400
12666
|
await fs6.mkdir(stageRoot, { recursive: true });
|
|
12401
12667
|
try {
|
|
12402
12668
|
const headSha = await fetchSnapshotRef(args.cfg, args.slug, "main", stageRoot);
|
|
@@ -12422,7 +12688,7 @@ async function treesEqual(cwd, tracked, headDir, headPaths) {
|
|
|
12422
12688
|
for (const relative6 of tracked) {
|
|
12423
12689
|
if (!expected.has(relative6))
|
|
12424
12690
|
return false;
|
|
12425
|
-
if (!equal(await readEntry(
|
|
12691
|
+
if (!equal(await readEntry(path13.resolve(cwd, relative6)), await readEntry(path13.resolve(headDir, relative6)))) {
|
|
12426
12692
|
return false;
|
|
12427
12693
|
}
|
|
12428
12694
|
}
|
|
@@ -12430,7 +12696,7 @@ async function treesEqual(cwd, tracked, headDir, headPaths) {
|
|
|
12430
12696
|
}
|
|
12431
12697
|
async function listFiles(root, relative6 = "") {
|
|
12432
12698
|
const files = [];
|
|
12433
|
-
const entries = await fs6.readdir(
|
|
12699
|
+
const entries = await fs6.readdir(path13.resolve(root, relative6), {
|
|
12434
12700
|
withFileTypes: true
|
|
12435
12701
|
});
|
|
12436
12702
|
for (const entry of entries) {
|
|
@@ -12506,7 +12772,7 @@ async function cleanAncestorRemovals(args) {
|
|
|
12506
12772
|
const checked = await checkParents(args.cwd, relative6, removals);
|
|
12507
12773
|
if (checked.kind === "blocked")
|
|
12508
12774
|
continue;
|
|
12509
|
-
const base = await readEntry(
|
|
12775
|
+
const base = await readEntry(path13.resolve(args.baseDir, relative6));
|
|
12510
12776
|
const local = checked.projectedMissing ? { kind: "missing" } : await readEntry(checked.target);
|
|
12511
12777
|
if (local.kind !== "missing" && cleanLocal(base, local))
|
|
12512
12778
|
removals.add(relative6);
|
|
@@ -12516,9 +12782,9 @@ async function cleanAncestorRemovals(args) {
|
|
|
12516
12782
|
async function planPath(args, relative6, removals) {
|
|
12517
12783
|
assertSafePath(relative6);
|
|
12518
12784
|
const checked = await checkParents(args.cwd, relative6, removals);
|
|
12519
|
-
const base = await readEntry(
|
|
12785
|
+
const base = await readEntry(path13.resolve(args.baseDir, relative6));
|
|
12520
12786
|
const upstreamDeleted = args.compare.deleted.includes(relative6);
|
|
12521
|
-
const head = upstreamDeleted ? { kind: "missing" } : await readEntry(
|
|
12787
|
+
const head = upstreamDeleted ? { kind: "missing" } : await readEntry(path13.resolve(args.headDir, relative6));
|
|
12522
12788
|
const unsupported2 = (args.compare.unsupported ?? []).includes(relative6);
|
|
12523
12789
|
if (checked.kind === "blocked") {
|
|
12524
12790
|
return conflictPlan(relative6, checked.target, { kind: "missing" }, head, upstreamDeleted, checked.reason);
|
|
@@ -12655,7 +12921,7 @@ async function writeAtomic(cwd, relative6, bytes, mode, allowMetadata = false) {
|
|
|
12655
12921
|
if (checked.kind === "blocked")
|
|
12656
12922
|
throw new Error(checked.reason);
|
|
12657
12923
|
const file = checked.target;
|
|
12658
|
-
await fs6.mkdir(
|
|
12924
|
+
await fs6.mkdir(path13.dirname(file), { recursive: true });
|
|
12659
12925
|
const rechecked = await checkParents(cwd, relative6, new Set, allowMetadata);
|
|
12660
12926
|
if (rechecked.kind === "blocked")
|
|
12661
12927
|
throw new Error(rechecked.reason);
|
|
@@ -12697,14 +12963,14 @@ function isSafeSourcePath(relative6) {
|
|
|
12697
12963
|
return isSafeRelativePath(relative6) && !relative6.startsWith(`${MARKER_DIR}/`);
|
|
12698
12964
|
}
|
|
12699
12965
|
function isSafeRelativePath(relative6) {
|
|
12700
|
-
const normalized =
|
|
12701
|
-
return relative6.length > 0 && !relative6.includes("\\") && !
|
|
12966
|
+
const normalized = path13.posix.normalize(relative6);
|
|
12967
|
+
return relative6.length > 0 && !relative6.includes("\\") && !path13.isAbsolute(relative6) && normalized === relative6 && normalized !== ".." && !normalized.startsWith("../");
|
|
12702
12968
|
}
|
|
12703
12969
|
function resolveInsideClone(cwd, relative6) {
|
|
12704
|
-
const root =
|
|
12705
|
-
const target =
|
|
12706
|
-
const fromRoot =
|
|
12707
|
-
if (fromRoot.length === 0 ||
|
|
12970
|
+
const root = path13.resolve(cwd);
|
|
12971
|
+
const target = path13.resolve(root, relative6);
|
|
12972
|
+
const fromRoot = path13.relative(root, target);
|
|
12973
|
+
if (fromRoot.length === 0 || path13.isAbsolute(fromRoot) || fromRoot === ".." || fromRoot.startsWith(`..${path13.sep}`)) {
|
|
12708
12974
|
throw new Error(`unsafe source path returned by bot: ${relative6}`);
|
|
12709
12975
|
}
|
|
12710
12976
|
return target;
|
|
@@ -12719,9 +12985,9 @@ async function checkParents(cwd, relative6, projectedRemovals, allowMetadata = f
|
|
|
12719
12985
|
}
|
|
12720
12986
|
const target = resolveInsideClone(cwd, relative6);
|
|
12721
12987
|
const components = relative6.split("/");
|
|
12722
|
-
let current =
|
|
12988
|
+
let current = path13.resolve(cwd);
|
|
12723
12989
|
for (let index = 0;index < components.length - 1; index += 1) {
|
|
12724
|
-
current =
|
|
12990
|
+
current = path13.join(current, components[index]);
|
|
12725
12991
|
const ancestor = components.slice(0, index + 1).join("/");
|
|
12726
12992
|
const stat = await lstatIfPresent(current);
|
|
12727
12993
|
if (stat === null)
|
|
@@ -12747,12 +13013,12 @@ async function checkParents(cwd, relative6, projectedRemovals, allowMetadata = f
|
|
|
12747
13013
|
return { kind: "safe", target, projectedMissing: false };
|
|
12748
13014
|
}
|
|
12749
13015
|
async function assertSafeCloneRoot(cwd) {
|
|
12750
|
-
const root = await fs6.lstat(
|
|
13016
|
+
const root = await fs6.lstat(path13.resolve(cwd));
|
|
12751
13017
|
if (!root.isDirectory() || root.isSymbolicLink()) {
|
|
12752
13018
|
throw new Error("launchpad pull requires a real clone directory");
|
|
12753
13019
|
}
|
|
12754
13020
|
try {
|
|
12755
|
-
const metadata = await fs6.lstat(
|
|
13021
|
+
const metadata = await fs6.lstat(path13.resolve(cwd, MARKER_DIR));
|
|
12756
13022
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
12757
13023
|
throw new Error("launchpad pull refuses a non-directory or symlinked .launchpad path");
|
|
12758
13024
|
}
|
|
@@ -12808,7 +13074,7 @@ async function runPull(args, io) {
|
|
|
12808
13074
|
}
|
|
12809
13075
|
const yaml = stringifyYaml(res.status);
|
|
12810
13076
|
if (parsed.out !== null) {
|
|
12811
|
-
|
|
13077
|
+
writeFileSync5(parsed.out, yaml);
|
|
12812
13078
|
io.err(`launchpad pull: wrote status (${res.role} view) to ${parsed.out}`);
|
|
12813
13079
|
} else {
|
|
12814
13080
|
io.out(yaml.replace(/\n$/, ""));
|
|
@@ -12829,7 +13095,7 @@ async function runPull(args, io) {
|
|
|
12829
13095
|
return 1;
|
|
12830
13096
|
}
|
|
12831
13097
|
if (parsed.out !== null) {
|
|
12832
|
-
|
|
13098
|
+
writeFileSync5(parsed.out, state.manifestYaml);
|
|
12833
13099
|
io.err(`launchpad pull: wrote ${state.manifestYaml.length} bytes to ${parsed.out}`);
|
|
12834
13100
|
} else {
|
|
12835
13101
|
io.out(state.manifestYaml.replace(/\n$/, ""));
|
|
@@ -13230,7 +13496,7 @@ var watchCommand = {
|
|
|
13230
13496
|
};
|
|
13231
13497
|
|
|
13232
13498
|
// src/commands/review.ts
|
|
13233
|
-
import * as
|
|
13499
|
+
import * as path14 from "node:path";
|
|
13234
13500
|
var reviewCommand = {
|
|
13235
13501
|
name: "review",
|
|
13236
13502
|
summary: "show the review state for a PR (slug-scoped)",
|
|
@@ -13250,7 +13516,7 @@ async function runReview(args, io) {
|
|
|
13250
13516
|
} else {
|
|
13251
13517
|
const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
|
|
13252
13518
|
if (inferred === null) {
|
|
13253
|
-
io.err(`launchpad review: could not infer slug from cwd (${
|
|
13519
|
+
io.err(`launchpad review: could not infer slug from cwd (${path14.basename(process.cwd())});
|
|
13254
13520
|
` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
|
|
13255
13521
|
return 64;
|
|
13256
13522
|
}
|
|
@@ -13383,7 +13649,7 @@ function describe28(e) {
|
|
|
13383
13649
|
}
|
|
13384
13650
|
|
|
13385
13651
|
// src/deploy/rollback.ts
|
|
13386
|
-
import { existsSync as
|
|
13652
|
+
import { existsSync as existsSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
|
|
13387
13653
|
import { resolve as resolvePath3 } from "node:path";
|
|
13388
13654
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
13389
13655
|
|
|
@@ -13485,7 +13751,7 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
13485
13751
|
}
|
|
13486
13752
|
}
|
|
13487
13753
|
try {
|
|
13488
|
-
|
|
13754
|
+
writeFileSync6(manifestPath, historicalYaml, "utf8");
|
|
13489
13755
|
} catch (e) {
|
|
13490
13756
|
io.err(`launchpad rollback: failed to write ${manifestPath}: ${describe29(e)}`);
|
|
13491
13757
|
return 2;
|
|
@@ -13507,16 +13773,16 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
13507
13773
|
yes: true
|
|
13508
13774
|
}, io, applyDeps);
|
|
13509
13775
|
}
|
|
13510
|
-
function readCurrentManifest(
|
|
13511
|
-
if (!
|
|
13776
|
+
function readCurrentManifest(path15) {
|
|
13777
|
+
if (!existsSync12(path15))
|
|
13512
13778
|
return null;
|
|
13513
13779
|
let raw;
|
|
13514
13780
|
try {
|
|
13515
|
-
raw =
|
|
13781
|
+
raw = readFileSync17(path15, "utf8");
|
|
13516
13782
|
} catch {
|
|
13517
13783
|
return null;
|
|
13518
13784
|
}
|
|
13519
|
-
const parsed = parseManifest2(raw,
|
|
13785
|
+
const parsed = parseManifest2(raw, path15);
|
|
13520
13786
|
if (parsed.kind !== "ok")
|
|
13521
13787
|
return null;
|
|
13522
13788
|
return summarise(parsed.manifest);
|
|
@@ -13668,7 +13934,7 @@ function parseArgs12(args) {
|
|
|
13668
13934
|
}
|
|
13669
13935
|
|
|
13670
13936
|
// src/secrets/push.ts
|
|
13671
|
-
import { existsSync as
|
|
13937
|
+
import { existsSync as existsSync13, readFileSync as readFileSync18 } from "node:fs";
|
|
13672
13938
|
import { resolve as resolvePath4 } from "node:path";
|
|
13673
13939
|
import { createInterface as createInterface4 } from "node:readline/promises";
|
|
13674
13940
|
import { stdin as input2, stdout as output2 } from "node:process";
|
|
@@ -13729,14 +13995,14 @@ async function runSecretsPush(opts, io, deps = {}) {
|
|
|
13729
13995
|
return 0;
|
|
13730
13996
|
}
|
|
13731
13997
|
const envPath = resolvePath4(process.cwd(), opts.env ?? ".env");
|
|
13732
|
-
if (!
|
|
13998
|
+
if (!existsSync13(envPath)) {
|
|
13733
13999
|
io.err(`launchpad secrets push: ${envPath}`);
|
|
13734
14000
|
io.err(" .env file not found. Run `launchpad secrets template` to scaffold one.");
|
|
13735
14001
|
return 2;
|
|
13736
14002
|
}
|
|
13737
14003
|
let envText;
|
|
13738
14004
|
try {
|
|
13739
|
-
envText =
|
|
14005
|
+
envText = readFileSync18(envPath, "utf8");
|
|
13740
14006
|
} catch (e) {
|
|
13741
14007
|
io.err(`launchpad secrets push: failed to read ${envPath}: ${describe30(e)}`);
|
|
13742
14008
|
return 2;
|
|
@@ -14079,7 +14345,7 @@ function renderManifestError5(result, io) {
|
|
|
14079
14345
|
}
|
|
14080
14346
|
|
|
14081
14347
|
// src/secrets/set.ts
|
|
14082
|
-
import { existsSync as
|
|
14348
|
+
import { existsSync as existsSync14, readFileSync as readFileSync19 } from "node:fs";
|
|
14083
14349
|
import { resolve as resolvePath6 } from "node:path";
|
|
14084
14350
|
import { parse as parseYaml8 } from "yaml";
|
|
14085
14351
|
var CELL_LABEL2 = {
|
|
@@ -14088,23 +14354,23 @@ var CELL_LABEL2 = {
|
|
|
14088
14354
|
not_deployed: "NOT_DEPLOYED"
|
|
14089
14355
|
};
|
|
14090
14356
|
function loadSet(fleetFile, setName, io) {
|
|
14091
|
-
const
|
|
14092
|
-
if (!
|
|
14093
|
-
io.err(`✗ ${
|
|
14357
|
+
const path15 = resolvePath6(process.cwd(), fleetFile ?? "fleet-secret-sets.yaml");
|
|
14358
|
+
if (!existsSync14(path15)) {
|
|
14359
|
+
io.err(`✗ ${path15}`);
|
|
14094
14360
|
io.err(" fleet-secret-sets.yaml not found. Run from the platform repo root or pass --fleet-file.");
|
|
14095
14361
|
return 2;
|
|
14096
14362
|
}
|
|
14097
14363
|
let obj;
|
|
14098
14364
|
try {
|
|
14099
|
-
obj = parseYaml8(
|
|
14365
|
+
obj = parseYaml8(readFileSync19(path15, "utf8"));
|
|
14100
14366
|
} catch (e) {
|
|
14101
|
-
io.err(`✗ ${
|
|
14367
|
+
io.err(`✗ ${path15}`);
|
|
14102
14368
|
io.err(` YAML parse error: ${e instanceof Error ? e.message : String(e)}`);
|
|
14103
14369
|
return 1;
|
|
14104
14370
|
}
|
|
14105
14371
|
const parsed = parseFleetSecretSets(obj);
|
|
14106
14372
|
if (!parsed.ok) {
|
|
14107
|
-
io.err(`✗ ${
|
|
14373
|
+
io.err(`✗ ${path15}`);
|
|
14108
14374
|
io.err(` ${parsed.issues.length} schema issue(s):`);
|
|
14109
14375
|
for (const i of parsed.issues)
|
|
14110
14376
|
io.err(` ${i.path}: ${i.message}`);
|
|
@@ -14113,7 +14379,7 @@ function loadSet(fleetFile, setName, io) {
|
|
|
14113
14379
|
const set = parsed.manifest.secretSets.find((s) => s.name === setName);
|
|
14114
14380
|
if (set === undefined) {
|
|
14115
14381
|
const names = parsed.manifest.secretSets.map((s) => s.name).join(", ");
|
|
14116
|
-
io.err(`✗ secret-set "${setName}" not found in ${
|
|
14382
|
+
io.err(`✗ secret-set "${setName}" not found in ${path15}`);
|
|
14117
14383
|
io.err(` declared sets: ${names}`);
|
|
14118
14384
|
return 1;
|
|
14119
14385
|
}
|
|
@@ -14181,14 +14447,14 @@ async function runSecretsPushSet(opts, io, deps = {}) {
|
|
|
14181
14447
|
if (typeof set === "number")
|
|
14182
14448
|
return set;
|
|
14183
14449
|
const envPath = resolvePath6(process.cwd(), opts.env ?? ".env");
|
|
14184
|
-
if (!
|
|
14450
|
+
if (!existsSync14(envPath)) {
|
|
14185
14451
|
io.err(`launchpad secrets push --set: ${envPath} not found.`);
|
|
14186
14452
|
io.err(` Create a .env carrying the set's secrets: ${set.secrets.join(", ")}`);
|
|
14187
14453
|
return 2;
|
|
14188
14454
|
}
|
|
14189
14455
|
let envText;
|
|
14190
14456
|
try {
|
|
14191
|
-
envText =
|
|
14457
|
+
envText = readFileSync19(envPath, "utf8");
|
|
14192
14458
|
} catch (e) {
|
|
14193
14459
|
io.err(`launchpad secrets push --set: failed to read ${envPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
14194
14460
|
return 2;
|
|
@@ -14332,7 +14598,7 @@ function setPushExit(e) {
|
|
|
14332
14598
|
}
|
|
14333
14599
|
|
|
14334
14600
|
// src/commands/secrets-template.ts
|
|
14335
|
-
import { existsSync as
|
|
14601
|
+
import { existsSync as existsSync15, writeFileSync as writeFileSync7 } from "node:fs";
|
|
14336
14602
|
import { resolve as resolve14 } from "node:path";
|
|
14337
14603
|
async function runSecretsTemplate(args, io) {
|
|
14338
14604
|
const flags = parseFlags4(args);
|
|
@@ -14356,13 +14622,13 @@ async function runSecretsTemplate(args, io) {
|
|
|
14356
14622
|
return 0;
|
|
14357
14623
|
}
|
|
14358
14624
|
const outPath = resolve14(process.cwd(), flags.out);
|
|
14359
|
-
if (
|
|
14625
|
+
if (existsSync15(outPath) && !flags.force) {
|
|
14360
14626
|
io.err(`launchpad secrets template: ${outPath} already exists`);
|
|
14361
14627
|
io.err("Pass --force to overwrite, or --stdout to print without writing.");
|
|
14362
14628
|
return 64;
|
|
14363
14629
|
}
|
|
14364
14630
|
try {
|
|
14365
|
-
|
|
14631
|
+
writeFileSync7(outPath, body, { encoding: "utf8" });
|
|
14366
14632
|
} catch (err) {
|
|
14367
14633
|
const e = err;
|
|
14368
14634
|
io.err(`launchpad secrets template: failed to write ${outPath}`);
|
|
@@ -14653,12 +14919,12 @@ function printHelp3(io) {
|
|
|
14653
14919
|
// src/commands/skills.ts
|
|
14654
14920
|
import { fileURLToPath } from "node:url";
|
|
14655
14921
|
import { dirname as dirname10, join as join16, resolve as resolve15 } from "node:path";
|
|
14656
|
-
import { promises as fs7, existsSync as
|
|
14922
|
+
import { promises as fs7, existsSync as existsSync16 } from "node:fs";
|
|
14657
14923
|
|
|
14658
14924
|
// src/skills-bundle.ts
|
|
14659
14925
|
import { homedir as homedir2 } from "node:os";
|
|
14660
14926
|
import { join as join15 } from "node:path";
|
|
14661
|
-
import { readFileSync as
|
|
14927
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync2 } from "node:fs";
|
|
14662
14928
|
var SKILL_PREFIX = "launchpad-";
|
|
14663
14929
|
function skillsTargetDir() {
|
|
14664
14930
|
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir2(), ".claude", "skills");
|
|
@@ -14677,9 +14943,9 @@ function readInstalledSkills() {
|
|
|
14677
14943
|
return { present: false, version: null };
|
|
14678
14944
|
}
|
|
14679
14945
|
}
|
|
14680
|
-
function readSkillVersion(
|
|
14946
|
+
function readSkillVersion(path15) {
|
|
14681
14947
|
try {
|
|
14682
|
-
const text =
|
|
14948
|
+
const text = readFileSync20(path15, "utf8");
|
|
14683
14949
|
const fenceEnd = text.indexOf(`
|
|
14684
14950
|
---`, 4);
|
|
14685
14951
|
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
@@ -14762,7 +15028,7 @@ function defaultBundleDir() {
|
|
|
14762
15028
|
resolve15(here, "..", "..", "skills")
|
|
14763
15029
|
];
|
|
14764
15030
|
for (const c of candidates) {
|
|
14765
|
-
if (
|
|
15031
|
+
if (existsSync16(join16(c, "launchpad-onboard", "SKILL.md"))) {
|
|
14766
15032
|
return c;
|
|
14767
15033
|
}
|
|
14768
15034
|
}
|
|
@@ -14842,10 +15108,10 @@ async function doList(io) {
|
|
|
14842
15108
|
}
|
|
14843
15109
|
return 0;
|
|
14844
15110
|
}
|
|
14845
|
-
async function readVersion(
|
|
15111
|
+
async function readVersion(path15) {
|
|
14846
15112
|
let text;
|
|
14847
15113
|
try {
|
|
14848
|
-
text = await fs7.readFile(
|
|
15114
|
+
text = await fs7.readFile(path15, "utf8");
|
|
14849
15115
|
} catch {
|
|
14850
15116
|
return null;
|
|
14851
15117
|
}
|
|
@@ -14855,9 +15121,9 @@ async function readVersion(path14) {
|
|
|
14855
15121
|
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
14856
15122
|
return m === null ? null : m[1] ?? null;
|
|
14857
15123
|
}
|
|
14858
|
-
async function isDir(
|
|
15124
|
+
async function isDir(path15) {
|
|
14859
15125
|
try {
|
|
14860
|
-
const stat = await fs7.stat(
|
|
15126
|
+
const stat = await fs7.stat(path15);
|
|
14861
15127
|
return stat.isDirectory();
|
|
14862
15128
|
} catch {
|
|
14863
15129
|
return false;
|
|
@@ -14873,7 +15139,7 @@ import { promisify } from "node:util";
|
|
|
14873
15139
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
14874
15140
|
import { dirname as dirname11, resolve as resolve16, relative as relative6, isAbsolute as isAbsolute3, join as join17 } from "node:path";
|
|
14875
15141
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
14876
|
-
import { readFileSync as
|
|
15142
|
+
import { readFileSync as readFileSync21, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync8, rmSync as rmSync3 } from "node:fs";
|
|
14877
15143
|
|
|
14878
15144
|
// src/commands/channel-auth.ts
|
|
14879
15145
|
import { createServer as createServer2 } from "node:http";
|
|
@@ -15123,17 +15389,17 @@ async function openSystemBrowser(url) {
|
|
|
15123
15389
|
await execFileAsync(opener, [url]);
|
|
15124
15390
|
}
|
|
15125
15391
|
async function runInstallerScript(script) {
|
|
15126
|
-
const dir =
|
|
15392
|
+
const dir = mkdtempSync2(join17(tmpdir(), "launchpad-update-"));
|
|
15127
15393
|
const file = join17(dir, "install.sh");
|
|
15128
15394
|
try {
|
|
15129
|
-
|
|
15395
|
+
writeFileSync8(file, script, { mode: 448 });
|
|
15130
15396
|
return await new Promise((resolvePromise) => {
|
|
15131
15397
|
const child = spawn5("bash", [file], { stdio: "inherit" });
|
|
15132
15398
|
child.on("error", () => resolvePromise(127));
|
|
15133
15399
|
child.on("close", (c) => resolvePromise(c ?? 1));
|
|
15134
15400
|
});
|
|
15135
15401
|
} finally {
|
|
15136
|
-
|
|
15402
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
15137
15403
|
}
|
|
15138
15404
|
}
|
|
15139
15405
|
async function runChannelInstaller() {
|
|
@@ -15148,7 +15414,7 @@ function resolveLatestVersion() {
|
|
|
15148
15414
|
}
|
|
15149
15415
|
function detectInstallChannel() {
|
|
15150
15416
|
try {
|
|
15151
|
-
return
|
|
15417
|
+
return readFileSync21(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
|
|
15152
15418
|
} catch {
|
|
15153
15419
|
return "github";
|
|
15154
15420
|
}
|
|
@@ -15356,7 +15622,7 @@ function printHelp5(io) {
|
|
|
15356
15622
|
}
|
|
15357
15623
|
|
|
15358
15624
|
// src/commands/validate.ts
|
|
15359
|
-
import { existsSync as
|
|
15625
|
+
import { existsSync as existsSync17, readFileSync as readFileSync22, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
15360
15626
|
import { dirname as dirname12, resolve as resolve17 } from "node:path";
|
|
15361
15627
|
var validateCommand = {
|
|
15362
15628
|
name: "validate",
|
|
@@ -15371,14 +15637,14 @@ async function runValidate(args, io) {
|
|
|
15371
15637
|
return 64;
|
|
15372
15638
|
}
|
|
15373
15639
|
const { file, json, strictGroups } = parseResult;
|
|
15374
|
-
const
|
|
15375
|
-
const result = loadManifestAnyVersion(
|
|
15640
|
+
const path15 = resolve17(process.cwd(), file ?? "launchpad.yaml");
|
|
15641
|
+
const result = loadManifestAnyVersion(path15);
|
|
15376
15642
|
if (result.kind !== "ok") {
|
|
15377
15643
|
return json ? renderJsonError(result, io) : renderHumanError(result, io);
|
|
15378
15644
|
}
|
|
15379
15645
|
const unsupported2 = rejectUnsupportedCliRuntime({
|
|
15380
15646
|
command: "validate",
|
|
15381
|
-
manifestPath:
|
|
15647
|
+
manifestPath: path15,
|
|
15382
15648
|
manifest: result.manifest,
|
|
15383
15649
|
io,
|
|
15384
15650
|
json,
|
|
@@ -15387,8 +15653,8 @@ async function runValidate(args, io) {
|
|
|
15387
15653
|
if (unsupported2 !== null)
|
|
15388
15654
|
return unsupported2;
|
|
15389
15655
|
const view = manifestView(result.manifest);
|
|
15390
|
-
const boundary = checkBoundary(
|
|
15391
|
-
const serveErrors = checkStaticServeDir(
|
|
15656
|
+
const boundary = checkBoundary(path15, view.appDeclared);
|
|
15657
|
+
const serveErrors = checkStaticServeDir(path15, result.manifest);
|
|
15392
15658
|
if (serveErrors.length > 0) {
|
|
15393
15659
|
boundary.errors = [...boundary.errors, ...serveErrors];
|
|
15394
15660
|
}
|
|
@@ -15405,7 +15671,7 @@ function checkBoundary(manifestPath, declared) {
|
|
|
15405
15671
|
}
|
|
15406
15672
|
let manifestYaml;
|
|
15407
15673
|
try {
|
|
15408
|
-
manifestYaml =
|
|
15674
|
+
manifestYaml = readFileSync22(manifestPath, "utf8");
|
|
15409
15675
|
} catch {
|
|
15410
15676
|
manifestYaml = null;
|
|
15411
15677
|
}
|
|
@@ -15431,7 +15697,7 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
15431
15697
|
}
|
|
15432
15698
|
const dir = dirname12(manifestPath);
|
|
15433
15699
|
const abs = resolve17(dir, serveDir);
|
|
15434
|
-
if (!
|
|
15700
|
+
if (!existsSync17(abs)) {
|
|
15435
15701
|
return [
|
|
15436
15702
|
`static app serves '${serveDir}/' but that directory does not exist — nothing would be served. ` + `Create ${serveDir}/index.html (or run \`launchpad init\` to scaffold it).`
|
|
15437
15703
|
];
|
|
@@ -15456,7 +15722,7 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
15456
15722
|
`static app's served directory '${serveDir}/' is empty — the deploy would serve an empty site.`
|
|
15457
15723
|
];
|
|
15458
15724
|
}
|
|
15459
|
-
if (!
|
|
15725
|
+
if (!existsSync17(resolve17(abs, "index.html"))) {
|
|
15460
15726
|
return [
|
|
15461
15727
|
`static app's served directory '${serveDir}/' has no index.html — the deploy would serve a directory with no entrypoint.`
|
|
15462
15728
|
];
|
|
@@ -16433,10 +16699,10 @@ import { spawn as spawn6 } from "node:child_process";
|
|
|
16433
16699
|
import { homedir as homedir4 } from "node:os";
|
|
16434
16700
|
import { join as join18 } from "node:path";
|
|
16435
16701
|
import {
|
|
16436
|
-
existsSync as
|
|
16437
|
-
mkdirSync as
|
|
16438
|
-
readFileSync as
|
|
16439
|
-
writeFileSync as
|
|
16702
|
+
existsSync as existsSync18,
|
|
16703
|
+
mkdirSync as mkdirSync5,
|
|
16704
|
+
readFileSync as readFileSync23,
|
|
16705
|
+
writeFileSync as writeFileSync9
|
|
16440
16706
|
} from "node:fs";
|
|
16441
16707
|
var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
16442
16708
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -16444,7 +16710,7 @@ var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
|
|
|
16444
16710
|
var CACHE_FILE = join18(homedir4(), ".launchpad", "update-check.json");
|
|
16445
16711
|
function readCache2() {
|
|
16446
16712
|
try {
|
|
16447
|
-
const raw = JSON.parse(
|
|
16713
|
+
const raw = JSON.parse(readFileSync23(CACHE_FILE, "utf8"));
|
|
16448
16714
|
if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
|
|
16449
16715
|
const latest = raw.latest;
|
|
16450
16716
|
return {
|
|
@@ -16457,23 +16723,23 @@ function readCache2() {
|
|
|
16457
16723
|
}
|
|
16458
16724
|
function writeCache2(state) {
|
|
16459
16725
|
try {
|
|
16460
|
-
|
|
16461
|
-
|
|
16726
|
+
mkdirSync5(join18(homedir4(), ".launchpad"), { recursive: true });
|
|
16727
|
+
writeFileSync9(CACHE_FILE, `${JSON.stringify(state)}
|
|
16462
16728
|
`, { mode: 384 });
|
|
16463
16729
|
} catch {}
|
|
16464
16730
|
}
|
|
16465
16731
|
var SKILLS_HINT_MARKER = join18(homedir4(), ".launchpad", "skills-hint-shown");
|
|
16466
16732
|
function absentHintShown() {
|
|
16467
16733
|
try {
|
|
16468
|
-
return
|
|
16734
|
+
return existsSync18(SKILLS_HINT_MARKER);
|
|
16469
16735
|
} catch {
|
|
16470
16736
|
return false;
|
|
16471
16737
|
}
|
|
16472
16738
|
}
|
|
16473
16739
|
function markAbsentHintShown() {
|
|
16474
16740
|
try {
|
|
16475
|
-
|
|
16476
|
-
|
|
16741
|
+
mkdirSync5(join18(homedir4(), ".launchpad"), { recursive: true });
|
|
16742
|
+
writeFileSync9(SKILLS_HINT_MARKER, `${Date.now()}
|
|
16477
16743
|
`, { mode: 384 });
|
|
16478
16744
|
} catch {}
|
|
16479
16745
|
}
|
|
@@ -16576,7 +16842,7 @@ var refreshUpdateCacheCommand = {
|
|
|
16576
16842
|
import { spawn as spawn7 } from "node:child_process";
|
|
16577
16843
|
import { homedir as homedir5 } from "node:os";
|
|
16578
16844
|
import { join as join19 } from "node:path";
|
|
16579
|
-
import { existsSync as
|
|
16845
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync6, readFileSync as readFileSync24, writeFileSync as writeFileSync10 } from "node:fs";
|
|
16580
16846
|
import { randomUUID } from "node:crypto";
|
|
16581
16847
|
var INTERNAL_EMIT_VERB = "__emit-telemetry";
|
|
16582
16848
|
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -16652,15 +16918,15 @@ async function resolveIdentityReal() {
|
|
|
16652
16918
|
}
|
|
16653
16919
|
function getOrCreateDeviceId() {
|
|
16654
16920
|
try {
|
|
16655
|
-
const raw = JSON.parse(
|
|
16921
|
+
const raw = JSON.parse(readFileSync24(DEVICE_ID_FILE, "utf8"));
|
|
16656
16922
|
const id2 = raw?.deviceId;
|
|
16657
16923
|
if (typeof id2 === "string" && id2)
|
|
16658
16924
|
return id2;
|
|
16659
16925
|
} catch {}
|
|
16660
16926
|
const id = randomUUID();
|
|
16661
16927
|
try {
|
|
16662
|
-
|
|
16663
|
-
|
|
16928
|
+
mkdirSync6(join19(homedir5(), ".launchpad"), { recursive: true });
|
|
16929
|
+
writeFileSync10(DEVICE_ID_FILE, `${JSON.stringify({ deviceId: id })}
|
|
16664
16930
|
`, {
|
|
16665
16931
|
mode: 384
|
|
16666
16932
|
});
|
|
@@ -16730,15 +16996,15 @@ function telemetryNoticeLine() {
|
|
|
16730
16996
|
}
|
|
16731
16997
|
function firstRunNoticeShown() {
|
|
16732
16998
|
try {
|
|
16733
|
-
return
|
|
16999
|
+
return existsSync19(FIRST_RUN_MARKER);
|
|
16734
17000
|
} catch {
|
|
16735
17001
|
return false;
|
|
16736
17002
|
}
|
|
16737
17003
|
}
|
|
16738
17004
|
function markFirstRunNotice() {
|
|
16739
17005
|
try {
|
|
16740
|
-
|
|
16741
|
-
|
|
17006
|
+
mkdirSync6(join19(homedir5(), ".launchpad"), { recursive: true });
|
|
17007
|
+
writeFileSync10(FIRST_RUN_MARKER, `${Date.now()}
|
|
16742
17008
|
`, { mode: 384 });
|
|
16743
17009
|
} catch {}
|
|
16744
17010
|
}
|
|
@@ -16803,7 +17069,7 @@ import { createInterface as createInterface7 } from "node:readline/promises";
|
|
|
16803
17069
|
import { platform } from "node:os";
|
|
16804
17070
|
|
|
16805
17071
|
// src/report/breadcrumb.ts
|
|
16806
|
-
import { writeFileSync as
|
|
17072
|
+
import { writeFileSync as writeFileSync11, readFileSync as readFileSync25, mkdirSync as mkdirSync7 } from "node:fs";
|
|
16807
17073
|
import { homedir as homedir6 } from "node:os";
|
|
16808
17074
|
import { join as join20, dirname as dirname13 } from "node:path";
|
|
16809
17075
|
function breadcrumbPath() {
|
|
@@ -16812,13 +17078,13 @@ function breadcrumbPath() {
|
|
|
16812
17078
|
function writeBreadcrumb(verb, exit) {
|
|
16813
17079
|
try {
|
|
16814
17080
|
const p = breadcrumbPath();
|
|
16815
|
-
|
|
16816
|
-
|
|
17081
|
+
mkdirSync7(dirname13(p), { recursive: true });
|
|
17082
|
+
writeFileSync11(p, JSON.stringify({ verb, exit, ts: Date.now() }), "utf8");
|
|
16817
17083
|
} catch {}
|
|
16818
17084
|
}
|
|
16819
17085
|
function readBreadcrumb() {
|
|
16820
17086
|
try {
|
|
16821
|
-
const raw = JSON.parse(
|
|
17087
|
+
const raw = JSON.parse(readFileSync25(breadcrumbPath(), "utf8"));
|
|
16822
17088
|
if (typeof raw !== "object" || raw === null)
|
|
16823
17089
|
return null;
|
|
16824
17090
|
const r = raw;
|
|
@@ -16987,6 +17253,7 @@ var COMMANDS = [
|
|
|
16987
17253
|
createCommand,
|
|
16988
17254
|
cloneCommand,
|
|
16989
17255
|
deployCommand,
|
|
17256
|
+
migrateCommand,
|
|
16990
17257
|
redeployCommand,
|
|
16991
17258
|
reviewCommand,
|
|
16992
17259
|
mergeCommand,
|