@m8t-stack/cli 0.2.105 → 0.2.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +842 -346
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1454,7 +1454,7 @@ function installFoundryDnsShim() {
|
|
|
1454
1454
|
}
|
|
1455
1455
|
|
|
1456
1456
|
// src/lib/package-version.ts
|
|
1457
|
-
var CLI_VERSION = "0.2.
|
|
1457
|
+
var CLI_VERSION = "0.2.107";
|
|
1458
1458
|
|
|
1459
1459
|
// src/lib/render-error.ts
|
|
1460
1460
|
init_errors();
|
|
@@ -20286,6 +20286,59 @@ async function makeSharedKeyClient(opts) {
|
|
|
20286
20286
|
if (!key2) return null;
|
|
20287
20287
|
return new TableClient(opts.tableEndpoint, TABLE, new AzureNamedKeyCredential(opts.accountName, key2));
|
|
20288
20288
|
}
|
|
20289
|
+
async function makeSharedKeyTransactionClient(opts) {
|
|
20290
|
+
return await makeSharedKeyClient(opts);
|
|
20291
|
+
}
|
|
20292
|
+
async function openMetadataTransactionClient(opts) {
|
|
20293
|
+
const discover = opts.discoverImpl ?? discoverStampStorage;
|
|
20294
|
+
const makeAad = opts.aadClientImpl ?? ((endpoint, credential2) => new TableClient(endpoint, TABLE, credential2));
|
|
20295
|
+
const makeShared = opts.sharedClientImpl ?? makeSharedKeyTransactionClient;
|
|
20296
|
+
const nap = opts.sleepImpl ?? sleep2;
|
|
20297
|
+
const storage = await discover({ credential: opts.credential, subscriptionId: opts.subscriptionId, resourceGroup: opts.resourceGroup });
|
|
20298
|
+
const probe = async (client) => {
|
|
20299
|
+
try {
|
|
20300
|
+
await client.getEntity("system", "platform");
|
|
20301
|
+
return "ok";
|
|
20302
|
+
} catch (e) {
|
|
20303
|
+
if (is404(e)) return "ok";
|
|
20304
|
+
if (is403(e)) return "forbidden";
|
|
20305
|
+
throw e;
|
|
20306
|
+
}
|
|
20307
|
+
};
|
|
20308
|
+
const aad = makeAad(storage.tableEndpoint, opts.credential);
|
|
20309
|
+
if (await probe(aad) === "ok") return aad;
|
|
20310
|
+
opts.onProgress?.(`opening the ${opts.label} with the account key \u2014 your identity lacks the Storage Table data role.`);
|
|
20311
|
+
const shared = await makeShared(storage);
|
|
20312
|
+
if (shared) {
|
|
20313
|
+
if (await probe(shared) === "ok") return shared;
|
|
20314
|
+
}
|
|
20315
|
+
opts.onProgress?.("shared-key access is disabled \u2014 granting Storage Table Data Contributor to your identity (one-time)\u2026");
|
|
20316
|
+
try {
|
|
20317
|
+
const oid = await getCallerObjectId();
|
|
20318
|
+
await grantStorageTableDataContributor({
|
|
20319
|
+
credential: opts.credential,
|
|
20320
|
+
subscriptionId: opts.subscriptionId,
|
|
20321
|
+
scope: storage.accountResourceId,
|
|
20322
|
+
principalId: oid
|
|
20323
|
+
});
|
|
20324
|
+
} catch (e) {
|
|
20325
|
+
throw new LocalCliError({
|
|
20326
|
+
code: opts.errorCode,
|
|
20327
|
+
message: `Cannot open the ${opts.label}: AAD lacks the Storage Table data role, shared-key access is disabled, and self-granting the role failed.`,
|
|
20328
|
+
hint: "Grant yourself 'Storage Table Data Contributor' on the storage account and retry.",
|
|
20329
|
+
cause: e
|
|
20330
|
+
});
|
|
20331
|
+
}
|
|
20332
|
+
for (let i = 0; i < 6; i += 1) {
|
|
20333
|
+
await nap(1e4);
|
|
20334
|
+
if (await probe(aad) === "ok") return aad;
|
|
20335
|
+
}
|
|
20336
|
+
throw new LocalCliError({
|
|
20337
|
+
code: opts.errorCode,
|
|
20338
|
+
message: `Cannot open the ${opts.label}: the granted Storage Table data role has not propagated yet.`,
|
|
20339
|
+
hint: opts.retryHint
|
|
20340
|
+
});
|
|
20341
|
+
}
|
|
20289
20342
|
async function upsertMetadataEntity(opts) {
|
|
20290
20343
|
const discover = opts.discoverImpl ?? discoverStampStorage;
|
|
20291
20344
|
const makeAad = opts.aadClientImpl ?? ((endpoint, credential2) => new TableClient(endpoint, TABLE, credential2));
|
|
@@ -21421,6 +21474,420 @@ function voiceSecretParam(read, gatewayName) {
|
|
|
21421
21474
|
};
|
|
21422
21475
|
}
|
|
21423
21476
|
|
|
21477
|
+
// src/lib/gateway-adoption.ts
|
|
21478
|
+
init_errors();
|
|
21479
|
+
|
|
21480
|
+
// src/lib/apply-request-store.ts
|
|
21481
|
+
import { TableClient as TableClient3 } from "@azure/data-tables";
|
|
21482
|
+
async function openApplyTable(opts) {
|
|
21483
|
+
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
21484
|
+
return new TableClient3(tableEndpoint, "Metadata", opts.credential);
|
|
21485
|
+
}
|
|
21486
|
+
function hasStatus(e, n) {
|
|
21487
|
+
return e.statusCode === n;
|
|
21488
|
+
}
|
|
21489
|
+
async function readApplyRequest(client) {
|
|
21490
|
+
try {
|
|
21491
|
+
const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
|
|
21492
|
+
return { ...entityToApplyRequest(row), etag: row.etag };
|
|
21493
|
+
} catch (e) {
|
|
21494
|
+
if (hasStatus(e, 404)) return null;
|
|
21495
|
+
throw e;
|
|
21496
|
+
}
|
|
21497
|
+
}
|
|
21498
|
+
async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
|
|
21499
|
+
const cur = await readApplyRequest(client);
|
|
21500
|
+
if (!cur) return null;
|
|
21501
|
+
const leaseExpired = isInFlight(cur.status) && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
|
|
21502
|
+
const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
|
|
21503
|
+
if (!claimable) return null;
|
|
21504
|
+
const nowMs = Date.parse(nowIso);
|
|
21505
|
+
const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
|
|
21506
|
+
if (leaseExpired && cur.attempt >= MAX_LEASE_TAKEOVERS) {
|
|
21507
|
+
const held = {
|
|
21508
|
+
...cur,
|
|
21509
|
+
status: "held",
|
|
21510
|
+
breaker: "held",
|
|
21511
|
+
result: {
|
|
21512
|
+
appliedVersion: cur.result?.appliedVersion ?? null,
|
|
21513
|
+
error: `the converge was abandoned mid-apply ${(cur.attempt + 1).toString()} times (last owner ${cur.claimedBy ?? "(unknown)"}, phase "${cur.phase ?? "(none)"}"); the updater stopped retrying to avoid re-rolling infra behind a converge that cannot finish`
|
|
21514
|
+
},
|
|
21515
|
+
updatedAt: nowIso
|
|
21516
|
+
};
|
|
21517
|
+
try {
|
|
21518
|
+
await client.updateEntity({ ...applyRequestToEntity(held), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
21519
|
+
} catch (e) {
|
|
21520
|
+
if (!hasStatus(e, 412)) throw e;
|
|
21521
|
+
}
|
|
21522
|
+
return null;
|
|
21523
|
+
}
|
|
21524
|
+
const claimed = {
|
|
21525
|
+
...cur,
|
|
21526
|
+
status: "claimed",
|
|
21527
|
+
claimedBy: executionId,
|
|
21528
|
+
leaseUntil,
|
|
21529
|
+
...leaseExpired ? { attempt: cur.attempt + 1 } : {},
|
|
21530
|
+
updatedAt: nowIso
|
|
21531
|
+
};
|
|
21532
|
+
try {
|
|
21533
|
+
await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
21534
|
+
return claimed;
|
|
21535
|
+
} catch (e) {
|
|
21536
|
+
if (hasStatus(e, 412)) return null;
|
|
21537
|
+
throw e;
|
|
21538
|
+
}
|
|
21539
|
+
}
|
|
21540
|
+
async function patchApplyRequest(client, mutate) {
|
|
21541
|
+
const cur = await readApplyRequest(client);
|
|
21542
|
+
if (!cur) return;
|
|
21543
|
+
const next = mutate(cur);
|
|
21544
|
+
await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
21545
|
+
}
|
|
21546
|
+
|
|
21547
|
+
// src/lib/gateway-adoption.ts
|
|
21548
|
+
function lowerId(v) {
|
|
21549
|
+
return v.replace(/\/$/, "").toLowerCase();
|
|
21550
|
+
}
|
|
21551
|
+
function recordOfStrings(v) {
|
|
21552
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
21553
|
+
const out = {};
|
|
21554
|
+
for (const [k, value] of Object.entries(v)) if (typeof value === "string") out[k] = value;
|
|
21555
|
+
return out;
|
|
21556
|
+
}
|
|
21557
|
+
function parseLiveGatewayDeployment(raw) {
|
|
21558
|
+
const w = raw ?? {};
|
|
21559
|
+
const containersRaw = w.properties?.template?.containers;
|
|
21560
|
+
const containers = Array.isArray(containersRaw) ? containersRaw : [];
|
|
21561
|
+
const gateway = containers.find((c) => c.name === "gateway");
|
|
21562
|
+
const uas = w.identity?.userAssignedIdentities;
|
|
21563
|
+
const userAssignedIdentityIds = uas && typeof uas === "object" && !Array.isArray(uas) ? Object.keys(uas) : [];
|
|
21564
|
+
const registriesRaw = w.properties?.configuration?.registries;
|
|
21565
|
+
const regs = Array.isArray(registriesRaw) ? registriesRaw : [];
|
|
21566
|
+
return {
|
|
21567
|
+
name: typeof w.name === "string" ? w.name : "",
|
|
21568
|
+
tags: recordOfStrings(w.tags),
|
|
21569
|
+
image: typeof gateway?.image === "string" ? gateway.image : "",
|
|
21570
|
+
userAssignedIdentityIds,
|
|
21571
|
+
registries: regs.map((r) => {
|
|
21572
|
+
const x = r ?? {};
|
|
21573
|
+
return {
|
|
21574
|
+
server: typeof x.server === "string" ? x.server : "",
|
|
21575
|
+
identity: typeof x.identity === "string" ? x.identity : "",
|
|
21576
|
+
username: typeof x.username === "string" ? x.username : "",
|
|
21577
|
+
passwordSecretRef: typeof x.passwordSecretRef === "string" ? x.passwordSecretRef : ""
|
|
21578
|
+
};
|
|
21579
|
+
}),
|
|
21580
|
+
provisioningState: typeof w.properties?.provisioningState === "string" ? w.properties.provisioningState : "",
|
|
21581
|
+
latestRevisionName: typeof w.properties?.latestRevisionName === "string" ? w.properties.latestRevisionName : "",
|
|
21582
|
+
latestReadyRevisionName: typeof w.properties?.latestReadyRevisionName === "string" ? w.properties.latestReadyRevisionName : ""
|
|
21583
|
+
};
|
|
21584
|
+
}
|
|
21585
|
+
function legacyGatewayTopologyError(live, gatewayResourceId, expectedSubscriptionId) {
|
|
21586
|
+
const { subscriptionId, resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
|
|
21587
|
+
if (expectedSubscriptionId && subscriptionId.toLowerCase() !== expectedSubscriptionId.toLowerCase()) return "gateway subscription does not match";
|
|
21588
|
+
const prefix = "m8t-gateway-";
|
|
21589
|
+
if (!name.startsWith(prefix) || name.length === prefix.length || live.name !== name) return "gateway name is not the generated m8t name";
|
|
21590
|
+
const suffix = name.slice(prefix.length);
|
|
21591
|
+
if (live.tags.managedBy !== "m8t" || live.tags.m8t !== "gateway") return "gateway ownership tags do not match";
|
|
21592
|
+
const parsed = parseImageRef2(live.image);
|
|
21593
|
+
const parts = parsed.repo.split("/");
|
|
21594
|
+
const host = parts.shift() ?? "";
|
|
21595
|
+
const repo = parts.join("/");
|
|
21596
|
+
if (!host.endsWith(".azurecr.io") || repo !== "m8t-web" || !parsed.tag || !/^v\d{8}-[0-9a-f]{7,40}$/.test(parsed.tag)) {
|
|
21597
|
+
return "image is not the legacy m8t ACR image shape";
|
|
21598
|
+
}
|
|
21599
|
+
const expectedIdentity = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/m8t-acrpull-${suffix}`;
|
|
21600
|
+
if (live.userAssignedIdentityIds.length !== 1 || lowerId(live.userAssignedIdentityIds[0]) !== lowerId(expectedIdentity)) {
|
|
21601
|
+
return "gateway does not have exactly the generated ACR pull identity";
|
|
21602
|
+
}
|
|
21603
|
+
if (live.registries.length !== 1) return "gateway does not have exactly one registry credential";
|
|
21604
|
+
const registry = live.registries[0];
|
|
21605
|
+
if (registry.server.toLowerCase() !== host.toLowerCase() || lowerId(registry.identity) !== lowerId(live.userAssignedIdentityIds[0]) || registry.username !== "" || registry.passwordSecretRef !== "") {
|
|
21606
|
+
return "registry credential does not match the generated identity and image host";
|
|
21607
|
+
}
|
|
21608
|
+
return null;
|
|
21609
|
+
}
|
|
21610
|
+
async function readLiveGatewayDeployment(gatewayResourceId) {
|
|
21611
|
+
const { subscriptionId, resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
|
|
21612
|
+
let raw;
|
|
21613
|
+
try {
|
|
21614
|
+
raw = await runAz(["containerapp", "show", "--subscription", subscriptionId, "-g", resourceGroup, "-n", name, "-o", "json"]);
|
|
21615
|
+
} catch (e) {
|
|
21616
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_UNREADABLE", message: `Could not read gateway '${name}'.`, cause: e });
|
|
21617
|
+
}
|
|
21618
|
+
try {
|
|
21619
|
+
return parseLiveGatewayDeployment(JSON.parse(raw));
|
|
21620
|
+
} catch (e) {
|
|
21621
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_UNREADABLE", message: `Gateway '${name}' returned invalid JSON.`, cause: e });
|
|
21622
|
+
}
|
|
21623
|
+
}
|
|
21624
|
+
async function resolveLegacyAcrDigest(live, subscriptionId) {
|
|
21625
|
+
const parsed = parseImageRef2(live.image);
|
|
21626
|
+
const [host, ...repoParts] = parsed.repo.split("/");
|
|
21627
|
+
const acrName = host.split(".")[0];
|
|
21628
|
+
const repo = repoParts.join("/");
|
|
21629
|
+
if (!acrName || !repo || !parsed.tag) {
|
|
21630
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The live gateway image is not a tagged ACR image." });
|
|
21631
|
+
}
|
|
21632
|
+
const command = ["acr", "repository", "show"];
|
|
21633
|
+
if (subscriptionId) command.push("--subscription", subscriptionId);
|
|
21634
|
+
command.push("-n", acrName, "--image", `${repo}:${parsed.tag}`, "--query", "digest", "-o", "tsv");
|
|
21635
|
+
const digest = (await runAz(command)).trim();
|
|
21636
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(digest)) {
|
|
21637
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The live ACR image did not resolve to a sha256 digest." });
|
|
21638
|
+
}
|
|
21639
|
+
return digest;
|
|
21640
|
+
}
|
|
21641
|
+
function adoptedAcrTopologyError(live, adoption) {
|
|
21642
|
+
const source = parseImageRef2(adoption.expectedSourceRef);
|
|
21643
|
+
const current = parseImageRef2(live.image);
|
|
21644
|
+
const sourceParts = source.repo.split("/");
|
|
21645
|
+
const currentParts = current.repo.split("/");
|
|
21646
|
+
if (sourceParts[0]?.toLowerCase() !== currentParts[0]?.toLowerCase() || currentParts.slice(1).join("/") !== "m8t-web") {
|
|
21647
|
+
return "live image left the explicitly adopted ACR repository";
|
|
21648
|
+
}
|
|
21649
|
+
if (live.userAssignedIdentityIds.length !== 1 || lowerId(live.userAssignedIdentityIds[0]) !== lowerId(adoption.expectedAcrPullIdentityResourceId)) {
|
|
21650
|
+
return "live pull identity differs from the adopted identity";
|
|
21651
|
+
}
|
|
21652
|
+
if (live.registries.length !== 1) return "live registry credential count differs from the adopted topology";
|
|
21653
|
+
const reg = live.registries[0];
|
|
21654
|
+
if (reg.server.toLowerCase() !== sourceParts[0]?.toLowerCase() || lowerId(reg.identity) !== lowerId(adoption.expectedAcrPullIdentityResourceId)) {
|
|
21655
|
+
return "live registry credential differs from the adopted topology";
|
|
21656
|
+
}
|
|
21657
|
+
return null;
|
|
21658
|
+
}
|
|
21659
|
+
async function readAcrDigest(subscriptionId, acrName, image) {
|
|
21660
|
+
try {
|
|
21661
|
+
const value = (await runAz(["acr", "repository", "show", "--subscription", subscriptionId, "-n", acrName, "--image", image, "--query", "digest", "-o", "tsv"])).trim();
|
|
21662
|
+
return /^sha256:[0-9a-f]{64}$/.test(value) ? value : null;
|
|
21663
|
+
} catch {
|
|
21664
|
+
return null;
|
|
21665
|
+
}
|
|
21666
|
+
}
|
|
21667
|
+
async function rollAdoptedGateway(args) {
|
|
21668
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(args.targetDigest)) {
|
|
21669
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_DIGEST_INVALID", message: "The manifest gateway digest is not a lowercase sha256 digest." });
|
|
21670
|
+
}
|
|
21671
|
+
const { subscriptionId, resourceGroup, name } = parseContainerAppResourceId(args.gatewayResourceId);
|
|
21672
|
+
let live = await readLiveGatewayDeployment(args.gatewayResourceId);
|
|
21673
|
+
const topologyError = adoptedAcrTopologyError(live, args.adoption);
|
|
21674
|
+
if (topologyError) throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_DRIFT", message: `Adopted gateway held: ${topologyError}.` });
|
|
21675
|
+
const acrName = args.adoption.expectedAcrResourceId.split("/").filter(Boolean).at(-1) ?? "";
|
|
21676
|
+
const host = parseImageRef2(args.adoption.expectedSourceRef).repo.split("/")[0];
|
|
21677
|
+
if (!acrName || !host) throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The adoption record lacks a usable ACR identity." });
|
|
21678
|
+
if (args.adoption.status === "pending") {
|
|
21679
|
+
if (args.targetDigest !== args.adoption.expectedForwardDigest && args.targetDigest !== args.adoption.expectedRollbackDigest && args.targetDigest !== args.adoption.expectedSourceDigest) {
|
|
21680
|
+
throw new LocalCliError({
|
|
21681
|
+
code: "PLATFORM_GATEWAY_ADOPTION_TARGET_CHANGED",
|
|
21682
|
+
message: "The pending adoption may move only to its recorded forward digest or back to its recorded source digest."
|
|
21683
|
+
});
|
|
21684
|
+
}
|
|
21685
|
+
const current = parseImageRef2(live.image);
|
|
21686
|
+
const currentDigest = current.digest ?? (current.tag ? await readAcrDigest(subscriptionId, acrName, `m8t-web:${current.tag}`) : null);
|
|
21687
|
+
const atSource = current.repo === `${host}/m8t-web` && currentDigest === args.adoption.expectedSourceDigest;
|
|
21688
|
+
const atAuthorizedForward = current.repo === `${host}/m8t-web` && currentDigest === args.adoption.expectedForwardDigest;
|
|
21689
|
+
const atTarget = current.repo === `${host}/m8t-web` && currentDigest === args.targetDigest;
|
|
21690
|
+
if (!atSource && !atAuthorizedForward && !atTarget) {
|
|
21691
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: "The adopted gateway changed after authorization; refusing to overwrite it." });
|
|
21692
|
+
}
|
|
21693
|
+
}
|
|
21694
|
+
const targetImage = `m8t-web:${args.targetTag}`;
|
|
21695
|
+
const existing = await readAcrDigest(subscriptionId, acrName, targetImage);
|
|
21696
|
+
if (existing && existing !== args.targetDigest) {
|
|
21697
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_TAG_CONFLICT", message: `ACR tag ${targetImage} already exists with a different digest; refusing to overwrite it.` });
|
|
21698
|
+
}
|
|
21699
|
+
if (!existing) {
|
|
21700
|
+
await runAz([
|
|
21701
|
+
"acr",
|
|
21702
|
+
"import",
|
|
21703
|
+
"--subscription",
|
|
21704
|
+
subscriptionId,
|
|
21705
|
+
"-n",
|
|
21706
|
+
acrName,
|
|
21707
|
+
"--source",
|
|
21708
|
+
`${DEFAULT_IMAGE_REPO}@${args.targetDigest}`,
|
|
21709
|
+
"--image",
|
|
21710
|
+
targetImage
|
|
21711
|
+
]);
|
|
21712
|
+
}
|
|
21713
|
+
const imported = await readAcrDigest(subscriptionId, acrName, targetImage);
|
|
21714
|
+
if (imported !== args.targetDigest) {
|
|
21715
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_IMPORT_MISMATCH", message: "The mirrored ACR tag does not match the official manifest digest." });
|
|
21716
|
+
}
|
|
21717
|
+
const attempts = args.pollAttempts ?? 12;
|
|
21718
|
+
const delayMs = args.pollDelayMs ?? 5e3;
|
|
21719
|
+
const waitReady = async (digest) => {
|
|
21720
|
+
for (let n = 0; n < attempts; n += 1) {
|
|
21721
|
+
live = await readLiveGatewayDeployment(args.gatewayResourceId);
|
|
21722
|
+
const actual = parseImageRef2(live.image);
|
|
21723
|
+
if (actual.repo === `${host}/m8t-web` && actual.digest === digest && live.provisioningState === "Succeeded" && live.latestRevisionName !== "" && live.latestRevisionName === live.latestReadyRevisionName) return true;
|
|
21724
|
+
if (n + 1 < attempts) await new Promise((resolve6) => setTimeout(resolve6, delayMs));
|
|
21725
|
+
}
|
|
21726
|
+
return false;
|
|
21727
|
+
};
|
|
21728
|
+
const updateTo = async (digest) => {
|
|
21729
|
+
const desired = `${host}/m8t-web@${digest}`;
|
|
21730
|
+
const actual = parseImageRef2(live.image);
|
|
21731
|
+
if (actual.repo !== `${host}/m8t-web` || actual.digest !== digest) {
|
|
21732
|
+
await runAz(["containerapp", "update", "--subscription", subscriptionId, "-g", resourceGroup, "-n", name, "--image", desired]);
|
|
21733
|
+
}
|
|
21734
|
+
};
|
|
21735
|
+
try {
|
|
21736
|
+
await updateTo(args.targetDigest);
|
|
21737
|
+
if (!await waitReady(args.targetDigest)) {
|
|
21738
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_NOT_READY", message: `Gateway '${name}' did not become ready on the manifest digest.` });
|
|
21739
|
+
}
|
|
21740
|
+
} catch (e) {
|
|
21741
|
+
if (args.adoption.status === "pending") {
|
|
21742
|
+
try {
|
|
21743
|
+
live = await readLiveGatewayDeployment(args.gatewayResourceId);
|
|
21744
|
+
await updateTo(args.adoption.expectedSourceDigest);
|
|
21745
|
+
if (!await waitReady(args.adoption.expectedSourceDigest)) throw new Error("source revision did not become ready");
|
|
21746
|
+
} catch (restoreError) {
|
|
21747
|
+
throw new LocalCliError({
|
|
21748
|
+
code: "PLATFORM_GATEWAY_ADOPTION_RESTORE_FAILED",
|
|
21749
|
+
message: `Gateway adoption failed and restoring the authorized source also failed: ${restoreError.message}`,
|
|
21750
|
+
cause: e
|
|
21751
|
+
});
|
|
21752
|
+
}
|
|
21753
|
+
}
|
|
21754
|
+
throw e;
|
|
21755
|
+
}
|
|
21756
|
+
args.onProgress?.(`gateway ready at ${args.targetTag} (${args.targetDigest}).`);
|
|
21757
|
+
return { ...args.adoption, status: "complete", completedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
21758
|
+
}
|
|
21759
|
+
function isSamePendingAdoption(existing, liveImage, digest, acrResourceId, gatewayState, forwardTag, forwardDigest, rollbackTag, rollbackDigest) {
|
|
21760
|
+
if (!existing) return false;
|
|
21761
|
+
return existing.expectedSourceRef === liveImage && existing.expectedSourceDigest === digest && existing.expectedAcrResourceId.toLowerCase() === acrResourceId.toLowerCase() && existing.expectedForwardTag === forwardTag && existing.expectedForwardDigest === forwardDigest && existing.expectedRollbackTag === rollbackTag && existing.expectedRollbackDigest === rollbackDigest && gatewayState === "managed" && existing.status === "pending";
|
|
21762
|
+
}
|
|
21763
|
+
async function adoptLegacyGateway(args) {
|
|
21764
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(args.expectedDigest)) {
|
|
21765
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-digest must be a lowercase sha256 digest." });
|
|
21766
|
+
}
|
|
21767
|
+
const parsedResource = parseContainerAppResourceId(args.gatewayResourceId);
|
|
21768
|
+
if (parsedResource.subscriptionId.toLowerCase() !== args.subscriptionId.toLowerCase() || parsedResource.resourceGroup.toLowerCase() !== args.resourceGroup.toLowerCase()) {
|
|
21769
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_SCOPE", message: "The discovered gateway does not belong to the requested subscription and resource group." });
|
|
21770
|
+
}
|
|
21771
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(args.targetGatewayDigest) || args.targetGatewayTag.trim() === "" || !/^sha256:[0-9a-f]{64}$/.test(args.rollbackGatewayDigest) || args.rollbackGatewayTag.trim() === "") {
|
|
21772
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The selected target lacks a valid gateway tag and digest." });
|
|
21773
|
+
}
|
|
21774
|
+
const client = await openMetadataTransactionClient({
|
|
21775
|
+
credential: args.credential,
|
|
21776
|
+
subscriptionId: args.subscriptionId,
|
|
21777
|
+
resourceGroup: args.resourceGroup,
|
|
21778
|
+
label: "gateway adoption transaction",
|
|
21779
|
+
errorCode: "PLATFORM_GATEWAY_ADOPTION_NO_ACCESS",
|
|
21780
|
+
retryHint: "The role assignment persists \u2014 retry the gateway adoption command in a few minutes.",
|
|
21781
|
+
onProgress: args.onProgress
|
|
21782
|
+
});
|
|
21783
|
+
const stampRow = await client.getEntity("system", "platform").catch((e) => {
|
|
21784
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_NO_STAMP", message: "The installed platform stamp is absent or unreadable; refusing to record gateway ownership.", cause: e });
|
|
21785
|
+
});
|
|
21786
|
+
const stamp = entityToStamp(stampRow);
|
|
21787
|
+
const stampEtag = stampRow.etag;
|
|
21788
|
+
if (!stamp || typeof stampEtag !== "string" || !stampEtag) {
|
|
21789
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_NO_STAMP", message: "The installed platform stamp is malformed or lacks an ETag." });
|
|
21790
|
+
}
|
|
21791
|
+
if (stamp.platformVersion !== args.rollbackPlatformVersion) {
|
|
21792
|
+
throw new LocalCliError({
|
|
21793
|
+
code: "PLATFORM_GATEWAY_ADOPTION_CHANGED",
|
|
21794
|
+
message: `The installed platform changed after preflight; expected rollback ${args.rollbackPlatformVersion}, found ${stamp.platformVersion}.`
|
|
21795
|
+
});
|
|
21796
|
+
}
|
|
21797
|
+
const existingRequest = await readApplyRequest(client);
|
|
21798
|
+
const live = await readLiveGatewayDeployment(args.gatewayResourceId);
|
|
21799
|
+
const topologyError = legacyGatewayTopologyError(live, args.gatewayResourceId, args.subscriptionId);
|
|
21800
|
+
if (topologyError) {
|
|
21801
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_REFUSED", message: `Gateway adoption refused: ${topologyError}.` });
|
|
21802
|
+
}
|
|
21803
|
+
if (live.image !== args.expectedImage) {
|
|
21804
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway image changed; expected '${args.expectedImage}', found '${live.image}'.` });
|
|
21805
|
+
}
|
|
21806
|
+
const digest = await resolveLegacyAcrDigest(live, args.subscriptionId);
|
|
21807
|
+
if (digest !== args.expectedDigest) {
|
|
21808
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway digest changed; expected '${args.expectedDigest}', found '${digest}'.` });
|
|
21809
|
+
}
|
|
21810
|
+
const expectedIdentity = live.userAssignedIdentityIds[0];
|
|
21811
|
+
if (args.expectedAcrResourceId.trim() === "" || typeof expectedIdentity !== "string") {
|
|
21812
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id and the generated pull identity are required." });
|
|
21813
|
+
}
|
|
21814
|
+
const imageHost = parseImageRef2(live.image).repo.split("/")[0].split(".")[0];
|
|
21815
|
+
const acrName = args.expectedAcrResourceId.split("/").filter(Boolean).at(-1) ?? "";
|
|
21816
|
+
const expectedAcrId = `/subscriptions/${args.subscriptionId}/resourceGroups/${args.resourceGroup}/providers/Microsoft.ContainerRegistry/registries/${imageHost}`;
|
|
21817
|
+
if (imageHost.toLowerCase() !== acrName.toLowerCase() || lowerId(args.expectedAcrResourceId) !== lowerId(expectedAcrId)) {
|
|
21818
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id does not match the live image registry." });
|
|
21819
|
+
}
|
|
21820
|
+
const existing = stamp.components.gateway.adoption;
|
|
21821
|
+
const samePending = isSamePendingAdoption(
|
|
21822
|
+
existing,
|
|
21823
|
+
live.image,
|
|
21824
|
+
digest,
|
|
21825
|
+
args.expectedAcrResourceId,
|
|
21826
|
+
stamp.components.gateway.state,
|
|
21827
|
+
args.targetGatewayTag,
|
|
21828
|
+
args.targetGatewayDigest,
|
|
21829
|
+
args.rollbackGatewayTag,
|
|
21830
|
+
args.rollbackGatewayDigest
|
|
21831
|
+
);
|
|
21832
|
+
if (existing && !samePending) {
|
|
21833
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CONFLICT", message: "The gateway already carries a different adoption record." });
|
|
21834
|
+
}
|
|
21835
|
+
if (existingRequest && blocksNewIntent(existingRequest.status)) {
|
|
21836
|
+
if (samePending && existingRequest.status === "pending" && existingRequest.target === args.target) {
|
|
21837
|
+
return { outcome: "already-adopted", image: live.image, digest };
|
|
21838
|
+
}
|
|
21839
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_IN_PROGRESS", message: `An update request for ${existingRequest.target} is already ${existingRequest.status}.` });
|
|
21840
|
+
}
|
|
21841
|
+
const now = args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
21842
|
+
const nextStamp = {
|
|
21843
|
+
...stamp,
|
|
21844
|
+
updatedAt: now,
|
|
21845
|
+
components: {
|
|
21846
|
+
...stamp.components,
|
|
21847
|
+
gateway: {
|
|
21848
|
+
...stamp.components.gateway,
|
|
21849
|
+
tag: parseImageRef2(live.image).tag ?? stamp.components.gateway.tag,
|
|
21850
|
+
digest,
|
|
21851
|
+
state: "managed",
|
|
21852
|
+
adoption: existing ?? {
|
|
21853
|
+
version: 1,
|
|
21854
|
+
status: "pending",
|
|
21855
|
+
expectedSourceRef: live.image,
|
|
21856
|
+
expectedSourceDigest: digest,
|
|
21857
|
+
expectedForwardDigest: args.targetGatewayDigest,
|
|
21858
|
+
expectedForwardTag: args.targetGatewayTag,
|
|
21859
|
+
expectedRollbackDigest: args.rollbackGatewayDigest,
|
|
21860
|
+
expectedRollbackTag: args.rollbackGatewayTag,
|
|
21861
|
+
expectedAcrResourceId: args.expectedAcrResourceId,
|
|
21862
|
+
expectedAcrPullIdentityResourceId: expectedIdentity,
|
|
21863
|
+
authorizedAt: now
|
|
21864
|
+
}
|
|
21865
|
+
}
|
|
21866
|
+
}
|
|
21867
|
+
};
|
|
21868
|
+
const intent = newIntent(args.target, { source: "policy", identity: args.operatorIdentity, at: now }, now);
|
|
21869
|
+
const stampWire = stampToEntity(nextStamp);
|
|
21870
|
+
const stampEntity = {
|
|
21871
|
+
...stampWire,
|
|
21872
|
+
partitionKey: String(stampWire.partitionKey),
|
|
21873
|
+
rowKey: String(stampWire.rowKey)
|
|
21874
|
+
};
|
|
21875
|
+
const applyEntity = applyRequestToEntity(intent);
|
|
21876
|
+
const stampAction = ["update", stampEntity, "Replace", { etag: stampEtag }];
|
|
21877
|
+
const requestAction = existingRequest ? ["update", applyEntity, "Replace", { etag: existingRequest.etag }] : ["create", applyEntity];
|
|
21878
|
+
try {
|
|
21879
|
+
await client.submitTransaction([stampAction, requestAction]);
|
|
21880
|
+
} catch (e) {
|
|
21881
|
+
const status = e.statusCode;
|
|
21882
|
+
if (status === 409 || status === 412) {
|
|
21883
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_RACE", message: "Gateway adoption lost a concurrent metadata update; no adoption was recorded.", hint: "Re-read the live image and digest, then retry the command.", cause: e });
|
|
21884
|
+
}
|
|
21885
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_WRITE_FAILED", message: "Could not atomically record gateway adoption and its update request.", cause: e });
|
|
21886
|
+
}
|
|
21887
|
+
args.onProgress?.(`recorded digest-bound gateway adoption and queued update to ${args.target}.`);
|
|
21888
|
+
return { outcome: samePending ? "already-adopted" : "adopted", image: live.image, digest };
|
|
21889
|
+
}
|
|
21890
|
+
|
|
21424
21891
|
// src/lib/platform-converge.ts
|
|
21425
21892
|
var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
|
|
21426
21893
|
function seedSetRev(manifest) {
|
|
@@ -21484,8 +21951,10 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
|
|
|
21484
21951
|
continue;
|
|
21485
21952
|
}
|
|
21486
21953
|
const from = cur?.tag ?? null;
|
|
21487
|
-
|
|
21488
|
-
|
|
21954
|
+
const adoption = component === "gateway" ? cur?.adoption : void 0;
|
|
21955
|
+
const withAdoption = adoption ? { gatewayAdoption: adoption } : {};
|
|
21956
|
+
if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag, ...withAdoption });
|
|
21957
|
+
else if (from !== img.tag || adoption?.status === "pending") actions.push({ component, reason: "changed", from, to: img.tag, ...withAdoption });
|
|
21489
21958
|
else skipped.push({ component, reason: "up-to-date" });
|
|
21490
21959
|
}
|
|
21491
21960
|
return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
|
|
@@ -21628,10 +22097,20 @@ function seedStamp(prior, manifest, plan) {
|
|
|
21628
22097
|
};
|
|
21629
22098
|
}
|
|
21630
22099
|
async function applyGatewayImage(a, ctx, gatewayResourceId) {
|
|
22100
|
+
const digest = ctx.manifest.components.gateway.digest;
|
|
22101
|
+
if (a.gatewayAdoption) {
|
|
22102
|
+
const adoption = await rollAdoptedGateway({
|
|
22103
|
+
gatewayResourceId,
|
|
22104
|
+
adoption: a.gatewayAdoption,
|
|
22105
|
+
targetTag: a.to,
|
|
22106
|
+
targetDigest: assertGatewayDigest(ctx.manifest.components.gateway),
|
|
22107
|
+
onProgress: ctx.onProgress
|
|
22108
|
+
});
|
|
22109
|
+
return { tag: a.to, digest, state: "managed", adoption };
|
|
22110
|
+
}
|
|
21631
22111
|
const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
|
|
21632
22112
|
const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
|
|
21633
22113
|
const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
|
|
21634
|
-
const digest = ctx.manifest.components.gateway.digest;
|
|
21635
22114
|
const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to, toDigest: digest });
|
|
21636
22115
|
switch (plan.kind) {
|
|
21637
22116
|
case "refuse-byoc":
|
|
@@ -22130,10 +22609,10 @@ async function resolveExecutorAgentName(args) {
|
|
|
22130
22609
|
}
|
|
22131
22610
|
|
|
22132
22611
|
// src/lib/platform-infra-params.ts
|
|
22133
|
-
import { TableClient as
|
|
22612
|
+
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
22134
22613
|
async function openInfraParamsTable(opts) {
|
|
22135
22614
|
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
22136
|
-
return new
|
|
22615
|
+
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
22137
22616
|
}
|
|
22138
22617
|
async function readInfraParams(client) {
|
|
22139
22618
|
try {
|
|
@@ -23006,73 +23485,6 @@ function resolveHeadlessContextFromEnv(env) {
|
|
|
23006
23485
|
};
|
|
23007
23486
|
}
|
|
23008
23487
|
|
|
23009
|
-
// src/lib/apply-request-store.ts
|
|
23010
|
-
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
23011
|
-
async function openApplyTable(opts) {
|
|
23012
|
-
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
23013
|
-
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
23014
|
-
}
|
|
23015
|
-
function hasStatus(e, n) {
|
|
23016
|
-
return e.statusCode === n;
|
|
23017
|
-
}
|
|
23018
|
-
async function readApplyRequest(client) {
|
|
23019
|
-
try {
|
|
23020
|
-
const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
|
|
23021
|
-
return { ...entityToApplyRequest(row), etag: row.etag };
|
|
23022
|
-
} catch (e) {
|
|
23023
|
-
if (hasStatus(e, 404)) return null;
|
|
23024
|
-
throw e;
|
|
23025
|
-
}
|
|
23026
|
-
}
|
|
23027
|
-
async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
|
|
23028
|
-
const cur = await readApplyRequest(client);
|
|
23029
|
-
if (!cur) return null;
|
|
23030
|
-
const leaseExpired = isInFlight(cur.status) && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
|
|
23031
|
-
const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
|
|
23032
|
-
if (!claimable) return null;
|
|
23033
|
-
const nowMs = Date.parse(nowIso);
|
|
23034
|
-
const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
|
|
23035
|
-
if (leaseExpired && cur.attempt >= MAX_LEASE_TAKEOVERS) {
|
|
23036
|
-
const held = {
|
|
23037
|
-
...cur,
|
|
23038
|
-
status: "held",
|
|
23039
|
-
breaker: "held",
|
|
23040
|
-
result: {
|
|
23041
|
-
appliedVersion: cur.result?.appliedVersion ?? null,
|
|
23042
|
-
error: `the converge was abandoned mid-apply ${(cur.attempt + 1).toString()} times (last owner ${cur.claimedBy ?? "(unknown)"}, phase "${cur.phase ?? "(none)"}"); the updater stopped retrying to avoid re-rolling infra behind a converge that cannot finish`
|
|
23043
|
-
},
|
|
23044
|
-
updatedAt: nowIso
|
|
23045
|
-
};
|
|
23046
|
-
try {
|
|
23047
|
-
await client.updateEntity({ ...applyRequestToEntity(held), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
23048
|
-
} catch (e) {
|
|
23049
|
-
if (!hasStatus(e, 412)) throw e;
|
|
23050
|
-
}
|
|
23051
|
-
return null;
|
|
23052
|
-
}
|
|
23053
|
-
const claimed = {
|
|
23054
|
-
...cur,
|
|
23055
|
-
status: "claimed",
|
|
23056
|
-
claimedBy: executionId,
|
|
23057
|
-
leaseUntil,
|
|
23058
|
-
...leaseExpired ? { attempt: cur.attempt + 1 } : {},
|
|
23059
|
-
updatedAt: nowIso
|
|
23060
|
-
};
|
|
23061
|
-
try {
|
|
23062
|
-
await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
23063
|
-
return claimed;
|
|
23064
|
-
} catch (e) {
|
|
23065
|
-
if (hasStatus(e, 412)) return null;
|
|
23066
|
-
throw e;
|
|
23067
|
-
}
|
|
23068
|
-
}
|
|
23069
|
-
async function patchApplyRequest(client, mutate) {
|
|
23070
|
-
const cur = await readApplyRequest(client);
|
|
23071
|
-
if (!cur) return;
|
|
23072
|
-
const next = mutate(cur);
|
|
23073
|
-
await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
23074
|
-
}
|
|
23075
|
-
|
|
23076
23488
|
// src/lib/rail-preconditions.ts
|
|
23077
23489
|
init_errors();
|
|
23078
23490
|
function assertBakedContentPresent(repoRoot) {
|
|
@@ -23233,7 +23645,7 @@ async function runGate(args, applied, budgetMs) {
|
|
|
23233
23645
|
}
|
|
23234
23646
|
async function rollbackOrHold(args, notify, error) {
|
|
23235
23647
|
const target = args.plan.targetVersion;
|
|
23236
|
-
const rollbackTarget = args.
|
|
23648
|
+
const rollbackTarget = args.preApplyVersion;
|
|
23237
23649
|
const current = await readBreaker(args.client);
|
|
23238
23650
|
const next = nextBreaker(current);
|
|
23239
23651
|
if (next === "held") {
|
|
@@ -23497,6 +23909,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
23497
23909
|
plan,
|
|
23498
23910
|
ctx: applyCtx,
|
|
23499
23911
|
stamp: stamp ?? seedStampFor(plan),
|
|
23912
|
+
preApplyVersion: stamp?.platformVersion ?? null,
|
|
23500
23913
|
manifest,
|
|
23501
23914
|
credential: ctx.credential,
|
|
23502
23915
|
endpoint: ctx.endpoint,
|
|
@@ -23722,8 +24135,8 @@ var PlatformRequestUpdateCommand = class extends M8tCommand {
|
|
|
23722
24135
|
description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
|
|
23723
24136
|
details: "Writes the update request the updater job claims on its next tick. This is the ONLY privilege the requester holds \u2014 it does not deploy anything itself; the install's own updater fetches the release, applies it, health-gates it and rolls back on failure. With --wait, follows the request to a terminal state and exits non-zero if it did not succeed.",
|
|
23724
24137
|
examples: [
|
|
23725
|
-
["Request an update", "m8t platform request-update --version 0.7.
|
|
23726
|
-
["Request it and wait", "m8t platform request-update --version 0.7.
|
|
24138
|
+
["Request an update", "m8t platform request-update --version 0.7.4 --resource-group rg-m8t --subscription <id>"],
|
|
24139
|
+
["Request it and wait", "m8t platform request-update --version 0.7.4 --wait --resource-group rg-m8t --subscription <id>"]
|
|
23727
24140
|
]
|
|
23728
24141
|
});
|
|
23729
24142
|
version = Option35.String("--version", { description: "The platform version to converge to." });
|
|
@@ -24021,9 +24434,91 @@ var PlatformSeedStampCommand = class extends M8tCommand {
|
|
|
24021
24434
|
}
|
|
24022
24435
|
};
|
|
24023
24436
|
|
|
24024
|
-
// src/commands/platform/
|
|
24437
|
+
// src/commands/platform/gateway-adopt.ts
|
|
24025
24438
|
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
24026
|
-
import { DefaultAzureCredential as DefaultAzureCredential21
|
|
24439
|
+
import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
|
|
24440
|
+
init_errors();
|
|
24441
|
+
var PlatformGatewayAdoptCommand = class extends M8tCommand {
|
|
24442
|
+
static paths = [["platform", "gateway", "adopt"]];
|
|
24443
|
+
static usage = Command40.Usage({
|
|
24444
|
+
description: "Explicitly authorize one legacy m8t ACR gateway for managed, digest-pinned updates.",
|
|
24445
|
+
details: "Fail-closed ownership transition. Reads the live gateway and ACR digest, requires the operator to repeat both exactly, verifies the generated legacy topology, and atomically writes the adoption marker with a pending platform update. Unadopted BYOC remains external."
|
|
24446
|
+
});
|
|
24447
|
+
subscription = Option37.String("--subscription");
|
|
24448
|
+
resourceGroup = Option37.String("--resource-group");
|
|
24449
|
+
target = Option37.String("--target", { description: "Published platform target whose installer contains adoption support." });
|
|
24450
|
+
expectedImage = Option37.String("--expected-image", { description: "Exact live legacy ACR image ref." });
|
|
24451
|
+
expectedDigest = Option37.String("--expected-digest", { description: "Exact sha256 digest resolved from that ACR image." });
|
|
24452
|
+
expectedAcrResourceId = Option37.String("--expected-acr-resource-id", { description: "Exact ARM id of the legacy ACR." });
|
|
24453
|
+
output = Option37.String("--output");
|
|
24454
|
+
async executeCommand() {
|
|
24455
|
+
const need = (v, flag) => {
|
|
24456
|
+
if (typeof v !== "string" || v.trim() === "") throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_ARG_MISSING", message: `${flag} is required.` });
|
|
24457
|
+
return v.trim();
|
|
24458
|
+
};
|
|
24459
|
+
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
24460
|
+
const account = await getAzAccount();
|
|
24461
|
+
const subscriptionId = need(this.subscription ?? account.subscriptionId, "--subscription");
|
|
24462
|
+
const gw = await resolveGatewayContext({ subscriptionId, resourceGroup: this.resourceGroup, interactive: mode !== "json" });
|
|
24463
|
+
const parsed = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
24464
|
+
const resourceGroup = need(this.resourceGroup ?? parsed.resourceGroup, "--resource-group");
|
|
24465
|
+
const target = need(this.target, "--target");
|
|
24466
|
+
const manifest = await fetchManifest({ channel: true, version: target });
|
|
24467
|
+
const assertTarget = (requested, actual) => {
|
|
24468
|
+
const normalized = requested.replace(/^platform-v/, "");
|
|
24469
|
+
if (actual.platform.version.replace(/^platform-v/, "") !== normalized || actual.platform.tag !== platformTag(normalized)) {
|
|
24470
|
+
throw new LocalCliError({
|
|
24471
|
+
code: "PLATFORM_GATEWAY_ADOPTION_TARGET_MISMATCH",
|
|
24472
|
+
message: `Requested target ${requested}, but the release service returned ${actual.platform.version}.`
|
|
24473
|
+
});
|
|
24474
|
+
}
|
|
24475
|
+
};
|
|
24476
|
+
assertTarget(target, manifest);
|
|
24477
|
+
if (compareSemver(`v${manifest.components.installer.version}`, "v0.1.74") < 0 || compareSemver(`v${manifest.components.cli.recommended}`, "v0.2.107") < 0) {
|
|
24478
|
+
throw new LocalCliError({
|
|
24479
|
+
code: "PLATFORM_GATEWAY_ADOPTION_TARGET_TOO_OLD",
|
|
24480
|
+
message: `Platform target ${target} predates digest-bound legacy gateway adoption support.`,
|
|
24481
|
+
hint: "Choose a published platform target whose installer is at least 0.1.74 and recommended CLI is at least 0.2.107."
|
|
24482
|
+
});
|
|
24483
|
+
}
|
|
24484
|
+
const credential2 = new DefaultAzureCredential21();
|
|
24485
|
+
const installed2 = await readStampOutcome({ credential: credential2, subscriptionId, resourceGroup });
|
|
24486
|
+
if (installed2.source !== "explicit") {
|
|
24487
|
+
throw new LocalCliError({
|
|
24488
|
+
code: "PLATFORM_GATEWAY_ADOPTION_NO_ROLLBACK",
|
|
24489
|
+
message: "The installed platform stamp is absent or unreadable; refusing a gateway ownership transition without its exact rollback target."
|
|
24490
|
+
});
|
|
24491
|
+
}
|
|
24492
|
+
const rollbackVersion = installed2.stamp.platformVersion;
|
|
24493
|
+
const rollbackManifest = await fetchManifest({ channel: true, version: rollbackVersion });
|
|
24494
|
+
assertTarget(rollbackVersion, rollbackManifest);
|
|
24495
|
+
const result2 = await adoptLegacyGateway({
|
|
24496
|
+
credential: credential2,
|
|
24497
|
+
subscriptionId,
|
|
24498
|
+
resourceGroup,
|
|
24499
|
+
gatewayResourceId: gw.containerAppResourceId,
|
|
24500
|
+
target,
|
|
24501
|
+
expectedImage: need(this.expectedImage, "--expected-image"),
|
|
24502
|
+
expectedDigest: need(this.expectedDigest, "--expected-digest"),
|
|
24503
|
+
expectedAcrResourceId: need(this.expectedAcrResourceId, "--expected-acr-resource-id"),
|
|
24504
|
+
targetGatewayTag: manifest.components.gateway.tag,
|
|
24505
|
+
targetGatewayDigest: manifest.components.gateway.digest,
|
|
24506
|
+
rollbackGatewayTag: rollbackManifest.components.gateway.tag,
|
|
24507
|
+
rollbackGatewayDigest: rollbackManifest.components.gateway.digest,
|
|
24508
|
+
rollbackPlatformVersion: rollbackVersion,
|
|
24509
|
+
operatorIdentity: await getCallerObjectId(),
|
|
24510
|
+
onProgress: (m) => this.context.stderr.write(`${m}
|
|
24511
|
+
`)
|
|
24512
|
+
});
|
|
24513
|
+
this.context.stdout.write(mode === "json" ? renderJson(result2) + "\n" : `${result2.outcome}: ${result2.image}@${result2.digest}
|
|
24514
|
+
`);
|
|
24515
|
+
return 0;
|
|
24516
|
+
}
|
|
24517
|
+
};
|
|
24518
|
+
|
|
24519
|
+
// src/commands/platform/policy.ts
|
|
24520
|
+
import { Command as Command41, Option as Option38 } from "clipanion";
|
|
24521
|
+
import { DefaultAzureCredential as DefaultAzureCredential22, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
|
|
24027
24522
|
init_errors();
|
|
24028
24523
|
|
|
24029
24524
|
// src/lib/platform-policy.ts
|
|
@@ -24078,16 +24573,16 @@ async function readPolicy(opts) {
|
|
|
24078
24573
|
// src/commands/platform/policy.ts
|
|
24079
24574
|
var PlatformPolicyCommand = class extends M8tCommand {
|
|
24080
24575
|
static paths = [["platform", "policy"]];
|
|
24081
|
-
static usage =
|
|
24576
|
+
static usage = Command41.Usage({
|
|
24082
24577
|
description: "Show or set how this install handles available platform updates.",
|
|
24083
24578
|
details: "Modes: 'notify-only' shows updates but never applies them; 'auto-critical' (the default when unset) applies critical releases without asking; 'auto-all' applies every release. Use 'notify-only' where something else already controls what is deployed.",
|
|
24084
24579
|
examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
|
|
24085
24580
|
});
|
|
24086
|
-
set =
|
|
24087
|
-
subscription =
|
|
24088
|
-
resourceGroup =
|
|
24089
|
-
miClientId =
|
|
24090
|
-
output =
|
|
24581
|
+
set = Option38.String("--set", { description: "notify-only | auto-critical | auto-all" });
|
|
24582
|
+
subscription = Option38.String("--subscription");
|
|
24583
|
+
resourceGroup = Option38.String("--resource-group");
|
|
24584
|
+
miClientId = Option38.String("--mi-client-id");
|
|
24585
|
+
output = Option38.String("--output");
|
|
24091
24586
|
async executeCommand() {
|
|
24092
24587
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
24093
24588
|
const need = (v, flag) => {
|
|
@@ -24100,7 +24595,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
24100
24595
|
this.context.stderr.write(`${m}
|
|
24101
24596
|
`);
|
|
24102
24597
|
};
|
|
24103
|
-
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new
|
|
24598
|
+
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential22();
|
|
24104
24599
|
const ctx = {
|
|
24105
24600
|
credential: credential2,
|
|
24106
24601
|
subscriptionId: need(this.subscription, "--subscription"),
|
|
@@ -24133,7 +24628,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
24133
24628
|
};
|
|
24134
24629
|
|
|
24135
24630
|
// src/commands/platform/enable-cost-report.ts
|
|
24136
|
-
import { Command as
|
|
24631
|
+
import { Command as Command42, Option as Option39 } from "clipanion";
|
|
24137
24632
|
|
|
24138
24633
|
// src/lib/wire-gateway-acs.ts
|
|
24139
24634
|
init_rbac();
|
|
@@ -24173,18 +24668,18 @@ async function wireGatewayForAcs(args) {
|
|
|
24173
24668
|
init_errors();
|
|
24174
24669
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
24175
24670
|
static paths = [["platform", "enable-cost-report"]];
|
|
24176
|
-
static usage =
|
|
24671
|
+
static usage = Command42.Usage({
|
|
24177
24672
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
24178
24673
|
details: "Discovers the live gateway Container App, grants its managed identity Contributor at the ACS resource scope (authorising the ACS Email send), and sets M8T_ACS_ENDPOINT / M8T_ACS_SENDER / M8T_ENABLE_COST_REPORTER=1 on the gateway. Idempotent \u2014 safely re-runnable. ACS is created during the executor deploy; pass its endpoint, sender, and resource id (from that deploy or 'az communication list')."
|
|
24179
24674
|
});
|
|
24180
|
-
subscription =
|
|
24181
|
-
resourceGroup =
|
|
24675
|
+
subscription = Option39.String("--subscription");
|
|
24676
|
+
resourceGroup = Option39.String("--resource-group", {
|
|
24182
24677
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24183
24678
|
});
|
|
24184
|
-
acsEndpoint =
|
|
24185
|
-
acsSender =
|
|
24186
|
-
acsResourceId =
|
|
24187
|
-
output =
|
|
24679
|
+
acsEndpoint = Option39.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
|
|
24680
|
+
acsSender = Option39.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
|
|
24681
|
+
acsResourceId = Option39.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
|
|
24682
|
+
output = Option39.String("--output");
|
|
24188
24683
|
async executeCommand() {
|
|
24189
24684
|
const mode = resolveOutputMode(
|
|
24190
24685
|
this.output,
|
|
@@ -24258,13 +24753,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
24258
24753
|
};
|
|
24259
24754
|
|
|
24260
24755
|
// src/commands/platform/email.ts
|
|
24261
|
-
import { Command as
|
|
24262
|
-
import { DefaultAzureCredential as
|
|
24756
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
24757
|
+
import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
|
|
24263
24758
|
import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
|
|
24264
24759
|
init_errors();
|
|
24265
24760
|
var PlatformEmailCommand = class extends M8tCommand {
|
|
24266
24761
|
static paths = [["platform", "email"]];
|
|
24267
|
-
static usage =
|
|
24762
|
+
static usage = Command43.Usage({
|
|
24268
24763
|
description: "Turn outbound email (advisor handoffs) on or off for this install.",
|
|
24269
24764
|
details: "`on` resolves the ACS sender (reusing an existing one, provisioning only if there is none), records the decision in the platform stamp, and reconciles the executor's environment. `off` records the decision and strips the executor's email wiring, leaving the ACS resource in place so turning it back on is a flag flip. Idempotent \u2014 re-running either direction is safe, and no new agent version is posted when nothing changes.",
|
|
24270
24765
|
examples: [
|
|
@@ -24272,19 +24767,19 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24272
24767
|
["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
|
|
24273
24768
|
]
|
|
24274
24769
|
});
|
|
24275
|
-
state =
|
|
24276
|
-
subscription =
|
|
24277
|
-
resourceGroup =
|
|
24770
|
+
state = Option40.String({ required: true, name: "on|off" });
|
|
24771
|
+
subscription = Option40.String("--subscription");
|
|
24772
|
+
resourceGroup = Option40.String("--resource-group", {
|
|
24278
24773
|
description: "m8t resource group, to disambiguate in a multi-deployment subscription."
|
|
24279
24774
|
});
|
|
24280
|
-
agent =
|
|
24281
|
-
kvUri =
|
|
24775
|
+
agent = Option40.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
|
|
24776
|
+
kvUri = Option40.String("--kv-uri", {
|
|
24282
24777
|
description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
|
|
24283
24778
|
});
|
|
24284
|
-
endpoint =
|
|
24779
|
+
endpoint = Option40.String("--endpoint", {
|
|
24285
24780
|
description: "Foundry project endpoint, to disambiguate a subscription holding several."
|
|
24286
24781
|
});
|
|
24287
|
-
output =
|
|
24782
|
+
output = Option40.String("--output");
|
|
24288
24783
|
async executeCommand() {
|
|
24289
24784
|
const wanted = this.state.trim().toLowerCase();
|
|
24290
24785
|
if (wanted !== "on" && wanted !== "off") {
|
|
@@ -24308,7 +24803,7 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24308
24803
|
});
|
|
24309
24804
|
const { resourceGroup } = parseContainerAppResourceId(ctx.containerAppResourceId);
|
|
24310
24805
|
const subscriptionId = ctx.subscriptionId;
|
|
24311
|
-
const credential2 = new
|
|
24806
|
+
const credential2 = new DefaultAzureCredential23();
|
|
24312
24807
|
const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
|
|
24313
24808
|
if (stamp === null) {
|
|
24314
24809
|
throw new LocalCliError({
|
|
@@ -24434,45 +24929,45 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24434
24929
|
};
|
|
24435
24930
|
|
|
24436
24931
|
// src/commands/platform/enable-auto-update.ts
|
|
24437
|
-
import { Command as
|
|
24932
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
24438
24933
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
24439
|
-
import { DefaultAzureCredential as
|
|
24934
|
+
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
24440
24935
|
init_errors();
|
|
24441
24936
|
var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
24442
24937
|
static paths = [["platform", "enable-auto-update"]];
|
|
24443
|
-
static usage =
|
|
24938
|
+
static usage = Command44.Usage({
|
|
24444
24939
|
category: "Platform",
|
|
24445
24940
|
description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
|
|
24446
24941
|
details: "For an install that predates the Platform Update Framework: recovers the deployment's resource-name suffix (from the stamped system/infra-params row, or by verified derivation from the live gateway), recovers the other bicep params from live state (WITHOUT changing the deployed gateway image), and re-runs deploy/main.bicep with provisionUpdater=true \u2014 which provisions the updater managed identity, the cron Container Apps Job, and every role assignment (Owner@RG + Foundry User@account + Storage Table + Storage Blob + AcrPull), all scoped to the resource group. On success, backfills system/infra-params so future converges never have to re-derive the suffix. Idempotent \u2014 safe to re-run."
|
|
24447
24942
|
});
|
|
24448
|
-
subscription =
|
|
24449
|
-
resourceGroup =
|
|
24943
|
+
subscription = Option41.String("--subscription");
|
|
24944
|
+
resourceGroup = Option41.String("--resource-group", {
|
|
24450
24945
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24451
24946
|
});
|
|
24452
|
-
suffix =
|
|
24947
|
+
suffix = Option41.String("--suffix", {
|
|
24453
24948
|
description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
|
|
24454
24949
|
});
|
|
24455
|
-
installerImage =
|
|
24950
|
+
installerImage = Option41.String("--installer-image", {
|
|
24456
24951
|
required: true,
|
|
24457
24952
|
description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
|
|
24458
24953
|
});
|
|
24459
|
-
updateCron =
|
|
24954
|
+
updateCron = Option41.String("--update-cron", {
|
|
24460
24955
|
description: "Cron schedule for the updater job (bicep default applies when omitted)."
|
|
24461
24956
|
});
|
|
24462
|
-
channelUrl =
|
|
24957
|
+
channelUrl = Option41.String("--channel-url", {
|
|
24463
24958
|
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
24464
24959
|
});
|
|
24465
|
-
location =
|
|
24960
|
+
location = Option41.String("--location", {
|
|
24466
24961
|
description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
|
|
24467
24962
|
});
|
|
24468
|
-
foundryTracing =
|
|
24963
|
+
foundryTracing = Option41.String("--foundry-tracing", {
|
|
24469
24964
|
description: "project | account | skip. Omitting it keeps the value already stamped on this install; only an install with nothing stamped falls through to the default (project)."
|
|
24470
24965
|
});
|
|
24471
|
-
endpoint =
|
|
24966
|
+
endpoint = Option41.String("--endpoint", {
|
|
24472
24967
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
24473
24968
|
});
|
|
24474
|
-
yes =
|
|
24475
|
-
output =
|
|
24969
|
+
yes = Option41.Boolean("--yes", false);
|
|
24970
|
+
output = Option41.String("--output");
|
|
24476
24971
|
async executeCommand() {
|
|
24477
24972
|
const mode = resolveOutputMode(
|
|
24478
24973
|
this.output,
|
|
@@ -24491,7 +24986,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
24491
24986
|
resourceGroup: this.resourceGroup
|
|
24492
24987
|
});
|
|
24493
24988
|
const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
24494
|
-
const credential2 = new
|
|
24989
|
+
const credential2 = new DefaultAzureCredential24();
|
|
24495
24990
|
const account = await getAzAccount();
|
|
24496
24991
|
const subscriptionId = this.subscription ?? account.subscriptionId;
|
|
24497
24992
|
const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
|
|
@@ -24653,7 +25148,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
24653
25148
|
};
|
|
24654
25149
|
|
|
24655
25150
|
// src/commands/deploy.ts
|
|
24656
|
-
import { Command as
|
|
25151
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
24657
25152
|
|
|
24658
25153
|
// src/lib/app-reg.ts
|
|
24659
25154
|
init_esm();
|
|
@@ -25217,15 +25712,15 @@ function classifyWhatIf(changes) {
|
|
|
25217
25712
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
|
|
25218
25713
|
var DeployCommand = class extends M8tCommand {
|
|
25219
25714
|
static paths = [["deploy"]];
|
|
25220
|
-
static usage =
|
|
25715
|
+
static usage = Command45.Usage({
|
|
25221
25716
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
25222
25717
|
details: "Ensures the Entra app reg (or pass --client-id to reuse an existing one \u2014 required if you can't create app regs), writes ~/.m8t/config.yaml, ensures the resource group, and runs deploy/main.bicep. The repo is located via ~/.m8t/repo-root."
|
|
25223
25718
|
});
|
|
25224
|
-
subscription =
|
|
25225
|
-
resourceGroup =
|
|
25226
|
-
location =
|
|
25227
|
-
suffix =
|
|
25228
|
-
imageRef =
|
|
25719
|
+
subscription = Option42.String("--subscription");
|
|
25720
|
+
resourceGroup = Option42.String("--resource-group", "rg-m8t-stack");
|
|
25721
|
+
location = Option42.String("--location", "eastus");
|
|
25722
|
+
suffix = Option42.String("--suffix", "");
|
|
25723
|
+
imageRef = Option42.String("--image-ref", DEFAULT_IMAGE_REF);
|
|
25229
25724
|
// Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
|
|
25230
25725
|
// from-zero case. It exists because the gateway and the voice relay do NOT
|
|
25231
25726
|
// always run the same image: a converge preserves a BYOC gateway on its own
|
|
@@ -25233,28 +25728,28 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25233
25728
|
// (`--what-if`) that can only express one image therefore reports the other
|
|
25234
25729
|
// app as drift on every single run, forever — which is exactly what the
|
|
25235
25730
|
// infra-drift gate did from 2026-08-11.
|
|
25236
|
-
gatewayImageRef =
|
|
25237
|
-
acrPullIdentity =
|
|
25238
|
-
acrResourceId =
|
|
25239
|
-
foundryEndpoint =
|
|
25240
|
-
foundryResourceId =
|
|
25241
|
-
foundryTracing =
|
|
25731
|
+
gatewayImageRef = Option42.String("--gateway-image-ref", "");
|
|
25732
|
+
acrPullIdentity = Option42.String("--acrpull-identity");
|
|
25733
|
+
acrResourceId = Option42.String("--acr-resource-id");
|
|
25734
|
+
foundryEndpoint = Option42.String("--foundry-endpoint");
|
|
25735
|
+
foundryResourceId = Option42.String("--foundry-resource-id");
|
|
25736
|
+
foundryTracing = Option42.String("--foundry-tracing");
|
|
25242
25737
|
// project | account | skip (bicep default: project)
|
|
25243
|
-
clientId =
|
|
25244
|
-
whatIf =
|
|
25738
|
+
clientId = Option42.String("--client-id");
|
|
25739
|
+
whatIf = Option42.Boolean("--what-if", false);
|
|
25245
25740
|
// Only meaningful with --what-if. Routes the comparison through the
|
|
25246
25741
|
// value-free renderers (see ./lib/whatif-redact.js) instead of the default
|
|
25247
25742
|
// before/after renderer. Defaults false so a local, interactive run keeps
|
|
25248
25743
|
// showing values — that is the whole diagnostic point of --what-if.
|
|
25249
25744
|
// Automation that forwards this output anywhere non-private (a CI log, an
|
|
25250
25745
|
// issue) MUST pass --redact.
|
|
25251
|
-
redact =
|
|
25252
|
-
output =
|
|
25746
|
+
redact = Option42.Boolean("--redact", false);
|
|
25747
|
+
output = Option42.String("--output");
|
|
25253
25748
|
// Subscription-scoped role assignments. Omitted ⇒ the template default (true).
|
|
25254
25749
|
// Pass false when deploying as a principal scoped to the resource group only:
|
|
25255
25750
|
// it cannot deploy at subscription scope, and those assignments persist
|
|
25256
25751
|
// idempotently from the initial deployment anyway.
|
|
25257
|
-
assignSubscriptionRoles =
|
|
25752
|
+
assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
|
|
25258
25753
|
/**
|
|
25259
25754
|
* The installer image the updater Container-Apps Job runs.
|
|
25260
25755
|
*
|
|
@@ -25265,23 +25760,23 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25265
25760
|
* than that — the comparison proposes REMOVING an updater job that exists and
|
|
25266
25761
|
* should, and reports it as drift on every single run.
|
|
25267
25762
|
*/
|
|
25268
|
-
installerImage =
|
|
25763
|
+
installerImage = Option42.String("--installer-image");
|
|
25269
25764
|
// Referee — all optional, undefined by default ⇒ the bicep defaults
|
|
25270
25765
|
// apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
|
|
25271
25766
|
// empty). Only pass these when explicitly enabling the referee exam stack.
|
|
25272
|
-
gatewayCpu =
|
|
25273
|
-
gatewayMemory =
|
|
25274
|
-
refereeEnabled =
|
|
25275
|
-
refereeBrainRepos =
|
|
25276
|
-
refereeFeedRepo =
|
|
25277
|
-
refereeInstallationId =
|
|
25278
|
-
refereeWebhookHmacKvUri =
|
|
25279
|
-
examKvUri =
|
|
25280
|
-
examLaWorkspaceId =
|
|
25281
|
-
brainEvalDeployment =
|
|
25282
|
-
brainAppLogin =
|
|
25283
|
-
refereeCheckpointDir =
|
|
25284
|
-
examApiBase =
|
|
25767
|
+
gatewayCpu = Option42.String("--gateway-cpu");
|
|
25768
|
+
gatewayMemory = Option42.String("--gateway-memory");
|
|
25769
|
+
refereeEnabled = Option42.String("--referee-enabled");
|
|
25770
|
+
refereeBrainRepos = Option42.String("--referee-brain-repos");
|
|
25771
|
+
refereeFeedRepo = Option42.String("--referee-feed-repo");
|
|
25772
|
+
refereeInstallationId = Option42.String("--referee-installation-id");
|
|
25773
|
+
refereeWebhookHmacKvUri = Option42.String("--referee-webhook-hmac-kv-uri");
|
|
25774
|
+
examKvUri = Option42.String("--exam-kv-uri");
|
|
25775
|
+
examLaWorkspaceId = Option42.String("--exam-la-workspace-id");
|
|
25776
|
+
brainEvalDeployment = Option42.String("--brain-eval-deployment");
|
|
25777
|
+
brainAppLogin = Option42.String("--brain-app-login");
|
|
25778
|
+
refereeCheckpointDir = Option42.String("--referee-checkpoint-dir");
|
|
25779
|
+
examApiBase = Option42.String("--exam-api-base");
|
|
25285
25780
|
async executeCommand() {
|
|
25286
25781
|
const mode = resolveOutputMode(
|
|
25287
25782
|
this.output,
|
|
@@ -25465,7 +25960,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25465
25960
|
|
|
25466
25961
|
// src/commands/eval/skill.ts
|
|
25467
25962
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
25468
|
-
import { Command as
|
|
25963
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
25469
25964
|
init_errors();
|
|
25470
25965
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
25471
25966
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -25490,14 +25985,14 @@ function parseVerdict(stdout) {
|
|
|
25490
25985
|
}
|
|
25491
25986
|
var EvalSkillCommand = class extends M8tCommand {
|
|
25492
25987
|
static paths = [["eval", "skill"]];
|
|
25493
|
-
static usage =
|
|
25988
|
+
static usage = Command46.Usage({
|
|
25494
25989
|
description: "Vet one inbox skill candidate: promote / reject / needs_review. Shells out to the Python `brain-eval` core (override its path with $BRAIN_EVAL_BIN)."
|
|
25495
25990
|
});
|
|
25496
|
-
candidate =
|
|
25497
|
-
skillsDir =
|
|
25498
|
-
noJudge =
|
|
25499
|
-
deployment =
|
|
25500
|
-
output =
|
|
25991
|
+
candidate = Option43.String();
|
|
25992
|
+
skillsDir = Option43.String("--skills-dir");
|
|
25993
|
+
noJudge = Option43.Boolean("--no-judge", false);
|
|
25994
|
+
deployment = Option43.String("--deployment");
|
|
25995
|
+
output = Option43.String("--output");
|
|
25501
25996
|
executeCommand() {
|
|
25502
25997
|
return Promise.resolve(this._runCommand());
|
|
25503
25998
|
}
|
|
@@ -25556,7 +26051,7 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
25556
26051
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
25557
26052
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
|
|
25558
26053
|
import { join as join30 } from "path";
|
|
25559
|
-
import { Command as
|
|
26054
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
25560
26055
|
init_errors();
|
|
25561
26056
|
init_esm();
|
|
25562
26057
|
function parseArmToken(tok, opts) {
|
|
@@ -25786,24 +26281,24 @@ function buildPlan(args) {
|
|
|
25786
26281
|
}
|
|
25787
26282
|
var EvalExamCommand = class extends M8tCommand {
|
|
25788
26283
|
static paths = [["eval", "exam"]];
|
|
25789
|
-
static usage =
|
|
26284
|
+
static usage = Command47.Usage({
|
|
25790
26285
|
description: "Run a brain exam: impact A/B + dream-delta. Resolves the plan, then shells out to the Python `brain-exam` orchestrator (override its path with $BRAIN_EXAM_BIN). Renders an ExamVerdict: three-valued verdict + power note + per-task flips."
|
|
25791
26286
|
});
|
|
25792
|
-
worker =
|
|
25793
|
-
arms =
|
|
25794
|
-
taskSet =
|
|
25795
|
-
examType =
|
|
25796
|
-
skill =
|
|
25797
|
-
reps =
|
|
25798
|
-
probes =
|
|
25799
|
-
pool =
|
|
25800
|
-
out =
|
|
25801
|
-
dryRun =
|
|
25802
|
-
keepArms =
|
|
25803
|
-
allowStub =
|
|
25804
|
-
deployment =
|
|
25805
|
-
output =
|
|
25806
|
-
observeWaitS =
|
|
26287
|
+
worker = Option44.String();
|
|
26288
|
+
arms = Option44.String("--arms");
|
|
26289
|
+
taskSet = Option44.String("--task-set");
|
|
26290
|
+
examType = Option44.String("--exam-type");
|
|
26291
|
+
skill = Option44.String("--skill");
|
|
26292
|
+
reps = Option44.String("-n,--reps");
|
|
26293
|
+
probes = Option44.String("--probes");
|
|
26294
|
+
pool = Option44.String("--pool");
|
|
26295
|
+
out = Option44.String("--out");
|
|
26296
|
+
dryRun = Option44.Boolean("--dry-run", false);
|
|
26297
|
+
keepArms = Option44.Boolean("--keep-arms", false);
|
|
26298
|
+
allowStub = Option44.Boolean("--allow-stub", false);
|
|
26299
|
+
deployment = Option44.String("--deployment");
|
|
26300
|
+
output = Option44.String("--output");
|
|
26301
|
+
observeWaitS = Option44.String("--observe-wait-s");
|
|
25807
26302
|
async executeCommand() {
|
|
25808
26303
|
await Promise.resolve();
|
|
25809
26304
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -25918,10 +26413,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
25918
26413
|
};
|
|
25919
26414
|
|
|
25920
26415
|
// src/commands/version.ts
|
|
25921
|
-
import { Command as
|
|
26416
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
25922
26417
|
var VersionCommand = class extends M8tCommand {
|
|
25923
26418
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
25924
|
-
static usage =
|
|
26419
|
+
static usage = Command48.Usage({
|
|
25925
26420
|
description: "Print the CLI version.",
|
|
25926
26421
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
25927
26422
|
examples: [
|
|
@@ -25929,8 +26424,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
25929
26424
|
["Print as JSON", "$0 version --output json"]
|
|
25930
26425
|
]
|
|
25931
26426
|
});
|
|
25932
|
-
output =
|
|
25933
|
-
verbose =
|
|
26427
|
+
output = Option45.String("--output", { description: "pretty | json | auto (default)" });
|
|
26428
|
+
verbose = Option45.Boolean("--verbose", false);
|
|
25934
26429
|
executeCommand() {
|
|
25935
26430
|
const mode = resolveOutputMode(
|
|
25936
26431
|
this.output ?? "auto",
|
|
@@ -25961,18 +26456,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
25961
26456
|
};
|
|
25962
26457
|
|
|
25963
26458
|
// src/commands/whoami.ts
|
|
25964
|
-
import { Command as
|
|
26459
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
25965
26460
|
var WhoamiCommand = class extends M8tCommand {
|
|
25966
26461
|
static paths = [["whoami"]];
|
|
25967
|
-
static usage =
|
|
26462
|
+
static usage = Command49.Usage({
|
|
25968
26463
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
25969
26464
|
});
|
|
25970
|
-
output =
|
|
25971
|
-
verbose =
|
|
25972
|
-
subscription =
|
|
26465
|
+
output = Option46.String("--output");
|
|
26466
|
+
verbose = Option46.Boolean("--verbose", false);
|
|
26467
|
+
subscription = Option46.String("--subscription", {
|
|
25973
26468
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
25974
26469
|
});
|
|
25975
|
-
resourceGroup =
|
|
26470
|
+
resourceGroup = Option46.String("--resource-group", {
|
|
25976
26471
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
25977
26472
|
});
|
|
25978
26473
|
async executeCommand() {
|
|
@@ -26037,7 +26532,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
26037
26532
|
};
|
|
26038
26533
|
|
|
26039
26534
|
// src/commands/status.ts
|
|
26040
|
-
import { Command as
|
|
26535
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
26041
26536
|
|
|
26042
26537
|
// src/lib/azd.ts
|
|
26043
26538
|
init_errors();
|
|
@@ -26102,10 +26597,10 @@ async function resolveLocalContext() {
|
|
|
26102
26597
|
// src/commands/status.ts
|
|
26103
26598
|
var StatusCommand = class extends M8tCommand {
|
|
26104
26599
|
static paths = [["status"]];
|
|
26105
|
-
static usage =
|
|
26600
|
+
static usage = Command50.Usage({
|
|
26106
26601
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
26107
26602
|
});
|
|
26108
|
-
output =
|
|
26603
|
+
output = Option47.String("--output");
|
|
26109
26604
|
async executeCommand() {
|
|
26110
26605
|
const mode = resolveOutputMode(
|
|
26111
26606
|
this.output,
|
|
@@ -26143,8 +26638,8 @@ var StatusCommand = class extends M8tCommand {
|
|
|
26143
26638
|
};
|
|
26144
26639
|
|
|
26145
26640
|
// src/commands/doctor.ts
|
|
26146
|
-
import { Command as
|
|
26147
|
-
import { DefaultAzureCredential as
|
|
26641
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
26642
|
+
import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
|
|
26148
26643
|
import * as fs30 from "fs";
|
|
26149
26644
|
import * as os12 from "os";
|
|
26150
26645
|
import * as path33 from "path";
|
|
@@ -26691,12 +27186,12 @@ function probeLegacyStateDir() {
|
|
|
26691
27186
|
}
|
|
26692
27187
|
var DoctorCommand = class extends M8tCommand {
|
|
26693
27188
|
static paths = [["doctor"]];
|
|
26694
|
-
static usage =
|
|
27189
|
+
static usage = Command51.Usage({
|
|
26695
27190
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
26696
27191
|
});
|
|
26697
|
-
output =
|
|
26698
|
-
agent =
|
|
26699
|
-
resourceGroup =
|
|
27192
|
+
output = Option48.String("--output");
|
|
27193
|
+
agent = Option48.String("--agent");
|
|
27194
|
+
resourceGroup = Option48.String("--resource-group", {
|
|
26700
27195
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
26701
27196
|
});
|
|
26702
27197
|
async executeCommand() {
|
|
@@ -26769,7 +27264,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26769
27264
|
let kvStatus = 0;
|
|
26770
27265
|
if (kv) {
|
|
26771
27266
|
try {
|
|
26772
|
-
const token = await new
|
|
27267
|
+
const token = await new DefaultAzureCredential25().getToken("https://vault.azure.net/.default");
|
|
26773
27268
|
const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
|
|
26774
27269
|
headers: { Authorization: `Bearer ${token.token}` }
|
|
26775
27270
|
});
|
|
@@ -26788,7 +27283,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26788
27283
|
emit(checkModelQuota(deployments, usages));
|
|
26789
27284
|
checking("outbound email");
|
|
26790
27285
|
try {
|
|
26791
|
-
const credential2 = new
|
|
27286
|
+
const credential2 = new DefaultAzureCredential25();
|
|
26792
27287
|
const outcome = platformRg && platformSub ? await readStampOutcome({ credential: credential2, subscriptionId: platformSub, resourceGroup: platformRg }).catch(
|
|
26793
27288
|
() => ({ source: "unreadable" })
|
|
26794
27289
|
) : { source: "unreadable" };
|
|
@@ -26843,7 +27338,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26843
27338
|
if (typeof this.agent === "string" && this.agent) {
|
|
26844
27339
|
checking(`delivery grant for ${this.agent}`);
|
|
26845
27340
|
try {
|
|
26846
|
-
const credential2 = new
|
|
27341
|
+
const credential2 = new DefaultAzureCredential25();
|
|
26847
27342
|
const cur = await getAgentVersion({
|
|
26848
27343
|
credential: credential2,
|
|
26849
27344
|
projectEndpoint: foundry.projectEndpoint,
|
|
@@ -26886,11 +27381,11 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26886
27381
|
};
|
|
26887
27382
|
|
|
26888
27383
|
// src/commands/prereqs.ts
|
|
26889
|
-
import { Command as
|
|
27384
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
26890
27385
|
init_errors();
|
|
26891
27386
|
|
|
26892
27387
|
// src/lib/prereq-deps.ts
|
|
26893
|
-
import { DefaultAzureCredential as
|
|
27388
|
+
import { DefaultAzureCredential as DefaultAzureCredential26 } from "@azure/identity";
|
|
26894
27389
|
|
|
26895
27390
|
// src/lib/bootstrap-preflight.ts
|
|
26896
27391
|
var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
|
|
@@ -27002,7 +27497,7 @@ function buildPrereqDeps(opts = {}) {
|
|
|
27002
27497
|
probeRedirectUri,
|
|
27003
27498
|
fixFoundryAccess,
|
|
27004
27499
|
fixKeyVaultAccess,
|
|
27005
|
-
credential: () => credentialSingleton2 ??= new
|
|
27500
|
+
credential: () => credentialSingleton2 ??= new DefaultAzureCredential26()
|
|
27006
27501
|
};
|
|
27007
27502
|
}
|
|
27008
27503
|
|
|
@@ -27592,7 +28087,7 @@ function renderVerdict(v) {
|
|
|
27592
28087
|
}
|
|
27593
28088
|
var PrereqsCommand = class extends M8tCommand {
|
|
27594
28089
|
static paths = [["prereqs"]];
|
|
27595
|
-
static usage =
|
|
28090
|
+
static usage = Command52.Usage({
|
|
27596
28091
|
description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
|
|
27597
28092
|
details: "Runs one of two phases. With no platform discoverable it checks INSTALL prerequisites: your Azure sign-in, subscription and directory rights, resource-provider registrations, soft-deleted Cognitive Services accounts (they hold their model quota until purged), and model quota. With a platform live it checks USAGE prerequisites for the signed-in account: the sign-in redirect URI, Foundry data-plane access, and Key Vault secrets access. Pass --fix to repair what is repairable, and --fix --for <upn> to set a teammate up (usage phase only). Read-only without --fix.",
|
|
27598
28093
|
examples: [
|
|
@@ -27603,15 +28098,15 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
27603
28098
|
["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
|
|
27604
28099
|
]
|
|
27605
28100
|
});
|
|
27606
|
-
fix =
|
|
27607
|
-
for_ =
|
|
27608
|
-
phase =
|
|
27609
|
-
region =
|
|
27610
|
-
model =
|
|
27611
|
-
clientId =
|
|
27612
|
-
subscription =
|
|
27613
|
-
resourceGroup =
|
|
27614
|
-
output =
|
|
28101
|
+
fix = Option49.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
|
|
28102
|
+
for_ = Option49.String("--for", { description: "UPN or object id of another person. Usage phase only." });
|
|
28103
|
+
phase = Option49.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
|
|
28104
|
+
region = Option49.String("--region", { description: "Target region. Enables the install-phase quota check, and judges soft-deleted accounts against the region you will install into (without it, they only warn)." });
|
|
28105
|
+
model = Option49.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
|
|
28106
|
+
clientId = Option49.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
|
|
28107
|
+
subscription = Option49.String("--subscription");
|
|
28108
|
+
resourceGroup = Option49.String("--resource-group");
|
|
28109
|
+
output = Option49.String("--output");
|
|
27615
28110
|
async executeCommand() {
|
|
27616
28111
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
27617
28112
|
const json = mode === "json";
|
|
@@ -27667,7 +28162,7 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
27667
28162
|
};
|
|
27668
28163
|
|
|
27669
28164
|
// src/commands/switch.ts
|
|
27670
|
-
import { Command as
|
|
28165
|
+
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
27671
28166
|
|
|
27672
28167
|
// src/lib/profiles.ts
|
|
27673
28168
|
import * as fs31 from "fs/promises";
|
|
@@ -27807,14 +28302,14 @@ async function profileSwitch(name, asName) {
|
|
|
27807
28302
|
init_errors();
|
|
27808
28303
|
var SwitchCommand = class extends M8tCommand {
|
|
27809
28304
|
static paths = [["switch"]];
|
|
27810
|
-
static usage =
|
|
28305
|
+
static usage = Command53.Usage({
|
|
27811
28306
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
27812
28307
|
});
|
|
27813
|
-
profile =
|
|
27814
|
-
subscription =
|
|
27815
|
-
list =
|
|
27816
|
-
as =
|
|
27817
|
-
output =
|
|
28308
|
+
profile = Option50.String({ required: false });
|
|
28309
|
+
subscription = Option50.String("--subscription");
|
|
28310
|
+
list = Option50.Boolean("--list", false);
|
|
28311
|
+
as = Option50.String("--as");
|
|
28312
|
+
output = Option50.String("--output");
|
|
27818
28313
|
async executeCommand() {
|
|
27819
28314
|
const mode = resolveOutputMode(
|
|
27820
28315
|
this.output,
|
|
@@ -27871,7 +28366,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
27871
28366
|
|
|
27872
28367
|
// src/commands/open.ts
|
|
27873
28368
|
import { spawn as spawn5 } from "child_process";
|
|
27874
|
-
import { Command as
|
|
28369
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
27875
28370
|
|
|
27876
28371
|
// src/lib/open-targets.ts
|
|
27877
28372
|
init_errors();
|
|
@@ -27917,14 +28412,14 @@ function openUrl(url) {
|
|
|
27917
28412
|
}
|
|
27918
28413
|
var OpenCommand = class extends M8tCommand {
|
|
27919
28414
|
static paths = [["open"]];
|
|
27920
|
-
static usage =
|
|
28415
|
+
static usage = Command54.Usage({
|
|
27921
28416
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
27922
28417
|
details: "Targets: webapp (deployed app, default) | foundry (ai.azure.com) | portal (resource group in the Azure portal). Pass --print to emit the URL instead of launching a browser (also the default when stdout isn't a TTY)."
|
|
27923
28418
|
});
|
|
27924
|
-
target =
|
|
27925
|
-
print =
|
|
27926
|
-
output =
|
|
27927
|
-
resourceGroup =
|
|
28419
|
+
target = Option51.String({ required: false });
|
|
28420
|
+
print = Option51.Boolean("--print", false);
|
|
28421
|
+
output = Option51.String("--output");
|
|
28422
|
+
resourceGroup = Option51.String("--resource-group", {
|
|
27928
28423
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
27929
28424
|
});
|
|
27930
28425
|
async executeCommand() {
|
|
@@ -27968,7 +28463,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
27968
28463
|
};
|
|
27969
28464
|
|
|
27970
28465
|
// src/commands/dream/run.ts
|
|
27971
|
-
import { Command as
|
|
28466
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
27972
28467
|
import { AzureCliCredential } from "@azure/identity";
|
|
27973
28468
|
import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
27974
28469
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
@@ -30368,28 +30863,28 @@ function redactTranscripts(input) {
|
|
|
30368
30863
|
}
|
|
30369
30864
|
var DreamRunCommand = class extends M8tCommand {
|
|
30370
30865
|
static paths = [["dream", "run"]];
|
|
30371
|
-
static usage =
|
|
30866
|
+
static usage = Command55.Usage({
|
|
30372
30867
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
|
|
30373
30868
|
details: "Builds AzureCliCredential + resolves the Foundry project, ledger table, and Log Analytics workspace, runs the consumption pipeline, and prints the skip-ledger, the partition invariant, and harvest stats. Transcripts are metadata-only unless --show-transcripts is passed.\n\nThe bare command is READ-ONLY. --live takes the other branch: a real model call and a commit to the worker's brain repo through a minted GitHub App token."
|
|
30374
30869
|
});
|
|
30375
|
-
worker =
|
|
30870
|
+
worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
30376
30871
|
// Opting IN to the side effects, rather than opting out of them. This command's
|
|
30377
30872
|
// own help has always described a dry run, but the bare invocation used to take
|
|
30378
30873
|
// the live branch — a real model call and a commit to the brain repo — so anyone
|
|
30379
30874
|
// acting on `--help` got the opposite of what they read.
|
|
30380
|
-
live =
|
|
30381
|
-
dryRun =
|
|
30382
|
-
since =
|
|
30383
|
-
reset =
|
|
30384
|
-
showTranscripts =
|
|
30875
|
+
live = Option52.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
|
|
30876
|
+
dryRun = Option52.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
|
|
30877
|
+
since = Option52.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
|
|
30878
|
+
reset = Option52.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
|
|
30879
|
+
showTranscripts = Option52.Boolean("--show-transcripts", false, {
|
|
30385
30880
|
description: "Print transcript bodies (default: metadata only)."
|
|
30386
30881
|
});
|
|
30387
|
-
subscription =
|
|
30388
|
-
endpoint =
|
|
30389
|
-
storageAccount =
|
|
30882
|
+
subscription = Option52.String("--subscription");
|
|
30883
|
+
endpoint = Option52.String("--endpoint");
|
|
30884
|
+
storageAccount = Option52.String("--storage-account", {
|
|
30390
30885
|
description: "Ledger storage account name (skips tag-based discovery)"
|
|
30391
30886
|
});
|
|
30392
|
-
output =
|
|
30887
|
+
output = Option52.String("--output");
|
|
30393
30888
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
30394
30889
|
deps;
|
|
30395
30890
|
async executeCommand() {
|
|
@@ -30740,7 +31235,7 @@ function defaultDeps(overrides) {
|
|
|
30740
31235
|
|
|
30741
31236
|
// src/commands/conversations/sweep.ts
|
|
30742
31237
|
import { createHash as createHash5 } from "crypto";
|
|
30743
|
-
import { Command as
|
|
31238
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
30744
31239
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
30745
31240
|
import { TableClient as TableClient8 } from "@azure/data-tables";
|
|
30746
31241
|
import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
|
|
@@ -30866,7 +31361,7 @@ function defaultDeps2() {
|
|
|
30866
31361
|
}
|
|
30867
31362
|
var ConversationsSweepCommand = class extends M8tCommand {
|
|
30868
31363
|
static paths = [["conversations", "sweep"]];
|
|
30869
|
-
static usage =
|
|
31364
|
+
static usage = Command56.Usage({
|
|
30870
31365
|
category: "Conversations",
|
|
30871
31366
|
description: "Delete expired public-visitor conversations (dry run by default)",
|
|
30872
31367
|
details: `
|
|
@@ -30882,22 +31377,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
30882
31377
|
["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
|
|
30883
31378
|
]
|
|
30884
31379
|
});
|
|
30885
|
-
doDelete =
|
|
31380
|
+
doDelete = Option53.Boolean("--delete", false, {
|
|
30886
31381
|
description: "Perform deletions (without this flag the command only reports)"
|
|
30887
31382
|
});
|
|
30888
|
-
principal =
|
|
31383
|
+
principal = Option53.String("--principal", {
|
|
30889
31384
|
description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
|
|
30890
31385
|
});
|
|
30891
|
-
max =
|
|
30892
|
-
graceDays =
|
|
31386
|
+
max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
|
|
31387
|
+
graceDays = Option53.String("--grace-days", "14", {
|
|
30893
31388
|
description: "Days past the 30-day key life before a conversation is eligible"
|
|
30894
31389
|
});
|
|
30895
|
-
lookbackDays =
|
|
31390
|
+
lookbackDays = Option53.String("--lookback-days", "180", {
|
|
30896
31391
|
description: "How far back to scan ledger activity for candidates"
|
|
30897
31392
|
});
|
|
30898
|
-
subscription =
|
|
30899
|
-
endpoint =
|
|
30900
|
-
storageAccount =
|
|
31393
|
+
subscription = Option53.String("--subscription", { description: "Azure subscription id override" });
|
|
31394
|
+
endpoint = Option53.String("--endpoint", { description: "Foundry project endpoint override" });
|
|
31395
|
+
storageAccount = Option53.String("--storage-account", {
|
|
30901
31396
|
description: "Ledger storage account name (skips tag-based discovery)"
|
|
30902
31397
|
});
|
|
30903
31398
|
deps = defaultDeps2();
|
|
@@ -31025,7 +31520,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
31025
31520
|
};
|
|
31026
31521
|
|
|
31027
31522
|
// src/commands/foundry/create.ts
|
|
31028
|
-
import { Command as
|
|
31523
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
31029
31524
|
|
|
31030
31525
|
// src/lib/foundry-create.ts
|
|
31031
31526
|
init_errors();
|
|
@@ -31263,7 +31758,7 @@ async function createFoundryProject(args) {
|
|
|
31263
31758
|
init_errors();
|
|
31264
31759
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
31265
31760
|
static paths = [["foundry", "create"]];
|
|
31266
|
-
static usage =
|
|
31761
|
+
static usage = Command57.Usage({
|
|
31267
31762
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
31268
31763
|
details: "Non-interactive and idempotent. Creates the AIServices account (custom subdomain + project management), a project, and a model deployment (default gpt-4.1-mini @ capacity 50). Region must be hosted-agent-eligible. Emits the project endpoint as structured output. Re-run is a clean no-op (account/project skipped if present; deployment capacity converges UP, never down).",
|
|
31269
31764
|
examples: [
|
|
@@ -31272,16 +31767,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
31272
31767
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
31273
31768
|
]
|
|
31274
31769
|
});
|
|
31275
|
-
resourceGroup =
|
|
31276
|
-
location =
|
|
31277
|
-
account =
|
|
31278
|
-
project =
|
|
31279
|
-
model =
|
|
31280
|
-
modelVersion =
|
|
31281
|
-
capacity =
|
|
31282
|
-
subscription =
|
|
31283
|
-
skipQuotaCheck =
|
|
31284
|
-
output =
|
|
31770
|
+
resourceGroup = Option54.String("--resource-group");
|
|
31771
|
+
location = Option54.String("--location");
|
|
31772
|
+
account = Option54.String("--account");
|
|
31773
|
+
project = Option54.String("--project", "m8t");
|
|
31774
|
+
model = Option54.String("--model", "gpt-4.1-mini");
|
|
31775
|
+
modelVersion = Option54.String("--model-version", "2025-04-14");
|
|
31776
|
+
capacity = Option54.String("--capacity", "50");
|
|
31777
|
+
subscription = Option54.String("--subscription");
|
|
31778
|
+
skipQuotaCheck = Option54.Boolean("--skip-quota-check", false);
|
|
31779
|
+
output = Option54.String("--output");
|
|
31285
31780
|
async executeCommand() {
|
|
31286
31781
|
const mode = resolveOutputMode(
|
|
31287
31782
|
this.output,
|
|
@@ -31353,22 +31848,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
31353
31848
|
};
|
|
31354
31849
|
|
|
31355
31850
|
// src/commands/foundry/await-ready.ts
|
|
31356
|
-
import { Command as
|
|
31851
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
31357
31852
|
import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
|
|
31358
31853
|
init_errors();
|
|
31359
31854
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
31360
31855
|
static paths = [["foundry", "await-ready"]];
|
|
31361
|
-
static usage =
|
|
31856
|
+
static usage = Command58.Usage({
|
|
31362
31857
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
31363
31858
|
details: "Probes the project (GET /agents) until it returns 200 on a few consecutive tries, or fails clearly after a bounded budget. A newly-created account can serve intermittent 404 'Project not found' for minutes; run this after 'foundry create' and before deploying agents so the worker phase doesn't catch the unstable window.",
|
|
31364
31859
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
31365
31860
|
});
|
|
31366
|
-
endpoint =
|
|
31367
|
-
consecutive =
|
|
31368
|
-
attempts =
|
|
31369
|
-
interval =
|
|
31370
|
-
subscription =
|
|
31371
|
-
output =
|
|
31861
|
+
endpoint = Option55.String("--endpoint");
|
|
31862
|
+
consecutive = Option55.String("--consecutive", "3");
|
|
31863
|
+
attempts = Option55.String("--attempts", "60");
|
|
31864
|
+
interval = Option55.String("--interval", "5");
|
|
31865
|
+
subscription = Option55.String("--subscription");
|
|
31866
|
+
output = Option55.String("--output");
|
|
31372
31867
|
async executeCommand() {
|
|
31373
31868
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
31374
31869
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -31402,7 +31897,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
31402
31897
|
};
|
|
31403
31898
|
|
|
31404
31899
|
// src/commands/bootstrap/preflight.ts
|
|
31405
|
-
import { Command as
|
|
31900
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
31406
31901
|
|
|
31407
31902
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
31408
31903
|
var EVENT_TIERS = {
|
|
@@ -31454,7 +31949,7 @@ function preflightRenderable(results) {
|
|
|
31454
31949
|
}
|
|
31455
31950
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
31456
31951
|
static paths = [["bootstrap", "preflight"]];
|
|
31457
|
-
static usage =
|
|
31952
|
+
static usage = Command59.Usage({
|
|
31458
31953
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
31459
31954
|
details: "Step 1 of `m8t bootstrap`. Prints an unmissable admin-credentials notice, then checks: Owner or User Access Administrator at subscription scope (hard requirement), directory-admin capability to register the app (or pass --client-id), every Azure resource provider the install uses (registering any that are missing), soft-deleted Cognitive Services accounts (they keep their model quota until purged, so quota can read free and the model deployment still fail), and \u2014 with --location \u2014 model quota in the target region. Exits non-zero with the exact failing check + remedy. `m8t prereqs` runs the same substrate checks on their own.",
|
|
31460
31955
|
examples: [
|
|
@@ -31463,9 +31958,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
31463
31958
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
31464
31959
|
]
|
|
31465
31960
|
});
|
|
31466
|
-
clientId =
|
|
31467
|
-
subscription =
|
|
31468
|
-
location =
|
|
31961
|
+
clientId = Option56.String("--client-id");
|
|
31962
|
+
subscription = Option56.String("--subscription");
|
|
31963
|
+
location = Option56.String("--location", {
|
|
31469
31964
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
31470
31965
|
});
|
|
31471
31966
|
async executeCommand() {
|
|
@@ -31565,7 +32060,7 @@ ${colors.error(" " + why)}
|
|
|
31565
32060
|
import * as fs34 from "fs";
|
|
31566
32061
|
import * as os15 from "os";
|
|
31567
32062
|
import * as path37 from "path";
|
|
31568
|
-
import { Command as
|
|
32063
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
31569
32064
|
init_errors();
|
|
31570
32065
|
|
|
31571
32066
|
// src/lib/bootstrap-mi.ts
|
|
@@ -31930,12 +32425,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
|
|
|
31930
32425
|
// src/commands/bootstrap/launch.ts
|
|
31931
32426
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
31932
32427
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
31933
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
32428
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.74";
|
|
31934
32429
|
var ACI_NAME = "m8t-installer";
|
|
31935
32430
|
var MI_NAME = "m8t-installer-mi";
|
|
31936
32431
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
31937
32432
|
static paths = [["bootstrap", "launch"]];
|
|
31938
|
-
static usage =
|
|
32433
|
+
static usage = Command60.Usage({
|
|
31939
32434
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
31940
32435
|
details: "Step 2 of `m8t bootstrap` (run after `preflight`). Ensures the resource group, creates the m8t app registration (or uses --client-id), creates a user-assigned managed identity granted Owner at subscription scope, and launches the published m8t-installer image as an ACI run-to-completion job under that identity. Writes ~/.m8t/bootstrap.json for `status` and `reap`, and threads your Azure object id so the installer can grant you the platform's data-plane roles itself.",
|
|
31941
32436
|
examples: [
|
|
@@ -31946,31 +32441,31 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
31946
32441
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
31947
32442
|
]
|
|
31948
32443
|
});
|
|
31949
|
-
location =
|
|
31950
|
-
resourceGroup =
|
|
31951
|
-
clientId =
|
|
31952
|
-
subscription =
|
|
31953
|
-
installerTag =
|
|
32444
|
+
location = Option57.String("--location");
|
|
32445
|
+
resourceGroup = Option57.String("--resource-group");
|
|
32446
|
+
clientId = Option57.String("--client-id");
|
|
32447
|
+
subscription = Option57.String("--subscription");
|
|
32448
|
+
installerTag = Option57.String("--installer-tag");
|
|
31954
32449
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
31955
32450
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
31956
32451
|
// Wins over --installer-tag / the pinned default.
|
|
31957
|
-
installerImage =
|
|
31958
|
-
gatewayImageRef =
|
|
31959
|
-
githubAppCreds =
|
|
31960
|
-
contactEmail =
|
|
31961
|
-
company =
|
|
32452
|
+
installerImage = Option57.String("--installer-image");
|
|
32453
|
+
gatewayImageRef = Option57.String("--gateway-image-ref");
|
|
32454
|
+
githubAppCreds = Option57.String("--github-app-creds");
|
|
32455
|
+
contactEmail = Option57.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
32456
|
+
company = Option57.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
31962
32457
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
31963
32458
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
31964
32459
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
31965
32460
|
// the target; --resource-group is what CHOOSES it.
|
|
31966
|
-
reinstallInto =
|
|
31967
|
-
org =
|
|
31968
|
-
noBrains =
|
|
32461
|
+
reinstallInto = Option57.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
|
|
32462
|
+
org = Option57.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
|
|
32463
|
+
noBrains = Option57.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
|
|
31969
32464
|
// The opt-out TELEMETRY.md names. Without it there is no way to decline at
|
|
31970
32465
|
// install time — the ACI env is built entirely from these options, so a
|
|
31971
32466
|
// founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
|
|
31972
32467
|
// privacy notice that documents a control has to be a control that exists.
|
|
31973
|
-
foundryTracing =
|
|
32468
|
+
foundryTracing = Option57.String("--foundry-tracing", { description: "Where agent traces go: project (default, your own App Insights) | account (legacy shared) | skip (no tracing \u2014 you lose the record of what your agents did)." });
|
|
31974
32469
|
async executeCommand() {
|
|
31975
32470
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
31976
32471
|
if (!location) {
|
|
@@ -32132,7 +32627,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
32132
32627
|
};
|
|
32133
32628
|
|
|
32134
32629
|
// src/commands/bootstrap/status.ts
|
|
32135
|
-
import { Command as
|
|
32630
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
32136
32631
|
init_errors();
|
|
32137
32632
|
|
|
32138
32633
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -34309,7 +34804,7 @@ async function uninstallCompanion(options) {
|
|
|
34309
34804
|
// src/commands/companion/install.ts
|
|
34310
34805
|
import * as os19 from "os";
|
|
34311
34806
|
import * as path43 from "path";
|
|
34312
|
-
import { Command as
|
|
34807
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
34313
34808
|
|
|
34314
34809
|
// src/lib/companion-channel.ts
|
|
34315
34810
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -34440,7 +34935,7 @@ Version: ${state.version}
|
|
|
34440
34935
|
}
|
|
34441
34936
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
34442
34937
|
static paths = [["companion", "install"]];
|
|
34443
|
-
static usage =
|
|
34938
|
+
static usage = Command61.Usage({
|
|
34444
34939
|
description: "Install the desktop companions for this user from the release channel.",
|
|
34445
34940
|
examples: [
|
|
34446
34941
|
["Install the released build", "$0 companion install"],
|
|
@@ -34450,10 +34945,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
34450
34945
|
]
|
|
34451
34946
|
]
|
|
34452
34947
|
});
|
|
34453
|
-
from =
|
|
34948
|
+
from = Option58.String("--from", {
|
|
34454
34949
|
description: "A locally staged build directory instead of the released one."
|
|
34455
34950
|
});
|
|
34456
|
-
resourceGroup =
|
|
34951
|
+
resourceGroup = Option58.String("--resource-group", {
|
|
34457
34952
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
34458
34953
|
});
|
|
34459
34954
|
async executeCommand() {
|
|
@@ -34668,7 +35163,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
34668
35163
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
34669
35164
|
// through — it prints a deprecation notice and does the right thing.
|
|
34670
35165
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
34671
|
-
static usage =
|
|
35166
|
+
static usage = Command62.Usage({
|
|
34672
35167
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
34673
35168
|
details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.\n\nOn reaching done under --watch this also completes the local half of the install, which nothing else in the bootstrap path can do: it registers the webapp's sign-in redirect URI (the installer runs as a managed identity with no directory role, and at launch time the gateway FQDN did not exist yet), writes ~/.m8t/repo-root and the gateway discovery cache, and seeds your advisors' brains from the onboarding intake. Re-runnable \u2014 `m8t bootstrap finish` is a deprecated alias that does exactly this on an already-done install.",
|
|
34674
35169
|
examples: [
|
|
@@ -34677,12 +35172,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
34677
35172
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
34678
35173
|
]
|
|
34679
35174
|
});
|
|
34680
|
-
watch =
|
|
34681
|
-
output =
|
|
34682
|
-
repoRoot =
|
|
34683
|
-
finalize =
|
|
34684
|
-
subscription =
|
|
34685
|
-
resourceGroup =
|
|
35175
|
+
watch = Option59.Boolean("--watch", false);
|
|
35176
|
+
output = Option59.String("--output");
|
|
35177
|
+
repoRoot = Option59.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
|
|
35178
|
+
finalize = Option59.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
|
|
35179
|
+
subscription = Option59.String("--subscription");
|
|
35180
|
+
resourceGroup = Option59.String("--resource-group");
|
|
34686
35181
|
async executeCommand() {
|
|
34687
35182
|
const state = await readBootstrapState();
|
|
34688
35183
|
if (!state) {
|
|
@@ -34815,7 +35310,7 @@ function formatStatus(d) {
|
|
|
34815
35310
|
}
|
|
34816
35311
|
|
|
34817
35312
|
// src/commands/bootstrap/reap.ts
|
|
34818
|
-
import { Command as
|
|
35313
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34819
35314
|
init_errors();
|
|
34820
35315
|
|
|
34821
35316
|
// src/lib/bootstrap-reap.ts
|
|
@@ -34909,7 +35404,7 @@ async function reapInstaller(opts) {
|
|
|
34909
35404
|
// src/commands/bootstrap/reap.ts
|
|
34910
35405
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
34911
35406
|
static paths = [["bootstrap", "reap"]];
|
|
34912
|
-
static usage =
|
|
35407
|
+
static usage = Command63.Usage({
|
|
34913
35408
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
34914
35409
|
details: "Runs locally on the 'done' signal (the installer can't delete its own identity). The platform RG and the gateway's own assignments persist. A failed install is left intact for diagnosis unless --force.\n\n--sweep-orphans is a DIFFERENT and much broader mode: instead of reaping this install, it scans the WHOLE SUBSCRIPTION for m8t role assignments left behind by installs whose resources are gone \u2014 orphaned installer Owner-at-subscription-scope grants and orphaned gateway subscription-scope roles. It is a dry run that only lists what it found unless you also pass --yes, which deletes them.",
|
|
34915
35410
|
examples: [
|
|
@@ -34919,9 +35414,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
34919
35414
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
34920
35415
|
]
|
|
34921
35416
|
});
|
|
34922
|
-
force =
|
|
34923
|
-
sweepOrphans =
|
|
34924
|
-
yes =
|
|
35417
|
+
force = Option60.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
|
|
35418
|
+
sweepOrphans = Option60.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
|
|
35419
|
+
yes = Option60.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
|
|
34925
35420
|
async executeCommand() {
|
|
34926
35421
|
if (this.sweepOrphans === true) {
|
|
34927
35422
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -35015,7 +35510,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
35015
35510
|
};
|
|
35016
35511
|
|
|
35017
35512
|
// src/commands/bootstrap/ui.ts
|
|
35018
|
-
import { Command as
|
|
35513
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
35019
35514
|
|
|
35020
35515
|
// src/lib/bootstrap-ui.ts
|
|
35021
35516
|
import * as fs40 from "fs";
|
|
@@ -35090,7 +35585,7 @@ function renderDeprecationNotice() {
|
|
|
35090
35585
|
}
|
|
35091
35586
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
35092
35587
|
static paths = [["bootstrap", "ui"]];
|
|
35093
|
-
static usage =
|
|
35588
|
+
static usage = Command64.Usage({
|
|
35094
35589
|
description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
|
|
35095
35590
|
details: [
|
|
35096
35591
|
"The local onboarding chat has been retired. Your details are collected by",
|
|
@@ -35110,14 +35605,14 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
35110
35605
|
// Accepted and ignored, deliberately: removing them would turn an old script's
|
|
35111
35606
|
// harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
|
|
35112
35607
|
// the whole surface when the rewrite lands.
|
|
35113
|
-
repoRoot =
|
|
35114
|
-
port =
|
|
35115
|
-
endpoint =
|
|
35116
|
-
prepOnly =
|
|
35117
|
-
skipInstall =
|
|
35118
|
-
foreground =
|
|
35119
|
-
voice =
|
|
35120
|
-
stop =
|
|
35608
|
+
repoRoot = Option61.String("--repo-root", { description: "Ignored (deprecated)." });
|
|
35609
|
+
port = Option61.String("--port", "3000", { description: "Ignored (deprecated)." });
|
|
35610
|
+
endpoint = Option61.String("--endpoint", { description: "Ignored (deprecated)." });
|
|
35611
|
+
prepOnly = Option61.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
|
|
35612
|
+
skipInstall = Option61.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
|
|
35613
|
+
foreground = Option61.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
|
|
35614
|
+
voice = Option61.Boolean("--voice", false, { description: "Ignored (deprecated)." });
|
|
35615
|
+
stop = Option61.Boolean("--stop", false, {
|
|
35121
35616
|
description: "Shut down a local chat UI left running by an earlier version of this command."
|
|
35122
35617
|
});
|
|
35123
35618
|
// Not `async`: there is nothing left to await. Everything this command used to
|
|
@@ -35139,7 +35634,7 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
35139
35634
|
|
|
35140
35635
|
// src/commands/bootstrap/profile.ts
|
|
35141
35636
|
import * as readline3 from "readline/promises";
|
|
35142
|
-
import { Command as
|
|
35637
|
+
import { Command as Command65, Option as Option62 } from "clipanion";
|
|
35143
35638
|
|
|
35144
35639
|
// src/lib/profile-collect.ts
|
|
35145
35640
|
init_errors();
|
|
@@ -35308,7 +35803,7 @@ async function openChatInvite(deps = {}) {
|
|
|
35308
35803
|
// src/commands/bootstrap/profile.ts
|
|
35309
35804
|
var BootstrapProfileCommand = class extends M8tCommand {
|
|
35310
35805
|
static paths = [["bootstrap", "profile"]];
|
|
35311
|
-
static usage =
|
|
35806
|
+
static usage = Command65.Usage({
|
|
35312
35807
|
description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
|
|
35313
35808
|
details: [
|
|
35314
35809
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
|
|
@@ -35328,12 +35823,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
|
|
|
35328
35823
|
["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
|
|
35329
35824
|
]
|
|
35330
35825
|
});
|
|
35331
|
-
founderEmail =
|
|
35332
|
-
advisorName =
|
|
35333
|
-
advisorEmail =
|
|
35334
|
-
noAdvisor =
|
|
35335
|
-
noChat =
|
|
35336
|
-
print =
|
|
35826
|
+
founderEmail = Option62.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
|
|
35827
|
+
advisorName = Option62.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
|
|
35828
|
+
advisorEmail = Option62.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
|
|
35829
|
+
noAdvisor = Option62.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
|
|
35830
|
+
noChat = Option62.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
|
|
35831
|
+
print = Option62.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
|
|
35337
35832
|
async executeCommand() {
|
|
35338
35833
|
const stdin = this.context.stdin;
|
|
35339
35834
|
const stdout = this.context.stdout;
|
|
@@ -35426,10 +35921,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
|
|
|
35426
35921
|
};
|
|
35427
35922
|
|
|
35428
35923
|
// src/commands/bootstrap/seed-profile.ts
|
|
35429
|
-
import { Command as
|
|
35924
|
+
import { Command as Command66, Option as Option63 } from "clipanion";
|
|
35430
35925
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
35431
35926
|
static paths = [["bootstrap", "seed-profile"]];
|
|
35432
|
-
static usage =
|
|
35927
|
+
static usage = Command66.Usage({
|
|
35433
35928
|
description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
|
|
35434
35929
|
details: [
|
|
35435
35930
|
"Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
|
|
@@ -35446,11 +35941,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
35446
35941
|
["Seed now (idempotent)", "$0 bootstrap seed-profile"]
|
|
35447
35942
|
]
|
|
35448
35943
|
});
|
|
35449
|
-
endpoint =
|
|
35450
|
-
brain =
|
|
35451
|
-
watch =
|
|
35452
|
-
timeout =
|
|
35453
|
-
githubAppCreds =
|
|
35944
|
+
endpoint = Option63.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
35945
|
+
brain = Option63.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
35946
|
+
watch = Option63.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
35947
|
+
timeout = Option63.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
35948
|
+
githubAppCreds = Option63.String("--github-app-creds");
|
|
35454
35949
|
async executeCommand() {
|
|
35455
35950
|
const ctx = await resolveSeedContext({
|
|
35456
35951
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -35538,7 +36033,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
35538
36033
|
import * as fs41 from "fs";
|
|
35539
36034
|
import * as os22 from "os";
|
|
35540
36035
|
import * as path46 from "path";
|
|
35541
|
-
import { Command as
|
|
36036
|
+
import { Command as Command67, Option as Option64 } from "clipanion";
|
|
35542
36037
|
init_errors();
|
|
35543
36038
|
|
|
35544
36039
|
// src/lib/telemetry-enroll.ts
|
|
@@ -35620,7 +36115,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
35620
36115
|
}
|
|
35621
36116
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
35622
36117
|
static paths = [["telemetry", "enroll"]];
|
|
35623
|
-
static usage =
|
|
36118
|
+
static usage = Command67.Usage({
|
|
35624
36119
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
35625
36120
|
details: "Generates an instance id + ingest key from the m8t telemetry ingest and stores the key in your platform Key Vault, the same place the installer writes it on a fresh install. Your installation is identified only by that random instance id \u2014 no contact, company, or subscription details are sent unless you pass them explicitly. Idempotent: refuses if a key already exists.",
|
|
35626
36121
|
examples: [
|
|
@@ -35628,11 +36123,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
35628
36123
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
35629
36124
|
]
|
|
35630
36125
|
});
|
|
35631
|
-
company =
|
|
35632
|
-
contactEmail =
|
|
35633
|
-
subscription =
|
|
35634
|
-
resourceGroup =
|
|
35635
|
-
force =
|
|
36126
|
+
company = Option64.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
36127
|
+
contactEmail = Option64.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
36128
|
+
subscription = Option64.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
36129
|
+
resourceGroup = Option64.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
36130
|
+
force = Option64.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
35636
36131
|
async executeCommand() {
|
|
35637
36132
|
const account = await getAzAccount();
|
|
35638
36133
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -35682,7 +36177,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
35682
36177
|
};
|
|
35683
36178
|
|
|
35684
36179
|
// src/commands/companion/bridge.ts
|
|
35685
|
-
import { Command as
|
|
36180
|
+
import { Command as Command68, Option as Option65 } from "clipanion";
|
|
35686
36181
|
|
|
35687
36182
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
35688
36183
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -37149,14 +37644,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
37149
37644
|
return 3;
|
|
37150
37645
|
}
|
|
37151
37646
|
}
|
|
37152
|
-
var CompanionBridgeCommand = class extends
|
|
37647
|
+
var CompanionBridgeCommand = class extends Command68 {
|
|
37153
37648
|
static paths = [["companion", "_bridge"]];
|
|
37154
37649
|
/**
|
|
37155
37650
|
* One process serving many requests instead of one per request, so the
|
|
37156
37651
|
* session keeps its authenticated context between them. A CLI predating the
|
|
37157
37652
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
37158
37653
|
*/
|
|
37159
|
-
serve =
|
|
37654
|
+
serve = Option65.Boolean("--serve", false);
|
|
37160
37655
|
async execute() {
|
|
37161
37656
|
if (this.serve) {
|
|
37162
37657
|
return runCompanionBridgeServe(
|
|
@@ -37174,7 +37669,7 @@ var CompanionBridgeCommand = class extends Command67 {
|
|
|
37174
37669
|
};
|
|
37175
37670
|
|
|
37176
37671
|
// src/commands/companion/status.ts
|
|
37177
|
-
import { Command as
|
|
37672
|
+
import { Command as Command69 } from "clipanion";
|
|
37178
37673
|
async function withTimeout(work, ms) {
|
|
37179
37674
|
let timer;
|
|
37180
37675
|
try {
|
|
@@ -37246,7 +37741,7 @@ Run: m8t companion install
|
|
|
37246
37741
|
}
|
|
37247
37742
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
37248
37743
|
static paths = [["companion", "status"]];
|
|
37249
|
-
static usage =
|
|
37744
|
+
static usage = Command69.Usage({
|
|
37250
37745
|
description: "Verify the installed desktop companion without launching it."
|
|
37251
37746
|
});
|
|
37252
37747
|
async executeCommand() {
|
|
@@ -37260,7 +37755,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
37260
37755
|
};
|
|
37261
37756
|
|
|
37262
37757
|
// src/commands/companion/repair.ts
|
|
37263
|
-
import { Command as
|
|
37758
|
+
import { Command as Command70, Option as Option66 } from "clipanion";
|
|
37264
37759
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
37265
37760
|
const state = await repair();
|
|
37266
37761
|
if (state.state === "not-released") {
|
|
@@ -37279,10 +37774,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
37279
37774
|
}
|
|
37280
37775
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
37281
37776
|
static paths = [["companion", "repair"]];
|
|
37282
|
-
static usage =
|
|
37777
|
+
static usage = Command70.Usage({
|
|
37283
37778
|
description: "Restore the desktop companions and start-at-login state."
|
|
37284
37779
|
});
|
|
37285
|
-
resourceGroup =
|
|
37780
|
+
resourceGroup = Option66.String("--resource-group", {
|
|
37286
37781
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
37287
37782
|
});
|
|
37288
37783
|
async executeCommand() {
|
|
@@ -37300,7 +37795,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
37300
37795
|
};
|
|
37301
37796
|
|
|
37302
37797
|
// src/commands/companion/uninstall.ts
|
|
37303
|
-
import { Command as
|
|
37798
|
+
import { Command as Command71 } from "clipanion";
|
|
37304
37799
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
37305
37800
|
const state = await uninstall();
|
|
37306
37801
|
if (state.state !== "not-installed") {
|
|
@@ -37312,7 +37807,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
37312
37807
|
}
|
|
37313
37808
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
37314
37809
|
static paths = [["companion", "uninstall"]];
|
|
37315
|
-
static usage =
|
|
37810
|
+
static usage = Command71.Usage({
|
|
37316
37811
|
description: "Remove only this user's desktop companion installation."
|
|
37317
37812
|
});
|
|
37318
37813
|
async executeCommand() {
|
|
@@ -37373,6 +37868,7 @@ cli.register(PlatformConvergeCommand);
|
|
|
37373
37868
|
cli.register(PlatformClearIntentCommand);
|
|
37374
37869
|
cli.register(PlatformRequestUpdateCommand);
|
|
37375
37870
|
cli.register(PlatformSeedStampCommand);
|
|
37871
|
+
cli.register(PlatformGatewayAdoptCommand);
|
|
37376
37872
|
cli.register(PlatformPolicyCommand);
|
|
37377
37873
|
cli.register(PlatformEnableCostReportCommand);
|
|
37378
37874
|
cli.register(PlatformEmailCommand);
|