@m8t-stack/cli 0.2.20 → 0.2.22
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 +290 -92
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1157,6 +1157,10 @@ async function enableHostedBrain(args) {
|
|
|
1157
1157
|
memory: def.memory,
|
|
1158
1158
|
env,
|
|
1159
1159
|
metadata: {
|
|
1160
|
+
// Spread the current metadata first so unmanaged keys (a2a, a2aCard, …)
|
|
1161
|
+
// survive the relink version; the managed fields are re-asserted over the
|
|
1162
|
+
// spread, and createCoderVersion applies the `brain` arg last.
|
|
1163
|
+
...current.metadata,
|
|
1160
1164
|
source: "m8t-stack-POC",
|
|
1161
1165
|
kind: "hosted",
|
|
1162
1166
|
persona: current.metadata?.persona ?? "coding-agent",
|
|
@@ -1227,7 +1231,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1227
1231
|
import { Builtins, Cli } from "clipanion";
|
|
1228
1232
|
|
|
1229
1233
|
// src/lib/package-version.ts
|
|
1230
|
-
var CLI_VERSION = "0.2.
|
|
1234
|
+
var CLI_VERSION = "0.2.22";
|
|
1231
1235
|
|
|
1232
1236
|
// src/lib/render-error.ts
|
|
1233
1237
|
init_errors();
|
|
@@ -15649,7 +15653,7 @@ async function awaitDataPlaneReady(opts) {
|
|
|
15649
15653
|
const consecutive = opts.consecutive ?? 3;
|
|
15650
15654
|
const attempts = opts.attempts ?? 60;
|
|
15651
15655
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
15652
|
-
const
|
|
15656
|
+
const sleep4 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15653
15657
|
let streak = 0;
|
|
15654
15658
|
for (let i = 1; i <= attempts; i++) {
|
|
15655
15659
|
const r = await opts.probe();
|
|
@@ -15671,7 +15675,7 @@ async function awaitDataPlaneReady(opts) {
|
|
|
15671
15675
|
streak = 0;
|
|
15672
15676
|
opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
|
|
15673
15677
|
}
|
|
15674
|
-
if (i < attempts) await
|
|
15678
|
+
if (i < attempts) await sleep4(intervalMs);
|
|
15675
15679
|
}
|
|
15676
15680
|
return { ready: false, attempts };
|
|
15677
15681
|
}
|
|
@@ -16699,7 +16703,7 @@ init_errors();
|
|
|
16699
16703
|
async function deployHostedWorker(args) {
|
|
16700
16704
|
const onProgress = args.onProgress ?? ((_m) => {
|
|
16701
16705
|
});
|
|
16702
|
-
const
|
|
16706
|
+
const sleep4 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
16703
16707
|
const now = args.now ?? (() => Date.now());
|
|
16704
16708
|
const timeout = args.pollTimeoutMs ?? 8 * 60 * 1e3;
|
|
16705
16709
|
const interval = args.pollIntervalMs ?? 1e4;
|
|
@@ -16751,7 +16755,7 @@ async function deployHostedWorker(args) {
|
|
|
16751
16755
|
hint: "Provisioning can take 2\u20135 min; re-run with the same name to resume, or check the version status in the Foundry portal."
|
|
16752
16756
|
});
|
|
16753
16757
|
}
|
|
16754
|
-
await
|
|
16758
|
+
await sleep4(interval);
|
|
16755
16759
|
}
|
|
16756
16760
|
}
|
|
16757
16761
|
|
|
@@ -22568,7 +22572,7 @@ init_errors();
|
|
|
22568
22572
|
// ../../packages/brain/engine/dist/esm/types.js
|
|
22569
22573
|
var ENGINE_VERSION = "0.1.0";
|
|
22570
22574
|
var DEFAULT_SAFETY_LAG_SECONDS = 3600;
|
|
22571
|
-
var SOURCES = ["webapp", "a2a", "mcp", "telegram"];
|
|
22575
|
+
var SOURCES = ["webapp", "a2a", "mcp", "telegram", "voice-relay"];
|
|
22572
22576
|
var WORKER_ALIASES = { "azure-advisor": "azzy" };
|
|
22573
22577
|
function canonicalWorker(pk) {
|
|
22574
22578
|
const lower = pk.toLowerCase();
|
|
@@ -22632,9 +22636,11 @@ var TableCursor = class {
|
|
|
22632
22636
|
throw e;
|
|
22633
22637
|
}
|
|
22634
22638
|
const watermarks = row.watermarks ? JSON.parse(row.watermarks) : {};
|
|
22639
|
+
const fetchFailures = row.fetchFailures ? JSON.parse(row.fetchFailures) : void 0;
|
|
22635
22640
|
return {
|
|
22636
22641
|
worker: canonical,
|
|
22637
22642
|
watermarks,
|
|
22643
|
+
...fetchFailures && Object.keys(fetchFailures).length > 0 ? { fetchFailures } : {},
|
|
22638
22644
|
safetyLagSeconds: row.safetyLagSeconds ?? DEFAULT_SAFETY_LAG_SECONDS,
|
|
22639
22645
|
engineVersion: row.engineVersion ?? ENGINE_VERSION,
|
|
22640
22646
|
...row.lastDreamAt ? { lastDreamAt: row.lastDreamAt } : {}
|
|
@@ -22657,9 +22663,12 @@ var TableCursor = class {
|
|
|
22657
22663
|
safetyLagSeconds: cursor.safetyLagSeconds,
|
|
22658
22664
|
engineVersion: cursor.engineVersion
|
|
22659
22665
|
};
|
|
22666
|
+
if (cursor.fetchFailures && Object.keys(cursor.fetchFailures).length > 0) {
|
|
22667
|
+
entity.fetchFailures = JSON.stringify(cursor.fetchFailures);
|
|
22668
|
+
}
|
|
22660
22669
|
if (cursor.lastDreamAt)
|
|
22661
22670
|
entity.lastDreamAt = cursor.lastDreamAt;
|
|
22662
|
-
await this.client.upsertEntity(entity);
|
|
22671
|
+
await this.client.upsertEntity(entity, "Replace");
|
|
22663
22672
|
}
|
|
22664
22673
|
async ensureTable() {
|
|
22665
22674
|
try {
|
|
@@ -22695,9 +22704,22 @@ function computeWatermark(rows, now, safetyLagSeconds) {
|
|
|
22695
22704
|
}
|
|
22696
22705
|
async function commitCursorAdvance(plan, cursor) {
|
|
22697
22706
|
const prior = await cursor.read(plan.worker);
|
|
22707
|
+
const watermarks = {};
|
|
22708
|
+
for (const [pk, watermark] of Object.entries(plan.watermarks)) {
|
|
22709
|
+
const priorWatermark = Object.hasOwn(prior.watermarks, pk) ? prior.watermarks[pk] : void 0;
|
|
22710
|
+
if (priorWatermark && watermark.ts <= priorWatermark.ts) {
|
|
22711
|
+
watermarks[pk] = {
|
|
22712
|
+
ts: watermark.ts,
|
|
22713
|
+
consumedRowKeys: [.../* @__PURE__ */ new Set([...priorWatermark.consumedRowKeys, ...watermark.consumedRowKeys])]
|
|
22714
|
+
};
|
|
22715
|
+
} else {
|
|
22716
|
+
watermarks[pk] = watermark;
|
|
22717
|
+
}
|
|
22718
|
+
}
|
|
22698
22719
|
const next = {
|
|
22699
22720
|
worker: plan.worker,
|
|
22700
|
-
watermarks
|
|
22721
|
+
watermarks,
|
|
22722
|
+
...plan.fetchFailures && Object.keys(plan.fetchFailures).length > 0 ? { fetchFailures: plan.fetchFailures } : {},
|
|
22701
22723
|
safetyLagSeconds: prior.safetyLagSeconds || DEFAULT_SAFETY_LAG_SECONDS,
|
|
22702
22724
|
engineVersion: ENGINE_VERSION,
|
|
22703
22725
|
lastDreamAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -22783,7 +22805,7 @@ function mapItems(raw) {
|
|
|
22783
22805
|
return acc.reverse();
|
|
22784
22806
|
}
|
|
22785
22807
|
function makeConversationSource(client, opts = {}) {
|
|
22786
|
-
const
|
|
22808
|
+
const sleep4 = opts.sleep ?? defaultSleep;
|
|
22787
22809
|
return {
|
|
22788
22810
|
async items(conversationId) {
|
|
22789
22811
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
@@ -22801,7 +22823,7 @@ function makeConversationSource(client, opts = {}) {
|
|
|
22801
22823
|
throw new ConvFetchError("auth", `auth failed reading ${conversationId}`);
|
|
22802
22824
|
const retryable = status === void 0 || status === TRANSIENT;
|
|
22803
22825
|
if (retryable && attempt < MAX_ATTEMPTS - 1) {
|
|
22804
|
-
await
|
|
22826
|
+
await sleep4(attempt);
|
|
22805
22827
|
continue;
|
|
22806
22828
|
}
|
|
22807
22829
|
throw new ConvFetchError("terminal", `exhausted ${String(MAX_ATTEMPTS)} retries reading ${conversationId}: ${e?.message ?? String(e)}`);
|
|
@@ -22951,6 +22973,7 @@ function normalize(group, items, enrichment) {
|
|
|
22951
22973
|
|
|
22952
22974
|
// ../../packages/brain/engine/dist/esm/pipeline.js
|
|
22953
22975
|
var isKnownSource = (s) => SOURCES.includes(s);
|
|
22976
|
+
var MAX_CONV_FETCH_ATTEMPTS = 3;
|
|
22954
22977
|
async function runPipeline(args) {
|
|
22955
22978
|
const { worker, sources, now, since, reset } = args;
|
|
22956
22979
|
const monotonicNow = args.monotonicNow ?? Date.now;
|
|
@@ -22977,6 +23000,7 @@ async function runPipeline(args) {
|
|
|
22977
23000
|
let recovered = 0;
|
|
22978
23001
|
const consumedRowsByPk = {};
|
|
22979
23002
|
const failedTsByPk = {};
|
|
23003
|
+
const newFailures = {};
|
|
22980
23004
|
const window2 = windowFor(rows, now, since);
|
|
22981
23005
|
for (const g of groups) {
|
|
22982
23006
|
const pk = g.worker;
|
|
@@ -23067,8 +23091,17 @@ async function runPipeline(args) {
|
|
|
23067
23091
|
else
|
|
23068
23092
|
recovered += 1;
|
|
23069
23093
|
} else {
|
|
23070
|
-
|
|
23071
|
-
if (outcome === "conv-fetch-failed"
|
|
23094
|
+
let lastError = fetchError?.message;
|
|
23095
|
+
if (outcome === "conv-fetch-failed") {
|
|
23096
|
+
const id = conversationId ?? "";
|
|
23097
|
+
const next = (cursor.fetchFailures?.[id] ?? 0) + 1;
|
|
23098
|
+
newFailures[id] = next;
|
|
23099
|
+
if (next >= MAX_CONV_FETCH_ATTEMPTS) {
|
|
23100
|
+
lastError = `terminal after ${String(next)} cycles \u2014 watermark released`;
|
|
23101
|
+
}
|
|
23102
|
+
}
|
|
23103
|
+
skips.addSkip(outcome, sampleId(g), lastError);
|
|
23104
|
+
if (outcome === "auth-failed" || outcome === "conv-fetch-failed" && conversationId && newFailures[conversationId] < MAX_CONV_FETCH_ATTEMPTS) {
|
|
23072
23105
|
const cur = failedTsByPk[pk];
|
|
23073
23106
|
if (!cur || g.startedAt < cur)
|
|
23074
23107
|
failedTsByPk[pk] = g.startedAt;
|
|
@@ -23088,7 +23121,7 @@ async function runPipeline(args) {
|
|
|
23088
23121
|
latencyMs: Math.round(monotonicNow() - t0)
|
|
23089
23122
|
};
|
|
23090
23123
|
assertInvariant(stats, groups.length);
|
|
23091
|
-
const advance = buildAdvancePlan(target, cursor, physicalPks, consumedRowsByPk, failedTsByPk, now, safetyLag);
|
|
23124
|
+
const advance = buildAdvancePlan(target, cursor, physicalPks, consumedRowsByPk, failedTsByPk, newFailures, now, safetyLag);
|
|
23092
23125
|
const out = {
|
|
23093
23126
|
worker: target,
|
|
23094
23127
|
window: window2,
|
|
@@ -23167,7 +23200,7 @@ function windowFor(rows, now, since) {
|
|
|
23167
23200
|
from = r.eventTimestamp;
|
|
23168
23201
|
return { from, to };
|
|
23169
23202
|
}
|
|
23170
|
-
function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedTsByPk, now, safetyLagSeconds) {
|
|
23203
|
+
function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedTsByPk, fetchFailures, now, safetyLagSeconds) {
|
|
23171
23204
|
const watermarks = {};
|
|
23172
23205
|
const consumedKeysByPk = {};
|
|
23173
23206
|
const priorTsByPk = {};
|
|
@@ -23191,6 +23224,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
23191
23224
|
watermarks,
|
|
23192
23225
|
consumedRowsByPk: consumedKeysByPk,
|
|
23193
23226
|
failedTsByPk,
|
|
23227
|
+
...Object.keys(fetchFailures).length > 0 ? { fetchFailures } : {},
|
|
23194
23228
|
now: now.toISOString(),
|
|
23195
23229
|
safetyLagSeconds,
|
|
23196
23230
|
priorTsByPk
|
|
@@ -23591,7 +23625,7 @@ function extractJson(text) {
|
|
|
23591
23625
|
return JSON.parse(candidate2.slice(start, end + 1));
|
|
23592
23626
|
}
|
|
23593
23627
|
async function propose(model, system, user, opts = {}) {
|
|
23594
|
-
const
|
|
23628
|
+
const sleep4 = opts.sleep ?? defaultSleep2;
|
|
23595
23629
|
const modelName = opts.model ?? MODEL_NAME_FALLBACK;
|
|
23596
23630
|
let inputTokens = 0;
|
|
23597
23631
|
let outputTokens = 0;
|
|
@@ -23607,7 +23641,7 @@ async function propose(model, system, user, opts = {}) {
|
|
|
23607
23641
|
} catch (e) {
|
|
23608
23642
|
lastErr = `parse: ${e.message}`;
|
|
23609
23643
|
if (attempt < MAX_MODEL_ATTEMPTS - 1) {
|
|
23610
|
-
await
|
|
23644
|
+
await sleep4(attempt);
|
|
23611
23645
|
continue;
|
|
23612
23646
|
}
|
|
23613
23647
|
break;
|
|
@@ -23616,7 +23650,7 @@ async function propose(model, system, user, opts = {}) {
|
|
|
23616
23650
|
if (!Array.isArray(rawDeltas)) {
|
|
23617
23651
|
lastErr = "schema: top-level { deltas: [] } missing";
|
|
23618
23652
|
if (attempt < MAX_MODEL_ATTEMPTS - 1) {
|
|
23619
|
-
await
|
|
23653
|
+
await sleep4(attempt);
|
|
23620
23654
|
continue;
|
|
23621
23655
|
}
|
|
23622
23656
|
break;
|
|
@@ -24183,6 +24217,7 @@ function narrowAdvance(src, kept) {
|
|
|
24183
24217
|
watermarks,
|
|
24184
24218
|
consumedRowsByPk,
|
|
24185
24219
|
failedTsByPk: src.failedTsByPk,
|
|
24220
|
+
...src.fetchFailures ? { fetchFailures: src.fetchFailures } : {},
|
|
24186
24221
|
now,
|
|
24187
24222
|
safetyLagSeconds,
|
|
24188
24223
|
priorTsByPk
|
|
@@ -24200,7 +24235,13 @@ function filterAdvance(src, keptRowsByPk) {
|
|
|
24200
24235
|
consumedRowsByPk[pk] = (src.consumedRowsByPk[pk] ?? []).filter((rk) => keep.has(rk));
|
|
24201
24236
|
watermarks[pk] = { ts: wm.ts, consumedRowKeys: wm.consumedRowKeys.filter((rk) => keep.has(rk)) };
|
|
24202
24237
|
}
|
|
24203
|
-
return {
|
|
24238
|
+
return {
|
|
24239
|
+
worker: src.worker,
|
|
24240
|
+
watermarks,
|
|
24241
|
+
consumedRowsByPk,
|
|
24242
|
+
failedTsByPk: src.failedTsByPk,
|
|
24243
|
+
...src.fetchFailures ? { fetchFailures: src.fetchFailures } : {}
|
|
24244
|
+
};
|
|
24204
24245
|
}
|
|
24205
24246
|
|
|
24206
24247
|
// ../../packages/brain/engine/dist/esm/dream/index.js
|
|
@@ -24218,6 +24259,18 @@ async function runDream(input, deps, seams = defaultDreamSeams()) {
|
|
|
24218
24259
|
});
|
|
24219
24260
|
}
|
|
24220
24261
|
input = capped;
|
|
24262
|
+
if (input.conversations.length === 0) {
|
|
24263
|
+
await commitCursorAdvance(input.advance, deps.cursor);
|
|
24264
|
+
deps.logger.info({
|
|
24265
|
+
at: "runDream",
|
|
24266
|
+
worker: input.worker,
|
|
24267
|
+
terminal: "no-corpus",
|
|
24268
|
+
skipped: input.stats.skipped,
|
|
24269
|
+
advanced: true,
|
|
24270
|
+
skipLedger: input.skipLedger.map((s) => `${s.reason}:${String(s.count)}`)
|
|
24271
|
+
});
|
|
24272
|
+
return { worker: input.worker, applied: [], digest, advanced: true };
|
|
24273
|
+
}
|
|
24221
24274
|
const proposed = await seams.propose(input, deps);
|
|
24222
24275
|
digest.push({ kind: "cost", model: proposed.cost.model, inputTokens: proposed.cost.inputTokens, outputTokens: proposed.cost.outputTokens, ms: proposed.cost.ms });
|
|
24223
24276
|
if (!proposed.deltas) {
|
|
@@ -25722,7 +25775,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
25722
25775
|
typeof this.output === "string" ? this.output : void 0,
|
|
25723
25776
|
this.context.stdout
|
|
25724
25777
|
);
|
|
25725
|
-
const
|
|
25778
|
+
const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
25726
25779
|
const watch = this.watch === true;
|
|
25727
25780
|
for (; ; ) {
|
|
25728
25781
|
let doc;
|
|
@@ -25732,7 +25785,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
25732
25785
|
if (watch && e instanceof LocalCliError && e.code === "BOOTSTRAP_STATUS_UNREADABLE") {
|
|
25733
25786
|
if (mode === "pretty") this.context.stderr.write(` ${colors.dim("waiting for the installer to start\u2026")}
|
|
25734
25787
|
`);
|
|
25735
|
-
await
|
|
25788
|
+
await sleep4(1e4);
|
|
25736
25789
|
continue;
|
|
25737
25790
|
}
|
|
25738
25791
|
throw e;
|
|
@@ -25757,7 +25810,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
25757
25810
|
);
|
|
25758
25811
|
return 1;
|
|
25759
25812
|
}
|
|
25760
|
-
await
|
|
25813
|
+
await sleep4(1e4);
|
|
25761
25814
|
}
|
|
25762
25815
|
}
|
|
25763
25816
|
};
|
|
@@ -26366,11 +26419,108 @@ import * as path35 from "path";
|
|
|
26366
26419
|
import { spawn as spawn7 } from "child_process";
|
|
26367
26420
|
init_errors();
|
|
26368
26421
|
init_rbac();
|
|
26422
|
+
|
|
26423
|
+
// src/lib/simple-stacey.ts
|
|
26424
|
+
var SIMPLE_STACEY_PERSONA = "startup-advisor-intake";
|
|
26425
|
+
var SIMPLE_STACEY_AGENT = "stacey-intake";
|
|
26426
|
+
async function deploySimpleStacey(args) {
|
|
26427
|
+
try {
|
|
26428
|
+
return await deployPromptAdvisor({
|
|
26429
|
+
credential: args.credential,
|
|
26430
|
+
endpoint: args.endpoint,
|
|
26431
|
+
repoRoot: args.repoRoot,
|
|
26432
|
+
persona: SIMPLE_STACEY_PERSONA,
|
|
26433
|
+
agentName: SIMPLE_STACEY_AGENT,
|
|
26434
|
+
fieldOverrides: args.fieldOverrides
|
|
26435
|
+
});
|
|
26436
|
+
} catch (e) {
|
|
26437
|
+
if (e && typeof e === "object" && "code" in e) {
|
|
26438
|
+
const err = e;
|
|
26439
|
+
if (err.code === "ADVISOR_PERSONA_MISSING") {
|
|
26440
|
+
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
26441
|
+
throw new LocalCliError2({
|
|
26442
|
+
code: "SIMPLE_STACEY_PERSONA_MISSING",
|
|
26443
|
+
message: err.message,
|
|
26444
|
+
hint: err.hint,
|
|
26445
|
+
cause: err.cause
|
|
26446
|
+
});
|
|
26447
|
+
}
|
|
26448
|
+
if (err.code === "ADVISOR_NO_MODEL") {
|
|
26449
|
+
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
26450
|
+
throw new LocalCliError2({
|
|
26451
|
+
code: "SIMPLE_STACEY_NO_MODEL",
|
|
26452
|
+
message: err.message,
|
|
26453
|
+
hint: err.hint,
|
|
26454
|
+
cause: err.cause
|
|
26455
|
+
});
|
|
26456
|
+
}
|
|
26457
|
+
if (err.code === "ADVISOR_BAD_EFFORT") {
|
|
26458
|
+
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
26459
|
+
throw new LocalCliError2({
|
|
26460
|
+
code: "SIMPLE_STACEY_BAD_EFFORT",
|
|
26461
|
+
message: err.message,
|
|
26462
|
+
hint: err.hint,
|
|
26463
|
+
cause: err.cause
|
|
26464
|
+
});
|
|
26465
|
+
}
|
|
26466
|
+
}
|
|
26467
|
+
throw e;
|
|
26468
|
+
}
|
|
26469
|
+
}
|
|
26470
|
+
|
|
26471
|
+
// src/lib/bootstrap-ui.ts
|
|
26472
|
+
function sleep3(ms) {
|
|
26473
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
26474
|
+
}
|
|
26475
|
+
function isAuthorizationShapedError(error) {
|
|
26476
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26477
|
+
let current = error;
|
|
26478
|
+
while (current != null && !seen.has(current)) {
|
|
26479
|
+
seen.add(current);
|
|
26480
|
+
if (typeof current === "object") {
|
|
26481
|
+
const record = current;
|
|
26482
|
+
if (record.statusCode === 403) return true;
|
|
26483
|
+
if (typeof record.message === "string" && /403|Forbidden|PermissionDenied|not authorized/i.test(record.message)) return true;
|
|
26484
|
+
current = record.cause;
|
|
26485
|
+
continue;
|
|
26486
|
+
}
|
|
26487
|
+
if (typeof current === "string" && /403|Forbidden|PermissionDenied|not authorized/i.test(current)) return true;
|
|
26488
|
+
break;
|
|
26489
|
+
}
|
|
26490
|
+
return false;
|
|
26491
|
+
}
|
|
26492
|
+
async function deploySimpleStaceyWithRetry(args) {
|
|
26493
|
+
const maxWaitMs = args.maxWaitMs ?? 3e5;
|
|
26494
|
+
const intervalMs = args.intervalMs ?? 1e4;
|
|
26495
|
+
const deadline = Date.now() + maxWaitMs;
|
|
26496
|
+
for (; ; ) {
|
|
26497
|
+
try {
|
|
26498
|
+
return await deploySimpleStacey({
|
|
26499
|
+
credential: args.credential,
|
|
26500
|
+
endpoint: args.endpoint,
|
|
26501
|
+
repoRoot: args.repoRoot,
|
|
26502
|
+
fieldOverrides: args.fieldOverrides
|
|
26503
|
+
});
|
|
26504
|
+
} catch (error) {
|
|
26505
|
+
if (!isAuthorizationShapedError(error)) throw error;
|
|
26506
|
+
const remainingMs = deadline - Date.now();
|
|
26507
|
+
if (remainingMs <= 0) {
|
|
26508
|
+
throw new LocalCliError({
|
|
26509
|
+
code: "BOOTSTRAP_UI_ROLE_PROPAGATION_TIMEOUT",
|
|
26510
|
+
message: "Timed out waiting for Azure role propagation before deploying Simple Stacey.",
|
|
26511
|
+
hint: "Azure role propagation can take a few minutes - re-run 'm8t bootstrap ui' (idempotent).",
|
|
26512
|
+
cause: error
|
|
26513
|
+
});
|
|
26514
|
+
}
|
|
26515
|
+
args.onWait?.("waiting for Azure role propagation...");
|
|
26516
|
+
await sleep3(Math.min(intervalMs, remainingMs));
|
|
26517
|
+
}
|
|
26518
|
+
}
|
|
26519
|
+
}
|
|
26369
26520
|
async function resolveFoundryEndpointWithWait(args, opts = {}) {
|
|
26370
26521
|
const pollMs = opts.pollMs ?? 1e4;
|
|
26371
26522
|
const timeoutMs = opts.timeoutMs ?? 15 * 6e4;
|
|
26372
26523
|
const deadline = Date.now() + timeoutMs;
|
|
26373
|
-
const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26374
26524
|
for (; ; ) {
|
|
26375
26525
|
try {
|
|
26376
26526
|
const p = await resolveFoundryProject({
|
|
@@ -26439,6 +26589,7 @@ function writeWebEnvLocal(args) {
|
|
|
26439
26589
|
`AZURE_TENANT_ID=${args.tenantId}`,
|
|
26440
26590
|
`AZURE_CLIENT_ID=${args.clientId}`,
|
|
26441
26591
|
`FOUNDRY_PROJECT_ENDPOINT=${args.foundryEndpoint}`,
|
|
26592
|
+
"VOICE_RELAY_PUBLIC_URL=ws://localhost:8790",
|
|
26442
26593
|
"STORAGE_ACCOUNT_NAME=",
|
|
26443
26594
|
"M8T_DISABLE_WORKER=1",
|
|
26444
26595
|
"M8T_ONBOARDING=1",
|
|
@@ -26493,11 +26644,16 @@ function onboardingUiPaths(home = os17.homedir()) {
|
|
|
26493
26644
|
pidPath: path35.join(dir, "onboarding-ui.pid")
|
|
26494
26645
|
};
|
|
26495
26646
|
}
|
|
26496
|
-
|
|
26497
|
-
const
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26647
|
+
function onboardingRelayPaths(home = os17.homedir()) {
|
|
26648
|
+
const dir = path35.join(home, ".m8t-stack");
|
|
26649
|
+
return {
|
|
26650
|
+
logPath: path35.join(dir, "onboarding-relay.log"),
|
|
26651
|
+
pidPath: path35.join(dir, "onboarding-relay.pid")
|
|
26652
|
+
};
|
|
26653
|
+
}
|
|
26654
|
+
function isLocalPortOpen(port) {
|
|
26655
|
+
return new Promise((resolve3) => {
|
|
26656
|
+
const s = net.createConnection({ host: "127.0.0.1", port });
|
|
26501
26657
|
s.setTimeout(1e3);
|
|
26502
26658
|
s.on("connect", () => {
|
|
26503
26659
|
s.destroy();
|
|
@@ -26511,6 +26667,11 @@ async function serveOnboardingUiDetached(args) {
|
|
|
26511
26667
|
resolve3(false);
|
|
26512
26668
|
});
|
|
26513
26669
|
});
|
|
26670
|
+
}
|
|
26671
|
+
async function serveOnboardingUiDetached(args) {
|
|
26672
|
+
const { logPath, pidPath } = onboardingUiPaths();
|
|
26673
|
+
const portNum = Number(args.port);
|
|
26674
|
+
const isPortOpen = () => isLocalPortOpen(portNum);
|
|
26514
26675
|
if (await isPortOpen()) {
|
|
26515
26676
|
return { alreadyRunning: true, logPath };
|
|
26516
26677
|
}
|
|
@@ -26534,13 +26695,53 @@ async function serveOnboardingUiDetached(args) {
|
|
|
26534
26695
|
child.unref();
|
|
26535
26696
|
fs32.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
26536
26697
|
const deadline = Date.now() + 45e3;
|
|
26537
|
-
const
|
|
26698
|
+
const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26538
26699
|
while (Date.now() < deadline) {
|
|
26539
|
-
await
|
|
26700
|
+
await sleep4(500);
|
|
26540
26701
|
if (await isPortOpen()) break;
|
|
26541
26702
|
}
|
|
26542
26703
|
return { alreadyRunning: false, logPath };
|
|
26543
26704
|
}
|
|
26705
|
+
async function serveOnboardingRelayDetached(args) {
|
|
26706
|
+
const { logPath, pidPath } = onboardingRelayPaths();
|
|
26707
|
+
const portNum = 8790;
|
|
26708
|
+
if (await isLocalPortOpen(portNum)) {
|
|
26709
|
+
return { alreadyRunning: true, logPath };
|
|
26710
|
+
}
|
|
26711
|
+
fs32.mkdirSync(path35.dirname(logPath), { recursive: true });
|
|
26712
|
+
const fd = fs32.openSync(logPath, "a");
|
|
26713
|
+
const child = spawn7("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
|
|
26714
|
+
cwd: args.repoRoot,
|
|
26715
|
+
detached: true,
|
|
26716
|
+
stdio: ["ignore", fd, fd],
|
|
26717
|
+
env: {
|
|
26718
|
+
...process.env,
|
|
26719
|
+
FOUNDRY_PROJECT_ENDPOINT: args.foundryEndpoint,
|
|
26720
|
+
AZURE_TENANT_ID: args.tenantId,
|
|
26721
|
+
AZURE_CLIENT_ID: args.clientId,
|
|
26722
|
+
RELAY_ALLOWED_ORIGINS: "http://localhost:3000",
|
|
26723
|
+
RELAY_PORT: "8790"
|
|
26724
|
+
},
|
|
26725
|
+
shell: false
|
|
26726
|
+
});
|
|
26727
|
+
fs32.closeSync(fd);
|
|
26728
|
+
if (child.pid == null) {
|
|
26729
|
+
throw new LocalCliError({
|
|
26730
|
+
code: "BOOTSTRAP_UI_RELAY_SPAWN_FAILED",
|
|
26731
|
+
message: "Could not start the voice relay (pnpm did not spawn).",
|
|
26732
|
+
hint: "Check that pnpm + Node 20+ are installed; see the log at " + logPath + "."
|
|
26733
|
+
});
|
|
26734
|
+
}
|
|
26735
|
+
child.unref();
|
|
26736
|
+
fs32.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
26737
|
+
const deadline = Date.now() + 45e3;
|
|
26738
|
+
const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26739
|
+
while (Date.now() < deadline) {
|
|
26740
|
+
await sleep4(500);
|
|
26741
|
+
if (await isLocalPortOpen(portNum)) break;
|
|
26742
|
+
}
|
|
26743
|
+
return { alreadyRunning: false, logPath };
|
|
26744
|
+
}
|
|
26544
26745
|
function autoOpenOnboardingUi(url, open = tryOpenUrl) {
|
|
26545
26746
|
try {
|
|
26546
26747
|
const result = open(url);
|
|
@@ -26551,8 +26752,7 @@ function autoOpenOnboardingUi(url, open = tryOpenUrl) {
|
|
|
26551
26752
|
} catch {
|
|
26552
26753
|
}
|
|
26553
26754
|
}
|
|
26554
|
-
function
|
|
26555
|
-
const { pidPath } = onboardingUiPaths(home);
|
|
26755
|
+
function stopPidFile(pidPath) {
|
|
26556
26756
|
let pidStr;
|
|
26557
26757
|
try {
|
|
26558
26758
|
pidStr = fs32.readFileSync(pidPath, "utf8").trim();
|
|
@@ -26573,56 +26773,27 @@ function stopOnboardingUi(home = os17.homedir()) {
|
|
|
26573
26773
|
}
|
|
26574
26774
|
return true;
|
|
26575
26775
|
}
|
|
26576
|
-
|
|
26577
|
-
|
|
26578
|
-
|
|
26579
|
-
|
|
26580
|
-
async function deploySimpleStacey(args) {
|
|
26581
|
-
try {
|
|
26582
|
-
return await deployPromptAdvisor({
|
|
26583
|
-
credential: args.credential,
|
|
26584
|
-
endpoint: args.endpoint,
|
|
26585
|
-
repoRoot: args.repoRoot,
|
|
26586
|
-
persona: SIMPLE_STACEY_PERSONA,
|
|
26587
|
-
agentName: SIMPLE_STACEY_AGENT,
|
|
26588
|
-
fieldOverrides: args.fieldOverrides
|
|
26589
|
-
});
|
|
26590
|
-
} catch (e) {
|
|
26591
|
-
if (e && typeof e === "object" && "code" in e) {
|
|
26592
|
-
const err = e;
|
|
26593
|
-
if (err.code === "ADVISOR_PERSONA_MISSING") {
|
|
26594
|
-
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
26595
|
-
throw new LocalCliError2({
|
|
26596
|
-
code: "SIMPLE_STACEY_PERSONA_MISSING",
|
|
26597
|
-
message: err.message,
|
|
26598
|
-
hint: err.hint,
|
|
26599
|
-
cause: err.cause
|
|
26600
|
-
});
|
|
26601
|
-
}
|
|
26602
|
-
if (err.code === "ADVISOR_NO_MODEL") {
|
|
26603
|
-
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
26604
|
-
throw new LocalCliError2({
|
|
26605
|
-
code: "SIMPLE_STACEY_NO_MODEL",
|
|
26606
|
-
message: err.message,
|
|
26607
|
-
hint: err.hint,
|
|
26608
|
-
cause: err.cause
|
|
26609
|
-
});
|
|
26610
|
-
}
|
|
26611
|
-
if (err.code === "ADVISOR_BAD_EFFORT") {
|
|
26612
|
-
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
26613
|
-
throw new LocalCliError2({
|
|
26614
|
-
code: "SIMPLE_STACEY_BAD_EFFORT",
|
|
26615
|
-
message: err.message,
|
|
26616
|
-
hint: err.hint,
|
|
26617
|
-
cause: err.cause
|
|
26618
|
-
});
|
|
26619
|
-
}
|
|
26620
|
-
}
|
|
26621
|
-
throw e;
|
|
26622
|
-
}
|
|
26776
|
+
function stopOnboardingUi(home = os17.homedir()) {
|
|
26777
|
+
const uiStopped = stopPidFile(onboardingUiPaths(home).pidPath);
|
|
26778
|
+
const relayStopped = stopPidFile(onboardingRelayPaths(home).pidPath);
|
|
26779
|
+
return uiStopped || relayStopped;
|
|
26623
26780
|
}
|
|
26624
26781
|
|
|
26625
26782
|
// src/commands/bootstrap/ui.ts
|
|
26783
|
+
function renderDeploySuccess(version, envPath) {
|
|
26784
|
+
return `${colors.success("\u2713")} Simple Stacey is live (stacey-intake v${version}).
|
|
26785
|
+
env: ${envPath}
|
|
26786
|
+
Opening ${colors.field("http://localhost:3000")} in your browser \u2192 Sign in with Microsoft \u2192 chat with Stacey.
|
|
26787
|
+
${colors.dim("(If it doesn't open, browse to http://localhost:3000 yourself.)")}
|
|
26788
|
+
${colors.dim("First turn may say 'warming up' for a few minutes while access propagates \u2014 that's expected.")}
|
|
26789
|
+
`;
|
|
26790
|
+
}
|
|
26791
|
+
function renderDeployFailure(error) {
|
|
26792
|
+
const hint = error instanceof LocalCliError && error.hint ? error.hint : "Re-run 'm8t bootstrap ui' (idempotent).";
|
|
26793
|
+
return `${colors.error("\u2717")} Simple Stacey deploy failed: ${error.message}
|
|
26794
|
+
${colors.hint(hint)}
|
|
26795
|
+
`;
|
|
26796
|
+
}
|
|
26626
26797
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
26627
26798
|
static paths = [["bootstrap", "ui"]];
|
|
26628
26799
|
static usage = Command53.Usage({
|
|
@@ -26702,14 +26873,18 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
26702
26873
|
out("granting you Foundry data-plane access\u2026");
|
|
26703
26874
|
const oid = await getSignedInUserOid();
|
|
26704
26875
|
await ensureFounderFoundryRole({ credential: credential2, subscriptionId: state.subscriptionId, principalId: oid, accountScope });
|
|
26705
|
-
out("deploying Simple Stacey (stacey-intake)\u2026");
|
|
26706
26876
|
const identity = await getSignedInUserIdentity();
|
|
26707
|
-
|
|
26877
|
+
out("deploying Simple Stacey (stacey-intake) in the background...");
|
|
26878
|
+
const deployOutcome = deploySimpleStaceyWithRetry({
|
|
26708
26879
|
credential: credential2,
|
|
26709
26880
|
endpoint,
|
|
26710
26881
|
repoRoot,
|
|
26711
|
-
fieldOverrides: { founder_identity_note: composeFounderIdentityNote(identity) }
|
|
26712
|
-
|
|
26882
|
+
fieldOverrides: { founder_identity_note: composeFounderIdentityNote(identity) },
|
|
26883
|
+
onWait: out
|
|
26884
|
+
}).then(
|
|
26885
|
+
(version) => ({ ok: true, version }),
|
|
26886
|
+
(error) => ({ ok: false, error: error instanceof Error ? error : new Error(String(error)) })
|
|
26887
|
+
);
|
|
26713
26888
|
const envPath = writeWebEnvLocal({
|
|
26714
26889
|
repoRoot,
|
|
26715
26890
|
tenantId: account.tenantId,
|
|
@@ -26720,24 +26895,41 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
26720
26895
|
out("installing apps/web dependencies (pnpm)\u2026");
|
|
26721
26896
|
await installWebDeps(repoRoot);
|
|
26722
26897
|
}
|
|
26723
|
-
this.context.stdout.write(
|
|
26724
|
-
`${colors.success("\u2713")} Simple Stacey is live (stacey-intake v${version}).
|
|
26725
|
-
env: ${envPath}
|
|
26726
|
-
Opening ${colors.field("http://localhost:3000")} in your browser \u2192 Sign in with Microsoft \u2192 chat with Stacey.
|
|
26727
|
-
${colors.dim("(If it doesn't open, browse to http://localhost:3000 yourself.)")}
|
|
26728
|
-
${colors.dim("First turn may say 'warming up' for a few minutes while access propagates \u2014 that's expected.")}
|
|
26729
|
-
`
|
|
26730
|
-
);
|
|
26731
26898
|
if (this.prepOnly === true) {
|
|
26899
|
+
const outcome2 = await deployOutcome;
|
|
26900
|
+
if (!outcome2.ok) throw outcome2.error;
|
|
26901
|
+
this.context.stdout.write(renderDeploySuccess(outcome2.version, envPath));
|
|
26732
26902
|
this.context.stdout.write(` ${colors.hint("serve it yourself:")} pnpm --filter web dev
|
|
26733
26903
|
`);
|
|
26734
26904
|
return 0;
|
|
26735
26905
|
}
|
|
26736
26906
|
if (this.foreground === true) {
|
|
26907
|
+
out("starting the voice relay on :8790 in the background\u2026");
|
|
26908
|
+
await serveOnboardingRelayDetached({
|
|
26909
|
+
repoRoot,
|
|
26910
|
+
foundryEndpoint: endpoint,
|
|
26911
|
+
tenantId: account.tenantId,
|
|
26912
|
+
clientId: state.appRegClientId
|
|
26913
|
+
});
|
|
26914
|
+
out("voice relay on :8790");
|
|
26915
|
+
const outcome2 = await deployOutcome;
|
|
26916
|
+
if (!outcome2.ok) {
|
|
26917
|
+
this.context.stderr.write(renderDeployFailure(outcome2.error));
|
|
26918
|
+
return 1;
|
|
26919
|
+
}
|
|
26920
|
+
this.context.stdout.write(renderDeploySuccess(outcome2.version, envPath));
|
|
26737
26921
|
out("starting the webapp on :3000 (Ctrl-C to stop)\u2026");
|
|
26738
26922
|
await runInherit("pnpm", ["--filter", "web", "dev"], repoRoot, "BOOTSTRAP_UI_DEV_FAILED");
|
|
26739
26923
|
return 0;
|
|
26740
26924
|
}
|
|
26925
|
+
out("starting the voice relay on :8790 in the background\u2026");
|
|
26926
|
+
await serveOnboardingRelayDetached({
|
|
26927
|
+
repoRoot,
|
|
26928
|
+
foundryEndpoint: endpoint,
|
|
26929
|
+
tenantId: account.tenantId,
|
|
26930
|
+
clientId: state.appRegClientId
|
|
26931
|
+
});
|
|
26932
|
+
out("voice relay on :8790");
|
|
26741
26933
|
out("starting the webapp on :3000 in the background\u2026");
|
|
26742
26934
|
const { alreadyRunning, logPath } = await serveOnboardingUiDetached({ repoRoot, port: this.port });
|
|
26743
26935
|
if (alreadyRunning) {
|
|
@@ -26753,6 +26945,12 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
26753
26945
|
);
|
|
26754
26946
|
}
|
|
26755
26947
|
autoOpenOnboardingUi(`http://localhost:${this.port}`);
|
|
26948
|
+
const outcome = await deployOutcome;
|
|
26949
|
+
if (!outcome.ok) {
|
|
26950
|
+
this.context.stderr.write(renderDeployFailure(outcome.error));
|
|
26951
|
+
return 1;
|
|
26952
|
+
}
|
|
26953
|
+
this.context.stdout.write(renderDeploySuccess(outcome.version, envPath));
|
|
26756
26954
|
return 0;
|
|
26757
26955
|
}
|
|
26758
26956
|
};
|
|
@@ -26790,7 +26988,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
26790
26988
|
const parsedTimeout = Number(rawTimeout);
|
|
26791
26989
|
const timeoutMin = rawTimeout !== "" && Number.isFinite(parsedTimeout) ? parsedTimeout : 20;
|
|
26792
26990
|
const deadline = Date.now() + timeoutMin * 6e4;
|
|
26793
|
-
const
|
|
26991
|
+
const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26794
26992
|
for (; ; ) {
|
|
26795
26993
|
const token = await getFoundryToken();
|
|
26796
26994
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
|
|
@@ -26813,7 +27011,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
26813
27011
|
}
|
|
26814
27012
|
this.context.stderr.write(` ${colors.dim("waiting for the questionnaire to complete\u2026")}
|
|
26815
27013
|
`);
|
|
26816
|
-
await
|
|
27014
|
+
await sleep4(2e4);
|
|
26817
27015
|
}
|
|
26818
27016
|
}
|
|
26819
27017
|
};
|