@m8t-stack/cli 0.2.105 → 0.2.106
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 +832 -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.106";
|
|
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,414 @@ 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
|
+
const existingRequest = await readApplyRequest(client);
|
|
21792
|
+
const live = await readLiveGatewayDeployment(args.gatewayResourceId);
|
|
21793
|
+
const topologyError = legacyGatewayTopologyError(live, args.gatewayResourceId, args.subscriptionId);
|
|
21794
|
+
if (topologyError) {
|
|
21795
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_REFUSED", message: `Gateway adoption refused: ${topologyError}.` });
|
|
21796
|
+
}
|
|
21797
|
+
if (live.image !== args.expectedImage) {
|
|
21798
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway image changed; expected '${args.expectedImage}', found '${live.image}'.` });
|
|
21799
|
+
}
|
|
21800
|
+
const digest = await resolveLegacyAcrDigest(live, args.subscriptionId);
|
|
21801
|
+
if (digest !== args.expectedDigest) {
|
|
21802
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway digest changed; expected '${args.expectedDigest}', found '${digest}'.` });
|
|
21803
|
+
}
|
|
21804
|
+
const expectedIdentity = live.userAssignedIdentityIds[0];
|
|
21805
|
+
if (args.expectedAcrResourceId.trim() === "" || typeof expectedIdentity !== "string") {
|
|
21806
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id and the generated pull identity are required." });
|
|
21807
|
+
}
|
|
21808
|
+
const imageHost = parseImageRef2(live.image).repo.split("/")[0].split(".")[0];
|
|
21809
|
+
const acrName = args.expectedAcrResourceId.split("/").filter(Boolean).at(-1) ?? "";
|
|
21810
|
+
const expectedAcrId = `/subscriptions/${args.subscriptionId}/resourceGroups/${args.resourceGroup}/providers/Microsoft.ContainerRegistry/registries/${imageHost}`;
|
|
21811
|
+
if (imageHost.toLowerCase() !== acrName.toLowerCase() || lowerId(args.expectedAcrResourceId) !== lowerId(expectedAcrId)) {
|
|
21812
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id does not match the live image registry." });
|
|
21813
|
+
}
|
|
21814
|
+
const existing = stamp.components.gateway.adoption;
|
|
21815
|
+
const samePending = isSamePendingAdoption(
|
|
21816
|
+
existing,
|
|
21817
|
+
live.image,
|
|
21818
|
+
digest,
|
|
21819
|
+
args.expectedAcrResourceId,
|
|
21820
|
+
stamp.components.gateway.state,
|
|
21821
|
+
args.targetGatewayTag,
|
|
21822
|
+
args.targetGatewayDigest,
|
|
21823
|
+
args.rollbackGatewayTag,
|
|
21824
|
+
args.rollbackGatewayDigest
|
|
21825
|
+
);
|
|
21826
|
+
if (existing && !samePending) {
|
|
21827
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CONFLICT", message: "The gateway already carries a different adoption record." });
|
|
21828
|
+
}
|
|
21829
|
+
if (existingRequest && blocksNewIntent(existingRequest.status)) {
|
|
21830
|
+
if (samePending && existingRequest.status === "pending" && existingRequest.target === args.target) {
|
|
21831
|
+
return { outcome: "already-adopted", image: live.image, digest };
|
|
21832
|
+
}
|
|
21833
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_IN_PROGRESS", message: `An update request for ${existingRequest.target} is already ${existingRequest.status}.` });
|
|
21834
|
+
}
|
|
21835
|
+
const now = args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
21836
|
+
const nextStamp = {
|
|
21837
|
+
...stamp,
|
|
21838
|
+
updatedAt: now,
|
|
21839
|
+
components: {
|
|
21840
|
+
...stamp.components,
|
|
21841
|
+
gateway: {
|
|
21842
|
+
...stamp.components.gateway,
|
|
21843
|
+
tag: parseImageRef2(live.image).tag ?? stamp.components.gateway.tag,
|
|
21844
|
+
digest,
|
|
21845
|
+
state: "managed",
|
|
21846
|
+
adoption: existing ?? {
|
|
21847
|
+
version: 1,
|
|
21848
|
+
status: "pending",
|
|
21849
|
+
expectedSourceRef: live.image,
|
|
21850
|
+
expectedSourceDigest: digest,
|
|
21851
|
+
expectedForwardDigest: args.targetGatewayDigest,
|
|
21852
|
+
expectedForwardTag: args.targetGatewayTag,
|
|
21853
|
+
expectedRollbackDigest: args.rollbackGatewayDigest,
|
|
21854
|
+
expectedRollbackTag: args.rollbackGatewayTag,
|
|
21855
|
+
expectedAcrResourceId: args.expectedAcrResourceId,
|
|
21856
|
+
expectedAcrPullIdentityResourceId: expectedIdentity,
|
|
21857
|
+
authorizedAt: now
|
|
21858
|
+
}
|
|
21859
|
+
}
|
|
21860
|
+
}
|
|
21861
|
+
};
|
|
21862
|
+
const intent = newIntent(args.target, { source: "policy", identity: args.operatorIdentity, at: now }, now);
|
|
21863
|
+
const stampWire = stampToEntity(nextStamp);
|
|
21864
|
+
const stampEntity = {
|
|
21865
|
+
...stampWire,
|
|
21866
|
+
partitionKey: String(stampWire.partitionKey),
|
|
21867
|
+
rowKey: String(stampWire.rowKey)
|
|
21868
|
+
};
|
|
21869
|
+
const applyEntity = applyRequestToEntity(intent);
|
|
21870
|
+
const stampAction = ["update", stampEntity, "Replace", { etag: stampEtag }];
|
|
21871
|
+
const requestAction = existingRequest ? ["update", applyEntity, "Replace", { etag: existingRequest.etag }] : ["create", applyEntity];
|
|
21872
|
+
try {
|
|
21873
|
+
await client.submitTransaction([stampAction, requestAction]);
|
|
21874
|
+
} catch (e) {
|
|
21875
|
+
const status = e.statusCode;
|
|
21876
|
+
if (status === 409 || status === 412) {
|
|
21877
|
+
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 });
|
|
21878
|
+
}
|
|
21879
|
+
throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_WRITE_FAILED", message: "Could not atomically record gateway adoption and its update request.", cause: e });
|
|
21880
|
+
}
|
|
21881
|
+
args.onProgress?.(`recorded digest-bound gateway adoption and queued update to ${args.target}.`);
|
|
21882
|
+
return { outcome: samePending ? "already-adopted" : "adopted", image: live.image, digest };
|
|
21883
|
+
}
|
|
21884
|
+
|
|
21424
21885
|
// src/lib/platform-converge.ts
|
|
21425
21886
|
var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
|
|
21426
21887
|
function seedSetRev(manifest) {
|
|
@@ -21484,8 +21945,10 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
|
|
|
21484
21945
|
continue;
|
|
21485
21946
|
}
|
|
21486
21947
|
const from = cur?.tag ?? null;
|
|
21487
|
-
|
|
21488
|
-
|
|
21948
|
+
const adoption = component === "gateway" ? cur?.adoption : void 0;
|
|
21949
|
+
const withAdoption = adoption ? { gatewayAdoption: adoption } : {};
|
|
21950
|
+
if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag, ...withAdoption });
|
|
21951
|
+
else if (from !== img.tag || adoption?.status === "pending") actions.push({ component, reason: "changed", from, to: img.tag, ...withAdoption });
|
|
21489
21952
|
else skipped.push({ component, reason: "up-to-date" });
|
|
21490
21953
|
}
|
|
21491
21954
|
return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
|
|
@@ -21628,10 +22091,20 @@ function seedStamp(prior, manifest, plan) {
|
|
|
21628
22091
|
};
|
|
21629
22092
|
}
|
|
21630
22093
|
async function applyGatewayImage(a, ctx, gatewayResourceId) {
|
|
22094
|
+
const digest = ctx.manifest.components.gateway.digest;
|
|
22095
|
+
if (a.gatewayAdoption) {
|
|
22096
|
+
const adoption = await rollAdoptedGateway({
|
|
22097
|
+
gatewayResourceId,
|
|
22098
|
+
adoption: a.gatewayAdoption,
|
|
22099
|
+
targetTag: a.to,
|
|
22100
|
+
targetDigest: assertGatewayDigest(ctx.manifest.components.gateway),
|
|
22101
|
+
onProgress: ctx.onProgress
|
|
22102
|
+
});
|
|
22103
|
+
return { tag: a.to, digest, state: "managed", adoption };
|
|
22104
|
+
}
|
|
21631
22105
|
const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
|
|
21632
22106
|
const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
|
|
21633
22107
|
const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
|
|
21634
|
-
const digest = ctx.manifest.components.gateway.digest;
|
|
21635
22108
|
const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to, toDigest: digest });
|
|
21636
22109
|
switch (plan.kind) {
|
|
21637
22110
|
case "refuse-byoc":
|
|
@@ -22130,10 +22603,10 @@ async function resolveExecutorAgentName(args) {
|
|
|
22130
22603
|
}
|
|
22131
22604
|
|
|
22132
22605
|
// src/lib/platform-infra-params.ts
|
|
22133
|
-
import { TableClient as
|
|
22606
|
+
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
22134
22607
|
async function openInfraParamsTable(opts) {
|
|
22135
22608
|
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
22136
|
-
return new
|
|
22609
|
+
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
22137
22610
|
}
|
|
22138
22611
|
async function readInfraParams(client) {
|
|
22139
22612
|
try {
|
|
@@ -23006,73 +23479,6 @@ function resolveHeadlessContextFromEnv(env) {
|
|
|
23006
23479
|
};
|
|
23007
23480
|
}
|
|
23008
23481
|
|
|
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
23482
|
// src/lib/rail-preconditions.ts
|
|
23077
23483
|
init_errors();
|
|
23078
23484
|
function assertBakedContentPresent(repoRoot) {
|
|
@@ -23233,7 +23639,7 @@ async function runGate(args, applied, budgetMs) {
|
|
|
23233
23639
|
}
|
|
23234
23640
|
async function rollbackOrHold(args, notify, error) {
|
|
23235
23641
|
const target = args.plan.targetVersion;
|
|
23236
|
-
const rollbackTarget = args.
|
|
23642
|
+
const rollbackTarget = args.preApplyVersion;
|
|
23237
23643
|
const current = await readBreaker(args.client);
|
|
23238
23644
|
const next = nextBreaker(current);
|
|
23239
23645
|
if (next === "held") {
|
|
@@ -23497,6 +23903,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
23497
23903
|
plan,
|
|
23498
23904
|
ctx: applyCtx,
|
|
23499
23905
|
stamp: stamp ?? seedStampFor(plan),
|
|
23906
|
+
preApplyVersion: stamp?.platformVersion ?? null,
|
|
23500
23907
|
manifest,
|
|
23501
23908
|
credential: ctx.credential,
|
|
23502
23909
|
endpoint: ctx.endpoint,
|
|
@@ -23722,8 +24129,8 @@ var PlatformRequestUpdateCommand = class extends M8tCommand {
|
|
|
23722
24129
|
description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
|
|
23723
24130
|
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
24131
|
examples: [
|
|
23725
|
-
["Request an update", "m8t platform request-update --version 0.7.
|
|
23726
|
-
["Request it and wait", "m8t platform request-update --version 0.7.
|
|
24132
|
+
["Request an update", "m8t platform request-update --version 0.7.4 --resource-group rg-m8t --subscription <id>"],
|
|
24133
|
+
["Request it and wait", "m8t platform request-update --version 0.7.4 --wait --resource-group rg-m8t --subscription <id>"]
|
|
23727
24134
|
]
|
|
23728
24135
|
});
|
|
23729
24136
|
version = Option35.String("--version", { description: "The platform version to converge to." });
|
|
@@ -24021,9 +24428,87 @@ var PlatformSeedStampCommand = class extends M8tCommand {
|
|
|
24021
24428
|
}
|
|
24022
24429
|
};
|
|
24023
24430
|
|
|
24024
|
-
// src/commands/platform/
|
|
24431
|
+
// src/commands/platform/gateway-adopt.ts
|
|
24025
24432
|
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
24026
|
-
import { DefaultAzureCredential as DefaultAzureCredential21
|
|
24433
|
+
import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
|
|
24434
|
+
init_errors();
|
|
24435
|
+
var PlatformGatewayAdoptCommand = class extends M8tCommand {
|
|
24436
|
+
static paths = [["platform", "gateway", "adopt"]];
|
|
24437
|
+
static usage = Command40.Usage({
|
|
24438
|
+
description: "Explicitly authorize one legacy m8t ACR gateway for managed, digest-pinned updates.",
|
|
24439
|
+
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."
|
|
24440
|
+
});
|
|
24441
|
+
subscription = Option37.String("--subscription");
|
|
24442
|
+
resourceGroup = Option37.String("--resource-group");
|
|
24443
|
+
target = Option37.String("--target", { description: "Published platform target whose installer contains adoption support." });
|
|
24444
|
+
expectedImage = Option37.String("--expected-image", { description: "Exact live legacy ACR image ref." });
|
|
24445
|
+
expectedDigest = Option37.String("--expected-digest", { description: "Exact sha256 digest resolved from that ACR image." });
|
|
24446
|
+
expectedAcrResourceId = Option37.String("--expected-acr-resource-id", { description: "Exact ARM id of the legacy ACR." });
|
|
24447
|
+
output = Option37.String("--output");
|
|
24448
|
+
async executeCommand() {
|
|
24449
|
+
const need = (v, flag) => {
|
|
24450
|
+
if (typeof v !== "string" || v.trim() === "") throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_ARG_MISSING", message: `${flag} is required.` });
|
|
24451
|
+
return v.trim();
|
|
24452
|
+
};
|
|
24453
|
+
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
24454
|
+
const account = await getAzAccount();
|
|
24455
|
+
const subscriptionId = need(this.subscription ?? account.subscriptionId, "--subscription");
|
|
24456
|
+
const gw = await resolveGatewayContext({ subscriptionId, resourceGroup: this.resourceGroup, interactive: mode !== "json" });
|
|
24457
|
+
const parsed = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
24458
|
+
const resourceGroup = need(this.resourceGroup ?? parsed.resourceGroup, "--resource-group");
|
|
24459
|
+
const target = need(this.target, "--target");
|
|
24460
|
+
const manifest = await fetchManifest({ channel: true, version: target });
|
|
24461
|
+
const assertTarget = (requested, actual) => {
|
|
24462
|
+
const normalized = requested.replace(/^platform-v/, "");
|
|
24463
|
+
if (actual.platform.version.replace(/^platform-v/, "") !== normalized || actual.platform.tag !== platformTag(normalized)) {
|
|
24464
|
+
throw new LocalCliError({
|
|
24465
|
+
code: "PLATFORM_GATEWAY_ADOPTION_TARGET_MISMATCH",
|
|
24466
|
+
message: `Requested target ${requested}, but the release service returned ${actual.platform.version}.`
|
|
24467
|
+
});
|
|
24468
|
+
}
|
|
24469
|
+
};
|
|
24470
|
+
assertTarget(target, manifest);
|
|
24471
|
+
if (compareSemver(`v${manifest.components.installer.version}`, "v0.1.74") < 0 || compareSemver(`v${manifest.components.cli.recommended}`, "v0.2.106") < 0) {
|
|
24472
|
+
throw new LocalCliError({
|
|
24473
|
+
code: "PLATFORM_GATEWAY_ADOPTION_TARGET_TOO_OLD",
|
|
24474
|
+
message: `Platform target ${target} predates digest-bound legacy gateway adoption support.`,
|
|
24475
|
+
hint: "Choose a published platform target whose installer is at least 0.1.74 and recommended CLI is at least 0.2.106."
|
|
24476
|
+
});
|
|
24477
|
+
}
|
|
24478
|
+
if (!manifest.platform.previousVersion) {
|
|
24479
|
+
throw new LocalCliError({
|
|
24480
|
+
code: "PLATFORM_GATEWAY_ADOPTION_NO_ROLLBACK",
|
|
24481
|
+
message: `Platform target ${target} has no recorded previous release; refusing a gateway ownership transition without a rollback target.`
|
|
24482
|
+
});
|
|
24483
|
+
}
|
|
24484
|
+
const rollbackManifest = await fetchManifest({ channel: true, version: manifest.platform.previousVersion });
|
|
24485
|
+
assertTarget(manifest.platform.previousVersion, rollbackManifest);
|
|
24486
|
+
const result2 = await adoptLegacyGateway({
|
|
24487
|
+
credential: new DefaultAzureCredential21(),
|
|
24488
|
+
subscriptionId,
|
|
24489
|
+
resourceGroup,
|
|
24490
|
+
gatewayResourceId: gw.containerAppResourceId,
|
|
24491
|
+
target,
|
|
24492
|
+
expectedImage: need(this.expectedImage, "--expected-image"),
|
|
24493
|
+
expectedDigest: need(this.expectedDigest, "--expected-digest"),
|
|
24494
|
+
expectedAcrResourceId: need(this.expectedAcrResourceId, "--expected-acr-resource-id"),
|
|
24495
|
+
targetGatewayTag: manifest.components.gateway.tag,
|
|
24496
|
+
targetGatewayDigest: manifest.components.gateway.digest,
|
|
24497
|
+
rollbackGatewayTag: rollbackManifest.components.gateway.tag,
|
|
24498
|
+
rollbackGatewayDigest: rollbackManifest.components.gateway.digest,
|
|
24499
|
+
operatorIdentity: await getCallerObjectId(),
|
|
24500
|
+
onProgress: (m) => this.context.stderr.write(`${m}
|
|
24501
|
+
`)
|
|
24502
|
+
});
|
|
24503
|
+
this.context.stdout.write(mode === "json" ? renderJson(result2) + "\n" : `${result2.outcome}: ${result2.image}@${result2.digest}
|
|
24504
|
+
`);
|
|
24505
|
+
return 0;
|
|
24506
|
+
}
|
|
24507
|
+
};
|
|
24508
|
+
|
|
24509
|
+
// src/commands/platform/policy.ts
|
|
24510
|
+
import { Command as Command41, Option as Option38 } from "clipanion";
|
|
24511
|
+
import { DefaultAzureCredential as DefaultAzureCredential22, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
|
|
24027
24512
|
init_errors();
|
|
24028
24513
|
|
|
24029
24514
|
// src/lib/platform-policy.ts
|
|
@@ -24078,16 +24563,16 @@ async function readPolicy(opts) {
|
|
|
24078
24563
|
// src/commands/platform/policy.ts
|
|
24079
24564
|
var PlatformPolicyCommand = class extends M8tCommand {
|
|
24080
24565
|
static paths = [["platform", "policy"]];
|
|
24081
|
-
static usage =
|
|
24566
|
+
static usage = Command41.Usage({
|
|
24082
24567
|
description: "Show or set how this install handles available platform updates.",
|
|
24083
24568
|
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
24569
|
examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
|
|
24085
24570
|
});
|
|
24086
|
-
set =
|
|
24087
|
-
subscription =
|
|
24088
|
-
resourceGroup =
|
|
24089
|
-
miClientId =
|
|
24090
|
-
output =
|
|
24571
|
+
set = Option38.String("--set", { description: "notify-only | auto-critical | auto-all" });
|
|
24572
|
+
subscription = Option38.String("--subscription");
|
|
24573
|
+
resourceGroup = Option38.String("--resource-group");
|
|
24574
|
+
miClientId = Option38.String("--mi-client-id");
|
|
24575
|
+
output = Option38.String("--output");
|
|
24091
24576
|
async executeCommand() {
|
|
24092
24577
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
24093
24578
|
const need = (v, flag) => {
|
|
@@ -24100,7 +24585,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
24100
24585
|
this.context.stderr.write(`${m}
|
|
24101
24586
|
`);
|
|
24102
24587
|
};
|
|
24103
|
-
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new
|
|
24588
|
+
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential22();
|
|
24104
24589
|
const ctx = {
|
|
24105
24590
|
credential: credential2,
|
|
24106
24591
|
subscriptionId: need(this.subscription, "--subscription"),
|
|
@@ -24133,7 +24618,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
24133
24618
|
};
|
|
24134
24619
|
|
|
24135
24620
|
// src/commands/platform/enable-cost-report.ts
|
|
24136
|
-
import { Command as
|
|
24621
|
+
import { Command as Command42, Option as Option39 } from "clipanion";
|
|
24137
24622
|
|
|
24138
24623
|
// src/lib/wire-gateway-acs.ts
|
|
24139
24624
|
init_rbac();
|
|
@@ -24173,18 +24658,18 @@ async function wireGatewayForAcs(args) {
|
|
|
24173
24658
|
init_errors();
|
|
24174
24659
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
24175
24660
|
static paths = [["platform", "enable-cost-report"]];
|
|
24176
|
-
static usage =
|
|
24661
|
+
static usage = Command42.Usage({
|
|
24177
24662
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
24178
24663
|
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
24664
|
});
|
|
24180
|
-
subscription =
|
|
24181
|
-
resourceGroup =
|
|
24665
|
+
subscription = Option39.String("--subscription");
|
|
24666
|
+
resourceGroup = Option39.String("--resource-group", {
|
|
24182
24667
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24183
24668
|
});
|
|
24184
|
-
acsEndpoint =
|
|
24185
|
-
acsSender =
|
|
24186
|
-
acsResourceId =
|
|
24187
|
-
output =
|
|
24669
|
+
acsEndpoint = Option39.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
|
|
24670
|
+
acsSender = Option39.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
|
|
24671
|
+
acsResourceId = Option39.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
|
|
24672
|
+
output = Option39.String("--output");
|
|
24188
24673
|
async executeCommand() {
|
|
24189
24674
|
const mode = resolveOutputMode(
|
|
24190
24675
|
this.output,
|
|
@@ -24258,13 +24743,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
24258
24743
|
};
|
|
24259
24744
|
|
|
24260
24745
|
// src/commands/platform/email.ts
|
|
24261
|
-
import { Command as
|
|
24262
|
-
import { DefaultAzureCredential as
|
|
24746
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
24747
|
+
import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
|
|
24263
24748
|
import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
|
|
24264
24749
|
init_errors();
|
|
24265
24750
|
var PlatformEmailCommand = class extends M8tCommand {
|
|
24266
24751
|
static paths = [["platform", "email"]];
|
|
24267
|
-
static usage =
|
|
24752
|
+
static usage = Command43.Usage({
|
|
24268
24753
|
description: "Turn outbound email (advisor handoffs) on or off for this install.",
|
|
24269
24754
|
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
24755
|
examples: [
|
|
@@ -24272,19 +24757,19 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24272
24757
|
["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
|
|
24273
24758
|
]
|
|
24274
24759
|
});
|
|
24275
|
-
state =
|
|
24276
|
-
subscription =
|
|
24277
|
-
resourceGroup =
|
|
24760
|
+
state = Option40.String({ required: true, name: "on|off" });
|
|
24761
|
+
subscription = Option40.String("--subscription");
|
|
24762
|
+
resourceGroup = Option40.String("--resource-group", {
|
|
24278
24763
|
description: "m8t resource group, to disambiguate in a multi-deployment subscription."
|
|
24279
24764
|
});
|
|
24280
|
-
agent =
|
|
24281
|
-
kvUri =
|
|
24765
|
+
agent = Option40.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
|
|
24766
|
+
kvUri = Option40.String("--kv-uri", {
|
|
24282
24767
|
description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
|
|
24283
24768
|
});
|
|
24284
|
-
endpoint =
|
|
24769
|
+
endpoint = Option40.String("--endpoint", {
|
|
24285
24770
|
description: "Foundry project endpoint, to disambiguate a subscription holding several."
|
|
24286
24771
|
});
|
|
24287
|
-
output =
|
|
24772
|
+
output = Option40.String("--output");
|
|
24288
24773
|
async executeCommand() {
|
|
24289
24774
|
const wanted = this.state.trim().toLowerCase();
|
|
24290
24775
|
if (wanted !== "on" && wanted !== "off") {
|
|
@@ -24308,7 +24793,7 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24308
24793
|
});
|
|
24309
24794
|
const { resourceGroup } = parseContainerAppResourceId(ctx.containerAppResourceId);
|
|
24310
24795
|
const subscriptionId = ctx.subscriptionId;
|
|
24311
|
-
const credential2 = new
|
|
24796
|
+
const credential2 = new DefaultAzureCredential23();
|
|
24312
24797
|
const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
|
|
24313
24798
|
if (stamp === null) {
|
|
24314
24799
|
throw new LocalCliError({
|
|
@@ -24434,45 +24919,45 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24434
24919
|
};
|
|
24435
24920
|
|
|
24436
24921
|
// src/commands/platform/enable-auto-update.ts
|
|
24437
|
-
import { Command as
|
|
24922
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
24438
24923
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
24439
|
-
import { DefaultAzureCredential as
|
|
24924
|
+
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
24440
24925
|
init_errors();
|
|
24441
24926
|
var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
24442
24927
|
static paths = [["platform", "enable-auto-update"]];
|
|
24443
|
-
static usage =
|
|
24928
|
+
static usage = Command44.Usage({
|
|
24444
24929
|
category: "Platform",
|
|
24445
24930
|
description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
|
|
24446
24931
|
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
24932
|
});
|
|
24448
|
-
subscription =
|
|
24449
|
-
resourceGroup =
|
|
24933
|
+
subscription = Option41.String("--subscription");
|
|
24934
|
+
resourceGroup = Option41.String("--resource-group", {
|
|
24450
24935
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24451
24936
|
});
|
|
24452
|
-
suffix =
|
|
24937
|
+
suffix = Option41.String("--suffix", {
|
|
24453
24938
|
description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
|
|
24454
24939
|
});
|
|
24455
|
-
installerImage =
|
|
24940
|
+
installerImage = Option41.String("--installer-image", {
|
|
24456
24941
|
required: true,
|
|
24457
24942
|
description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
|
|
24458
24943
|
});
|
|
24459
|
-
updateCron =
|
|
24944
|
+
updateCron = Option41.String("--update-cron", {
|
|
24460
24945
|
description: "Cron schedule for the updater job (bicep default applies when omitted)."
|
|
24461
24946
|
});
|
|
24462
|
-
channelUrl =
|
|
24947
|
+
channelUrl = Option41.String("--channel-url", {
|
|
24463
24948
|
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
24464
24949
|
});
|
|
24465
|
-
location =
|
|
24950
|
+
location = Option41.String("--location", {
|
|
24466
24951
|
description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
|
|
24467
24952
|
});
|
|
24468
|
-
foundryTracing =
|
|
24953
|
+
foundryTracing = Option41.String("--foundry-tracing", {
|
|
24469
24954
|
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
24955
|
});
|
|
24471
|
-
endpoint =
|
|
24956
|
+
endpoint = Option41.String("--endpoint", {
|
|
24472
24957
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
24473
24958
|
});
|
|
24474
|
-
yes =
|
|
24475
|
-
output =
|
|
24959
|
+
yes = Option41.Boolean("--yes", false);
|
|
24960
|
+
output = Option41.String("--output");
|
|
24476
24961
|
async executeCommand() {
|
|
24477
24962
|
const mode = resolveOutputMode(
|
|
24478
24963
|
this.output,
|
|
@@ -24491,7 +24976,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
24491
24976
|
resourceGroup: this.resourceGroup
|
|
24492
24977
|
});
|
|
24493
24978
|
const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
24494
|
-
const credential2 = new
|
|
24979
|
+
const credential2 = new DefaultAzureCredential24();
|
|
24495
24980
|
const account = await getAzAccount();
|
|
24496
24981
|
const subscriptionId = this.subscription ?? account.subscriptionId;
|
|
24497
24982
|
const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
|
|
@@ -24653,7 +25138,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
24653
25138
|
};
|
|
24654
25139
|
|
|
24655
25140
|
// src/commands/deploy.ts
|
|
24656
|
-
import { Command as
|
|
25141
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
24657
25142
|
|
|
24658
25143
|
// src/lib/app-reg.ts
|
|
24659
25144
|
init_esm();
|
|
@@ -25217,15 +25702,15 @@ function classifyWhatIf(changes) {
|
|
|
25217
25702
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
|
|
25218
25703
|
var DeployCommand = class extends M8tCommand {
|
|
25219
25704
|
static paths = [["deploy"]];
|
|
25220
|
-
static usage =
|
|
25705
|
+
static usage = Command45.Usage({
|
|
25221
25706
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
25222
25707
|
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
25708
|
});
|
|
25224
|
-
subscription =
|
|
25225
|
-
resourceGroup =
|
|
25226
|
-
location =
|
|
25227
|
-
suffix =
|
|
25228
|
-
imageRef =
|
|
25709
|
+
subscription = Option42.String("--subscription");
|
|
25710
|
+
resourceGroup = Option42.String("--resource-group", "rg-m8t-stack");
|
|
25711
|
+
location = Option42.String("--location", "eastus");
|
|
25712
|
+
suffix = Option42.String("--suffix", "");
|
|
25713
|
+
imageRef = Option42.String("--image-ref", DEFAULT_IMAGE_REF);
|
|
25229
25714
|
// Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
|
|
25230
25715
|
// from-zero case. It exists because the gateway and the voice relay do NOT
|
|
25231
25716
|
// always run the same image: a converge preserves a BYOC gateway on its own
|
|
@@ -25233,28 +25718,28 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25233
25718
|
// (`--what-if`) that can only express one image therefore reports the other
|
|
25234
25719
|
// app as drift on every single run, forever — which is exactly what the
|
|
25235
25720
|
// infra-drift gate did from 2026-08-11.
|
|
25236
|
-
gatewayImageRef =
|
|
25237
|
-
acrPullIdentity =
|
|
25238
|
-
acrResourceId =
|
|
25239
|
-
foundryEndpoint =
|
|
25240
|
-
foundryResourceId =
|
|
25241
|
-
foundryTracing =
|
|
25721
|
+
gatewayImageRef = Option42.String("--gateway-image-ref", "");
|
|
25722
|
+
acrPullIdentity = Option42.String("--acrpull-identity");
|
|
25723
|
+
acrResourceId = Option42.String("--acr-resource-id");
|
|
25724
|
+
foundryEndpoint = Option42.String("--foundry-endpoint");
|
|
25725
|
+
foundryResourceId = Option42.String("--foundry-resource-id");
|
|
25726
|
+
foundryTracing = Option42.String("--foundry-tracing");
|
|
25242
25727
|
// project | account | skip (bicep default: project)
|
|
25243
|
-
clientId =
|
|
25244
|
-
whatIf =
|
|
25728
|
+
clientId = Option42.String("--client-id");
|
|
25729
|
+
whatIf = Option42.Boolean("--what-if", false);
|
|
25245
25730
|
// Only meaningful with --what-if. Routes the comparison through the
|
|
25246
25731
|
// value-free renderers (see ./lib/whatif-redact.js) instead of the default
|
|
25247
25732
|
// before/after renderer. Defaults false so a local, interactive run keeps
|
|
25248
25733
|
// showing values — that is the whole diagnostic point of --what-if.
|
|
25249
25734
|
// Automation that forwards this output anywhere non-private (a CI log, an
|
|
25250
25735
|
// issue) MUST pass --redact.
|
|
25251
|
-
redact =
|
|
25252
|
-
output =
|
|
25736
|
+
redact = Option42.Boolean("--redact", false);
|
|
25737
|
+
output = Option42.String("--output");
|
|
25253
25738
|
// Subscription-scoped role assignments. Omitted ⇒ the template default (true).
|
|
25254
25739
|
// Pass false when deploying as a principal scoped to the resource group only:
|
|
25255
25740
|
// it cannot deploy at subscription scope, and those assignments persist
|
|
25256
25741
|
// idempotently from the initial deployment anyway.
|
|
25257
|
-
assignSubscriptionRoles =
|
|
25742
|
+
assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
|
|
25258
25743
|
/**
|
|
25259
25744
|
* The installer image the updater Container-Apps Job runs.
|
|
25260
25745
|
*
|
|
@@ -25265,23 +25750,23 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25265
25750
|
* than that — the comparison proposes REMOVING an updater job that exists and
|
|
25266
25751
|
* should, and reports it as drift on every single run.
|
|
25267
25752
|
*/
|
|
25268
|
-
installerImage =
|
|
25753
|
+
installerImage = Option42.String("--installer-image");
|
|
25269
25754
|
// Referee — all optional, undefined by default ⇒ the bicep defaults
|
|
25270
25755
|
// apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
|
|
25271
25756
|
// 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 =
|
|
25757
|
+
gatewayCpu = Option42.String("--gateway-cpu");
|
|
25758
|
+
gatewayMemory = Option42.String("--gateway-memory");
|
|
25759
|
+
refereeEnabled = Option42.String("--referee-enabled");
|
|
25760
|
+
refereeBrainRepos = Option42.String("--referee-brain-repos");
|
|
25761
|
+
refereeFeedRepo = Option42.String("--referee-feed-repo");
|
|
25762
|
+
refereeInstallationId = Option42.String("--referee-installation-id");
|
|
25763
|
+
refereeWebhookHmacKvUri = Option42.String("--referee-webhook-hmac-kv-uri");
|
|
25764
|
+
examKvUri = Option42.String("--exam-kv-uri");
|
|
25765
|
+
examLaWorkspaceId = Option42.String("--exam-la-workspace-id");
|
|
25766
|
+
brainEvalDeployment = Option42.String("--brain-eval-deployment");
|
|
25767
|
+
brainAppLogin = Option42.String("--brain-app-login");
|
|
25768
|
+
refereeCheckpointDir = Option42.String("--referee-checkpoint-dir");
|
|
25769
|
+
examApiBase = Option42.String("--exam-api-base");
|
|
25285
25770
|
async executeCommand() {
|
|
25286
25771
|
const mode = resolveOutputMode(
|
|
25287
25772
|
this.output,
|
|
@@ -25465,7 +25950,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25465
25950
|
|
|
25466
25951
|
// src/commands/eval/skill.ts
|
|
25467
25952
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
25468
|
-
import { Command as
|
|
25953
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
25469
25954
|
init_errors();
|
|
25470
25955
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
25471
25956
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -25490,14 +25975,14 @@ function parseVerdict(stdout) {
|
|
|
25490
25975
|
}
|
|
25491
25976
|
var EvalSkillCommand = class extends M8tCommand {
|
|
25492
25977
|
static paths = [["eval", "skill"]];
|
|
25493
|
-
static usage =
|
|
25978
|
+
static usage = Command46.Usage({
|
|
25494
25979
|
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
25980
|
});
|
|
25496
|
-
candidate =
|
|
25497
|
-
skillsDir =
|
|
25498
|
-
noJudge =
|
|
25499
|
-
deployment =
|
|
25500
|
-
output =
|
|
25981
|
+
candidate = Option43.String();
|
|
25982
|
+
skillsDir = Option43.String("--skills-dir");
|
|
25983
|
+
noJudge = Option43.Boolean("--no-judge", false);
|
|
25984
|
+
deployment = Option43.String("--deployment");
|
|
25985
|
+
output = Option43.String("--output");
|
|
25501
25986
|
executeCommand() {
|
|
25502
25987
|
return Promise.resolve(this._runCommand());
|
|
25503
25988
|
}
|
|
@@ -25556,7 +26041,7 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
25556
26041
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
25557
26042
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
|
|
25558
26043
|
import { join as join30 } from "path";
|
|
25559
|
-
import { Command as
|
|
26044
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
25560
26045
|
init_errors();
|
|
25561
26046
|
init_esm();
|
|
25562
26047
|
function parseArmToken(tok, opts) {
|
|
@@ -25786,24 +26271,24 @@ function buildPlan(args) {
|
|
|
25786
26271
|
}
|
|
25787
26272
|
var EvalExamCommand = class extends M8tCommand {
|
|
25788
26273
|
static paths = [["eval", "exam"]];
|
|
25789
|
-
static usage =
|
|
26274
|
+
static usage = Command47.Usage({
|
|
25790
26275
|
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
26276
|
});
|
|
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 =
|
|
26277
|
+
worker = Option44.String();
|
|
26278
|
+
arms = Option44.String("--arms");
|
|
26279
|
+
taskSet = Option44.String("--task-set");
|
|
26280
|
+
examType = Option44.String("--exam-type");
|
|
26281
|
+
skill = Option44.String("--skill");
|
|
26282
|
+
reps = Option44.String("-n,--reps");
|
|
26283
|
+
probes = Option44.String("--probes");
|
|
26284
|
+
pool = Option44.String("--pool");
|
|
26285
|
+
out = Option44.String("--out");
|
|
26286
|
+
dryRun = Option44.Boolean("--dry-run", false);
|
|
26287
|
+
keepArms = Option44.Boolean("--keep-arms", false);
|
|
26288
|
+
allowStub = Option44.Boolean("--allow-stub", false);
|
|
26289
|
+
deployment = Option44.String("--deployment");
|
|
26290
|
+
output = Option44.String("--output");
|
|
26291
|
+
observeWaitS = Option44.String("--observe-wait-s");
|
|
25807
26292
|
async executeCommand() {
|
|
25808
26293
|
await Promise.resolve();
|
|
25809
26294
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -25918,10 +26403,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
25918
26403
|
};
|
|
25919
26404
|
|
|
25920
26405
|
// src/commands/version.ts
|
|
25921
|
-
import { Command as
|
|
26406
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
25922
26407
|
var VersionCommand = class extends M8tCommand {
|
|
25923
26408
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
25924
|
-
static usage =
|
|
26409
|
+
static usage = Command48.Usage({
|
|
25925
26410
|
description: "Print the CLI version.",
|
|
25926
26411
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
25927
26412
|
examples: [
|
|
@@ -25929,8 +26414,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
25929
26414
|
["Print as JSON", "$0 version --output json"]
|
|
25930
26415
|
]
|
|
25931
26416
|
});
|
|
25932
|
-
output =
|
|
25933
|
-
verbose =
|
|
26417
|
+
output = Option45.String("--output", { description: "pretty | json | auto (default)" });
|
|
26418
|
+
verbose = Option45.Boolean("--verbose", false);
|
|
25934
26419
|
executeCommand() {
|
|
25935
26420
|
const mode = resolveOutputMode(
|
|
25936
26421
|
this.output ?? "auto",
|
|
@@ -25961,18 +26446,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
25961
26446
|
};
|
|
25962
26447
|
|
|
25963
26448
|
// src/commands/whoami.ts
|
|
25964
|
-
import { Command as
|
|
26449
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
25965
26450
|
var WhoamiCommand = class extends M8tCommand {
|
|
25966
26451
|
static paths = [["whoami"]];
|
|
25967
|
-
static usage =
|
|
26452
|
+
static usage = Command49.Usage({
|
|
25968
26453
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
25969
26454
|
});
|
|
25970
|
-
output =
|
|
25971
|
-
verbose =
|
|
25972
|
-
subscription =
|
|
26455
|
+
output = Option46.String("--output");
|
|
26456
|
+
verbose = Option46.Boolean("--verbose", false);
|
|
26457
|
+
subscription = Option46.String("--subscription", {
|
|
25973
26458
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
25974
26459
|
});
|
|
25975
|
-
resourceGroup =
|
|
26460
|
+
resourceGroup = Option46.String("--resource-group", {
|
|
25976
26461
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
25977
26462
|
});
|
|
25978
26463
|
async executeCommand() {
|
|
@@ -26037,7 +26522,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
26037
26522
|
};
|
|
26038
26523
|
|
|
26039
26524
|
// src/commands/status.ts
|
|
26040
|
-
import { Command as
|
|
26525
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
26041
26526
|
|
|
26042
26527
|
// src/lib/azd.ts
|
|
26043
26528
|
init_errors();
|
|
@@ -26102,10 +26587,10 @@ async function resolveLocalContext() {
|
|
|
26102
26587
|
// src/commands/status.ts
|
|
26103
26588
|
var StatusCommand = class extends M8tCommand {
|
|
26104
26589
|
static paths = [["status"]];
|
|
26105
|
-
static usage =
|
|
26590
|
+
static usage = Command50.Usage({
|
|
26106
26591
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
26107
26592
|
});
|
|
26108
|
-
output =
|
|
26593
|
+
output = Option47.String("--output");
|
|
26109
26594
|
async executeCommand() {
|
|
26110
26595
|
const mode = resolveOutputMode(
|
|
26111
26596
|
this.output,
|
|
@@ -26143,8 +26628,8 @@ var StatusCommand = class extends M8tCommand {
|
|
|
26143
26628
|
};
|
|
26144
26629
|
|
|
26145
26630
|
// src/commands/doctor.ts
|
|
26146
|
-
import { Command as
|
|
26147
|
-
import { DefaultAzureCredential as
|
|
26631
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
26632
|
+
import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
|
|
26148
26633
|
import * as fs30 from "fs";
|
|
26149
26634
|
import * as os12 from "os";
|
|
26150
26635
|
import * as path33 from "path";
|
|
@@ -26691,12 +27176,12 @@ function probeLegacyStateDir() {
|
|
|
26691
27176
|
}
|
|
26692
27177
|
var DoctorCommand = class extends M8tCommand {
|
|
26693
27178
|
static paths = [["doctor"]];
|
|
26694
|
-
static usage =
|
|
27179
|
+
static usage = Command51.Usage({
|
|
26695
27180
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
26696
27181
|
});
|
|
26697
|
-
output =
|
|
26698
|
-
agent =
|
|
26699
|
-
resourceGroup =
|
|
27182
|
+
output = Option48.String("--output");
|
|
27183
|
+
agent = Option48.String("--agent");
|
|
27184
|
+
resourceGroup = Option48.String("--resource-group", {
|
|
26700
27185
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
26701
27186
|
});
|
|
26702
27187
|
async executeCommand() {
|
|
@@ -26769,7 +27254,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26769
27254
|
let kvStatus = 0;
|
|
26770
27255
|
if (kv) {
|
|
26771
27256
|
try {
|
|
26772
|
-
const token = await new
|
|
27257
|
+
const token = await new DefaultAzureCredential25().getToken("https://vault.azure.net/.default");
|
|
26773
27258
|
const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
|
|
26774
27259
|
headers: { Authorization: `Bearer ${token.token}` }
|
|
26775
27260
|
});
|
|
@@ -26788,7 +27273,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26788
27273
|
emit(checkModelQuota(deployments, usages));
|
|
26789
27274
|
checking("outbound email");
|
|
26790
27275
|
try {
|
|
26791
|
-
const credential2 = new
|
|
27276
|
+
const credential2 = new DefaultAzureCredential25();
|
|
26792
27277
|
const outcome = platformRg && platformSub ? await readStampOutcome({ credential: credential2, subscriptionId: platformSub, resourceGroup: platformRg }).catch(
|
|
26793
27278
|
() => ({ source: "unreadable" })
|
|
26794
27279
|
) : { source: "unreadable" };
|
|
@@ -26843,7 +27328,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26843
27328
|
if (typeof this.agent === "string" && this.agent) {
|
|
26844
27329
|
checking(`delivery grant for ${this.agent}`);
|
|
26845
27330
|
try {
|
|
26846
|
-
const credential2 = new
|
|
27331
|
+
const credential2 = new DefaultAzureCredential25();
|
|
26847
27332
|
const cur = await getAgentVersion({
|
|
26848
27333
|
credential: credential2,
|
|
26849
27334
|
projectEndpoint: foundry.projectEndpoint,
|
|
@@ -26886,11 +27371,11 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26886
27371
|
};
|
|
26887
27372
|
|
|
26888
27373
|
// src/commands/prereqs.ts
|
|
26889
|
-
import { Command as
|
|
27374
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
26890
27375
|
init_errors();
|
|
26891
27376
|
|
|
26892
27377
|
// src/lib/prereq-deps.ts
|
|
26893
|
-
import { DefaultAzureCredential as
|
|
27378
|
+
import { DefaultAzureCredential as DefaultAzureCredential26 } from "@azure/identity";
|
|
26894
27379
|
|
|
26895
27380
|
// src/lib/bootstrap-preflight.ts
|
|
26896
27381
|
var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
|
|
@@ -27002,7 +27487,7 @@ function buildPrereqDeps(opts = {}) {
|
|
|
27002
27487
|
probeRedirectUri,
|
|
27003
27488
|
fixFoundryAccess,
|
|
27004
27489
|
fixKeyVaultAccess,
|
|
27005
|
-
credential: () => credentialSingleton2 ??= new
|
|
27490
|
+
credential: () => credentialSingleton2 ??= new DefaultAzureCredential26()
|
|
27006
27491
|
};
|
|
27007
27492
|
}
|
|
27008
27493
|
|
|
@@ -27592,7 +28077,7 @@ function renderVerdict(v) {
|
|
|
27592
28077
|
}
|
|
27593
28078
|
var PrereqsCommand = class extends M8tCommand {
|
|
27594
28079
|
static paths = [["prereqs"]];
|
|
27595
|
-
static usage =
|
|
28080
|
+
static usage = Command52.Usage({
|
|
27596
28081
|
description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
|
|
27597
28082
|
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
28083
|
examples: [
|
|
@@ -27603,15 +28088,15 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
27603
28088
|
["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
|
|
27604
28089
|
]
|
|
27605
28090
|
});
|
|
27606
|
-
fix =
|
|
27607
|
-
for_ =
|
|
27608
|
-
phase =
|
|
27609
|
-
region =
|
|
27610
|
-
model =
|
|
27611
|
-
clientId =
|
|
27612
|
-
subscription =
|
|
27613
|
-
resourceGroup =
|
|
27614
|
-
output =
|
|
28091
|
+
fix = Option49.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
|
|
28092
|
+
for_ = Option49.String("--for", { description: "UPN or object id of another person. Usage phase only." });
|
|
28093
|
+
phase = Option49.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
|
|
28094
|
+
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)." });
|
|
28095
|
+
model = Option49.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
|
|
28096
|
+
clientId = Option49.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
|
|
28097
|
+
subscription = Option49.String("--subscription");
|
|
28098
|
+
resourceGroup = Option49.String("--resource-group");
|
|
28099
|
+
output = Option49.String("--output");
|
|
27615
28100
|
async executeCommand() {
|
|
27616
28101
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
27617
28102
|
const json = mode === "json";
|
|
@@ -27667,7 +28152,7 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
27667
28152
|
};
|
|
27668
28153
|
|
|
27669
28154
|
// src/commands/switch.ts
|
|
27670
|
-
import { Command as
|
|
28155
|
+
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
27671
28156
|
|
|
27672
28157
|
// src/lib/profiles.ts
|
|
27673
28158
|
import * as fs31 from "fs/promises";
|
|
@@ -27807,14 +28292,14 @@ async function profileSwitch(name, asName) {
|
|
|
27807
28292
|
init_errors();
|
|
27808
28293
|
var SwitchCommand = class extends M8tCommand {
|
|
27809
28294
|
static paths = [["switch"]];
|
|
27810
|
-
static usage =
|
|
28295
|
+
static usage = Command53.Usage({
|
|
27811
28296
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
27812
28297
|
});
|
|
27813
|
-
profile =
|
|
27814
|
-
subscription =
|
|
27815
|
-
list =
|
|
27816
|
-
as =
|
|
27817
|
-
output =
|
|
28298
|
+
profile = Option50.String({ required: false });
|
|
28299
|
+
subscription = Option50.String("--subscription");
|
|
28300
|
+
list = Option50.Boolean("--list", false);
|
|
28301
|
+
as = Option50.String("--as");
|
|
28302
|
+
output = Option50.String("--output");
|
|
27818
28303
|
async executeCommand() {
|
|
27819
28304
|
const mode = resolveOutputMode(
|
|
27820
28305
|
this.output,
|
|
@@ -27871,7 +28356,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
27871
28356
|
|
|
27872
28357
|
// src/commands/open.ts
|
|
27873
28358
|
import { spawn as spawn5 } from "child_process";
|
|
27874
|
-
import { Command as
|
|
28359
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
27875
28360
|
|
|
27876
28361
|
// src/lib/open-targets.ts
|
|
27877
28362
|
init_errors();
|
|
@@ -27917,14 +28402,14 @@ function openUrl(url) {
|
|
|
27917
28402
|
}
|
|
27918
28403
|
var OpenCommand = class extends M8tCommand {
|
|
27919
28404
|
static paths = [["open"]];
|
|
27920
|
-
static usage =
|
|
28405
|
+
static usage = Command54.Usage({
|
|
27921
28406
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
27922
28407
|
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
28408
|
});
|
|
27924
|
-
target =
|
|
27925
|
-
print =
|
|
27926
|
-
output =
|
|
27927
|
-
resourceGroup =
|
|
28409
|
+
target = Option51.String({ required: false });
|
|
28410
|
+
print = Option51.Boolean("--print", false);
|
|
28411
|
+
output = Option51.String("--output");
|
|
28412
|
+
resourceGroup = Option51.String("--resource-group", {
|
|
27928
28413
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
27929
28414
|
});
|
|
27930
28415
|
async executeCommand() {
|
|
@@ -27968,7 +28453,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
27968
28453
|
};
|
|
27969
28454
|
|
|
27970
28455
|
// src/commands/dream/run.ts
|
|
27971
|
-
import { Command as
|
|
28456
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
27972
28457
|
import { AzureCliCredential } from "@azure/identity";
|
|
27973
28458
|
import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
27974
28459
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
@@ -30368,28 +30853,28 @@ function redactTranscripts(input) {
|
|
|
30368
30853
|
}
|
|
30369
30854
|
var DreamRunCommand = class extends M8tCommand {
|
|
30370
30855
|
static paths = [["dream", "run"]];
|
|
30371
|
-
static usage =
|
|
30856
|
+
static usage = Command55.Usage({
|
|
30372
30857
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
|
|
30373
30858
|
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
30859
|
});
|
|
30375
|
-
worker =
|
|
30860
|
+
worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
30376
30861
|
// Opting IN to the side effects, rather than opting out of them. This command's
|
|
30377
30862
|
// own help has always described a dry run, but the bare invocation used to take
|
|
30378
30863
|
// the live branch — a real model call and a commit to the brain repo — so anyone
|
|
30379
30864
|
// acting on `--help` got the opposite of what they read.
|
|
30380
|
-
live =
|
|
30381
|
-
dryRun =
|
|
30382
|
-
since =
|
|
30383
|
-
reset =
|
|
30384
|
-
showTranscripts =
|
|
30865
|
+
live = Option52.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
|
|
30866
|
+
dryRun = Option52.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
|
|
30867
|
+
since = Option52.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
|
|
30868
|
+
reset = Option52.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
|
|
30869
|
+
showTranscripts = Option52.Boolean("--show-transcripts", false, {
|
|
30385
30870
|
description: "Print transcript bodies (default: metadata only)."
|
|
30386
30871
|
});
|
|
30387
|
-
subscription =
|
|
30388
|
-
endpoint =
|
|
30389
|
-
storageAccount =
|
|
30872
|
+
subscription = Option52.String("--subscription");
|
|
30873
|
+
endpoint = Option52.String("--endpoint");
|
|
30874
|
+
storageAccount = Option52.String("--storage-account", {
|
|
30390
30875
|
description: "Ledger storage account name (skips tag-based discovery)"
|
|
30391
30876
|
});
|
|
30392
|
-
output =
|
|
30877
|
+
output = Option52.String("--output");
|
|
30393
30878
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
30394
30879
|
deps;
|
|
30395
30880
|
async executeCommand() {
|
|
@@ -30740,7 +31225,7 @@ function defaultDeps(overrides) {
|
|
|
30740
31225
|
|
|
30741
31226
|
// src/commands/conversations/sweep.ts
|
|
30742
31227
|
import { createHash as createHash5 } from "crypto";
|
|
30743
|
-
import { Command as
|
|
31228
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
30744
31229
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
30745
31230
|
import { TableClient as TableClient8 } from "@azure/data-tables";
|
|
30746
31231
|
import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
|
|
@@ -30866,7 +31351,7 @@ function defaultDeps2() {
|
|
|
30866
31351
|
}
|
|
30867
31352
|
var ConversationsSweepCommand = class extends M8tCommand {
|
|
30868
31353
|
static paths = [["conversations", "sweep"]];
|
|
30869
|
-
static usage =
|
|
31354
|
+
static usage = Command56.Usage({
|
|
30870
31355
|
category: "Conversations",
|
|
30871
31356
|
description: "Delete expired public-visitor conversations (dry run by default)",
|
|
30872
31357
|
details: `
|
|
@@ -30882,22 +31367,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
30882
31367
|
["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
|
|
30883
31368
|
]
|
|
30884
31369
|
});
|
|
30885
|
-
doDelete =
|
|
31370
|
+
doDelete = Option53.Boolean("--delete", false, {
|
|
30886
31371
|
description: "Perform deletions (without this flag the command only reports)"
|
|
30887
31372
|
});
|
|
30888
|
-
principal =
|
|
31373
|
+
principal = Option53.String("--principal", {
|
|
30889
31374
|
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
31375
|
});
|
|
30891
|
-
max =
|
|
30892
|
-
graceDays =
|
|
31376
|
+
max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
|
|
31377
|
+
graceDays = Option53.String("--grace-days", "14", {
|
|
30893
31378
|
description: "Days past the 30-day key life before a conversation is eligible"
|
|
30894
31379
|
});
|
|
30895
|
-
lookbackDays =
|
|
31380
|
+
lookbackDays = Option53.String("--lookback-days", "180", {
|
|
30896
31381
|
description: "How far back to scan ledger activity for candidates"
|
|
30897
31382
|
});
|
|
30898
|
-
subscription =
|
|
30899
|
-
endpoint =
|
|
30900
|
-
storageAccount =
|
|
31383
|
+
subscription = Option53.String("--subscription", { description: "Azure subscription id override" });
|
|
31384
|
+
endpoint = Option53.String("--endpoint", { description: "Foundry project endpoint override" });
|
|
31385
|
+
storageAccount = Option53.String("--storage-account", {
|
|
30901
31386
|
description: "Ledger storage account name (skips tag-based discovery)"
|
|
30902
31387
|
});
|
|
30903
31388
|
deps = defaultDeps2();
|
|
@@ -31025,7 +31510,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
31025
31510
|
};
|
|
31026
31511
|
|
|
31027
31512
|
// src/commands/foundry/create.ts
|
|
31028
|
-
import { Command as
|
|
31513
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
31029
31514
|
|
|
31030
31515
|
// src/lib/foundry-create.ts
|
|
31031
31516
|
init_errors();
|
|
@@ -31263,7 +31748,7 @@ async function createFoundryProject(args) {
|
|
|
31263
31748
|
init_errors();
|
|
31264
31749
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
31265
31750
|
static paths = [["foundry", "create"]];
|
|
31266
|
-
static usage =
|
|
31751
|
+
static usage = Command57.Usage({
|
|
31267
31752
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
31268
31753
|
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
31754
|
examples: [
|
|
@@ -31272,16 +31757,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
31272
31757
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
31273
31758
|
]
|
|
31274
31759
|
});
|
|
31275
|
-
resourceGroup =
|
|
31276
|
-
location =
|
|
31277
|
-
account =
|
|
31278
|
-
project =
|
|
31279
|
-
model =
|
|
31280
|
-
modelVersion =
|
|
31281
|
-
capacity =
|
|
31282
|
-
subscription =
|
|
31283
|
-
skipQuotaCheck =
|
|
31284
|
-
output =
|
|
31760
|
+
resourceGroup = Option54.String("--resource-group");
|
|
31761
|
+
location = Option54.String("--location");
|
|
31762
|
+
account = Option54.String("--account");
|
|
31763
|
+
project = Option54.String("--project", "m8t");
|
|
31764
|
+
model = Option54.String("--model", "gpt-4.1-mini");
|
|
31765
|
+
modelVersion = Option54.String("--model-version", "2025-04-14");
|
|
31766
|
+
capacity = Option54.String("--capacity", "50");
|
|
31767
|
+
subscription = Option54.String("--subscription");
|
|
31768
|
+
skipQuotaCheck = Option54.Boolean("--skip-quota-check", false);
|
|
31769
|
+
output = Option54.String("--output");
|
|
31285
31770
|
async executeCommand() {
|
|
31286
31771
|
const mode = resolveOutputMode(
|
|
31287
31772
|
this.output,
|
|
@@ -31353,22 +31838,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
31353
31838
|
};
|
|
31354
31839
|
|
|
31355
31840
|
// src/commands/foundry/await-ready.ts
|
|
31356
|
-
import { Command as
|
|
31841
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
31357
31842
|
import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
|
|
31358
31843
|
init_errors();
|
|
31359
31844
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
31360
31845
|
static paths = [["foundry", "await-ready"]];
|
|
31361
|
-
static usage =
|
|
31846
|
+
static usage = Command58.Usage({
|
|
31362
31847
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
31363
31848
|
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
31849
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
31365
31850
|
});
|
|
31366
|
-
endpoint =
|
|
31367
|
-
consecutive =
|
|
31368
|
-
attempts =
|
|
31369
|
-
interval =
|
|
31370
|
-
subscription =
|
|
31371
|
-
output =
|
|
31851
|
+
endpoint = Option55.String("--endpoint");
|
|
31852
|
+
consecutive = Option55.String("--consecutive", "3");
|
|
31853
|
+
attempts = Option55.String("--attempts", "60");
|
|
31854
|
+
interval = Option55.String("--interval", "5");
|
|
31855
|
+
subscription = Option55.String("--subscription");
|
|
31856
|
+
output = Option55.String("--output");
|
|
31372
31857
|
async executeCommand() {
|
|
31373
31858
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
31374
31859
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -31402,7 +31887,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
31402
31887
|
};
|
|
31403
31888
|
|
|
31404
31889
|
// src/commands/bootstrap/preflight.ts
|
|
31405
|
-
import { Command as
|
|
31890
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
31406
31891
|
|
|
31407
31892
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
31408
31893
|
var EVENT_TIERS = {
|
|
@@ -31454,7 +31939,7 @@ function preflightRenderable(results) {
|
|
|
31454
31939
|
}
|
|
31455
31940
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
31456
31941
|
static paths = [["bootstrap", "preflight"]];
|
|
31457
|
-
static usage =
|
|
31942
|
+
static usage = Command59.Usage({
|
|
31458
31943
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
31459
31944
|
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
31945
|
examples: [
|
|
@@ -31463,9 +31948,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
31463
31948
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
31464
31949
|
]
|
|
31465
31950
|
});
|
|
31466
|
-
clientId =
|
|
31467
|
-
subscription =
|
|
31468
|
-
location =
|
|
31951
|
+
clientId = Option56.String("--client-id");
|
|
31952
|
+
subscription = Option56.String("--subscription");
|
|
31953
|
+
location = Option56.String("--location", {
|
|
31469
31954
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
31470
31955
|
});
|
|
31471
31956
|
async executeCommand() {
|
|
@@ -31565,7 +32050,7 @@ ${colors.error(" " + why)}
|
|
|
31565
32050
|
import * as fs34 from "fs";
|
|
31566
32051
|
import * as os15 from "os";
|
|
31567
32052
|
import * as path37 from "path";
|
|
31568
|
-
import { Command as
|
|
32053
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
31569
32054
|
init_errors();
|
|
31570
32055
|
|
|
31571
32056
|
// src/lib/bootstrap-mi.ts
|
|
@@ -31930,12 +32415,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
|
|
|
31930
32415
|
// src/commands/bootstrap/launch.ts
|
|
31931
32416
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
31932
32417
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
31933
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
32418
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.74";
|
|
31934
32419
|
var ACI_NAME = "m8t-installer";
|
|
31935
32420
|
var MI_NAME = "m8t-installer-mi";
|
|
31936
32421
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
31937
32422
|
static paths = [["bootstrap", "launch"]];
|
|
31938
|
-
static usage =
|
|
32423
|
+
static usage = Command60.Usage({
|
|
31939
32424
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
31940
32425
|
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
32426
|
examples: [
|
|
@@ -31946,31 +32431,31 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
31946
32431
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
31947
32432
|
]
|
|
31948
32433
|
});
|
|
31949
|
-
location =
|
|
31950
|
-
resourceGroup =
|
|
31951
|
-
clientId =
|
|
31952
|
-
subscription =
|
|
31953
|
-
installerTag =
|
|
32434
|
+
location = Option57.String("--location");
|
|
32435
|
+
resourceGroup = Option57.String("--resource-group");
|
|
32436
|
+
clientId = Option57.String("--client-id");
|
|
32437
|
+
subscription = Option57.String("--subscription");
|
|
32438
|
+
installerTag = Option57.String("--installer-tag");
|
|
31954
32439
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
31955
32440
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
31956
32441
|
// Wins over --installer-tag / the pinned default.
|
|
31957
|
-
installerImage =
|
|
31958
|
-
gatewayImageRef =
|
|
31959
|
-
githubAppCreds =
|
|
31960
|
-
contactEmail =
|
|
31961
|
-
company =
|
|
32442
|
+
installerImage = Option57.String("--installer-image");
|
|
32443
|
+
gatewayImageRef = Option57.String("--gateway-image-ref");
|
|
32444
|
+
githubAppCreds = Option57.String("--github-app-creds");
|
|
32445
|
+
contactEmail = Option57.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
32446
|
+
company = Option57.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
31962
32447
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
31963
32448
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
31964
32449
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
31965
32450
|
// the target; --resource-group is what CHOOSES it.
|
|
31966
|
-
reinstallInto =
|
|
31967
|
-
org =
|
|
31968
|
-
noBrains =
|
|
32451
|
+
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." });
|
|
32452
|
+
org = Option57.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
|
|
32453
|
+
noBrains = Option57.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
|
|
31969
32454
|
// The opt-out TELEMETRY.md names. Without it there is no way to decline at
|
|
31970
32455
|
// install time — the ACI env is built entirely from these options, so a
|
|
31971
32456
|
// founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
|
|
31972
32457
|
// privacy notice that documents a control has to be a control that exists.
|
|
31973
|
-
foundryTracing =
|
|
32458
|
+
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
32459
|
async executeCommand() {
|
|
31975
32460
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
31976
32461
|
if (!location) {
|
|
@@ -32132,7 +32617,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
32132
32617
|
};
|
|
32133
32618
|
|
|
32134
32619
|
// src/commands/bootstrap/status.ts
|
|
32135
|
-
import { Command as
|
|
32620
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
32136
32621
|
init_errors();
|
|
32137
32622
|
|
|
32138
32623
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -34309,7 +34794,7 @@ async function uninstallCompanion(options) {
|
|
|
34309
34794
|
// src/commands/companion/install.ts
|
|
34310
34795
|
import * as os19 from "os";
|
|
34311
34796
|
import * as path43 from "path";
|
|
34312
|
-
import { Command as
|
|
34797
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
34313
34798
|
|
|
34314
34799
|
// src/lib/companion-channel.ts
|
|
34315
34800
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -34440,7 +34925,7 @@ Version: ${state.version}
|
|
|
34440
34925
|
}
|
|
34441
34926
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
34442
34927
|
static paths = [["companion", "install"]];
|
|
34443
|
-
static usage =
|
|
34928
|
+
static usage = Command61.Usage({
|
|
34444
34929
|
description: "Install the desktop companions for this user from the release channel.",
|
|
34445
34930
|
examples: [
|
|
34446
34931
|
["Install the released build", "$0 companion install"],
|
|
@@ -34450,10 +34935,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
34450
34935
|
]
|
|
34451
34936
|
]
|
|
34452
34937
|
});
|
|
34453
|
-
from =
|
|
34938
|
+
from = Option58.String("--from", {
|
|
34454
34939
|
description: "A locally staged build directory instead of the released one."
|
|
34455
34940
|
});
|
|
34456
|
-
resourceGroup =
|
|
34941
|
+
resourceGroup = Option58.String("--resource-group", {
|
|
34457
34942
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
34458
34943
|
});
|
|
34459
34944
|
async executeCommand() {
|
|
@@ -34668,7 +35153,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
34668
35153
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
34669
35154
|
// through — it prints a deprecation notice and does the right thing.
|
|
34670
35155
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
34671
|
-
static usage =
|
|
35156
|
+
static usage = Command62.Usage({
|
|
34672
35157
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
34673
35158
|
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
35159
|
examples: [
|
|
@@ -34677,12 +35162,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
34677
35162
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
34678
35163
|
]
|
|
34679
35164
|
});
|
|
34680
|
-
watch =
|
|
34681
|
-
output =
|
|
34682
|
-
repoRoot =
|
|
34683
|
-
finalize =
|
|
34684
|
-
subscription =
|
|
34685
|
-
resourceGroup =
|
|
35165
|
+
watch = Option59.Boolean("--watch", false);
|
|
35166
|
+
output = Option59.String("--output");
|
|
35167
|
+
repoRoot = Option59.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
|
|
35168
|
+
finalize = Option59.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
|
|
35169
|
+
subscription = Option59.String("--subscription");
|
|
35170
|
+
resourceGroup = Option59.String("--resource-group");
|
|
34686
35171
|
async executeCommand() {
|
|
34687
35172
|
const state = await readBootstrapState();
|
|
34688
35173
|
if (!state) {
|
|
@@ -34815,7 +35300,7 @@ function formatStatus(d) {
|
|
|
34815
35300
|
}
|
|
34816
35301
|
|
|
34817
35302
|
// src/commands/bootstrap/reap.ts
|
|
34818
|
-
import { Command as
|
|
35303
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34819
35304
|
init_errors();
|
|
34820
35305
|
|
|
34821
35306
|
// src/lib/bootstrap-reap.ts
|
|
@@ -34909,7 +35394,7 @@ async function reapInstaller(opts) {
|
|
|
34909
35394
|
// src/commands/bootstrap/reap.ts
|
|
34910
35395
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
34911
35396
|
static paths = [["bootstrap", "reap"]];
|
|
34912
|
-
static usage =
|
|
35397
|
+
static usage = Command63.Usage({
|
|
34913
35398
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
34914
35399
|
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
35400
|
examples: [
|
|
@@ -34919,9 +35404,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
34919
35404
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
34920
35405
|
]
|
|
34921
35406
|
});
|
|
34922
|
-
force =
|
|
34923
|
-
sweepOrphans =
|
|
34924
|
-
yes =
|
|
35407
|
+
force = Option60.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
|
|
35408
|
+
sweepOrphans = Option60.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
|
|
35409
|
+
yes = Option60.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
|
|
34925
35410
|
async executeCommand() {
|
|
34926
35411
|
if (this.sweepOrphans === true) {
|
|
34927
35412
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -35015,7 +35500,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
35015
35500
|
};
|
|
35016
35501
|
|
|
35017
35502
|
// src/commands/bootstrap/ui.ts
|
|
35018
|
-
import { Command as
|
|
35503
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
35019
35504
|
|
|
35020
35505
|
// src/lib/bootstrap-ui.ts
|
|
35021
35506
|
import * as fs40 from "fs";
|
|
@@ -35090,7 +35575,7 @@ function renderDeprecationNotice() {
|
|
|
35090
35575
|
}
|
|
35091
35576
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
35092
35577
|
static paths = [["bootstrap", "ui"]];
|
|
35093
|
-
static usage =
|
|
35578
|
+
static usage = Command64.Usage({
|
|
35094
35579
|
description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
|
|
35095
35580
|
details: [
|
|
35096
35581
|
"The local onboarding chat has been retired. Your details are collected by",
|
|
@@ -35110,14 +35595,14 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
35110
35595
|
// Accepted and ignored, deliberately: removing them would turn an old script's
|
|
35111
35596
|
// harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
|
|
35112
35597
|
// the whole surface when the rewrite lands.
|
|
35113
|
-
repoRoot =
|
|
35114
|
-
port =
|
|
35115
|
-
endpoint =
|
|
35116
|
-
prepOnly =
|
|
35117
|
-
skipInstall =
|
|
35118
|
-
foreground =
|
|
35119
|
-
voice =
|
|
35120
|
-
stop =
|
|
35598
|
+
repoRoot = Option61.String("--repo-root", { description: "Ignored (deprecated)." });
|
|
35599
|
+
port = Option61.String("--port", "3000", { description: "Ignored (deprecated)." });
|
|
35600
|
+
endpoint = Option61.String("--endpoint", { description: "Ignored (deprecated)." });
|
|
35601
|
+
prepOnly = Option61.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
|
|
35602
|
+
skipInstall = Option61.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
|
|
35603
|
+
foreground = Option61.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
|
|
35604
|
+
voice = Option61.Boolean("--voice", false, { description: "Ignored (deprecated)." });
|
|
35605
|
+
stop = Option61.Boolean("--stop", false, {
|
|
35121
35606
|
description: "Shut down a local chat UI left running by an earlier version of this command."
|
|
35122
35607
|
});
|
|
35123
35608
|
// Not `async`: there is nothing left to await. Everything this command used to
|
|
@@ -35139,7 +35624,7 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
35139
35624
|
|
|
35140
35625
|
// src/commands/bootstrap/profile.ts
|
|
35141
35626
|
import * as readline3 from "readline/promises";
|
|
35142
|
-
import { Command as
|
|
35627
|
+
import { Command as Command65, Option as Option62 } from "clipanion";
|
|
35143
35628
|
|
|
35144
35629
|
// src/lib/profile-collect.ts
|
|
35145
35630
|
init_errors();
|
|
@@ -35308,7 +35793,7 @@ async function openChatInvite(deps = {}) {
|
|
|
35308
35793
|
// src/commands/bootstrap/profile.ts
|
|
35309
35794
|
var BootstrapProfileCommand = class extends M8tCommand {
|
|
35310
35795
|
static paths = [["bootstrap", "profile"]];
|
|
35311
|
-
static usage =
|
|
35796
|
+
static usage = Command65.Usage({
|
|
35312
35797
|
description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
|
|
35313
35798
|
details: [
|
|
35314
35799
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
|
|
@@ -35328,12 +35813,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
|
|
|
35328
35813
|
["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
|
|
35329
35814
|
]
|
|
35330
35815
|
});
|
|
35331
|
-
founderEmail =
|
|
35332
|
-
advisorName =
|
|
35333
|
-
advisorEmail =
|
|
35334
|
-
noAdvisor =
|
|
35335
|
-
noChat =
|
|
35336
|
-
print =
|
|
35816
|
+
founderEmail = Option62.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
|
|
35817
|
+
advisorName = Option62.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
|
|
35818
|
+
advisorEmail = Option62.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
|
|
35819
|
+
noAdvisor = Option62.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
|
|
35820
|
+
noChat = Option62.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
|
|
35821
|
+
print = Option62.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
|
|
35337
35822
|
async executeCommand() {
|
|
35338
35823
|
const stdin = this.context.stdin;
|
|
35339
35824
|
const stdout = this.context.stdout;
|
|
@@ -35426,10 +35911,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
|
|
|
35426
35911
|
};
|
|
35427
35912
|
|
|
35428
35913
|
// src/commands/bootstrap/seed-profile.ts
|
|
35429
|
-
import { Command as
|
|
35914
|
+
import { Command as Command66, Option as Option63 } from "clipanion";
|
|
35430
35915
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
35431
35916
|
static paths = [["bootstrap", "seed-profile"]];
|
|
35432
|
-
static usage =
|
|
35917
|
+
static usage = Command66.Usage({
|
|
35433
35918
|
description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
|
|
35434
35919
|
details: [
|
|
35435
35920
|
"Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
|
|
@@ -35446,11 +35931,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
35446
35931
|
["Seed now (idempotent)", "$0 bootstrap seed-profile"]
|
|
35447
35932
|
]
|
|
35448
35933
|
});
|
|
35449
|
-
endpoint =
|
|
35450
|
-
brain =
|
|
35451
|
-
watch =
|
|
35452
|
-
timeout =
|
|
35453
|
-
githubAppCreds =
|
|
35934
|
+
endpoint = Option63.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
35935
|
+
brain = Option63.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
35936
|
+
watch = Option63.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
35937
|
+
timeout = Option63.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
35938
|
+
githubAppCreds = Option63.String("--github-app-creds");
|
|
35454
35939
|
async executeCommand() {
|
|
35455
35940
|
const ctx = await resolveSeedContext({
|
|
35456
35941
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -35538,7 +36023,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
35538
36023
|
import * as fs41 from "fs";
|
|
35539
36024
|
import * as os22 from "os";
|
|
35540
36025
|
import * as path46 from "path";
|
|
35541
|
-
import { Command as
|
|
36026
|
+
import { Command as Command67, Option as Option64 } from "clipanion";
|
|
35542
36027
|
init_errors();
|
|
35543
36028
|
|
|
35544
36029
|
// src/lib/telemetry-enroll.ts
|
|
@@ -35620,7 +36105,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
35620
36105
|
}
|
|
35621
36106
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
35622
36107
|
static paths = [["telemetry", "enroll"]];
|
|
35623
|
-
static usage =
|
|
36108
|
+
static usage = Command67.Usage({
|
|
35624
36109
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
35625
36110
|
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
36111
|
examples: [
|
|
@@ -35628,11 +36113,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
35628
36113
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
35629
36114
|
]
|
|
35630
36115
|
});
|
|
35631
|
-
company =
|
|
35632
|
-
contactEmail =
|
|
35633
|
-
subscription =
|
|
35634
|
-
resourceGroup =
|
|
35635
|
-
force =
|
|
36116
|
+
company = Option64.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
36117
|
+
contactEmail = Option64.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
36118
|
+
subscription = Option64.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
36119
|
+
resourceGroup = Option64.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
36120
|
+
force = Option64.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
35636
36121
|
async executeCommand() {
|
|
35637
36122
|
const account = await getAzAccount();
|
|
35638
36123
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -35682,7 +36167,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
35682
36167
|
};
|
|
35683
36168
|
|
|
35684
36169
|
// src/commands/companion/bridge.ts
|
|
35685
|
-
import { Command as
|
|
36170
|
+
import { Command as Command68, Option as Option65 } from "clipanion";
|
|
35686
36171
|
|
|
35687
36172
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
35688
36173
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -37149,14 +37634,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
37149
37634
|
return 3;
|
|
37150
37635
|
}
|
|
37151
37636
|
}
|
|
37152
|
-
var CompanionBridgeCommand = class extends
|
|
37637
|
+
var CompanionBridgeCommand = class extends Command68 {
|
|
37153
37638
|
static paths = [["companion", "_bridge"]];
|
|
37154
37639
|
/**
|
|
37155
37640
|
* One process serving many requests instead of one per request, so the
|
|
37156
37641
|
* session keeps its authenticated context between them. A CLI predating the
|
|
37157
37642
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
37158
37643
|
*/
|
|
37159
|
-
serve =
|
|
37644
|
+
serve = Option65.Boolean("--serve", false);
|
|
37160
37645
|
async execute() {
|
|
37161
37646
|
if (this.serve) {
|
|
37162
37647
|
return runCompanionBridgeServe(
|
|
@@ -37174,7 +37659,7 @@ var CompanionBridgeCommand = class extends Command67 {
|
|
|
37174
37659
|
};
|
|
37175
37660
|
|
|
37176
37661
|
// src/commands/companion/status.ts
|
|
37177
|
-
import { Command as
|
|
37662
|
+
import { Command as Command69 } from "clipanion";
|
|
37178
37663
|
async function withTimeout(work, ms) {
|
|
37179
37664
|
let timer;
|
|
37180
37665
|
try {
|
|
@@ -37246,7 +37731,7 @@ Run: m8t companion install
|
|
|
37246
37731
|
}
|
|
37247
37732
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
37248
37733
|
static paths = [["companion", "status"]];
|
|
37249
|
-
static usage =
|
|
37734
|
+
static usage = Command69.Usage({
|
|
37250
37735
|
description: "Verify the installed desktop companion without launching it."
|
|
37251
37736
|
});
|
|
37252
37737
|
async executeCommand() {
|
|
@@ -37260,7 +37745,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
37260
37745
|
};
|
|
37261
37746
|
|
|
37262
37747
|
// src/commands/companion/repair.ts
|
|
37263
|
-
import { Command as
|
|
37748
|
+
import { Command as Command70, Option as Option66 } from "clipanion";
|
|
37264
37749
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
37265
37750
|
const state = await repair();
|
|
37266
37751
|
if (state.state === "not-released") {
|
|
@@ -37279,10 +37764,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
37279
37764
|
}
|
|
37280
37765
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
37281
37766
|
static paths = [["companion", "repair"]];
|
|
37282
|
-
static usage =
|
|
37767
|
+
static usage = Command70.Usage({
|
|
37283
37768
|
description: "Restore the desktop companions and start-at-login state."
|
|
37284
37769
|
});
|
|
37285
|
-
resourceGroup =
|
|
37770
|
+
resourceGroup = Option66.String("--resource-group", {
|
|
37286
37771
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
37287
37772
|
});
|
|
37288
37773
|
async executeCommand() {
|
|
@@ -37300,7 +37785,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
37300
37785
|
};
|
|
37301
37786
|
|
|
37302
37787
|
// src/commands/companion/uninstall.ts
|
|
37303
|
-
import { Command as
|
|
37788
|
+
import { Command as Command71 } from "clipanion";
|
|
37304
37789
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
37305
37790
|
const state = await uninstall();
|
|
37306
37791
|
if (state.state !== "not-installed") {
|
|
@@ -37312,7 +37797,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
37312
37797
|
}
|
|
37313
37798
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
37314
37799
|
static paths = [["companion", "uninstall"]];
|
|
37315
|
-
static usage =
|
|
37800
|
+
static usage = Command71.Usage({
|
|
37316
37801
|
description: "Remove only this user's desktop companion installation."
|
|
37317
37802
|
});
|
|
37318
37803
|
async executeCommand() {
|
|
@@ -37373,6 +37858,7 @@ cli.register(PlatformConvergeCommand);
|
|
|
37373
37858
|
cli.register(PlatformClearIntentCommand);
|
|
37374
37859
|
cli.register(PlatformRequestUpdateCommand);
|
|
37375
37860
|
cli.register(PlatformSeedStampCommand);
|
|
37861
|
+
cli.register(PlatformGatewayAdoptCommand);
|
|
37376
37862
|
cli.register(PlatformPolicyCommand);
|
|
37377
37863
|
cli.register(PlatformEnableCostReportCommand);
|
|
37378
37864
|
cli.register(PlatformEmailCommand);
|