@mutmutco/cli 4.3.39 → 4.3.41
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/README.md +6 -4
- package/dist/main.cjs +72 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,21 +6,23 @@ This package is published from [mutmutco/MMI-Hub](https://github.com/mutmutco/MM
|
|
|
6
6
|
|
|
7
7
|
The CLI carries the org **Hub endpoint** intrinsically (override with the `MMI_HUB_URL` env var), so a product repo needs **no committed control-plane config** to reach the Hub — board coords, deploy coordinates, OAuth, and the secrets layout are all discovered from the Hub registry at runtime.
|
|
8
8
|
|
|
9
|
+
Local stage recipes and printed plans use Bash on every platform. Windows requires Git Bash in its standard system or user installation, or `SHELL` set to the absolute `bash.exe` path. A missing Bash installation fails explicitly. Native Windows administration helpers remain independent of the stage shell.
|
|
10
|
+
|
|
9
11
|
## Install
|
|
10
12
|
|
|
11
|
-
```
|
|
13
|
+
```bash
|
|
12
14
|
npm install -g @mutmutco/cli
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
Authenticate GitHub once for Hub session issuance and Project board operations:
|
|
16
18
|
|
|
17
|
-
```
|
|
19
|
+
```bash
|
|
18
20
|
gh auth login --hostname github.com --git-protocol https --web --scopes "project"
|
|
19
21
|
```
|
|
20
22
|
|
|
21
23
|
Then verify the installed command:
|
|
22
24
|
|
|
23
|
-
```
|
|
25
|
+
```bash
|
|
24
26
|
mmi-cli --version
|
|
25
27
|
mmi-cli doctor --json
|
|
26
28
|
```
|
|
@@ -79,7 +81,7 @@ they don't get re-derived or re-litigated per session:
|
|
|
79
81
|
|
|
80
82
|
When working inside an `MMI-Hub` checkout before npm is available, use the committed bundle directly:
|
|
81
83
|
|
|
82
|
-
```
|
|
84
|
+
```bash
|
|
83
85
|
node cli/dist/index.cjs --version
|
|
84
86
|
node cli/dist/index.cjs doctor --json
|
|
85
87
|
```
|
package/dist/main.cjs
CHANGED
|
@@ -12198,10 +12198,9 @@ async function postIssueComment(client, input) {
|
|
|
12198
12198
|
|
|
12199
12199
|
// src/board-claim-move.ts
|
|
12200
12200
|
var CLAIM_CONCURRENCY = 5;
|
|
12201
|
-
function
|
|
12202
|
-
if (platform2 !== "win32") return message2;
|
|
12201
|
+
function withGatedWriteChainHint(message2) {
|
|
12203
12202
|
return `${message2}
|
|
12204
|
-
note:
|
|
12203
|
+
note: run refused writes separately or join dependent commands with '&&'; a ';' chain continues after failure`;
|
|
12205
12204
|
}
|
|
12206
12205
|
function isArchivedItemRefusal(message2) {
|
|
12207
12206
|
return /The item is archived and cannot be updated/i.test(message2);
|
|
@@ -12387,7 +12386,7 @@ async function claimOneBoardItem(ctx, selector, options) {
|
|
|
12387
12386
|
}
|
|
12388
12387
|
if (!options.force) {
|
|
12389
12388
|
const refusal = laneContestMessage(item.ref, contest, "claim");
|
|
12390
|
-
throw new Error(options.bulk ? refusal :
|
|
12389
|
+
throw new Error(options.bulk ? refusal : withGatedWriteChainHint(refusal));
|
|
12391
12390
|
}
|
|
12392
12391
|
previousHolder = displaced();
|
|
12393
12392
|
};
|
|
@@ -12571,7 +12570,7 @@ async function unclaimBoardIssue(options, deps = {}) {
|
|
|
12571
12570
|
const toStatus = options.toStatus ?? "Todo";
|
|
12572
12571
|
if (!options.force && item.contentType === "Issue") {
|
|
12573
12572
|
const contest = await checkLaneContest(client, item);
|
|
12574
|
-
if (contest.contested) throw new Error(
|
|
12573
|
+
if (contest.contested) throw new Error(withGatedWriteChainHint(laneContestMessage(item.ref, contest, "unclaim")));
|
|
12575
12574
|
}
|
|
12576
12575
|
try {
|
|
12577
12576
|
await client.rest("DELETE", `repos/${item.repository}/issues/${item.number}/assignees`, {
|
|
@@ -14100,7 +14099,7 @@ function expectedHosts(cfg) {
|
|
|
14100
14099
|
if (cfg.fofuSubdomain !== void 0) {
|
|
14101
14100
|
out.push(cfg.fofuSubdomain ? `${cfg.fofuSubdomain}.fofu.ai` : "fofu.ai");
|
|
14102
14101
|
}
|
|
14103
|
-
return uniq(out);
|
|
14102
|
+
return uniq([...out, ...cfg.edgeHosts ?? []]);
|
|
14104
14103
|
}
|
|
14105
14104
|
function expectedJsOrigins(cfg) {
|
|
14106
14105
|
return uniq([...expectedHosts(cfg).map((h) => `https://${h}`), ...LOOPBACK]);
|
|
@@ -14167,7 +14166,7 @@ function parseOauthConfig(mmiConfig, slug) {
|
|
|
14167
14166
|
const meta = mmiConfig ?? {};
|
|
14168
14167
|
const rawFofuSub = raw.fofuSubdomain;
|
|
14169
14168
|
const fofuSubdomain = meta.fofuEnabled === true ? typeof rawFofuSub === "string" ? rawFofuSub : defaultSubdomain(slug) : void 0;
|
|
14170
|
-
return { subdomains, domains, callbackPath, extraCallbackPaths, fofuSubdomain };
|
|
14169
|
+
return { subdomains, domains, callbackPath, extraCallbackPaths, fofuSubdomain, edgeHosts: Object.values(edgeDomainsByStage(mmiConfig)) };
|
|
14171
14170
|
}
|
|
14172
14171
|
function probeRedirectUri(callbackPath, port = 9123) {
|
|
14173
14172
|
return `http://localhost:${port}${callbackPath}`;
|
|
@@ -14186,6 +14185,20 @@ function buildAuthorizeProbeUrl(clientId, redirectUri) {
|
|
|
14186
14185
|
function authorizeBodyHasMismatch(body) {
|
|
14187
14186
|
return /redirect_uri_mismatch/i.test(body);
|
|
14188
14187
|
}
|
|
14188
|
+
async function probeOauthRedirects(cfg, clientId, request = fetch) {
|
|
14189
|
+
const uris = uniq([probeRedirectUri(cfg.callbackPath), ...expectedRedirectUris(cfg)]);
|
|
14190
|
+
const signal = AbortSignal.timeout(1e4);
|
|
14191
|
+
const results = [];
|
|
14192
|
+
for (let i = 0; i < uris.length; i += 4) {
|
|
14193
|
+
results.push(...await Promise.all(uris.slice(i, i + 4).map(async (redirectUri) => {
|
|
14194
|
+
const response = await request(buildAuthorizeProbeUrl(clientId, redirectUri), { redirect: "follow", signal });
|
|
14195
|
+
const mismatch = authorizeBodyHasMismatch(await response.text());
|
|
14196
|
+
if (!mismatch && !response.ok) throw new Error(`authorize probe returned HTTP ${response.status}`);
|
|
14197
|
+
return { redirectUri, mismatch };
|
|
14198
|
+
})));
|
|
14199
|
+
}
|
|
14200
|
+
return results;
|
|
14201
|
+
}
|
|
14189
14202
|
|
|
14190
14203
|
// src/project-runtime.ts
|
|
14191
14204
|
function hasRuntimeSecretContract(contract) {
|
|
@@ -15695,10 +15708,10 @@ var rollout_plan_default = {
|
|
|
15695
15708
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
15696
15709
|
},
|
|
15697
15710
|
baseline: {
|
|
15698
|
-
version: "4.3.
|
|
15699
|
-
tag: "v4.3.
|
|
15700
|
-
commit: "
|
|
15701
|
-
npm: "@mutmutco/cli@4.3.
|
|
15711
|
+
version: "4.3.41",
|
|
15712
|
+
tag: "v4.3.41",
|
|
15713
|
+
commit: "02cc438cfbf4",
|
|
15714
|
+
npm: "@mutmutco/cli@4.3.41"
|
|
15702
15715
|
},
|
|
15703
15716
|
exitCriterion: "fleet-n-of-n",
|
|
15704
15717
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15715,14 +15728,14 @@ var rollout_plan_default = {
|
|
|
15715
15728
|
repo: "mutmutco/mmi-hub",
|
|
15716
15729
|
role: "canary",
|
|
15717
15730
|
schedule: "train",
|
|
15718
|
-
v3Target: "v4.3.
|
|
15731
|
+
v3Target: "v4.3.41"
|
|
15719
15732
|
}
|
|
15720
15733
|
],
|
|
15721
15734
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
15722
15735
|
rollback: {
|
|
15723
15736
|
independent: true,
|
|
15724
|
-
mechanism: "npm dist-tag latest -> 4.3.
|
|
15725
|
-
v3Target: "v4.3.
|
|
15737
|
+
mechanism: "npm dist-tag latest -> 4.3.41 and redeploy the Hub Lambda from tag v4.3.41 (02cc438cfbf4); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15738
|
+
v3Target: "v4.3.41 (@mutmutco/cli@4.3.41, tag commit 02cc438cfbf4 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15726
15739
|
}
|
|
15727
15740
|
},
|
|
15728
15741
|
{
|
|
@@ -25957,7 +25970,7 @@ function registerBoardCommands(program3) {
|
|
|
25957
25970
|
return `Claimed ${ref} for ${holder} - In Progress${reclaimed}`;
|
|
25958
25971
|
}
|
|
25959
25972
|
const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
|
|
25960
|
-
board.command("read", { isDefault: true }).alias("list").description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--out <path>", "write the output to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802)").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\
|
|
25973
|
+
board.command("read", { isDefault: true }).alias("list").description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--direct", "bypass the Hub snapshot and read the live board through direct GitHub GraphQL").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--out <path>", "write the output to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802)").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n").action((o) => runBoardRead(o));
|
|
25961
25974
|
withExamples(mutating(
|
|
25962
25975
|
board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs); the board scan rides the Hub snapshot, same as board read").addHelpText("after", "\nclaim reads the board through the same Hub snapshot leg as `board read` (the App-installation\ncredential, never your personal GraphQL pool); the direct user-auth read is an emergency fallback\nand is named in a Warning line after the verdict (#6162).\n\nevery claim stamps a lane-identity marker comment on the issue (`<!-- mmi-claim: \u2026 -->`,\nsurface/session@host) so other agents can attribute the hold (#3727). The session is the\nhost-exported id when the surface provides one, otherwise a per-process `synth-` fallback \u2014\na claim is never anonymous (#5245). `board show`, doctor and unclaim read the latest marker.\n\nsame-owner resume (#6035): when the prior marker was posted by YOUR login on THIS host and its\nlocal session is verifiably dead (transcript probe), the claim proceeds as a resume without\n--force and names the evidence. A live, foreign-host, or unprobeable prior lane still refuses.\n\nreclaim from In Review (#6339): an In Review item with no holder, or one held by the claiming\nlogin, is claimable \u2014 it moves back to In Progress and the receipt names the status it came from.\nAn In Review item another login holds is refused (ask the holder, or wait for the review to land),\nand Done stays refused. The board status is the authority; no PR state is consulted.\n").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
|
|
25963
25976
|
(_opts, args) => ({ command: "oracle board claim", issues: args[0] ?? [] })
|
|
@@ -30097,6 +30110,18 @@ function stageComposeFileEnv(files) {
|
|
|
30097
30110
|
// src/stage-runner.ts
|
|
30098
30111
|
var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process11.execFile);
|
|
30099
30112
|
var DOCKER_TIMEOUT_MS = 15e3;
|
|
30113
|
+
function stageBash() {
|
|
30114
|
+
if (process.platform !== "win32") return "bash";
|
|
30115
|
+
const configured = process.env.SHELL;
|
|
30116
|
+
if (configured && (0, import_node_path27.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs28.existsSync)(configured)) return configured;
|
|
30117
|
+
const candidates = [
|
|
30118
|
+
(0, import_node_path27.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
30119
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path27.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
30120
|
+
];
|
|
30121
|
+
const bash = candidates.find((path2) => (0, import_node_fs28.existsSync)(path2));
|
|
30122
|
+
if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
|
|
30123
|
+
return bash;
|
|
30124
|
+
}
|
|
30100
30125
|
var EARLY_EXIT_GRACE_MS = 2e3;
|
|
30101
30126
|
function earlyExitGraceMs() {
|
|
30102
30127
|
if (process.env.NODE_ENV === "test") {
|
|
@@ -30287,24 +30312,6 @@ function mergeEnvSecretsIntoFile(content, secrets) {
|
|
|
30287
30312
|
return body.endsWith("\n") ? body : `${body}
|
|
30288
30313
|
`;
|
|
30289
30314
|
}
|
|
30290
|
-
var POSIX_ONLY_VERBS = ["cp", "mv", "rm", "ln", "cat", "touch", "chmod", "export"];
|
|
30291
|
-
function posixOnlyShellProblems(command, field, platform2 = process.platform) {
|
|
30292
|
-
if (platform2 !== "win32" || !command?.trim()) return [];
|
|
30293
|
-
const problems = [];
|
|
30294
|
-
if (/(^|&&|\||;)\s*[A-Za-z_][A-Za-z0-9_]*=\S/.test(command)) {
|
|
30295
|
-
problems.push(
|
|
30296
|
-
`stage.${field} uses POSIX inline env assignment (VAR=value command) which fails in cmd.exe on Windows; use 'set VAR=value && command' or a cross-platform launcher`
|
|
30297
|
-
);
|
|
30298
|
-
}
|
|
30299
|
-
for (const verb of POSIX_ONLY_VERBS) {
|
|
30300
|
-
if (new RegExp(`(^|&&|\\||;|\\()\\s*${verb}\\b`).test(command)) {
|
|
30301
|
-
problems.push(
|
|
30302
|
-
`stage.${field} calls POSIX '${verb}' which does not exist in cmd.exe on Windows; use the cmd/PowerShell equivalent or a cross-platform script`
|
|
30303
|
-
);
|
|
30304
|
-
}
|
|
30305
|
-
}
|
|
30306
|
-
return problems;
|
|
30307
|
-
}
|
|
30308
30315
|
function validateStageConfig(config = {}, action) {
|
|
30309
30316
|
const problems = [];
|
|
30310
30317
|
if (action === "run" && !config.build?.trim() && !config.ensureEnv) problems.push("stage.build is required for stage run");
|
|
@@ -30312,8 +30319,6 @@ function validateStageConfig(config = {}, action) {
|
|
|
30312
30319
|
if (config.healthUrl != null && config.healthUrl.trim() && !/^https?:\/\//.test(config.healthUrl.trim())) {
|
|
30313
30320
|
problems.push("stage.healthUrl must be an http(s) URL");
|
|
30314
30321
|
}
|
|
30315
|
-
if (action === "run") problems.push(...posixOnlyShellProblems(config.build, "build"));
|
|
30316
|
-
problems.push(...posixOnlyShellProblems(config.up, "up"));
|
|
30317
30322
|
if (config.portRange != null) {
|
|
30318
30323
|
const r = config.portRange;
|
|
30319
30324
|
const ok = Array.isArray(r) && r.length === 2 && r.every((n) => Number.isInteger(n) && n >= 1024 && n <= 65535) && r[0] <= r[1];
|
|
@@ -30338,14 +30343,27 @@ function isPortFree(port) {
|
|
|
30338
30343
|
});
|
|
30339
30344
|
}
|
|
30340
30345
|
async function shell(command, cwd, timeoutMs, env) {
|
|
30341
|
-
|
|
30346
|
+
const execution = execFileP3(stageBash(), ["-c", command], {
|
|
30342
30347
|
cwd,
|
|
30343
|
-
|
|
30344
|
-
timeout: timeoutMs,
|
|
30348
|
+
timeout: process.platform === "win32" ? timeoutMs + 5e3 : timeoutMs,
|
|
30345
30349
|
windowsHide: true,
|
|
30346
30350
|
maxBuffer: 1024 * 1024 * 4,
|
|
30347
30351
|
...env ? { env: { ...process.env, ...env } } : {}
|
|
30348
30352
|
});
|
|
30353
|
+
let cleanup;
|
|
30354
|
+
const timer = process.platform === "win32" ? setTimeout(() => {
|
|
30355
|
+
cleanup = killTree(execution.child.pid ?? 0);
|
|
30356
|
+
}, timeoutMs) : void 0;
|
|
30357
|
+
try {
|
|
30358
|
+
await execution;
|
|
30359
|
+
if (cleanup) throw new Error(`stage command timed out after ${timeoutMs}ms`);
|
|
30360
|
+
} catch (error) {
|
|
30361
|
+
if (cleanup) throw new Error(`stage command timed out after ${timeoutMs}ms`, { cause: error });
|
|
30362
|
+
throw error;
|
|
30363
|
+
} finally {
|
|
30364
|
+
clearTimeout(timer);
|
|
30365
|
+
await cleanup;
|
|
30366
|
+
}
|
|
30349
30367
|
}
|
|
30350
30368
|
async function listDockerContainers() {
|
|
30351
30369
|
const { stdout } = await execFileP3("docker", ["container", "ls", "--format", "{{json .}}"], {
|
|
@@ -30585,7 +30603,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
30585
30603
|
async function killTree(pid) {
|
|
30586
30604
|
if (!Number.isInteger(pid) || pid <= 0) return;
|
|
30587
30605
|
if (process.platform === "win32") {
|
|
30588
|
-
await execFileP3("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true }).catch(() => void 0);
|
|
30606
|
+
await execFileP3("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true, timeout: 5e3 }).catch(() => void 0);
|
|
30589
30607
|
return;
|
|
30590
30608
|
}
|
|
30591
30609
|
try {
|
|
@@ -30651,6 +30669,7 @@ async function stopStage(opts = {}) {
|
|
|
30651
30669
|
};
|
|
30652
30670
|
}
|
|
30653
30671
|
async function startStage(config = {}, opts = {}) {
|
|
30672
|
+
const bash = stageBash();
|
|
30654
30673
|
const problems = validateStageConfig(config, "start");
|
|
30655
30674
|
if (problems.length) throw new Error(problems.join("; "));
|
|
30656
30675
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -30672,9 +30691,8 @@ async function startStage(config = {}, opts = {}) {
|
|
|
30672
30691
|
let up = sub(config.up.trim());
|
|
30673
30692
|
if (opts.forceRecreate) up = appendForceRecreate(up);
|
|
30674
30693
|
const identity = await resolveStageIdentity(cwd);
|
|
30675
|
-
const child2 = (0, import_node_child_process11.spawn)(up, {
|
|
30694
|
+
const child2 = (0, import_node_child_process11.spawn)(bash, ["-c", up], {
|
|
30676
30695
|
cwd,
|
|
30677
|
-
shell: true,
|
|
30678
30696
|
// POSIX-only: the process group exists for the group-kill in stopStage. On win32 teardown is
|
|
30679
30697
|
// `taskkill /T /F` (no group needed), and detached+shell defeats windowsHide — every spawn would
|
|
30680
30698
|
// flash a Windows Terminal window (0x800700e8) on dev machines.
|
|
@@ -30728,6 +30746,7 @@ function stageEnvRefusal(cwd) {
|
|
|
30728
30746
|
return `stage refuses to run: .env file(s) present in the repo (${envFiles.join(", ")}) \u2014 secrets belong in the Hub vault; delete the file(s) and re-run (#5908)`;
|
|
30729
30747
|
}
|
|
30730
30748
|
async function runStage(config = {}, opts = {}) {
|
|
30749
|
+
stageBash();
|
|
30731
30750
|
const problems = validateStageConfig(config, "run");
|
|
30732
30751
|
if (problems.length) throw new Error(problems.join("; "));
|
|
30733
30752
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -36038,8 +36057,8 @@ var import_node_fs34 = require("node:fs");
|
|
|
36038
36057
|
var import_node_path31 = require("node:path");
|
|
36039
36058
|
|
|
36040
36059
|
// src/stage-default.ts
|
|
36041
|
-
function shellFor(
|
|
36042
|
-
return
|
|
36060
|
+
function shellFor() {
|
|
36061
|
+
return "bash";
|
|
36043
36062
|
}
|
|
36044
36063
|
function isCentralContainerModel(model) {
|
|
36045
36064
|
return model === "tenant-container" || model === "solo-container";
|
|
@@ -36510,7 +36529,7 @@ function registerStageCommands(program3) {
|
|
|
36510
36529
|
fail(`stage stop: ${e.message}`);
|
|
36511
36530
|
}
|
|
36512
36531
|
});
|
|
36513
|
-
stage.command("start").description("start the configured local stage process and optionally wait for health").option("--json", "machine-readable output").option("--apply", "start the configured stage.up process").option("--port <port>", "loopback port for this worktree stage (1024..65535)").option("--timeout-ms <ms>", "bounded health timeout", "60000").option("--allow-stale-env", "start despite a stale ensureEnv target file").action(async () => {
|
|
36532
|
+
stage.command("start").description("start the configured local stage process and optionally wait for health").option("--json", "machine-readable output").option("--apply", "start the configured stage.up process in Bash (Git Bash on Windows)").option("--port <port>", "loopback port for this worktree stage (1024..65535)").option("--timeout-ms <ms>", "bounded health timeout", "60000").option("--allow-stale-env", "start despite a stale ensureEnv target file").action(async () => {
|
|
36514
36533
|
const o = { json: rawFlag("--json"), apply: rawFlag("--apply"), timeoutMs: rawValue("--timeout-ms", "60000"), allowStaleEnv: rawFlag("--allow-stale-env") };
|
|
36515
36534
|
const { resolution: res, project: project2, cfg: stageCfg } = await resolveStage();
|
|
36516
36535
|
if (!o.apply) {
|
|
@@ -45554,22 +45573,22 @@ oauth.command("verify").description("probe Google authorize with an arbitrary po
|
|
|
45554
45573
|
return failGraceful("org oauth verify: no client_id (pass --client-id, or provision the repo so GOOGLE_CLIENT_ID exists)");
|
|
45555
45574
|
}
|
|
45556
45575
|
const redirectUri = probeRedirectUri(oc.callbackPath);
|
|
45557
|
-
let
|
|
45576
|
+
let probes;
|
|
45558
45577
|
try {
|
|
45559
|
-
|
|
45560
|
-
body = await res.text();
|
|
45578
|
+
probes = await probeOauthRedirects(oc, clientId);
|
|
45561
45579
|
} catch (e) {
|
|
45562
45580
|
return failGraceful(`org oauth verify: probe request failed: ${e.message}`);
|
|
45563
45581
|
}
|
|
45564
|
-
const
|
|
45582
|
+
const portAgnostic = !probes[0].mismatch;
|
|
45583
|
+
const mismatches = probes.filter((probe) => probe.mismatch);
|
|
45565
45584
|
if (o.json) {
|
|
45566
|
-
console.log(JSON.stringify({ slug, redirectUri, portAgnostic
|
|
45567
|
-
} else if (
|
|
45568
|
-
console.error(`FAIL ${slug}: redirect_uri_mismatch for ${redirectUri} \u2014
|
|
45585
|
+
console.log(JSON.stringify({ slug, redirectUri, portAgnostic, probes }));
|
|
45586
|
+
} else if (mismatches.length) {
|
|
45587
|
+
for (const probe of mismatches) console.error(`FAIL ${slug}: redirect_uri_mismatch for ${probe.redirectUri} \u2014 run /oauth-provision`);
|
|
45569
45588
|
} else {
|
|
45570
|
-
console.log(`PASS ${slug}: ${
|
|
45589
|
+
console.log(`PASS ${slug}: no redirect_uri_mismatch across ${probes.length} callbacks, including the arbitrary-port loopback`);
|
|
45571
45590
|
}
|
|
45572
|
-
if (
|
|
45591
|
+
if (mismatches.length) process.exitCode = 1;
|
|
45573
45592
|
});
|
|
45574
45593
|
registerCollaborationCommands(program2);
|
|
45575
45594
|
function ciAuditDeps2() {
|
package/package.json
CHANGED