@base44-preview/cli 0.1.15-pr.630.c53fe4d → 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
@@ -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();
@@ -269956,7 +269960,7 @@ async function saveBuilderModel(userId, modelId) {
269956
269960
  });
269957
269961
  } catch (error) {
269958
269962
  if (error instanceof HTTPError && error.response.status === 400) {
269959
- 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.");
269960
269964
  }
269961
269965
  throw await ApiError.fromHttpError(error, "saving your model choice");
269962
269966
  }
@@ -270037,7 +270041,25 @@ var RESULT_LINE_MAX = 110;
270037
270041
  var DIFF_LINES_MAX = 12;
270038
270042
  var ERROR_LINES_MAX = 8;
270039
270043
  function renderEntry(entry, options = {}) {
270040
- return typeof entry === "string" ? entry : eventLine(entry.event, entry.elapsedMs, 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}`;
270041
270063
  }
270042
270064
  function fold2(lines, max, options) {
270043
270065
  if (options.verbose || lines.length <= max)
@@ -270137,7 +270159,7 @@ function eventLine(event, elapsedMs, options = {}) {
270137
270159
  return null;
270138
270160
  case "waiting": {
270139
270161
  const what = event.label || toolAlias(event.name);
270140
- 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"})`);
270141
270163
  }
270142
270164
  case "tool_end": {
270143
270165
  const alias = toolAlias(event.name);
@@ -270186,6 +270208,14 @@ ${indent(shown)}`;
270186
270208
  }
270187
270209
  }
270188
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
+ }
270189
270219
  var MUSINGS = [
270190
270220
  "Shmoozing",
270191
270221
  "Shmoogling",
@@ -270249,7 +270279,7 @@ function createTurnStream(interactive, write = (text) => process.stdout.write(te
270249
270279
  return;
270250
270280
  const lines = [
270251
270281
  ...footer.length ? ["", ...footer] : [],
270252
- source_default.dim(`${FRAMES[frame]} ${statusLabel()}`)
270282
+ running.size === 0 ? `${source_default.magenta(shimmer())} ${source_default.dim(statusLabel())}` : source_default.dim(`${FRAMES[frame]} ${statusLabel()}`)
270253
270283
  ];
270254
270284
  write(lines.join(`
270255
270285
  `));
@@ -270325,6 +270355,10 @@ var CreatedAppSchema = object({
270325
270355
  name: string2().nullish(),
270326
270356
  imported_repo_url: string2().nullish()
270327
270357
  });
270358
+ var WixCreatedAppSchema = object({
270359
+ app_id: string2().min(1),
270360
+ client_creation_id: string2().min(1)
270361
+ });
270328
270362
  var AppStateSchema = object({
270329
270363
  id: string2(),
270330
270364
  app_type: string2().nullish(),
@@ -270364,7 +270398,8 @@ var ConversationMessageSchema = object({
270364
270398
  name: string2(),
270365
270399
  arguments_string: string2().nullish(),
270366
270400
  status: string2().nullish(),
270367
- results: unknown().nullish()
270401
+ results: unknown().nullish(),
270402
+ waiting_on: object({ kind: string2().nullish() }).nullish()
270368
270403
  })).nullish()
270369
270404
  });
270370
270405
  var FullConversationSchema = object({
@@ -270381,6 +270416,24 @@ function parseOrThrow(schema, payload, what) {
270381
270416
  function branchScope(branchId) {
270382
270417
  return branchId ? { branch_id: branchId } : {};
270383
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
+ }
270384
270437
  async function createApp(options) {
270385
270438
  let response;
270386
270439
  try {
@@ -270438,6 +270491,25 @@ async function getAppState(appId) {
270438
270491
  }
270439
270492
  return parseOrThrow(AppStateSchema, await response.json(), "app status");
270440
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
+ }
270441
270513
  async function sendTurn(content, branchId) {
270442
270514
  let response;
270443
270515
  try {
@@ -270526,7 +270598,15 @@ async function createAndLinkApp(options) {
270526
270598
  if (await appConfigExists(targetDir)) {
270527
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.`);
270528
270600
  }
