@m8t-stack/cli 0.2.103 → 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 +839 -348
- 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();
|
|
@@ -19479,7 +19479,7 @@ var SIZE_PRESETS = {
|
|
|
19479
19479
|
};
|
|
19480
19480
|
var DEFAULT_REGISTRY = "ghcr.io/m8t-labs";
|
|
19481
19481
|
var DEFAULT_IMAGE = "m8t-coding-agent";
|
|
19482
|
-
var DEFAULT_TAG = "v0.1.
|
|
19482
|
+
var DEFAULT_TAG = "v0.1.4";
|
|
19483
19483
|
var DEFAULT_MODEL = "gpt-4.1-mini";
|
|
19484
19484
|
var NAME_RE = /^[a-z0-9-]+$/;
|
|
19485
19485
|
var CoderDeployCommand = class extends M8tCommand {
|
|
@@ -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));
|
|
@@ -21164,6 +21217,9 @@ function buildBicepParams(p) {
|
|
|
21164
21217
|
];
|
|
21165
21218
|
if (p.gatewayImageRef) params.push(`gatewayImageRef=${p.gatewayImageRef}`);
|
|
21166
21219
|
if (p.foundryTracingMode) params.push(`foundryTracingMode=${p.foundryTracingMode}`);
|
|
21220
|
+
if (p.deployFoundryTracingConnection !== void 0) {
|
|
21221
|
+
params.push(`deployFoundryTracingConnection=${String(p.deployFoundryTracingConnection)}`);
|
|
21222
|
+
}
|
|
21167
21223
|
if (p.assignSubscriptionRoles !== void 0) params.push(`assignSubscriptionRoles=${String(p.assignSubscriptionRoles)}`);
|
|
21168
21224
|
if (p.provisionUpdater !== void 0) params.push(`provisionUpdater=${String(p.provisionUpdater)}`);
|
|
21169
21225
|
if (p.installerImage !== void 0) params.push(`installerImage=${p.installerImage}`);
|
|
@@ -21418,6 +21474,414 @@ function voiceSecretParam(read, gatewayName) {
|
|
|
21418
21474
|
};
|
|
21419
21475
|
}
|
|
21420
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
|
+
|
|
21421
21885
|
// src/lib/platform-converge.ts
|
|
21422
21886
|
var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
|
|
21423
21887
|
function seedSetRev(manifest) {
|
|
@@ -21481,8 +21945,10 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
|
|
|
21481
21945
|
continue;
|
|
21482
21946
|
}
|
|
21483
21947
|
const from = cur?.tag ?? null;
|
|
21484
|
-
|
|
21485
|
-
|
|
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 });
|
|
21486
21952
|
else skipped.push({ component, reason: "up-to-date" });
|
|
21487
21953
|
}
|
|
21488
21954
|
return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
|
|
@@ -21625,10 +22091,20 @@ function seedStamp(prior, manifest, plan) {
|
|
|
21625
22091
|
};
|
|
21626
22092
|
}
|
|
21627
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
|
+
}
|
|
21628
22105
|
const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
|
|
21629
22106
|
const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
|
|
21630
22107
|
const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
|
|
21631
|
-
const digest = ctx.manifest.components.gateway.digest;
|
|
21632
22108
|
const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to, toDigest: digest });
|
|
21633
22109
|
switch (plan.kind) {
|
|
21634
22110
|
case "refuse-byoc":
|
|
@@ -21711,6 +22187,7 @@ async function resolveBicepParamsForConverge(ctx, opts = {}) {
|
|
|
21711
22187
|
acrPullIdentityResourceId,
|
|
21712
22188
|
acrResourceId,
|
|
21713
22189
|
foundryTracingMode: opts.foundryTracingMode,
|
|
22190
|
+
...opts.deployFoundryTracingConnection !== void 0 ? { deployFoundryTracingConnection: opts.deployFoundryTracingConnection } : {},
|
|
21714
22191
|
...opts.assignSubscriptionRoles !== void 0 ? { assignSubscriptionRoles: opts.assignSubscriptionRoles } : {},
|
|
21715
22192
|
...opts.voiceInternalSecret ? { voiceInternalSecret: opts.voiceInternalSecret } : {},
|
|
21716
22193
|
...opts.channelUrl ? { channelUrl: opts.channelUrl } : {}
|
|
@@ -22126,10 +22603,10 @@ async function resolveExecutorAgentName(args) {
|
|
|
22126
22603
|
}
|
|
22127
22604
|
|
|
22128
22605
|
// src/lib/platform-infra-params.ts
|
|
22129
|
-
import { TableClient as
|
|
22606
|
+
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
22130
22607
|
async function openInfraParamsTable(opts) {
|
|
22131
22608
|
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
22132
|
-
return new
|
|
22609
|
+
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
22133
22610
|
}
|
|
22134
22611
|
async function readInfraParams(client) {
|
|
22135
22612
|
try {
|
|
@@ -22645,7 +23122,7 @@ async function buildConvergeDeps(args) {
|
|
|
22645
23122
|
hint: "Run `m8t platform enable-auto-update` against this deployment (it captures the full bicep parameter set), then let the next tick retry."
|
|
22646
23123
|
});
|
|
22647
23124
|
}
|
|
22648
|
-
args.onProgress?.("
|
|
23125
|
+
args.onProgress?.("cross-scope modules skipped: updater is RG-scoped; subscription roles and the existing Foundry tracing connection are preserved.");
|
|
22649
23126
|
const gatewayName = args.suffix ? `m8t-gateway-${args.suffix}` : void 0;
|
|
22650
23127
|
const secret = gatewayName ? voiceSecretParam(
|
|
22651
23128
|
await readLiveVoiceInternalSecret({
|
|
@@ -22660,6 +23137,7 @@ async function buildConvergeDeps(args) {
|
|
|
22660
23137
|
const opts = {
|
|
22661
23138
|
suffix: args.suffix,
|
|
22662
23139
|
assignSubscriptionRoles: false,
|
|
23140
|
+
deployFoundryTracingConnection: false,
|
|
22663
23141
|
// Unconditional: the guard above has already refused an absent row. The
|
|
22664
23142
|
// ternary that used to be here read as "these are optional" — which is
|
|
22665
23143
|
// how the empty-endpoint death got written in the first place.
|
|
@@ -23001,73 +23479,6 @@ function resolveHeadlessContextFromEnv(env) {
|
|
|
23001
23479
|
};
|
|
23002
23480
|
}
|
|
23003
23481
|
|
|
23004
|
-
// src/lib/apply-request-store.ts
|
|
23005
|
-
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
23006
|
-
async function openApplyTable(opts) {
|
|
23007
|
-
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
23008
|
-
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
23009
|
-
}
|
|
23010
|
-
function hasStatus(e, n) {
|
|
23011
|
-
return e.statusCode === n;
|
|
23012
|
-
}
|
|
23013
|
-
async function readApplyRequest(client) {
|
|
23014
|
-
try {
|
|
23015
|
-
const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
|
|
23016
|
-
return { ...entityToApplyRequest(row), etag: row.etag };
|
|
23017
|
-
} catch (e) {
|
|
23018
|
-
if (hasStatus(e, 404)) return null;
|
|
23019
|
-
throw e;
|
|
23020
|
-
}
|
|
23021
|
-
}
|
|
23022
|
-
async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
|
|
23023
|
-
const cur = await readApplyRequest(client);
|
|
23024
|
-
if (!cur) return null;
|
|
23025
|
-
const leaseExpired = isInFlight(cur.status) && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
|
|
23026
|
-
const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
|
|
23027
|
-
if (!claimable) return null;
|
|
23028
|
-
const nowMs = Date.parse(nowIso);
|
|
23029
|
-
const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
|
|
23030
|
-
if (leaseExpired && cur.attempt >= MAX_LEASE_TAKEOVERS) {
|
|
23031
|
-
const held = {
|
|
23032
|
-
...cur,
|
|
23033
|
-
status: "held",
|
|
23034
|
-
breaker: "held",
|
|
23035
|
-
result: {
|
|
23036
|
-
appliedVersion: cur.result?.appliedVersion ?? null,
|
|
23037
|
-
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`
|
|
23038
|
-
},
|
|
23039
|
-
updatedAt: nowIso
|
|
23040
|
-
};
|
|
23041
|
-
try {
|
|
23042
|
-
await client.updateEntity({ ...applyRequestToEntity(held), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
23043
|
-
} catch (e) {
|
|
23044
|
-
if (!hasStatus(e, 412)) throw e;
|
|
23045
|
-
}
|
|
23046
|
-
return null;
|
|
23047
|
-
}
|
|
23048
|
-
const claimed = {
|
|
23049
|
-
...cur,
|
|
23050
|
-
status: "claimed",
|
|
23051
|
-
claimedBy: executionId,
|
|
23052
|
-
leaseUntil,
|
|
23053
|
-
...leaseExpired ? { attempt: cur.attempt + 1 } : {},
|
|
23054
|
-
updatedAt: nowIso
|
|
23055
|
-
};
|
|
23056
|
-
try {
|
|
23057
|
-
await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
23058
|
-
return claimed;
|
|
23059
|
-
} catch (e) {
|
|
23060
|
-
if (hasStatus(e, 412)) return null;
|
|
23061
|
-
throw e;
|
|
23062
|
-
}
|
|
23063
|
-
}
|
|
23064
|
-
async function patchApplyRequest(client, mutate) {
|
|
23065
|
-
const cur = await readApplyRequest(client);
|
|
23066
|
-
if (!cur) return;
|
|
23067
|
-
const next = mutate(cur);
|
|
23068
|
-
await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
23069
|
-
}
|
|
23070
|
-
|
|
23071
23482
|
// src/lib/rail-preconditions.ts
|
|
23072
23483
|
init_errors();
|
|
23073
23484
|
function assertBakedContentPresent(repoRoot) {
|
|
@@ -23228,7 +23639,7 @@ async function runGate(args, applied, budgetMs) {
|
|
|
23228
23639
|
}
|
|
23229
23640
|
async function rollbackOrHold(args, notify, error) {
|
|
23230
23641
|
const target = args.plan.targetVersion;
|
|
23231
|
-
const rollbackTarget = args.
|
|
23642
|
+
const rollbackTarget = args.preApplyVersion;
|
|
23232
23643
|
const current = await readBreaker(args.client);
|
|
23233
23644
|
const next = nextBreaker(current);
|
|
23234
23645
|
if (next === "held") {
|
|
@@ -23492,6 +23903,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
23492
23903
|
plan,
|
|
23493
23904
|
ctx: applyCtx,
|
|
23494
23905
|
stamp: stamp ?? seedStampFor(plan),
|
|
23906
|
+
preApplyVersion: stamp?.platformVersion ?? null,
|
|
23495
23907
|
manifest,
|
|
23496
23908
|
credential: ctx.credential,
|
|
23497
23909
|
endpoint: ctx.endpoint,
|
|
@@ -23717,8 +24129,8 @@ var PlatformRequestUpdateCommand = class extends M8tCommand {
|
|
|
23717
24129
|
description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
|
|
23718
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.",
|
|
23719
24131
|
examples: [
|
|
23720
|
-
["Request an update", "m8t platform request-update --version 0.7.
|
|
23721
|
-
["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>"]
|
|
23722
24134
|
]
|
|
23723
24135
|
});
|
|
23724
24136
|
version = Option35.String("--version", { description: "The platform version to converge to." });
|
|
@@ -24016,9 +24428,87 @@ var PlatformSeedStampCommand = class extends M8tCommand {
|
|
|
24016
24428
|
}
|
|
24017
24429
|
};
|
|
24018
24430
|
|
|
24019
|
-
// src/commands/platform/
|
|
24431
|
+
// src/commands/platform/gateway-adopt.ts
|
|
24020
24432
|
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
24021
|
-
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";
|
|
24022
24512
|
init_errors();
|
|
24023
24513
|
|
|
24024
24514
|
// src/lib/platform-policy.ts
|
|
@@ -24073,16 +24563,16 @@ async function readPolicy(opts) {
|
|
|
24073
24563
|
// src/commands/platform/policy.ts
|
|
24074
24564
|
var PlatformPolicyCommand = class extends M8tCommand {
|
|
24075
24565
|
static paths = [["platform", "policy"]];
|
|
24076
|
-
static usage =
|
|
24566
|
+
static usage = Command41.Usage({
|
|
24077
24567
|
description: "Show or set how this install handles available platform updates.",
|
|
24078
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.",
|
|
24079
24569
|
examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
|
|
24080
24570
|
});
|
|
24081
|
-
set =
|
|
24082
|
-
subscription =
|
|
24083
|
-
resourceGroup =
|
|
24084
|
-
miClientId =
|
|
24085
|
-
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");
|
|
24086
24576
|
async executeCommand() {
|
|
24087
24577
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
24088
24578
|
const need = (v, flag) => {
|
|
@@ -24095,7 +24585,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
24095
24585
|
this.context.stderr.write(`${m}
|
|
24096
24586
|
`);
|
|
24097
24587
|
};
|
|
24098
|
-
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();
|
|
24099
24589
|
const ctx = {
|
|
24100
24590
|
credential: credential2,
|
|
24101
24591
|
subscriptionId: need(this.subscription, "--subscription"),
|
|
@@ -24128,7 +24618,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
24128
24618
|
};
|
|
24129
24619
|
|
|
24130
24620
|
// src/commands/platform/enable-cost-report.ts
|
|
24131
|
-
import { Command as
|
|
24621
|
+
import { Command as Command42, Option as Option39 } from "clipanion";
|
|
24132
24622
|
|
|
24133
24623
|
// src/lib/wire-gateway-acs.ts
|
|
24134
24624
|
init_rbac();
|
|
@@ -24168,18 +24658,18 @@ async function wireGatewayForAcs(args) {
|
|
|
24168
24658
|
init_errors();
|
|
24169
24659
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
24170
24660
|
static paths = [["platform", "enable-cost-report"]];
|
|
24171
|
-
static usage =
|
|
24661
|
+
static usage = Command42.Usage({
|
|
24172
24662
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
24173
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')."
|
|
24174
24664
|
});
|
|
24175
|
-
subscription =
|
|
24176
|
-
resourceGroup =
|
|
24665
|
+
subscription = Option39.String("--subscription");
|
|
24666
|
+
resourceGroup = Option39.String("--resource-group", {
|
|
24177
24667
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24178
24668
|
});
|
|
24179
|
-
acsEndpoint =
|
|
24180
|
-
acsSender =
|
|
24181
|
-
acsResourceId =
|
|
24182
|
-
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");
|
|
24183
24673
|
async executeCommand() {
|
|
24184
24674
|
const mode = resolveOutputMode(
|
|
24185
24675
|
this.output,
|
|
@@ -24253,13 +24743,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
24253
24743
|
};
|
|
24254
24744
|
|
|
24255
24745
|
// src/commands/platform/email.ts
|
|
24256
|
-
import { Command as
|
|
24257
|
-
import { DefaultAzureCredential as
|
|
24746
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
24747
|
+
import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
|
|
24258
24748
|
import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
|
|
24259
24749
|
init_errors();
|
|
24260
24750
|
var PlatformEmailCommand = class extends M8tCommand {
|
|
24261
24751
|
static paths = [["platform", "email"]];
|
|
24262
|
-
static usage =
|
|
24752
|
+
static usage = Command43.Usage({
|
|
24263
24753
|
description: "Turn outbound email (advisor handoffs) on or off for this install.",
|
|
24264
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.",
|
|
24265
24755
|
examples: [
|
|
@@ -24267,19 +24757,19 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24267
24757
|
["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
|
|
24268
24758
|
]
|
|
24269
24759
|
});
|
|
24270
|
-
state =
|
|
24271
|
-
subscription =
|
|
24272
|
-
resourceGroup =
|
|
24760
|
+
state = Option40.String({ required: true, name: "on|off" });
|
|
24761
|
+
subscription = Option40.String("--subscription");
|
|
24762
|
+
resourceGroup = Option40.String("--resource-group", {
|
|
24273
24763
|
description: "m8t resource group, to disambiguate in a multi-deployment subscription."
|
|
24274
24764
|
});
|
|
24275
|
-
agent =
|
|
24276
|
-
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", {
|
|
24277
24767
|
description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
|
|
24278
24768
|
});
|
|
24279
|
-
endpoint =
|
|
24769
|
+
endpoint = Option40.String("--endpoint", {
|
|
24280
24770
|
description: "Foundry project endpoint, to disambiguate a subscription holding several."
|
|
24281
24771
|
});
|
|
24282
|
-
output =
|
|
24772
|
+
output = Option40.String("--output");
|
|
24283
24773
|
async executeCommand() {
|
|
24284
24774
|
const wanted = this.state.trim().toLowerCase();
|
|
24285
24775
|
if (wanted !== "on" && wanted !== "off") {
|
|
@@ -24303,7 +24793,7 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24303
24793
|
});
|
|
24304
24794
|
const { resourceGroup } = parseContainerAppResourceId(ctx.containerAppResourceId);
|
|
24305
24795
|
const subscriptionId = ctx.subscriptionId;
|
|
24306
|
-
const credential2 = new
|
|
24796
|
+
const credential2 = new DefaultAzureCredential23();
|
|
24307
24797
|
const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
|
|
24308
24798
|
if (stamp === null) {
|
|
24309
24799
|
throw new LocalCliError({
|
|
@@ -24429,45 +24919,45 @@ var PlatformEmailCommand = class extends M8tCommand {
|
|
|
24429
24919
|
};
|
|
24430
24920
|
|
|
24431
24921
|
// src/commands/platform/enable-auto-update.ts
|
|
24432
|
-
import { Command as
|
|
24922
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
24433
24923
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
24434
|
-
import { DefaultAzureCredential as
|
|
24924
|
+
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
24435
24925
|
init_errors();
|
|
24436
24926
|
var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
24437
24927
|
static paths = [["platform", "enable-auto-update"]];
|
|
24438
|
-
static usage =
|
|
24928
|
+
static usage = Command44.Usage({
|
|
24439
24929
|
category: "Platform",
|
|
24440
24930
|
description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
|
|
24441
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."
|
|
24442
24932
|
});
|
|
24443
|
-
subscription =
|
|
24444
|
-
resourceGroup =
|
|
24933
|
+
subscription = Option41.String("--subscription");
|
|
24934
|
+
resourceGroup = Option41.String("--resource-group", {
|
|
24445
24935
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24446
24936
|
});
|
|
24447
|
-
suffix =
|
|
24937
|
+
suffix = Option41.String("--suffix", {
|
|
24448
24938
|
description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
|
|
24449
24939
|
});
|
|
24450
|
-
installerImage =
|
|
24940
|
+
installerImage = Option41.String("--installer-image", {
|
|
24451
24941
|
required: true,
|
|
24452
24942
|
description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
|
|
24453
24943
|
});
|
|
24454
|
-
updateCron =
|
|
24944
|
+
updateCron = Option41.String("--update-cron", {
|
|
24455
24945
|
description: "Cron schedule for the updater job (bicep default applies when omitted)."
|
|
24456
24946
|
});
|
|
24457
|
-
channelUrl =
|
|
24947
|
+
channelUrl = Option41.String("--channel-url", {
|
|
24458
24948
|
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
24459
24949
|
});
|
|
24460
|
-
location =
|
|
24950
|
+
location = Option41.String("--location", {
|
|
24461
24951
|
description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
|
|
24462
24952
|
});
|
|
24463
|
-
foundryTracing =
|
|
24953
|
+
foundryTracing = Option41.String("--foundry-tracing", {
|
|
24464
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)."
|
|
24465
24955
|
});
|
|
24466
|
-
endpoint =
|
|
24956
|
+
endpoint = Option41.String("--endpoint", {
|
|
24467
24957
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
24468
24958
|
});
|
|
24469
|
-
yes =
|
|
24470
|
-
output =
|
|
24959
|
+
yes = Option41.Boolean("--yes", false);
|
|
24960
|
+
output = Option41.String("--output");
|
|
24471
24961
|
async executeCommand() {
|
|
24472
24962
|
const mode = resolveOutputMode(
|
|
24473
24963
|
this.output,
|
|
@@ -24486,7 +24976,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
24486
24976
|
resourceGroup: this.resourceGroup
|
|
24487
24977
|
});
|
|
24488
24978
|
const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
24489
|
-
const credential2 = new
|
|
24979
|
+
const credential2 = new DefaultAzureCredential24();
|
|
24490
24980
|
const account = await getAzAccount();
|
|
24491
24981
|
const subscriptionId = this.subscription ?? account.subscriptionId;
|
|
24492
24982
|
const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
|
|
@@ -24648,7 +25138,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
24648
25138
|
};
|
|
24649
25139
|
|
|
24650
25140
|
// src/commands/deploy.ts
|
|
24651
|
-
import { Command as
|
|
25141
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
24652
25142
|
|
|
24653
25143
|
// src/lib/app-reg.ts
|
|
24654
25144
|
init_esm();
|
|
@@ -25212,15 +25702,15 @@ function classifyWhatIf(changes) {
|
|
|
25212
25702
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
|
|
25213
25703
|
var DeployCommand = class extends M8tCommand {
|
|
25214
25704
|
static paths = [["deploy"]];
|
|
25215
|
-
static usage =
|
|
25705
|
+
static usage = Command45.Usage({
|
|
25216
25706
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
25217
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."
|
|
25218
25708
|
});
|
|
25219
|
-
subscription =
|
|
25220
|
-
resourceGroup =
|
|
25221
|
-
location =
|
|
25222
|
-
suffix =
|
|
25223
|
-
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);
|
|
25224
25714
|
// Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
|
|
25225
25715
|
// from-zero case. It exists because the gateway and the voice relay do NOT
|
|
25226
25716
|
// always run the same image: a converge preserves a BYOC gateway on its own
|
|
@@ -25228,28 +25718,28 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25228
25718
|
// (`--what-if`) that can only express one image therefore reports the other
|
|
25229
25719
|
// app as drift on every single run, forever — which is exactly what the
|
|
25230
25720
|
// infra-drift gate did from 2026-08-11.
|
|
25231
|
-
gatewayImageRef =
|
|
25232
|
-
acrPullIdentity =
|
|
25233
|
-
acrResourceId =
|
|
25234
|
-
foundryEndpoint =
|
|
25235
|
-
foundryResourceId =
|
|
25236
|
-
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");
|
|
25237
25727
|
// project | account | skip (bicep default: project)
|
|
25238
|
-
clientId =
|
|
25239
|
-
whatIf =
|
|
25728
|
+
clientId = Option42.String("--client-id");
|
|
25729
|
+
whatIf = Option42.Boolean("--what-if", false);
|
|
25240
25730
|
// Only meaningful with --what-if. Routes the comparison through the
|
|
25241
25731
|
// value-free renderers (see ./lib/whatif-redact.js) instead of the default
|
|
25242
25732
|
// before/after renderer. Defaults false so a local, interactive run keeps
|
|
25243
25733
|
// showing values — that is the whole diagnostic point of --what-if.
|
|
25244
25734
|
// Automation that forwards this output anywhere non-private (a CI log, an
|
|
25245
25735
|
// issue) MUST pass --redact.
|
|
25246
|
-
redact =
|
|
25247
|
-
output =
|
|
25736
|
+
redact = Option42.Boolean("--redact", false);
|
|
25737
|
+
output = Option42.String("--output");
|
|
25248
25738
|
// Subscription-scoped role assignments. Omitted ⇒ the template default (true).
|
|
25249
25739
|
// Pass false when deploying as a principal scoped to the resource group only:
|
|
25250
25740
|
// it cannot deploy at subscription scope, and those assignments persist
|
|
25251
25741
|
// idempotently from the initial deployment anyway.
|
|
25252
|
-
assignSubscriptionRoles =
|
|
25742
|
+
assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
|
|
25253
25743
|
/**
|
|
25254
25744
|
* The installer image the updater Container-Apps Job runs.
|
|
25255
25745
|
*
|
|
@@ -25260,23 +25750,23 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25260
25750
|
* than that — the comparison proposes REMOVING an updater job that exists and
|
|
25261
25751
|
* should, and reports it as drift on every single run.
|
|
25262
25752
|
*/
|
|
25263
|
-
installerImage =
|
|
25753
|
+
installerImage = Option42.String("--installer-image");
|
|
25264
25754
|
// Referee — all optional, undefined by default ⇒ the bicep defaults
|
|
25265
25755
|
// apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
|
|
25266
25756
|
// empty). Only pass these when explicitly enabling the referee exam stack.
|
|
25267
|
-
gatewayCpu =
|
|
25268
|
-
gatewayMemory =
|
|
25269
|
-
refereeEnabled =
|
|
25270
|
-
refereeBrainRepos =
|
|
25271
|
-
refereeFeedRepo =
|
|
25272
|
-
refereeInstallationId =
|
|
25273
|
-
refereeWebhookHmacKvUri =
|
|
25274
|
-
examKvUri =
|
|
25275
|
-
examLaWorkspaceId =
|
|
25276
|
-
brainEvalDeployment =
|
|
25277
|
-
brainAppLogin =
|
|
25278
|
-
refereeCheckpointDir =
|
|
25279
|
-
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");
|
|
25280
25770
|
async executeCommand() {
|
|
25281
25771
|
const mode = resolveOutputMode(
|
|
25282
25772
|
this.output,
|
|
@@ -25460,7 +25950,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
25460
25950
|
|
|
25461
25951
|
// src/commands/eval/skill.ts
|
|
25462
25952
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
25463
|
-
import { Command as
|
|
25953
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
25464
25954
|
init_errors();
|
|
25465
25955
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
25466
25956
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -25485,14 +25975,14 @@ function parseVerdict(stdout) {
|
|
|
25485
25975
|
}
|
|
25486
25976
|
var EvalSkillCommand = class extends M8tCommand {
|
|
25487
25977
|
static paths = [["eval", "skill"]];
|
|
25488
|
-
static usage =
|
|
25978
|
+
static usage = Command46.Usage({
|
|
25489
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)."
|
|
25490
25980
|
});
|
|
25491
|
-
candidate =
|
|
25492
|
-
skillsDir =
|
|
25493
|
-
noJudge =
|
|
25494
|
-
deployment =
|
|
25495
|
-
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");
|
|
25496
25986
|
executeCommand() {
|
|
25497
25987
|
return Promise.resolve(this._runCommand());
|
|
25498
25988
|
}
|
|
@@ -25551,7 +26041,7 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
25551
26041
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
25552
26042
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
|
|
25553
26043
|
import { join as join30 } from "path";
|
|
25554
|
-
import { Command as
|
|
26044
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
25555
26045
|
init_errors();
|
|
25556
26046
|
init_esm();
|
|
25557
26047
|
function parseArmToken(tok, opts) {
|
|
@@ -25781,24 +26271,24 @@ function buildPlan(args) {
|
|
|
25781
26271
|
}
|
|
25782
26272
|
var EvalExamCommand = class extends M8tCommand {
|
|
25783
26273
|
static paths = [["eval", "exam"]];
|
|
25784
|
-
static usage =
|
|
26274
|
+
static usage = Command47.Usage({
|
|
25785
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."
|
|
25786
26276
|
});
|
|
25787
|
-
worker =
|
|
25788
|
-
arms =
|
|
25789
|
-
taskSet =
|
|
25790
|
-
examType =
|
|
25791
|
-
skill =
|
|
25792
|
-
reps =
|
|
25793
|
-
probes =
|
|
25794
|
-
pool =
|
|
25795
|
-
out =
|
|
25796
|
-
dryRun =
|
|
25797
|
-
keepArms =
|
|
25798
|
-
allowStub =
|
|
25799
|
-
deployment =
|
|
25800
|
-
output =
|
|
25801
|
-
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");
|
|
25802
26292
|
async executeCommand() {
|
|
25803
26293
|
await Promise.resolve();
|
|
25804
26294
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -25913,10 +26403,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
25913
26403
|
};
|
|
25914
26404
|
|
|
25915
26405
|
// src/commands/version.ts
|
|
25916
|
-
import { Command as
|
|
26406
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
25917
26407
|
var VersionCommand = class extends M8tCommand {
|
|
25918
26408
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
25919
|
-
static usage =
|
|
26409
|
+
static usage = Command48.Usage({
|
|
25920
26410
|
description: "Print the CLI version.",
|
|
25921
26411
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
25922
26412
|
examples: [
|
|
@@ -25924,8 +26414,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
25924
26414
|
["Print as JSON", "$0 version --output json"]
|
|
25925
26415
|
]
|
|
25926
26416
|
});
|
|
25927
|
-
output =
|
|
25928
|
-
verbose =
|
|
26417
|
+
output = Option45.String("--output", { description: "pretty | json | auto (default)" });
|
|
26418
|
+
verbose = Option45.Boolean("--verbose", false);
|
|
25929
26419
|
executeCommand() {
|
|
25930
26420
|
const mode = resolveOutputMode(
|
|
25931
26421
|
this.output ?? "auto",
|
|
@@ -25956,18 +26446,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
25956
26446
|
};
|
|
25957
26447
|
|
|
25958
26448
|
// src/commands/whoami.ts
|
|
25959
|
-
import { Command as
|
|
26449
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
25960
26450
|
var WhoamiCommand = class extends M8tCommand {
|
|
25961
26451
|
static paths = [["whoami"]];
|
|
25962
|
-
static usage =
|
|
26452
|
+
static usage = Command49.Usage({
|
|
25963
26453
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
25964
26454
|
});
|
|
25965
|
-
output =
|
|
25966
|
-
verbose =
|
|
25967
|
-
subscription =
|
|
26455
|
+
output = Option46.String("--output");
|
|
26456
|
+
verbose = Option46.Boolean("--verbose", false);
|
|
26457
|
+
subscription = Option46.String("--subscription", {
|
|
25968
26458
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
25969
26459
|
});
|
|
25970
|
-
resourceGroup =
|
|
26460
|
+
resourceGroup = Option46.String("--resource-group", {
|
|
25971
26461
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
25972
26462
|
});
|
|
25973
26463
|
async executeCommand() {
|
|
@@ -26032,7 +26522,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
26032
26522
|
};
|
|
26033
26523
|
|
|
26034
26524
|
// src/commands/status.ts
|
|
26035
|
-
import { Command as
|
|
26525
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
26036
26526
|
|
|
26037
26527
|
// src/lib/azd.ts
|
|
26038
26528
|
init_errors();
|
|
@@ -26097,10 +26587,10 @@ async function resolveLocalContext() {
|
|
|
26097
26587
|
// src/commands/status.ts
|
|
26098
26588
|
var StatusCommand = class extends M8tCommand {
|
|
26099
26589
|
static paths = [["status"]];
|
|
26100
|
-
static usage =
|
|
26590
|
+
static usage = Command50.Usage({
|
|
26101
26591
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
26102
26592
|
});
|
|
26103
|
-
output =
|
|
26593
|
+
output = Option47.String("--output");
|
|
26104
26594
|
async executeCommand() {
|
|
26105
26595
|
const mode = resolveOutputMode(
|
|
26106
26596
|
this.output,
|
|
@@ -26138,8 +26628,8 @@ var StatusCommand = class extends M8tCommand {
|
|
|
26138
26628
|
};
|
|
26139
26629
|
|
|
26140
26630
|
// src/commands/doctor.ts
|
|
26141
|
-
import { Command as
|
|
26142
|
-
import { DefaultAzureCredential as
|
|
26631
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
26632
|
+
import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
|
|
26143
26633
|
import * as fs30 from "fs";
|
|
26144
26634
|
import * as os12 from "os";
|
|
26145
26635
|
import * as path33 from "path";
|
|
@@ -26686,12 +27176,12 @@ function probeLegacyStateDir() {
|
|
|
26686
27176
|
}
|
|
26687
27177
|
var DoctorCommand = class extends M8tCommand {
|
|
26688
27178
|
static paths = [["doctor"]];
|
|
26689
|
-
static usage =
|
|
27179
|
+
static usage = Command51.Usage({
|
|
26690
27180
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
26691
27181
|
});
|
|
26692
|
-
output =
|
|
26693
|
-
agent =
|
|
26694
|
-
resourceGroup =
|
|
27182
|
+
output = Option48.String("--output");
|
|
27183
|
+
agent = Option48.String("--agent");
|
|
27184
|
+
resourceGroup = Option48.String("--resource-group", {
|
|
26695
27185
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
26696
27186
|
});
|
|
26697
27187
|
async executeCommand() {
|
|
@@ -26764,7 +27254,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26764
27254
|
let kvStatus = 0;
|
|
26765
27255
|
if (kv) {
|
|
26766
27256
|
try {
|
|
26767
|
-
const token = await new
|
|
27257
|
+
const token = await new DefaultAzureCredential25().getToken("https://vault.azure.net/.default");
|
|
26768
27258
|
const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
|
|
26769
27259
|
headers: { Authorization: `Bearer ${token.token}` }
|
|
26770
27260
|
});
|
|
@@ -26783,7 +27273,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26783
27273
|
emit(checkModelQuota(deployments, usages));
|
|
26784
27274
|
checking("outbound email");
|
|
26785
27275
|
try {
|
|
26786
|
-
const credential2 = new
|
|
27276
|
+
const credential2 = new DefaultAzureCredential25();
|
|
26787
27277
|
const outcome = platformRg && platformSub ? await readStampOutcome({ credential: credential2, subscriptionId: platformSub, resourceGroup: platformRg }).catch(
|
|
26788
27278
|
() => ({ source: "unreadable" })
|
|
26789
27279
|
) : { source: "unreadable" };
|
|
@@ -26838,7 +27328,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26838
27328
|
if (typeof this.agent === "string" && this.agent) {
|
|
26839
27329
|
checking(`delivery grant for ${this.agent}`);
|
|
26840
27330
|
try {
|
|
26841
|
-
const credential2 = new
|
|
27331
|
+
const credential2 = new DefaultAzureCredential25();
|
|
26842
27332
|
const cur = await getAgentVersion({
|
|
26843
27333
|
credential: credential2,
|
|
26844
27334
|
projectEndpoint: foundry.projectEndpoint,
|
|
@@ -26881,11 +27371,11 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
26881
27371
|
};
|
|
26882
27372
|
|
|
26883
27373
|
// src/commands/prereqs.ts
|
|
26884
|
-
import { Command as
|
|
27374
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
26885
27375
|
init_errors();
|
|
26886
27376
|
|
|
26887
27377
|
// src/lib/prereq-deps.ts
|
|
26888
|
-
import { DefaultAzureCredential as
|
|
27378
|
+
import { DefaultAzureCredential as DefaultAzureCredential26 } from "@azure/identity";
|
|
26889
27379
|
|
|
26890
27380
|
// src/lib/bootstrap-preflight.ts
|
|
26891
27381
|
var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
|
|
@@ -26997,7 +27487,7 @@ function buildPrereqDeps(opts = {}) {
|
|
|
26997
27487
|
probeRedirectUri,
|
|
26998
27488
|
fixFoundryAccess,
|
|
26999
27489
|
fixKeyVaultAccess,
|
|
27000
|
-
credential: () => credentialSingleton2 ??= new
|
|
27490
|
+
credential: () => credentialSingleton2 ??= new DefaultAzureCredential26()
|
|
27001
27491
|
};
|
|
27002
27492
|
}
|
|
27003
27493
|
|
|
@@ -27587,7 +28077,7 @@ function renderVerdict(v) {
|
|
|
27587
28077
|
}
|
|
27588
28078
|
var PrereqsCommand = class extends M8tCommand {
|
|
27589
28079
|
static paths = [["prereqs"]];
|
|
27590
|
-
static usage =
|
|
28080
|
+
static usage = Command52.Usage({
|
|
27591
28081
|
description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
|
|
27592
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.",
|
|
27593
28083
|
examples: [
|
|
@@ -27598,15 +28088,15 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
27598
28088
|
["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
|
|
27599
28089
|
]
|
|
27600
28090
|
});
|
|
27601
|
-
fix =
|
|
27602
|
-
for_ =
|
|
27603
|
-
phase =
|
|
27604
|
-
region =
|
|
27605
|
-
model =
|
|
27606
|
-
clientId =
|
|
27607
|
-
subscription =
|
|
27608
|
-
resourceGroup =
|
|
27609
|
-
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");
|
|
27610
28100
|
async executeCommand() {
|
|
27611
28101
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
27612
28102
|
const json = mode === "json";
|
|
@@ -27662,7 +28152,7 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
27662
28152
|
};
|
|
27663
28153
|
|
|
27664
28154
|
// src/commands/switch.ts
|
|
27665
|
-
import { Command as
|
|
28155
|
+
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
27666
28156
|
|
|
27667
28157
|
// src/lib/profiles.ts
|
|
27668
28158
|
import * as fs31 from "fs/promises";
|
|
@@ -27802,14 +28292,14 @@ async function profileSwitch(name, asName) {
|
|
|
27802
28292
|
init_errors();
|
|
27803
28293
|
var SwitchCommand = class extends M8tCommand {
|
|
27804
28294
|
static paths = [["switch"]];
|
|
27805
|
-
static usage =
|
|
28295
|
+
static usage = Command53.Usage({
|
|
27806
28296
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
27807
28297
|
});
|
|
27808
|
-
profile =
|
|
27809
|
-
subscription =
|
|
27810
|
-
list =
|
|
27811
|
-
as =
|
|
27812
|
-
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");
|
|
27813
28303
|
async executeCommand() {
|
|
27814
28304
|
const mode = resolveOutputMode(
|
|
27815
28305
|
this.output,
|
|
@@ -27866,7 +28356,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
27866
28356
|
|
|
27867
28357
|
// src/commands/open.ts
|
|
27868
28358
|
import { spawn as spawn5 } from "child_process";
|
|
27869
|
-
import { Command as
|
|
28359
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
27870
28360
|
|
|
27871
28361
|
// src/lib/open-targets.ts
|
|
27872
28362
|
init_errors();
|
|
@@ -27912,14 +28402,14 @@ function openUrl(url) {
|
|
|
27912
28402
|
}
|
|
27913
28403
|
var OpenCommand = class extends M8tCommand {
|
|
27914
28404
|
static paths = [["open"]];
|
|
27915
|
-
static usage =
|
|
28405
|
+
static usage = Command54.Usage({
|
|
27916
28406
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
27917
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)."
|
|
27918
28408
|
});
|
|
27919
|
-
target =
|
|
27920
|
-
print =
|
|
27921
|
-
output =
|
|
27922
|
-
resourceGroup =
|
|
28409
|
+
target = Option51.String({ required: false });
|
|
28410
|
+
print = Option51.Boolean("--print", false);
|
|
28411
|
+
output = Option51.String("--output");
|
|
28412
|
+
resourceGroup = Option51.String("--resource-group", {
|
|
27923
28413
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
27924
28414
|
});
|
|
27925
28415
|
async executeCommand() {
|
|
@@ -27963,7 +28453,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
27963
28453
|
};
|
|
27964
28454
|
|
|
27965
28455
|
// src/commands/dream/run.ts
|
|
27966
|
-
import { Command as
|
|
28456
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
27967
28457
|
import { AzureCliCredential } from "@azure/identity";
|
|
27968
28458
|
import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
27969
28459
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
@@ -30363,28 +30853,28 @@ function redactTranscripts(input) {
|
|
|
30363
30853
|
}
|
|
30364
30854
|
var DreamRunCommand = class extends M8tCommand {
|
|
30365
30855
|
static paths = [["dream", "run"]];
|
|
30366
|
-
static usage =
|
|
30856
|
+
static usage = Command55.Usage({
|
|
30367
30857
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
|
|
30368
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."
|
|
30369
30859
|
});
|
|
30370
|
-
worker =
|
|
30860
|
+
worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
30371
30861
|
// Opting IN to the side effects, rather than opting out of them. This command's
|
|
30372
30862
|
// own help has always described a dry run, but the bare invocation used to take
|
|
30373
30863
|
// the live branch — a real model call and a commit to the brain repo — so anyone
|
|
30374
30864
|
// acting on `--help` got the opposite of what they read.
|
|
30375
|
-
live =
|
|
30376
|
-
dryRun =
|
|
30377
|
-
since =
|
|
30378
|
-
reset =
|
|
30379
|
-
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, {
|
|
30380
30870
|
description: "Print transcript bodies (default: metadata only)."
|
|
30381
30871
|
});
|
|
30382
|
-
subscription =
|
|
30383
|
-
endpoint =
|
|
30384
|
-
storageAccount =
|
|
30872
|
+
subscription = Option52.String("--subscription");
|
|
30873
|
+
endpoint = Option52.String("--endpoint");
|
|
30874
|
+
storageAccount = Option52.String("--storage-account", {
|
|
30385
30875
|
description: "Ledger storage account name (skips tag-based discovery)"
|
|
30386
30876
|
});
|
|
30387
|
-
output =
|
|
30877
|
+
output = Option52.String("--output");
|
|
30388
30878
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
30389
30879
|
deps;
|
|
30390
30880
|
async executeCommand() {
|
|
@@ -30735,7 +31225,7 @@ function defaultDeps(overrides) {
|
|
|
30735
31225
|
|
|
30736
31226
|
// src/commands/conversations/sweep.ts
|
|
30737
31227
|
import { createHash as createHash5 } from "crypto";
|
|
30738
|
-
import { Command as
|
|
31228
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
30739
31229
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
30740
31230
|
import { TableClient as TableClient8 } from "@azure/data-tables";
|
|
30741
31231
|
import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
|
|
@@ -30861,7 +31351,7 @@ function defaultDeps2() {
|
|
|
30861
31351
|
}
|
|
30862
31352
|
var ConversationsSweepCommand = class extends M8tCommand {
|
|
30863
31353
|
static paths = [["conversations", "sweep"]];
|
|
30864
|
-
static usage =
|
|
31354
|
+
static usage = Command56.Usage({
|
|
30865
31355
|
category: "Conversations",
|
|
30866
31356
|
description: "Delete expired public-visitor conversations (dry run by default)",
|
|
30867
31357
|
details: `
|
|
@@ -30877,22 +31367,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
30877
31367
|
["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
|
|
30878
31368
|
]
|
|
30879
31369
|
});
|
|
30880
|
-
doDelete =
|
|
31370
|
+
doDelete = Option53.Boolean("--delete", false, {
|
|
30881
31371
|
description: "Perform deletions (without this flag the command only reports)"
|
|
30882
31372
|
});
|
|
30883
|
-
principal =
|
|
31373
|
+
principal = Option53.String("--principal", {
|
|
30884
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)"
|
|
30885
31375
|
});
|
|
30886
|
-
max =
|
|
30887
|
-
graceDays =
|
|
31376
|
+
max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
|
|
31377
|
+
graceDays = Option53.String("--grace-days", "14", {
|
|
30888
31378
|
description: "Days past the 30-day key life before a conversation is eligible"
|
|
30889
31379
|
});
|
|
30890
|
-
lookbackDays =
|
|
31380
|
+
lookbackDays = Option53.String("--lookback-days", "180", {
|
|
30891
31381
|
description: "How far back to scan ledger activity for candidates"
|
|
30892
31382
|
});
|
|
30893
|
-
subscription =
|
|
30894
|
-
endpoint =
|
|
30895
|
-
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", {
|
|
30896
31386
|
description: "Ledger storage account name (skips tag-based discovery)"
|
|
30897
31387
|
});
|
|
30898
31388
|
deps = defaultDeps2();
|
|
@@ -31020,7 +31510,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
31020
31510
|
};
|
|
31021
31511
|
|
|
31022
31512
|
// src/commands/foundry/create.ts
|
|
31023
|
-
import { Command as
|
|
31513
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
31024
31514
|
|
|
31025
31515
|
// src/lib/foundry-create.ts
|
|
31026
31516
|
init_errors();
|
|
@@ -31258,7 +31748,7 @@ async function createFoundryProject(args) {
|
|
|
31258
31748
|
init_errors();
|
|
31259
31749
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
31260
31750
|
static paths = [["foundry", "create"]];
|
|
31261
|
-
static usage =
|
|
31751
|
+
static usage = Command57.Usage({
|
|
31262
31752
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
31263
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).",
|
|
31264
31754
|
examples: [
|
|
@@ -31267,16 +31757,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
31267
31757
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
31268
31758
|
]
|
|
31269
31759
|
});
|
|
31270
|
-
resourceGroup =
|
|
31271
|
-
location =
|
|
31272
|
-
account =
|
|
31273
|
-
project =
|
|
31274
|
-
model =
|
|
31275
|
-
modelVersion =
|
|
31276
|
-
capacity =
|
|
31277
|
-
subscription =
|
|
31278
|
-
skipQuotaCheck =
|
|
31279
|
-
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");
|
|
31280
31770
|
async executeCommand() {
|
|
31281
31771
|
const mode = resolveOutputMode(
|
|
31282
31772
|
this.output,
|
|
@@ -31348,22 +31838,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
31348
31838
|
};
|
|
31349
31839
|
|
|
31350
31840
|
// src/commands/foundry/await-ready.ts
|
|
31351
|
-
import { Command as
|
|
31841
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
31352
31842
|
import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
|
|
31353
31843
|
init_errors();
|
|
31354
31844
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
31355
31845
|
static paths = [["foundry", "await-ready"]];
|
|
31356
|
-
static usage =
|
|
31846
|
+
static usage = Command58.Usage({
|
|
31357
31847
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
31358
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.",
|
|
31359
31849
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
31360
31850
|
});
|
|
31361
|
-
endpoint =
|
|
31362
|
-
consecutive =
|
|
31363
|
-
attempts =
|
|
31364
|
-
interval =
|
|
31365
|
-
subscription =
|
|
31366
|
-
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");
|
|
31367
31857
|
async executeCommand() {
|
|
31368
31858
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
31369
31859
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -31397,7 +31887,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
31397
31887
|
};
|
|
31398
31888
|
|
|
31399
31889
|
// src/commands/bootstrap/preflight.ts
|
|
31400
|
-
import { Command as
|
|
31890
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
31401
31891
|
|
|
31402
31892
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
31403
31893
|
var EVENT_TIERS = {
|
|
@@ -31449,7 +31939,7 @@ function preflightRenderable(results) {
|
|
|
31449
31939
|
}
|
|
31450
31940
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
31451
31941
|
static paths = [["bootstrap", "preflight"]];
|
|
31452
|
-
static usage =
|
|
31942
|
+
static usage = Command59.Usage({
|
|
31453
31943
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
31454
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.",
|
|
31455
31945
|
examples: [
|
|
@@ -31458,9 +31948,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
31458
31948
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
31459
31949
|
]
|
|
31460
31950
|
});
|
|
31461
|
-
clientId =
|
|
31462
|
-
subscription =
|
|
31463
|
-
location =
|
|
31951
|
+
clientId = Option56.String("--client-id");
|
|
31952
|
+
subscription = Option56.String("--subscription");
|
|
31953
|
+
location = Option56.String("--location", {
|
|
31464
31954
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
31465
31955
|
});
|
|
31466
31956
|
async executeCommand() {
|
|
@@ -31560,7 +32050,7 @@ ${colors.error(" " + why)}
|
|
|
31560
32050
|
import * as fs34 from "fs";
|
|
31561
32051
|
import * as os15 from "os";
|
|
31562
32052
|
import * as path37 from "path";
|
|
31563
|
-
import { Command as
|
|
32053
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
31564
32054
|
init_errors();
|
|
31565
32055
|
|
|
31566
32056
|
// src/lib/bootstrap-mi.ts
|
|
@@ -31925,12 +32415,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
|
|
|
31925
32415
|
// src/commands/bootstrap/launch.ts
|
|
31926
32416
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
31927
32417
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
31928
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
32418
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.74";
|
|
31929
32419
|
var ACI_NAME = "m8t-installer";
|
|
31930
32420
|
var MI_NAME = "m8t-installer-mi";
|
|
31931
32421
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
31932
32422
|
static paths = [["bootstrap", "launch"]];
|
|
31933
|
-
static usage =
|
|
32423
|
+
static usage = Command60.Usage({
|
|
31934
32424
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
31935
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.",
|
|
31936
32426
|
examples: [
|
|
@@ -31941,31 +32431,31 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
31941
32431
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
31942
32432
|
]
|
|
31943
32433
|
});
|
|
31944
|
-
location =
|
|
31945
|
-
resourceGroup =
|
|
31946
|
-
clientId =
|
|
31947
|
-
subscription =
|
|
31948
|
-
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");
|
|
31949
32439
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
31950
32440
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
31951
32441
|
// Wins over --installer-tag / the pinned default.
|
|
31952
|
-
installerImage =
|
|
31953
|
-
gatewayImageRef =
|
|
31954
|
-
githubAppCreds =
|
|
31955
|
-
contactEmail =
|
|
31956
|
-
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." });
|
|
31957
32447
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
31958
32448
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
31959
32449
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
31960
32450
|
// the target; --resource-group is what CHOOSES it.
|
|
31961
|
-
reinstallInto =
|
|
31962
|
-
org =
|
|
31963
|
-
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." });
|
|
31964
32454
|
// The opt-out TELEMETRY.md names. Without it there is no way to decline at
|
|
31965
32455
|
// install time — the ACI env is built entirely from these options, so a
|
|
31966
32456
|
// founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
|
|
31967
32457
|
// privacy notice that documents a control has to be a control that exists.
|
|
31968
|
-
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)." });
|
|
31969
32459
|
async executeCommand() {
|
|
31970
32460
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
31971
32461
|
if (!location) {
|
|
@@ -32127,7 +32617,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
32127
32617
|
};
|
|
32128
32618
|
|
|
32129
32619
|
// src/commands/bootstrap/status.ts
|
|
32130
|
-
import { Command as
|
|
32620
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
32131
32621
|
init_errors();
|
|
32132
32622
|
|
|
32133
32623
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -34304,7 +34794,7 @@ async function uninstallCompanion(options) {
|
|
|
34304
34794
|
// src/commands/companion/install.ts
|
|
34305
34795
|
import * as os19 from "os";
|
|
34306
34796
|
import * as path43 from "path";
|
|
34307
|
-
import { Command as
|
|
34797
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
34308
34798
|
|
|
34309
34799
|
// src/lib/companion-channel.ts
|
|
34310
34800
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -34435,7 +34925,7 @@ Version: ${state.version}
|
|
|
34435
34925
|
}
|
|
34436
34926
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
34437
34927
|
static paths = [["companion", "install"]];
|
|
34438
|
-
static usage =
|
|
34928
|
+
static usage = Command61.Usage({
|
|
34439
34929
|
description: "Install the desktop companions for this user from the release channel.",
|
|
34440
34930
|
examples: [
|
|
34441
34931
|
["Install the released build", "$0 companion install"],
|
|
@@ -34445,10 +34935,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
34445
34935
|
]
|
|
34446
34936
|
]
|
|
34447
34937
|
});
|
|
34448
|
-
from =
|
|
34938
|
+
from = Option58.String("--from", {
|
|
34449
34939
|
description: "A locally staged build directory instead of the released one."
|
|
34450
34940
|
});
|
|
34451
|
-
resourceGroup =
|
|
34941
|
+
resourceGroup = Option58.String("--resource-group", {
|
|
34452
34942
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
34453
34943
|
});
|
|
34454
34944
|
async executeCommand() {
|
|
@@ -34663,7 +35153,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
34663
35153
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
34664
35154
|
// through — it prints a deprecation notice and does the right thing.
|
|
34665
35155
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
34666
|
-
static usage =
|
|
35156
|
+
static usage = Command62.Usage({
|
|
34667
35157
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
34668
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.",
|
|
34669
35159
|
examples: [
|
|
@@ -34672,12 +35162,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
34672
35162
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
34673
35163
|
]
|
|
34674
35164
|
});
|
|
34675
|
-
watch =
|
|
34676
|
-
output =
|
|
34677
|
-
repoRoot =
|
|
34678
|
-
finalize =
|
|
34679
|
-
subscription =
|
|
34680
|
-
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");
|
|
34681
35171
|
async executeCommand() {
|
|
34682
35172
|
const state = await readBootstrapState();
|
|
34683
35173
|
if (!state) {
|
|
@@ -34810,7 +35300,7 @@ function formatStatus(d) {
|
|
|
34810
35300
|
}
|
|
34811
35301
|
|
|
34812
35302
|
// src/commands/bootstrap/reap.ts
|
|
34813
|
-
import { Command as
|
|
35303
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34814
35304
|
init_errors();
|
|
34815
35305
|
|
|
34816
35306
|
// src/lib/bootstrap-reap.ts
|
|
@@ -34904,7 +35394,7 @@ async function reapInstaller(opts) {
|
|
|
34904
35394
|
// src/commands/bootstrap/reap.ts
|
|
34905
35395
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
34906
35396
|
static paths = [["bootstrap", "reap"]];
|
|
34907
|
-
static usage =
|
|
35397
|
+
static usage = Command63.Usage({
|
|
34908
35398
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
34909
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.",
|
|
34910
35400
|
examples: [
|
|
@@ -34914,9 +35404,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
34914
35404
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
34915
35405
|
]
|
|
34916
35406
|
});
|
|
34917
|
-
force =
|
|
34918
|
-
sweepOrphans =
|
|
34919
|
-
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." });
|
|
34920
35410
|
async executeCommand() {
|
|
34921
35411
|
if (this.sweepOrphans === true) {
|
|
34922
35412
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -35010,7 +35500,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
35010
35500
|
};
|
|
35011
35501
|
|
|
35012
35502
|
// src/commands/bootstrap/ui.ts
|
|
35013
|
-
import { Command as
|
|
35503
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
35014
35504
|
|
|
35015
35505
|
// src/lib/bootstrap-ui.ts
|
|
35016
35506
|
import * as fs40 from "fs";
|
|
@@ -35085,7 +35575,7 @@ function renderDeprecationNotice() {
|
|
|
35085
35575
|
}
|
|
35086
35576
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
35087
35577
|
static paths = [["bootstrap", "ui"]];
|
|
35088
|
-
static usage =
|
|
35578
|
+
static usage = Command64.Usage({
|
|
35089
35579
|
description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
|
|
35090
35580
|
details: [
|
|
35091
35581
|
"The local onboarding chat has been retired. Your details are collected by",
|
|
@@ -35105,14 +35595,14 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
35105
35595
|
// Accepted and ignored, deliberately: removing them would turn an old script's
|
|
35106
35596
|
// harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
|
|
35107
35597
|
// the whole surface when the rewrite lands.
|
|
35108
|
-
repoRoot =
|
|
35109
|
-
port =
|
|
35110
|
-
endpoint =
|
|
35111
|
-
prepOnly =
|
|
35112
|
-
skipInstall =
|
|
35113
|
-
foreground =
|
|
35114
|
-
voice =
|
|
35115
|
-
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, {
|
|
35116
35606
|
description: "Shut down a local chat UI left running by an earlier version of this command."
|
|
35117
35607
|
});
|
|
35118
35608
|
// Not `async`: there is nothing left to await. Everything this command used to
|
|
@@ -35134,7 +35624,7 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
35134
35624
|
|
|
35135
35625
|
// src/commands/bootstrap/profile.ts
|
|
35136
35626
|
import * as readline3 from "readline/promises";
|
|
35137
|
-
import { Command as
|
|
35627
|
+
import { Command as Command65, Option as Option62 } from "clipanion";
|
|
35138
35628
|
|
|
35139
35629
|
// src/lib/profile-collect.ts
|
|
35140
35630
|
init_errors();
|
|
@@ -35303,7 +35793,7 @@ async function openChatInvite(deps = {}) {
|
|
|
35303
35793
|
// src/commands/bootstrap/profile.ts
|
|
35304
35794
|
var BootstrapProfileCommand = class extends M8tCommand {
|
|
35305
35795
|
static paths = [["bootstrap", "profile"]];
|
|
35306
|
-
static usage =
|
|
35796
|
+
static usage = Command65.Usage({
|
|
35307
35797
|
description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
|
|
35308
35798
|
details: [
|
|
35309
35799
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
|
|
@@ -35323,12 +35813,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
|
|
|
35323
35813
|
["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
|
|
35324
35814
|
]
|
|
35325
35815
|
});
|
|
35326
|
-
founderEmail =
|
|
35327
|
-
advisorName =
|
|
35328
|
-
advisorEmail =
|
|
35329
|
-
noAdvisor =
|
|
35330
|
-
noChat =
|
|
35331
|
-
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)." });
|
|
35332
35822
|
async executeCommand() {
|
|
35333
35823
|
const stdin = this.context.stdin;
|
|
35334
35824
|
const stdout = this.context.stdout;
|
|
@@ -35421,10 +35911,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
|
|
|
35421
35911
|
};
|
|
35422
35912
|
|
|
35423
35913
|
// src/commands/bootstrap/seed-profile.ts
|
|
35424
|
-
import { Command as
|
|
35914
|
+
import { Command as Command66, Option as Option63 } from "clipanion";
|
|
35425
35915
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
35426
35916
|
static paths = [["bootstrap", "seed-profile"]];
|
|
35427
|
-
static usage =
|
|
35917
|
+
static usage = Command66.Usage({
|
|
35428
35918
|
description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
|
|
35429
35919
|
details: [
|
|
35430
35920
|
"Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
|
|
@@ -35441,11 +35931,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
35441
35931
|
["Seed now (idempotent)", "$0 bootstrap seed-profile"]
|
|
35442
35932
|
]
|
|
35443
35933
|
});
|
|
35444
|
-
endpoint =
|
|
35445
|
-
brain =
|
|
35446
|
-
watch =
|
|
35447
|
-
timeout =
|
|
35448
|
-
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");
|
|
35449
35939
|
async executeCommand() {
|
|
35450
35940
|
const ctx = await resolveSeedContext({
|
|
35451
35941
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -35533,7 +36023,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
35533
36023
|
import * as fs41 from "fs";
|
|
35534
36024
|
import * as os22 from "os";
|
|
35535
36025
|
import * as path46 from "path";
|
|
35536
|
-
import { Command as
|
|
36026
|
+
import { Command as Command67, Option as Option64 } from "clipanion";
|
|
35537
36027
|
init_errors();
|
|
35538
36028
|
|
|
35539
36029
|
// src/lib/telemetry-enroll.ts
|
|
@@ -35615,7 +36105,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
35615
36105
|
}
|
|
35616
36106
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
35617
36107
|
static paths = [["telemetry", "enroll"]];
|
|
35618
|
-
static usage =
|
|
36108
|
+
static usage = Command67.Usage({
|
|
35619
36109
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
35620
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.",
|
|
35621
36111
|
examples: [
|
|
@@ -35623,11 +36113,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
35623
36113
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
35624
36114
|
]
|
|
35625
36115
|
});
|
|
35626
|
-
company =
|
|
35627
|
-
contactEmail =
|
|
35628
|
-
subscription =
|
|
35629
|
-
resourceGroup =
|
|
35630
|
-
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." });
|
|
35631
36121
|
async executeCommand() {
|
|
35632
36122
|
const account = await getAzAccount();
|
|
35633
36123
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -35677,7 +36167,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
35677
36167
|
};
|
|
35678
36168
|
|
|
35679
36169
|
// src/commands/companion/bridge.ts
|
|
35680
|
-
import { Command as
|
|
36170
|
+
import { Command as Command68, Option as Option65 } from "clipanion";
|
|
35681
36171
|
|
|
35682
36172
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
35683
36173
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -37144,14 +37634,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
37144
37634
|
return 3;
|
|
37145
37635
|
}
|
|
37146
37636
|
}
|
|
37147
|
-
var CompanionBridgeCommand = class extends
|
|
37637
|
+
var CompanionBridgeCommand = class extends Command68 {
|
|
37148
37638
|
static paths = [["companion", "_bridge"]];
|
|
37149
37639
|
/**
|
|
37150
37640
|
* One process serving many requests instead of one per request, so the
|
|
37151
37641
|
* session keeps its authenticated context between them. A CLI predating the
|
|
37152
37642
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
37153
37643
|
*/
|
|
37154
|
-
serve =
|
|
37644
|
+
serve = Option65.Boolean("--serve", false);
|
|
37155
37645
|
async execute() {
|
|
37156
37646
|
if (this.serve) {
|
|
37157
37647
|
return runCompanionBridgeServe(
|
|
@@ -37169,7 +37659,7 @@ var CompanionBridgeCommand = class extends Command67 {
|
|
|
37169
37659
|
};
|
|
37170
37660
|
|
|
37171
37661
|
// src/commands/companion/status.ts
|
|
37172
|
-
import { Command as
|
|
37662
|
+
import { Command as Command69 } from "clipanion";
|
|
37173
37663
|
async function withTimeout(work, ms) {
|
|
37174
37664
|
let timer;
|
|
37175
37665
|
try {
|
|
@@ -37241,7 +37731,7 @@ Run: m8t companion install
|
|
|
37241
37731
|
}
|
|
37242
37732
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
37243
37733
|
static paths = [["companion", "status"]];
|
|
37244
|
-
static usage =
|
|
37734
|
+
static usage = Command69.Usage({
|
|
37245
37735
|
description: "Verify the installed desktop companion without launching it."
|
|
37246
37736
|
});
|
|
37247
37737
|
async executeCommand() {
|
|
@@ -37255,7 +37745,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
37255
37745
|
};
|
|
37256
37746
|
|
|
37257
37747
|
// src/commands/companion/repair.ts
|
|
37258
|
-
import { Command as
|
|
37748
|
+
import { Command as Command70, Option as Option66 } from "clipanion";
|
|
37259
37749
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
37260
37750
|
const state = await repair();
|
|
37261
37751
|
if (state.state === "not-released") {
|
|
@@ -37274,10 +37764,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
37274
37764
|
}
|
|
37275
37765
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
37276
37766
|
static paths = [["companion", "repair"]];
|
|
37277
|
-
static usage =
|
|
37767
|
+
static usage = Command70.Usage({
|
|
37278
37768
|
description: "Restore the desktop companions and start-at-login state."
|
|
37279
37769
|
});
|
|
37280
|
-
resourceGroup =
|
|
37770
|
+
resourceGroup = Option66.String("--resource-group", {
|
|
37281
37771
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
37282
37772
|
});
|
|
37283
37773
|
async executeCommand() {
|
|
@@ -37295,7 +37785,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
37295
37785
|
};
|
|
37296
37786
|
|
|
37297
37787
|
// src/commands/companion/uninstall.ts
|
|
37298
|
-
import { Command as
|
|
37788
|
+
import { Command as Command71 } from "clipanion";
|
|
37299
37789
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
37300
37790
|
const state = await uninstall();
|
|
37301
37791
|
if (state.state !== "not-installed") {
|
|
@@ -37307,7 +37797,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
37307
37797
|
}
|
|
37308
37798
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
37309
37799
|
static paths = [["companion", "uninstall"]];
|
|
37310
|
-
static usage =
|
|
37800
|
+
static usage = Command71.Usage({
|
|
37311
37801
|
description: "Remove only this user's desktop companion installation."
|
|
37312
37802
|
});
|
|
37313
37803
|
async executeCommand() {
|
|
@@ -37368,6 +37858,7 @@ cli.register(PlatformConvergeCommand);
|
|
|
37368
37858
|
cli.register(PlatformClearIntentCommand);
|
|
37369
37859
|
cli.register(PlatformRequestUpdateCommand);
|
|
37370
37860
|
cli.register(PlatformSeedStampCommand);
|
|
37861
|
+
cli.register(PlatformGatewayAdoptCommand);
|
|
37371
37862
|
cli.register(PlatformPolicyCommand);
|
|
37372
37863
|
cli.register(PlatformEnableCostReportCommand);
|
|
37373
37864
|
cli.register(PlatformEmailCommand);
|