@m8t-stack/cli 0.2.80 → 0.2.82
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 +912 -163
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -229,10 +229,77 @@ var init_agent_roster = __esm({
|
|
|
229
229
|
});
|
|
230
230
|
|
|
231
231
|
// ../../packages/api-contract/dist/esm/ui-directive.js
|
|
232
|
-
|
|
232
|
+
function parseArgs(input) {
|
|
233
|
+
if (!isRecord(input) || !nonEmpty(input.title) || !Array.isArray(input.options))
|
|
234
|
+
return null;
|
|
235
|
+
const { options } = input;
|
|
236
|
+
if (options.length < MIN_OPTIONS || options.length > MAX_OPTIONS)
|
|
237
|
+
return null;
|
|
238
|
+
const parsed = [];
|
|
239
|
+
for (const option of options) {
|
|
240
|
+
if (!isRecord(option) || !nonEmpty(option.label) || !nonEmpty(option.detail))
|
|
241
|
+
return null;
|
|
242
|
+
parsed.push({ label: option.label, detail: option.detail });
|
|
243
|
+
}
|
|
244
|
+
return { title: input.title, options: parsed };
|
|
245
|
+
}
|
|
246
|
+
function parseState(input, optionCount) {
|
|
247
|
+
if (!isRecord(input))
|
|
248
|
+
return null;
|
|
249
|
+
switch (input.status) {
|
|
250
|
+
case "pending":
|
|
251
|
+
return { status: "pending" };
|
|
252
|
+
case "dismissed":
|
|
253
|
+
return { status: "dismissed" };
|
|
254
|
+
case "submitting":
|
|
255
|
+
return inRange(input.optionIndex, optionCount) ? { status: "submitting", optionIndex: input.optionIndex } : null;
|
|
256
|
+
case "selected":
|
|
257
|
+
if (!inRange(input.optionIndex, optionCount))
|
|
258
|
+
return null;
|
|
259
|
+
return input.acknowledgement === "pending" || input.acknowledgement === "complete" || input.acknowledgement === "error" ? { status: "selected", optionIndex: input.optionIndex, acknowledgement: input.acknowledgement } : null;
|
|
260
|
+
case "error":
|
|
261
|
+
return nonEmpty(input.message) ? { status: "error", message: input.message } : null;
|
|
262
|
+
default:
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function parseUiDirectiveFrame(input) {
|
|
267
|
+
if (!isRecord(input))
|
|
268
|
+
return null;
|
|
269
|
+
if (input.source !== "voice" && input.source !== "text")
|
|
270
|
+
return null;
|
|
271
|
+
const directive = input.directive;
|
|
272
|
+
if (!isRecord(directive))
|
|
273
|
+
return null;
|
|
274
|
+
if (directive.version !== 1 || directive.name !== "present_decision")
|
|
275
|
+
return null;
|
|
276
|
+
if (!nonEmpty(directive.callId))
|
|
277
|
+
return null;
|
|
278
|
+
const args = parseArgs(directive.args);
|
|
279
|
+
if (!args)
|
|
280
|
+
return null;
|
|
281
|
+
const state = parseState(directive.state, args.options.length);
|
|
282
|
+
if (!state)
|
|
283
|
+
return null;
|
|
284
|
+
return {
|
|
285
|
+
directive: { version: 1, name: "present_decision", callId: directive.callId, args, state },
|
|
286
|
+
source: input.source
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function isDecisionRefusalReason(value) {
|
|
290
|
+
return typeof value === "string" && decisionRefusalReasons.includes(value);
|
|
291
|
+
}
|
|
292
|
+
var UI_DIRECTIVE_PART_TYPE, MIN_OPTIONS, MAX_OPTIONS, isRecord, nonEmpty, inRange, UI_DIRECTIVE_RESULT_PART_TYPE, DECISION_REFUSAL_REASONS, decisionRefusalReasons;
|
|
233
293
|
var init_ui_directive = __esm({
|
|
234
294
|
"../../packages/api-contract/dist/esm/ui-directive.js"() {
|
|
235
295
|
"use strict";
|
|
296
|
+
UI_DIRECTIVE_PART_TYPE = "data-ui-directive";
|
|
297
|
+
MIN_OPTIONS = 2;
|
|
298
|
+
MAX_OPTIONS = 4;
|
|
299
|
+
isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
300
|
+
nonEmpty = (value) => typeof value === "string" && value.trim().length > 0;
|
|
301
|
+
inRange = (value, count) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value < count;
|
|
302
|
+
UI_DIRECTIVE_RESULT_PART_TYPE = "data-ui-result";
|
|
236
303
|
DECISION_REFUSAL_REASONS = {
|
|
237
304
|
/** 409 — no live call with that `callId`. Answered twice, or talked past and dismissed. */
|
|
238
305
|
notPending: "decision_not_pending",
|
|
@@ -1400,7 +1467,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1400
1467
|
import { Builtins, Cli } from "clipanion";
|
|
1401
1468
|
|
|
1402
1469
|
// src/lib/package-version.ts
|
|
1403
|
-
var CLI_VERSION = "0.2.
|
|
1470
|
+
var CLI_VERSION = "0.2.82";
|
|
1404
1471
|
|
|
1405
1472
|
// src/lib/render-error.ts
|
|
1406
1473
|
init_errors();
|
|
@@ -12728,6 +12795,25 @@ function classifyFoundryError(err) {
|
|
|
12728
12795
|
return { category: "unknown", retryable: false, status, message };
|
|
12729
12796
|
}
|
|
12730
12797
|
|
|
12798
|
+
// ../../packages/foundry-invoke/dist/esm/artifacts.js
|
|
12799
|
+
var FENCE_RE = /```json m8t:artifacts\s*\n([\s\S]*?)\n```/;
|
|
12800
|
+
function parseArtifacts(raw) {
|
|
12801
|
+
const text = raw;
|
|
12802
|
+
const m = FENCE_RE.exec(text);
|
|
12803
|
+
if (!m)
|
|
12804
|
+
return { text: text.trimEnd(), artifacts: [] };
|
|
12805
|
+
let artifacts = [];
|
|
12806
|
+
try {
|
|
12807
|
+
const parsed = JSON.parse(m[1]);
|
|
12808
|
+
if (Array.isArray(parsed))
|
|
12809
|
+
artifacts = parsed;
|
|
12810
|
+
} catch {
|
|
12811
|
+
artifacts = [];
|
|
12812
|
+
}
|
|
12813
|
+
const clean2 = text.slice(0, m.index).trimEnd();
|
|
12814
|
+
return { text: clean2, artifacts };
|
|
12815
|
+
}
|
|
12816
|
+
|
|
12731
12817
|
// src/lib/render-error.ts
|
|
12732
12818
|
function renderHint(hint) {
|
|
12733
12819
|
return hint.split("\n").join("\n ");
|
|
@@ -26631,13 +26717,66 @@ import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
|
26631
26717
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
26632
26718
|
import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
|
|
26633
26719
|
|
|
26720
|
+
// ../../packages/agent-ledger/dist/esm/pii.js
|
|
26721
|
+
import { createHmac } from "crypto";
|
|
26722
|
+
var EMAIL_PLACEHOLDER = "[email]";
|
|
26723
|
+
var PHONE_PLACEHOLDER = "[phone]";
|
|
26724
|
+
var SECRET_PLACEHOLDER = "[secret]";
|
|
26725
|
+
var PEM_BLOCK_RE = /-----BEGIN [A-Z0-9 ]{0,32}PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]{0,32}PRIVATE KEY-----/g;
|
|
26726
|
+
var JWT_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}(?:\.[A-Za-z0-9_-]{10,})?/g;
|
|
26727
|
+
var HTTP_CREDENTIAL_RE = /\b(Bearer|Basic)[ \t]+(?=[A-Za-z0-9._~+/=-]{0,80}[0-9+/=])[A-Za-z0-9._~+/=-]{16,}/gi;
|
|
26728
|
+
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;
|
|
26729
|
+
var KEYED_VALUE = `("[^"\\n]{8,}"|'[^'\\n]{8,}'|(?=[A-Za-z0-9+/=_.~%-]{0,64}[0-9+/=])[A-Za-z0-9+/=_.~%-]{8,})`;
|
|
26730
|
+
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");
|
|
26731
|
+
var KEYED_WEAK_SECRET_RE = new RegExp(`(?<![A-Za-z0-9])(authorization|secret|token|sig)(["']?\\s*[:=]\\s*)` + KEYED_VALUE, "gi");
|
|
26732
|
+
var TELEGRAM_BOT_TOKEN_RE = /(?<!\d)\d{8,10}:AA[A-Za-z0-9_-]{32,34}(?![A-Za-z0-9_-])/g;
|
|
26733
|
+
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;
|
|
26734
|
+
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;
|
|
26735
|
+
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;
|
|
26736
|
+
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");
|
|
26737
|
+
var PHONE_INTL_RE = /(?<![\d/.-])\+\d{1,3}(?:[ .-]?\(\d{1,4}\))?(?:[ .-]?\d){6,12}(?!\d)/g;
|
|
26738
|
+
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;
|
|
26739
|
+
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;
|
|
26740
|
+
var PEM_OPEN_RE = /-----BEGIN [A-Z0-9 ]{0,32}PRIVATE KEY-----/;
|
|
26741
|
+
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;
|
|
26742
|
+
function failClosedOnCutConstructs(text) {
|
|
26743
|
+
const pem = PEM_OPEN_RE.exec(text);
|
|
26744
|
+
if (pem && !/-----END [A-Z0-9 ]{0,32}PRIVATE KEY-----/.test(text.slice(pem.index))) {
|
|
26745
|
+
return text.slice(0, pem.index) + SECRET_PLACEHOLDER;
|
|
26746
|
+
}
|
|
26747
|
+
const quoted = UNCLOSED_QUOTED_SECRET_RE.exec(text);
|
|
26748
|
+
if (quoted)
|
|
26749
|
+
return text.slice(0, quoted.index) + SECRET_PLACEHOLDER;
|
|
26750
|
+
return text;
|
|
26751
|
+
}
|
|
26752
|
+
function maskSecrets(text) {
|
|
26753
|
+
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);
|
|
26754
|
+
return failClosedOnCutConstructs(masked);
|
|
26755
|
+
}
|
|
26756
|
+
function maskContact(text) {
|
|
26757
|
+
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}`);
|
|
26758
|
+
}
|
|
26759
|
+
var BARE_PHONE_RUN_RE = /(?<![\w.,-])\d{7,15}(?!\.?\d)(?![\w,-])/g;
|
|
26760
|
+
function maskContactStrict(text) {
|
|
26761
|
+
return maskContact(text).replace(BARE_PHONE_RUN_RE, PHONE_PLACEHOLDER);
|
|
26762
|
+
}
|
|
26763
|
+
var USERREF_DIGEST_VERSION = "1";
|
|
26764
|
+
function userRefHashKey() {
|
|
26765
|
+
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");
|
|
26766
|
+
}
|
|
26767
|
+
function maskUserRef(ref) {
|
|
26768
|
+
if (ref == null)
|
|
26769
|
+
return void 0;
|
|
26770
|
+
return ref.replace(EMAIL_RE, (m) => `email#${USERREF_DIGEST_VERSION}:${createHmac("sha256", userRefHashKey()).update(m.toLowerCase()).digest("hex").slice(0, 32)}`);
|
|
26771
|
+
}
|
|
26772
|
+
|
|
26634
26773
|
// ../../packages/agent-ledger/dist/esm/read/ledger-identity.js
|
|
26635
26774
|
var CHANNEL_SOURCES = /* @__PURE__ */ new Set(["telegram", "slack"]);
|
|
26636
26775
|
function resolveIdentity(source, userRef, members = []) {
|
|
26637
26776
|
if (!userRef)
|
|
26638
26777
|
return { source, ref: "unknown" };
|
|
26639
26778
|
if (CHANNEL_SOURCES.has(source)) {
|
|
26640
|
-
const m = members.find((x) => x.source === source && x.handle.toLowerCase() === userRef.toLowerCase());
|
|
26779
|
+
const m = members.find((x) => x.source === source && (x.handle.toLowerCase() === userRef.toLowerCase() || maskUserRef(x.handle.toLowerCase()) === userRef.toLowerCase()));
|
|
26641
26780
|
if (m)
|
|
26642
26781
|
return { source, ref: userRef, display: m.displayName };
|
|
26643
26782
|
}
|
|
@@ -26655,7 +26794,7 @@ var str3 = (v) => {
|
|
|
26655
26794
|
return "";
|
|
26656
26795
|
};
|
|
26657
26796
|
var req = (v, fallback = "") => str3(v) ?? fallback;
|
|
26658
|
-
function
|
|
26797
|
+
function parseArtifacts2(v) {
|
|
26659
26798
|
if (typeof v !== "string" || v === "")
|
|
26660
26799
|
return void 0;
|
|
26661
26800
|
try {
|
|
@@ -26689,7 +26828,7 @@ function mapEntity(e) {
|
|
|
26689
26828
|
delegationId: str3(e.delegationId),
|
|
26690
26829
|
parentEventId: str3(e.parentEventId),
|
|
26691
26830
|
depth: e.depth == null ? void 0 : Number(e.depth),
|
|
26692
|
-
artifacts:
|
|
26831
|
+
artifacts: parseArtifacts2(e.artifacts)
|
|
26693
26832
|
};
|
|
26694
26833
|
}
|
|
26695
26834
|
var rank = (o) => o === "ok" ? 0 : 1;
|
|
@@ -26947,6 +27086,10 @@ function makeRowKey(timestampIso, eventId) {
|
|
|
26947
27086
|
return `${String(reverse).padStart(19, "0")}_${eventId}`;
|
|
26948
27087
|
}
|
|
26949
27088
|
|
|
27089
|
+
// ../../packages/agent-ledger/dist/esm/entity.js
|
|
27090
|
+
var SNIPPET_MAX_LEN = 256;
|
|
27091
|
+
var PII_SCAN_WINDOW = SNIPPET_MAX_LEN * 16;
|
|
27092
|
+
|
|
26950
27093
|
// ../../packages/brain/engine/dist/esm/cursor.js
|
|
26951
27094
|
var CURSOR_PARTITION_KEY = "dreamcursor";
|
|
26952
27095
|
function isAbsentCursorError(e) {
|
|
@@ -27175,7 +27318,7 @@ function mapItems(raw) {
|
|
|
27175
27318
|
continue;
|
|
27176
27319
|
if (item.role !== "user" && item.role !== "assistant")
|
|
27177
27320
|
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")) : "";
|
|
27321
|
+
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
27322
|
const at = typeof item.created_at === "number" ? new Date(item.created_at * 1e3).toISOString() : void 0;
|
|
27180
27323
|
acc.push(at ? { role: item.role, text, at } : { role: item.role, text });
|
|
27181
27324
|
}
|
|
@@ -27460,7 +27603,9 @@ async function runPipeline(args) {
|
|
|
27460
27603
|
const grpRows = rowsByGroupKey.get(groupKeyOf(g)) ?? [];
|
|
27461
27604
|
(consumedRowsByPk[pk] ??= []).push(...grpRows);
|
|
27462
27605
|
const provRows = grpRows.map((r) => ({ rowKey: rowStorageKey(r), eventTimestamp: r.eventTimestamp }));
|
|
27463
|
-
const
|
|
27606
|
+
const trustedOwner = g.source !== "a2a" && g.user.ref !== "unknown" && !g.user.ref.startsWith("svc:");
|
|
27607
|
+
const dreamItems = trustedOwner ? items : items.map((i) => ({ ...i, text: maskContactStrict(i.text) }));
|
|
27608
|
+
const conv = normalize(ng, dreamItems, enrichment);
|
|
27464
27609
|
conv.provenance = { pk, rows: provRows };
|
|
27465
27610
|
conversations.push(conv);
|
|
27466
27611
|
if (outcome === "consumed")
|
|
@@ -29217,14 +29362,278 @@ function defaultDeps(overrides) {
|
|
|
29217
29362
|
return deps;
|
|
29218
29363
|
}
|
|
29219
29364
|
|
|
29220
|
-
// src/commands/
|
|
29365
|
+
// src/commands/conversations/sweep.ts
|
|
29366
|
+
import { createHash as createHash4 } from "crypto";
|
|
29221
29367
|
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
29368
|
+
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
29369
|
+
import { TableClient as TableClient8 } from "@azure/data-tables";
|
|
29370
|
+
import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
|
|
29371
|
+
init_errors();
|
|
29372
|
+
var LEDGER_TABLE_NAME2 = "AgentLedger";
|
|
29373
|
+
var KEY_LIFETIME_DAYS = 30;
|
|
29374
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
29375
|
+
function ownerDigest(nativeUserKey) {
|
|
29376
|
+
return createHash4("sha256").update(nativeUserKey).digest("base64url");
|
|
29377
|
+
}
|
|
29378
|
+
function svcPrincipal(svcRef) {
|
|
29379
|
+
return svcRef.split(":")[1] ?? "";
|
|
29380
|
+
}
|
|
29381
|
+
function svcConversationFacts(rows) {
|
|
29382
|
+
const facts = /* @__PURE__ */ new Map();
|
|
29383
|
+
for (const r of rows) {
|
|
29384
|
+
if (!r.foundryConversationId) continue;
|
|
29385
|
+
if (!r.userRef?.startsWith("svc:")) continue;
|
|
29386
|
+
const prev = facts.get(r.foundryConversationId);
|
|
29387
|
+
if (!prev || r.eventTimestamp > prev.lastSeen) {
|
|
29388
|
+
facts.set(r.foundryConversationId, {
|
|
29389
|
+
conversationId: r.foundryConversationId,
|
|
29390
|
+
svcRef: r.userRef,
|
|
29391
|
+
principal: svcPrincipal(r.userRef),
|
|
29392
|
+
agent: r.agentName,
|
|
29393
|
+
lastSeen: r.eventTimestamp
|
|
29394
|
+
});
|
|
29395
|
+
}
|
|
29396
|
+
}
|
|
29397
|
+
return facts;
|
|
29398
|
+
}
|
|
29399
|
+
function selectStaleCandidates(facts, cutoffIso) {
|
|
29400
|
+
return [...facts.values()].filter((f) => f.lastSeen < cutoffIso).sort((a, b) => a.lastSeen < b.lastSeen ? -1 : 1);
|
|
29401
|
+
}
|
|
29402
|
+
function confirmCandidate(fact, meta, newestItemAt, cutoffIso) {
|
|
29403
|
+
if (meta.app !== "m8t-webapp") return { ok: false, reason: `unexpected app tag '${meta.app ?? ""}'` };
|
|
29404
|
+
const expectedOwner = `web:${ownerDigest(fact.svcRef)}`;
|
|
29405
|
+
if (meta.ownerKey !== expectedOwner) return { ok: false, reason: "owner mismatch" };
|
|
29406
|
+
if (newestItemAt === null) return { ok: false, reason: "no items to prove inactivity" };
|
|
29407
|
+
if (newestItemAt >= cutoffIso) return { ok: false, reason: "recent activity" };
|
|
29408
|
+
return { ok: true };
|
|
29409
|
+
}
|
|
29410
|
+
function statusOf2(e) {
|
|
29411
|
+
const err = e;
|
|
29412
|
+
return err?.statusCode ?? err?.status;
|
|
29413
|
+
}
|
|
29414
|
+
function defaultDeps2() {
|
|
29415
|
+
const credential2 = new AzureCliCredential2();
|
|
29416
|
+
const openaiFor = (ctx) => new AIProjectClient4(ctx.projectEndpoint, credential2).getOpenAIClient();
|
|
29417
|
+
return {
|
|
29418
|
+
async resolveContext(opts) {
|
|
29419
|
+
const account = await getAzAccount();
|
|
29420
|
+
const subscriptionId = opts.subscription ?? account.subscriptionId;
|
|
29421
|
+
const project = await resolveFoundryProject({
|
|
29422
|
+
credential: credential2,
|
|
29423
|
+
subscriptionId,
|
|
29424
|
+
interactive: false,
|
|
29425
|
+
endpoint: opts.endpoint
|
|
29426
|
+
});
|
|
29427
|
+
const m = /\/resourceGroups\/([^/]+)/i.exec(project.accountScope);
|
|
29428
|
+
if (!m) throw new LocalCliError({ code: "sweep_context", message: `Could not parse resource group from '${project.accountScope}'.` });
|
|
29429
|
+
const { ledgerTableEndpoint } = await discoverLedgerResources({
|
|
29430
|
+
credential: credential2,
|
|
29431
|
+
subscriptionId,
|
|
29432
|
+
resourceGroup: m[1]
|
|
29433
|
+
});
|
|
29434
|
+
return { projectEndpoint: project.endpoint, ledgerTableEndpoint };
|
|
29435
|
+
},
|
|
29436
|
+
async fetchRows(ctx, fromIso, toIso) {
|
|
29437
|
+
const client = new TableClient8(ctx.ledgerTableEndpoint, LEDGER_TABLE_NAME2, credential2);
|
|
29438
|
+
return fetchLedgerRows({ workers: [], source: "all", from: fromIso, to: toIso }, client);
|
|
29439
|
+
},
|
|
29440
|
+
async retrieveMetadata(ctx, conversationId) {
|
|
29441
|
+
try {
|
|
29442
|
+
const conv = await openaiFor(ctx).conversations.retrieve(conversationId);
|
|
29443
|
+
const md = conv.metadata;
|
|
29444
|
+
const s = (v) => typeof v === "string" ? v : void 0;
|
|
29445
|
+
if (!md || typeof md !== "object") return {};
|
|
29446
|
+
return { app: s(md.app), ownerKey: s(md.ownerKey), platform: s(md.platform) };
|
|
29447
|
+
} catch (e) {
|
|
29448
|
+
if (statusOf2(e) === 404) return "gone";
|
|
29449
|
+
throw e;
|
|
29450
|
+
}
|
|
29451
|
+
},
|
|
29452
|
+
async newestItemAt(ctx, conversationId) {
|
|
29453
|
+
try {
|
|
29454
|
+
for await (const item of openaiFor(ctx).conversations.items.list(conversationId)) {
|
|
29455
|
+
const created = item.created_at;
|
|
29456
|
+
if (typeof created !== "number") return { at: null };
|
|
29457
|
+
return { at: new Date(created * 1e3).toISOString() };
|
|
29458
|
+
}
|
|
29459
|
+
return { at: null };
|
|
29460
|
+
} catch (e) {
|
|
29461
|
+
if (statusOf2(e) === 404) return "gone";
|
|
29462
|
+
throw e;
|
|
29463
|
+
}
|
|
29464
|
+
},
|
|
29465
|
+
async deleteConversation(ctx, conversationId) {
|
|
29466
|
+
try {
|
|
29467
|
+
await openaiFor(ctx).conversations.delete(conversationId);
|
|
29468
|
+
return "deleted";
|
|
29469
|
+
} catch (e) {
|
|
29470
|
+
if (statusOf2(e) === 404) return "already-gone";
|
|
29471
|
+
throw e;
|
|
29472
|
+
}
|
|
29473
|
+
}
|
|
29474
|
+
};
|
|
29475
|
+
}
|
|
29476
|
+
var ConversationsSweepCommand = class extends M8tCommand {
|
|
29477
|
+
static paths = [["conversations", "sweep"]];
|
|
29478
|
+
static usage = Command53.Usage({
|
|
29479
|
+
category: "Conversations",
|
|
29480
|
+
description: "Delete expired public-visitor conversations (dry run by default)",
|
|
29481
|
+
details: `
|
|
29482
|
+
Finds conversations owned by delegated end users (public visitors) whose
|
|
29483
|
+
last activity predates the retention window (${String(KEY_LIFETIME_DAYS)}-day key life + grace),
|
|
29484
|
+
confirms each against its own metadata and newest item, and \u2014 only with
|
|
29485
|
+
--delete \u2014 permanently removes them. Team, channel, and delegated-work
|
|
29486
|
+
conversations are structurally out of scope. The masked ledger telemetry
|
|
29487
|
+
of swept conversations is kept.
|
|
29488
|
+
`,
|
|
29489
|
+
examples: [
|
|
29490
|
+
["Report what would be deleted (nothing is)", "m8t conversations sweep"],
|
|
29491
|
+
["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
|
|
29492
|
+
]
|
|
29493
|
+
});
|
|
29494
|
+
doDelete = Option50.Boolean("--delete", false, {
|
|
29495
|
+
description: "Perform deletions (without this flag the command only reports)"
|
|
29496
|
+
});
|
|
29497
|
+
principal = Option50.String("--principal", {
|
|
29498
|
+
description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
|
|
29499
|
+
});
|
|
29500
|
+
max = Option50.String("--max", "200", { description: "Maximum deletions per run" });
|
|
29501
|
+
graceDays = Option50.String("--grace-days", "14", {
|
|
29502
|
+
description: "Days past the 30-day key life before a conversation is eligible"
|
|
29503
|
+
});
|
|
29504
|
+
lookbackDays = Option50.String("--lookback-days", "180", {
|
|
29505
|
+
description: "How far back to scan ledger activity for candidates"
|
|
29506
|
+
});
|
|
29507
|
+
subscription = Option50.String("--subscription", { description: "Azure subscription id override" });
|
|
29508
|
+
endpoint = Option50.String("--endpoint", { description: "Foundry project endpoint override" });
|
|
29509
|
+
deps = defaultDeps2();
|
|
29510
|
+
async executeCommand() {
|
|
29511
|
+
const out = this.context.stdout;
|
|
29512
|
+
const max = Number.parseInt(this.max, 10);
|
|
29513
|
+
const grace = Number.parseInt(this.graceDays, 10);
|
|
29514
|
+
const lookback = Number.parseInt(this.lookbackDays, 10);
|
|
29515
|
+
if (!Number.isFinite(max) || max < 1) throw new LocalCliError({ code: "sweep_args", message: "--max must be a positive integer." });
|
|
29516
|
+
if (!Number.isFinite(grace) || grace < 0) throw new LocalCliError({ code: "sweep_args", message: "--grace-days must be a non-negative integer." });
|
|
29517
|
+
const retentionDays = KEY_LIFETIME_DAYS + grace;
|
|
29518
|
+
if (!Number.isFinite(lookback) || lookback <= retentionDays) {
|
|
29519
|
+
throw new LocalCliError({ code: "sweep_args", message: `--lookback-days must exceed the retention window (${String(retentionDays)} days).` });
|
|
29520
|
+
}
|
|
29521
|
+
const now = Date.now();
|
|
29522
|
+
const cutoffIso = new Date(now - retentionDays * DAY_MS).toISOString();
|
|
29523
|
+
const fromIso = new Date(now - lookback * DAY_MS).toISOString();
|
|
29524
|
+
const toIso = new Date(now).toISOString();
|
|
29525
|
+
const ctx = await this.deps.resolveContext({ subscription: this.subscription, endpoint: this.endpoint });
|
|
29526
|
+
out.write(`Scanning ledger activity ${fromIso} \u2192 ${toIso}; retention cutoff ${cutoffIso}.
|
|
29527
|
+
`);
|
|
29528
|
+
const rows = await this.deps.fetchRows(ctx, fromIso, toIso);
|
|
29529
|
+
const facts = svcConversationFacts(rows);
|
|
29530
|
+
const stale = selectStaleCandidates(facts, cutoffIso);
|
|
29531
|
+
if (this.doDelete && !this.principal) {
|
|
29532
|
+
throw new LocalCliError({
|
|
29533
|
+
code: "sweep_principal_required",
|
|
29534
|
+
message: "--delete requires --principal <service-principal oid>.",
|
|
29535
|
+
hint: "Run without --delete first: the dry run lists candidates grouped by principal so you can identify the visitor-minting one."
|
|
29536
|
+
});
|
|
29537
|
+
}
|
|
29538
|
+
const inScope = this.principal ? stale.filter((f) => f.principal === this.principal) : stale;
|
|
29539
|
+
const outOfScope = stale.length - inScope.length;
|
|
29540
|
+
out.write(
|
|
29541
|
+
`${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"
|
|
29542
|
+
);
|
|
29543
|
+
if (!this.principal && stale.length > 0) {
|
|
29544
|
+
const byPrincipal = /* @__PURE__ */ new Map();
|
|
29545
|
+
for (const f of stale) byPrincipal.set(f.principal, (byPrincipal.get(f.principal) ?? 0) + 1);
|
|
29546
|
+
out.write("Stale candidates by principal (pass --principal to scope deletion):\n");
|
|
29547
|
+
for (const [pr, n] of [...byPrincipal.entries()].sort((a, b) => b[1] - a[1])) {
|
|
29548
|
+
out.write(` ${pr || "(malformed)"} ${String(n)}
|
|
29549
|
+
`);
|
|
29550
|
+
}
|
|
29551
|
+
}
|
|
29552
|
+
out.write("\n");
|
|
29553
|
+
let attempts = 0;
|
|
29554
|
+
let deleted = 0;
|
|
29555
|
+
let alreadyGone = 0;
|
|
29556
|
+
let skipped = 0;
|
|
29557
|
+
let failures = 0;
|
|
29558
|
+
let remaining = 0;
|
|
29559
|
+
for (const fact of inScope) {
|
|
29560
|
+
if (attempts >= max) {
|
|
29561
|
+
remaining += 1;
|
|
29562
|
+
continue;
|
|
29563
|
+
}
|
|
29564
|
+
const ageDays = Math.floor((now - Date.parse(fact.lastSeen)) / DAY_MS);
|
|
29565
|
+
const label = `${fact.conversationId} agent=${fact.agent} last-active=${fact.lastSeen} (${String(ageDays)}d)`;
|
|
29566
|
+
let meta;
|
|
29567
|
+
let newest;
|
|
29568
|
+
try {
|
|
29569
|
+
meta = await this.deps.retrieveMetadata(ctx, fact.conversationId);
|
|
29570
|
+
newest = meta === "gone" ? "gone" : await this.deps.newestItemAt(ctx, fact.conversationId);
|
|
29571
|
+
} catch (e) {
|
|
29572
|
+
failures += 1;
|
|
29573
|
+
out.write(`${colors.error("failed")} ${label} \u2014 ${e.message}
|
|
29574
|
+
`);
|
|
29575
|
+
continue;
|
|
29576
|
+
}
|
|
29577
|
+
if (meta === "gone" || newest === "gone") {
|
|
29578
|
+
alreadyGone += 1;
|
|
29579
|
+
out.write(`${colors.dim("already gone")} ${label}
|
|
29580
|
+
`);
|
|
29581
|
+
continue;
|
|
29582
|
+
}
|
|
29583
|
+
const verdict = confirmCandidate(fact, meta, newest.at, cutoffIso);
|
|
29584
|
+
if (!verdict.ok) {
|
|
29585
|
+
skipped += 1;
|
|
29586
|
+
out.write(`${colors.warn("skip")} ${label} \u2014 ${verdict.reason}
|
|
29587
|
+
`);
|
|
29588
|
+
continue;
|
|
29589
|
+
}
|
|
29590
|
+
attempts += 1;
|
|
29591
|
+
if (!this.doDelete) {
|
|
29592
|
+
out.write(`${colors.field("would delete")} ${label}
|
|
29593
|
+
`);
|
|
29594
|
+
continue;
|
|
29595
|
+
}
|
|
29596
|
+
try {
|
|
29597
|
+
const result2 = await this.deps.deleteConversation(ctx, fact.conversationId);
|
|
29598
|
+
if (result2 === "deleted") {
|
|
29599
|
+
deleted += 1;
|
|
29600
|
+
out.write(`${colors.error("deleted")} ${label}
|
|
29601
|
+
`);
|
|
29602
|
+
} else {
|
|
29603
|
+
alreadyGone += 1;
|
|
29604
|
+
out.write(`${colors.dim("already gone")} ${label}
|
|
29605
|
+
`);
|
|
29606
|
+
}
|
|
29607
|
+
} catch (e) {
|
|
29608
|
+
failures += 1;
|
|
29609
|
+
out.write(`${colors.error("failed")} ${label} \u2014 ${e.message}
|
|
29610
|
+
`);
|
|
29611
|
+
}
|
|
29612
|
+
}
|
|
29613
|
+
out.write("\n");
|
|
29614
|
+
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";
|
|
29615
|
+
if (this.doDelete) {
|
|
29616
|
+
out.write(`Deleted ${String(deleted)}, ${tail}`);
|
|
29617
|
+
} else {
|
|
29618
|
+
out.write(`DRY RUN \u2014 nothing deleted. ${String(attempts)} conversation(s) would be deleted, ${tail}`);
|
|
29619
|
+
}
|
|
29620
|
+
if (failures > 0) {
|
|
29621
|
+
out.write(`${colors.error("Run incomplete:")} ${String(failures)} verification/deletion failure(s) \u2014 fix access and re-run.
|
|
29622
|
+
`);
|
|
29623
|
+
return 1;
|
|
29624
|
+
}
|
|
29625
|
+
return 0;
|
|
29626
|
+
}
|
|
29627
|
+
};
|
|
29628
|
+
|
|
29629
|
+
// src/commands/foundry/create.ts
|
|
29630
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
29222
29631
|
|
|
29223
29632
|
// src/lib/foundry-create.ts
|
|
29224
29633
|
init_errors();
|
|
29225
|
-
import { createHash as
|
|
29634
|
+
import { createHash as createHash5 } from "crypto";
|
|
29226
29635
|
function deriveAccountName(subscriptionId) {
|
|
29227
|
-
const h =
|
|
29636
|
+
const h = createHash5("sha256").update(subscriptionId).digest("hex").slice(0, 12);
|
|
29228
29637
|
return `m8t${h}`;
|
|
29229
29638
|
}
|
|
29230
29639
|
function assertHostedRegion(location) {
|
|
@@ -29456,7 +29865,7 @@ async function createFoundryProject(args) {
|
|
|
29456
29865
|
init_errors();
|
|
29457
29866
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
29458
29867
|
static paths = [["foundry", "create"]];
|
|
29459
|
-
static usage =
|
|
29868
|
+
static usage = Command54.Usage({
|
|
29460
29869
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
29461
29870
|
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
29871
|
examples: [
|
|
@@ -29465,16 +29874,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
29465
29874
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
29466
29875
|
]
|
|
29467
29876
|
});
|
|
29468
|
-
resourceGroup =
|
|
29469
|
-
location =
|
|
29470
|
-
account =
|
|
29471
|
-
project =
|
|
29472
|
-
model =
|
|
29473
|
-
modelVersion =
|
|
29474
|
-
capacity =
|
|
29475
|
-
subscription =
|
|
29476
|
-
skipQuotaCheck =
|
|
29477
|
-
output =
|
|
29877
|
+
resourceGroup = Option51.String("--resource-group");
|
|
29878
|
+
location = Option51.String("--location");
|
|
29879
|
+
account = Option51.String("--account");
|
|
29880
|
+
project = Option51.String("--project", "m8t");
|
|
29881
|
+
model = Option51.String("--model", "gpt-4.1-mini");
|
|
29882
|
+
modelVersion = Option51.String("--model-version", "2025-04-14");
|
|
29883
|
+
capacity = Option51.String("--capacity", "50");
|
|
29884
|
+
subscription = Option51.String("--subscription");
|
|
29885
|
+
skipQuotaCheck = Option51.Boolean("--skip-quota-check", false);
|
|
29886
|
+
output = Option51.String("--output");
|
|
29478
29887
|
async executeCommand() {
|
|
29479
29888
|
const mode = resolveOutputMode(
|
|
29480
29889
|
this.output,
|
|
@@ -29546,22 +29955,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
29546
29955
|
};
|
|
29547
29956
|
|
|
29548
29957
|
// src/commands/foundry/await-ready.ts
|
|
29549
|
-
import { Command as
|
|
29550
|
-
import { AzureCliCredential as
|
|
29958
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
29959
|
+
import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
|
|
29551
29960
|
init_errors();
|
|
29552
29961
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
29553
29962
|
static paths = [["foundry", "await-ready"]];
|
|
29554
|
-
static usage =
|
|
29963
|
+
static usage = Command55.Usage({
|
|
29555
29964
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
29556
29965
|
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
29966
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
29558
29967
|
});
|
|
29559
|
-
endpoint =
|
|
29560
|
-
consecutive =
|
|
29561
|
-
attempts =
|
|
29562
|
-
interval =
|
|
29563
|
-
subscription =
|
|
29564
|
-
output =
|
|
29968
|
+
endpoint = Option52.String("--endpoint");
|
|
29969
|
+
consecutive = Option52.String("--consecutive", "3");
|
|
29970
|
+
attempts = Option52.String("--attempts", "60");
|
|
29971
|
+
interval = Option52.String("--interval", "5");
|
|
29972
|
+
subscription = Option52.String("--subscription");
|
|
29973
|
+
output = Option52.String("--output");
|
|
29565
29974
|
async executeCommand() {
|
|
29566
29975
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
29567
29976
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -29572,7 +29981,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
29572
29981
|
const attempts = typeof this.attempts === "string" ? Number(this.attempts) : 60;
|
|
29573
29982
|
const intervalMs = (typeof this.interval === "string" ? Number(this.interval) : 5) * 1e3;
|
|
29574
29983
|
await getAzAccount();
|
|
29575
|
-
const credential2 = new
|
|
29984
|
+
const credential2 = new AzureCliCredential3();
|
|
29576
29985
|
const probe = makeAgentsProbe(credential2, endpoint);
|
|
29577
29986
|
const onProgress = mode === "pretty" ? (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
29578
29987
|
`) : void 0;
|
|
@@ -29595,7 +30004,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
29595
30004
|
};
|
|
29596
30005
|
|
|
29597
30006
|
// src/commands/bootstrap/preflight.ts
|
|
29598
|
-
import { Command as
|
|
30007
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
29599
30008
|
|
|
29600
30009
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
29601
30010
|
var EVENT_TIERS = {
|
|
@@ -29647,7 +30056,7 @@ function preflightRenderable(results) {
|
|
|
29647
30056
|
}
|
|
29648
30057
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
29649
30058
|
static paths = [["bootstrap", "preflight"]];
|
|
29650
|
-
static usage =
|
|
30059
|
+
static usage = Command56.Usage({
|
|
29651
30060
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
29652
30061
|
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
30062
|
examples: [
|
|
@@ -29656,9 +30065,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
29656
30065
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
29657
30066
|
]
|
|
29658
30067
|
});
|
|
29659
|
-
clientId =
|
|
29660
|
-
subscription =
|
|
29661
|
-
location =
|
|
30068
|
+
clientId = Option53.String("--client-id");
|
|
30069
|
+
subscription = Option53.String("--subscription");
|
|
30070
|
+
location = Option53.String("--location", {
|
|
29662
30071
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
29663
30072
|
});
|
|
29664
30073
|
async executeCommand() {
|
|
@@ -29758,7 +30167,7 @@ ${colors.error(" " + why)}
|
|
|
29758
30167
|
import * as fs35 from "fs";
|
|
29759
30168
|
import * as os15 from "os";
|
|
29760
30169
|
import * as path38 from "path";
|
|
29761
|
-
import { Command as
|
|
30170
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
29762
30171
|
init_errors();
|
|
29763
30172
|
|
|
29764
30173
|
// src/lib/bootstrap-mi.ts
|
|
@@ -29913,7 +30322,7 @@ async function kickInstaller(s) {
|
|
|
29913
30322
|
}
|
|
29914
30323
|
|
|
29915
30324
|
// src/lib/bootstrap-status.ts
|
|
29916
|
-
import { createHash as
|
|
30325
|
+
import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
|
|
29917
30326
|
import * as fs33 from "fs/promises";
|
|
29918
30327
|
import * as os13 from "os";
|
|
29919
30328
|
import * as path36 from "path";
|
|
@@ -29921,7 +30330,7 @@ init_errors();
|
|
|
29921
30330
|
var STATUS_CONTAINER = "status";
|
|
29922
30331
|
var STATUS_BLOB = "status.json";
|
|
29923
30332
|
function deriveStatusSaName(resourceGroup, subscriptionId) {
|
|
29924
|
-
const hex =
|
|
30333
|
+
const hex = createHash6("sha256").update(resourceGroup + subscriptionId).digest("hex").slice(0, 12);
|
|
29925
30334
|
return `m8tinst${hex}`;
|
|
29926
30335
|
}
|
|
29927
30336
|
function statusBlobUrl(saName) {
|
|
@@ -30128,7 +30537,7 @@ var ACI_NAME = "m8t-installer";
|
|
|
30128
30537
|
var MI_NAME = "m8t-installer-mi";
|
|
30129
30538
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
30130
30539
|
static paths = [["bootstrap", "launch"]];
|
|
30131
|
-
static usage =
|
|
30540
|
+
static usage = Command57.Usage({
|
|
30132
30541
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
30133
30542
|
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
30543
|
examples: [
|
|
@@ -30139,26 +30548,26 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30139
30548
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
30140
30549
|
]
|
|
30141
30550
|
});
|
|
30142
|
-
location =
|
|
30143
|
-
resourceGroup =
|
|
30144
|
-
clientId =
|
|
30145
|
-
subscription =
|
|
30146
|
-
installerTag =
|
|
30551
|
+
location = Option54.String("--location");
|
|
30552
|
+
resourceGroup = Option54.String("--resource-group");
|
|
30553
|
+
clientId = Option54.String("--client-id");
|
|
30554
|
+
subscription = Option54.String("--subscription");
|
|
30555
|
+
installerTag = Option54.String("--installer-tag");
|
|
30147
30556
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
30148
30557
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
30149
30558
|
// Wins over --installer-tag / the pinned default.
|
|
30150
|
-
installerImage =
|
|
30151
|
-
gatewayImageRef =
|
|
30152
|
-
githubAppCreds =
|
|
30153
|
-
contactEmail =
|
|
30154
|
-
company =
|
|
30559
|
+
installerImage = Option54.String("--installer-image");
|
|
30560
|
+
gatewayImageRef = Option54.String("--gateway-image-ref");
|
|
30561
|
+
githubAppCreds = Option54.String("--github-app-creds");
|
|
30562
|
+
contactEmail = Option54.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
30563
|
+
company = Option54.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
30155
30564
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
30156
30565
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
30157
30566
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
30158
30567
|
// the target; --resource-group is what CHOOSES it.
|
|
30159
|
-
reinstallInto =
|
|
30160
|
-
org =
|
|
30161
|
-
noBrains =
|
|
30568
|
+
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." });
|
|
30569
|
+
org = Option54.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
|
|
30570
|
+
noBrains = Option54.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
|
|
30162
30571
|
async executeCommand() {
|
|
30163
30572
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
30164
30573
|
if (!location) {
|
|
@@ -30319,7 +30728,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30319
30728
|
};
|
|
30320
30729
|
|
|
30321
30730
|
// src/commands/bootstrap/status.ts
|
|
30322
|
-
import { Command as
|
|
30731
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
30323
30732
|
init_errors();
|
|
30324
30733
|
|
|
30325
30734
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -30405,7 +30814,7 @@ var ONBOARDING_BLOCK_KEYS = [
|
|
|
30405
30814
|
"advisor_name",
|
|
30406
30815
|
"advisor_email"
|
|
30407
30816
|
];
|
|
30408
|
-
function
|
|
30817
|
+
function isRecord2(value) {
|
|
30409
30818
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30410
30819
|
}
|
|
30411
30820
|
function hasValidUniqueJsonKeys(source) {
|
|
@@ -30513,7 +30922,7 @@ var V3_COMPANY_KEYS = ["name", "one_liner"];
|
|
|
30513
30922
|
var V3_ADDRESS_KEYS = ["address", "city", "postal_code", "country"];
|
|
30514
30923
|
var V3_REQUEST_KEYS = ["type", "model", "region", "consent", "company_address"];
|
|
30515
30924
|
function exactStringRecord(value, keys) {
|
|
30516
|
-
if (!
|
|
30925
|
+
if (!isRecord2(value)) return "non-string-value";
|
|
30517
30926
|
const present = new Set(Object.keys(value));
|
|
30518
30927
|
for (const key2 of keys) if (!present.has(key2)) return "missing-key";
|
|
30519
30928
|
if (present.size !== keys.length) return "unexpected-key";
|
|
@@ -30525,7 +30934,7 @@ function canonicalPendingRequests(value) {
|
|
|
30525
30934
|
if (value.length > 1) return { ok: false, reason: "too-many-pending-requests" };
|
|
30526
30935
|
const requests = [];
|
|
30527
30936
|
for (const entry of value) {
|
|
30528
|
-
if (!
|
|
30937
|
+
if (!isRecord2(entry)) return { ok: false, reason: "non-string-value" };
|
|
30529
30938
|
const present = new Set(Object.keys(entry));
|
|
30530
30939
|
for (const key2 of V3_REQUEST_KEYS) {
|
|
30531
30940
|
if (!present.has(key2)) return { ok: false, reason: "missing-key" };
|
|
@@ -30575,7 +30984,7 @@ function canonicalV3Block(value) {
|
|
|
30575
30984
|
};
|
|
30576
30985
|
}
|
|
30577
30986
|
function canonicalBlock(value) {
|
|
30578
|
-
if (!
|
|
30987
|
+
if (!isRecord2(value)) return { ok: false, reason: "non-string-value" };
|
|
30579
30988
|
if (value.schema_version === "3") return canonicalV3Block(value);
|
|
30580
30989
|
if (value.schema_version !== "2") return { ok: false, reason: "unknown-schema-version" };
|
|
30581
30990
|
const keys = new Set(Object.keys(value));
|
|
@@ -30620,7 +31029,7 @@ function parseOnboardingArtifactResult(machineText) {
|
|
|
30620
31029
|
if (json.includes("m8t_onboarding")) return { ok: false, reason: "malformed-json" };
|
|
30621
31030
|
continue;
|
|
30622
31031
|
}
|
|
30623
|
-
if (!
|
|
31032
|
+
if (!isRecord2(parsed) || !Object.hasOwn(parsed, "m8t_onboarding")) continue;
|
|
30624
31033
|
if (Object.keys(parsed).length !== 1) return { ok: false, reason: "unexpected-key" };
|
|
30625
31034
|
const outcome = canonicalBlock(parsed.m8t_onboarding);
|
|
30626
31035
|
if (!outcome.ok) return outcome;
|
|
@@ -30666,7 +31075,7 @@ async function readCursorPages(args) {
|
|
|
30666
31075
|
} catch {
|
|
30667
31076
|
return null;
|
|
30668
31077
|
}
|
|
30669
|
-
if (!
|
|
31078
|
+
if (!isRecord2(body) || !Array.isArray(body.data)) return null;
|
|
30670
31079
|
const page = body;
|
|
30671
31080
|
all.push(...page.data);
|
|
30672
31081
|
if (page.has_more !== true) {
|
|
@@ -30687,7 +31096,7 @@ function decodeFoundryUser(token) {
|
|
|
30687
31096
|
if (parts.length !== 3 || parts.some((part) => part.length === 0) || !/^[A-Za-z0-9_-]+$/.test(parts[1] ?? "")) return null;
|
|
30688
31097
|
try {
|
|
30689
31098
|
const payload = JSON.parse(Buffer.from(parts[1] ?? "", "base64url").toString("utf8"));
|
|
30690
|
-
if (!
|
|
31099
|
+
if (!isRecord2(payload)) return null;
|
|
30691
31100
|
const claim = payload.upn ?? payload.preferred_username ?? payload.email;
|
|
30692
31101
|
return typeof claim === "string" && claim.trim().length > 0 ? claim : null;
|
|
30693
31102
|
} catch {
|
|
@@ -30718,7 +31127,7 @@ async function findOnboardingProfile(args) {
|
|
|
30718
31127
|
});
|
|
30719
31128
|
if (!listed) return { ...EMPTY_PROFILE_RESULT };
|
|
30720
31129
|
const conversations = orderNewest(listed.flatMap((value, ordinal) => {
|
|
30721
|
-
if (!
|
|
31130
|
+
if (!isRecord2(value) || typeof value.id !== "string" || value.id.length === 0 || !isRecord2(value.metadata)) return [];
|
|
30722
31131
|
const metadata = value.metadata;
|
|
30723
31132
|
if (metadata.app !== "m8t-webapp" || metadata.agent !== INTAKE_AGENT_NAME) return [];
|
|
30724
31133
|
return [{
|
|
@@ -30736,7 +31145,7 @@ async function findOnboardingProfile(args) {
|
|
|
30736
31145
|
});
|
|
30737
31146
|
if (!items) return { hadIntake: true, block: null, machineText: null, speechText: null, rejection: null };
|
|
30738
31147
|
const assistantItems = orderNewest(items.flatMap((value, ordinal) => {
|
|
30739
|
-
if (!
|
|
31148
|
+
if (!isRecord2(value) || value.type !== "message" || value.role !== "assistant" || !Array.isArray(value.content)) return [];
|
|
30740
31149
|
const id = typeof value.id === "string" ? value.id : "";
|
|
30741
31150
|
return [{ value, id, createdAt: timestamp(value.created_at), ordinal }];
|
|
30742
31151
|
}));
|
|
@@ -30745,7 +31154,7 @@ async function findOnboardingProfile(args) {
|
|
|
30745
31154
|
for (const item of assistantItems) {
|
|
30746
31155
|
const content = item.value.content;
|
|
30747
31156
|
const machineText = content.flatMap((part) => {
|
|
30748
|
-
if (!
|
|
31157
|
+
if (!isRecord2(part)) return [];
|
|
30749
31158
|
if (typeof part.text === "string" && part.text.length > 0) return [part.text];
|
|
30750
31159
|
if (typeof part.transcript === "string") return [part.transcript];
|
|
30751
31160
|
return [];
|
|
@@ -31323,7 +31732,7 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
31323
31732
|
import { execFile, spawn as spawn7 } from "child_process";
|
|
31324
31733
|
|
|
31325
31734
|
// src/lib/companion-artifact.ts
|
|
31326
|
-
import { createHash as
|
|
31735
|
+
import { createHash as createHash7 } from "crypto";
|
|
31327
31736
|
import { constants } from "fs";
|
|
31328
31737
|
import * as fs36 from "fs/promises";
|
|
31329
31738
|
import * as path40 from "path";
|
|
@@ -31361,7 +31770,7 @@ function canonicalEntry(entry) {
|
|
|
31361
31770
|
`;
|
|
31362
31771
|
}
|
|
31363
31772
|
function artifactTreeSha256(entries) {
|
|
31364
|
-
const hash =
|
|
31773
|
+
const hash = createHash7("sha256");
|
|
31365
31774
|
const ordered = [...entries].sort(
|
|
31366
31775
|
(left, right) => left.path.localeCompare(right.path)
|
|
31367
31776
|
);
|
|
@@ -31431,7 +31840,7 @@ function parseArtifactManifest(value) {
|
|
|
31431
31840
|
};
|
|
31432
31841
|
}
|
|
31433
31842
|
async function sha256File2(filePath) {
|
|
31434
|
-
return
|
|
31843
|
+
return createHash7("sha256").update(await fs36.readFile(filePath)).digest("hex");
|
|
31435
31844
|
}
|
|
31436
31845
|
async function walk2(root, relative4 = "") {
|
|
31437
31846
|
const directory = path40.join(root, ...relative4.split("/").filter(Boolean));
|
|
@@ -32247,7 +32656,7 @@ async function uninstallCompanion(options) {
|
|
|
32247
32656
|
// src/commands/companion/install.ts
|
|
32248
32657
|
import * as os18 from "os";
|
|
32249
32658
|
import * as path43 from "path";
|
|
32250
|
-
import { Command as
|
|
32659
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
32251
32660
|
|
|
32252
32661
|
// src/lib/companion-channel.ts
|
|
32253
32662
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -32262,7 +32671,7 @@ async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps =
|
|
|
32262
32671
|
}
|
|
32263
32672
|
|
|
32264
32673
|
// src/lib/companion-download.ts
|
|
32265
|
-
import { createHash as
|
|
32674
|
+
import { createHash as createHash8 } from "crypto";
|
|
32266
32675
|
import { execFile as execFile2 } from "child_process";
|
|
32267
32676
|
import * as fs38 from "fs/promises";
|
|
32268
32677
|
import * as os17 from "os";
|
|
@@ -32311,7 +32720,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
32311
32720
|
message: `${pinned.asset} is larger than this command will accept.`
|
|
32312
32721
|
});
|
|
32313
32722
|
}
|
|
32314
|
-
const digest =
|
|
32723
|
+
const digest = createHash8("sha256").update(bytes).digest("hex");
|
|
32315
32724
|
if (digest !== pinned.sha256) {
|
|
32316
32725
|
throw new LocalCliError({
|
|
32317
32726
|
code: "COMPANION_ASSET_DIGEST_MISMATCH",
|
|
@@ -32405,7 +32814,7 @@ Version: ${state.version}
|
|
|
32405
32814
|
}
|
|
32406
32815
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
32407
32816
|
static paths = [["companion", "install"]];
|
|
32408
|
-
static usage =
|
|
32817
|
+
static usage = Command58.Usage({
|
|
32409
32818
|
description: "Install the desktop companions for this user from the release channel.",
|
|
32410
32819
|
examples: [
|
|
32411
32820
|
["Install the released build", "$0 companion install"],
|
|
@@ -32415,10 +32824,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
32415
32824
|
]
|
|
32416
32825
|
]
|
|
32417
32826
|
});
|
|
32418
|
-
from =
|
|
32827
|
+
from = Option55.String("--from", {
|
|
32419
32828
|
description: "A locally staged build directory instead of the released one."
|
|
32420
32829
|
});
|
|
32421
|
-
resourceGroup =
|
|
32830
|
+
resourceGroup = Option55.String("--resource-group", {
|
|
32422
32831
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
32423
32832
|
});
|
|
32424
32833
|
async executeCommand() {
|
|
@@ -32446,7 +32855,7 @@ async function looksLikeCheckout(dir) {
|
|
|
32446
32855
|
return false;
|
|
32447
32856
|
}
|
|
32448
32857
|
}
|
|
32449
|
-
var
|
|
32858
|
+
var defaultDeps3 = {
|
|
32450
32859
|
discoverGateway,
|
|
32451
32860
|
writeConfig,
|
|
32452
32861
|
ensureGatewayRedirectUri,
|
|
@@ -32459,7 +32868,7 @@ var defaultDeps2 = {
|
|
|
32459
32868
|
}),
|
|
32460
32869
|
homedir: () => os19.homedir()
|
|
32461
32870
|
};
|
|
32462
|
-
async function finalizeInstall(args, deps =
|
|
32871
|
+
async function finalizeInstall(args, deps = defaultDeps3) {
|
|
32463
32872
|
const markerDir = path44.join(deps.homedir(), ".m8t");
|
|
32464
32873
|
const markerPath = path44.join(markerDir, "repo-root");
|
|
32465
32874
|
const cwd = process.cwd();
|
|
@@ -32634,7 +33043,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
32634
33043
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
32635
33044
|
// through — it prints a deprecation notice and does the right thing.
|
|
32636
33045
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
32637
|
-
static usage =
|
|
33046
|
+
static usage = Command59.Usage({
|
|
32638
33047
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
32639
33048
|
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
33049
|
examples: [
|
|
@@ -32643,12 +33052,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
32643
33052
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
32644
33053
|
]
|
|
32645
33054
|
});
|
|
32646
|
-
watch =
|
|
32647
|
-
output =
|
|
32648
|
-
repoRoot =
|
|
32649
|
-
finalize =
|
|
32650
|
-
subscription =
|
|
32651
|
-
resourceGroup =
|
|
33055
|
+
watch = Option56.Boolean("--watch", false);
|
|
33056
|
+
output = Option56.String("--output");
|
|
33057
|
+
repoRoot = Option56.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
|
|
33058
|
+
finalize = Option56.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
|
|
33059
|
+
subscription = Option56.String("--subscription");
|
|
33060
|
+
resourceGroup = Option56.String("--resource-group");
|
|
32652
33061
|
async executeCommand() {
|
|
32653
33062
|
const state = await readBootstrapState();
|
|
32654
33063
|
if (!state) {
|
|
@@ -32781,7 +33190,7 @@ function formatStatus(d) {
|
|
|
32781
33190
|
}
|
|
32782
33191
|
|
|
32783
33192
|
// src/commands/bootstrap/reap.ts
|
|
32784
|
-
import { Command as
|
|
33193
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
32785
33194
|
init_errors();
|
|
32786
33195
|
|
|
32787
33196
|
// src/lib/bootstrap-reap.ts
|
|
@@ -32875,7 +33284,7 @@ async function reapInstaller(opts) {
|
|
|
32875
33284
|
// src/commands/bootstrap/reap.ts
|
|
32876
33285
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
32877
33286
|
static paths = [["bootstrap", "reap"]];
|
|
32878
|
-
static usage =
|
|
33287
|
+
static usage = Command60.Usage({
|
|
32879
33288
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
32880
33289
|
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
33290
|
examples: [
|
|
@@ -32885,9 +33294,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
32885
33294
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
32886
33295
|
]
|
|
32887
33296
|
});
|
|
32888
|
-
force =
|
|
32889
|
-
sweepOrphans =
|
|
32890
|
-
yes =
|
|
33297
|
+
force = Option57.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
|
|
33298
|
+
sweepOrphans = Option57.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
|
|
33299
|
+
yes = Option57.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
|
|
32891
33300
|
async executeCommand() {
|
|
32892
33301
|
if (this.sweepOrphans === true) {
|
|
32893
33302
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -32984,7 +33393,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
32984
33393
|
import * as fs41 from "fs";
|
|
32985
33394
|
import * as os21 from "os";
|
|
32986
33395
|
import * as path46 from "path";
|
|
32987
|
-
import { Command as
|
|
33396
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
32988
33397
|
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
32989
33398
|
init_errors();
|
|
32990
33399
|
|
|
@@ -33812,7 +34221,7 @@ function renderDeployFailure(error) {
|
|
|
33812
34221
|
}
|
|
33813
34222
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
33814
34223
|
static paths = [["bootstrap", "ui"]];
|
|
33815
|
-
static usage =
|
|
34224
|
+
static usage = Command61.Usage({
|
|
33816
34225
|
description: "Deploy Ezra + start the local onboarding chat UI in the background (returns immediately).",
|
|
33817
34226
|
details: [
|
|
33818
34227
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -33833,16 +34242,16 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
33833
34242
|
["Experimental: start the voice relay (no effect on the text-only intake)", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
|
|
33834
34243
|
]
|
|
33835
34244
|
});
|
|
33836
|
-
repoRoot =
|
|
33837
|
-
port =
|
|
33838
|
-
endpoint =
|
|
34245
|
+
repoRoot = Option58.String("--repo-root");
|
|
34246
|
+
port = Option58.String("--port", "3000");
|
|
34247
|
+
endpoint = Option58.String("--endpoint", {
|
|
33839
34248
|
description: "Foundry project endpoint to target \u2014 overrides discovery when this install's resource group holds more than one Foundry project."
|
|
33840
34249
|
});
|
|
33841
|
-
prepOnly =
|
|
33842
|
-
skipInstall =
|
|
33843
|
-
stop =
|
|
33844
|
-
foreground =
|
|
33845
|
-
voice =
|
|
34250
|
+
prepOnly = Option58.Boolean("--prep-only", false);
|
|
34251
|
+
skipInstall = Option58.Boolean("--skip-install", false);
|
|
34252
|
+
stop = Option58.Boolean("--stop", false);
|
|
34253
|
+
foreground = Option58.Boolean("--foreground", false);
|
|
34254
|
+
voice = Option58.Boolean("--voice", false, {
|
|
33846
34255
|
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
34256
|
});
|
|
33848
34257
|
async executeCommand() {
|
|
@@ -34014,10 +34423,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
34014
34423
|
};
|
|
34015
34424
|
|
|
34016
34425
|
// src/commands/bootstrap/seed-profile.ts
|
|
34017
|
-
import { Command as
|
|
34426
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
34018
34427
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
34019
34428
|
static paths = [["bootstrap", "seed-profile"]];
|
|
34020
|
-
static usage =
|
|
34429
|
+
static usage = Command62.Usage({
|
|
34021
34430
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding intake.",
|
|
34022
34431
|
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
34432
|
examples: [
|
|
@@ -34025,11 +34434,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34025
34434
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
34026
34435
|
]
|
|
34027
34436
|
});
|
|
34028
|
-
endpoint =
|
|
34029
|
-
brain =
|
|
34030
|
-
watch =
|
|
34031
|
-
timeout =
|
|
34032
|
-
githubAppCreds =
|
|
34437
|
+
endpoint = Option59.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
34438
|
+
brain = Option59.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
34439
|
+
watch = Option59.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
34440
|
+
timeout = Option59.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
34441
|
+
githubAppCreds = Option59.String("--github-app-creds");
|
|
34033
34442
|
async executeCommand() {
|
|
34034
34443
|
const ctx = await resolveSeedContext({
|
|
34035
34444
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -34092,7 +34501,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34092
34501
|
import * as fs42 from "fs";
|
|
34093
34502
|
import * as os22 from "os";
|
|
34094
34503
|
import * as path47 from "path";
|
|
34095
|
-
import { Command as
|
|
34504
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34096
34505
|
init_errors();
|
|
34097
34506
|
|
|
34098
34507
|
// src/lib/telemetry-enroll.ts
|
|
@@ -34174,7 +34583,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
34174
34583
|
}
|
|
34175
34584
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
34176
34585
|
static paths = [["telemetry", "enroll"]];
|
|
34177
|
-
static usage =
|
|
34586
|
+
static usage = Command63.Usage({
|
|
34178
34587
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
34179
34588
|
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
34589
|
examples: [
|
|
@@ -34182,11 +34591,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34182
34591
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
34183
34592
|
]
|
|
34184
34593
|
});
|
|
34185
|
-
company =
|
|
34186
|
-
contactEmail =
|
|
34187
|
-
subscription =
|
|
34188
|
-
resourceGroup =
|
|
34189
|
-
force =
|
|
34594
|
+
company = Option60.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
34595
|
+
contactEmail = Option60.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
34596
|
+
subscription = Option60.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
34597
|
+
resourceGroup = Option60.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
34598
|
+
force = Option60.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
34190
34599
|
async executeCommand() {
|
|
34191
34600
|
const account = await getAzAccount();
|
|
34192
34601
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -34236,7 +34645,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34236
34645
|
};
|
|
34237
34646
|
|
|
34238
34647
|
// src/commands/companion/bridge.ts
|
|
34239
|
-
import { Command as
|
|
34648
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
34240
34649
|
|
|
34241
34650
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
34242
34651
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -34248,6 +34657,14 @@ var COMPANION_HISTORY_LIMIT_MAX = 40;
|
|
|
34248
34657
|
var COMPANION_TURN_TEXT_MAX_CODE_POINTS = 32768;
|
|
34249
34658
|
var COMPANION_REPLY_DELTA_MAX_CODE_POINTS = 4096;
|
|
34250
34659
|
var COMPANION_TURN_ID_MAX_LENGTH = 256;
|
|
34660
|
+
var COMPANION_DECISION_CALL_ID_MAX_LENGTH = 256;
|
|
34661
|
+
var COMPANION_DECISION_TITLE_MAX_LENGTH = 200;
|
|
34662
|
+
var COMPANION_DECISION_LABEL_MAX_LENGTH = 100;
|
|
34663
|
+
var COMPANION_DECISION_DETAIL_MAX_CODE_POINTS = 400;
|
|
34664
|
+
var COMPANION_DECISION_OPTIONS_MIN = 2;
|
|
34665
|
+
var COMPANION_DECISION_OPTIONS_MAX = 4;
|
|
34666
|
+
var COMPANION_ARTIFACT_NAME_MAX_LENGTH = 200;
|
|
34667
|
+
var COMPANION_ARTIFACTS_MAX = 16;
|
|
34251
34668
|
var PERSONAS = /* @__PURE__ */ new Set([
|
|
34252
34669
|
"startup-advisor",
|
|
34253
34670
|
"ezra"
|
|
@@ -34257,7 +34674,8 @@ var FAILURE_REASONS = /* @__PURE__ */ new Set([
|
|
|
34257
34674
|
"not-authorized",
|
|
34258
34675
|
"mate-unavailable",
|
|
34259
34676
|
"busy",
|
|
34260
|
-
"bad-request"
|
|
34677
|
+
"bad-request",
|
|
34678
|
+
"decision-not-pending"
|
|
34261
34679
|
]);
|
|
34262
34680
|
var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/u;
|
|
34263
34681
|
var URL_SAFE_OPAQUE_ID = /^[A-Za-z0-9_-]+$/u;
|
|
@@ -34266,7 +34684,7 @@ var VERSION3 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/u;
|
|
|
34266
34684
|
function isVersionOrNull(value) {
|
|
34267
34685
|
return value === null || typeof value === "string" && VERSION3.test(value);
|
|
34268
34686
|
}
|
|
34269
|
-
function
|
|
34687
|
+
function isRecord3(value) {
|
|
34270
34688
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34271
34689
|
}
|
|
34272
34690
|
function hasExactKeys(value, keys) {
|
|
@@ -34290,8 +34708,72 @@ var INSTANT_SHAPE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
|
|
|
34290
34708
|
function isInstantOrNull(value) {
|
|
34291
34709
|
return value === null || typeof value === "string" && INSTANT_SHAPE.test(value) && Number.isFinite(Date.parse(value));
|
|
34292
34710
|
}
|
|
34293
|
-
function
|
|
34294
|
-
|
|
34711
|
+
function parseDecision(value) {
|
|
34712
|
+
if (!isRecord3(value) || !isBoundedPlainString(value.callId, COMPANION_DECISION_CALL_ID_MAX_LENGTH) || !isBoundedPlainString(value.title, COMPANION_DECISION_TITLE_MAX_LENGTH) || !Array.isArray(value.options) || value.options.length < COMPANION_DECISION_OPTIONS_MIN || value.options.length > COMPANION_DECISION_OPTIONS_MAX) {
|
|
34713
|
+
return eventError();
|
|
34714
|
+
}
|
|
34715
|
+
const options = value.options.map((option) => {
|
|
34716
|
+
if (!isRecord3(option) || !hasExactKeys(option, ["label", "detail"]) || !isBoundedPlainString(option.label, COMPANION_DECISION_LABEL_MAX_LENGTH) || !isBoundedMessageText(option.detail, COMPANION_DECISION_DETAIL_MAX_CODE_POINTS)) {
|
|
34717
|
+
return eventError();
|
|
34718
|
+
}
|
|
34719
|
+
return { label: option.label, detail: option.detail };
|
|
34720
|
+
});
|
|
34721
|
+
const base = { callId: value.callId, title: value.title, options };
|
|
34722
|
+
if (value.status === "pending" || value.status === "dismissed") {
|
|
34723
|
+
if (!hasExactKeys(value, ["callId", "title", "options", "status"])) {
|
|
34724
|
+
return eventError();
|
|
34725
|
+
}
|
|
34726
|
+
return { ...base, status: value.status };
|
|
34727
|
+
}
|
|
34728
|
+
if (value.status !== "selected" || !hasExactKeys(value, ["callId", "title", "options", "status", "optionIndex"]) || !Number.isSafeInteger(value.optionIndex) || value.optionIndex < 0 || value.optionIndex >= options.length) {
|
|
34729
|
+
return eventError();
|
|
34730
|
+
}
|
|
34731
|
+
return { ...base, status: "selected", optionIndex: value.optionIndex };
|
|
34732
|
+
}
|
|
34733
|
+
function parseArtifact(value) {
|
|
34734
|
+
if (!isRecord3(value) || !isBoundedPlainString(value.name, COMPANION_ARTIFACT_NAME_MAX_LENGTH)) {
|
|
34735
|
+
return eventError();
|
|
34736
|
+
}
|
|
34737
|
+
if (value.sizeBytes === void 0) {
|
|
34738
|
+
if (!hasExactKeys(value, ["name"])) return eventError();
|
|
34739
|
+
return { name: value.name };
|
|
34740
|
+
}
|
|
34741
|
+
if (!hasExactKeys(value, ["name", "sizeBytes"]) || !Number.isSafeInteger(value.sizeBytes) || value.sizeBytes < 0) {
|
|
34742
|
+
return eventError();
|
|
34743
|
+
}
|
|
34744
|
+
return { name: value.name, sizeBytes: value.sizeBytes };
|
|
34745
|
+
}
|
|
34746
|
+
function parseTurnArtifacts(value) {
|
|
34747
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > COMPANION_ARTIFACTS_MAX) {
|
|
34748
|
+
return eventError();
|
|
34749
|
+
}
|
|
34750
|
+
return value.map(parseArtifact);
|
|
34751
|
+
}
|
|
34752
|
+
function parseTurn(value) {
|
|
34753
|
+
if (!isRecord3(value) || !isBoundedPlainString(value.id, COMPANION_TURN_ID_MAX_LENGTH) || value.role !== "user" && value.role !== "mate" || !isInstantOrNull(value.at)) {
|
|
34754
|
+
return eventError();
|
|
34755
|
+
}
|
|
34756
|
+
const keys = [
|
|
34757
|
+
"id",
|
|
34758
|
+
"role",
|
|
34759
|
+
"text",
|
|
34760
|
+
"at",
|
|
34761
|
+
...value.decision === void 0 ? [] : ["decision"],
|
|
34762
|
+
...value.artifacts === void 0 ? [] : ["artifacts"]
|
|
34763
|
+
];
|
|
34764
|
+
if (!hasExactKeys(value, keys)) return eventError();
|
|
34765
|
+
const allowEmptyText = value.decision !== void 0 || value.artifacts !== void 0;
|
|
34766
|
+
if (!(allowEmptyText && value.text === "" || isBoundedMessageText(value.text, COMPANION_TURN_TEXT_MAX_CODE_POINTS))) {
|
|
34767
|
+
return eventError();
|
|
34768
|
+
}
|
|
34769
|
+
return {
|
|
34770
|
+
id: value.id,
|
|
34771
|
+
role: value.role,
|
|
34772
|
+
text: value.text,
|
|
34773
|
+
at: value.at,
|
|
34774
|
+
...value.decision === void 0 ? {} : { decision: parseDecision(value.decision) },
|
|
34775
|
+
...value.artifacts === void 0 ? {} : { artifacts: parseTurnArtifacts(value.artifacts) }
|
|
34776
|
+
};
|
|
34295
34777
|
}
|
|
34296
34778
|
function isCanonicalInstant(value) {
|
|
34297
34779
|
if (typeof value !== "string") return false;
|
|
@@ -34347,7 +34829,7 @@ function isValidMateRoute(value) {
|
|
|
34347
34829
|
}
|
|
34348
34830
|
}
|
|
34349
34831
|
function parseRequest(value) {
|
|
34350
|
-
if (!
|
|
34832
|
+
if (!isRecord3(value)) return requestError();
|
|
34351
34833
|
if (value.type === "roster") {
|
|
34352
34834
|
if (!hasExactKeys(value, ["type"])) return requestError();
|
|
34353
34835
|
return { type: "roster" };
|
|
@@ -34356,18 +34838,36 @@ function parseRequest(value) {
|
|
|
34356
34838
|
if (!hasExactKeys(value, ["type"])) return requestError();
|
|
34357
34839
|
return { type: "update" };
|
|
34358
34840
|
}
|
|
34359
|
-
if (value.type === "history") {
|
|
34841
|
+
if (value.type === "history" || value.type === "recall") {
|
|
34360
34842
|
if (!hasExactKeys(value, ["type", "requestId", "personaKey", "limit"]) || !isRequestId(value.requestId) || !isPersonaKey(value.personaKey) || !Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > COMPANION_HISTORY_LIMIT_MAX) {
|
|
34361
34843
|
return requestError();
|
|
34362
34844
|
}
|
|
34363
34845
|
return {
|
|
34364
|
-
type:
|
|
34846
|
+
type: value.type,
|
|
34365
34847
|
requestId: value.requestId,
|
|
34366
34848
|
personaKey: value.personaKey,
|
|
34367
34849
|
limit: value.limit
|
|
34368
34850
|
};
|
|
34369
34851
|
}
|
|
34370
|
-
if (value.type
|
|
34852
|
+
if (value.type === "decide") {
|
|
34853
|
+
if (!hasExactKeys(value, [
|
|
34854
|
+
"type",
|
|
34855
|
+
"requestId",
|
|
34856
|
+
"personaKey",
|
|
34857
|
+
"callId",
|
|
34858
|
+
"optionIndex"
|
|
34859
|
+
]) || !isRequestId(value.requestId) || !isPersonaKey(value.personaKey) || !isBoundedPlainString(value.callId, COMPANION_DECISION_CALL_ID_MAX_LENGTH) || !Number.isSafeInteger(value.optionIndex) || value.optionIndex < 0 || value.optionIndex >= COMPANION_DECISION_OPTIONS_MAX) {
|
|
34860
|
+
return requestError();
|
|
34861
|
+
}
|
|
34862
|
+
return {
|
|
34863
|
+
type: "decide",
|
|
34864
|
+
requestId: value.requestId,
|
|
34865
|
+
personaKey: value.personaKey,
|
|
34866
|
+
callId: value.callId,
|
|
34867
|
+
optionIndex: value.optionIndex
|
|
34868
|
+
};
|
|
34869
|
+
}
|
|
34870
|
+
if (value.type !== "send" && value.type !== "converse" && value.type !== "chat" || !hasExactKeys(value, [
|
|
34371
34871
|
"type",
|
|
34372
34872
|
"requestId",
|
|
34373
34873
|
"personaKey",
|
|
@@ -34395,7 +34895,7 @@ function parseRequestLine(line2) {
|
|
|
34395
34895
|
}
|
|
34396
34896
|
}
|
|
34397
34897
|
function parseMate(value) {
|
|
34398
|
-
if (!
|
|
34898
|
+
if (!isRecord3(value) || !hasExactKeys(value, [
|
|
34399
34899
|
"personaKey",
|
|
34400
34900
|
"agentName",
|
|
34401
34901
|
"displayName",
|
|
@@ -34420,7 +34920,7 @@ function parseMate(value) {
|
|
|
34420
34920
|
};
|
|
34421
34921
|
}
|
|
34422
34922
|
function parseEvent(value) {
|
|
34423
|
-
if (!
|
|
34923
|
+
if (!isRecord3(value)) return eventError();
|
|
34424
34924
|
if (value.type === "update") {
|
|
34425
34925
|
if (!hasExactKeys(value, ["type", "installed", "available", "severity"]) || !isVersionOrNull(value.installed) || !isVersionOrNull(value.available) || !(value.severity === null || SEVERITIES2.has(value.severity))) {
|
|
34426
34926
|
return eventError();
|
|
@@ -34463,18 +34963,33 @@ function parseEvent(value) {
|
|
|
34463
34963
|
return { type: "completed", requestId: value.requestId };
|
|
34464
34964
|
}
|
|
34465
34965
|
if (value.type === "turn") {
|
|
34466
|
-
if (!hasExactKeys(value, ["type", "requestId", "turn"]) || !isRequestId(value.requestId)
|
|
34966
|
+
if (!hasExactKeys(value, ["type", "requestId", "turn"]) || !isRequestId(value.requestId)) {
|
|
34467
34967
|
return eventError();
|
|
34468
34968
|
}
|
|
34469
34969
|
return {
|
|
34470
34970
|
type: "turn",
|
|
34471
34971
|
requestId: value.requestId,
|
|
34472
|
-
turn:
|
|
34473
|
-
|
|
34474
|
-
|
|
34475
|
-
|
|
34476
|
-
|
|
34477
|
-
|
|
34972
|
+
turn: parseTurn(value.turn)
|
|
34973
|
+
};
|
|
34974
|
+
}
|
|
34975
|
+
if (value.type === "reply-decision") {
|
|
34976
|
+
if (!hasExactKeys(value, ["type", "requestId", "decision"]) || !isRequestId(value.requestId)) {
|
|
34977
|
+
return eventError();
|
|
34978
|
+
}
|
|
34979
|
+
return {
|
|
34980
|
+
type: "reply-decision",
|
|
34981
|
+
requestId: value.requestId,
|
|
34982
|
+
decision: parseDecision(value.decision)
|
|
34983
|
+
};
|
|
34984
|
+
}
|
|
34985
|
+
if (value.type === "reply-artifacts") {
|
|
34986
|
+
if (!hasExactKeys(value, ["type", "requestId", "artifacts"]) || !isRequestId(value.requestId) || !Array.isArray(value.artifacts) || value.artifacts.length === 0 || value.artifacts.length > COMPANION_ARTIFACTS_MAX) {
|
|
34987
|
+
return eventError();
|
|
34988
|
+
}
|
|
34989
|
+
return {
|
|
34990
|
+
type: "reply-artifacts",
|
|
34991
|
+
requestId: value.requestId,
|
|
34992
|
+
artifacts: value.artifacts.map(parseArtifact)
|
|
34478
34993
|
};
|
|
34479
34994
|
}
|
|
34480
34995
|
if (value.type === "history-end") {
|
|
@@ -34531,6 +35046,7 @@ function serializeEvent(value) {
|
|
|
34531
35046
|
}
|
|
34532
35047
|
|
|
34533
35048
|
// src/lib/companion-chat-client.ts
|
|
35049
|
+
init_esm();
|
|
34534
35050
|
import { stat as stat4 } from "fs/promises";
|
|
34535
35051
|
var RESPONSE_MAX_BYTES = 1e6;
|
|
34536
35052
|
var REQUEST_DEADLINE_MS = 12e4;
|
|
@@ -34543,7 +35059,7 @@ var PredispatchFailure = class extends Error {
|
|
|
34543
35059
|
}
|
|
34544
35060
|
reason;
|
|
34545
35061
|
};
|
|
34546
|
-
function
|
|
35062
|
+
function isRecord4(value) {
|
|
34547
35063
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34548
35064
|
}
|
|
34549
35065
|
function hasControlCharacter(value) {
|
|
@@ -34613,16 +35129,16 @@ async function readJsonEnvelope(response) {
|
|
|
34613
35129
|
if (error instanceof PredispatchFailure) throw error;
|
|
34614
35130
|
throw new PredispatchFailure("not-connected");
|
|
34615
35131
|
}
|
|
34616
|
-
if (!
|
|
35132
|
+
if (!isRecord4(envelope) || envelope.ok !== true || !("data" in envelope)) {
|
|
34617
35133
|
throw new PredispatchFailure("not-connected");
|
|
34618
35134
|
}
|
|
34619
35135
|
return envelope.data;
|
|
34620
35136
|
}
|
|
34621
35137
|
function decodeAgents(data) {
|
|
34622
|
-
if (!
|
|
35138
|
+
if (!isRecord4(data) || !Array.isArray(data.agents)) {
|
|
34623
35139
|
throw new PredispatchFailure("mate-unavailable");
|
|
34624
35140
|
}
|
|
34625
|
-
return data.agents.filter(
|
|
35141
|
+
return data.agents.filter(isRecord4);
|
|
34626
35142
|
}
|
|
34627
35143
|
function resolveAgentName(agents, personaKey) {
|
|
34628
35144
|
const matches = agents.filter(
|
|
@@ -34644,18 +35160,18 @@ function requireAgentName(agents, personaKey) {
|
|
|
34644
35160
|
return name;
|
|
34645
35161
|
}
|
|
34646
35162
|
function decodeConversationId(data) {
|
|
34647
|
-
if (!
|
|
35163
|
+
if (!isRecord4(data) || typeof data.id !== "string" || !OPAQUE_ID.test(data.id)) {
|
|
34648
35164
|
throw new PredispatchFailure("not-connected");
|
|
34649
35165
|
}
|
|
34650
35166
|
return data.id;
|
|
34651
35167
|
}
|
|
34652
35168
|
function decodeLastMessageId(data) {
|
|
34653
|
-
if (!
|
|
35169
|
+
if (!isRecord4(data) || !Array.isArray(data.messages)) {
|
|
34654
35170
|
throw new PredispatchFailure("not-connected");
|
|
34655
35171
|
}
|
|
34656
35172
|
const messages = data.messages;
|
|
34657
35173
|
const last = messages.at(-1);
|
|
34658
|
-
if (!
|
|
35174
|
+
if (!isRecord4(last) || typeof last.id !== "string" || !OPAQUE_ID.test(last.id)) {
|
|
34659
35175
|
return void 0;
|
|
34660
35176
|
}
|
|
34661
35177
|
return last.id;
|
|
@@ -34706,13 +35222,61 @@ function boundCodePoints(value, maxCodePoints) {
|
|
|
34706
35222
|
}
|
|
34707
35223
|
return value;
|
|
34708
35224
|
}
|
|
35225
|
+
function boundPlainLine(value, maxLength) {
|
|
35226
|
+
return value.replace(/[\u0000-\u001f\u007f]/gu, " ").trim().slice(0, maxLength);
|
|
35227
|
+
}
|
|
35228
|
+
var ARTIFACTS_PART_TYPE = "data-artifacts";
|
|
35229
|
+
function toCompanionDecision(directive) {
|
|
35230
|
+
const state = directive.state;
|
|
35231
|
+
if (state.status !== "pending" && state.status !== "selected" && state.status !== "dismissed") {
|
|
35232
|
+
return null;
|
|
35233
|
+
}
|
|
35234
|
+
const callId = boundPlainLine(
|
|
35235
|
+
directive.callId,
|
|
35236
|
+
COMPANION_DECISION_CALL_ID_MAX_LENGTH
|
|
35237
|
+
);
|
|
35238
|
+
const title = boundPlainLine(
|
|
35239
|
+
directive.args.title,
|
|
35240
|
+
COMPANION_DECISION_TITLE_MAX_LENGTH
|
|
35241
|
+
);
|
|
35242
|
+
if (callId.length === 0 || title.length === 0) return null;
|
|
35243
|
+
const options = [];
|
|
35244
|
+
for (const option of directive.args.options) {
|
|
35245
|
+
const label = boundPlainLine(option.label, COMPANION_DECISION_LABEL_MAX_LENGTH);
|
|
35246
|
+
const detail = boundCodePoints(
|
|
35247
|
+
sanitizeMessageText(option.detail).trim(),
|
|
35248
|
+
COMPANION_DECISION_DETAIL_MAX_CODE_POINTS
|
|
35249
|
+
);
|
|
35250
|
+
if (label.length === 0 || detail.length === 0) return null;
|
|
35251
|
+
options.push({ label, detail });
|
|
35252
|
+
}
|
|
35253
|
+
const base = { callId, title, options };
|
|
35254
|
+
return state.status === "selected" ? { ...base, status: "selected", optionIndex: state.optionIndex } : { ...base, status: state.status };
|
|
35255
|
+
}
|
|
35256
|
+
function toCompanionArtifacts(data) {
|
|
35257
|
+
const artifacts = [];
|
|
35258
|
+
for (const entry of data) {
|
|
35259
|
+
if (artifacts.length >= COMPANION_ARTIFACTS_MAX) break;
|
|
35260
|
+
if (!isRecord4(entry) || typeof entry.name !== "string") continue;
|
|
35261
|
+
const name = boundPlainLine(entry.name, COMPANION_ARTIFACT_NAME_MAX_LENGTH);
|
|
35262
|
+
if (name.length === 0) continue;
|
|
35263
|
+
const size = entry.size_bytes;
|
|
35264
|
+
artifacts.push(
|
|
35265
|
+
typeof size === "number" && Number.isSafeInteger(size) && size >= 0 ? { name, sizeBytes: size } : { name }
|
|
35266
|
+
);
|
|
35267
|
+
}
|
|
35268
|
+
return artifacts;
|
|
35269
|
+
}
|
|
35270
|
+
function decodeMessageInstant(value) {
|
|
35271
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 && value < EPOCH_SECONDS_LIMIT ? new Date(Math.round(value) * 1e3).toISOString() : null;
|
|
35272
|
+
}
|
|
34709
35273
|
function decodeTurns(data, limit2) {
|
|
34710
|
-
if (!
|
|
35274
|
+
if (!isRecord4(data) || !Array.isArray(data.messages)) {
|
|
34711
35275
|
throw new PredispatchFailure("not-connected");
|
|
34712
35276
|
}
|
|
34713
35277
|
const turns = [];
|
|
34714
35278
|
for (const entry of data.messages) {
|
|
34715
|
-
if (!
|
|
35279
|
+
if (!isRecord4(entry)) continue;
|
|
34716
35280
|
if (entry.role !== "user" && entry.role !== "assistant") continue;
|
|
34717
35281
|
if (typeof entry.id !== "string" || !OPAQUE_ID.test(entry.id)) continue;
|
|
34718
35282
|
if (typeof entry.content !== "string") continue;
|
|
@@ -34721,12 +35285,52 @@ function decodeTurns(data, limit2) {
|
|
|
34721
35285
|
COMPANION_TURN_TEXT_MAX_CODE_POINTS
|
|
34722
35286
|
);
|
|
34723
35287
|
if (text.length === 0) continue;
|
|
34724
|
-
const at = typeof entry.createdAt === "number" && Number.isFinite(entry.createdAt) && entry.createdAt > 0 && entry.createdAt < EPOCH_SECONDS_LIMIT ? new Date(Math.round(entry.createdAt) * 1e3).toISOString() : null;
|
|
34725
35288
|
turns.push({
|
|
34726
35289
|
id: entry.id,
|
|
34727
35290
|
role: entry.role === "user" ? "user" : "mate",
|
|
34728
35291
|
text,
|
|
34729
|
-
at
|
|
35292
|
+
at: decodeMessageInstant(entry.createdAt)
|
|
35293
|
+
});
|
|
35294
|
+
}
|
|
35295
|
+
return turns.slice(-limit2);
|
|
35296
|
+
}
|
|
35297
|
+
function decodeDurableDecision(entry) {
|
|
35298
|
+
const frame = parseUiDirectiveFrame({
|
|
35299
|
+
directive: entry.uiDirective,
|
|
35300
|
+
source: entry.uiDirectiveSource === "voice" ? "voice" : "text"
|
|
35301
|
+
});
|
|
35302
|
+
if (!frame) return null;
|
|
35303
|
+
return toCompanionDecision(frame.directive);
|
|
35304
|
+
}
|
|
35305
|
+
function decodeRichTurns(data, limit2) {
|
|
35306
|
+
if (!isRecord4(data) || !Array.isArray(data.messages)) {
|
|
35307
|
+
throw new PredispatchFailure("not-connected");
|
|
35308
|
+
}
|
|
35309
|
+
const turns = [];
|
|
35310
|
+
for (const entry of data.messages) {
|
|
35311
|
+
if (!isRecord4(entry)) continue;
|
|
35312
|
+
if (entry.role !== "user" && entry.role !== "assistant") continue;
|
|
35313
|
+
if (typeof entry.id !== "string" || !OPAQUE_ID.test(entry.id)) continue;
|
|
35314
|
+
if (typeof entry.content !== "string") continue;
|
|
35315
|
+
const parsed = parseArtifacts(entry.content);
|
|
35316
|
+
const artifacts = toCompanionArtifacts(parsed.artifacts);
|
|
35317
|
+
const text = boundCodePoints(
|
|
35318
|
+
sanitizeMessageText(parsed.text),
|
|
35319
|
+
COMPANION_TURN_TEXT_MAX_CODE_POINTS
|
|
35320
|
+
);
|
|
35321
|
+
const at = decodeMessageInstant(entry.createdAt);
|
|
35322
|
+
const decision = entry.uiDirective === void 0 ? null : decodeDurableDecision(entry);
|
|
35323
|
+
if (decision !== null) {
|
|
35324
|
+
turns.push({ id: entry.id, role: "mate", text, at, decision });
|
|
35325
|
+
continue;
|
|
35326
|
+
}
|
|
35327
|
+
if (text.length === 0 && artifacts.length === 0) continue;
|
|
35328
|
+
turns.push({
|
|
35329
|
+
id: entry.id,
|
|
35330
|
+
role: entry.role === "user" ? "user" : "mate",
|
|
35331
|
+
text,
|
|
35332
|
+
at,
|
|
35333
|
+
...artifacts.length > 0 ? { artifacts } : {}
|
|
34730
35334
|
});
|
|
34731
35335
|
}
|
|
34732
35336
|
return turns.slice(-limit2);
|
|
@@ -34840,7 +35444,7 @@ async function drainAcceptedSse(response) {
|
|
|
34840
35444
|
continue;
|
|
34841
35445
|
}
|
|
34842
35446
|
const parsed = JSON.parse(data);
|
|
34843
|
-
if (!
|
|
35447
|
+
if (!isRecord4(parsed) || typeof parsed.type !== "string" || parsed.type === "error") {
|
|
34844
35448
|
throw new Error("invalid stream event");
|
|
34845
35449
|
}
|
|
34846
35450
|
}
|
|
@@ -34862,7 +35466,7 @@ function emitBoundedText(text, emit) {
|
|
|
34862
35466
|
}
|
|
34863
35467
|
if (slice.length > 0) emit(slice);
|
|
34864
35468
|
}
|
|
34865
|
-
async function streamAcceptedSse(response, now, onText) {
|
|
35469
|
+
async function streamAcceptedSse(response, now, onText, onData) {
|
|
34866
35470
|
if (!response.body) throw new Error("response body unavailable");
|
|
34867
35471
|
const reader = response.body.getReader();
|
|
34868
35472
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
@@ -34888,7 +35492,7 @@ async function streamAcceptedSse(response, now, onText) {
|
|
|
34888
35492
|
return;
|
|
34889
35493
|
}
|
|
34890
35494
|
const parsed = JSON.parse(data);
|
|
34891
|
-
if (!
|
|
35495
|
+
if (!isRecord4(parsed) || typeof parsed.type !== "string" || parsed.type === "error") {
|
|
34892
35496
|
throw new Error("invalid stream event");
|
|
34893
35497
|
}
|
|
34894
35498
|
if (parsed.type === "text-delta" && typeof parsed.delta === "string" && parsed.delta.length > 0) {
|
|
@@ -34897,6 +35501,10 @@ async function streamAcceptedSse(response, now, onText) {
|
|
|
34897
35501
|
flush();
|
|
34898
35502
|
}
|
|
34899
35503
|
}
|
|
35504
|
+
if (onData && parsed.type.startsWith("data-")) {
|
|
35505
|
+
flush();
|
|
35506
|
+
onData(parsed);
|
|
35507
|
+
}
|
|
34900
35508
|
};
|
|
34901
35509
|
const drainFrames = () => {
|
|
34902
35510
|
for (; ; ) {
|
|
@@ -34932,7 +35540,7 @@ async function streamAcceptedSse(response, now, onText) {
|
|
|
34932
35540
|
throw new Error("stream ended without terminal marker");
|
|
34933
35541
|
}
|
|
34934
35542
|
}
|
|
34935
|
-
async function dispatchConversation(request, sink, deps, consume, readDesktopMarker) {
|
|
35543
|
+
async function dispatchConversation(request, sink, deps, consume, readDesktopMarker, chatBody, classifyRefusal) {
|
|
34936
35544
|
const { controller, timer } = createDeadline(deps);
|
|
34937
35545
|
const fetchImpl = deps.fetch ?? fetch;
|
|
34938
35546
|
let route;
|
|
@@ -34978,17 +35586,19 @@ async function dispatchConversation(request, sink, deps, consume, readDesktopMar
|
|
|
34978
35586
|
chatResponse = await fetchImpl(`${origin}/api/chat`, {
|
|
34979
35587
|
method: "POST",
|
|
34980
35588
|
headers: { ...headers, "content-type": "application/json" },
|
|
34981
|
-
body: JSON.stringify(
|
|
34982
|
-
messages: [{ role: "user", content: request.text }],
|
|
34983
|
-
agentName,
|
|
34984
|
-
conversationId
|
|
34985
|
-
}),
|
|
35589
|
+
body: JSON.stringify(chatBody(agentName, conversationId)),
|
|
34986
35590
|
signal: controller.signal
|
|
34987
35591
|
});
|
|
34988
35592
|
} catch {
|
|
34989
35593
|
throw new Error("chat dispatch outcome unavailable");
|
|
34990
35594
|
}
|
|
34991
35595
|
if (!chatResponse.ok || !chatResponse.body || !(chatResponse.headers.get("content-type") ?? "").toLowerCase().includes("text/event-stream")) {
|
|
35596
|
+
if (!chatResponse.ok && classifyRefusal) {
|
|
35597
|
+
const reason = await classifyRefusal(chatResponse);
|
|
35598
|
+
if (reason !== null) {
|
|
35599
|
+
return emitFailure(sink, reason, request.requestId);
|
|
35600
|
+
}
|
|
35601
|
+
}
|
|
34992
35602
|
throw new Error("chat response not accepted");
|
|
34993
35603
|
}
|
|
34994
35604
|
const acceptedEvent = parseEvent({
|
|
@@ -35026,8 +35636,40 @@ async function dispatchConversation(request, sink, deps, consume, readDesktopMar
|
|
|
35026
35636
|
clearTimeout(timer);
|
|
35027
35637
|
}
|
|
35028
35638
|
}
|
|
35639
|
+
function textTurnBody(text) {
|
|
35640
|
+
return (agentName, conversationId) => ({
|
|
35641
|
+
messages: [{ role: "user", content: text }],
|
|
35642
|
+
agentName,
|
|
35643
|
+
conversationId
|
|
35644
|
+
});
|
|
35645
|
+
}
|
|
35646
|
+
function replyDataForwarder(requestId, sink) {
|
|
35647
|
+
return (part) => {
|
|
35648
|
+
if (part.type === UI_DIRECTIVE_PART_TYPE) {
|
|
35649
|
+
const frame = parseUiDirectiveFrame(part.data);
|
|
35650
|
+
if (!frame) return;
|
|
35651
|
+
const decision = toCompanionDecision(frame.directive);
|
|
35652
|
+
if (decision === null) return;
|
|
35653
|
+
sink(parseEvent({ type: "reply-decision", requestId, decision }));
|
|
35654
|
+
return;
|
|
35655
|
+
}
|
|
35656
|
+
if (part.type === ARTIFACTS_PART_TYPE && Array.isArray(part.data)) {
|
|
35657
|
+
const artifacts = toCompanionArtifacts(part.data);
|
|
35658
|
+
if (artifacts.length > 0) {
|
|
35659
|
+
sink(parseEvent({ type: "reply-artifacts", requestId, artifacts }));
|
|
35660
|
+
}
|
|
35661
|
+
}
|
|
35662
|
+
};
|
|
35663
|
+
}
|
|
35029
35664
|
async function sendCompanionMessage(request, sink, deps = {}) {
|
|
35030
|
-
return dispatchConversation(
|
|
35665
|
+
return dispatchConversation(
|
|
35666
|
+
request,
|
|
35667
|
+
sink,
|
|
35668
|
+
deps,
|
|
35669
|
+
drainAcceptedSse,
|
|
35670
|
+
true,
|
|
35671
|
+
textTurnBody(request.text)
|
|
35672
|
+
);
|
|
35031
35673
|
}
|
|
35032
35674
|
async function converseCompanionMessage(request, sink, deps = {}) {
|
|
35033
35675
|
return dispatchConversation(
|
|
@@ -35043,10 +35685,101 @@ async function converseCompanionMessage(request, sink, deps = {}) {
|
|
|
35043
35685
|
})
|
|
35044
35686
|
);
|
|
35045
35687
|
}),
|
|
35046
|
-
false
|
|
35688
|
+
false,
|
|
35689
|
+
textTurnBody(request.text)
|
|
35690
|
+
);
|
|
35691
|
+
}
|
|
35692
|
+
async function chatCompanionMessage(request, sink, deps = {}) {
|
|
35693
|
+
return dispatchConversation(
|
|
35694
|
+
request,
|
|
35695
|
+
sink,
|
|
35696
|
+
deps,
|
|
35697
|
+
(response) => streamAcceptedSse(
|
|
35698
|
+
response,
|
|
35699
|
+
deps.now ?? Date.now,
|
|
35700
|
+
(text) => {
|
|
35701
|
+
sink(
|
|
35702
|
+
parseEvent({
|
|
35703
|
+
type: "reply-delta",
|
|
35704
|
+
requestId: request.requestId,
|
|
35705
|
+
text
|
|
35706
|
+
})
|
|
35707
|
+
);
|
|
35708
|
+
},
|
|
35709
|
+
replyDataForwarder(request.requestId, sink)
|
|
35710
|
+
),
|
|
35711
|
+
false,
|
|
35712
|
+
textTurnBody(request.text)
|
|
35713
|
+
);
|
|
35714
|
+
}
|
|
35715
|
+
async function classifyDecideRefusal(response) {
|
|
35716
|
+
let reason;
|
|
35717
|
+
try {
|
|
35718
|
+
const envelope = JSON.parse(await readBoundedText(response));
|
|
35719
|
+
if (isRecord4(envelope) && isRecord4(envelope.error) && isRecord4(envelope.error.details)) {
|
|
35720
|
+
reason = envelope.error.details.reason;
|
|
35721
|
+
}
|
|
35722
|
+
} catch {
|
|
35723
|
+
}
|
|
35724
|
+
if (isDecisionRefusalReason(reason)) {
|
|
35725
|
+
return reason === DECISION_REFUSAL_REASONS.notPending ? "decision-not-pending" : "bad-request";
|
|
35726
|
+
}
|
|
35727
|
+
if (response.status === 401 || response.status === 403) {
|
|
35728
|
+
return "not-authorized";
|
|
35729
|
+
}
|
|
35730
|
+
if (response.status === 400) return "bad-request";
|
|
35731
|
+
if (response.status === 409) return "busy";
|
|
35732
|
+
return null;
|
|
35733
|
+
}
|
|
35734
|
+
async function decideCompanionMessage(request, sink, deps = {}) {
|
|
35735
|
+
const message = {
|
|
35736
|
+
role: "user",
|
|
35737
|
+
parts: [
|
|
35738
|
+
{
|
|
35739
|
+
type: UI_DIRECTIVE_RESULT_PART_TYPE,
|
|
35740
|
+
data: {
|
|
35741
|
+
version: 1,
|
|
35742
|
+
name: "present_decision",
|
|
35743
|
+
callId: request.callId,
|
|
35744
|
+
result: { status: "selected", optionIndex: request.optionIndex }
|
|
35745
|
+
}
|
|
35746
|
+
}
|
|
35747
|
+
]
|
|
35748
|
+
};
|
|
35749
|
+
return dispatchConversation(
|
|
35750
|
+
request,
|
|
35751
|
+
sink,
|
|
35752
|
+
deps,
|
|
35753
|
+
(response) => streamAcceptedSse(
|
|
35754
|
+
response,
|
|
35755
|
+
deps.now ?? Date.now,
|
|
35756
|
+
(text) => {
|
|
35757
|
+
sink(
|
|
35758
|
+
parseEvent({
|
|
35759
|
+
type: "reply-delta",
|
|
35760
|
+
requestId: request.requestId,
|
|
35761
|
+
text
|
|
35762
|
+
})
|
|
35763
|
+
);
|
|
35764
|
+
},
|
|
35765
|
+
replyDataForwarder(request.requestId, sink)
|
|
35766
|
+
),
|
|
35767
|
+
false,
|
|
35768
|
+
(agentName, conversationId) => ({
|
|
35769
|
+
messages: [message],
|
|
35770
|
+
agentName,
|
|
35771
|
+
conversationId
|
|
35772
|
+
}),
|
|
35773
|
+
classifyDecideRefusal
|
|
35047
35774
|
);
|
|
35048
35775
|
}
|
|
35049
35776
|
async function historyCompanionMessages(request, sink, deps = {}) {
|
|
35777
|
+
return fetchConversationTurns(request, sink, deps, decodeTurns);
|
|
35778
|
+
}
|
|
35779
|
+
async function recallCompanionMessages(request, sink, deps = {}) {
|
|
35780
|
+
return fetchConversationTurns(request, sink, deps, decodeRichTurns);
|
|
35781
|
+
}
|
|
35782
|
+
async function fetchConversationTurns(request, sink, deps, decode) {
|
|
35050
35783
|
const { controller, timer } = createDeadline(deps);
|
|
35051
35784
|
const fetchImpl = deps.fetch ?? fetch;
|
|
35052
35785
|
try {
|
|
@@ -35066,7 +35799,7 @@ async function historyCompanionMessages(request, sink, deps = {}) {
|
|
|
35066
35799
|
agentName,
|
|
35067
35800
|
controller.signal
|
|
35068
35801
|
);
|
|
35069
|
-
const turns =
|
|
35802
|
+
const turns = decode(
|
|
35070
35803
|
await fetchConversationMessages(
|
|
35071
35804
|
fetchImpl,
|
|
35072
35805
|
origin,
|
|
@@ -35222,7 +35955,10 @@ async function dispatchRequest(request, sink, handlers) {
|
|
|
35222
35955
|
return terminal;
|
|
35223
35956
|
}
|
|
35224
35957
|
if (request.type === "history") return handlers.history(request, sink);
|
|
35958
|
+
if (request.type === "recall") return handlers.recall(request, sink);
|
|
35225
35959
|
if (request.type === "converse") return handlers.converse(request, sink);
|
|
35960
|
+
if (request.type === "chat") return handlers.chat(request, sink);
|
|
35961
|
+
if (request.type === "decide") return handlers.decide(request, sink);
|
|
35226
35962
|
return handlers.send(request, sink);
|
|
35227
35963
|
}
|
|
35228
35964
|
async function handleWithContext(request, sink, handlers, context) {
|
|
@@ -35258,7 +35994,10 @@ async function runCompanionBridgeServe(stdin, stdout, stderr, deps = {}) {
|
|
|
35258
35994
|
roster: (sink2) => rosterCompanions(sink2, chat),
|
|
35259
35995
|
send: (request, sink2) => sendCompanionMessage(request, sink2, chat),
|
|
35260
35996
|
converse: (request, sink2) => converseCompanionMessage(request, sink2, chat),
|
|
35997
|
+
chat: (request, sink2) => chatCompanionMessage(request, sink2, chat),
|
|
35998
|
+
decide: (request, sink2) => decideCompanionMessage(request, sink2, chat),
|
|
35261
35999
|
history: (request, sink2) => historyCompanionMessages(request, sink2, chat),
|
|
36000
|
+
recall: (request, sink2) => recallCompanionMessages(request, sink2, chat),
|
|
35262
36001
|
update: deps.update ?? (() => checkCompanionUpdate(defaultLocalCompanionInstallOptions()))
|
|
35263
36002
|
};
|
|
35264
36003
|
const sink = (event) => {
|
|
@@ -35289,11 +36028,14 @@ async function runCompanionBridgeServe(stdin, stdout, stderr, deps = {}) {
|
|
|
35289
36028
|
}
|
|
35290
36029
|
|
|
35291
36030
|
// src/commands/companion/bridge.ts
|
|
35292
|
-
var
|
|
36031
|
+
var defaultDeps4 = {
|
|
35293
36032
|
roster: rosterCompanions,
|
|
35294
36033
|
send: sendCompanionMessage,
|
|
35295
36034
|
converse: converseCompanionMessage,
|
|
36035
|
+
chat: chatCompanionMessage,
|
|
36036
|
+
decide: decideCompanionMessage,
|
|
35296
36037
|
history: historyCompanionMessages,
|
|
36038
|
+
recall: recallCompanionMessages,
|
|
35297
36039
|
update: () => checkCompanionUpdate(defaultLocalCompanionInstallOptions())
|
|
35298
36040
|
};
|
|
35299
36041
|
async function readSingleRequest(stdin) {
|
|
@@ -35324,7 +36066,7 @@ function exitFor(terminal) {
|
|
|
35324
36066
|
}
|
|
35325
36067
|
return 2;
|
|
35326
36068
|
}
|
|
35327
|
-
async function runCompanionBridge(stdin, stdout, stderr, deps =
|
|
36069
|
+
async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps4) {
|
|
35328
36070
|
let request;
|
|
35329
36071
|
try {
|
|
35330
36072
|
request = parseRequestLine(await readSingleRequest(stdin));
|
|
@@ -35344,8 +36086,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
35344
36086
|
sink(terminal);
|
|
35345
36087
|
} else if (request.type === "history") {
|
|
35346
36088
|
terminal = await deps.history(request, sink);
|
|
36089
|
+
} else if (request.type === "recall") {
|
|
36090
|
+
terminal = await deps.recall(request, sink);
|
|
35347
36091
|
} else if (request.type === "converse") {
|
|
35348
36092
|
terminal = await deps.converse(request, sink);
|
|
36093
|
+
} else if (request.type === "chat") {
|
|
36094
|
+
terminal = await deps.chat(request, sink);
|
|
36095
|
+
} else if (request.type === "decide") {
|
|
36096
|
+
terminal = await deps.decide(request, sink);
|
|
35349
36097
|
} else terminal = await deps.send(request, sink);
|
|
35350
36098
|
return exitFor(terminal);
|
|
35351
36099
|
} catch {
|
|
@@ -35353,14 +36101,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
35353
36101
|
return 3;
|
|
35354
36102
|
}
|
|
35355
36103
|
}
|
|
35356
|
-
var CompanionBridgeCommand = class extends
|
|
36104
|
+
var CompanionBridgeCommand = class extends Command64 {
|
|
35357
36105
|
static paths = [["companion", "_bridge"]];
|
|
35358
36106
|
/**
|
|
35359
36107
|
* One process serving many requests instead of one per request, so the
|
|
35360
36108
|
* session keeps its authenticated context between them. A CLI predating the
|
|
35361
36109
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
35362
36110
|
*/
|
|
35363
|
-
serve =
|
|
36111
|
+
serve = Option61.Boolean("--serve", false);
|
|
35364
36112
|
async execute() {
|
|
35365
36113
|
if (this.serve) {
|
|
35366
36114
|
return runCompanionBridgeServe(
|
|
@@ -35378,7 +36126,7 @@ var CompanionBridgeCommand = class extends Command63 {
|
|
|
35378
36126
|
};
|
|
35379
36127
|
|
|
35380
36128
|
// src/commands/companion/status.ts
|
|
35381
|
-
import { Command as
|
|
36129
|
+
import { Command as Command65 } from "clipanion";
|
|
35382
36130
|
async function withTimeout(work, ms) {
|
|
35383
36131
|
let timer;
|
|
35384
36132
|
try {
|
|
@@ -35450,7 +36198,7 @@ Run: m8t companion install
|
|
|
35450
36198
|
}
|
|
35451
36199
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
35452
36200
|
static paths = [["companion", "status"]];
|
|
35453
|
-
static usage =
|
|
36201
|
+
static usage = Command65.Usage({
|
|
35454
36202
|
description: "Verify the installed desktop companion without launching it."
|
|
35455
36203
|
});
|
|
35456
36204
|
async executeCommand() {
|
|
@@ -35464,7 +36212,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
35464
36212
|
};
|
|
35465
36213
|
|
|
35466
36214
|
// src/commands/companion/repair.ts
|
|
35467
|
-
import { Command as
|
|
36215
|
+
import { Command as Command66, Option as Option62 } from "clipanion";
|
|
35468
36216
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
35469
36217
|
const state = await repair();
|
|
35470
36218
|
if (state.state === "not-released") {
|
|
@@ -35483,10 +36231,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
35483
36231
|
}
|
|
35484
36232
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
35485
36233
|
static paths = [["companion", "repair"]];
|
|
35486
|
-
static usage =
|
|
36234
|
+
static usage = Command66.Usage({
|
|
35487
36235
|
description: "Restore the desktop companions and start-at-login state."
|
|
35488
36236
|
});
|
|
35489
|
-
resourceGroup =
|
|
36237
|
+
resourceGroup = Option62.String("--resource-group", {
|
|
35490
36238
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
35491
36239
|
});
|
|
35492
36240
|
async executeCommand() {
|
|
@@ -35500,7 +36248,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
35500
36248
|
};
|
|
35501
36249
|
|
|
35502
36250
|
// src/commands/companion/uninstall.ts
|
|
35503
|
-
import { Command as
|
|
36251
|
+
import { Command as Command67 } from "clipanion";
|
|
35504
36252
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
35505
36253
|
const state = await uninstall();
|
|
35506
36254
|
if (state.state !== "not-installed") {
|
|
@@ -35512,7 +36260,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
35512
36260
|
}
|
|
35513
36261
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
35514
36262
|
static paths = [["companion", "uninstall"]];
|
|
35515
|
-
static usage =
|
|
36263
|
+
static usage = Command67.Usage({
|
|
35516
36264
|
description: "Remove only this user's desktop companion installation."
|
|
35517
36265
|
});
|
|
35518
36266
|
async executeCommand() {
|
|
@@ -35582,6 +36330,7 @@ cli.register(A2aDisableCommand);
|
|
|
35582
36330
|
cli.register(EvalSkillCommand);
|
|
35583
36331
|
cli.register(EvalExamCommand);
|
|
35584
36332
|
cli.register(DreamRunCommand);
|
|
36333
|
+
cli.register(ConversationsSweepCommand);
|
|
35585
36334
|
cli.register(FoundryCreateCommand);
|
|
35586
36335
|
cli.register(FoundryAwaitReadyCommand);
|
|
35587
36336
|
cli.register(BootstrapPreflightCommand);
|