270529
- const created = options.importRepo ? await createImportedApp({
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({
270530
270610
  appName: name,
270531
270611
  repoUrl: options.importRepo,
270532
270612
  sourceMode: options.mode ?? "direct",
@@ -270550,7 +270630,8 @@ async function createAndLinkApp(options) {
270550
270630
  repoUrl: created.imported_repo_url ?? undefined,
270551
270631
  dirName,
270552
270632
  targetDir,
270553
- here
270633
+ here,
270634
+ ...clientCreationId ? { clientCreationId } : {}
270554
270635
  };
270555
270636
  }
270556
270637
  function repoLabel(url) {
@@ -270902,6 +270983,12 @@ function getSendCommand() {
270902
270983
  // src/cli/commands/builder/new.ts
270903
270984
  var POLL_TIMEOUT_MS = 20 * 60000;
270904
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
+ }
270905
270992
  async function newAction({ log, runTask, jsonMode }, prompt, options) {
270906
270993
  if (options.mode && !MODES.includes(options.mode)) {
270907
270994
  throw new InvalidInputError("--mode must be direct, fork, or copy.");
@@ -270909,6 +270996,20 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270909
270996
  if ((options.mode || options.repoName || options.fromBranch) && !options.import) {
270910
270997
  throw new InvalidInputError("--mode, --repo-name and --from-branch apply only with --import <repo>.");
270911
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
+ }
270912
271013
  if (!prompt && !options.import) {
270913
271014
  throw new InvalidInputError('Describe the app ("<prompt>") or pass --import <repo>.');
270914
271015
  }
@@ -270925,7 +271026,8 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270925
271026
  mode: options.mode,
270926
271027
  repoName: options.repoName,
270927
271028
  fromBranch: options.fromBranch,
270928
- path: options.path
271029
+ path: options.path,
271030
+ wixInstance
270929
271031
  }));
270930
271032
  } catch (error) {
270931
271033
  for (const line of await githubReauthLines(error) ?? [])
@@ -270939,11 +271041,15 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270939
271041
  repo_url: app.repoUrl ?? null,
270940
271042
  editor_url: app.editorUrl,
270941
271043
  dir: app.dirName,
270942
- path: app.targetDir
271044
+ path: app.targetDir,
271045
+ ...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
270943
271046
  });
270944
271047
  } else if (!jsonMode) {
270945
271048
  if (app.repoUrl)
270946
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
+ }
270947
271053
  log.message(source_default.dim(`editor ${app.editorUrl}`));
270948
271054
  log.message(source_default.dim(`linked ${app.here ? "./ (this directory)" : `./${app.dirName}`}`));
270949
271055
  }
@@ -270994,7 +271100,8 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
270994
271100
  preview_url: previewUrl ?? null,
270995
271101
  dir: app.dirName,
270996
271102
  path: app.targetDir,
270997
- status: finalState ?? "created"
271103
+ status: finalState ?? "created",
271104
+ ...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
270998
271105
  })}
