@m8t-stack/cli 0.2.80 → 0.2.81
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 +434 -110
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1400,7 +1400,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1400
1400
|
import { Builtins, Cli } from "clipanion";
|
|
1401
1401
|
|
|
1402
1402
|
// src/lib/package-version.ts
|
|
1403
|
-
var CLI_VERSION = "0.2.
|
|
1403
|
+
var CLI_VERSION = "0.2.81";
|
|
1404
1404
|
|
|
1405
1405
|
// src/lib/render-error.ts
|
|
1406
1406
|
init_errors();
|
|
@@ -26631,13 +26631,66 @@ import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
|
26631
26631
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
26632
26632
|
import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
|
|
26633
26633
|
|
|
26634
|
+
// ../../packages/agent-ledger/dist/esm/pii.js
|
|
26635
|
+
import { createHmac } from "crypto";
|
|
26636
|
+
var EMAIL_PLACEHOLDER = "[email]";
|
|
26637
|
+
var PHONE_PLACEHOLDER = "[phone]";
|
|
26638
|
+
var SECRET_PLACEHOLDER = "[secret]";
|
|
26639
|
+
var PEM_BLOCK_RE = /-----BEGIN [A-Z0-9 ]{0,32}PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]{0,32}PRIVATE KEY-----/g;
|
|
26640
|
+
var JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}(?:\.[A-Za-z0-9_-]{10,})?/g;
|
|
26641
|
+
var HTTP_CREDENTIAL_RE = /\b(Bearer|Basic)[ \t]+(?=[A-Za-z0-9._~+/=-]{0,80}[0-9+/=])[A-Za-z0-9._~+/=-]{16,}/gi;
|
|
26642
|
+
var KNOWN_PREFIX_RE = /(?<![A-Za-z0-9_-])(?:sk-[A-Za-z0-9_-]{16,}|rk-[A-Za-z0-9_-]{16,}|xox[a-z]-[A-Za-z0-9-]{10,}|gh[pousr]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{28,}|AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|ya29\.[A-Za-z0-9_-]{20,}|dckr_pat_[A-Za-z0-9_-]{20,}|shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32,})(?![A-Za-z0-9_-])/g;
|
|
26643
|
+
var KEYED_VALUE = `("[^"\\n]{8,}"|'[^'\\n]{8,}'|(?=[A-Za-z0-9+/=_.~%-]{0,64}[0-9+/=])[A-Za-z0-9+/=_.~%-]{8,})`;
|
|
26644
|
+
var KEYED_STRONG_SECRET_RE = new RegExp(`(?<![A-Za-z0-9])(shared[_-]?access[_-]?key|account[_-]?key|access[_-]?key|storage[_-]?key|api[_-]?key|apikey|x[_-]?api[_-]?key|client[_-]?secret|sas[_-]?token|access[_-]?token|refresh[_-]?token|auth[_-]?token|id[_-]?token|bot[_-]?token|private[_-]?key|password|passwd|pwd)(["']?\\s*[:=]\\s*)("[^"\\n]{8,}"|'[^'\\n]{8,}'|[A-Za-z0-9+/=_.~%-]{8,})`, "gi");
|
|
26645
|
+
var KEYED_WEAK_SECRET_RE = new RegExp(`(?<![A-Za-z0-9])(authorization|secret|token|sig)(["']?\\s*[:=]\\s*)` + KEYED_VALUE, "gi");
|
|
26646
|
+
var TELEGRAM_BOT_TOKEN_RE = /(?<!\d)\d{8,10}:AA[A-Za-z0-9_-]{32,34}(?![A-Za-z0-9_-])/g;
|
|
26647
|
+
var SPOKEN_SECRET_RE = /\b((?:password|passcode|passphrase|api[ _-]?key|access code|pin(?: code)?)\s+is[ \t:]+)(?=\S{0,32}[0-9])\S{4,64}/gi;
|
|
26648
|
+
var SPOKEN_PASSWORD_RE = /\b((?:password|passcode|passphrase)\s+is[ \t:]+)(?!\[)(?!(?:wrong|incorrect|invalid|expired|required|missing|reset|unchanged|correct|secure|weak|strong|blank|empty|not|now|still|too|the|already|also|only|just|being|getting)\b)("[^"\n]{4,64}"|'[^'\n]{4,64}'|\S{4,64})/gi;
|
|
26649
|
+
var B64ISH_RE = /(?<![A-Za-z0-9+=_-])(?=[A-Za-z0-9+=_-]{0,64}[A-Z])(?=[A-Za-z0-9+=_-]{0,64}[a-z])(?=[A-Za-z0-9+=_-]{0,64}[0-9])[A-Za-z0-9+=_-]{40,}(?![A-Za-z0-9+=_-])/g;
|
|
26650
|
+
var EMAIL_RE = new RegExp("[\\p{L}\\p{N}._%+-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{N}.-]*[\\p{L}\\p{N}])?\\.\\p{L}{2,}(?![\\p{L}\\p{N}-])", "gu");
|
|
26651
|
+
var PHONE_INTL_RE = /(?<![\d/.-])\+\d{1,3}(?:[ .-]?\(\d{1,4}\))?(?:[ .-]?\d){6,12}(?!\d)/g;
|
|
26652
|
+
var PHONE_SEP_RE = /(?<![\d.-])(?:\d[ .-])?(?:\(\d{3}\)[ .-]?\d{3}[ .-]?\d{4}|\d{3}[ .-]\d{3}[ .-]\d{4}|0\d{1,2}[ .-]\d{7})(?![\d-])/g;
|
|
26653
|
+
var PHONE_CONTEXT_RE = /\b((?:call|text|dial|whatsapp)(?:\s+(?:me|us|him|her|them|back))?(?:\s+(?:at|on))?\s*[:-]?\s*|(?:phone|mobile|cell|tel)(?:\s*(?:number|no\.?))?\s*(?:is|:)?\s*)(?<![\d.(-])\d{7,15}(?![\d.])/gi;
|
|
26654
|
+
var PEM_OPEN_RE = /-----BEGIN [A-Z0-9 ]{0,32}PRIVATE KEY-----/;
|
|
26655
|
+
var UNCLOSED_QUOTED_SECRET_RE = /(?<![A-Za-z0-9])(?:shared[_-]?access[_-]?key|account[_-]?key|access[_-]?key|storage[_-]?key|api[_-]?key|apikey|x[_-]?api[_-]?key|client[_-]?secret|sas[_-]?token|access[_-]?token|refresh[_-]?token|auth[_-]?token|id[_-]?token|bot[_-]?token|private[_-]?key|password|passwd|pwd|authorization|secret|token|sig)["']?\s*[:=]\s*["'][^"'\n]*$/i;
|
|
26656
|
+
function failClosedOnCutConstructs(text) {
|
|
26657
|
+
const pem = PEM_OPEN_RE.exec(text);
|
|
26658
|
+
if (pem && !/-----END [A-Z0-9 ]{0,32}PRIVATE KEY-----/.test(text.slice(pem.index))) {
|
|
26659
|
+
return text.slice(0, pem.index) + SECRET_PLACEHOLDER;
|
|
26660
|
+
}
|
|
26661
|
+
const quoted = UNCLOSED_QUOTED_SECRET_RE.exec(text);
|
|
26662
|
+
if (quoted)
|
|
26663
|
+
return text.slice(0, quoted.index) + SECRET_PLACEHOLDER;
|
|
26664
|
+
return text;
|
|
26665
|
+
}
|
|
26666
|
+
function maskSecrets(text) {
|
|
26667
|
+
const masked = text.replace(PEM_BLOCK_RE, SECRET_PLACEHOLDER).replace(JWT_RE, SECRET_PLACEHOLDER).replace(HTTP_CREDENTIAL_RE, (_, scheme) => `${scheme} ${SECRET_PLACEHOLDER}`).replace(KNOWN_PREFIX_RE, SECRET_PLACEHOLDER).replace(TELEGRAM_BOT_TOKEN_RE, SECRET_PLACEHOLDER).replace(KEYED_STRONG_SECRET_RE, (_, key2, sep4) => `${key2}${sep4}${SECRET_PLACEHOLDER}`).replace(KEYED_WEAK_SECRET_RE, (_, key2, sep4) => `${key2}${sep4}${SECRET_PLACEHOLDER}`).replace(SPOKEN_SECRET_RE, (_, lead) => `${lead}${SECRET_PLACEHOLDER}`).replace(SPOKEN_PASSWORD_RE, (_, lead) => `${lead}${SECRET_PLACEHOLDER}`).replace(B64ISH_RE, SECRET_PLACEHOLDER);
|
|
26668
|
+
return failClosedOnCutConstructs(masked);
|
|
26669
|
+
}
|
|
26670
|
+
function maskContact(text) {
|
|
26671
|
+
return text.replace(EMAIL_RE, EMAIL_PLACEHOLDER).replace(PHONE_INTL_RE, PHONE_PLACEHOLDER).replace(PHONE_SEP_RE, PHONE_PLACEHOLDER).replace(PHONE_CONTEXT_RE, (_, lead) => `${lead}${PHONE_PLACEHOLDER}`);
|
|
26672
|
+
}
|
|
26673
|
+
var BARE_PHONE_RUN_RE = /(?<![\w.,-])\d{7,15}(?!\.?\d)(?![\w,-])/g;
|
|
26674
|
+
function maskContactStrict(text) {
|
|
26675
|
+
return maskContact(text).replace(BARE_PHONE_RUN_RE, PHONE_PLACEHOLDER);
|
|
26676
|
+
}
|
|
26677
|
+
var USERREF_DIGEST_VERSION = "1";
|
|
26678
|
+
function userRefHashKey() {
|
|
26679
|
+
return process.env.M8T_LEDGER_USERREF_KEY ?? (process.env.STORAGE_ACCOUNT_NAME ? `m8t-ledger-userref:${process.env.STORAGE_ACCOUNT_NAME}` : "m8t-agent-ledger-userref-dev");
|
|
26680
|
+
}
|
|
26681
|
+
function maskUserRef(ref) {
|
|
26682
|
+
if (ref == null)
|
|
26683
|
+
return void 0;
|
|
26684
|
+
return ref.replace(EMAIL_RE, (m) => `email#${USERREF_DIGEST_VERSION}:${createHmac("sha256", userRefHashKey()).update(m.toLowerCase()).digest("hex").slice(0, 32)}`);
|
|
26685
|
+
}
|
|
26686
|
+
|
|
26634
26687
|
// ../../packages/agent-ledger/dist/esm/read/ledger-identity.js
|
|
26635
26688
|
var CHANNEL_SOURCES = /* @__PURE__ */ new Set(["telegram", "slack"]);
|
|
26636
26689
|
function resolveIdentity(source, userRef, members = []) {
|
|
26637
26690
|
if (!userRef)
|
|
26638
26691
|
return { source, ref: "unknown" };
|
|
26639
26692
|
if (CHANNEL_SOURCES.has(source)) {
|
|
26640
|
-
const m = members.find((x) => x.source === source && x.handle.toLowerCase() === userRef.toLowerCase());
|
|
26693
|
+
const m = members.find((x) => x.source === source && (x.handle.toLowerCase() === userRef.toLowerCase() || maskUserRef(x.handle.toLowerCase()) === userRef.toLowerCase()));
|
|
26641
26694
|
if (m)
|
|
26642
26695
|
return { source, ref: userRef, display: m.displayName };
|
|
26643
26696
|
}
|
|
@@ -26947,6 +27000,10 @@ function makeRowKey(timestampIso, eventId) {
|
|
|
26947
27000
|
return `${String(reverse).padStart(19, "0")}_${eventId}`;
|
|
26948
27001
|
}
|
|
26949
27002
|
|
|
27003
|
+
// ../../packages/agent-ledger/dist/esm/entity.js
|
|
27004
|
+
var SNIPPET_MAX_LEN = 256;
|
|
27005
|
+
var PII_SCAN_WINDOW = SNIPPET_MAX_LEN * 16;
|
|
27006
|
+
|
|
26950
27007
|
// ../../packages/brain/engine/dist/esm/cursor.js
|
|
26951
27008
|
var CURSOR_PARTITION_KEY = "dreamcursor";
|
|
26952
27009
|
function isAbsentCursorError(e) {
|
|
@@ -27175,7 +27232,7 @@ function mapItems(raw) {
|
|
|
27175
27232
|
continue;
|
|
27176
27233
|
if (item.role !== "user" && item.role !== "assistant")
|
|
27177
27234
|
continue;
|
|
27178
|
-
const text = Array.isArray(item.content) ? stripVoiceEnvelope(item.content.map((c) => typeof c.text === "string" && c.text.length > 0 ? c.text : typeof c.transcript === "string" && c.transcript.length > 0 ? c.transcript : "").filter(Boolean).join("\n\n")) : "";
|
|
27235
|
+
const text = Array.isArray(item.content) ? maskSecrets(stripVoiceEnvelope(item.content.map((c) => typeof c.text === "string" && c.text.length > 0 ? c.text : typeof c.transcript === "string" && c.transcript.length > 0 ? c.transcript : "").filter(Boolean).join("\n\n"))) : "";
|
|
27179
27236
|
const at = typeof item.created_at === "number" ? new Date(item.created_at * 1e3).toISOString() : void 0;
|
|
27180
27237
|
acc.push(at ? { role: item.role, text, at } : { role: item.role, text });
|
|
27181
27238
|
}
|
|
@@ -27460,7 +27517,9 @@ async function runPipeline(args) {
|
|
|
27460
27517
|
const grpRows = rowsByGroupKey.get(groupKeyOf(g)) ?? [];
|
|
27461
27518
|
(consumedRowsByPk[pk] ??= []).push(...grpRows);
|
|
27462
27519
|
const provRows = grpRows.map((r) => ({ rowKey: rowStorageKey(r), eventTimestamp: r.eventTimestamp }));
|
|
27463
|
-
const
|
|
27520
|
+
const trustedOwner = g.source !== "a2a" && g.user.ref !== "unknown" && !g.user.ref.startsWith("svc:");
|
|
27521
|
+
const dreamItems = trustedOwner ? items : items.map((i) => ({ ...i, text: maskContactStrict(i.text) }));
|
|
27522
|
+
const conv = normalize(ng, dreamItems, enrichment);
|
|
27464
27523
|
conv.provenance = { pk, rows: provRows };
|
|
27465
27524
|
conversations.push(conv);
|
|
27466
27525
|
if (outcome === "consumed")
|
|
@@ -29217,14 +29276,278 @@ function defaultDeps(overrides) {
|
|
|
29217
29276
|
return deps;
|
|
29218
29277
|
}
|
|
29219
29278
|
|
|
29220
|
-
// src/commands/
|
|
29279
|
+
// src/commands/conversations/sweep.ts
|
|
29280
|
+
import { createHash as createHash4 } from "crypto";
|
|
29221
29281
|
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
29282
|
+
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
29283
|
+
import { TableClient as TableClient8 } from "@azure/data-tables";
|
|
29284
|
+
import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
|
|
29285
|
+
init_errors();
|
|
29286
|
+
var LEDGER_TABLE_NAME2 = "AgentLedger";
|
|
29287
|
+
var KEY_LIFETIME_DAYS = 30;
|
|
29288
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
29289
|
+
function ownerDigest(nativeUserKey) {
|
|
29290
|
+
return createHash4("sha256").update(nativeUserKey).digest("base64url");
|
|
29291
|
+
}
|
|
29292
|
+
function svcPrincipal(svcRef) {
|
|
29293
|
+
return svcRef.split(":")[1] ?? "";
|
|
29294
|
+
}
|
|
29295
|
+
function svcConversationFacts(rows) {
|
|
29296
|
+
const facts = /* @__PURE__ */ new Map();
|
|
29297
|
+
for (const r of rows) {
|
|
29298
|
+
if (!r.foundryConversationId) continue;
|
|
29299
|
+
if (!r.userRef?.startsWith("svc:")) continue;
|
|
29300
|
+
const prev = facts.get(r.foundryConversationId);
|
|
29301
|
+
if (!prev || r.eventTimestamp > prev.lastSeen) {
|
|
29302
|
+
facts.set(r.foundryConversationId, {
|
|
29303
|
+
conversationId: r.foundryConversationId,
|
|
29304
|
+
svcRef: r.userRef,
|
|
29305
|
+
principal: svcPrincipal(r.userRef),
|
|
29306
|
+
agent: r.agentName,
|
|
29307
|
+
lastSeen: r.eventTimestamp
|
|
29308
|
+
});
|
|
29309
|
+
}
|
|
29310
|
+
}
|
|
29311
|
+
return facts;
|
|
29312
|
+
}
|
|
29313
|
+
function selectStaleCandidates(facts, cutoffIso) {
|
|
29314
|
+
return [...facts.values()].filter((f) => f.lastSeen < cutoffIso).sort((a, b) => a.lastSeen < b.lastSeen ? -1 : 1);
|
|
29315
|
+
}
|
|
29316
|
+
function confirmCandidate(fact, meta, newestItemAt, cutoffIso) {
|
|
29317
|
+
if (meta.app !== "m8t-webapp") return { ok: false, reason: `unexpected app tag '${meta.app ?? ""}'` };
|
|
29318
|
+
const expectedOwner = `web:${ownerDigest(fact.svcRef)}`;
|
|
29319
|
+
if (meta.ownerKey !== expectedOwner) return { ok: false, reason: "owner mismatch" };
|
|
29320
|
+
if (newestItemAt === null) return { ok: false, reason: "no items to prove inactivity" };
|
|
29321
|
+
if (newestItemAt >= cutoffIso) return { ok: false, reason: "recent activity" };
|
|
29322
|
+
return { ok: true };
|
|
29323
|
+
}
|
|
29324
|
+
function statusOf2(e) {
|
|
29325
|
+
const err = e;
|
|
29326
|
+
return err?.statusCode ?? err?.status;
|
|
29327
|
+
}
|
|
29328
|
+
function defaultDeps2() {
|
|
29329
|
+
const credential2 = new AzureCliCredential2();
|
|
29330
|
+
const openaiFor = (ctx) => new AIProjectClient4(ctx.projectEndpoint, credential2).getOpenAIClient();
|
|
29331
|
+
return {
|
|
29332
|
+
async resolveContext(opts) {
|
|
29333
|
+
const account = await getAzAccount();
|
|
29334
|
+
const subscriptionId = opts.subscription ?? account.subscriptionId;
|
|
29335
|
+
const project = await resolveFoundryProject({
|
|
29336
|
+
credential: credential2,
|
|
29337
|
+
subscriptionId,
|
|
29338
|
+
interactive: false,
|
|
29339
|
+
endpoint: opts.endpoint
|
|
29340
|
+
});
|
|
29341
|
+
const m = /\/resourceGroups\/([^/]+)/i.exec(project.accountScope);
|
|
29342
|
+
if (!m) throw new LocalCliError({ code: "sweep_context", message: `Could not parse resource group from '${project.accountScope}'.` });
|
|
29343
|
+
const { ledgerTableEndpoint } = await discoverLedgerResources({
|
|
29344
|
+
credential: credential2,
|
|
29345
|
+
subscriptionId,
|
|
29346
|
+
resourceGroup: m[1]
|
|
29347
|
+
});
|
|
29348
|
+
return { projectEndpoint: project.endpoint, ledgerTableEndpoint };
|
|
29349
|
+
},
|
|
29350
|
+
async fetchRows(ctx, fromIso, toIso) {
|
|
29351
|
+
const client = new TableClient8(ctx.ledgerTableEndpoint, LEDGER_TABLE_NAME2, credential2);
|
|
29352
|
+
return fetchLedgerRows({ workers: [], source: "all", from: fromIso, to: toIso }, client);
|
|
29353
|
+
},
|
|
29354
|
+
async retrieveMetadata(ctx, conversationId) {
|
|
29355
|
+
try {
|
|
29356
|
+
const conv = await openaiFor(ctx).conversations.retrieve(conversationId);
|
|
29357
|
+
const md = conv.metadata;
|
|
29358
|
+
const s = (v) => typeof v === "string" ? v : void 0;
|
|
29359
|
+
if (!md || typeof md !== "object") return {};
|
|
29360
|
+
return { app: s(md.app), ownerKey: s(md.ownerKey), platform: s(md.platform) };
|
|
29361
|
+
} catch (e) {
|
|
29362
|
+
if (statusOf2(e) === 404) return "gone";
|
|
29363
|
+
throw e;
|
|
29364
|
+
}
|
|
29365
|
+
},
|
|
29366
|
+
async newestItemAt(ctx, conversationId) {
|
|
29367
|
+
try {
|
|
29368
|
+
for await (const item of openaiFor(ctx).conversations.items.list(conversationId)) {
|
|
29369
|
+
const created = item.created_at;
|
|
29370
|
+
if (typeof created !== "number") return { at: null };
|
|
29371
|
+
return { at: new Date(created * 1e3).toISOString() };
|
|
29372
|
+
}
|
|
29373
|
+
return { at: null };
|
|
29374
|
+
} catch (e) {
|
|
29375
|
+
if (statusOf2(e) === 404) return "gone";
|
|
29376
|
+
throw e;
|
|
29377
|
+
}
|
|
29378
|
+
},
|
|
29379
|
+
async deleteConversation(ctx, conversationId) {
|
|
29380
|
+
try {
|
|
29381
|
+
await openaiFor(ctx).conversations.delete(conversationId);
|
|
29382
|
+
return "deleted";
|
|
29383
|
+
} catch (e) {
|
|
29384
|
+
if (statusOf2(e) === 404) return "already-gone";
|
|
29385
|
+
throw e;
|
|
29386
|
+
}
|
|
29387
|
+
}
|
|
29388
|
+
};
|
|
29389
|
+
}
|
|
29390
|
+
var ConversationsSweepCommand = class extends M8tCommand {
|
|
29391
|
+
static paths = [["conversations", "sweep"]];
|
|
29392
|
+
static usage = Command53.Usage({
|
|
29393
|
+
category: "Conversations",
|
|
29394
|
+
description: "Delete expired public-visitor conversations (dry run by default)",
|
|
29395
|
+
details: `
|
|
29396
|
+
Finds conversations owned by delegated end users (public visitors) whose
|
|
29397
|
+
last activity predates the retention window (${String(KEY_LIFETIME_DAYS)}-day key life + grace),
|
|
29398
|
+
confirms each against its own metadata and newest item, and \u2014 only with
|
|
29399
|
+
--delete \u2014 permanently removes them. Team, channel, and delegated-work
|
|
29400
|
+
conversations are structurally out of scope. The masked ledger telemetry
|
|
29401
|
+
of swept conversations is kept.
|
|
29402
|
+
`,
|
|
29403
|
+
examples: [
|
|
29404
|
+
["Report what would be deleted (nothing is)", "m8t conversations sweep"],
|
|
29405
|
+
["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
|
|
29406
|
+
]
|
|
29407
|
+
});
|
|
29408
|
+
doDelete = Option50.Boolean("--delete", false, {
|
|
29409
|
+
description: "Perform deletions (without this flag the command only reports)"
|
|
29410
|
+
});
|
|
29411
|
+
principal = Option50.String("--principal", {
|
|
29412
|
+
description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
|
|
29413
|
+
});
|
|
29414
|
+
max = Option50.String("--max", "200", { description: "Maximum deletions per run" });
|
|
29415
|
+
graceDays = Option50.String("--grace-days", "14", {
|
|
29416
|
+
description: "Days past the 30-day key life before a conversation is eligible"
|
|
29417
|
+
});
|
|
29418
|
+
lookbackDays = Option50.String("--lookback-days", "180", {
|
|
29419
|
+
description: "How far back to scan ledger activity for candidates"
|
|
29420
|
+
});
|
|
29421
|
+
subscription = Option50.String("--subscription", { description: "Azure subscription id override" });
|
|
29422
|
+
endpoint = Option50.String("--endpoint", { description: "Foundry project endpoint override" });
|
|
29423
|
+
deps = defaultDeps2();
|
|
29424
|
+
async executeCommand() {
|
|
29425
|
+
const out = this.context.stdout;
|
|
29426
|
+
const max = Number.parseInt(this.max, 10);
|
|
29427
|
+
const grace = Number.parseInt(this.graceDays, 10);
|
|
29428
|
+
const lookback = Number.parseInt(this.lookbackDays, 10);
|
|
29429
|
+
if (!Number.isFinite(max) || max < 1) throw new LocalCliError({ code: "sweep_args", message: "--max must be a positive integer." });
|
|
29430
|
+
if (!Number.isFinite(grace) || grace < 0) throw new LocalCliError({ code: "sweep_args", message: "--grace-days must be a non-negative integer." });
|
|
29431
|
+
const retentionDays = KEY_LIFETIME_DAYS + grace;
|
|
29432
|
+
if (!Number.isFinite(lookback) || lookback <= retentionDays) {
|
|
29433
|
+
throw new LocalCliError({ code: "sweep_args", message: `--lookback-days must exceed the retention window (${String(retentionDays)} days).` });
|
|
29434
|
+
}
|
|
29435
|
+
const now = Date.now();
|
|
29436
|
+
const cutoffIso = new Date(now - retentionDays * DAY_MS).toISOString();
|
|
29437
|
+
const fromIso = new Date(now - lookback * DAY_MS).toISOString();
|
|
29438
|
+
const toIso = new Date(now).toISOString();
|
|
29439
|
+
const ctx = await this.deps.resolveContext({ subscription: this.subscription, endpoint: this.endpoint });
|
|
29440
|
+
out.write(`Scanning ledger activity ${fromIso} \u2192 ${toIso}; retention cutoff ${cutoffIso}.
|
|
29441
|
+
`);
|
|
29442
|
+
const rows = await this.deps.fetchRows(ctx, fromIso, toIso);
|
|
29443
|
+
const facts = svcConversationFacts(rows);
|
|
29444
|
+
const stale = selectStaleCandidates(facts, cutoffIso);
|
|
29445
|
+
if (this.doDelete && !this.principal) {
|
|
29446
|
+
throw new LocalCliError({
|
|
29447
|
+
code: "sweep_principal_required",
|
|
29448
|
+
message: "--delete requires --principal <service-principal oid>.",
|
|
29449
|
+
hint: "Run without --delete first: the dry run lists candidates grouped by principal so you can identify the visitor-minting one."
|
|
29450
|
+
});
|
|
29451
|
+
}
|
|
29452
|
+
const inScope = this.principal ? stale.filter((f) => f.principal === this.principal) : stale;
|
|
29453
|
+
const outOfScope = stale.length - inScope.length;
|
|
29454
|
+
out.write(
|
|
29455
|
+
`${String(facts.size)} delegated conversation(s) seen in window; ${String(stale.length)} past cutoff` + (this.principal ? ` (${String(inScope.length)} for principal ${this.principal}, ${String(outOfScope)} other-principal ignored)` : "") + ".\n"
|
|
29456
|
+
);
|
|
29457
|
+
if (!this.principal && stale.length > 0) {
|
|
29458
|
+
const byPrincipal = /* @__PURE__ */ new Map();
|
|
29459
|
+
for (const f of stale) byPrincipal.set(f.principal, (byPrincipal.get(f.principal) ?? 0) + 1);
|
|
29460
|
+
out.write("Stale candidates by principal (pass --principal to scope deletion):\n");
|
|
29461
|
+
for (const [pr, n] of [...byPrincipal.entries()].sort((a, b) => b[1] - a[1])) {
|
|
29462
|
+
out.write(` ${pr || "(malformed)"} ${String(n)}
|
|
29463
|
+
`);
|
|
29464
|
+
}
|
|
29465
|
+
}
|
|
29466
|
+
out.write("\n");
|
|
29467
|
+
let attempts = 0;
|
|
29468
|
+
let deleted = 0;
|
|
29469
|
+
let alreadyGone = 0;
|
|
29470
|
+
let skipped = 0;
|
|
29471
|
+
let failures = 0;
|
|
29472
|
+
let remaining = 0;
|
|
29473
|
+
for (const fact of inScope) {
|
|
29474
|
+
if (attempts >= max) {
|
|
29475
|
+
remaining += 1;
|
|
29476
|
+
continue;
|
|
29477
|
+
}
|
|
29478
|
+
const ageDays = Math.floor((now - Date.parse(fact.lastSeen)) / DAY_MS);
|
|
29479
|
+
const label = `${fact.conversationId} agent=${fact.agent} last-active=${fact.lastSeen} (${String(ageDays)}d)`;
|
|
29480
|
+
let meta;
|
|
29481
|
+
let newest;
|
|
29482
|
+
try {
|
|
29483
|
+
meta = await this.deps.retrieveMetadata(ctx, fact.conversationId);
|
|
29484
|
+
newest = meta === "gone" ? "gone" : await this.deps.newestItemAt(ctx, fact.conversationId);
|
|
29485
|
+
} catch (e) {
|
|
29486
|
+
failures += 1;
|
|
29487
|
+
out.write(`${colors.error("failed")} ${label} \u2014 ${e.message}
|
|
29488
|
+
`);
|
|
29489
|
+
continue;
|
|
29490
|
+
}
|
|
29491
|
+
if (meta === "gone" || newest === "gone") {
|
|
29492
|
+
alreadyGone += 1;
|
|
29493
|
+
out.write(`${colors.dim("already gone")} ${label}
|
|
29494
|
+
`);
|
|
29495
|
+
continue;
|
|
29496
|
+
}
|
|
29497
|
+
const verdict = confirmCandidate(fact, meta, newest.at, cutoffIso);
|
|
29498
|
+
if (!verdict.ok) {
|
|
29499
|
+
skipped += 1;
|
|
29500
|
+
out.write(`${colors.warn("skip")} ${label} \u2014 ${verdict.reason}
|
|
29501
|
+
`);
|
|
29502
|
+
continue;
|
|
29503
|
+
}
|
|
29504
|
+
attempts += 1;
|
|
29505
|
+
if (!this.doDelete) {
|
|
29506
|
+
out.write(`${colors.field("would delete")} ${label}
|
|
29507
|
+
`);
|
|
29508
|
+
continue;
|
|
29509
|
+
}
|
|
29510
|
+
try {
|
|
29511
|
+
const result2 = await this.deps.deleteConversation(ctx, fact.conversationId);
|
|
29512
|
+
if (result2 === "deleted") {
|
|
29513
|
+
deleted += 1;
|
|
29514
|
+
out.write(`${colors.error("deleted")} ${label}
|
|
29515
|
+
`);
|
|
29516
|
+
} else {
|
|
29517
|
+
alreadyGone += 1;
|
|
29518
|
+
out.write(`${colors.dim("already gone")} ${label}
|
|
29519
|
+
`);
|
|
29520
|
+
}
|
|
29521
|
+
} catch (e) {
|
|
29522
|
+
failures += 1;
|
|
29523
|
+
out.write(`${colors.error("failed")} ${label} \u2014 ${e.message}
|
|
29524
|
+
`);
|
|
29525
|
+
}
|
|
29526
|
+
}
|
|
29527
|
+
out.write("\n");
|
|
29528
|
+
const tail = `already gone ${String(alreadyGone)}, skipped ${String(skipped)} (fail-closed), failed ${String(failures)}` + (remaining > 0 ? `, ${String(remaining)} past the cap \u2014 re-run to continue` : "") + ".\n";
|
|
29529
|
+
if (this.doDelete) {
|
|
29530
|
+
out.write(`Deleted ${String(deleted)}, ${tail}`);
|
|
29531
|
+
} else {
|
|
29532
|
+
out.write(`DRY RUN \u2014 nothing deleted. ${String(attempts)} conversation(s) would be deleted, ${tail}`);
|
|
29533
|
+
}
|
|
29534
|
+
if (failures > 0) {
|
|
29535
|
+
out.write(`${colors.error("Run incomplete:")} ${String(failures)} verification/deletion failure(s) \u2014 fix access and re-run.
|
|
29536
|
+
`);
|
|
29537
|
+
return 1;
|
|
29538
|
+
}
|
|
29539
|
+
return 0;
|
|
29540
|
+
}
|
|
29541
|
+
};
|
|
29542
|
+
|
|
29543
|
+
// src/commands/foundry/create.ts
|
|
29544
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
29222
29545
|
|
|
29223
29546
|
// src/lib/foundry-create.ts
|
|
29224
29547
|
init_errors();
|
|
29225
|
-
import { createHash as
|
|
29548
|
+
import { createHash as createHash5 } from "crypto";
|
|
29226
29549
|
function deriveAccountName(subscriptionId) {
|
|
29227
|
-
const h =
|
|
29550
|
+
const h = createHash5("sha256").update(subscriptionId).digest("hex").slice(0, 12);
|
|
29228
29551
|
return `m8t${h}`;
|
|
29229
29552
|
}
|
|
29230
29553
|
function assertHostedRegion(location) {
|
|
@@ -29456,7 +29779,7 @@ async function createFoundryProject(args) {
|
|
|
29456
29779
|
init_errors();
|
|
29457
29780
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
29458
29781
|
static paths = [["foundry", "create"]];
|
|
29459
|
-
static usage =
|
|
29782
|
+
static usage = Command54.Usage({
|
|
29460
29783
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
29461
29784
|
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).",
|
|
29462
29785
|
examples: [
|
|
@@ -29465,16 +29788,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
29465
29788
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
29466
29789
|
]
|
|
29467
29790
|
});
|
|
29468
|
-
resourceGroup =
|
|
29469
|
-
location =
|
|
29470
|
-
account =
|
|
29471
|
-
project =
|
|
29472
|
-
model =
|
|
29473
|
-
modelVersion =
|
|
29474
|
-
capacity =
|
|
29475
|
-
subscription =
|
|
29476
|
-
skipQuotaCheck =
|
|
29477
|
-
output =
|
|
29791
|
+
resourceGroup = Option51.String("--resource-group");
|
|
29792
|
+
location = Option51.String("--location");
|
|
29793
|
+
account = Option51.String("--account");
|
|
29794
|
+
project = Option51.String("--project", "m8t");
|
|
29795
|
+
model = Option51.String("--model", "gpt-4.1-mini");
|
|
29796
|
+
modelVersion = Option51.String("--model-version", "2025-04-14");
|
|
29797
|
+
capacity = Option51.String("--capacity", "50");
|
|
29798
|
+
subscription = Option51.String("--subscription");
|
|
29799
|
+
skipQuotaCheck = Option51.Boolean("--skip-quota-check", false);
|
|
29800
|
+
output = Option51.String("--output");
|
|
29478
29801
|
async executeCommand() {
|
|
29479
29802
|
const mode = resolveOutputMode(
|
|
29480
29803
|
this.output,
|
|
@@ -29546,22 +29869,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
29546
29869
|
};
|
|
29547
29870
|
|
|
29548
29871
|
// src/commands/foundry/await-ready.ts
|
|
29549
|
-
import { Command as
|
|
29550
|
-
import { AzureCliCredential as
|
|
29872
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
29873
|
+
import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
|
|
29551
29874
|
init_errors();
|
|
29552
29875
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
29553
29876
|
static paths = [["foundry", "await-ready"]];
|
|
29554
|
-
static usage =
|
|
29877
|
+
static usage = Command55.Usage({
|
|
29555
29878
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
29556
29879
|
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.",
|
|
29557
29880
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
29558
29881
|
});
|
|
29559
|
-
endpoint =
|
|
29560
|
-
consecutive =
|
|
29561
|
-
attempts =
|
|
29562
|
-
interval =
|
|
29563
|
-
subscription =
|
|
29564
|
-
output =
|
|
29882
|
+
endpoint = Option52.String("--endpoint");
|
|
29883
|
+
consecutive = Option52.String("--consecutive", "3");
|
|
29884
|
+
attempts = Option52.String("--attempts", "60");
|
|
29885
|
+
interval = Option52.String("--interval", "5");
|
|
29886
|
+
subscription = Option52.String("--subscription");
|
|
29887
|
+
output = Option52.String("--output");
|
|
29565
29888
|
async executeCommand() {
|
|
29566
29889
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
29567
29890
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -29572,7 +29895,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
29572
29895
|
const attempts = typeof this.attempts === "string" ? Number(this.attempts) : 60;
|
|
29573
29896
|
const intervalMs = (typeof this.interval === "string" ? Number(this.interval) : 5) * 1e3;
|
|
29574
29897
|
await getAzAccount();
|
|
29575
|
-
const credential2 = new
|
|
29898
|
+
const credential2 = new AzureCliCredential3();
|
|
29576
29899
|
const probe = makeAgentsProbe(credential2, endpoint);
|
|
29577
29900
|
const onProgress = mode === "pretty" ? (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
29578
29901
|
`) : void 0;
|
|
@@ -29595,7 +29918,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
29595
29918
|
};
|
|
29596
29919
|
|
|
29597
29920
|
// src/commands/bootstrap/preflight.ts
|
|
29598
|
-
import { Command as
|
|
29921
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
29599
29922
|
|
|
29600
29923
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
29601
29924
|
var EVENT_TIERS = {
|
|
@@ -29647,7 +29970,7 @@ function preflightRenderable(results) {
|
|
|
29647
29970
|
}
|
|
29648
29971
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
29649
29972
|
static paths = [["bootstrap", "preflight"]];
|
|
29650
|
-
static usage =
|
|
29973
|
+
static usage = Command56.Usage({
|
|
29651
29974
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
29652
29975
|
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.",
|
|
29653
29976
|
examples: [
|
|
@@ -29656,9 +29979,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
29656
29979
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
29657
29980
|
]
|
|
29658
29981
|
});
|
|
29659
|
-
clientId =
|
|
29660
|
-
subscription =
|
|
29661
|
-
location =
|
|
29982
|
+
clientId = Option53.String("--client-id");
|
|
29983
|
+
subscription = Option53.String("--subscription");
|
|
29984
|
+
location = Option53.String("--location", {
|
|
29662
29985
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
29663
29986
|
});
|
|
29664
29987
|
async executeCommand() {
|
|
@@ -29758,7 +30081,7 @@ ${colors.error(" " + why)}
|
|
|
29758
30081
|
import * as fs35 from "fs";
|
|
29759
30082
|
import * as os15 from "os";
|
|
29760
30083
|
import * as path38 from "path";
|
|
29761
|
-
import { Command as
|
|
30084
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
29762
30085
|
init_errors();
|
|
29763
30086
|
|
|
29764
30087
|
// src/lib/bootstrap-mi.ts
|
|
@@ -29913,7 +30236,7 @@ async function kickInstaller(s) {
|
|
|
29913
30236
|
}
|
|
29914
30237
|
|
|
29915
30238
|
// src/lib/bootstrap-status.ts
|
|
29916
|
-
import { createHash as
|
|
30239
|
+
import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
|
|
29917
30240
|
import * as fs33 from "fs/promises";
|
|
29918
30241
|
import * as os13 from "os";
|
|
29919
30242
|
import * as path36 from "path";
|
|
@@ -29921,7 +30244,7 @@ init_errors();
|
|
|
29921
30244
|
var STATUS_CONTAINER = "status";
|
|
29922
30245
|
var STATUS_BLOB = "status.json";
|
|
29923
30246
|
function deriveStatusSaName(resourceGroup, subscriptionId) {
|
|
29924
|
-
const hex =
|
|
30247
|
+
const hex = createHash6("sha256").update(resourceGroup + subscriptionId).digest("hex").slice(0, 12);
|
|
29925
30248
|
return `m8tinst${hex}`;
|
|
29926
30249
|
}
|
|
29927
30250
|
function statusBlobUrl(saName) {
|
|
@@ -30128,7 +30451,7 @@ var ACI_NAME = "m8t-installer";
|
|
|
30128
30451
|
var MI_NAME = "m8t-installer-mi";
|
|
30129
30452
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
30130
30453
|
static paths = [["bootstrap", "launch"]];
|
|
30131
|
-
static usage =
|
|
30454
|
+
static usage = Command57.Usage({
|
|
30132
30455
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
30133
30456
|
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.",
|
|
30134
30457
|
examples: [
|
|
@@ -30139,26 +30462,26 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30139
30462
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
30140
30463
|
]
|
|
30141
30464
|
});
|
|
30142
|
-
location =
|
|
30143
|
-
resourceGroup =
|
|
30144
|
-
clientId =
|
|
30145
|
-
subscription =
|
|
30146
|
-
installerTag =
|
|
30465
|
+
location = Option54.String("--location");
|
|
30466
|
+
resourceGroup = Option54.String("--resource-group");
|
|
30467
|
+
clientId = Option54.String("--client-id");
|
|
30468
|
+
subscription = Option54.String("--subscription");
|
|
30469
|
+
installerTag = Option54.String("--installer-tag");
|
|
30147
30470
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
30148
30471
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
30149
30472
|
// Wins over --installer-tag / the pinned default.
|
|
30150
|
-
installerImage =
|
|
30151
|
-
gatewayImageRef =
|
|
30152
|
-
githubAppCreds =
|
|
30153
|
-
contactEmail =
|
|
30154
|
-
company =
|
|
30473
|
+
installerImage = Option54.String("--installer-image");
|
|
30474
|
+
gatewayImageRef = Option54.String("--gateway-image-ref");
|
|
30475
|
+
githubAppCreds = Option54.String("--github-app-creds");
|
|
30476
|
+
contactEmail = Option54.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
30477
|
+
company = Option54.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
30155
30478
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
30156
30479
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
30157
30480
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
30158
30481
|
// the target; --resource-group is what CHOOSES it.
|
|
30159
|
-
reinstallInto =
|
|
30160
|
-
org =
|
|
30161
|
-
noBrains =
|
|
30482
|
+
reinstallInto = Option54.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
|
|
30483
|
+
org = Option54.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
|
|
30484
|
+
noBrains = Option54.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
|
|
30162
30485
|
async executeCommand() {
|
|
30163
30486
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
30164
30487
|
if (!location) {
|
|
@@ -30319,7 +30642,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30319
30642
|
};
|
|
30320
30643
|
|
|
30321
30644
|
// src/commands/bootstrap/status.ts
|
|
30322
|
-
import { Command as
|
|
30645
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
30323
30646
|
init_errors();
|
|
30324
30647
|
|
|
30325
30648
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -31323,7 +31646,7 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
31323
31646
|
import { execFile, spawn as spawn7 } from "child_process";
|
|
31324
31647
|
|
|
31325
31648
|
// src/lib/companion-artifact.ts
|
|
31326
|
-
import { createHash as
|
|
31649
|
+
import { createHash as createHash7 } from "crypto";
|
|
31327
31650
|
import { constants } from "fs";
|
|
31328
31651
|
import * as fs36 from "fs/promises";
|
|
31329
31652
|
import * as path40 from "path";
|
|
@@ -31361,7 +31684,7 @@ function canonicalEntry(entry) {
|
|
|
31361
31684
|
`;
|
|
31362
31685
|
}
|
|
31363
31686
|
function artifactTreeSha256(entries) {
|
|
31364
|
-
const hash =
|
|
31687
|
+
const hash = createHash7("sha256");
|
|
31365
31688
|
const ordered = [...entries].sort(
|
|
31366
31689
|
(left, right) => left.path.localeCompare(right.path)
|
|
31367
31690
|
);
|
|
@@ -31431,7 +31754,7 @@ function parseArtifactManifest(value) {
|
|
|
31431
31754
|
};
|
|
31432
31755
|
}
|
|
31433
31756
|
async function sha256File2(filePath) {
|
|
31434
|
-
return
|
|
31757
|
+
return createHash7("sha256").update(await fs36.readFile(filePath)).digest("hex");
|
|
31435
31758
|
}
|
|
31436
31759
|
async function walk2(root, relative4 = "") {
|
|
31437
31760
|
const directory = path40.join(root, ...relative4.split("/").filter(Boolean));
|
|
@@ -32247,7 +32570,7 @@ async function uninstallCompanion(options) {
|
|
|
32247
32570
|
// src/commands/companion/install.ts
|
|
32248
32571
|
import * as os18 from "os";
|
|
32249
32572
|
import * as path43 from "path";
|
|
32250
|
-
import { Command as
|
|
32573
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
32251
32574
|
|
|
32252
32575
|
// src/lib/companion-channel.ts
|
|
32253
32576
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -32262,7 +32585,7 @@ async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps =
|
|
|
32262
32585
|
}
|
|
32263
32586
|
|
|
32264
32587
|
// src/lib/companion-download.ts
|
|
32265
|
-
import { createHash as
|
|
32588
|
+
import { createHash as createHash8 } from "crypto";
|
|
32266
32589
|
import { execFile as execFile2 } from "child_process";
|
|
32267
32590
|
import * as fs38 from "fs/promises";
|
|
32268
32591
|
import * as os17 from "os";
|
|
@@ -32311,7 +32634,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
32311
32634
|
message: `${pinned.asset} is larger than this command will accept.`
|
|
32312
32635
|
});
|
|
32313
32636
|
}
|
|
32314
|
-
const digest =
|
|
32637
|
+
const digest = createHash8("sha256").update(bytes).digest("hex");
|
|
32315
32638
|
if (digest !== pinned.sha256) {
|
|
32316
32639
|
throw new LocalCliError({
|
|
32317
32640
|
code: "COMPANION_ASSET_DIGEST_MISMATCH",
|
|
@@ -32405,7 +32728,7 @@ Version: ${state.version}
|
|
|
32405
32728
|
}
|
|
32406
32729
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
32407
32730
|
static paths = [["companion", "install"]];
|
|
32408
|
-
static usage =
|
|
32731
|
+
static usage = Command58.Usage({
|
|
32409
32732
|
description: "Install the desktop companions for this user from the release channel.",
|
|
32410
32733
|
examples: [
|
|
32411
32734
|
["Install the released build", "$0 companion install"],
|
|
@@ -32415,10 +32738,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
32415
32738
|
]
|
|
32416
32739
|
]
|
|
32417
32740
|
});
|
|
32418
|
-
from =
|
|
32741
|
+
from = Option55.String("--from", {
|
|
32419
32742
|
description: "A locally staged build directory instead of the released one."
|
|
32420
32743
|
});
|
|
32421
|
-
resourceGroup =
|
|
32744
|
+
resourceGroup = Option55.String("--resource-group", {
|
|
32422
32745
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
32423
32746
|
});
|
|
32424
32747
|
async executeCommand() {
|
|
@@ -32446,7 +32769,7 @@ async function looksLikeCheckout(dir) {
|
|
|
32446
32769
|
return false;
|
|
32447
32770
|
}
|
|
32448
32771
|
}
|
|
32449
|
-
var
|
|
32772
|
+
var defaultDeps3 = {
|
|
32450
32773
|
discoverGateway,
|
|
32451
32774
|
writeConfig,
|
|
32452
32775
|
ensureGatewayRedirectUri,
|
|
@@ -32459,7 +32782,7 @@ var defaultDeps2 = {
|
|
|
32459
32782
|
}),
|
|
32460
32783
|
homedir: () => os19.homedir()
|
|
32461
32784
|
};
|
|
32462
|
-
async function finalizeInstall(args, deps =
|
|
32785
|
+
async function finalizeInstall(args, deps = defaultDeps3) {
|
|
32463
32786
|
const markerDir = path44.join(deps.homedir(), ".m8t");
|
|
32464
32787
|
const markerPath = path44.join(markerDir, "repo-root");
|
|
32465
32788
|
const cwd = process.cwd();
|
|
@@ -32634,7 +32957,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
32634
32957
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
32635
32958
|
// through — it prints a deprecation notice and does the right thing.
|
|
32636
32959
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
32637
|
-
static usage =
|
|
32960
|
+
static usage = Command59.Usage({
|
|
32638
32961
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
32639
32962
|
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.",
|
|
32640
32963
|
examples: [
|
|
@@ -32643,12 +32966,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
32643
32966
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
32644
32967
|
]
|
|
32645
32968
|
});
|
|
32646
|
-
watch =
|
|
32647
|
-
output =
|
|
32648
|
-
repoRoot =
|
|
32649
|
-
finalize =
|
|
32650
|
-
subscription =
|
|
32651
|
-
resourceGroup =
|
|
32969
|
+
watch = Option56.Boolean("--watch", false);
|
|
32970
|
+
output = Option56.String("--output");
|
|
32971
|
+
repoRoot = Option56.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
|
|
32972
|
+
finalize = Option56.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
|
|
32973
|
+
subscription = Option56.String("--subscription");
|
|
32974
|
+
resourceGroup = Option56.String("--resource-group");
|
|
32652
32975
|
async executeCommand() {
|
|
32653
32976
|
const state = await readBootstrapState();
|
|
32654
32977
|
if (!state) {
|
|
@@ -32781,7 +33104,7 @@ function formatStatus(d) {
|
|
|
32781
33104
|
}
|
|
32782
33105
|
|
|
32783
33106
|
// src/commands/bootstrap/reap.ts
|
|
32784
|
-
import { Command as
|
|
33107
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
32785
33108
|
init_errors();
|
|
32786
33109
|
|
|
32787
33110
|
// src/lib/bootstrap-reap.ts
|
|
@@ -32875,7 +33198,7 @@ async function reapInstaller(opts) {
|
|
|
32875
33198
|
// src/commands/bootstrap/reap.ts
|
|
32876
33199
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
32877
33200
|
static paths = [["bootstrap", "reap"]];
|
|
32878
|
-
static usage =
|
|
33201
|
+
static usage = Command60.Usage({
|
|
32879
33202
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
32880
33203
|
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.",
|
|
32881
33204
|
examples: [
|
|
@@ -32885,9 +33208,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
32885
33208
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
32886
33209
|
]
|
|
32887
33210
|
});
|
|
32888
|
-
force =
|
|
32889
|
-
sweepOrphans =
|
|
32890
|
-
yes =
|
|
33211
|
+
force = Option57.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
|
|
33212
|
+
sweepOrphans = Option57.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
|
|
33213
|
+
yes = Option57.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
|
|
32891
33214
|
async executeCommand() {
|
|
32892
33215
|
if (this.sweepOrphans === true) {
|
|
32893
33216
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -32984,7 +33307,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
32984
33307
|
import * as fs41 from "fs";
|
|
32985
33308
|
import * as os21 from "os";
|
|
32986
33309
|
import * as path46 from "path";
|
|
32987
|
-
import { Command as
|
|
33310
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
32988
33311
|
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
32989
33312
|
init_errors();
|
|
32990
33313
|
|
|
@@ -33812,7 +34135,7 @@ function renderDeployFailure(error) {
|
|
|
33812
34135
|
}
|
|
33813
34136
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
33814
34137
|
static paths = [["bootstrap", "ui"]];
|
|
33815
|
-
static usage =
|
|
34138
|
+
static usage = Command61.Usage({
|
|
33816
34139
|
description: "Deploy Ezra + start the local onboarding chat UI in the background (returns immediately).",
|
|
33817
34140
|
details: [
|
|
33818
34141
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -33833,16 +34156,16 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
33833
34156
|
["Experimental: start the voice relay (no effect on the text-only intake)", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
|
|
33834
34157
|
]
|
|
33835
34158
|
});
|
|
33836
|
-
repoRoot =
|
|
33837
|
-
port =
|
|
33838
|
-
endpoint =
|
|
34159
|
+
repoRoot = Option58.String("--repo-root");
|
|
34160
|
+
port = Option58.String("--port", "3000");
|
|
34161
|
+
endpoint = Option58.String("--endpoint", {
|
|
33839
34162
|
description: "Foundry project endpoint to target \u2014 overrides discovery when this install's resource group holds more than one Foundry project."
|
|
33840
34163
|
});
|
|
33841
|
-
prepOnly =
|
|
33842
|
-
skipInstall =
|
|
33843
|
-
stop =
|
|
33844
|
-
foreground =
|
|
33845
|
-
voice =
|
|
34164
|
+
prepOnly = Option58.Boolean("--prep-only", false);
|
|
34165
|
+
skipInstall = Option58.Boolean("--skip-install", false);
|
|
34166
|
+
stop = Option58.Boolean("--stop", false);
|
|
34167
|
+
foreground = Option58.Boolean("--foreground", false);
|
|
34168
|
+
voice = Option58.Boolean("--voice", false, {
|
|
33846
34169
|
description: "Experimental: when serving, starts the voice relay and writes the intake voice env var. The onboarding intake is text-only and is unaffected by this flag \u2014 no voice worker is registered for it."
|
|
33847
34170
|
});
|
|
33848
34171
|
async executeCommand() {
|
|
@@ -34014,10 +34337,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
34014
34337
|
};
|
|
34015
34338
|
|
|
34016
34339
|
// src/commands/bootstrap/seed-profile.ts
|
|
34017
|
-
import { Command as
|
|
34340
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
34018
34341
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
34019
34342
|
static paths = [["bootstrap", "seed-profile"]];
|
|
34020
|
-
static usage =
|
|
34343
|
+
static usage = Command62.Usage({
|
|
34021
34344
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding intake.",
|
|
34022
34345
|
details: "Reads the latest onboarding conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/ezra-brain via the GitHub App. Idempotent. --watch polls until the founder finishes the intake.",
|
|
34023
34346
|
examples: [
|
|
@@ -34025,11 +34348,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34025
34348
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
34026
34349
|
]
|
|
34027
34350
|
});
|
|
34028
|
-
endpoint =
|
|
34029
|
-
brain =
|
|
34030
|
-
watch =
|
|
34031
|
-
timeout =
|
|
34032
|
-
githubAppCreds =
|
|
34351
|
+
endpoint = Option59.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
34352
|
+
brain = Option59.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
34353
|
+
watch = Option59.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
34354
|
+
timeout = Option59.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
34355
|
+
githubAppCreds = Option59.String("--github-app-creds");
|
|
34033
34356
|
async executeCommand() {
|
|
34034
34357
|
const ctx = await resolveSeedContext({
|
|
34035
34358
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -34092,7 +34415,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34092
34415
|
import * as fs42 from "fs";
|
|
34093
34416
|
import * as os22 from "os";
|
|
34094
34417
|
import * as path47 from "path";
|
|
34095
|
-
import { Command as
|
|
34418
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34096
34419
|
init_errors();
|
|
34097
34420
|
|
|
34098
34421
|
// src/lib/telemetry-enroll.ts
|
|
@@ -34174,7 +34497,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
34174
34497
|
}
|
|
34175
34498
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
34176
34499
|
static paths = [["telemetry", "enroll"]];
|
|
34177
|
-
static usage =
|
|
34500
|
+
static usage = Command63.Usage({
|
|
34178
34501
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
34179
34502
|
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.",
|
|
34180
34503
|
examples: [
|
|
@@ -34182,11 +34505,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34182
34505
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
34183
34506
|
]
|
|
34184
34507
|
});
|
|
34185
|
-
company =
|
|
34186
|
-
contactEmail =
|
|
34187
|
-
subscription =
|
|
34188
|
-
resourceGroup =
|
|
34189
|
-
force =
|
|
34508
|
+
company = Option60.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
34509
|
+
contactEmail = Option60.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
34510
|
+
subscription = Option60.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
34511
|
+
resourceGroup = Option60.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
34512
|
+
force = Option60.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
34190
34513
|
async executeCommand() {
|
|
34191
34514
|
const account = await getAzAccount();
|
|
34192
34515
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -34236,7 +34559,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34236
34559
|
};
|
|
34237
34560
|
|
|
34238
34561
|
// src/commands/companion/bridge.ts
|
|
34239
|
-
import { Command as
|
|
34562
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
34240
34563
|
|
|
34241
34564
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
34242
34565
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -35289,7 +35612,7 @@ async function runCompanionBridgeServe(stdin, stdout, stderr, deps = {}) {
|
|
|
35289
35612
|
}
|
|
35290
35613
|
|
|
35291
35614
|
// src/commands/companion/bridge.ts
|
|
35292
|
-
var
|
|
35615
|
+
var defaultDeps4 = {
|
|
35293
35616
|
roster: rosterCompanions,
|
|
35294
35617
|
send: sendCompanionMessage,
|
|
35295
35618
|
converse: converseCompanionMessage,
|
|
@@ -35324,7 +35647,7 @@ function exitFor(terminal) {
|
|
|
35324
35647
|
}
|
|
35325
35648
|
return 2;
|
|
35326
35649
|
}
|
|
35327
|
-
async function runCompanionBridge(stdin, stdout, stderr, deps =
|
|
35650
|
+
async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps4) {
|
|
35328
35651
|
let request;
|
|
35329
35652
|
try {
|
|
35330
35653
|
request = parseRequestLine(await readSingleRequest(stdin));
|
|
@@ -35353,14 +35676,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
35353
35676
|
return 3;
|
|
35354
35677
|
}
|
|
35355
35678
|
}
|
|
35356
|
-
var CompanionBridgeCommand = class extends
|
|
35679
|
+
var CompanionBridgeCommand = class extends Command64 {
|
|
35357
35680
|
static paths = [["companion", "_bridge"]];
|
|
35358
35681
|
/**
|
|
35359
35682
|
* One process serving many requests instead of one per request, so the
|
|
35360
35683
|
* session keeps its authenticated context between them. A CLI predating the
|
|
35361
35684
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
35362
35685
|
*/
|
|
35363
|
-
serve =
|
|
35686
|
+
serve = Option61.Boolean("--serve", false);
|
|
35364
35687
|
async execute() {
|
|
35365
35688
|
if (this.serve) {
|
|
35366
35689
|
return runCompanionBridgeServe(
|
|
@@ -35378,7 +35701,7 @@ var CompanionBridgeCommand = class extends Command63 {
|
|
|
35378
35701
|
};
|
|
35379
35702
|
|
|
35380
35703
|
// src/commands/companion/status.ts
|
|
35381
|
-
import { Command as
|
|
35704
|
+
import { Command as Command65 } from "clipanion";
|
|
35382
35705
|
async function withTimeout(work, ms) {
|
|
35383
35706
|
let timer;
|
|
35384
35707
|
try {
|
|
@@ -35450,7 +35773,7 @@ Run: m8t companion install
|
|
|
35450
35773
|
}
|
|
35451
35774
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
35452
35775
|
static paths = [["companion", "status"]];
|
|
35453
|
-
static usage =
|
|
35776
|
+
static usage = Command65.Usage({
|
|
35454
35777
|
description: "Verify the installed desktop companion without launching it."
|
|
35455
35778
|
});
|
|
35456
35779
|
async executeCommand() {
|
|
@@ -35464,7 +35787,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
35464
35787
|
};
|
|
35465
35788
|
|
|
35466
35789
|
// src/commands/companion/repair.ts
|
|
35467
|
-
import { Command as
|
|
35790
|
+
import { Command as Command66, Option as Option62 } from "clipanion";
|
|
35468
35791
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
35469
35792
|
const state = await repair();
|
|
35470
35793
|
if (state.state === "not-released") {
|
|
@@ -35483,10 +35806,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
35483
35806
|
}
|
|
35484
35807
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
35485
35808
|
static paths = [["companion", "repair"]];
|
|
35486
|
-
static usage =
|
|
35809
|
+
static usage = Command66.Usage({
|
|
35487
35810
|
description: "Restore the desktop companions and start-at-login state."
|
|
35488
35811
|
});
|
|
35489
|
-
resourceGroup =
|
|
35812
|
+
resourceGroup = Option62.String("--resource-group", {
|
|
35490
35813
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
35491
35814
|
});
|
|
35492
35815
|
async executeCommand() {
|
|
@@ -35500,7 +35823,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
35500
35823
|
};
|
|
35501
35824
|
|
|
35502
35825
|
// src/commands/companion/uninstall.ts
|
|
35503
|
-
import { Command as
|
|
35826
|
+
import { Command as Command67 } from "clipanion";
|
|
35504
35827
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
35505
35828
|
const state = await uninstall();
|
|
35506
35829
|
if (state.state !== "not-installed") {
|
|
@@ -35512,7 +35835,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
35512
35835
|
}
|
|
35513
35836
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
35514
35837
|
static paths = [["companion", "uninstall"]];
|
|
35515
|
-
static usage =
|
|
35838
|
+
static usage = Command67.Usage({
|
|
35516
35839
|
description: "Remove only this user's desktop companion installation."
|
|
35517
35840
|
});
|
|
35518
35841
|
async executeCommand() {
|
|
@@ -35582,6 +35905,7 @@ cli.register(A2aDisableCommand);
|
|
|
35582
35905
|
cli.register(EvalSkillCommand);
|
|
35583
35906
|
cli.register(EvalExamCommand);
|
|
35584
35907
|
cli.register(DreamRunCommand);
|
|
35908
|
+
cli.register(ConversationsSweepCommand);
|
|
35585
35909
|
cli.register(FoundryCreateCommand);
|
|
35586
35910
|
cli.register(FoundryAwaitReadyCommand);
|
|
35587
35911
|
cli.register(BootstrapPreflightCommand);
|