@m8t-stack/cli 0.2.21 → 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 CHANGED
@@ -1231,7 +1231,7 @@ var init_enable_hosted_brain = __esm({
1231
1231
  import { Builtins, Cli } from "clipanion";
1232
1232
 
1233
1233
  // src/lib/package-version.ts
1234
- var CLI_VERSION = "0.2.21";
1234
+ var CLI_VERSION = "0.2.22";
1235
1235
 
1236
1236
  // src/lib/render-error.ts
1237
1237
  init_errors();
@@ -15653,7 +15653,7 @@ async function awaitDataPlaneReady(opts) {
15653
15653
  const consecutive = opts.consecutive ?? 3;
15654
15654
  const attempts = opts.attempts ?? 60;
15655
15655
  const intervalMs = opts.intervalMs ?? 5e3;
15656
- const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
15656
+ const sleep4 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
15657
15657
  let streak = 0;
15658
15658
  for (let i = 1; i <= attempts; i++) {
15659
15659
  const r = await opts.probe();
@@ -15675,7 +15675,7 @@ async function awaitDataPlaneReady(opts) {
15675
15675
  streak = 0;
15676
15676
  opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
15677
15677
  }
15678
- if (i < attempts) await sleep3(intervalMs);
15678
+ if (i < attempts) await sleep4(intervalMs);
15679
15679
  }
15680
15680
  return { ready: false, attempts };
15681
15681
  }
@@ -16703,7 +16703,7 @@ init_errors();
16703
16703
  async function deployHostedWorker(args) {
16704
16704
  const onProgress = args.onProgress ?? ((_m) => {
16705
16705
  });
16706
- const sleep3 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16706
+ const sleep4 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16707
16707
  const now = args.now ?? (() => Date.now());
16708
16708
  const timeout = args.pollTimeoutMs ?? 8 * 60 * 1e3;
16709
16709
  const interval = args.pollIntervalMs ?? 1e4;
@@ -16755,7 +16755,7 @@ async function deployHostedWorker(args) {
16755
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."
16756
16756
  });
16757
16757
  }
16758
- await sleep3(interval);
16758
+ await sleep4(interval);
16759
16759
  }
16760
16760
  }
16761
16761
 
@@ -22805,7 +22805,7 @@ function mapItems(raw) {
22805
22805
  return acc.reverse();
22806
22806
  }