270999
271106
  `
271000
271107
  };
@@ -271019,7 +271126,7 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
271019
271126
  }
271020
271127
  function getNewCommand() {
271021
271128
  const command = new Base44Command("new", { requireAppContext: false });
271022
- 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").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);
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);
271023
271130
  return command;
271024
271131
  }
271025
271132
 
@@ -276214,6 +276321,393 @@ function createPasteFriendlyStdin(real) {
276214
276321
  return proxy;
276215
276322
  }
276216
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
+
276217
276711
  // src/cli/commands/code/session-engine.ts
276218
276712
  var POLL_MS = 1000;
276219
276713
  function createSessionEngine(options) {
@@ -276233,6 +276727,28 @@ function createSessionEngine(options) {
276233
276727
  let awaitingTurn = options.awaitingTurnLabel ?? null;
276234
276728
  const awaitingSince = Date.now();
276235
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
+ };
276236
276752
  const submit = (raw) => {
276237
276753
  const typed = raw.trim();
276238
276754
  if (!typed)
@@ -276273,15 +276789,23 @@ function createSessionEngine(options) {
276273
276789
  return;
276274
276790
  }
276275
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));
276276
276798
  if (!prime) {
276277
276799
  for (const event of events) {
276278
276800
  if (event.kind === "tool_start") {
276801
+ const startedAt = Date.now();
276279
276802
  running.set(event.id, {
276280
276803
  alias: toolAlias(event.name),
276281
276804
  label: event.label,
276282
276805
  summary: event.summary,
276283
- startedAt: Date.now()
276806
+ startedAt
276284
276807
  });
276808
+ options.onLine({ running: event, startedAt });
276285
276809
  continue;
276286
276810
  }
276287
276811
  if (event.kind === "tool_end") {
@@ -276294,7 +276818,9 @@ function createSessionEngine(options) {
276294
276818
  });
276295
276819
  continue;
276296
276820
  }
276297
- const line = eventLine(event);
276821
+ const line = eventLine(event, undefined, {
276822
+ waitingHint: "answer in the card below"
276823
+ });
276298
276824
  if (line != null) {
276299
276825
  lastEventAt = Date.now();
276300
276826
  options.onLine(line);
@@ -276361,6 +276887,7 @@ function createSessionEngine(options) {
276361
276887
  });
276362
276888
  },
276363
276889
  submit,
276890
+ answer,
276364
276891
  status() {
276365
276892
  let phase = "idle";
276366
276893
  if (awaitingTurn != null)
@@ -276383,7 +276910,8 @@ function createSessionEngine(options) {
276383
276910
  turnStartedAt,
276384
276911
  runningTool,
276385
276912
  lastTurnMs,
276386
- lastTurnOk
276913
+ lastTurnOk,
276914
+ pending
276387
276915
  };
276388
276916
  },
276389
276917
  turnRunning() {
@@ -276415,6 +276943,17 @@ function exitAltScreen() {
276415
276943
  altScreenActive = false;
276416
276944
  process.stdout.write("\x1B[?1007l\x1B[?1049l");
276417
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
+ }
276418
276957
  function statusText(status, musingSeed) {
276419
276958
  const frame = FRAMES2[Math.floor(Date.now() / 120) % FRAMES2.length];
276420
276959
  switch (status.phase) {
@@ -276422,15 +276961,8 @@ function statusText(status, musingSeed) {
276422
276961
  return source_default.dim(`${frame} ${status.awaitingLabel} (${formatDuration2(Date.now() - status.awaitingSince)})`);
276423
276962
  case "running": {
276424
276963
  const turnFor = formatDuration2(Date.now() - (status.turnStartedAt ?? Date.now()));
276425
- const tool = status.runningTool;
276426
- if (tool) {
276427
- const toolFor = Math.round((Date.now() - tool.startedAt) / 1000);
276428
- const others = tool.others > 0 ? ` (+${tool.others})` : "";
276429
- const what = tool.label || `${tool.alias}${tool.summary ? ` ${tool.summary}` : ""}`;
276430
- return source_default.dim(`${frame} ${what}${others} · ${toolFor}s (turn ${turnFor})`);
276431
- }
276432
276964
  const quiet = status.quietForMs > 60000 ? " · a long private step — details render in the editor" : "";
276433
- return `${source_default.magenta("✻")} ${source_default.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`;
276965
+ return `${source_default.magenta(shimmer())} ${source_default.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`;
276434
276966
  }
276435
276967
  case "sending":
276436
276968
  return source_default.dim(`${frame} sending…`);
@@ -276451,10 +276983,12 @@ function SessionView({ engine, footer, subscribe }) {
276451
276983
  const [musingSeed] = import_react23.useState(() => Math.floor(Math.random() * 97));
276452
276984
  const [currentModel, setCurrentModel] = import_react23.useState(null);
276453
276985
  const [pickerIndex, setPickerIndex] = import_react23.useState(null);
276986
+ const [card, setCard] = import_react23.useState(null);
276987
+ const dismissedRef = import_react23.useRef(new Set);
276454
276988
  const maxScrollRef = import_react23.useRef(0);
276455
276989
  const meIdRef = import_react23.useRef(null);
276456
- const emit = (entry) => setItems((h) => [...h, entry]);
276457
- import_react23.useEffect(() => subscribe((entry) => setItems((h) => [...h, entry])), [subscribe]);
276990
+ const emit = (entry) => setItems((h) => withEntry(h, entry));
276991
+ import_react23.useEffect(() => subscribe((entry) => setItems((h) => withEntry(h, entry))), [subscribe]);
276458
276992
  import_react23.useEffect(() => {
276459
276993
  const timer = setInterval(tick, 120);
276460
276994
  return () => clearInterval(timer);
@@ -276496,6 +277030,29 @@ function SessionView({ engine, footer, subscribe }) {
276496
277030
  emit(source_default.red(` /model: ${error instanceof Error ? error.message : String(error)}`));
276497
277031
  }
276498
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]);
276499
277056
  use_input_default((char, key) => {
276500
277057
  if (pickerIndex !== null) {
276501
277058
  if (key.upArrow)
@@ -276511,6 +277068,23 @@ function SessionView({ engine, footer, subscribe }) {
276511
277068
  }
276512
277069
  return;
276513
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
+ }
276514
277088
  if (key.ctrl && char === "c") {
276515
277089
  if (input)
276516
277090
  setInput("");
@@ -276555,7 +277129,9 @@ function SessionView({ engine, footer, subscribe }) {
276555
277129
  const innerWidth = Math.max(10, width - 4);
276556
277130
  const inputRows = Math.max(1, Math.ceil((input.length + 3) / innerWidth));
276557
277131
  const pickerOpen = pickerIndex !== null;
276558
- 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);
276559
277135
  const widgetHeight = inputBlockHeight + 3 + (footer.length ? 1 : 0);
276560
277136
  const viewHeight = Math.max(3, rows - widgetHeight - 1);
276561
277137
  const lines = items.flatMap((item) => hardWrapAnsi(`${renderEntry(item, { verbose, foldHint: "Ctrl+O to expand" })}
@@ -276606,29 +277182,64 @@ function SessionView({ engine, footer, subscribe }) {
276606
277182
  }, m.name, false, undefined, this);
276607
277183
  })
