@base44-preview/cli 0.1.15-pr.630.ca4815b → 0.1.15-pr.630.e926db5

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/index.js CHANGED
@@ -52822,7 +52822,7 @@ function canBreakFn(doc) {
52822
52822
  function canBreak(doc) {
52823
52823
  return findInDoc(doc, canBreakFn, false);
52824
52824
  }
52825
- function indent(contents) {
52825
+ function indent2(contents) {
52826
52826
  assertDoc(contents);
52827
52827
  return { type: DOC_TYPE_INDENT, contents };
52828
52828
  }
@@ -52845,7 +52845,7 @@ function addAlignmentToDoc(doc, size, tabWidth) {
52845
52845
  let aligned = doc;
52846
52846
  if (size > 0) {
52847
52847
  for (let level = 0;level < Math.floor(size / tabWidth); ++level) {
52848
- aligned = indent(aligned);
52848
+ aligned = indent2(aligned);
52849
52849
  }
52850
52850
  aligned = align(size % tabWidth, aligned);
52851
52851
  aligned = align(Number.NEGATIVE_INFINITY, aligned);
@@ -53179,7 +53179,7 @@ function printDocToString(doc, options) {
53179
53179
  propagateBreaks(doc);
53180
53180
  while (commands.length > 0) {
53181
53181
  const {
53182
- indent: indent2,
53182
+ indent: indent22,
53183
53183
  mode,
53184
53184
  doc: doc2
53185
53185
  } = commands.pop();
@@ -53199,7 +53199,7 @@ function printDocToString(doc, options) {
53199
53199
  case DOC_TYPE_ARRAY:
53200
53200
  for (let index = doc2.length - 1;index >= 0; index--) {
53201
53201
  commands.push({
53202
- indent: indent2,
53202
+ indent: indent22,
53203
53203
  mode,
53204
53204
  doc: doc2[index]
53205
53205
  });
@@ -53213,14 +53213,14 @@ function printDocToString(doc, options) {
53213
53213
  break;
53214
53214
  case DOC_TYPE_INDENT:
53215
53215
  commands.push({
53216
- indent: makeIndent(indent2, options),
53216
+ indent: makeIndent(indent22, options),
53217
53217
  mode,
53218
53218
  doc: doc2.contents
53219
53219
  });
53220
53220
  break;
53221
53221
  case DOC_TYPE_ALIGN:
53222
53222
  commands.push({
53223
- indent: makeAlign(indent2, doc2.n, options),
53223
+ indent: makeAlign(indent22, doc2.n, options),
53224
53224
  mode,
53225
53225
  doc: doc2.contents
53226
53226
  });
@@ -53233,7 +53233,7 @@ function printDocToString(doc, options) {
53233
53233
  case MODE_FLAT:
53234
53234
  if (!shouldRemeasure) {
53235
53235
  commands.push({
53236
- indent: indent2,
53236
+ indent: indent22,
53237
53237
  mode: doc2.break ? MODE_BREAK : MODE_FLAT,
53238
53238
  doc: doc2.contents
53239
53239
  });
@@ -53242,7 +53242,7 @@ function printDocToString(doc, options) {
53242
53242
  case MODE_BREAK: {
53243
53243
  shouldRemeasure = false;
53244
53244
  const next = {
53245
- indent: indent2,
53245
+ indent: indent22,
53246
53246
  mode: MODE_FLAT,
53247
53247
  doc: doc2.contents
53248
53248
  };
@@ -53255,7 +53255,7 @@ function printDocToString(doc, options) {
53255
53255
  const mostExpanded = method_at_default(0, doc2.expandedStates, -1);
53256
53256
  if (doc2.break) {
53257
53257
  commands.push({
53258
- indent: indent2,
53258
+ indent: indent22,
53259
53259
  mode: MODE_BREAK,
53260
53260
  doc: mostExpanded
53261
53261
  });
@@ -53264,7 +53264,7 @@ function printDocToString(doc, options) {
53264
53264
  for (let index = 1;index < doc2.expandedStates.length + 1; index++) {
53265
53265
  if (index >= doc2.expandedStates.length) {
53266
53266
  commands.push({
53267
- indent: indent2,
53267
+ indent: indent22,
53268
53268
  mode: MODE_BREAK,
53269
53269
  doc: mostExpanded
53270
53270
  });
@@ -53272,7 +53272,7 @@ function printDocToString(doc, options) {
53272
53272
  } else {
53273
53273
  const state = doc2.expandedStates[index];
53274
53274
  const cmd = {
53275
- indent: indent2,
53275
+ indent: indent22,
53276
53276
  mode: MODE_FLAT,
53277
53277
  doc: state
53278
53278
  };
@@ -53285,7 +53285,7 @@ function printDocToString(doc, options) {
53285
53285
  }
53286
53286
  } else {
53287
53287
  commands.push({
53288
- indent: indent2,
53288
+ indent: indent22,
53289
53289
  mode: MODE_BREAK,
53290
53290
  doc: doc2.contents
53291
53291
  });
@@ -53311,12 +53311,12 @@ function printDocToString(doc, options) {
53311
53311
  const content = parts[offset + 0];
53312
53312
  const whitespace = parts[offset + 1];
53313
53313
  const contentFlatCommand = {
53314
- indent: indent2,
53314
+ indent: indent22,
53315
53315
  mode: MODE_FLAT,
53316
53316
  doc: content
53317
53317
  };
53318
53318
  const contentBreakCommand = {
53319
- indent: indent2,
53319
+ indent: indent22,
53320
53320
  mode: MODE_BREAK,
53321
53321
  doc: content
53322
53322
  };
@@ -53330,12 +53330,12 @@ function printDocToString(doc, options) {
53330
53330
  break;
53331
53331
  }
53332
53332
  const whitespaceFlatCommand = {
53333
- indent: indent2,
53333
+ indent: indent22,
53334
53334
  mode: MODE_FLAT,
53335
53335
  doc: whitespace
53336
53336
  };
53337
53337
  const whitespaceBreakCommand = {
53338
- indent: indent2,
53338
+ indent: indent22,
53339
53339
  mode: MODE_BREAK,
53340
53340
  doc: whitespace
53341
53341
  };
@@ -53349,7 +53349,7 @@ function printDocToString(doc, options) {
53349
53349
  }
53350
53350
  const secondContent = parts[offset + 2];
53351
53351
  const remainingCommand = {
53352
- indent: indent2,
53352
+ indent: indent22,
53353
53353
  mode,
53354
53354
  doc: {
53355
53355
  ...doc2,
@@ -53357,7 +53357,7 @@ function printDocToString(doc, options) {
53357
53357
  }
53358
53358
  };
53359
53359
  const firstAndSecondContentFlatCommand = {
53360
- indent: indent2,
53360
+ indent: indent22,
53361
53361
  mode: MODE_FLAT,
53362
53362
  doc: [content, whitespace, secondContent]
53363
53363
  };
@@ -53376,20 +53376,20 @@ function printDocToString(doc, options) {
53376
53376
  case DOC_TYPE_INDENT_IF_BREAK: {
53377
53377
  const groupMode = doc2.groupId ? groupModeMap[doc2.groupId] : mode;
53378
53378
  if (groupMode === MODE_BREAK) {
53379
- const breakContents = doc2.type === DOC_TYPE_IF_BREAK ? doc2.breakContents : doc2.negate ? doc2.contents : indent(doc2.contents);
53379
+ const breakContents = doc2.type === DOC_TYPE_IF_BREAK ? doc2.breakContents : doc2.negate ? doc2.contents : indent2(doc2.contents);
53380
53380
  if (breakContents) {
53381
53381
  commands.push({
53382
- indent: indent2,
53382
+ indent: indent22,
53383
53383
  mode,
53384
53384
  doc: breakContents
53385
53385
  });
53386
53386
  }
53387
53387
  }
53388
53388
  if (groupMode === MODE_FLAT) {
53389
- const flatContents = doc2.type === DOC_TYPE_IF_BREAK ? doc2.flatContents : doc2.negate ? indent(doc2.contents) : doc2.contents;
53389
+ const flatContents = doc2.type === DOC_TYPE_IF_BREAK ? doc2.flatContents : doc2.negate ? indent2(doc2.contents) : doc2.contents;
53390
53390
  if (flatContents) {
53391
53391
  commands.push({
53392
- indent: indent2,
53392
+ indent: indent22,
53393
53393
  mode,
53394
53394
  doc: flatContents
53395
53395
  });
@@ -53399,7 +53399,7 @@ function printDocToString(doc, options) {
53399
53399
  }
53400
53400
  case DOC_TYPE_LINE_SUFFIX:
53401
53401
  lineSuffix2.push({
53402
- indent: indent2,
53402
+ indent: indent22,
53403
53403
  mode,
53404
53404
  doc: doc2.contents
53405
53405
  });
@@ -53407,7 +53407,7 @@ function printDocToString(doc, options) {
53407
53407
  case DOC_TYPE_LINE_SUFFIX_BOUNDARY:
53408
53408
  if (lineSuffix2.length > 0) {
53409
53409
  commands.push({
53410
- indent: indent2,
53410
+ indent: indent22,
53411
53411
  mode,
53412
53412
  doc: hardlineWithoutBreakParent
53413
53413
  });
@@ -53428,7 +53428,7 @@ function printDocToString(doc, options) {
53428
53428
  case MODE_BREAK:
53429
53429
  if (lineSuffix2.length > 0) {
53430
53430
  commands.push({
53431
- indent: indent2,
53431
+ indent: indent22,
53432
53432
  mode,
53433
53433
  doc: doc2
53434
53434
  }, ...lineSuffix2.reverse());
@@ -53438,23 +53438,23 @@ function printDocToString(doc, options) {
53438
53438
  if (doc2.literal) {
53439
53439
  output += newLine;
53440
53440
  position = 0;
53441
- if (indent2.root) {
53442
- if (indent2.root.value) {
53443
- output += indent2.root.value;
53441
+ if (indent22.root) {
53442
+ if (indent22.root.value) {
53443
+ output += indent22.root.value;
53444
53444
  }
53445
- position = indent2.root.length;
53445
+ position = indent22.root.length;
53446
53446
  }
53447
53447
  } else {
53448
53448
  trim2();
53449
- output += newLine + indent2.value;
53450
- position = indent2.length;
53449
+ output += newLine + indent22.value;
53450
+ position = indent22.length;
53451
53451
  }
53452
53452
  break;
53453
53453
  }
53454
53454
  break;
53455
53455
  case DOC_TYPE_LABEL:
53456
53456
  commands.push({
53457
- indent: indent2,
53457
+ indent: indent22,
53458
53458
  mode,
53459
53459
  doc: doc2.contents
53460
53460
  });
@@ -53616,7 +53616,7 @@ var init_doc = __esm(() => {
53616
53616
  breakParent,
53617
53617
  ifBreak,
53618
53618
  trim: trim2,
53619
- indent,
53619
+ indent: indent2,
53620
53620
  indentIfBreak,
53621
53621
  align,
53622
53622
  addAlignmentToDoc,
@@ -140754,7 +140754,7 @@ import { pathToFileURL as pathToFileURL5 } from "url";
140754
140754
  import path122 from "path";
140755
140755
  import path142 from "path";
140756
140756
  import url22 from "url";
140757
- function diffLines(oldStr, newStr, options8) {
140757
+ function diffLines2(oldStr, newStr, options8) {
140758
140758
  return lineDiff.diff(oldStr, newStr, options8);
140759
140759
  }
140760
140760
  function tokenize4(value, options8) {
@@ -140796,10 +140796,10 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne
140796
140796
  throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
140797
140797
  }
140798
140798
  if (!optionsObj.callback) {
140799
- return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
140799
+ return diffLinesResultToPatch(diffLines2(oldStr, newStr, optionsObj));
140800
140800
  } else {
140801
140801
  const { callback } = optionsObj;
140802
- diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
140802
+ diffLines2(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
140803
140803
  const patch = diffLinesResultToPatch(diff);
140804
140804
  callback(patch);
140805
140805
  } }));
@@ -146441,7 +146441,7 @@ var require2, __filename2, __dirname3, __create2, __defProp3, __getOwnPropDesc2,
146441
146441
  }
146442
146442
  }
146443
146443
  }, ast_path_default, is_object_default, skipWhitespace, skipSpaces, skipToLineEnd, skipEverythingButNewLine, isNewlineCharacter = (character) => character === `
146444
- ` || character === "\r" || character === "\u2028" || character === "\u2029", skip_newline_default, has_newline_default, is_non_empty_array_default, get_sorted_child_nodes_default, childNodesCache, returnFalse = () => false, isAllEmptyAndNoLineBreak = (text) => !/[\S\n\u2028\u2029]/u.test(text), is_previous_line_empty_default, breakParent2, hardline2, indent2, join32, line22, lineSuffix2, create_print_pre_check_function_default = () => noop_default2, core_options_evaluate_default, arrayToReversed, toReversed, method_to_reversed_default, import_n_readlines, get_interpreter_default, getFileBasename = (file) => {
146444
+ ` || character === "\r" || character === "\u2028" || character === "\u2029", skip_newline_default, has_newline_default, is_non_empty_array_default, get_sorted_child_nodes_default, childNodesCache, returnFalse = () => false, isAllEmptyAndNoLineBreak = (text) => !/[\S\n\u2028\u2029]/u.test(text), is_previous_line_empty_default, breakParent2, hardline2, indent3, join32, line22, lineSuffix2, create_print_pre_check_function_default = () => noop_default2, core_options_evaluate_default, arrayToReversed, toReversed, method_to_reversed_default, import_n_readlines, get_interpreter_default, getFileBasename = (file) => {
146445
146445
  try {
146446
146446
  return path112.basename(toPath4(file));
146447
146447
  } catch {
@@ -156469,7 +156469,7 @@ ${codeblock}`, options8);
156469
156469
  ({
156470
156470
  breakParent: breakParent2,
156471
156471
  hardline: hardline2,
156472
- indent: indent2,
156472
+ indent: indent3,
156473
156473
  join: join32,
156474
156474
  line: line22,
156475
156475
  lineSuffix: lineSuffix2
@@ -268401,6 +268401,10 @@ async function captureRequestBody(request, options) {
268401
268401
  if (request.body == null) {
268402
268402
  return;
268403
268403
  }
268404
+ if (options.context.__redactBody) {
268405
+ options.context.__requestBody = "[redacted]";
268406
+ return;
268407
+ }
268404
268408
  try {
268405
268409
  const cloned = request.clone();
268406
268410
  const text = await cloned.text();
@@ -269450,20 +269454,490 @@ function getAgentsCommand() {
269450
269454
  return new Command2("agents").description("Manage project agents").addCommand(getAgentsPushCommand()).addCommand(getAgentsPullCommand());
269451
269455
  }
269452
269456
 
269457
+ // src/cli/commands/auth/password-login.ts
269458
+ import { dirname as dirname15, join as join21 } from "node:path";
269459
+ async function passwordLoginAction({ log, runTask }, action) {
269460
+ const shouldEnable = action === "enable";
269461
+ const { project } = await readProjectConfig();
269462
+ const configDir = dirname15(project.configPath);
269463
+ const authDir = join21(configDir, project.authDir);
269464
+ const updated = await runTask("Updating local auth config", async () => {
269465
+ const current = await readAuthConfig(authDir) ?? DEFAULT_AUTH_CONFIG;
269466
+ const merged = { ...current, enableUsernamePassword: shouldEnable };
269467
+ await writeAuthConfig(authDir, merged);
269468
+ return merged;
269469
+ });
269470
+ if (!shouldEnable && !hasAnyLoginMethod(updated)) {
269471
+ log.warn("Disabling password auth will leave no login methods enabled. Users will be locked out.");
269472
+ }
269473
+ const newStatus = shouldEnable ? "enabled" : "disabled";
269474
+ return {
269475
+ outroMessage: `Username & password authentication ${newStatus} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
269476
+ };
269477
+ }
269478
+ function getPasswordLoginCommand() {
269479
+ return new Base44Command("password-login").description("Enable or disable username & password authentication").addArgument(new Argument2("<action>", "enable or disable password authentication").choices(["enable", "disable"])).action(passwordLoginAction);
269480
+ }
269481
+
269482
+ // src/cli/commands/auth/pull.ts
269483
+ import { dirname as dirname16, join as join22 } from "node:path";
269484
+ async function pullAuthAction({
269485
+ log,
269486
+ runTask
269487
+ }) {
269488
+ const { project } = await readProjectConfig();
269489
+ const configDir = dirname16(project.configPath);
269490
+ const authDir = join22(configDir, project.authDir);
269491
+ const remoteConfig = await runTask("Fetching auth config from Base44", async () => {
269492
+ return await pullAuthConfig();
269493
+ }, {
269494
+ successMessage: "Auth config fetched successfully",
269495
+ errorMessage: "Failed to fetch auth config"
269496
+ });
269497
+ const { written } = await runTask("Syncing auth config file", async () => {
269498
+ return await writeAuthConfig(authDir, remoteConfig);
269499
+ }, {
269500
+ successMessage: "Auth config file synced successfully",
269501
+ errorMessage: "Failed to sync auth config file"
269502
+ });
269503
+ if (written) {
269504
+ log.success("Auth config written to local file");
269505
+ } else {
269506
+ log.info("Auth config is already up to date");
269507
+ }
269508
+ return {
269509
+ outroMessage: `Pulled auth config to ${authDir} (overwrites local file)`
269510
+ };
269511
+ }
269512
+ function getAuthPullCommand() {
269513
+ return new Base44Command("pull").description("Pull auth config from Base44 to local file").action(pullAuthAction);
269514
+ }
269515
+
269516
+ // src/cli/commands/auth/push.ts
269517
+ async function pushAuthAction({ isNonInteractive, log, runTask }, options) {
269518
+ const { authConfig } = await readProjectConfig();
269519
+ if (authConfig.length === 0) {
269520
+ log.info("No local auth config found");
269521
+ return {
269522
+ outroMessage: "No auth config to push. Run `base44 auth pull` to fetch the remote config first."
269523
+ };
269524
+ }
269525
+ if (!hasAnyLoginMethod(authConfig[0])) {
269526
+ log.warn("This config has no login methods enabled. Pushing it will lock out all users.");
269527
+ }
269528
+ if (!options.yes) {
269529
+ if (isNonInteractive) {
269530
+ throw new InvalidInputError("--yes is required in non-interactive mode");
269531
+ }
269532
+ const shouldPush = await Re({
269533
+ message: "Push auth config to Base44?"
269534
+ });
269535
+ if (Ct(shouldPush) || !shouldPush) {
269536
+ return { outroMessage: "Push cancelled" };
269537
+ }
269538
+ }
269539
+ await runTask("Pushing auth config to Base44", async () => {
269540
+ return await pushAuthConfig(authConfig[0] ?? null);
269541
+ }, {
269542
+ successMessage: "Auth config pushed successfully",
269543
+ errorMessage: "Failed to push auth config"
269544
+ });
269545
+ return {
269546
+ outroMessage: "Auth config pushed to Base44"
269547
+ };
269548
+ }
269549
+ function getAuthPushCommand() {
269550
+ return new Base44Command("push").description("Push local auth config to Base44").option("-y, --yes", "Skip confirmation prompt").action(pushAuthAction);
269551
+ }
269552
+
269553
+ // src/cli/commands/auth/social-login.ts
269554
+ import { dirname as dirname17, join as join23, resolve as resolve6 } from "node:path";
269555
+ var PROVIDER_LABELS = {
269556
+ google: "Google",
269557
+ microsoft: "Microsoft",
269558
+ facebook: "Facebook",
269559
+ apple: "Apple"
269560
+ };
269561
+ var VALID_PROVIDER_NAMES = Object.keys(SOCIAL_PROVIDERS);
269562
+ var PROVIDER_OAUTH_CLI = {
269563
+ google: {
269564
+ envVar: "google_oauth_client_secret",
269565
+ promptMessage: "Enter Google OAuth client secret"
269566
+ }
269567
+ };
269568
+ function hasSecretOptions(options) {
269569
+ return Boolean(options.clientSecret || options.clientSecretStdin || options.envFile);
269570
+ }
269571
+ function hasCustomOAuthOptions(options) {
269572
+ return Boolean(options.clientId || hasSecretOptions(options));
269573
+ }
269574
+ async function socialLoginAction({ log, isNonInteractive, runTask }, provider, action, options) {
269575
+ const shouldEnable = action === "enable";
269576
+ const providerInfo = SOCIAL_PROVIDERS[provider];
269577
+ const label = PROVIDER_LABELS[provider];
269578
+ const hasOAuthOptions = hasCustomOAuthOptions(options);
269579
+ if (hasOAuthOptions && !providerInfo.customOAuth) {
269580
+ throw new InvalidInputError(`Custom OAuth options are only supported for providers with custom OAuth (e.g., google). Use: base44 auth social-login ${provider} ${action}`);
269581
+ }
269582
+ if (hasOAuthOptions && !shouldEnable) {
269583
+ throw new InvalidInputError(`Custom OAuth options cannot be used with disable. To disable ${label} login: base44 auth social-login ${provider} disable`);
269584
+ }
269585
+ if (hasSecretOptions(options) && !options.clientId) {
269586
+ throw new InvalidInputError(`--client-id is required when providing a client secret. Use: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`);
269587
+ }
269588
+ const oauth = providerInfo.customOAuth;
269589
+ const oauthCli = PROVIDER_OAUTH_CLI[provider];
269590
+ const useCustomOAuth = shouldEnable && hasOAuthOptions && oauth != null;
269591
+ let clientSecret;
269592
+ if (useCustomOAuth && oauth && oauthCli && hasSecretOptions(options)) {
269593
+ if (options.envFile) {
269594
+ const secrets = await parseEnvFile(resolve6(options.envFile));
269595
+ const value = secrets[oauthCli.envVar];
269596
+ if (!value) {
269597
+ throw new InvalidInputError(`Key "${oauthCli.envVar}" not found in ${options.envFile}.`);
269598
+ }
269599
+ clientSecret = value;
269600
+ } else {
269601
+ clientSecret = await resolveSecret({
269602
+ flagValue: options.clientSecret,
269603
+ fromStdin: options.clientSecretStdin,
269604
+ envVar: oauthCli.envVar,
269605
+ promptMessage: oauthCli.promptMessage,
269606
+ isNonInteractive,
269607
+ name: "client secret",
269608
+ hints: [
269609
+ {
269610
+ message: `Provide via flag: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`,
269611
+ command: `base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`
269612
+ },
269613
+ {
269614
+ message: `Provide via stdin: echo <secret> | base44 auth social-login ${provider} enable --client-id <id> --client-secret-stdin`
269615
+ },
269616
+ {
269617
+ message: `Provide via env: ${oauthCli.envVar}=<secret> base44 auth social-login ${provider} enable --client-id <id>`
269618
+ }
269619
+ ]
269620
+ });
269621
+ }
269622
+ }
269623
+ const { project } = await readProjectConfig();
269624
+ const configDir = dirname17(project.configPath);
269625
+ const authDir = join23(configDir, project.authDir);
269626
+ const { config: updated } = await runTask("Updating local auth config", async () => updateSocialLoginConfig(authDir, provider, shouldEnable, useCustomOAuth && options.clientId ? { clientId: options.clientId } : undefined));
269627
+ if (clientSecret) {
269628
+ await runTask("Saving client secret", async () => pushCustomOAuthSecret(provider, clientSecret));
269629
+ }
269630
+ if (!shouldEnable && !hasAnyLoginMethod(updated)) {
269631
+ log.warn(`Disabling ${label} login will leave no login methods enabled. Users will be locked out.`);
269632
+ }
269633
+ const newStatus = shouldEnable ? "enabled" : "disabled";
269634
+ const oauthNote = useCustomOAuth ? " with custom OAuth" : "";
269635
+ let outroMessage = `${label} login ${newStatus}${oauthNote} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`;
269636
+ if (useCustomOAuth && !clientSecret) {
269637
+ outroMessage += `
269638
+ Remember to push the client secret separately: base44 secrets set --env-file <path>`;
269639
+ }
269640
+ return { outroMessage };
269641
+ }
269642
+ function getSocialLoginCommand() {
269643
+ return new Base44Command("social-login").description("Enable or disable social login providers (google, microsoft, facebook, apple)").addArgument(new Argument2("<provider>", "social login provider").choices(VALID_PROVIDER_NAMES)).addArgument(new Argument2("<action>", "enable or disable the provider").choices([
269644
+ "enable",
269645
+ "disable"
269646
+ ])).option("--client-id <id>", "custom OAuth client ID (Google only)").option("--client-secret <secret>", "custom OAuth client secret (Google only)").option("--client-secret-stdin", "read client secret from stdin (Google only)").option("--env-file <path>", "read client secret from a .env file (Google only)").action(socialLoginAction);
269647
+ }
269648
+
269649
+ // src/cli/commands/auth/sso.ts
269650
+ import { dirname as dirname18, join as join24, resolve as resolve7 } from "node:path";
269651
+ var SSOConfigFileSchema = object({
269652
+ provider: _enum(Object.values(KNOWN_SSO_PROVIDERS)),
269653
+ clientId: string2(),
269654
+ clientSecret: string2(),
269655
+ scope: string2().optional(),
269656
+ discoveryUrl: string2().optional(),
269657
+ tenantId: string2().optional(),
269658
+ oktaDomain: string2().optional(),
269659
+ authEndpoint: string2().optional(),
269660
+ tokenEndpoint: string2().optional(),
269661
+ userinfoEndpoint: string2().optional(),
269662
+ jwksUri: string2().optional(),
269663
+ ssoName: string2().optional()
269664
+ });
269665
+ async function loadSSOConfigFile(filePath) {
269666
+ const resolved = resolve7(filePath);
269667
+ const raw = await readJsonFile(resolved);
269668
+ const result = SSOConfigFileSchema.safeParse(raw);
269669
+ if (!result.success) {
269670
+ throw new SchemaValidationError("Invalid SSO config file", result.error, filePath);
269671
+ }
269672
+ return result.data;
269673
+ }
269674
+ function mergeFileWithFlags(fileConfig, options) {
269675
+ return {
269676
+ provider: options.provider ?? fileConfig.provider,
269677
+ clientId: options.clientId ?? fileConfig.clientId,
269678
+ clientSecret: options.clientSecret ?? fileConfig.clientSecret,
269679
+ clientSecretStdin: options.clientSecretStdin,
269680
+ envFile: options.envFile,
269681
+ scope: options.scope ?? fileConfig.scope,
269682
+ discoveryUrl: options.discoveryUrl ?? fileConfig.discoveryUrl,
269683
+ tenantId: options.tenantId ?? fileConfig.tenantId,
269684
+ oktaDomain: options.oktaDomain ?? fileConfig.oktaDomain,
269685
+ authEndpoint: options.authEndpoint ?? fileConfig.authEndpoint,
269686
+ tokenEndpoint: options.tokenEndpoint ?? fileConfig.tokenEndpoint,
269687
+ userinfoEndpoint: options.userinfoEndpoint ?? fileConfig.userinfoEndpoint,
269688
+ jwksUri: options.jwksUri ?? fileConfig.jwksUri,
269689
+ ssoName: options.ssoName ?? fileConfig.ssoName
269690
+ };
269691
+ }
269692
+ var providerNames = Object.keys(KNOWN_SSO_PROVIDERS);
269693
+ var SECRET_KEY_TO_FLAG = {
269694
+ ["sso_name" /* Name */]: "--sso-name",
269695
+ ["sso_client_id" /* ClientId */]: "--client-id",
269696
+ ["sso_client_secret" /* ClientSecret */]: "--client-secret",
269697
+ ["sso_scope" /* Scope */]: "--scope",
269698
+ ["sso_discovery_url" /* DiscoveryUrl */]: "--discovery-url",
269699
+ ["sso_tenant_id" /* TenantId */]: "--tenant-id",
269700
+ ["sso_auth_endpoint" /* AuthEndpoint */]: "--auth-endpoint",
269701
+ ["sso_token_endpoint" /* TokenEndpoint */]: "--token-endpoint",
269702
+ ["sso_userinfo_endpoint" /* UserinfoEndpoint */]: "--userinfo-endpoint",
269703
+ ["sso_okta_domain" /* OktaDomain */]: "--okta-domain",
269704
+ ["sso_jwks_uri" /* JwksUri */]: "--jwks-uri"
269705
+ };
269706
+ function secretKeyToFlag(key) {
269707
+ return SECRET_KEY_TO_FLAG[key];
269708
+ }
269709
+ function exampleCommand(provider) {
269710
+ let cmd = `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`;
269711
+ if (provider === KNOWN_SSO_PROVIDERS.microsoft)
269712
+ cmd += " --tenant-id <id>";
269713
+ if (provider === KNOWN_SSO_PROVIDERS.okta)
269714
+ cmd += " --okta-domain <domain>";
269715
+ if (provider === KNOWN_SSO_PROVIDERS.custom)
269716
+ cmd += " --sso-name <name> --auth-endpoint <url> --token-endpoint <url> --userinfo-endpoint <url> --jwks-uri <url>";
269717
+ return cmd;
269718
+ }
269719
+ function validateProvider(provider) {
269720
+ if (!provider) {
269721
+ throw new InvalidInputError("Missing --provider.", {
269722
+ hints: [
269723
+ {
269724
+ message: `Valid providers: ${providerNames.join(", ")}`,
269725
+ command: "base44 auth sso enable --provider <provider> --client-id <id> --client-secret <secret>"
269726
+ }
269727
+ ]
269728
+ });
269729
+ }
269730
+ return provider;
269731
+ }
269732
+ async function ssoEnableAction({ isNonInteractive, runTask }, options) {
269733
+ if (options.file && options.envFile) {
269734
+ throw new InvalidInputError("--file and --env-file cannot be used together. Provide the client secret either inside --file or via --env-file.");
269735
+ }
269736
+ let merged = options;
269737
+ if (options.file) {
269738
+ const fileConfig = await loadSSOConfigFile(options.file);
269739
+ merged = mergeFileWithFlags(fileConfig, options);
269740
+ }
269741
+ const provider = validateProvider(merged.provider);
269742
+ if (!merged.clientId) {
269743
+ throw new InvalidInputError("Missing --client-id.", {
269744
+ hints: [
269745
+ {
269746
+ message: `Example: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
269747
+ command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
269748
+ }
269749
+ ]
269750
+ });
269751
+ }
269752
+ let clientSecret;
269753
+ if (merged.envFile && !merged.clientSecret) {
269754
+ const secrets = await parseEnvFile(resolve7(merged.envFile));
269755
+ const value = secrets.sso_client_secret;
269756
+ if (!value) {
269757
+ throw new InvalidInputError(`Key "sso_client_secret" not found in ${merged.envFile}.`);
269758
+ }
269759
+ clientSecret = value;
269760
+ } else {
269761
+ clientSecret = await resolveSecret({
269762
+ flagValue: merged.clientSecret,
269763
+ fromStdin: merged.clientSecretStdin,
269764
+ envVar: "sso_client_secret",
269765
+ promptMessage: "Enter SSO client secret",
269766
+ isNonInteractive,
269767
+ name: "client secret",
269768
+ hints: [
269769
+ {
269770
+ message: `Provide via flag: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
269771
+ command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
269772
+ },
269773
+ {
269774
+ message: `Provide via stdin: echo <secret> | base44 auth sso enable --provider ${provider} --client-id <id> --client-secret-stdin`
269775
+ },
269776
+ {
269777
+ message: `Provide via env: sso_client_secret=<secret> base44 auth sso enable --provider ${provider} --client-id <id>`
269778
+ }
269779
+ ]
269780
+ });
269781
+ }
269782
+ const secretOptions = {
269783
+ clientId: merged.clientId,
269784
+ clientSecret,
269785
+ scope: merged.scope,
269786
+ discoveryUrl: merged.discoveryUrl,
269787
+ tenantId: merged.tenantId,
269788
+ oktaDomain: merged.oktaDomain,
269789
+ authEndpoint: merged.authEndpoint,
269790
+ tokenEndpoint: merged.tokenEndpoint,
269791
+ userinfoEndpoint: merged.userinfoEndpoint,
269792
+ jwksUri: merged.jwksUri,
269793
+ ssoName: merged.ssoName
269794
+ };
269795
+ let secrets;
269796
+ try {
269797
+ secrets = buildSSOSecrets(provider, secretOptions);
269798
+ } catch (error) {
269799
+ if (error instanceof MissingSSOFieldsError) {
269800
+ const flagNames = error.missingKeys.map(secretKeyToFlag);
269801
+ throw new InvalidInputError(`Missing required fields for ${error.provider}: ${flagNames.join(", ")}`, {
269802
+ hints: [
269803
+ {
269804
+ message: `Example: ${exampleCommand(provider)}`,
269805
+ command: exampleCommand(provider)
269806
+ }
269807
+ ]
269808
+ });
269809
+ }
269810
+ throw error;
269811
+ }
269812
+ const { project } = await readProjectConfig();
269813
+ const configDir = dirname18(project.configPath);
269814
+ const authDir = join24(configDir, project.authDir);
269815
+ await runTask("Updating local auth config", async () => updateSSOConfig(authDir, provider, true));
269816
+ await runTask("Saving SSO credentials", async () => pushSSOSecrets(secrets));
269817
+ return {
269818
+ outroMessage: `SSO configured with ${provider} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
269819
+ };
269820
+ }
269821
+ function hasEnableOnlyOptions(options) {
269822
+ return Boolean(options.provider || options.clientId || options.clientSecret || options.clientSecretStdin || options.envFile || options.file || options.scope || options.discoveryUrl || options.tenantId || options.oktaDomain || options.authEndpoint || options.tokenEndpoint || options.userinfoEndpoint || options.jwksUri || options.ssoName);
269823
+ }
269824
+ async function ssoDisableAction({ log, runTask }, options) {
269825
+ if (hasEnableOnlyOptions(options)) {
269826
+ throw new InvalidInputError("Configuration options cannot be used with disable. To disable SSO: base44 auth sso disable");
269827
+ }
269828
+ const { project } = await readProjectConfig();
269829
+ const configDir = dirname18(project.configPath);
269830
+ const authDir = join24(configDir, project.authDir);
269831
+ const updated = await runTask("Updating local auth config", async () => updateSSOConfig(authDir, null, false));
269832
+ await runTask("Removing SSO credentials", async () => deleteSSOSecrets());
269833
+ if (!hasAnyLoginMethod(updated)) {
269834
+ log.warn("Disabling SSO will leave no login methods enabled. Users will be locked out.");
269835
+ }
269836
+ return {
269837
+ outroMessage: "SSO disabled in local config and credentials removed. Run `base44 auth push` or `base44 deploy` to apply."
269838
+ };
269839
+ }
269840
+ async function ssoAction(context, action, options) {
269841
+ if (action === "disable") {
269842
+ return ssoDisableAction(context, options);
269843
+ }
269844
+ return ssoEnableAction(context, options);
269845
+ }
269846
+ function getSSOCommand() {
269847
+ return new Base44Command("sso").description("Configure SSO identity provider (google, microsoft, github, okta, custom). SSO and social login are mutually exclusive — enabling one disables the other in the local auth config.").addArgument(new Argument2("<action>", "enable or disable SSO").choices([
269848
+ "enable",
269849
+ "disable"
269850
+ ])).addOption(new Option2("--provider <provider>", "SSO provider").choices(Object.values(KNOWN_SSO_PROVIDERS))).option("--client-id <id>", "OAuth client ID").option("--client-secret <secret>", "OAuth client secret").option("--client-secret-stdin", "Read client secret from stdin").option("--env-file <path>", "Read client secret from a .env file (key: sso_client_secret)").option("--file <path>", "JSON config file with all SSO settings").option("--scope <scope>", "OAuth scope (defaults per provider)").option("--discovery-url <url>", "OIDC discovery URL").option("--tenant-id <id>", "Microsoft tenant ID (required for microsoft)").option("--okta-domain <domain>", "Okta domain (required for okta)").option("--auth-endpoint <url>", "Authorization endpoint (required for custom)").option("--token-endpoint <url>", "Token endpoint (required for custom)").option("--userinfo-endpoint <url>", "Userinfo endpoint (required for custom)").option("--jwks-uri <url>", "JWKS URI (required for custom)").option("--sso-name <name>", "Provider display name (required for custom)").action(ssoAction);
269851
+ }
269852
+
269853
+ // src/cli/commands/auth/index.ts
269854
+ function getAuthCommand() {
269855
+ return new Command2("auth").description("Manage app authentication settings").addCommand(getPasswordLoginCommand()).addCommand(getSocialLoginCommand()).addCommand(getSSOCommand()).addCommand(getAuthPullCommand()).addCommand(getAuthPushCommand());
269856
+ }
269857
+
269858
+ // src/cli/commands/auth/login.ts
269859
+ function getLoginCommand() {
269860
+ return new Base44Command("login", {
269861
+ requireAuth: false,
269862
+ requireAppContext: false
269863
+ }).description("Authenticate with Base44").action(login);
269864
+ }
269865
+
269866
+ // src/cli/commands/auth/logout.ts
269867
+ async function logout(_ctx) {
269868
+ await deleteAuth();
269869
+ return { outroMessage: "Logged out successfully" };
269870
+ }
269871
+ function getLogoutCommand() {
269872
+ return new Base44Command("logout", {
269873
+ requireAuth: false,
269874
+ requireAppContext: false
269875
+ }).description("Logout from current device").action(logout);
269876
+ }
269877
+
269878
+ // src/cli/commands/auth/whoami.ts
269879
+ async function whoami(_ctx) {
269880
+ const workspaceApiKey = getWorkspaceApiKeyFromEnv();
269881
+ if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
269882
+ return {
269883
+ outroMessage: `Using workspace API key: ${theme.styles.bold(workspaceApiKey.slice(0, 10))}`
269884
+ };
269885
+ }
269886
+ const auth = await readAuth();
269887
+ return { outroMessage: `Logged in as: ${theme.styles.bold(auth.email)}` };
269888
+ }
269889
+ function getWhoamiCommand() {
269890
+ return new Base44Command("whoami", { requireAppContext: false }).description("Display current authenticated user").action(whoami);
269891
+ }
269892
+
269893
+ // src/cli/commands/branches/index.ts
269894
+ async function listBranchesAction({
269895
+ log,
269896
+ runTask,
269897
+ jsonMode
269898
+ }) {
269899
+ const remote = await runTask("Fetching branches", () => listBranches());
269900
+ const branches = [
269901
+ { name: "main", status: "active" },
269902
+ ...remote.map((branch) => ({
269903
+ name: branch.branch_name,
269904
+ status: branch.status
269905
+ }))
269906
+ ];
269907
+ if (jsonMode)
269908
+ return { stdout: `${JSON.stringify({ branches })}
269909
+ ` };
269910
+ for (const branch of branches)
269911
+ log.message(`${branch.name} (${branch.status})`);
269912
+ return { outroMessage: `${branches.length} branches` };
269913
+ }
269914
+ function getBranchesCommand() {
269915
+ return new Command2("branches").description("Discover an app's branches").addCommand(new Base44Command("list").description("List main and active branch names for use with --branch").action(listBranchesAction));
269916
+ }
269917
+
269453
269918
  // src/core/model.ts
269454
269919
  var MODELS = [
269455
- { name: "default", id: null, note: "let Base44 choose" },
269920
+ {
269921
+ name: "Automatic",
269922
+ id: null,
269923
+ note: "matched with the best model",
269924
+ aliases: ["default", "auto"]
269925
+ },
269456
269926
  { name: "Opus 5", id: "claude_opus_5" },
269457
269927
  { name: "Sonnet 5", id: "claude-sonnet-5" },
269458
269928
  { name: "Fable 5", id: "claude_fable_5", note: "uses more credits" },
269459
269929
  { name: "GPT-5.6 Sol", id: "gpt_5_6_sol" },
269460
- { name: "Gemini 3.8 Flash", id: "gemini_3_8_flash", note: "fast" },
269930
+ {
269931
+ name: "Gemini 3.8 Flash",
269932
+ id: "gemini_3_8_flash",
269933
+ note: "fast responses for everyday tasks"
269934
+ },
269461
269935
  { name: "Base 1", id: "base1" }
269462
269936
  ];
269463
269937
  var fold = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
269464
269938
  function resolvePick(input) {
269465
269939
  const key = fold(input);
269466
- const byExact = MODELS.find((m) => fold(m.name) === key || m.id && fold(m.id) === key);
269940
+ const byExact = MODELS.find((m) => fold(m.name) === key || m.id && fold(m.id) === key || m.aliases?.some((a) => fold(a) === key));
269467
269941
  if (byExact)
269468
269942
  return byExact;
269469
269943
  const byContains = MODELS.filter((m) => fold(m.name).includes(key) || m.id && fold(m.id).includes(key));
@@ -269486,14 +269960,14 @@ async function saveBuilderModel(userId, modelId) {
269486
269960
  });
269487
269961
  } catch (error) {
269488
269962
  if (error instanceof HTTPError && error.response.status === 400) {
269489
- throw new InvalidInputError("This account can't pick a builder model yet — enable the PER_USER_BUILDER_MODEL_SELECTION flag for it in PostHog, or the model isn't available in this workspace.");
269963
+ throw new InvalidInputError("This account can't pick a builder model yet, or the model isn't available in this workspace. Ask your workspace admin to enable model selection.");
269490
269964
  }
269491
269965
  throw await ApiError.fromHttpError(error, "saving your model choice");
269492
269966
  }
269493
269967
  }
269494
- var displayName = (id) => MODELS.find((m) => m.id === id)?.name ?? id ?? "default";
269968
+ var displayName = (id) => MODELS.find((m) => m.id === id)?.name ?? id ?? "Automatic";
269495
269969
 
269496
- // src/cli/commands/app/model.ts
269970
+ // src/cli/commands/builder/model.ts
269497
269971
  async function modelAction({ log, jsonMode }, input) {
269498
269972
  const me = await getMe();
269499
269973
  const current = me.builder_model ?? null;
@@ -269514,7 +269988,7 @@ async function modelAction({ log, jsonMode }, input) {
269514
269988
  log.message(`${marker} ${active ? theme.styles.bold(m.name) : m.name}${note}`);
269515
269989
  }
269516
269990
  return {
269517
- outroMessage: `Current: ${theme.styles.bold(displayName(current))}. Set with \`base44 app model <name>\`.`
269991
+ outroMessage: `Current: ${theme.styles.bold(displayName(current))}. Set with \`base44 builder model <name>\`.`
269518
269992
  };
269519
269993
  }
269520
269994
  const pick = resolvePick(input);
@@ -269535,9 +270009,9 @@ function getModelCommand() {
269535
270009
  return command;
269536
270010
  }
269537
270011
 
269538
- // src/cli/commands/app/shared.ts
270012
+ // src/cli/commands/builder/shared.ts
269539
270013
  import { mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
269540
- import { join as join21 } from "node:path";
270014
+ import { basename as basename6, join as join25, relative as relative6, resolve as resolve8 } from "node:path";
269541
270015
 
269542
270016
  // src/cli/commands/code/render.ts
269543
270017
  var TOOL_ALIASES = {
@@ -269545,6 +270019,10 @@ var TOOL_ALIASES = {
269545
270019
  read_repo_file: "read",
269546
270020
  write_repo_file: "write",
269547
270021
  edit_repo_file: "edit",
270022
+ read_file: "read",
270023
+ write_file: "write",
270024
+ find_replace: "edit",
270025
+ delete_file: "delete",
269548
270026
  set_secrets: "secrets",
269549
270027
  generate_development_secrets: "secrets",
269550
270028
  create_pull_request: "pr",
@@ -269558,7 +270036,55 @@ var TOOL_ALIASES = {
269558
270036
  preview_screenshot: "screenshot",
269559
270037
  connect_github_account: "github"
269560
270038
  };
269561
- var QUIET_OK_RESULTS = new Set(["read", "write", "edit", "reload"]);
270039
+ var QUIET_OK_RESULTS = new Set(["read", "write", "edit", "reload", "delete"]);
270040
+ var RESULT_LINE_MAX = 110;
270041
+ var DIFF_LINES_MAX = 12;
270042
+ var ERROR_LINES_MAX = 8;
270043
+ function renderEntry(entry, options = {}) {
270044
+ if (typeof entry === "string")
270045
+ return entry;
270046
+ if ("running" in entry)
270047
+ return runningLine(entry.running, entry.startedAt);
270048
+ return eventLine(entry.event, entry.elapsedMs, options) ?? "";
270049
+ }
270050
+ var RUNNING_DOT = "#E86B3C";
270051
+ var PULSE_MS = 500;
270052
+ function runningLine(event, startedAt, now = Date.now()) {
270053
+ const lit = Math.floor(now / PULSE_MS) % 2 === 0;
270054
+ const dot = lit ? source_default.hex(RUNNING_DOT)("●") : source_default.dim("●");
270055
+ const alias = toolAlias(event.name);
270056
+ const title = source_default.bold(event.label || alias);
270057
+ const inline = !event.label && event.summary ? ` ${source_default.dim(event.summary)}` : "";
270058
+ const seconds = Math.round((now - startedAt) / 1000);
270059
+ const took = seconds >= 3 ? ` ${source_default.dim(`· ${seconds}s`)}` : "";
270060
+ const detail = event.label && event.summary ? `
270061
+ ${source_default.dim(`└ ${alias}: ${event.summary}`)}` : "";
270062
+ return `${dot} ${title}${inline}${took}${detail}`;
270063
+ }
270064
+ function fold2(lines, max, options) {
270065
+ if (options.verbose || lines.length <= max)
270066
+ return lines;
270067
+ const hint = options.foldHint ? ` (${options.foldHint})` : "";
270068
+ return [
270069
+ ...lines.slice(0, max),
270070
+ source_default.dim(`… +${lines.length - max} lines${hint}`)
270071
+ ];
270072
+ }
270073
+ var str = (v) => typeof v === "string" ? v : undefined;
270074
+ function diffLines(args) {
270075
+ const find = str(args.find);
270076
+ const replace = str(args.replace);
270077
+ if (find == null && replace == null)
270078
+ return null;
270079
+ return [
270080
+ ...(find ?? "").split(`
270081
+ `).map((l) => source_default.red(`- ${l}`)),
270082
+ ...(replace ?? "").split(`
270083
+ `).map((l) => source_default.green(`+ ${l}`))
270084
+ ];
270085
+ }
270086
+ var indent = (lines) => lines.map((l) => ` ${l}`).join(`
270087
+ `);
269562
270088
  function toolAlias(name) {
269563
270089
  return TOOL_ALIASES[name] ?? name;
269564
270090
  }
@@ -269623,7 +270149,7 @@ function formatDuration2(ms) {
269623
270149
  return `${seconds}s`;
269624
270150
  return `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
269625
270151
  }
269626
- function eventLine(event, elapsedMs) {
270152
+ function eventLine(event, elapsedMs, options = {}) {
269627
270153
  switch (event.kind) {
269628
270154
  case "thinking":
269629
270155
  return source_default.dim(`✻ ${event.text}`);
@@ -269633,7 +270159,7 @@ function eventLine(event, elapsedMs) {
269633
270159
  return null;
269634
270160
  case "waiting": {
269635
270161
  const what = event.label || toolAlias(event.name);
269636
- return source_default.yellow(`⏸ ${what} — needs your input (answer in the editor)`);
270162
+ return source_default.yellow(`⏸ ${what} — needs your input (${options.waitingHint ?? "answer in the editor"})`);
269637
270163
  }
269638
270164
  case "tool_end": {
269639
270165
  const alias = toolAlias(event.name);
@@ -269644,16 +270170,52 @@ function eventLine(event, elapsedMs) {
269644
270170
  const paramsLine = event.label && event.summary ? `
269645
270171
  ${source_default.dim(`${alias}: ${event.summary}`)}` : "";
269646
270172
  const head = `${mark} ${title}${inlineDetail}${took}${paramsLine}`;
270173
+ const args = event.args ?? null;
270174
+ if (event.ok && alias === "edit" && args) {
270175
+ const diff = diffLines(args);
270176
+ if (diff)
270177
+ return `${head}
270178
+ ${indent(fold2(diff, DIFF_LINES_MAX, options))}`;
270179
+ }
270180
+ if (event.ok && alias === "write" && args && str(args.content) != null) {
270181
+ const lines = str(args.content).split(`
270182
+ `);
270183
+ const count = source_default.dim(`+${lines.length} lines`);
270184
+ if (!options.verbose)
270185
+ return `${head} ${count}`;
270186
+ return `${head} ${count}
270187
+ ${indent(lines.map((l) => source_default.dim(l)))}`;
270188
+ }
269647
270189
  if (event.ok && (QUIET_OK_RESULTS.has(alias) || !event.result)) {
269648
270190
  return head;
269649
270191
  }
269650
- const result = event.ok ? source_default.dim(linkifyUrls(event.result)) : source_default.red(linkifyUrls(event.result));
269651
- return `${head}${event.result ? `
269652
- ${result}` : ""}`;
270192
+ const paint = event.ok ? source_default.dim : source_default.red;
270193
+ const lines = event.result.split(`
270194
+ `).map((l) => l.trimEnd()).filter((l, i, all) => l.length > 0 || i > 0 && i < all.length - 1);
270195
+ if (event.ok && !options.verbose) {
270196
+ const first = lines[0] ?? "";
270197
+ const cut = first.length > RESULT_LINE_MAX ? `${first.slice(0, RESULT_LINE_MAX)}…` : first;
270198
+ const more = lines.length - 1;
270199
+ const hint = options.foldHint ? ` (${options.foldHint})` : "";
270200
+ const tail = more > 0 ? source_default.dim(` … +${more} lines${hint}`) : "";
270201
+ return `${head}
270202
+ ${paint(linkifyUrls(cut))}${tail}`;
270203
+ }
270204
+ const shown = fold2(lines.map((l) => paint(linkifyUrls(l))), event.ok ? Number.POSITIVE_INFINITY : ERROR_LINES_MAX, options);
270205
+ return `${head}
270206
+ ${indent(shown)}`;
269653
270207
  }
269654
270208
  }
269655
270209
  }
269656
270210
  var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
270211
+ var SHIMMER = ["·", "✢", "✳", "✶", "✻", "✽"];
270212
+ var SHIMMER_STEP_MS = 120;
270213
+ function shimmer(now = Date.now()) {
270214
+ const period = SHIMMER.length * 2 - 2;
270215
+ const step = Math.floor(now / SHIMMER_STEP_MS) % period;
270216
+ const index = step < SHIMMER.length ? step : period - step;
270217
+ return SHIMMER[index];
270218
+ }
269657
270219
  var MUSINGS = [
269658
270220
  "Shmoozing",
269659
270221
  "Shmoogling",
@@ -269717,7 +270279,7 @@ function createTurnStream(interactive, write = (text) => process.stdout.write(te
269717
270279
  return;
269718
270280
  const lines = [
269719
270281
  ...footer.length ? ["", ...footer] : [],
269720
- source_default.dim(`${FRAMES[frame]} ${statusLabel()}`)
270282
+ running.size === 0 ? `${source_default.magenta(shimmer())} ${source_default.dim(statusLabel())}` : source_default.dim(`${FRAMES[frame]} ${statusLabel()}`)
269721
270283
  ];
269722
270284
  write(lines.join(`
269723
270285
  `));
@@ -269755,7 +270317,10 @@ function createTurnStream(interactive, write = (text) => process.stdout.write(te
269755
270317
  elapsedMs = Date.now() - started;
269756
270318
  running.delete(event.id);
269757
270319
  }
269758
- const line = eventLine(event, elapsedMs);
270320
+ const line = eventLine(event, elapsedMs, {
270321
+ verbose: options.verbose,
270322
+ foldHint: "--verbose shows everything"
270323
+ });
269759
270324
  if (line == null)
269760
270325
  return;
269761
270326
  if (interactive) {
@@ -269790,8 +270355,15 @@ var CreatedAppSchema = object({
269790
270355
  name: string2().nullish(),
269791
270356
  imported_repo_url: string2().nullish()
269792
270357
  });
270358
+ var WixCreatedAppSchema = object({
270359
+ app_id: string2().min(1),
270360
+ client_creation_id: string2().min(1)
270361
+ });
269793
270362
  var AppStateSchema = object({
269794
270363
  id: string2(),
270364
+ app_type: string2().nullish(),
270365
+ is_managed_source_code: boolean2().nullish(),
270366
+ imported_repo_url: string2().nullish(),
269795
270367
  status: object({
269796
270368
  state: string2().nullish(),
269797
270369
  message: string2().nullish()
@@ -269826,7 +270398,8 @@ var ConversationMessageSchema = object({
269826
270398
  name: string2(),
269827
270399
  arguments_string: string2().nullish(),
269828
270400
  status: string2().nullish(),
269829
- results: unknown().nullish()
270401
+ results: unknown().nullish(),
270402
+ waiting_on: object({ kind: string2().nullish() }).nullish()
269830
270403
  })).nullish()
269831
270404
  });
269832
270405
  var FullConversationSchema = object({
@@ -269843,6 +270416,24 @@ function parseOrThrow(schema, payload, what) {
269843
270416
  function branchScope(branchId) {
269844
270417
  return branchId ? { branch_id: branchId } : {};
269845
270418
  }
270419
+ async function createWixLaunchedApp(options) {
270420
+ let response;
270421
+ try {
270422
+ response = await base44Client.post("api/wix/create-app", {
270423
+ timeout: false,
270424
+ context: { __redactBody: true },
270425
+ json: {
270426
+ prompt: options.prompt,
270427
+ signed_instance: options.signedInstance,
270428
+ ...options.wixClientId ? { wix_client_id: options.wixClientId } : {}
270429
+ }
270430
+ });
270431
+ } catch (error) {
270432
+ throw await ApiError.fromHttpError(error, "creating app via Wix launch");
270433
+ }
270434
+ const created = parseOrThrow(WixCreatedAppSchema, await response.json(), "app");
270435
+ return { id: created.app_id, client_creation_id: created.client_creation_id };
270436
+ }
269846
270437
  async function createApp(options) {
269847
270438
  let response;
269848
270439
  try {
@@ -269891,13 +270482,34 @@ async function getAppState(appId) {
269891
270482
  let response;
269892
270483
  try {
269893
270484
  response = await base44Client.get(`api/apps/${appId}`, {
269894
- searchParams: { fields: "id,status" }
270485
+ searchParams: {
270486
+ fields: "id,status,app_type,is_managed_source_code,imported_repo_url"
270487
+ }
269895
270488
  });
269896
270489
  } catch (error) {
269897
270490
  throw await ApiError.fromHttpError(error, "reading app status");
269898
270491
  }
269899
270492
  return parseOrThrow(AppStateSchema, await response.json(), "app status");
269900
270493
  }
270494
+ async function answerToolCall(options, branchId) {
270495
+ let response;
270496
+ try {
270497
+ response = await getAppClient().post("chat/submit-tool-call-input", {
270498
+ timeout: false,
270499
+ context: { __redactBody: true },
270500
+ searchParams: branchScope(branchId),
270501
+ json: {
270502
+ tool_call_id: options.toolCallId,
270503
+ action: options.action,
270504
+ extra_user_input: options.input ?? {},
270505
+ ...options.messageId ? { message_id: options.messageId } : {}
270506
+ }
270507
+ });
270508
+ } catch (error) {
270509
+ throw await ApiError.fromHttpError(error, "answering the agent");
270510
+ }
270511
+ return parseOrThrow(ChatTurnSchema, await response.json(), "chat turn");
270512
+ }
269901
270513
  async function sendTurn(content, branchId) {
269902
270514
  let response;
269903
270515
  try {
@@ -269952,7 +270564,7 @@ async function getPreviewUrl() {
269952
270564
  return /^https?:\/\//.test(url) ? url : `https://${url}`;
269953
270565
  }
269954
270566
 
269955
- // src/cli/commands/app/shared.ts
270567
+ // src/cli/commands/builder/shared.ts
269956
270568
  var APP_NAME_RE = /^[A-Za-z0-9._-]+$/;
269957
270569
  var NAME_STOPWORDS = new Set("a an the and or of for with to in on that this its it my our your me".split(" "));
269958
270570
  var FALLBACK_WORDS = [
@@ -269974,13 +270586,27 @@ async function createAndLinkApp(options) {
269974
270586
  if (options.name && !APP_NAME_RE.test(options.name)) {
269975
270587
  throw new InvalidInputError("The name becomes a directory — letters, digits, dots, dashes and underscores only.");
269976
270588
  }
269977
- const name = options.name ?? (options.importRepo ? repoBasename(options.importRepo) : inventAppName(options.prompt));
269978
- const targetDir = join21(process.cwd(), name);
270589
+ const cwd = process.cwd();
270590
+ const fallbackName = () => options.importRepo ? repoBasename(options.importRepo) : inventAppName(options.prompt);
270591
+ const chosenDir = options.path ? resolve8(cwd, options.path) : await isDirEmpty(cwd) ? cwd : undefined;
270592
+ const dirBase = chosenDir ? basename6(chosenDir) : undefined;
270593
+ const name = options.name ?? (dirBase && APP_NAME_RE.test(dirBase) ? dirBase : fallbackName());
270594
+ const targetDir = chosenDir ?? join25(cwd, name);
270595
+ const here = targetDir === cwd;
270596
+ const dirName = here ? "." : relative6(cwd, targetDir) || name;
269979
270597
  await mkdir3(targetDir, { recursive: true });
269980
270598
  if (await appConfigExists(targetDir)) {
269981
- throw new InvalidInputError(`./${name} is already linked to a Base44 app. Pick another name.`);
269982
- }
269983
- const created = options.importRepo ? await createImportedApp({
270599
+ throw new InvalidInputError(here ? "This directory is already linked to a Base44 app. Run `base44 code` here to keep building it, or pass --path for a new one." : `./${dirName} is already linked to a Base44 app. Pick another name or --path.`);
270600
+ }
270601
+ let clientCreationId;
270602
+ const created = options.wixInstance ? await createWixLaunchedApp({
270603
+ prompt: options.prompt ?? "",
270604
+ signedInstance: options.wixInstance.signedInstance,
270605
+ wixClientId: options.wixInstance.wixClientId
270606
+ }).then((c) => {
270607
+ clientCreationId = c.client_creation_id;
270608
+ return c;
270609
+ }) : options.importRepo ? await createImportedApp({
269984
270610
  appName: name,
269985
270611
  repoUrl: options.importRepo,
269986
270612
  sourceMode: options.mode ?? "direct",
@@ -269989,9 +270615,9 @@ async function createAndLinkApp(options) {
269989
270615
  prompt: options.prompt
269990
270616
  }) : await createApp({ appName: name, prompt: options.prompt });
269991
270617
  await writeAppConfig(targetDir, created.id);
269992
- await mkdir3(join21(targetDir, "base44"), { recursive: true });
270618
+ await mkdir3(join25(targetDir, "base44"), { recursive: true });
269993
270619
  try {
269994
- await writeFile2(join21(targetDir, "base44", "config.jsonc"), `// Base44 project configuration.
270620
+ await writeFile2(join25(targetDir, "base44", "config.jsonc"), `// Base44 project configuration.
269995
270621
  {
269996
270622
  "name": ${JSON.stringify(name)}
269997
270623
  }
@@ -270002,16 +270628,49 @@ async function createAndLinkApp(options) {
270002
270628
  id: created.id,
270003
270629
  editorUrl: `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`,
270004
270630
  repoUrl: created.imported_repo_url ?? undefined,
270005
- dirName: name,
270006
- targetDir
270007
- };
270631
+ dirName,
270632
+ targetDir,
270633
+ here,
270634
+ ...clientCreationId ? { clientCreationId } : {}
270635
+ };
270636
+ }
270637
+ function repoLabel(url) {
270638
+ return url.replace(/^https?:\/\//, "").replace(/\.git$/, "").replace(/\/$/, "");
270639
+ }
270640
+ function appTypeChip(state) {
270641
+ switch (state.app_type ?? "user_app") {
270642
+ case "imported_app":
270643
+ return state.imported_repo_url ? repoLabel(state.imported_repo_url) : "repository app";
270644
+ case "user_game":
270645
+ return "game";
270646
+ case "mobile_app":
270647
+ return "mobile app";
270648
+ case "slide":
270649
+ return "slides";
270650
+ case "user_agent":
270651
+ return "superagent";
270652
+ default:
270653
+ return "web app";
270654
+ }
270655
+ }
270656
+ async function assertBuilderApp(appId) {
270657
+ const state = await getAppState(appId);
270658
+ if (state.is_managed_source_code === false) {
270659
+ throw new InvalidInputError("This project is code-first (base44 create): you own the source and the builder cannot work on it. Run `base44 builder new` for an agent-built app.");
270660
+ }
270661
+ if (state.app_type === "user_agent") {
270662
+ throw new InvalidInputError("This is a Superagent, not an app the builder works on. Superagent has no CLI surface yet.");
270663
+ }
270664
+ return state;
270008
270665
  }
270009
270666
  function nextStepsLines(app) {
270667
+ const cd = app.here ? "" : `cd ${app.dirName} && `;
270010
270668
  return [
270011
270669
  "",
270012
- `${source_default.bold("Your app lives in")} ./${app.dirName}`,
270013
- source_default.dim(` cd ${app.dirName} && base44 code # keep building with the agent`),
270014
- source_default.dim(` cd ${app.dirName} && base44 app send "…" # one non-interactive turn`)
270670
+ `${source_default.bold("Your app lives in")} ${app.here ? "./ (this directory)" : `./${app.dirName}`}`,
270671
+ source_default.dim(` ${cd}base44 code # keep building with the agent`),
270672
+ source_default.dim(` ${cd}base44 builder send "…" # one non-interactive turn`),
270673
+ source_default.dim(` files live remotely — ${cd}base44 sandbox ls to look, base44 eject for a copy`)
270015
270674
  ];
270016
270675
  }
270017
270676
  async function githubReauthLines(error) {
@@ -270034,12 +270693,27 @@ function newStreamState() {
270034
270693
  return { perMessage: new Map };
270035
270694
  }
270036
270695
  var TOOL_SETTLED = new Set(["success", "error", "stopped"]);
270696
+ function fullText(value) {
270697
+ if (typeof value === "string")
270698
+ return value.trimEnd();
270699
+ if (value == null)
270700
+ return "";
270701
+ return JSON.stringify(value);
270702
+ }
270703
+ function toolArgs(argumentsString) {
270704
+ try {
270705
+ const parsed = JSON.parse(argumentsString ?? "");
270706
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
270707
+ } catch {
270708
+ return null;
270709
+ }
270710
+ }
270037
270711
  function oneLine(value, max) {
270038
270712
  const text = typeof value === "string" ? value : value == null ? "" : JSON.stringify(value);
270039
270713
  const flat = text.replace(/\s+/g, " ").trim();
270040
270714
  return flat.length > max ? `${flat.slice(0, max)}…` : flat;
270041
270715
  }
270042
- var SALIENT_KEYS = ["command", "path", "file_path", "title"];
270716
+ var SALIENT_KEYS = ["command", "path", "file_path", "title", "find"];
270043
270717
  function salvageKey(raw, keys) {
270044
270718
  for (const key of keys) {
270045
270719
  const match = raw.match(new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`));
@@ -270068,6 +270742,10 @@ function toolMeta(name, argumentsString) {
270068
270742
  read_repo_file: pick("path") ?? pick("file_path"),
270069
270743
  write_repo_file: pick("path") ?? pick("file_path"),
270070
270744
  edit_repo_file: pick("path") ?? pick("file_path"),
270745
+ read_file: pick("file_path") ?? pick("path"),
270746
+ write_file: pick("file_path") ?? pick("path"),
270747
+ find_replace: pick("file_path") ?? pick("path"),
270748
+ delete_file: pick("file_path") ?? pick("path"),
270071
270749
  create_pull_request: pick("title"),
270072
270750
  reload_preview: ""
270073
270751
  };
@@ -270118,7 +270796,10 @@ function diffConversation(state, messages) {
270118
270796
  }
270119
270797
  for (const tool of message.tool_calls ?? []) {
270120
270798
  if (!progress.announcedTools.has(tool.id)) {
270121
- progress.announcedTools.set(tool.id, toolMeta(tool.name, tool.arguments_string));
270799
+ progress.announcedTools.set(tool.id, {
270800
+ ...toolMeta(tool.name, tool.arguments_string),
270801
+ args: toolArgs(tool.arguments_string)
270802
+ });
270122
270803
  const meta = progress.announcedTools.get(tool.id);
270123
270804
  events.push({
270124
270805
  kind: "tool_start",
@@ -270149,7 +270830,8 @@ function diffConversation(state, messages) {
270149
270830
  label: labelTense(meta.label, "done"),
270150
270831
  summary: meta.summary,
270151
270832
  ok: status === "success",
270152
- result: oneLine(tool.results, 110)
270833
+ result: fullText(tool.results),
270834
+ args: meta.args
270153
270835
  });
270154
270836
  }
270155
270837
  }
@@ -270223,9 +270905,90 @@ async function streamConversationUntilSettled(onEvent, options = {}) {
270223
270905
  return "timeout";
270224
270906
  }
270225
270907
 
270226
- // src/cli/commands/app/new.ts
270908
+ // src/cli/commands/builder/send.ts
270909
+ function lastAssistantReply(turn) {
270910
+ const messages = turn.conversation?.messages ?? [];
270911
+ for (let i = messages.length - 1;i >= 0; i--) {
270912
+ const { role, content } = messages[i];
270913
+ if (role === "assistant" && typeof content === "string" && content.trim()) {
270914
+ return content.trim();
270915
+ }
270916
+ }
270917
+ return;
270918
+ }
270919
+ function ndjsonWriter() {
270920
+ return (record) => process.stdout.write(`${JSON.stringify(record)}
270921
+ `);
270922
+ }
270923
+ async function sendAction(ctx, message, options) {
270924
+ if (options.streamJson && ctx.jsonMode) {
270925
+ throw new InvalidInputError("--stream-json and --json are exclusive.");
270926
+ }
270927
+ if (ctx.app)
270928
+ await assertBuilderApp(ctx.app.id);
270929
+ const branchId = await resolveBranchId(ctx);
270930
+ if (options.streamJson) {
270931
+ const write = ndjsonWriter();
270932
+ const turn = await streamConversationDuring(() => sendTurn(message, branchId), ({ kind, ...event }) => write({ type: kind, ...event }), { branchId });
270933
+ write({
270934
+ type: "result",
270935
+ queued: turn.queued === true,
270936
+ status: turn.status?.state ?? "ready",
270937
+ error_source: turn.status?.error_source ?? null,
270938
+ reply: lastAssistantReply(turn) ?? null
270939
+ });
270940
+ return {};
270941
+ }
270942
+ if (ctx.jsonMode) {
270943
+ const turn = await ctx.runTask("Agent working (a turn can take minutes)", () => sendTurn(message, branchId));
270944
+ if (turn.queued)
270945
+ return { stdout: `${JSON.stringify({ queued: true })}
270946
+ ` };
270947
+ return {
270948
+ stdout: `${JSON.stringify({
270949
+ status: turn.status?.state ?? "ready",
270950
+ error_source: turn.status?.error_source ?? null,
270951
+ reply: lastAssistantReply(turn) ?? null
270952
+ })}
270953
+ `
270954
+ };
270955
+ }
270956
+ const stream = createTurnStream(process.stdout.isTTY === true, undefined, {
270957
+ verbose: options.verbose
270958
+ });
270959
+ let turn;
270960
+ try {
270961
+ turn = await streamConversationDuring(() => sendTurn(message, branchId), stream.onEvent, { branchId });
270962
+ } finally {
270963
+ stream.stop();
270964
+ }
270965
+ if (turn.queued) {
270966
+ return {
270967
+ outroMessage: "The agent is busy with an earlier message — yours was queued and runs next."
270968
+ };
270969
+ }
270970
+ if (turn.status?.state === "error") {
270971
+ return {
270972
+ outroMessage: `Turn failed (${turn.status.error_source ?? "unknown"}) — see the editor for details.`
270973
+ };
270974
+ }
270975
+ return { outroMessage: "Turn finished." };
270976
+ }
270977
+ function getSendCommand() {
270978
+ const command = new Base44Command("send", { supportsBranch: true });
270979
+ command.description("Send the agent one message and stream the turn until it finishes").argument("<message>", "What you want the agent to do").option("--verbose", "Show every tool result in full (no folding)").option("--stream-json", "Emit each stream event as a JSON line as it happens, then a final result line").action(sendAction);
270980
+ return command;
270981
+ }
270982
+
270983
+ // src/cli/commands/builder/new.ts
270227
270984
  var POLL_TIMEOUT_MS = 20 * 60000;
270228
270985
  var MODES = ["direct", "fork", "copy"];
270986
+ async function readStdin2() {
270987
+ const chunks = [];
270988
+ for await (const chunk of process.stdin)
270989
+ chunks.push(chunk);
270990
+ return Buffer.concat(chunks).toString("utf8");
270991
+ }
270229
270992
  async function newAction({ log, runTask, jsonMode }, prompt, options) {
270230
270993
  if (options.mode && !MODES.includes(options.mode)) {
270231
270994
  throw new InvalidInputError("--mode must be direct, fork, or copy.");
@@ -270233,9 +270996,27 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270233
270996
  if ((options.mode || options.repoName || options.fromBranch) && !options.import) {
270234
270997
  throw new InvalidInputError("--mode, --repo-name and --from-branch apply only with --import <repo>.");
270235
270998
  }
270999
+ if (options.wixInstance && options.import) {
271000
+ throw new InvalidInputError("--wix-instance and --import are exclusive.");
271001
+ }
271002
+ if (options.wixClientId && !options.wixInstance) {
271003
+ throw new InvalidInputError("--wix-client-id applies only with --wix-instance.");
271004
+ }
271005
+ const signedInstance = options.wixInstance ? (options.wixInstance === "-" ? await readStdin2() : options.wixInstance).trim() : undefined;
271006
+ if (options.wixInstance && !signedInstance) {
271007
+ throw new InvalidInputError("--wix-instance is empty.");
271008
+ }
271009
+ const wixInstance = signedInstance ? { signedInstance, wixClientId: options.wixClientId?.trim() || undefined } : undefined;
271010
+ if (wixInstance && !prompt) {
271011
+ throw new InvalidInputError("--wix-instance needs the prompt argument: what the agent should build.");
271012
+ }
270236
271013
  if (!prompt && !options.import) {
270237
271014
  throw new InvalidInputError('Describe the app ("<prompt>") or pass --import <repo>.');
270238
271015
  }
271016
+ if (options.streamJson && jsonMode) {
271017
+ throw new InvalidInputError("--stream-json and --json are exclusive.");
271018
+ }
271019
+ const ndjson = options.streamJson ? ndjsonWriter() : null;
270239
271020
  let app;
270240
271021
  try {
270241
271022
  app = await runTask(options.import ? "Importing the repository" : "Creating your app", () => createAndLinkApp({
@@ -270244,18 +271025,33 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270244
271025
  importRepo: options.import,
270245
271026
  mode: options.mode,
270246
271027
  repoName: options.repoName,
270247
- fromBranch: options.fromBranch
271028
+ fromBranch: options.fromBranch,
271029
+ path: options.path,
271030
+ wixInstance
270248
271031
  }));
270249
271032
  } catch (error) {
270250
271033
  for (const line of await githubReauthLines(error) ?? [])
270251
271034
  log.message(line);
270252
271035
  throw error;
270253
271036
  }
270254
- if (!jsonMode) {
271037
+ if (ndjson) {
271038
+ ndjson({
271039
+ type: "created",
271040
+ id: app.id,
271041
+ repo_url: app.repoUrl ?? null,
271042
+ editor_url: app.editorUrl,
271043
+ dir: app.dirName,
271044
+ path: app.targetDir,
271045
+ ...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
271046
+ });
271047
+ } else if (!jsonMode) {
270255
271048
  if (app.repoUrl)
270256
271049
  log.message(source_default.dim(`repo ${app.repoUrl}`));
271050
+ if (app.clientCreationId) {
271051
+ log.message(source_default.dim("wix launched — connector connected before the first turn"));
271052
+ }
270257
271053
  log.message(source_default.dim(`editor ${app.editorUrl}`));
270258
- log.message(source_default.dim(`linked ./${app.dirName}`));
271054
+ log.message(source_default.dim(`linked ${app.here ? "./ (this directory)" : `./${app.dirName}`}`));
270259
271055
  }
270260
271056
  let finalState;
270261
271057
  let previewUrl;
@@ -270264,10 +271060,16 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270264
271060
  const branchId = await resolveActiveBranchId().catch(() => {
270265
271061
  return;
270266
271062
  });
270267
- const stream = createTurnStream(process.stdout.isTTY === true && !jsonMode, undefined, { idleLabel: "provisioning the sandbox and starting the build" });
271063
+ const stream = createTurnStream(process.stdout.isTTY === true && !jsonMode && !ndjson, undefined, {
271064
+ idleLabel: "provisioning the sandbox and starting the build",
271065
+ verbose: options.verbose
271066
+ });
270268
271067
  try {
270269
271068
  const settled = await streamConversationUntilSettled((event) => {
270270
- if (!jsonMode)
271069
+ if (ndjson) {
271070
+ const { kind, ...rest } = event;
271071
+ ndjson({ type: kind, ...rest });
271072
+ } else if (!jsonMode)
270271
271073
  stream.onEvent(event);
270272
271074
  }, { branchId, timeoutMs: POLL_TIMEOUT_MS });
270273
271075
  finalState = settled === "timeout" ? "processing" : (await getAppState(app.id)).status?.state ?? "ready";
@@ -270280,6 +271082,15 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270280
271082
  stream.stop();
270281
271083
  }
270282
271084
  }
271085
+ if (ndjson) {
271086
+ ndjson({
271087
+ type: "result",
271088
+ id: app.id,
271089
+ preview_url: previewUrl ?? null,
271090
+ status: finalState ?? "created"
271091
+ });
271092
+ return {};
271093
+ }
270283
271094
  if (jsonMode) {
270284
271095
  return {
270285
271096
  stdout: `${JSON.stringify({
@@ -270289,7 +271100,8 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270289
271100
  preview_url: previewUrl ?? null,
270290
271101
  dir: app.dirName,
270291
271102
  path: app.targetDir,
270292
- status: finalState ?? "created"
271103
+ status: finalState ?? "created",
271104
+ ...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
270293
271105
  })}
270294
271106
  `
270295
271107
  };
@@ -270305,7 +271117,7 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270305
271117
  }
270306
271118
  if (finalState === "processing") {
270307
271119
  return {
270308
- outroMessage: `Still building — follow it with \`base44 app status\`.`
271120
+ outroMessage: `Still building — follow it with \`base44 builder status\`.`
270309
271121
  };
270310
271122
  }
270311
271123
  return {
@@ -270314,78 +271126,11 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270314
271126
  }
270315
271127
  function getNewCommand() {
270316
271128
  const command = new Base44Command("new", { requireAppContext: false });
270317
- command.description("Create an app and start building: from a prompt (the Base44 template), or over an existing GitHub repo with --import").argument("[prompt]", "What to build; the first agent turn starts immediately").option("--import <repo>", "Build over an existing GitHub repository instead of the template").option("--mode <mode>", "How to import: direct, fork, or copy (default: direct)").option("--name <name>", "Directory and app name (invented when omitted)").option("--repo-name <name>", "Name for the new GitHub repo when forking/copying").option("--from-branch <name>", "Import a specific branch of the repo").action(newAction);
270318
- return command;
270319
- }
270320
-
270321
- // src/cli/commands/app/preview.ts
270322
- async function previewAction(ctx) {
270323
- const url = await ctx.runTask("Resolving preview URL (boots the sandbox if needed)", () => getPreviewUrl());
270324
- if (ctx.jsonMode)
270325
- return { stdout: `${JSON.stringify({ preview_url: url })}
270326
- ` };
270327
- ctx.log.message(url);
270328
- return { outroMessage: "Preview is live." };
270329
- }
270330
- function getPreviewCommand() {
270331
- const command = new Base44Command("preview");
270332
- command.description("Print the app's live preview URL").action(previewAction);
270333
- return command;
270334
- }
270335
-
270336
- // src/cli/commands/app/send.ts
270337
- function lastAssistantReply(turn) {
270338
- const messages = turn.conversation?.messages ?? [];
270339
- for (let i = messages.length - 1;i >= 0; i--) {
270340
- const { role, content } = messages[i];
270341
- if (role === "assistant" && typeof content === "string" && content.trim()) {
270342
- return content.trim();
270343
- }
270344
- }
270345
- return;
270346
- }
270347
- async function sendAction(ctx, message) {
270348
- const branchId = await resolveBranchId(ctx);
270349
- if (ctx.jsonMode) {
270350
- const turn = await ctx.runTask("Agent working (a turn can take minutes)", () => sendTurn(message, branchId));
270351
- if (turn.queued)
270352
- return { stdout: `${JSON.stringify({ queued: true })}
270353
- ` };
270354
- return {
270355
- stdout: `${JSON.stringify({
270356
- status: turn.status?.state ?? "ready",
270357
- error_source: turn.status?.error_source ?? null,
270358
- reply: lastAssistantReply(turn) ?? null
270359
- })}
270360
- `
270361
- };
270362
- }
270363
- const stream = createTurnStream(process.stdout.isTTY === true);
270364
- let turn;
270365
- try {
270366
- turn = await streamConversationDuring(() => sendTurn(message, branchId), stream.onEvent, { branchId });
270367
- } finally {
270368
- stream.stop();
270369
- }
270370
- if (turn.queued) {
270371
- return {
270372
- outroMessage: "The agent is busy with an earlier message — yours was queued and runs next."
270373
- };
270374
- }
270375
- if (turn.status?.state === "error") {
270376
- return {
270377
- outroMessage: `Turn failed (${turn.status.error_source ?? "unknown"}) — see the editor for details.`
270378
- };
270379
- }
270380
- return { outroMessage: "Turn finished." };
270381
- }
270382
- function getSendCommand() {
270383
- const command = new Base44Command("send", { supportsBranch: true });
270384
- command.description("Send the agent one message and stream the turn until it finishes").argument("<message>", "What you want the agent to do").action(sendAction);
271129
+ command.description("Create an app and start building: from a prompt (the Base44 template), or over an existing GitHub repo with --import").argument("[prompt]", "What to build; the first agent turn starts immediately").option("--import <repo>", "Build over an existing GitHub repository instead of the template").option("--mode <mode>", "How to import: direct, fork, or copy (default: direct)").option("--name <name>", "Directory and app name (invented when omitted)").option("--path <dir>", "Directory to link (default: the current directory when empty, else ./<name>)").option("--repo-name <name>", "Name for the new GitHub repo when forking/copying").option("--from-branch <name>", "Import a specific branch of the repo").addOption(new Option2("--wix-instance <token>", 'Create through the Wix route with this signed instance (the Wix connector is connected before the first turn); "-" reads the token from stdin').env("BASE44_WIX_INSTANCE")).option("--wix-client-id <id>", "The companion OAuth app's client id, when the Wix launch has one").option("--verbose", "Show every tool result in full (no folding)").option("--stream-json", "Emit each stream event as a JSON line as it happens, then a final result line").action(newAction);
270385
271130
  return command;
270386
271131
  }
270387
271132
 
270388
- // src/cli/commands/app/status.ts
271133
+ // src/cli/commands/builder/status.ts
270389
271134
  async function statusAction(ctx) {
270390
271135
  const id = ctx.app?.id;
270391
271136
  const app = await ctx.runTask("Reading app status", () => getAppState(id));
@@ -270407,7 +271152,7 @@ function getStatusCommand() {
270407
271152
  return command;
270408
271153
  }
270409
271154
 
270410
- // src/cli/commands/app/stop.ts
271155
+ // src/cli/commands/builder/stop.ts
270411
271156
  async function stopAction(ctx) {
270412
271157
  const branchId = await resolveBranchId(ctx);
270413
271158
  await ctx.runTask("Stopping the running turn", () => stopTurn(branchId));
@@ -270422,470 +271167,9 @@ function getStopCommand() {
270422
271167
  return command;
270423
271168
  }
270424
271169
 
270425
- // src/cli/commands/app/index.ts
270426
- function getAppCommand() {
270427
- return new Command2("app").description("Build a Base44 app with the agent, non-interactively: create it, send turns, read status, get the preview").addCommand(getNewCommand()).addCommand(getSendCommand()).addCommand(getStatusCommand()).addCommand(getPreviewCommand()).addCommand(getStopCommand()).addCommand(getModelCommand());
270428
- }
270429
-
270430
- // src/cli/commands/auth/password-login.ts
270431
- import { dirname as dirname15, join as join22 } from "node:path";
270432
- async function passwordLoginAction({ log, runTask }, action) {
270433
- const shouldEnable = action === "enable";
270434
- const { project } = await readProjectConfig();
270435
- const configDir = dirname15(project.configPath);
270436
- const authDir = join22(configDir, project.authDir);
270437
- const updated = await runTask("Updating local auth config", async () => {
270438
- const current = await readAuthConfig(authDir) ?? DEFAULT_AUTH_CONFIG;
270439
- const merged = { ...current, enableUsernamePassword: shouldEnable };
270440
- await writeAuthConfig(authDir, merged);
270441
- return merged;
270442
- });
270443
- if (!shouldEnable && !hasAnyLoginMethod(updated)) {
270444
- log.warn("Disabling password auth will leave no login methods enabled. Users will be locked out.");
270445
- }
270446
- const newStatus = shouldEnable ? "enabled" : "disabled";
270447
- return {
270448
- outroMessage: `Username & password authentication ${newStatus} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
270449
- };
270450
- }
270451
- function getPasswordLoginCommand() {
270452
- return new Base44Command("password-login").description("Enable or disable username & password authentication").addArgument(new Argument2("<action>", "enable or disable password authentication").choices(["enable", "disable"])).action(passwordLoginAction);
270453
- }
270454
-
270455
- // src/cli/commands/auth/pull.ts
270456
- import { dirname as dirname16, join as join23 } from "node:path";
270457
- async function pullAuthAction({
270458
- log,
270459
- runTask
270460
- }) {
270461
- const { project } = await readProjectConfig();
270462
- const configDir = dirname16(project.configPath);
270463
- const authDir = join23(configDir, project.authDir);
270464
- const remoteConfig = await runTask("Fetching auth config from Base44", async () => {
270465
- return await pullAuthConfig();
270466
- }, {
270467
- successMessage: "Auth config fetched successfully",
270468
- errorMessage: "Failed to fetch auth config"
270469
- });
270470
- const { written } = await runTask("Syncing auth config file", async () => {
270471
- return await writeAuthConfig(authDir, remoteConfig);
270472
- }, {
270473
- successMessage: "Auth config file synced successfully",
270474
- errorMessage: "Failed to sync auth config file"
270475
- });
270476
- if (written) {
270477
- log.success("Auth config written to local file");
270478
- } else {
270479
- log.info("Auth config is already up to date");
270480
- }
270481
- return {
270482
- outroMessage: `Pulled auth config to ${authDir} (overwrites local file)`
270483
- };
270484
- }
270485
- function getAuthPullCommand() {
270486
- return new Base44Command("pull").description("Pull auth config from Base44 to local file").action(pullAuthAction);
270487
- }
270488
-
270489
- // src/cli/commands/auth/push.ts
270490
- async function pushAuthAction({ isNonInteractive, log, runTask }, options) {
270491
- const { authConfig } = await readProjectConfig();
270492
- if (authConfig.length === 0) {
270493
- log.info("No local auth config found");
270494
- return {
270495
- outroMessage: "No auth config to push. Run `base44 auth pull` to fetch the remote config first."
270496
- };
270497
- }
270498
- if (!hasAnyLoginMethod(authConfig[0])) {
270499
- log.warn("This config has no login methods enabled. Pushing it will lock out all users.");
270500
- }
270501
- if (!options.yes) {
270502
- if (isNonInteractive) {
270503
- throw new InvalidInputError("--yes is required in non-interactive mode");
270504
- }
270505
- const shouldPush = await Re({
270506
- message: "Push auth config to Base44?"
270507
- });
270508
- if (Ct(shouldPush) || !shouldPush) {
270509
- return { outroMessage: "Push cancelled" };
270510
- }
270511
- }
270512
- await runTask("Pushing auth config to Base44", async () => {
270513
- return await pushAuthConfig(authConfig[0] ?? null);
270514
- }, {
270515
- successMessage: "Auth config pushed successfully",
270516
- errorMessage: "Failed to push auth config"
270517
- });
270518
- return {
270519
- outroMessage: "Auth config pushed to Base44"
270520
- };
270521
- }
270522
- function getAuthPushCommand() {
270523
- return new Base44Command("push").description("Push local auth config to Base44").option("-y, --yes", "Skip confirmation prompt").action(pushAuthAction);
270524
- }
270525
-
270526
- // src/cli/commands/auth/social-login.ts
270527
- import { dirname as dirname17, join as join24, resolve as resolve6 } from "node:path";
270528
- var PROVIDER_LABELS = {
270529
- google: "Google",
270530
- microsoft: "Microsoft",
270531
- facebook: "Facebook",
270532
- apple: "Apple"
270533
- };
270534
- var VALID_PROVIDER_NAMES = Object.keys(SOCIAL_PROVIDERS);
270535
- var PROVIDER_OAUTH_CLI = {
270536
- google: {
270537
- envVar: "google_oauth_client_secret",
270538
- promptMessage: "Enter Google OAuth client secret"
270539
- }
270540
- };
270541
- function hasSecretOptions(options) {
270542
- return Boolean(options.clientSecret || options.clientSecretStdin || options.envFile);
270543
- }
270544
- function hasCustomOAuthOptions(options) {
270545
- return Boolean(options.clientId || hasSecretOptions(options));
270546
- }
270547
- async function socialLoginAction({ log, isNonInteractive, runTask }, provider, action, options) {
270548
- const shouldEnable = action === "enable";
270549
- const providerInfo = SOCIAL_PROVIDERS[provider];
270550
- const label = PROVIDER_LABELS[provider];
270551
- const hasOAuthOptions = hasCustomOAuthOptions(options);
270552
- if (hasOAuthOptions && !providerInfo.customOAuth) {
270553
- throw new InvalidInputError(`Custom OAuth options are only supported for providers with custom OAuth (e.g., google). Use: base44 auth social-login ${provider} ${action}`);
270554
- }
270555
- if (hasOAuthOptions && !shouldEnable) {
270556
- throw new InvalidInputError(`Custom OAuth options cannot be used with disable. To disable ${label} login: base44 auth social-login ${provider} disable`);
270557
- }
270558
- if (hasSecretOptions(options) && !options.clientId) {
270559
- throw new InvalidInputError(`--client-id is required when providing a client secret. Use: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`);
270560
- }
270561
- const oauth = providerInfo.customOAuth;
270562
- const oauthCli = PROVIDER_OAUTH_CLI[provider];
270563
- const useCustomOAuth = shouldEnable && hasOAuthOptions && oauth != null;
270564
- let clientSecret;
270565
- if (useCustomOAuth && oauth && oauthCli && hasSecretOptions(options)) {
270566
- if (options.envFile) {
270567
- const secrets = await parseEnvFile(resolve6(options.envFile));
270568
- const value = secrets[oauthCli.envVar];
270569
- if (!value) {
270570
- throw new InvalidInputError(`Key "${oauthCli.envVar}" not found in ${options.envFile}.`);
270571
- }
270572
- clientSecret = value;
270573
- } else {
270574
- clientSecret = await resolveSecret({
270575
- flagValue: options.clientSecret,
270576
- fromStdin: options.clientSecretStdin,
270577
- envVar: oauthCli.envVar,
270578
- promptMessage: oauthCli.promptMessage,
270579
- isNonInteractive,
270580
- name: "client secret",
270581
- hints: [
270582
- {
270583
- message: `Provide via flag: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`,
270584
- command: `base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`
270585
- },
270586
- {
270587
- message: `Provide via stdin: echo <secret> | base44 auth social-login ${provider} enable --client-id <id> --client-secret-stdin`
270588
- },
270589
- {
270590
- message: `Provide via env: ${oauthCli.envVar}=<secret> base44 auth social-login ${provider} enable --client-id <id>`
270591
- }
270592
- ]
270593
- });
270594
- }
270595
- }
270596
- const { project } = await readProjectConfig();
270597
- const configDir = dirname17(project.configPath);
270598
- const authDir = join24(configDir, project.authDir);
270599
- const { config: updated } = await runTask("Updating local auth config", async () => updateSocialLoginConfig(authDir, provider, shouldEnable, useCustomOAuth && options.clientId ? { clientId: options.clientId } : undefined));
270600
- if (clientSecret) {
270601
- await runTask("Saving client secret", async () => pushCustomOAuthSecret(provider, clientSecret));
270602
- }
270603
- if (!shouldEnable && !hasAnyLoginMethod(updated)) {
270604
- log.warn(`Disabling ${label} login will leave no login methods enabled. Users will be locked out.`);
270605
- }
270606
- const newStatus = shouldEnable ? "enabled" : "disabled";
270607
- const oauthNote = useCustomOAuth ? " with custom OAuth" : "";
270608
- let outroMessage = `${label} login ${newStatus}${oauthNote} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`;
270609
- if (useCustomOAuth && !clientSecret) {
270610
- outroMessage += `
270611
- Remember to push the client secret separately: base44 secrets set --env-file <path>`;
270612
- }
270613
- return { outroMessage };
270614
- }
270615
- function getSocialLoginCommand() {
270616
- return new Base44Command("social-login").description("Enable or disable social login providers (google, microsoft, facebook, apple)").addArgument(new Argument2("<provider>", "social login provider").choices(VALID_PROVIDER_NAMES)).addArgument(new Argument2("<action>", "enable or disable the provider").choices([
270617
- "enable",
270618
- "disable"
270619
- ])).option("--client-id <id>", "custom OAuth client ID (Google only)").option("--client-secret <secret>", "custom OAuth client secret (Google only)").option("--client-secret-stdin", "read client secret from stdin (Google only)").option("--env-file <path>", "read client secret from a .env file (Google only)").action(socialLoginAction);
270620
- }
270621
-
270622
- // src/cli/commands/auth/sso.ts
270623
- import { dirname as dirname18, join as join25, resolve as resolve7 } from "node:path";
270624
- var SSOConfigFileSchema = object({
270625
- provider: _enum(Object.values(KNOWN_SSO_PROVIDERS)),
270626
- clientId: string2(),
270627
- clientSecret: string2(),
270628
- scope: string2().optional(),
270629
- discoveryUrl: string2().optional(),
270630
- tenantId: string2().optional(),
270631
- oktaDomain: string2().optional(),
270632
- authEndpoint: string2().optional(),
270633
- tokenEndpoint: string2().optional(),
270634
- userinfoEndpoint: string2().optional(),
270635
- jwksUri: string2().optional(),
270636
- ssoName: string2().optional()
270637
- });
270638
- async function loadSSOConfigFile(filePath) {
270639
- const resolved = resolve7(filePath);
270640
- const raw = await readJsonFile(resolved);
270641
- const result = SSOConfigFileSchema.safeParse(raw);
270642
- if (!result.success) {
270643
- throw new SchemaValidationError("Invalid SSO config file", result.error, filePath);
270644
- }
270645
- return result.data;
270646
- }
270647
- function mergeFileWithFlags(fileConfig, options) {
270648
- return {
270649
- provider: options.provider ?? fileConfig.provider,
270650
- clientId: options.clientId ?? fileConfig.clientId,
270651
- clientSecret: options.clientSecret ?? fileConfig.clientSecret,
270652
- clientSecretStdin: options.clientSecretStdin,
270653
- envFile: options.envFile,
270654
- scope: options.scope ?? fileConfig.scope,
270655
- discoveryUrl: options.discoveryUrl ?? fileConfig.discoveryUrl,
270656
- tenantId: options.tenantId ?? fileConfig.tenantId,
270657
- oktaDomain: options.oktaDomain ?? fileConfig.oktaDomain,
270658
- authEndpoint: options.authEndpoint ?? fileConfig.authEndpoint,
270659
- tokenEndpoint: options.tokenEndpoint ?? fileConfig.tokenEndpoint,
270660
- userinfoEndpoint: options.userinfoEndpoint ?? fileConfig.userinfoEndpoint,
270661
- jwksUri: options.jwksUri ?? fileConfig.jwksUri,
270662
- ssoName: options.ssoName ?? fileConfig.ssoName
270663
- };
270664
- }
270665
- var providerNames = Object.keys(KNOWN_SSO_PROVIDERS);
270666
- var SECRET_KEY_TO_FLAG = {
270667
- ["sso_name" /* Name */]: "--sso-name",
270668
- ["sso_client_id" /* ClientId */]: "--client-id",
270669
- ["sso_client_secret" /* ClientSecret */]: "--client-secret",
270670
- ["sso_scope" /* Scope */]: "--scope",
270671
- ["sso_discovery_url" /* DiscoveryUrl */]: "--discovery-url",
270672
- ["sso_tenant_id" /* TenantId */]: "--tenant-id",
270673
- ["sso_auth_endpoint" /* AuthEndpoint */]: "--auth-endpoint",
270674
- ["sso_token_endpoint" /* TokenEndpoint */]: "--token-endpoint",
270675
- ["sso_userinfo_endpoint" /* UserinfoEndpoint */]: "--userinfo-endpoint",
270676
- ["sso_okta_domain" /* OktaDomain */]: "--okta-domain",
270677
- ["sso_jwks_uri" /* JwksUri */]: "--jwks-uri"
270678
- };
270679
- function secretKeyToFlag(key) {
270680
- return SECRET_KEY_TO_FLAG[key];
270681
- }
270682
- function exampleCommand(provider) {
270683
- let cmd = `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`;
270684
- if (provider === KNOWN_SSO_PROVIDERS.microsoft)
270685
- cmd += " --tenant-id <id>";
270686
- if (provider === KNOWN_SSO_PROVIDERS.okta)
270687
- cmd += " --okta-domain <domain>";
270688
- if (provider === KNOWN_SSO_PROVIDERS.custom)
270689
- cmd += " --sso-name <name> --auth-endpoint <url> --token-endpoint <url> --userinfo-endpoint <url> --jwks-uri <url>";
270690
- return cmd;
270691
- }
270692
- function validateProvider(provider) {
270693
- if (!provider) {
270694
- throw new InvalidInputError("Missing --provider.", {
270695
- hints: [
270696
- {
270697
- message: `Valid providers: ${providerNames.join(", ")}`,
270698
- command: "base44 auth sso enable --provider <provider> --client-id <id> --client-secret <secret>"
270699
- }
270700
- ]
270701
- });
270702
- }
270703
- return provider;
270704
- }
270705
- async function ssoEnableAction({ isNonInteractive, runTask }, options) {
270706
- if (options.file && options.envFile) {
270707
- throw new InvalidInputError("--file and --env-file cannot be used together. Provide the client secret either inside --file or via --env-file.");
270708
- }
270709
- let merged = options;
270710
- if (options.file) {
270711
- const fileConfig = await loadSSOConfigFile(options.file);
270712
- merged = mergeFileWithFlags(fileConfig, options);
270713
- }
270714
- const provider = validateProvider(merged.provider);
270715
- if (!merged.clientId) {
270716
- throw new InvalidInputError("Missing --client-id.", {
270717
- hints: [
270718
- {
270719
- message: `Example: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
270720
- command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
270721
- }
270722
- ]
270723
- });
270724
- }
270725
- let clientSecret;
270726
- if (merged.envFile && !merged.clientSecret) {
270727
- const secrets = await parseEnvFile(resolve7(merged.envFile));
270728
- const value = secrets.sso_client_secret;
270729
- if (!value) {
270730
- throw new InvalidInputError(`Key "sso_client_secret" not found in ${merged.envFile}.`);
270731
- }
270732
- clientSecret = value;
270733
- } else {
270734
- clientSecret = await resolveSecret({
270735
- flagValue: merged.clientSecret,
270736
- fromStdin: merged.clientSecretStdin,
270737
- envVar: "sso_client_secret",
270738
- promptMessage: "Enter SSO client secret",
270739
- isNonInteractive,
270740
- name: "client secret",
270741
- hints: [
270742
- {
270743
- message: `Provide via flag: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
270744
- command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
270745
- },
270746
- {
270747
- message: `Provide via stdin: echo <secret> | base44 auth sso enable --provider ${provider} --client-id <id> --client-secret-stdin`
270748
- },
270749
- {
270750
- message: `Provide via env: sso_client_secret=<secret> base44 auth sso enable --provider ${provider} --client-id <id>`
270751
- }
270752
- ]
270753
- });
270754
- }
270755
- const secretOptions = {
270756
- clientId: merged.clientId,
270757
- clientSecret,
270758
- scope: merged.scope,
270759
- discoveryUrl: merged.discoveryUrl,
270760
- tenantId: merged.tenantId,
270761
- oktaDomain: merged.oktaDomain,
270762
- authEndpoint: merged.authEndpoint,
270763
- tokenEndpoint: merged.tokenEndpoint,
270764
- userinfoEndpoint: merged.userinfoEndpoint,
270765
- jwksUri: merged.jwksUri,
270766
- ssoName: merged.ssoName
270767
- };
270768
- let secrets;
270769
- try {
270770
- secrets = buildSSOSecrets(provider, secretOptions);
270771
- } catch (error) {
270772
- if (error instanceof MissingSSOFieldsError) {
270773
- const flagNames = error.missingKeys.map(secretKeyToFlag);
270774
- throw new InvalidInputError(`Missing required fields for ${error.provider}: ${flagNames.join(", ")}`, {
270775
- hints: [
270776
- {
270777
- message: `Example: ${exampleCommand(provider)}`,
270778
- command: exampleCommand(provider)
270779
- }
270780
- ]
270781
- });
270782
- }
270783
- throw error;
270784
- }
270785
- const { project } = await readProjectConfig();
270786
- const configDir = dirname18(project.configPath);
270787
- const authDir = join25(configDir, project.authDir);
270788
- await runTask("Updating local auth config", async () => updateSSOConfig(authDir, provider, true));
270789
- await runTask("Saving SSO credentials", async () => pushSSOSecrets(secrets));
270790
- return {
270791
- outroMessage: `SSO configured with ${provider} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
270792
- };
270793
- }
270794
- function hasEnableOnlyOptions(options) {
270795
- return Boolean(options.provider || options.clientId || options.clientSecret || options.clientSecretStdin || options.envFile || options.file || options.scope || options.discoveryUrl || options.tenantId || options.oktaDomain || options.authEndpoint || options.tokenEndpoint || options.userinfoEndpoint || options.jwksUri || options.ssoName);
270796
- }
270797
- async function ssoDisableAction({ log, runTask }, options) {
270798
- if (hasEnableOnlyOptions(options)) {
270799
- throw new InvalidInputError("Configuration options cannot be used with disable. To disable SSO: base44 auth sso disable");
270800
- }
270801
- const { project } = await readProjectConfig();
270802
- const configDir = dirname18(project.configPath);
270803
- const authDir = join25(configDir, project.authDir);
270804
- const updated = await runTask("Updating local auth config", async () => updateSSOConfig(authDir, null, false));
270805
- await runTask("Removing SSO credentials", async () => deleteSSOSecrets());
270806
- if (!hasAnyLoginMethod(updated)) {
270807
- log.warn("Disabling SSO will leave no login methods enabled. Users will be locked out.");
270808
- }
270809
- return {
270810
- outroMessage: "SSO disabled in local config and credentials removed. Run `base44 auth push` or `base44 deploy` to apply."
270811
- };
270812
- }
270813
- async function ssoAction(context, action, options) {
270814
- if (action === "disable") {
270815
- return ssoDisableAction(context, options);
270816
- }
270817
- return ssoEnableAction(context, options);
270818
- }
270819
- function getSSOCommand() {
270820
- return new Base44Command("sso").description("Configure SSO identity provider (google, microsoft, github, okta, custom). SSO and social login are mutually exclusive — enabling one disables the other in the local auth config.").addArgument(new Argument2("<action>", "enable or disable SSO").choices([
270821
- "enable",
270822
- "disable"
270823
- ])).addOption(new Option2("--provider <provider>", "SSO provider").choices(Object.values(KNOWN_SSO_PROVIDERS))).option("--client-id <id>", "OAuth client ID").option("--client-secret <secret>", "OAuth client secret").option("--client-secret-stdin", "Read client secret from stdin").option("--env-file <path>", "Read client secret from a .env file (key: sso_client_secret)").option("--file <path>", "JSON config file with all SSO settings").option("--scope <scope>", "OAuth scope (defaults per provider)").option("--discovery-url <url>", "OIDC discovery URL").option("--tenant-id <id>", "Microsoft tenant ID (required for microsoft)").option("--okta-domain <domain>", "Okta domain (required for okta)").option("--auth-endpoint <url>", "Authorization endpoint (required for custom)").option("--token-endpoint <url>", "Token endpoint (required for custom)").option("--userinfo-endpoint <url>", "Userinfo endpoint (required for custom)").option("--jwks-uri <url>", "JWKS URI (required for custom)").option("--sso-name <name>", "Provider display name (required for custom)").action(ssoAction);
270824
- }
270825
-
270826
- // src/cli/commands/auth/index.ts
270827
- function getAuthCommand() {
270828
- return new Command2("auth").description("Manage app authentication settings").addCommand(getPasswordLoginCommand()).addCommand(getSocialLoginCommand()).addCommand(getSSOCommand()).addCommand(getAuthPullCommand()).addCommand(getAuthPushCommand());
270829
- }
270830
-
270831
- // src/cli/commands/auth/login.ts
270832
- function getLoginCommand() {
270833
- return new Base44Command("login", {
270834
- requireAuth: false,
270835
- requireAppContext: false
270836
- }).description("Authenticate with Base44").action(login);
270837
- }
270838
-
270839
- // src/cli/commands/auth/logout.ts
270840
- async function logout(_ctx) {
270841
- await deleteAuth();
270842
- return { outroMessage: "Logged out successfully" };
270843
- }
270844
- function getLogoutCommand() {
270845
- return new Base44Command("logout", {
270846
- requireAuth: false,
270847
- requireAppContext: false
270848
- }).description("Logout from current device").action(logout);
270849
- }
270850
-
270851
- // src/cli/commands/auth/whoami.ts
270852
- async function whoami(_ctx) {
270853
- const workspaceApiKey = getWorkspaceApiKeyFromEnv();
270854
- if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
270855
- return {
270856
- outroMessage: `Using workspace API key: ${theme.styles.bold(workspaceApiKey.slice(0, 10))}`
270857
- };
270858
- }
270859
- const auth = await readAuth();
270860
- return { outroMessage: `Logged in as: ${theme.styles.bold(auth.email)}` };
270861
- }
270862
- function getWhoamiCommand() {
270863
- return new Base44Command("whoami", { requireAppContext: false }).description("Display current authenticated user").action(whoami);
270864
- }
270865
-
270866
- // src/cli/commands/branches/index.ts
270867
- async function listBranchesAction({
270868
- log,
270869
- runTask,
270870
- jsonMode
270871
- }) {
270872
- const remote = await runTask("Fetching branches", () => listBranches());
270873
- const branches = [
270874
- { name: "main", status: "active" },
270875
- ...remote.map((branch) => ({
270876
- name: branch.branch_name,
270877
- status: branch.status
270878
- }))
270879
- ];
270880
- if (jsonMode)
270881
- return { stdout: `${JSON.stringify({ branches })}
270882
- ` };
270883
- for (const branch of branches)
270884
- log.message(`${branch.name} (${branch.status})`);
270885
- return { outroMessage: `${branches.length} branches` };
270886
- }
270887
- function getBranchesCommand() {
270888
- return new Command2("branches").description("Discover an app's branches").addCommand(new Base44Command("list").description("List main and active branch names for use with --branch").action(listBranchesAction));
271170
+ // src/cli/commands/builder/index.ts
271171
+ function getBuilderCommand() {
271172
+ return new Command2("builder").description("Build an app with the Base44 builder agent, non-interactively: create it, send turns, read status, stop, pick the model").addCommand(getNewCommand()).addCommand(getSendCommand()).addCommand(getStatusCommand()).addCommand(getStopCommand()).addCommand(getModelCommand());
270889
271173
  }
270890
271174
 
270891
271175
  // ../../node_modules/ink/build/render.js
@@ -275851,6 +276135,119 @@ var build_default = TextInput;
275851
276135
  // src/cli/commands/code/session.tsx
275852
276136
  var import_react23 = __toESM(require_react(), 1);
275853
276137
 
276138
+ // src/cli/commands/code/logo.ts
276139
+ var ROWS = 6;
276140
+ var ASPECT = 0.44;
276141
+ var SAMPLES = 4;
276142
+ var FILL = 0.6;
276143
+ var GAP_ROW = 3.5 + 0.5;
276144
+ var GAP_HEIGHT_ROWS = 0.8;
276145
+ var LOGO_COLS = Math.max(2, Math.floor(ROWS / ASPECT + 0.5));
276146
+ var OCTANT = {
276147
+ rx: 2,
276148
+ ry: 4,
276149
+ table: Array.from(" \uD83E\uDF82\uD833\uDD00▘\uD833\uDD01\uD833\uDD02\uD833\uDD03\uD833\uDD04▝\uD833\uDD05\uD833\uDD06\uD833\uDD07\uD833\uDD08▀\uD833\uDD09\uD833\uDD0A\uD833\uDD0B\uD833\uDD0C\uD833\uDD00\uD833\uDD0D\uD833\uDD0E\uD833\uDD0F\uD833\uDD10\uD833\uDD11\uD833\uDD12\uD833\uDD13\uD833\uDD14\uD833\uDD15\uD833\uDD16\uD833\uDD17\uD833\uDD18\uD833\uDD19\uD833\uDD1A\uD833\uDD1B\uD833\uDD1C\uD833\uDD1D\uD833\uDD1E\uD833\uDD1F\uD833\uDD03\uD833\uDD20\uD833\uDD21\uD833\uDD22\uD833\uDD23\uD833\uDD24\uD833\uDD25\uD833\uDD26\uD833\uDD27\uD833\uDD28\uD833\uDD29\uD833\uDD2A\uD833\uDD2B\uD833\uDD2C\uD833\uDD2D\uD833\uDD2E\uD833\uDD2F\uD833\uDD30\uD833\uDD31\uD833\uDD32\uD833\uDD33\uD833\uDD34\uD833\uDD35\uD83E\uDF85 \uD833\uDD36\uD833\uDD37\uD833\uDD38\uD833\uDD39\uD833\uDD3A\uD833\uDD3B\uD833\uDD3C\uD833\uDD3D\uD833\uDD3E\uD833\uDD3F\uD833\uDD40\uD833\uDD41\uD833\uDD42\uD833\uDD43\uD833\uDD44▖\uD833\uDD45\uD833\uDD46\uD833\uDD47\uD833\uDD48▌\uD833\uDD49\uD833\uDD4A\uD833\uDD4B\uD833\uDD4C▞\uD833\uDD4D\uD833\uDD4E\uD833\uDD4F\uD833\uDD50▛\uD833\uDD51\uD833\uDD52\uD833\uDD53\uD833\uDD54\uD833\uDD55\uD833\uDD56\uD833\uDD57\uD833\uDD58\uD833\uDD59\uD833\uDD5A\uD833\uDD5B\uD833\uDD5C\uD833\uDD5D\uD833\uDD5E\uD833\uDD5F\uD833\uDD60\uD833\uDD61\uD833\uDD62\uD833\uDD63\uD833\uDD64\uD833\uDD65\uD833\uDD66\uD833\uDD67\uD833\uDD68\uD833\uDD69\uD833\uDD6A\uD833\uDD6B\uD833\uDD6C\uD833\uDD6D\uD833\uDD6E\uD833\uDD6F\uD833\uDD70 \uD833\uDD71\uD833\uDD72\uD833\uDD73\uD833\uDD74\uD833\uDD75\uD833\uDD76\uD833\uDD77\uD833\uDD78\uD833\uDD79\uD833\uDD7A\uD833\uDD7B\uD833\uDD7C\uD833\uDD7D\uD833\uDD7E\uD833\uDD7F\uD833\uDD80\uD833\uDD81\uD833\uDD82\uD833\uDD83\uD833\uDD84\uD833\uDD85\uD833\uDD86\uD833\uDD87\uD833\uDD88\uD833\uDD89\uD833\uDD8A\uD833\uDD8B\uD833\uDD8C\uD833\uDD8D\uD833\uDD8E\uD833\uDD8F▗\uD833\uDD90\uD833\uDD91\uD833\uDD92\uD833\uDD93▚\uD833\uDD94\uD833\uDD95\uD833\uDD96\uD833\uDD97▐\uD833\uDD98\uD833\uDD99\uD833\uDD9A\uD833\uDD9B▜\uD833\uDD9C\uD833\uDD9D\uD833\uDD9E\uD833\uDD9F\uD833\uDDA0\uD833\uDDA1\uD833\uDDA2\uD833\uDDA3\uD833\uDDA4\uD833\uDDA5\uD833\uDDA6\uD833\uDDA7\uD833\uDDA8\uD833\uDDA9\uD833\uDDAA\uD833\uDDAB▂\uD833\uDDAC\uD833\uDDAD\uD833\uDDAE\uD833\uDDAF\uD833\uDDB0\uD833\uDDB1\uD833\uDDB2\uD833\uDDB3\uD833\uDDB4\uD833\uDDB5\uD833\uDDB6\uD833\uDDB7\uD833\uDDB8\uD833\uDDB9\uD833\uDDBA\uD833\uDDBB\uD833\uDDBC\uD833\uDDBD\uD833\uDDBE\uD833\uDDBF\uD833\uDDC0\uD833\uDDC1\uD833\uDDC2\uD833\uDDC3\uD833\uDDC4\uD833\uDDC5\uD833\uDDC6\uD833\uDDC7\uD833\uDDC8\uD833\uDDC9\uD833\uDDCA\uD833\uDDCB\uD833\uDDCC\uD833\uDDCD\uD833\uDDCE\uD833\uDDCF\uD833\uDDD0\uD833\uDDD1\uD833\uDDD2\uD833\uDDD3\uD833\uDDD4\uD833\uDDD5\uD833\uDDD6\uD833\uDDD7\uD833\uDDD8\uD833\uDDD9\uD833\uDDDA▄\uD833\uDDDB\uD833\uDDDC\uD833\uDDDD\uD833\uDDDE▙\uD833\uDDDF\uD833\uDDE0\uD833\uDDE1\uD833\uDDE2▟\uD833\uDDE3▆\uD833\uDDE4\uD833\uDDE5█")
276150
+ };
276151
+ var QUAD = {
276152
+ rx: 2,
276153
+ ry: 2,
276154
+ table: Array.from(" ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█")
276155
+ };
276156
+ function detectTier(env = process.env) {
276157
+ const term = env.TERM ?? "";
276158
+ const prog = env.TERM_PROGRAM ?? "";
276159
+ if (prog === "ghostty" || term.includes("ghostty"))
276160
+ return "octant";
276161
+ if (env.KITTY_WINDOW_ID || term.includes("kitty"))
276162
+ return "octant";
276163
+ if (env.WEZTERM_PANE || env.WEZTERM_EXECUTABLE)
276164
+ return "octant";
276165
+ if (term.startsWith("foot") || prog === "contour")
276166
+ return "octant";
276167
+ return "quad";
276168
+ }
276169
+ function coverageGrid(rows, cols, aspect, pixelH) {
276170
+ const worldH = rows * pixelH;
276171
+ const worldW = cols * aspect;
276172
+ const radius = Math.min(worldH, worldW) / 2;
276173
+ const cx = worldW / 2;
276174
+ const cy = worldH / 2;
276175
+ const step = 1 / SAMPLES;
276176
+ const grid = [];
276177
+ for (let py = 0;py < rows; py++) {
276178
+ const y0 = py * pixelH;
276179
+ const y1 = y0 + pixelH;
276180
+ const dyLo = y0 <= cy && cy <= y1 ? 0 : Math.min(Math.abs(y0 - cy), Math.abs(y1 - cy));
276181
+ const dyHi = Math.max(Math.abs(y0 - cy), Math.abs(y1 - cy));
276182
+ const line = [];
276183
+ for (let px = 0;px < cols; px++) {
276184
+ const x0 = px * aspect;
276185
+ const x1 = x0 + aspect;
276186
+ const dxLo = x0 <= cx && cx <= x1 ? 0 : Math.min(Math.abs(x0 - cx), Math.abs(x1 - cx));
276187
+ const dxHi = Math.max(Math.abs(x0 - cx), Math.abs(x1 - cx));
276188
+ if (Math.hypot(dxLo, dyLo) >= radius) {
276189
+ line.push(0);
276190
+ continue;
276191
+ }
276192
+ if (Math.hypot(dxHi, dyHi) <= radius) {
276193
+ line.push(1);
276194
+ continue;
276195
+ }
276196
+ let hits = 0;
276197
+ for (let j = 0;j < SAMPLES; j++) {
276198
+ const dy = y0 + (j + 0.5) * step * pixelH - cy;
276199
+ for (let i = 0;i < SAMPLES; i++) {
276200
+ const dx = x0 + (i + 0.5) * step * aspect - cx;
276201
+ if (Math.hypot(dx, dy) <= radius)
276202
+ hits++;
276203
+ }
276204
+ }
276205
+ line.push(hits / (SAMPLES * SAMPLES));
276206
+ }
276207
+ grid.push(line);
276208
+ }
276209
+ return grid;
276210
+ }
276211
+ function carveGap(grid, ry) {
276212
+ const n = grid.length;
276213
+ const thick = Math.max(1, Math.round(GAP_HEIGHT_ROWS * ry));
276214
+ const centre = GAP_ROW >= 0 ? GAP_ROW * ry : n + GAP_ROW * ry;
276215
+ const start = Math.max(0, Math.min(n - thick, centre - thick / 2));
276216
+ for (let i = Math.trunc(start);i < Math.trunc(start + thick); i++) {
276217
+ grid[i].fill(0);
276218
+ }
276219
+ }
276220
+ function logoRows(color, tier = detectTier()) {
276221
+ const { rx, ry, table } = tier === "octant" ? OCTANT : QUAD;
276222
+ const grid = coverageGrid(ROWS * ry, LOGO_COLS * rx, ASPECT / rx, 1 / ry);
276223
+ carveGap(grid, ry);
276224
+ const fg = color ? source_default.hex(color) : (s) => s;
276225
+ const bg = color ? source_default.bgHex(color) : (s) => s;
276226
+ const full = (1 << rx * ry) - 1;
276227
+ const out = [];
276228
+ for (let r = 0;r < ROWS; r++) {
276229
+ let row = "";
276230
+ for (let c = 0;c < LOGO_COLS; c++) {
276231
+ let mask = 0;
276232
+ for (let sr = 0;sr < ry; sr++) {
276233
+ for (let sc = 0;sc < rx; sc++) {
276234
+ if (grid[r * ry + sr][c * rx + sc] >= FILL)
276235
+ mask |= 1 << sr * rx + sc;
276236
+ }
276237
+ }
276238
+ const glyph = table[mask];
276239
+ if (glyph === " ")
276240
+ row += " ";
276241
+ else if (mask === full && color)
276242
+ row += bg(" ");
276243
+ else
276244
+ row += fg(glyph);
276245
+ }
276246
+ out.push(row);
276247
+ }
276248
+ return out;
276249
+ }
276250
+
275854
276251
  // src/cli/commands/code/paste.ts
275855
276252
  import { PassThrough as PassThrough3 } from "node:stream";
275856
276253
  var START = "\x1B[200~";
@@ -275924,6 +276321,393 @@ function createPasteFriendlyStdin(real) {
275924
276321
  return proxy;
275925
276322
  }
275926
276323
 
276324
+ // src/core/resources/apps/pending.ts
276325
+ var CHOICE_TOOLS = new Set([
276326
+ "ask_clarifying_questions",
276327
+ "ask_plan_questions"
276328
+ ]);
276329
+ var SECRET_TOOLS = new Set(["set_secrets"]);
276330
+ var PERMISSION_TOOLS = new Set(["request_agent_tool_permissions"]);
276331
+ var BROWSER_TOOLS = new Set([
276332
+ "connect_github_account",
276333
+ "request_oauth_authorization",
276334
+ "register_workspace_connector",
276335
+ "configure_psp_credentials",
276336
+ "plaid_connect"
276337
+ ]);
276338
+ var str2 = (v) => typeof v === "string" && v.trim() ? v.trim() : undefined;
276339
+ function parseArgs(raw) {
276340
+ try {
276341
+ const parsed = JSON.parse(raw ?? "");
276342
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
276343
+ } catch {
276344
+ return {};
276345
+ }
276346
+ }
276347
+ function questionsFrom(args) {
276348
+ const raw = Array.isArray(args.questions) ? args.questions : [];
276349
+ return raw.flatMap((q) => {
276350
+ if (!q || typeof q !== "object")
276351
+ return [];
276352
+ const item = q;
276353
+ const question = str2(item.question);
276354
+ if (!question)
276355
+ return [];
276356
+ const options = (Array.isArray(item.options) ? item.options : []).flatMap((o) => {
276357
+ const opt = o;
276358
+ const label = str2(opt?.label);
276359
+ return label ? [{ label, description: str2(opt.description) }] : [];
276360
+ });
276361
+ return [
276362
+ {
276363
+ question,
276364
+ description: str2(item.description),
276365
+ options,
276366
+ multiSelect: item.multi_select === true
276367
+ }
276368
+ ];
276369
+ });
276370
+ }
276371
+ function secretsFrom(args) {
276372
+ const raw = Array.isArray(args.secrets_schema) ? args.secrets_schema : [];
276373
+ return raw.flatMap((s) => {
276374
+ const item = s;
276375
+ const name = str2(item?.secretName) ?? str2(item?.name);
276376
+ return name ? [{ name, description: str2(item.description) }] : [];
276377
+ });
276378
+ }
276379
+ function permissionKey(row) {
276380
+ switch (row.type) {
276381
+ case "entity":
276382
+ return row.entity_name ? `entity:${row.entity_name}` : null;
276383
+ case "backend_function":
276384
+ return row.function_name ? `backend_function:${row.function_name}` : null;
276385
+ case "app_user_connector":
276386
+ return row.connector_id ? `app_user_connector:${row.connector_id}` : null;
276387
+ default:
276388
+ return null;
276389
+ }
276390
+ }
276391
+ function permissionsFrom(args) {
276392
+ const raw = Array.isArray(args.requested_permissions) ? args.requested_permissions : [];
276393
+ return raw.flatMap((r) => {
276394
+ const row = r;
276395
+ const key = permissionKey(row);
276396
+ if (!key)
276397
+ return [];
276398
+ const ops = Array.isArray(row.allowed_operations) ? ` (${row.allowed_operations.join(", ")})` : "";
276399
+ const target = str2(row.entity_name) ?? str2(row.function_name) ?? str2(row.connector_name) ?? key;
276400
+ return [
276401
+ { key, label: `${row.type}: ${target}${ops}`, reason: str2(row.reason) }
276402
+ ];
276403
+ });
276404
+ }
276405
+ function guardFrom(results) {
276406
+ const value = typeof results === "string" ? (() => {
276407
+ try {
276408
+ return JSON.parse(results);
276409
+ } catch {
276410
+ return null;
276411
+ }
276412
+ })() : results;
276413
+ if (!value || typeof value !== "object")
276414
+ return null;
276415
+ const g = value;
276416
+ if (!str2(g.guard))
276417
+ return null;
276418
+ return { title: `${g.guard}: needs your approval`, detail: str2(g.reason) };
276419
+ }
276420
+ function humanize(tool) {
276421
+ return tool.replace(/_/g, " ");
276422
+ }
276423
+ function pendingInputs(messages) {
276424
+ const out = [];
276425
+ for (const message of messages) {
276426
+ for (const call of message.tool_calls ?? []) {
276427
+ if (call.status !== "waiting_for_user_input")
276428
+ continue;
276429
+ const args = parseArgs(call.arguments_string);
276430
+ const base = {
276431
+ toolCallId: call.id,
276432
+ messageId: message.id,
276433
+ tool: call.name
276434
+ };
276435
+ const summary = str2(args.summary);
276436
+ if (CHOICE_TOOLS.has(call.name)) {
276437
+ out.push({
276438
+ ...base,
276439
+ kind: "choice",
276440
+ title: summary ?? "The agent has a few questions",
276441
+ questions: questionsFrom(args)
276442
+ });
276443
+ } else if (SECRET_TOOLS.has(call.name)) {
276444
+ out.push({
276445
+ ...base,
276446
+ kind: "secrets",
276447
+ title: summary ?? "The agent needs secrets",
276448
+ secrets: secretsFrom(args)
276449
+ });
276450
+ } else if (PERMISSION_TOOLS.has(call.name)) {
276451
+ out.push({
276452
+ ...base,
276453
+ kind: "permissions",
276454
+ title: summary ?? "Grant the app's agent these permissions?",
276455
+ detail: str2(args.reason),
276456
+ permissions: permissionsFrom(args)
276457
+ });
276458
+ } else if (BROWSER_TOOLS.has(call.name)) {
276459
+ out.push({
276460
+ ...base,
276461
+ kind: "browser",
276462
+ title: summary ?? humanize(call.name),
276463
+ detail: str2(args.reason)
276464
+ });
276465
+ } else {
276466
+ const guard = guardFrom(call.results);
276467
+ const integration = str2(args.integration_type);
276468
+ out.push({
276469
+ ...base,
276470
+ kind: "approval",
276471
+ title: guard?.title ?? (integration ? `Enable ${integration}?` : summary ?? `${humanize(call.name)}?`),
276472
+ detail: guard?.detail ?? (integration ? summary : undefined)
276473
+ });
276474
+ }
276475
+ }
276476
+ }
276477
+ return out;
276478
+ }
276479
+ function choiceAnswers(questions, selections) {
276480
+ const answers = questions.flatMap((q, index) => {
276481
+ const sel = selections[index];
276482
+ if (!sel || sel.labels.length === 0 && !sel.customText)
276483
+ return [];
276484
+ const answer = { question_index: index };
276485
+ if (q.multiSelect) {
276486
+ if (sel.labels.length)
276487
+ answer.selected_labels = sel.labels;
276488
+ } else if (sel.labels[0]) {
276489
+ answer.selected_label = sel.labels[0];
276490
+ }
276491
+ if (sel.customText)
276492
+ answer.custom_text = sel.customText;
276493
+ return [answer];
276494
+ });
276495
+ return { answers };
276496
+ }
276497
+
276498
+ // src/cli/commands/code/pending-card.ts
276499
+ function openCard(pending) {
276500
+ return {
276501
+ pending,
276502
+ step: 0,
276503
+ cursor: 0,
276504
+ selections: (pending.questions ?? []).map(() => ({ labels: [] })),
276505
+ granted: new Set((pending.permissions ?? []).map((p) => p.key)),
276506
+ typing: pending.kind === "secrets" ? "secret" : null,
276507
+ secretValues: {}
276508
+ };
276509
+ }
276510
+ var done = (action, input = {}) => ({ state: null, submit: { action, input } });
276511
+ var later = { state: null, dismissed: true };
276512
+ function choiceRows(state) {
276513
+ return (state.pending.questions?.[state.step]?.options.length ?? 0) + 1;
276514
+ }
276515
+ function advanceChoice(state) {
276516
+ const questions = state.pending.questions ?? [];
276517
+ if (state.step + 1 < questions.length) {
276518
+ return {
276519
+ state: { ...state, step: state.step + 1, cursor: 0, typing: null }
276520
+ };
276521
+ }
276522
+ return done("approved", choiceAnswers(questions, state.selections));
276523
+ }
276524
+ function choiceKey(state, key) {
276525
+ const question = state.pending.questions?.[state.step];
276526
+ if (!question)
276527
+ return done("approved", { answers: [] });
276528
+ const rows = choiceRows(state);
276529
+ const custom = state.cursor === rows - 1;
276530
+ const sel = state.selections[state.step] ?? { labels: [] };
276531
+ const setSel = (next) => {
276532
+ const selections = [...state.selections];
276533
+ selections[state.step] = next;
276534
+ return { ...state, selections };
276535
+ };
276536
+ switch (key) {
276537
+ case "up":
276538
+ return { state: { ...state, cursor: (state.cursor - 1 + rows) % rows } };
276539
+ case "down":
276540
+ return { state: { ...state, cursor: (state.cursor + 1) % rows } };
276541
+ case "space": {
276542
+ if (custom)
276543
+ return { state: { ...state, typing: "custom" } };
276544
+ const label = question.options[state.cursor].label;
276545
+ const labels = question.multiSelect ? sel.labels.includes(label) ? sel.labels.filter((l) => l !== label) : [...sel.labels, label] : [label];
276546
+ return { state: setSel({ ...sel, labels }) };
276547
+ }
276548
+ case "enter": {
276549
+ if (custom)
276550
+ return { state: { ...state, typing: "custom" } };
276551
+ if (question.multiSelect) {
276552
+ if (sel.labels.length === 0 && !sel.customText)
276553
+ return { state };
276554
+ return advanceChoice(state);
276555
+ }
276556
+ const label = question.options[state.cursor].label;
276557
+ return advanceChoice(setSel({ labels: [label] }));
276558
+ }
276559
+ case "s":
276560
+ return done("approved", { answers: [] });
276561
+ case "escape":
276562
+ return later;
276563
+ default:
276564
+ return { state };
276565
+ }
276566
+ }
276567
+ function permissionsKey(state, key) {
276568
+ const rows = state.pending.permissions ?? [];
276569
+ switch (key) {
276570
+ case "up":
276571
+ return {
276572
+ state: {
276573
+ ...state,
276574
+ cursor: (state.cursor - 1 + rows.length) % rows.length
276575
+ }
276576
+ };
276577
+ case "down":
276578
+ return { state: { ...state, cursor: (state.cursor + 1) % rows.length } };
276579
+ case "space": {
276580
+ const k = rows[state.cursor]?.key;
276581
+ if (!k)
276582
+ return { state };
276583
+ const granted = new Set(state.granted);
276584
+ if (granted.has(k))
276585
+ granted.delete(k);
276586
+ else
276587
+ granted.add(k);
276588
+ return { state: { ...state, granted } };
276589
+ }
276590
+ case "enter":
276591
+ case "y":
276592
+ return done("approved", {
276593
+ approved_permission_keys: rows.map((r) => r.key).filter((k) => state.granted.has(k))
276594
+ });
276595
+ case "n":
276596
+ return done("rejected");
276597
+ case "escape":
276598
+ return later;
276599
+ default:
276600
+ return { state };
276601
+ }
276602
+ }
276603
+ function cardKey(state, key) {
276604
+ if (state.typing) {
276605
+ if (key === "escape") {
276606
+ return state.typing === "secret" ? later : { state: { ...state, typing: null } };
276607
+ }
276608
+ return { state };
276609
+ }
276610
+ switch (state.pending.kind) {
276611
+ case "choice":
276612
+ return choiceKey(state, key);
276613
+ case "permissions":
276614
+ return permissionsKey(state, key);
276615
+ default:
276616
+ if (key === "y" || key === "enter")
276617
+ return done("approved");
276618
+ if (key === "n")
276619
+ return done("rejected");
276620
+ if (key === "escape")
276621
+ return later;
276622
+ return { state };
276623
+ }
276624
+ }
276625
+ function cardText(state, text) {
276626
+ const value = text.trim();
276627
+ if (state.typing === "custom") {
276628
+ if (!value)
276629
+ return { state: { ...state, typing: null } };
276630
+ const selections = [...state.selections];
276631
+ const current = selections[state.step] ?? { labels: [] };
276632
+ selections[state.step] = { ...current, customText: value };
276633
+ return advanceChoice({ ...state, selections, typing: null });
276634
+ }
276635
+ if (state.typing === "secret") {
276636
+ const fields = state.pending.secrets ?? [];
276637
+ const field = fields[state.step];
276638
+ if (!field || !value)
276639
+ return { state };
276640
+ const secretValues = { ...state.secretValues, [field.name]: value };
276641
+ if (state.step + 1 < fields.length) {
276642
+ return { state: { ...state, secretValues, step: state.step + 1 } };
276643
+ }
276644
+ return done("approved", { secrets: secretValues });
276645
+ }
276646
+ return { state };
276647
+ }
276648
+ function cardLines(state) {
276649
+ const p = state.pending;
276650
+ const head = [source_default.bold(`⏸ ${p.title}`)];
276651
+ if (p.detail)
276652
+ head.push(source_default.dim(` ${p.detail}`));
276653
+ switch (p.kind) {
276654
+ case "choice": {
276655
+ const q = p.questions?.[state.step];
276656
+ if (!q)
276657
+ return [...head, source_default.dim(" (no questions) · Enter to continue")];
276658
+ const total = p.questions?.length ?? 1;
276659
+ const sel = state.selections[state.step] ?? { labels: [] };
276660
+ const rows = q.options.map((o, i) => {
276661
+ const on = sel.labels.includes(o.label);
276662
+ const mark = q.multiSelect ? on ? "☑" : "☐" : on ? "●" : "○";
276663
+ const text = `${state.cursor === i ? "▸" : " "} ${mark} ${o.label}${o.description ? source_default.dim(` — ${o.description}`) : ""}`;
276664
+ return state.cursor === i ? source_default.cyan(text) : text;
276665
+ });
276666
+ const customRow = `${state.cursor === q.options.length ? "▸" : " "} ✎ something else${sel.customText ? source_default.dim(` — ${sel.customText}`) : ""}`;
276667
+ rows.push(state.cursor === q.options.length ? source_default.cyan(customRow) : customRow);
276668
+ return [
276669
+ ...head,
276670
+ ` ${source_default.bold(q.question)} ${source_default.dim(`(${state.step + 1}/${total})`)}`,
276671
+ ...q.description ? [source_default.dim(` ${q.description}`)] : [],
276672
+ ...rows.map((r) => ` ${r}`),
276673
+ source_default.dim(q.multiSelect ? " ↑↓ move · space toggle · Enter next · s skip all · Esc later" : " ↑↓ move · Enter choose · s skip all · Esc later")
276674
+ ];
276675
+ }
276676
+ case "permissions": {
276677
+ const rows = (p.permissions ?? []).map((r, i) => {
276678
+ const text = `${state.cursor === i ? "▸" : " "} ${state.granted.has(r.key) ? "☑" : "☐"} ${r.label}${r.reason ? source_default.dim(` — ${r.reason}`) : ""}`;
276679
+ return ` ${state.cursor === i ? source_default.cyan(text) : text}`;
276680
+ });
276681
+ return [
276682
+ ...head,
276683
+ ...rows,
276684
+ source_default.dim(" space toggle · Enter grant ticked · n reject all · Esc later")
276685
+ ];
276686
+ }
276687
+ case "secrets": {
276688
+ const fields = p.secrets ?? [];
276689
+ const rows = fields.map((f, i) => {
276690
+ const filled = f.name in state.secretValues;
276691
+ const mark = filled ? source_default.green("✓") : i === state.step ? "▸" : "○";
276692
+ return ` ${mark} ${f.name}${f.description ? source_default.dim(` — ${f.description}`) : ""}`;
276693
+ });
276694
+ return [
276695
+ ...head,
276696
+ ...rows,
276697
+ source_default.dim(" type the value below (hidden) · Enter next · Esc later")
276698
+ ];
276699
+ }
276700
+ case "browser":
276701
+ return [
276702
+ ...head,
276703
+ source_default.dim(" finish this step in the editor (footer link), then press y"),
276704
+ source_default.dim(" y continue · n reject · Esc later")
276705
+ ];
276706
+ default:
276707
+ return [...head, source_default.dim(" y approve · n reject · Esc later")];
276708
+ }
276709
+ }
276710
+
275927
276711
  // src/cli/commands/code/session-engine.ts
275928
276712
  var POLL_MS = 1000;
275929
276713
  function createSessionEngine(options) {
@@ -275943,6 +276727,28 @@ function createSessionEngine(options) {
275943
276727
  let awaitingTurn = options.awaitingTurnLabel ?? null;
275944
276728
  const awaitingSince = Date.now();
275945
276729
  let lastEventAt = Date.now();
276730
+ let pending = [];
276731
+ const answered = new Set;
276732
+ const answer = (p, action, input = {}) => {
276733
+ answered.add(p.toolCallId);
276734
+ pending = pending.filter((x) => x.toolCallId !== p.toolCallId);
276735
+ const verb = action === "rejected" ? source_default.red("✗ rejected") : Object.keys(input).length ? source_default.green("→ answered") : source_default.green("✓ approved");
276736
+ options.onLine(`${verb} ${source_default.dim("—")} ${p.title}`);
276737
+ pendingSubmitAt = Date.now();
276738
+ sendsInFlight++;
276739
+ answerToolCall({ toolCallId: p.toolCallId, messageId: p.messageId, action, input }, options.branchId).catch((error) => {
276740
+ const status = error instanceof ApiError ? error.statusCode : undefined;
276741
+ const edgeDrop = status === 502 || status === 503 || status === 504 || /timeout|gateway/i.test(error instanceof Error ? error.message : "");
276742
+ if (edgeDrop && (turnStartedAt != null || pendingSubmitAt == null)) {
276743
+ return;
276744
+ }
276745
+ answered.delete(p.toolCallId);
276746
+ pendingSubmitAt = null;
276747
+ options.onLine(source_default.red(`✗ answer failed: ${error instanceof Error ? error.message : String(error)}`));
276748
+ }).finally(() => {
276749
+ sendsInFlight--;
276750
+ });
276751
+ };
275946
276752
  const submit = (raw) => {
275947
276753
  const typed = raw.trim();
275948
276754
  if (!typed)
@@ -275983,25 +276789,38 @@ function createSessionEngine(options) {
275983
276789
  return;
275984
276790
  }
275985
276791
  const events = diffConversation(diffState, messages);
276792
+ const waiting = pendingInputs(messages);
276793
+ for (const id of answered) {
276794
+ if (!waiting.some((w) => w.toolCallId === id))
276795
+ answered.delete(id);
276796
+ }
276797
+ pending = waiting.filter((w) => !answered.has(w.toolCallId));
275986
276798
  if (!prime) {
275987
276799
  for (const event of events) {
275988
276800
  if (event.kind === "tool_start") {
276801
+ const startedAt = Date.now();
275989
276802
  running.set(event.id, {
275990
276803
  alias: toolAlias(event.name),
275991
276804
  label: event.label,
275992
276805
  summary: event.summary,
275993
- startedAt: Date.now()
276806
+ startedAt
275994
276807
  });
276808
+ options.onLine({ running: event, startedAt });
275995
276809
  continue;
275996
276810
  }
275997
- let elapsedMs;
275998
276811
  if (event.kind === "tool_end") {
275999
276812
  const started = running.get(event.id)?.startedAt;
276000
- if (started != null)
276001
- elapsedMs = Date.now() - started;
276002
276813
  running.delete(event.id);
276814
+ lastEventAt = Date.now();
276815
+ options.onLine({
276816
+ event,
276817
+ elapsedMs: started != null ? Date.now() - started : undefined
276818
+ });
276819
+ continue;
276003
276820
  }
276004
- const line = eventLine(event, elapsedMs);
276821
+ const line = eventLine(event, undefined, {
276822
+ waitingHint: "answer in the card below"
276823
+ });
276005
276824
  if (line != null) {
276006
276825
  lastEventAt = Date.now();
276007
276826
  options.onLine(line);
@@ -276068,6 +276887,7 @@ function createSessionEngine(options) {
276068
276887
  });
276069
276888
  },
276070
276889
  submit,
276890
+ answer,
276071
276891
  status() {
276072
276892
  let phase = "idle";
276073
276893
  if (awaitingTurn != null)
@@ -276090,7 +276910,8 @@ function createSessionEngine(options) {
276090
276910
  turnStartedAt,
276091
276911
  runningTool,
276092
276912
  lastTurnMs,
276093
- lastTurnOk
276913
+ lastTurnOk,
276914
+ pending
276094
276915
  };
276095
276916
  },
276096
276917
  turnRunning() {
@@ -276122,6 +276943,17 @@ function exitAltScreen() {
276122
276943
  altScreenActive = false;
276123
276944
  process.stdout.write("\x1B[?1007l\x1B[?1049l");
276124
276945
  }
276946
+ function withEntry(items, entry) {
276947
+ if (typeof entry !== "string" && "event" in entry) {
276948
+ const i = items.findIndex((e) => typeof e !== "string" && ("running" in e) && e.running.id === entry.event.id);
276949
+ if (i >= 0) {
276950
+ const next = [...items];
276951
+ next[i] = entry;
276952
+ return next;
276953
+ }
276954
+ }
276955
+ return [...items, entry];
276956
+ }
276125
276957
  function statusText(status, musingSeed) {
276126
276958
  const frame = FRAMES2[Math.floor(Date.now() / 120) % FRAMES2.length];
276127
276959
  switch (status.phase) {
@@ -276129,15 +276961,8 @@ function statusText(status, musingSeed) {
276129
276961
  return source_default.dim(`${frame} ${status.awaitingLabel} (${formatDuration2(Date.now() - status.awaitingSince)})`);
276130
276962
  case "running": {
276131
276963
  const turnFor = formatDuration2(Date.now() - (status.turnStartedAt ?? Date.now()));
276132
- const tool = status.runningTool;
276133
- if (tool) {
276134
- const toolFor = Math.round((Date.now() - tool.startedAt) / 1000);
276135
- const others = tool.others > 0 ? ` (+${tool.others})` : "";
276136
- const what = tool.label || `${tool.alias}${tool.summary ? ` ${tool.summary}` : ""}`;
276137
- return source_default.dim(`${frame} ${what}${others} · ${toolFor}s (turn ${turnFor})`);
276138
- }
276139
276964
  const quiet = status.quietForMs > 60000 ? " · a long private step — details render in the editor" : "";
276140
- return `${source_default.magenta("✻")} ${source_default.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`;
276965
+ return `${source_default.magenta(shimmer())} ${source_default.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`;
276141
276966
  }
276142
276967
  case "sending":
276143
276968
  return source_default.dim(`${frame} sending…`);
@@ -276151,18 +276976,19 @@ function statusText(status, musingSeed) {
276151
276976
  function SessionView({ engine, footer, subscribe }) {
276152
276977
  const { exit } = use_app_default();
276153
276978
  const [items, setItems] = import_react23.useState([]);
276979
+ const [verbose, setVerbose] = import_react23.useState(false);
276154
276980
  const [input, setInput] = import_react23.useState("");
276155
276981
  const [scroll, setScroll] = import_react23.useState(0);
276156
276982
  const [, tick] = import_react23.useReducer((x) => x + 1, 0);
276157
276983
  const [musingSeed] = import_react23.useState(() => Math.floor(Math.random() * 97));
276158
276984
  const [currentModel, setCurrentModel] = import_react23.useState(null);
276159
276985
  const [pickerIndex, setPickerIndex] = import_react23.useState(null);
276986
+ const [card, setCard] = import_react23.useState(null);
276987
+ const dismissedRef = import_react23.useRef(new Set);
276160
276988
  const maxScrollRef = import_react23.useRef(0);
276161
276989
  const meIdRef = import_react23.useRef(null);
276162
- const emit = (line) => setItems((h) => [...h, `${line}
276163
- `]);
276164
- import_react23.useEffect(() => subscribe((line) => setItems((h) => [...h, `${line}
276165
- `])), [subscribe]);
276990
+ const emit = (entry) => setItems((h) => withEntry(h, entry));
276991
+ import_react23.useEffect(() => subscribe((entry) => setItems((h) => withEntry(h, entry))), [subscribe]);
276166
276992
  import_react23.useEffect(() => {
276167
276993
  const timer = setInterval(tick, 120);
276168
276994
  return () => clearInterval(timer);
@@ -276204,6 +277030,29 @@ function SessionView({ engine, footer, subscribe }) {
276204
277030
  emit(source_default.red(` /model: ${error instanceof Error ? error.message : String(error)}`));
276205
277031
  }
276206
277032
  };
277033
+ const applyCard = (outcome) => {
277034
+ if (outcome.submit && card) {
277035
+ engine.answer(card.pending, outcome.submit.action, outcome.submit.input);
277036
+ }
277037
+ if (outcome.dismissed && card) {
277038
+ dismissedRef.current.add(card.pending.toolCallId);
277039
+ }
277040
+ setCard(outcome.state);
277041
+ };
277042
+ const pending = engine.status().pending;
277043
+ import_react23.useEffect(() => {
277044
+ if (card) {
277045
+ if (!pending.some((p) => p.toolCallId === card.pending.toolCallId)) {
277046
+ setCard(null);
277047
+ }
277048
+ return;
277049
+ }
277050
+ if (pickerIndex !== null)
277051
+ return;
277052
+ const next = pending.find((p) => !dismissedRef.current.has(p.toolCallId));
277053
+ if (next)
277054
+ setCard(openCard(next));
277055
+ }, [pending, card, pickerIndex]);
276207
277056
  use_input_default((char, key) => {
276208
277057
  if (pickerIndex !== null) {
276209
277058
  if (key.upArrow)
@@ -276219,6 +277068,23 @@ function SessionView({ engine, footer, subscribe }) {
276219
277068
  }
276220
277069
  return;
276221
277070
  }
277071
+ if (key.tab && !card) {
277072
+ const next = pending.find((p) => dismissedRef.current.has(p.toolCallId));
277073
+ if (next) {
277074
+ dismissedRef.current.delete(next.toolCallId);
277075
+ setCard(openCard(next));
277076
+ }
277077
+ return;
277078
+ }
277079
+ if (card) {
277080
+ const mapped = key.upArrow ? "up" : key.downArrow ? "down" : key.return ? "enter" : key.escape ? "escape" : char === " " ? "space" : char === "y" || char === "n" || char === "s" ? char : null;
277081
+ if (card.typing && mapped !== "escape")
277082
+ return;
277083
+ if (mapped)
277084
+ applyCard(cardKey(card, mapped));
277085
+ if (mapped || !card.typing)
277086
+ return;
277087
+ }
276222
277088
  if (key.ctrl && char === "c") {
276223
277089
  if (input)
276224
277090
  setInput("");
@@ -276230,6 +277096,10 @@ function SessionView({ engine, footer, subscribe }) {
276230
277096
  exit();
276231
277097
  return;
276232
277098
  }
277099
+ if (key.ctrl && char === "o") {
277100
+ setVerbose((v) => !v);
277101
+ return;
277102
+ }
276233
277103
  if (key.upArrow) {
276234
277104
  setScroll((s) => Math.min(s + 3, maxScrollRef.current));
276235
277105
  return;
@@ -276255,14 +277125,17 @@ function SessionView({ engine, footer, subscribe }) {
276255
277125
  });
276256
277126
  const columns = process.stdout.columns || 80;
276257
277127
  const rows = process.stdout.rows || 24;
276258
- const width = Math.min(columns, 100);
277128
+ const width = columns;
276259
277129
  const innerWidth = Math.max(10, width - 4);
276260
277130
  const inputRows = Math.max(1, Math.ceil((input.length + 3) / innerWidth));
276261
277131
  const pickerOpen = pickerIndex !== null;
276262
- const inputBlockHeight = pickerOpen ? MODELS.length + 3 : inputRows + 2;
277132
+ const cardOpen = card !== null && !card.typing;
277133
+ const cardRows = card ? cardLines(card).length : 0;
277134
+ const inputBlockHeight = pickerOpen ? MODELS.length + 3 : cardOpen ? cardRows + 2 : inputRows + 2 + (card?.typing ? 1 : 0);
276263
277135
  const widgetHeight = inputBlockHeight + 3 + (footer.length ? 1 : 0);
276264
277136
  const viewHeight = Math.max(3, rows - widgetHeight - 1);
276265
- const lines = items.flatMap((item) => hardWrapAnsi(item, columns));
277137
+ const lines = items.flatMap((item) => hardWrapAnsi(`${renderEntry(item, { verbose, foldHint: "Ctrl+O to expand" })}
277138
+ `, columns));
276266
277139
  const maxScroll = Math.max(0, lines.length - viewHeight);
276267
277140
  maxScrollRef.current = maxScroll;
276268
277141
  const clamped = Math.min(scroll, maxScroll);
@@ -276309,29 +277182,64 @@ function SessionView({ engine, footer, subscribe }) {
276309
277182
  }, m.name, false, undefined, this);
276310
277183
  })
276311
277184
  ]
276312
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
277185
+ }, undefined, true, undefined, this) : cardOpen && card ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
277186
+ flexDirection: "column",
277187
+ borderStyle: "round",
277188
+ borderColor: "yellow",
277189
+ paddingX: 1,
277190
+ width,
277191
+ children: cardLines(card).map((line, i) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
277192
+ wrap: "truncate-end",
277193
+ children: line
277194
+ }, i, false, undefined, this))
277195
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
277196
+ flexDirection: "column",
276313
277197
  borderStyle: "round",
276314
- borderColor: "gray",
277198
+ borderColor: card?.typing ? "yellow" : "gray",
276315
277199
  paddingX: 1,
276316
277200
  width,
276317
277201
  children: [
276318
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
276319
- color: "cyan",
276320
- children: "❯ "
276321
- }, undefined, false, undefined, this),
276322
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(build_default, {
276323
- value: input,
276324
- onChange: setInput,
276325
- onSubmit: (value) => {
276326
- const trimmed = value.trim();
276327
- if (trimmed === "/model" || trimmed.startsWith("/model ")) {
276328
- runModelSlash(trimmed.slice("/model".length).trim());
276329
- } else if (trimmed) {
276330
- engine.submit(value);
276331
- }
276332
- setInput("");
276333
- }
276334
- }, undefined, false, undefined, this)
277202
+ card?.typing === "secret" && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
277203
+ wrap: "truncate-end",
277204
+ children: [
277205
+ source_default.bold(card.pending.secrets?.[card.step]?.name ?? "secret"),
277206
+ source_default.dim(" — value is hidden · Enter to save · Esc to cancel")
277207
+ ]
277208
+ }, undefined, true, undefined, this),
277209
+ card?.typing === "custom" && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
277210
+ wrap: "truncate-end",
277211
+ children: [
277212
+ source_default.bold(card.pending.questions?.[card.step]?.question ?? ""),
277213
+ source_default.dim(" — your own answer · Enter to save · Esc to go back")
277214
+ ]
277215
+ }, undefined, true, undefined, this),
277216
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
277217
+ children: [
277218
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
277219
+ color: "cyan",
277220
+ children: "❯ "
277221
+ }, undefined, false, undefined, this),
277222
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(build_default, {
277223
+ value: input,
277224
+ onChange: setInput,
277225
+ mask: card?.typing === "secret" ? "•" : undefined,
277226
+ onSubmit: (value) => {
277227
+ if (card?.typing) {
277228
+ applyCard(cardText(card, value));
277229
+ setInput("");
277230
+ return;
277231
+ }
277232
+ const trimmed = value.trim();
277233
+ if (trimmed === "/model" || trimmed.startsWith("/model ")) {
277234
+ runModelSlash(trimmed.slice("/model".length).trim());
277235
+ } else if (trimmed) {
277236
+ engine.submit(value);
277237
+ }
277238
+ setInput("");
277239
+ }
277240
+ }, undefined, false, undefined, this)
277241
+ ]
277242
+ }, undefined, true, undefined, this)
276335
277243
  ]
276336
277244
  }, undefined, true, undefined, this),
276337
277245
  footer.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
@@ -276345,48 +277253,16 @@ function SessionView({ engine, footer, subscribe }) {
276345
277253
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
276346
277254
  dimColor: true,
276347
277255
  wrap: "truncate-end",
276348
- children: pickerOpen ? " ↑↓ to move · Enter to select · Esc to cancel" : engine.turnRunning() ? " Esc to stop · type to queue · scroll to read · Ctrl+C to exit" : " Enter to send · /model to switch model · Esc for live · Ctrl+C to exit"
277256
+ children: pickerOpen ? " ↑↓ to move · Enter to select · Esc to cancel" : cardOpen ? " the agent is waiting on you · answer above, or Esc for later" : pending.length > 0 && !card ? ` ⏸ ${pending[0].title} — Tab to answer · Ctrl+C to exit` : engine.turnRunning() ? " Esc to stop · type to queue · scroll to read · Ctrl+O to expand · Ctrl+C to exit" : " Enter to send · /model to switch model · Esc for live · Ctrl+O to expand · Ctrl+C to exit"
276349
277257
  }, undefined, false, undefined, this)
276350
277258
  ]
276351
277259
  }, undefined, true, undefined, this);
276352
277260
  }
276353
- var LOGO_ROWS = 6;
276354
- var LOGO_GAP_SUBROW = 8;
276355
- var QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█";
276356
- function buildLogoRows() {
276357
- const sy = 2 * LOGO_ROWS;
276358
- const sx = 2 * sy;
276359
- const cx = (sx - 1) / 2;
276360
- const cy = (sy - 1) / 2;
276361
- const rad = sy / 2 - 0.5;
276362
- const on = (px, py) => {
276363
- if (py === LOGO_GAP_SUBROW)
276364
- return false;
276365
- const dx = (px - cx) * 0.5;
276366
- const dy = py - cy;
276367
- return dx * dx + dy * dy <= rad * rad;
276368
- };
276369
- const rows = [];
276370
- for (let ty = 0;ty < sy; ty += 2) {
276371
- let row = "";
276372
- for (let tx = 0;tx < sx; tx += 2) {
276373
- const bits = (on(tx, ty) ? 1 : 0) | (on(tx + 1, ty) ? 2 : 0) | (on(tx, ty + 1) ? 4 : 0) | (on(tx + 1, ty + 1) ? 8 : 0);
276374
- row += QUAD[bits];
276375
- }
276376
- rows.push(row.trim());
276377
- }
276378
- return rows.map((row) => row.trim());
276379
- }
276380
277261
  function renderHeader(who, mode) {
276381
277262
  const orange = source_default.hex(BRAND_ORANGE);
276382
277263
  const cwd = process.cwd().replace(process.env.HOME ?? "", "~");
276383
- const logo = buildLogoRows();
276384
- const logoW = Math.max(...logo.map((r) => r.length));
276385
- const center = (s) => {
276386
- const total = Math.max(0, logoW - s.length);
276387
- const left = Math.floor(total / 2);
276388
- return " ".repeat(left) + s + " ".repeat(total - left);
276389
- };
277264
+ const logo = logoRows(BRAND_ORANGE);
277265
+ const logoW = LOGO_COLS;
276390
277266
  const text = [
276391
277267
  `${orange.bold("Base44 Code")} ${source_default.dim(`v${package_default.version}`)}`,
276392
277268
  source_default.bold(who ? `Welcome back, ${who}!` : "Welcome!"),
@@ -276398,7 +277274,7 @@ function renderHeader(who, mode) {
276398
277274
  const textTop = Math.max(0, Math.floor((logo.length - text.length) / 2));
276399
277275
  const out = [];
276400
277276
  for (let i = 0;i < height; i++) {
276401
- const left = i < logo.length ? orange(center(logo[i])) : " ".repeat(logoW);
277277
+ const left = i < logo.length ? logo[i] : " ".repeat(logoW);
276402
277278
  const right = text[i - textTop] ?? "";
276403
277279
  out.push(` ${left} ${right}`.trimEnd());
276404
277280
  }
@@ -276502,6 +277378,7 @@ async function runGenesisSession(options) {
276502
277378
  runningTool: null,
276503
277379
  lastTurnMs: null,
276504
277380
  lastTurnOk: true,
277381
+ pending: [],
276505
277382
  quietForMs: 0
276506
277383
  };
276507
277384
  const genesis = {
@@ -276557,6 +277434,9 @@ async function runGenesisSession(options) {
276557
277434
  }
276558
277435
  return IDLE_STATUS;
276559
277436
  },
277437
+ answer(p, action, input) {
277438
+ inner?.answer(p, action, input);
277439
+ },
276560
277440
  turnRunning() {
276561
277441
  return inner?.turnRunning() ?? creating;
276562
277442
  }
@@ -276589,21 +277469,21 @@ async function runGenesisSession(options) {
276589
277469
 
276590
277470
  // src/cli/commands/code/index.ts
276591
277471
  var BRAND_ORANGE2 = "#E86B3C";
276592
- async function bootstrapApp(prompt, footer, emit, onCreated, importRepo) {
277472
+ async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path, wixInstance) {
276593
277473
  let app;
276594
277474
  try {
276595
- app = await createAndLinkApp({ prompt, importRepo });
277475
+ app = await createAndLinkApp({ prompt, importRepo, path, wixInstance });
276596
277476
  } catch (error) {
276597
277477
  for (const line of await githubReauthLines(error) ?? [])
276598
277478
  emit(line);
276599
277479
  throw error;
276600
277480
  }
276601
277481
  onCreated(app);
276602
- footer.push(source_default.dim(`dir ./${app.dirName}`));
277482
+ footer.push(source_default.dim(`dir ${app.here ? "./" : `./${app.dirName}`}`));
276603
277483
  if (app.repoUrl)
276604
277484
  footer.push(terminalLink("repo", app.repoUrl));
276605
277485
  footer.push(terminalLink("editor", app.editorUrl));
276606
- emit(source_default.dim(`linked ./${app.dirName} (cd ${app.dirName} after the session)`));
277486
+ emit(source_default.dim(app.here ? "linked ./ (this directory)" : `linked ./${app.dirName} (cd ${app.dirName} after the session)`));
276607
277487
  const branchId = await resolveActiveBranchId().catch(() => {
276608
277488
  return;
276609
277489
  });
@@ -276627,46 +277507,61 @@ async function bootstrapApp(prompt, footer, emit, onCreated, importRepo) {
276627
277507
  }
276628
277508
  };
276629
277509
  }
276630
- async function codeAction({ log }, options) {
277510
+ async function codeAction({ log }, options, appId) {
277511
+ const orange = source_default.hex(BRAND_ORANGE2);
277512
+ const chip = (label) => source_default.dim(`${orange("●")} ${label}`);
276631
277513
  if (process.stdout.isTTY !== true) {
276632
277514
  throw new InvalidInputError("base44 code is an interactive session and needs a terminal.");
276633
277515
  }
276634
277516
  let linked = false;
276635
277517
  try {
276636
- await initAppContext();
277518
+ await initAppContext(appId ? { appId } : {});
276637
277519
  linked = true;
276638
277520
  } catch {}
276639
277521
  if (linked) {
276640
- if (options.import) {
276641
- throw new InvalidInputError("--import creates a new app; run it outside a linked project.");
277522
+ if (options.import || options.path || options.wixInstance) {
277523
+ throw new InvalidInputError("--import, --path and --wix-instance create a new app; run them outside a linked project, without --app-id.");
276642
277524
  }
277525
+ const { id, projectRoot } = getAppContext();
277526
+ const state = await assertBuilderApp(id);
276643
277527
  const branchId = await resolveActiveBranchId().catch(() => {
276644
277528
  return;
276645
277529
  });
276646
277530
  await runInteractiveSession({
276647
277531
  branchId,
276648
- footer: [],
277532
+ footer: [chip(appTypeChip(state))],
276649
277533
  primeFirstPoll: true,
276650
277534
  idleHint: "what should the agent do next?"
276651
277535
  });
276652
- log.message(source_default.dim(`app dir ${getAppContext().projectRoot}`));
277536
+ if (projectRoot) {
277537
+ log.message(source_default.dim(`app dir ${projectRoot}`));
277538
+ return {
277539
+ outroMessage: "Session closed. Run `base44 code` here to resume."
277540
+ };
277541
+ }
276653
277542
  return {
276654
- outroMessage: "Session closed. Run `base44 code` here to resume."
277543
+ outroMessage: `Session closed. Resume with \`base44 code --app-id ${id}\`.`
276655
277544
  };
276656
277545
  }
277546
+ if (options.wixInstance && options.import) {
277547
+ throw new InvalidInputError("--wix-instance and --import are exclusive.");
277548
+ }
277549
+ const wixInstance = options.wixInstance?.trim() ? {
277550
+ signedInstance: options.wixInstance.trim(),
277551
+ wixClientId: options.wixClientId?.trim() || undefined
277552
+ } : undefined;
276657
277553
  let created;
276658
- const orange = source_default.hex(BRAND_ORANGE2);
276659
277554
  const footer = [
276660
- source_default.dim(`${orange("●")} ${options.import ? "import" : "builder"}`)
277555
+ chip(options.import ? repoLabel(options.import) : wixInstance ? "web app · wix" : "web app")
276661
277556
  ];
276662
277557
  await runGenesisSession({
276663
- idleHint: options.import ? "describe what to build over the repo" : "describe the app you want to build",
277558
+ idleHint: options.import ? "describe what to build over the repository" : "describe the app you want to build · have one already? base44 code --app-id <id>, or base44 link",
276664
277559
  creatingLabel: options.import ? "importing the repository" : "creating your app",
276665
- modeLabel: options.import ? `Import — ${options.import}` : "Builder — Base44 template + agent",
277560
+ modeLabel: options.import ? `Repository — ${repoLabel(options.import)}` : wixInstance ? "Web app Wix launch (connector connected first)" : "Web app — Base44 template + builder agent",
276666
277561
  footer,
276667
277562
  createApp: (prompt, emit) => bootstrapApp(prompt, footer, emit, (app) => {
276668
277563
  created = app;
276669
- }, options.import)
277564
+ }, options.import, options.path, wixInstance)
276670
277565
  });
276671
277566
  if (!created) {
276672
277567
  return { outroMessage: "Session closed. No app was created." };
@@ -276678,7 +277573,7 @@ async function codeAction({ log }, options) {
276678
277573
  }
276679
277574
  function getCodeCommand() {
276680
277575
  const command = new Base44Command("code", { requireAppContext: false });
276681
- command.description("Open Base44 Code: an interactive agent session in an empty directory your first prompt creates the app (add --import <repo> to build over an existing repository)").option("--import <repo>", "Build over an existing GitHub repository instead of the Base44 template").action(codeAction);
277576
+ command.description("Open Base44 Code, an interactive builder session. In a linked directory (or with --app-id <id>) it opens that app; anywhere else your first prompt creates one (--import <repo> to build over your own repository). Attach a directory to an existing app with base44 link.").option("--import <repo>", "Build over an existing GitHub repository instead of the Base44 template").option("--path <dir>", "Directory to link the new app to (default: the current directory when empty, else ./<name>)").option("--wix-instance <token>", "Create the new app through the Wix route with this signed instance (your first prompt is what the agent runs)").option("--wix-client-id <id>", "The companion OAuth app's client id").action((ctx, options) => codeAction(ctx, options, command.optsWithGlobals().appId));
276682
277577
  return command;
276683
277578
  }
276684
277579
 
@@ -277476,10 +278371,10 @@ function getConnectorsListAvailableCommand() {
277476
278371
  }
277477
278372
 
277478
278373
  // src/cli/commands/connectors/pull.ts
277479
- import { dirname as dirname19, join as join27, resolve as resolve8 } from "node:path";
278374
+ import { dirname as dirname19, join as join27, resolve as resolve9 } from "node:path";
277480
278375
  async function resolveConnectorsDir(options) {
277481
278376
  if (!getAppContext().projectRoot) {
277482
- return resolve8(options.dir ?? "connectors");
278377
+ return resolve9(options.dir ?? "connectors");
277483
278378
  }
277484
278379
  const { project } = await readProjectConfig();
277485
278380
  return join27(dirname19(project.configPath), project.connectorsDir);
@@ -277523,10 +278418,10 @@ function getConnectorsPullCommand() {
277523
278418
  }
277524
278419
 
277525
278420
  // src/cli/commands/connectors/push.ts
277526
- import { resolve as resolve9 } from "node:path";
278421
+ import { resolve as resolve10 } from "node:path";
277527
278422
  async function readConnectorsToPush(options) {
277528
278423
  if (!getAppContext().projectRoot) {
277529
- return readAllConnectors(resolve9(options.dir ?? "connectors"));
278424
+ return readAllConnectors(resolve10(options.dir ?? "connectors"));
277530
278425
  }
277531
278426
  const { connectors } = await readProjectConfig();
277532
278427
  return connectors;
@@ -277995,7 +278890,7 @@ function getBuildCommand() {
277995
278890
  }
277996
278891
 
277997
278892
  // src/cli/commands/project/create.ts
277998
- import { basename as basename6, resolve as resolve10 } from "node:path";
278893
+ import { basename as basename7, resolve as resolve11 } from "node:path";
277999
278894
  var import_kebabCase2 = __toESM(require_kebabCase(), 1);
278000
278895
 
278001
278896
  // src/cli/commands/project/scaffold-shared.ts
@@ -278160,8 +279055,8 @@ async function createInteractive(options, ctx) {
278160
279055
  name: () => {
278161
279056
  return options.name ? Promise.resolve(options.name) : Ze({
278162
279057
  message: "What is the name of your project?",
278163
- placeholder: basename6(process.cwd()),
278164
- initialValue: basename6(process.cwd()),
279058
+ placeholder: basename7(process.cwd()),
279059
+ initialValue: basename7(process.cwd()),
278165
279060
  validate: (value) => {
278166
279061
  if (!value || value.trim().length === 0) {
278167
279062
  return "Every project deserves a name";
@@ -278191,7 +279086,7 @@ async function createInteractive(options, ctx) {
278191
279086
  }, ctx);
278192
279087
  }
278193
279088
  async function createNonInteractive(options, ctx) {
278194
- ctx.log.info(`Creating a new project at ${resolve10(options.path)}`);
279089
+ ctx.log.info(`Creating a new project at ${resolve11(options.path)}`);
278195
279090
  const template = await getTemplateById(options.template ?? DEFAULT_TEMPLATE_ID);
278196
279091
  return await executeCreate({
278197
279092
  template,
@@ -278215,7 +279110,7 @@ async function executeCreate({
278215
279110
  }, ctx) {
278216
279111
  const { log, runTask } = ctx;
278217
279112
  const name = rawName.trim();
278218
- const resolvedPath = resolve10(projectPath);
279113
+ const resolvedPath = resolve11(projectPath);
278219
279114
  const organizationId = await resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive);
278220
279115
  const { projectId } = await runTask("Setting up your project...", async () => {
278221
279116
  return await createProjectFiles({
@@ -278856,7 +279751,7 @@ function getLogsCommand() {
278856
279751
  }
278857
279752
 
278858
279753
  // src/cli/commands/project/scaffold.ts
278859
- import { basename as basename7, resolve as resolve11 } from "node:path";
279754
+ import { basename as basename8, resolve as resolve12 } from "node:path";
278860
279755
  function resolveAppId(options) {
278861
279756
  const appId = options.appId;
278862
279757
  if (!appId) {
@@ -278872,8 +279767,8 @@ function resolveAppId(options) {
278872
279767
  async function scaffoldAction(ctx, name, options, command) {
278873
279768
  const { log, runTask } = ctx;
278874
279769
  const appId = resolveAppId(command.optsWithGlobals());
278875
- const resolvedPath = resolve11("./");
278876
- const projectName = (name ?? basename7(resolvedPath)).trim();
279770
+ const resolvedPath = resolve12("./");
279771
+ const projectName = (name ?? basename8(resolvedPath)).trim();
278877
279772
  const template = await getTemplateById("backend-only");
278878
279773
  log.info(`Scaffolding project at ${resolvedPath}`);
278879
279774
  const { projectId } = await runTask("Setting up your project...", async () => {
@@ -279190,6 +280085,21 @@ function getSandboxListDirectoryCommand() {
279190
280085
  return new Base44Command("ls", { supportsBranch: true }).description("List directory entries in an app's remote sandbox").argument("[path]", "Directory relative to the app root (default: app root)").option("--recursive", "List nested entries").option("--max-depth <n>", "Max depth when recursive (1-10, default 3)").option("--include-hidden", "Include dotfiles").action(listDirectoryAction);
279191
280086
  }
279192
280087
 
280088
+ // src/cli/commands/sandbox/preview.ts
280089
+ async function previewAction(ctx) {
280090
+ const url = await ctx.runTask("Resolving preview URL (boots the sandbox if needed)", () => getPreviewUrl());
280091
+ if (ctx.jsonMode)
280092
+ return { stdout: `${JSON.stringify({ preview_url: url })}
280093
+ ` };
280094
+ ctx.log.message(url);
280095
+ return { outroMessage: "Preview is live." };
280096
+ }
280097
+ function getSandboxPreviewCommand() {
280098
+ const command = new Base44Command("preview");
280099
+ command.description("Print the app's live preview URL").action(previewAction);
280100
+ return command;
280101
+ }
280102
+
279193
280103
  // src/cli/commands/sandbox/read-file.ts
279194
280104
  async function readFileAction({ runTask, branchId }, paths, options) {
279195
280105
  const { id: appId } = getAppContext();
@@ -279243,7 +280153,7 @@ Examples:
279243
280153
 
279244
280154
  // src/cli/commands/sandbox/index.ts
279245
280155
  function getSandboxCommand() {
279246
- return new Command2("sandbox").description("Develop an app remotely via its server-side sandbox").addCommand(getSandboxListDirectoryCommand()).addCommand(getSandboxReadFileCommand()).addCommand(getSandboxWriteFileCommand()).addCommand(getSandboxEditFileCommand()).addCommand(getSandboxGrepCommand()).addCommand(getSandboxRunCommandCommand()).addCommand(getSandboxCheckpointCommand());
280156
+ return new Command2("sandbox").description("Develop an app remotely via its server-side sandbox").addCommand(getSandboxListDirectoryCommand()).addCommand(getSandboxReadFileCommand()).addCommand(getSandboxWriteFileCommand()).addCommand(getSandboxEditFileCommand()).addCommand(getSandboxGrepCommand()).addCommand(getSandboxRunCommandCommand()).addCommand(getSandboxCheckpointCommand()).addCommand(getSandboxPreviewCommand());
279247
280157
  }
279248
280158
 
279249
280159
  // src/cli/commands/secrets/delete.ts
@@ -279289,7 +280199,7 @@ function getSecretsListCommand() {
279289
280199
  }
279290
280200
 
279291
280201
  // src/cli/commands/secrets/set.ts
279292
- import { resolve as resolve12 } from "node:path";
280202
+ import { resolve as resolve13 } from "node:path";
279293
280203
  function parseEntries(entries) {
279294
280204
  const secrets = {};
279295
280205
  for (const entry of entries) {
@@ -279320,7 +280230,7 @@ async function setSecretsAction({ log, runTask }, entries, options) {
279320
280230
  validateInput(entries, options);
279321
280231
  let secrets;
279322
280232
  if (options.envFile) {
279323
- secrets = await parseEnvFile(resolve12(options.envFile));
280233
+ secrets = await parseEnvFile(resolve13(options.envFile));
279324
280234
  if (Object.keys(secrets).length === 0) {
279325
280235
  throw new InvalidInputError("The env file contains no valid KEY=VALUE entries.");
279326
280236
  }
@@ -279349,7 +280259,7 @@ function getSecretsCommand() {
279349
280259
  }
279350
280260
 
279351
280261
  // src/cli/commands/site/deploy.ts
279352
- import { resolve as resolve13 } from "node:path";
280262
+ import { resolve as resolve14 } from "node:path";
279353
280263
  async function deployAction2(ctx, options) {
279354
280264
  const { isNonInteractive } = ctx;
279355
280265
  if (isNonInteractive && !options.yes) {
@@ -279427,7 +280337,7 @@ async function deployTarball({ runTask }, project) {
279427
280337
  }
279428
280338
  function siteOutputDir(project) {
279429
280339
  const outputDirectory = project.site?.outputDirectory;
279430
- return outputDirectory ? resolve13(project.root, outputDirectory) : null;
280340
+ return outputDirectory ? resolve14(project.root, outputDirectory) : null;
279431
280341
  }
279432
280342
  function getSiteDeployCommand() {
279433
280343
  const command = new Base44Command("deploy").description("Deploy built site files to Base44 hosting").option("-y, --yes", "Skip confirmation prompt").option("--build", "Build the site before deploying (skips the prompt)").option("--no-build", "Deploy without building (skips the prompt)");
@@ -281929,7 +282839,7 @@ function createCustomIntegrationRoutes(remoteProxy, logger) {
281929
282839
 
281930
282840
  // src/cli/dev/dev-server/watcher.ts
281931
282841
  import { EventEmitter as EventEmitter6 } from "node:events";
281932
- import { relative as relative9 } from "node:path";
282842
+ import { relative as relative10 } from "node:path";
281933
282843
 
281934
282844
  // ../../node_modules/chokidar/index.js
281935
282845
  import { EventEmitter as EventEmitter5 } from "node:events";
@@ -283614,7 +284524,7 @@ class WatchBase44 extends EventEmitter6 {
283614
284524
  ignoreInitial: true
283615
284525
  });
283616
284526
  watcher.on("all", import_debounce4.default(async (_event, path) => {
283617
- this.emit("change", name, relative9(targetPath, path));
284527
+ this.emit("change", name, relative10(targetPath, path));
283618
284528
  }, WATCH_DEBOUNCE_MS));
283619
284529
  watcher.on("error", (err) => {
283620
284530
  this.logger.error(`Watch handler failed for ${targetPath}`, err);
@@ -284046,7 +284956,7 @@ async function runScript(options) {
284046
284956
  }
284047
284957
  }
284048
284958
  // src/cli/commands/exec.ts
284049
- function readStdin2() {
284959
+ function readStdin3() {
284050
284960
  return new Promise((resolve, reject) => {
284051
284961
  let data = "";
284052
284962
  process.stdin.setEncoding("utf-8");
@@ -284090,7 +285000,7 @@ async function execAction({ app, isNonInteractive }, options) {
284090
285000
  if (!isNonInteractive) {
284091
285001
  throw noInputError;
284092
285002
  }
284093
- const code = await readStdin2();
285003
+ const code = await readStdin3();
284094
285004
  if (!code.trim()) {
284095
285005
  throw noInputError;
284096
285006
  }
@@ -284124,7 +285034,7 @@ Examples:
284124
285034
  }
284125
285035
 
284126
285036
  // src/cli/commands/project/eject.ts
284127
- import { resolve as resolve17 } from "node:path";
285037
+ import { resolve as resolve18 } from "node:path";
284128
285038
  var import_kebabCase3 = __toESM(require_kebabCase(), 1);
284129
285039
  async function eject(ctx, options, command) {
284130
285040
  const { log, runTask, isNonInteractive } = ctx;
@@ -284188,7 +285098,7 @@ async function eject(ctx, options, command) {
284188
285098
  Ne("Operation cancelled.");
284189
285099
  throw new CLIExitError(0);
284190
285100
  }
284191
- const resolvedPath = resolve17(selectedPath);
285101
+ const resolvedPath = resolve18(selectedPath);
284192
285102
  await runTask("Downloading your project's code...", async (updateMessage) => {
284193
285103
  await createProjectFilesForExistingProject({
284194
285104
  projectId,
@@ -284270,7 +285180,7 @@ function createProgram(context) {
284270
285180
  program.addCommand(getSecretsCommand());
284271
285181
  program.addCommand(getSandboxCommand());
284272
285182
  program.addCommand(getBranchesCommand());
284273
- program.addCommand(getAppCommand());
285183
+ program.addCommand(getBuilderCommand());
284274
285184
  program.addCommand(getCodeCommand());
284275
285185
  program.addCommand(getAuthCommand());
284276
285186
  program.addCommand(getSiteCommand());
@@ -288271,6 +289181,13 @@ function getFullCommandName(command) {
288271
289181
  }
288272
289182
  return parts.join(" ");
288273
289183
  }
289184
+ var SENSITIVE_OPTION = /secret|token|password|launch|instance|key$/i;
289185
+ function redactSensitiveOptions(options) {
289186
+ return Object.fromEntries(Object.entries(options).map(([k, v]) => [
289187
+ k,
289188
+ SENSITIVE_OPTION.test(k) && v != null && v !== false ? "[redacted]" : v
289189
+ ]));
289190
+ }
288274
289191
  function addCommandInfoToErrorReporter(program, errorReporter) {
288275
289192
  program.hook("preAction", (_, actionCommand) => {
288276
289193
  const fullCommandName = getFullCommandName(actionCommand);
@@ -288278,7 +289195,7 @@ function addCommandInfoToErrorReporter(program, errorReporter) {
288278
289195
  command: {
288279
289196
  name: fullCommandName,
288280
289197
  args: actionCommand.args,
288281
- options: actionCommand.opts()
289198
+ options: redactSensitiveOptions(actionCommand.opts())
288282
289199
  }
288283
289200
  });
288284
289201
  });
@@ -288325,4 +289242,4 @@ export {
288325
289242
  runCLI
288326
289243
  };
288327
289244
 
288328
- //# debugId=3E91D87E1D11D84464756E2164756E21
289245
+ //# debugId=F9AF4FAF927903AE64756E2164756E21