22807
22807
  function makeConversationSource(client, opts = {}) {
22808
- const sleep3 = opts.sleep ?? defaultSleep;
22808
+ const sleep4 = opts.sleep ?? defaultSleep;
22809
22809
  return {
22810
22810
  async items(conversationId) {
22811
22811
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
@@ -22823,7 +22823,7 @@ function makeConversationSource(client, opts = {}) {
22823
22823
  throw new ConvFetchError("auth", `auth failed reading ${conversationId}`);
22824
22824
  const retryable = status === void 0 || status === TRANSIENT;
22825
22825
  if (retryable && attempt < MAX_ATTEMPTS - 1) {
22826
- await sleep3(attempt);
22826
+ await sleep4(attempt);
22827
22827
  continue;
22828
22828
  }
22829
22829
  throw new ConvFetchError("terminal", `exhausted ${String(MAX_ATTEMPTS)} retries reading ${conversationId}: ${e?.message ?? String(e)}`);
@@ -23625,7 +23625,7 @@ function extractJson(text) {
23625
23625
  return JSON.parse(candidate2.slice(start, end + 1));
23626
23626
  }
23627
23627
  async function propose(model, system, user, opts = {}) {
23628
- const sleep3 = opts.sleep ?? defaultSleep2;
23628
+ const sleep4 = opts.sleep ?? defaultSleep2;
23629
23629
  const modelName = opts.model ?? MODEL_NAME_FALLBACK;
23630
23630
  let inputTokens = 0;
23631
23631
  let outputTokens = 0;
@@ -23641,7 +23641,7 @@ async function propose(model, system, user, opts = {}) {
23641
23641
  } catch (e) {
23642
23642
  lastErr = `parse: ${e.message}`;
23643
23643
  if (attempt < MAX_MODEL_ATTEMPTS - 1) {
23644
- await sleep3(attempt);
23644
+ await sleep4(attempt);
23645
23645
  continue;
23646
23646
  }
23647
23647
  break;
@@ -23650,7 +23650,7 @@ async function propose(model, system, user, opts = {}) {
23650
23650
  if (!Array.isArray(rawDeltas)) {
23651
23651
  lastErr = "schema: top-level { deltas: [] } missing";
23652
23652
  if (attempt < MAX_MODEL_ATTEMPTS - 1) {
23653
- await sleep3(attempt);
23653
+ await sleep4(attempt);
23654
23654
  continue;
23655
23655
  }
23656
23656
  break;
@@ -25775,7 +25775,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
25775
25775
  typeof this.output === "string" ? this.output : void 0,
25776
25776
  this.context.stdout
25777
25777
  );
25778
- const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
25778
+ const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
25779
25779
  const watch = this.watch === true;
25780
25780
  for (; ; ) {
25781
25781
  let doc;
@@ -25785,7 +25785,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
25785
25785
  if (watch && e instanceof LocalCliError && e.code === "BOOTSTRAP_STATUS_UNREADABLE") {
25786
25786
  if (mode === "pretty") this.context.stderr.write(` ${colors.dim("waiting for the installer to start\u2026")}
25787
25787
  `);
25788
- await sleep3(1e4);
25788
+ await sleep4(1e4);
25789
25789
  continue;
25790
25790
  }
25791
25791
  throw e;
@@ -25810,7 +25810,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
25810
25810
  );
25811
25811
  return 1;
25812
25812
  }
25813
- await sleep3(1e4);
25813
+ await sleep4(1e4);
25814
25814
  }
25815
25815
  }
25816
25816
  };
@@ -26419,11 +26419,108 @@ import * as path35 from "path";
26419
26419
  import { spawn as spawn7 } from "child_process";
26420
26420
  init_errors();
26421
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
+ }
26422
26520
  async function resolveFoundryEndpointWithWait(args, opts = {}) {
26423
26521
  const pollMs = opts.pollMs ?? 1e4;
26424
26522
  const timeoutMs = opts.timeoutMs ?? 15 * 6e4;
26425
26523
  const deadline = Date.now() + timeoutMs;
26426
- const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
26427
26524
  for (; ; ) {
26428
26525
  try {
26429
26526
  const p = await resolveFoundryProject({
@@ -26598,9 +26695,9 @@ async function serveOnboardingUiDetached(args) {
26598
26695
  child.unref();
26599
26696
  fs32.writeFileSync(pidPath, String(child.pid), "utf8");
26600
26697
  const deadline = Date.now() + 45e3;
26601
- const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
26698
+ const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
26602
26699
  while (Date.now() < deadline) {
26603
- await sleep3(500);
26700
+ await sleep4(500);
26604
26701
  if (await isPortOpen()) break;
26605
26702
  }
26606
26703
  return { alreadyRunning: false, logPath };
@@ -26638,9 +26735,9 @@ async function serveOnboardingRelayDetached(args) {
26638
26735
  child.unref();
26639
26736
  fs32.writeFileSync(pidPath, String(child.pid), "utf8");
26640
26737
  const deadline = Date.now() + 45e3;
26641
- const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
26738
+ const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
26642
26739
  while (Date.now() < deadline) {
26643
- await sleep3(500);
26740
+ await sleep4(500);
26644
26741
  if (await isLocalPortOpen(portNum)) break;
26645
26742
  }
26646
26743
  return { alreadyRunning: false, logPath };
@@ -26682,55 +26779,21 @@ function stopOnboardingUi(home = os17.homedir()) {
26682
26779
  return uiStopped || relayStopped;
26683
26780
  }
26684
26781
 
26685
- // src/lib/simple-stacey.ts
26686
- var SIMPLE_STACEY_PERSONA = "startup-advisor-intake";
26687
- var SIMPLE_STACEY_AGENT = "stacey-intake";
26688
- async function deploySimpleStacey(args) {
26689
- try {
26690
- return await deployPromptAdvisor({
26691
- credential: args.credential,
26692
- endpoint: args.endpoint,
26693
- repoRoot: args.repoRoot,
26694
- persona: SIMPLE_STACEY_PERSONA,
26695
- agentName: SIMPLE_STACEY_AGENT,
26696
- fieldOverrides: args.fieldOverrides
26697
- });
26698
- } catch (e) {
26699
- if (e && typeof e === "object" && "code" in e) {
26700
- const err = e;
26701
- if (err.code === "ADVISOR_PERSONA_MISSING") {
26702
- const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
26703
- throw new LocalCliError2({
26704
- code: "SIMPLE_STACEY_PERSONA_MISSING",
26705
- message: err.message,
26706
- hint: err.hint,
26707
- cause: err.cause
26708
- });
26709
- }
26710
- if (err.code === "ADVISOR_NO_MODEL") {
26711
- const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
26712
- throw new LocalCliError2({
26713
- code: "SIMPLE_STACEY_NO_MODEL",
26714
- message: err.message,
26715
- hint: err.hint,
26716
- cause: err.cause
26717
- });
26718
- }
26719
- if (err.code === "ADVISOR_BAD_EFFORT") {
26720
- const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
26721
- throw new LocalCliError2({
26722
- code: "SIMPLE_STACEY_BAD_EFFORT",
26723
- message: err.message,
26724
- hint: err.hint,
26725
- cause: err.cause
26726
- });
26727
- }
26728
- }
26729
- throw e;
26730
- }
26731
- }
26732
-
26733
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
+ }
26734
26797
  var BootstrapUiCommand = class extends M8tCommand {
26735
26798
  static paths = [["bootstrap", "ui"]];
26736
26799
  static usage = Command53.Usage({
@@ -26810,14 +26873,18 @@ var BootstrapUiCommand = class extends M8tCommand {
26810
26873
  out("granting you Foundry data-plane access\u2026");
26811
26874
  const oid = await getSignedInUserOid();
26812
26875
  await ensureFounderFoundryRole({ credential: credential2, subscriptionId: state.subscriptionId, principalId: oid, accountScope });
26813
- out("deploying Simple Stacey (stacey-intake)\u2026");
26814
26876
  const identity = await getSignedInUserIdentity();
26815
- const version = await deploySimpleStacey({
26877
+ out("deploying Simple Stacey (stacey-intake) in the background...");
26878
+ const deployOutcome = deploySimpleStaceyWithRetry({
26816
26879
  credential: credential2,
26817
26880
  endpoint,
26818
26881
  repoRoot,
26819
- fieldOverrides: { founder_identity_note: composeFounderIdentityNote(identity) }
26820
- });
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
+ );
26821
26888
  const envPath = writeWebEnvLocal({
26822
26889
  repoRoot,
26823
26890
  tenantId: account.tenantId,
@@ -26828,15 +26895,10 @@ var BootstrapUiCommand = class extends M8tCommand {
26828
26895
  out("installing apps/web dependencies (pnpm)\u2026");
26829
26896
  await installWebDeps(repoRoot);
26830
26897
  }
26831
- this.context.stdout.write(
26832
- `${colors.success("\u2713")} Simple Stacey is live (stacey-intake v${version}).
26833
- env: ${envPath}
26834
- Opening ${colors.field("http://localhost:3000")} in your browser \u2192 Sign in with Microsoft \u2192 chat with Stacey.
26835
- ${colors.dim("(If it doesn't open, browse to http://localhost:3000 yourself.)")}
26836
- ${colors.dim("First turn may say 'warming up' for a few minutes while access propagates \u2014 that's expected.")}
26837
- `
26838
- );
26839
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));
26840
26902
  this.context.stdout.write(` ${colors.hint("serve it yourself:")} pnpm --filter web dev
26841
26903
  `);
26842
26904
  return 0;
@@ -26850,6 +26912,12 @@ var BootstrapUiCommand = class extends M8tCommand {
26850
26912
  clientId: state.appRegClientId
26851
26913
  });
26852
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));
26853
26921
  out("starting the webapp on :3000 (Ctrl-C to stop)\u2026");
26854
26922
  await runInherit("pnpm", ["--filter", "web", "dev"], repoRoot, "BOOTSTRAP_UI_DEV_FAILED");
26855
26923
  return 0;
@@ -26877,6 +26945,12 @@ var BootstrapUiCommand = class extends M8tCommand {
26877
26945
  );
26878
26946
  }
26879
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));
26880
26954
  return 0;
26881
26955
  }
26882
26956
  };
@@ -26914,7 +26988,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
26914
26988
  const parsedTimeout = Number(rawTimeout);
26915
26989
  const timeoutMin = rawTimeout !== "" && Number.isFinite(parsedTimeout) ? parsedTimeout : 20;
26916
26990
  const deadline = Date.now() + timeoutMin * 6e4;
26917
- const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
26991
+ const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
26918
26992
  for (; ; ) {
26919
26993
  const token = await getFoundryToken();
26920
26994
  const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
@@ -26937,7 +27011,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
26937
27011
  }
26938
27012
  this.context.stderr.write(` ${colors.dim("waiting for the questionnaire to complete\u2026")}
26939
27013
  `);
26940
- await sleep3(2e4);
27014
+ await sleep4(2e4);
26941
27015
  }
26942
27016
  }
26943
27017
  };