276608
277184
  ]
276609
- }, 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",
276610
277197
  borderStyle: "round",
276611
- borderColor: "gray",
277198
+ borderColor: card?.typing ? "yellow" : "gray",
276612
277199
  paddingX: 1,
276613
277200
  width,
276614
277201
  children: [
276615
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
276616
- color: "cyan",
276617
- children: "❯ "
276618
- }, undefined, false, undefined, this),
276619
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(build_default, {
276620
- value: input,
276621
- onChange: setInput,
276622
- onSubmit: (value) => {
276623
- const trimmed = value.trim();
276624
- if (trimmed === "/model" || trimmed.startsWith("/model ")) {
276625
- runModelSlash(trimmed.slice("/model".length).trim());
276626
- } else if (trimmed) {
276627
- engine.submit(value);
276628
- }
276629
- setInput("");
276630
- }
276631
- }, 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)
276632
277243
  ]
276633
277244
  }, undefined, true, undefined, this),
276634
277245
  footer.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
@@ -276642,7 +277253,7 @@ function SessionView({ engine, footer, subscribe }) {
276642
277253
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
276643
277254
  dimColor: true,
276644
277255
  wrap: "truncate-end",
276645
- children: pickerOpen ? " ↑↓ to move · Enter to select · Esc to cancel" : 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"
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"
276646
277257
  }, undefined, false, undefined, this)
276647
277258
  ]
276648
277259
  }, undefined, true, undefined, this);
@@ -276767,6 +277378,7 @@ async function runGenesisSession(options) {
276767
277378
  runningTool: null,
276768
277379
  lastTurnMs: null,
276769
277380
  lastTurnOk: true,
277381
+ pending: [],
276770
277382
  quietForMs: 0
276771
277383
  };
276772
277384
  const genesis = {
@@ -276822,6 +277434,9 @@ async function runGenesisSession(options) {
276822
277434
  }
276823
277435
  return IDLE_STATUS;
276824
277436
  },
277437
+ answer(p, action, input) {
277438
+ inner?.answer(p, action, input);
277439
+ },
276825
277440
  turnRunning() {
276826
277441
  return inner?.turnRunning() ?? creating;
276827
277442
  }
@@ -276854,10 +277469,10 @@ async function runGenesisSession(options) {
276854
277469
 
276855
277470
  // src/cli/commands/code/index.ts
276856
277471
  var BRAND_ORANGE2 = "#E86B3C";
276857
- async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path) {
277472
+ async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path, wixInstance) {
276858
277473
  let app;
276859
277474
  try {
276860
- app = await createAndLinkApp({ prompt, importRepo, path });
277475
+ app = await createAndLinkApp({ prompt, importRepo, path, wixInstance });
276861
277476
  } catch (error) {
276862
277477
  for (const line of await githubReauthLines(error) ?? [])
276863
277478
  emit(line);
@@ -276904,8 +277519,8 @@ async function codeAction({ log }, options, appId) {
276904
277519
  linked = true;
276905
277520
  } catch {}
276906
277521
  if (linked) {
276907
- if (options.import || options.path) {
276908
- throw new InvalidInputError("--import and --path create a new app; run them outside a linked project, without --app-id.");
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.");
276909
277524
  }
276910
277525
  const { id, projectRoot } = getAppContext();
276911
277526
  const state = await assertBuilderApp(id);
@@ -276928,16 +277543,25 @@ async function codeAction({ log }, options, appId) {
276928
277543
  outroMessage: `Session closed. Resume with \`base44 code --app-id ${id}\`.`
276929
277544
  };
276930
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;
276931
277553
  let created;
276932
- const footer = [chip(options.import ? repoLabel(options.import) : "web app")];
277554
+ const footer = [
277555
+ chip(options.import ? repoLabel(options.import) : wixInstance ? "web app · wix" : "web app")
277556
+ ];
276933
277557
  await runGenesisSession({
276934
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",
276935
277559
  creatingLabel: options.import ? "importing the repository" : "creating your app",
276936
- modeLabel: options.import ? `Repository — ${repoLabel(options.import)}` : "Web app — Base44 template + builder agent",
277560
+ modeLabel: options.import ? `Repository — ${repoLabel(options.import)}` : wixInstance ? "Web app — Wix launch (connector connected first)" : "Web app — Base44 template + builder agent",
276937
277561
  footer,
276938
277562
  createApp: (prompt, emit) => bootstrapApp(prompt, footer, emit, (app) => {
276939
277563
  created = app;
276940
- }, options.import, options.path)
277564
+ }, options.import, options.path, wixInstance)
276941
277565
  });
276942
277566
  if (!created) {
276943
277567
  return { outroMessage: "Session closed. No app was created." };
@@ -276949,7 +277573,7 @@ async function codeAction({ log }, options, appId) {
276949
277573
  }
276950
277574
  function getCodeCommand() {
276951
277575
  const command = new Base44Command("code", { requireAppContext: false });
276952
- 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>)").action((ctx, options) => codeAction(ctx, options, command.optsWithGlobals().appId));
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));
276953
277577
  return command;
276954
277578
  }
276955
277579
 
@@ -284332,7 +284956,7 @@ async function runScript(options) {
284332
284956
  }
284333
284957
  }
284334
284958
  // src/cli/commands/exec.ts
284335
- function readStdin2() {
284959
+ function readStdin3() {
284336
284960
  return new Promise((resolve, reject) => {
284337
284961
  let data = "";
284338
284962
  process.stdin.setEncoding("utf-8");
@@ -284376,7 +285000,7 @@ async function execAction({ app, isNonInteractive }, options) {
284376
285000
  if (!isNonInteractive) {
284377
285001
  throw noInputError;
284378
285002
  }
284379
- const code = await readStdin2();
285003
+ const code = await readStdin3();
284380
285004
  if (!code.trim()) {
284381
285005
  throw noInputError;
284382
285006
  }
@@ -288557,6 +289181,13 @@ function getFullCommandName(command) {
288557
289181
  }
288558
289182
  return parts.join(" ");
288559
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
+ }
288560
289191
  function addCommandInfoToErrorReporter(program, errorReporter) {
288561
289192
  program.hook("preAction", (_, actionCommand) => {
288562
289193
  const fullCommandName = getFullCommandName(actionCommand);
@@ -288564,7 +289195,7 @@ function addCommandInfoToErrorReporter(program, errorReporter) {
288564
289195
  command: {
288565
289196
  name: fullCommandName,
288566
289197
  args: actionCommand.args,
288567
- options: actionCommand.opts()
289198
+ options: redactSensitiveOptions(actionCommand.opts())
288568
289199
  }
288569
289200
  });
288570
289201
  });
@@ -288611,4 +289242,4 @@ export {
288611
289242
  runCLI
288612
289243
  };
288613
289244
 
288614
- //# debugId=0D34292BC79AD42A64756E2164756E21
289245
+ //# debugId=F9AF4FAF927903AE64756E2164756E21