@base44-preview/cli 0.1.15-pr.630.66e368f → 0.1.15-pr.630.80e82e8
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 +1676 -734
- package/dist/cli/index.js.map +25 -22
- package/package.json +1 -1
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
|
|
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 =
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
53314
|
+
indent: indent22,
|
|
53315
53315
|
mode: MODE_FLAT,
|
|
53316
53316
|
doc: content
|
|
53317
53317
|
};
|
|
53318
53318
|
const contentBreakCommand = {
|
|
53319
|
-
indent:
|
|
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:
|
|
53333
|
+
indent: indent22,
|
|
53334
53334
|
mode: MODE_FLAT,
|
|
53335
53335
|
doc: whitespace
|
|
53336
53336
|
};
|
|
53337
53337
|
const whitespaceBreakCommand = {
|
|
53338
|
-
indent:
|
|
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:
|
|
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:
|
|
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 :
|
|
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:
|
|
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 ?
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 (
|
|
53442
|
-
if (
|
|
53443
|
-
output +=
|
|
53441
|
+
if (indent22.root) {
|
|
53442
|
+
if (indent22.root.value) {
|
|
53443
|
+
output += indent22.root.value;
|
|
53444
53444
|
}
|
|
53445
|
-
position =
|
|
53445
|
+
position = indent22.root.length;
|
|
53446
53446
|
}
|
|
53447
53447
|
} else {
|
|
53448
53448
|
trim2();
|
|
53449
|
-
output += newLine +
|
|
53450
|
-
position =
|
|
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:
|
|
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
|
|
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(
|
|
140799
|
+
return diffLinesResultToPatch(diffLines2(oldStr, newStr, optionsObj));
|
|
140800
140800
|
} else {
|
|
140801
140801
|
const { callback } = optionsObj;
|
|
140802
|
-
|
|
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,
|
|
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:
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
|
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 ?? "
|
|
269968
|
+
var displayName = (id) => MODELS.find((m) => m.id === id)?.name ?? id ?? "Automatic";
|
|
269495
269969
|
|
|
269496
|
-
// src/cli/commands/
|
|
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
|
|
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/
|
|
270012
|
+
// src/cli/commands/builder/shared.ts
|
|
269539
270013
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
269540
|
-
import { join as
|
|
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
|
|
269651
|
-
|
|
269652
|
-
|
|
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: {
|
|
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/
|
|
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
|
|
269978
|
-
const
|
|
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(`./${
|
|
269982
|
-
}
|
|
269983
|
-
|
|
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(
|
|
270618
|
+
await mkdir3(join25(targetDir, "base44"), { recursive: true });
|
|
269993
270619
|
try {
|
|
269994
|
-
await writeFile2(
|
|
270620
|
+
await writeFile2(join25(targetDir, "base44", "config.jsonc"), `// Base44 project configuration.
|
|
269995
270621
|
{
|
|
269996
270622
|
"name": ${JSON.stringify(name)}
|
|
269997
270623
|
}
|
|
@@ -270002,9 +270628,50 @@ 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
|
|
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;
|
|
270665
|
+
}
|
|
270666
|
+
function nextStepsLines(app) {
|
|
270667
|
+
const cd = app.here ? "" : `cd ${app.dirName} && `;
|
|
270668
|
+
return [
|
|
270669
|
+
"",
|
|
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`)
|
|
270674
|
+
];
|
|
270008
270675
|
}
|
|
270009
270676
|
async function githubReauthLines(error) {
|
|
270010
270677
|
if (!isGithubUserTokenError(error))
|
|
@@ -270026,12 +270693,27 @@ function newStreamState() {
|
|
|
270026
270693
|
return { perMessage: new Map };
|
|
270027
270694
|
}
|
|
270028
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
|
+
}
|
|
270029
270711
|
function oneLine(value, max) {
|
|
270030
270712
|
const text = typeof value === "string" ? value : value == null ? "" : JSON.stringify(value);
|
|
270031
270713
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
270032
270714
|
return flat.length > max ? `${flat.slice(0, max)}…` : flat;
|
|
270033
270715
|
}
|
|
270034
|
-
var SALIENT_KEYS = ["command", "path", "file_path", "title"];
|
|
270716
|
+
var SALIENT_KEYS = ["command", "path", "file_path", "title", "find"];
|
|
270035
270717
|
function salvageKey(raw, keys) {
|
|
270036
270718
|
for (const key of keys) {
|
|
270037
270719
|
const match = raw.match(new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`));
|
|
@@ -270060,6 +270742,10 @@ function toolMeta(name, argumentsString) {
|
|
|
270060
270742
|
read_repo_file: pick("path") ?? pick("file_path"),
|
|
270061
270743
|
write_repo_file: pick("path") ?? pick("file_path"),
|
|
270062
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"),
|
|
270063
270749
|
create_pull_request: pick("title"),
|
|
270064
270750
|
reload_preview: ""
|
|
270065
270751
|
};
|
|
@@ -270110,7 +270796,10 @@ function diffConversation(state, messages) {
|
|
|
270110
270796
|
}
|
|
270111
270797
|
for (const tool of message.tool_calls ?? []) {
|
|
270112
270798
|
if (!progress.announcedTools.has(tool.id)) {
|
|
270113
|
-
progress.announcedTools.set(tool.id,
|
|
270799
|
+
progress.announcedTools.set(tool.id, {
|
|
270800
|
+
...toolMeta(tool.name, tool.arguments_string),
|
|
270801
|
+
args: toolArgs(tool.arguments_string)
|
|
270802
|
+
});
|
|
270114
270803
|
const meta = progress.announcedTools.get(tool.id);
|
|
270115
270804
|
events.push({
|
|
270116
270805
|
kind: "tool_start",
|
|
@@ -270141,7 +270830,8 @@ function diffConversation(state, messages) {
|
|
|
270141
270830
|
label: labelTense(meta.label, "done"),
|
|
270142
270831
|
summary: meta.summary,
|
|
270143
270832
|
ok: status === "success",
|
|
270144
|
-
result:
|
|
270833
|
+
result: fullText(tool.results),
|
|
270834
|
+
args: meta.args
|
|
270145
270835
|
});
|
|
270146
270836
|
}
|
|
270147
270837
|
}
|
|
@@ -270215,9 +270905,90 @@ async function streamConversationUntilSettled(onEvent, options = {}) {
|
|
|
270215
270905
|
return "timeout";
|
|
270216
270906
|
}
|
|
270217
270907
|
|
|
270218
|
-
// src/cli/commands/
|
|
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
|
|
270219
270984
|
var POLL_TIMEOUT_MS = 20 * 60000;
|
|
270220
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
|
+
}
|
|
270221
270992
|
async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
270222
270993
|
if (options.mode && !MODES.includes(options.mode)) {
|
|
270223
270994
|
throw new InvalidInputError("--mode must be direct, fork, or copy.");
|
|
@@ -270225,9 +270996,27 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270225
270996
|
if ((options.mode || options.repoName || options.fromBranch) && !options.import) {
|
|
270226
270997
|
throw new InvalidInputError("--mode, --repo-name and --from-branch apply only with --import <repo>.");
|
|
270227
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
|
+
}
|
|
270228
271013
|
if (!prompt && !options.import) {
|
|
270229
271014
|
throw new InvalidInputError('Describe the app ("<prompt>") or pass --import <repo>.');
|
|
270230
271015
|
}
|
|
271016
|
+
if (options.streamJson && jsonMode) {
|
|
271017
|
+
throw new InvalidInputError("--stream-json and --json are exclusive.");
|
|
271018
|
+
}
|
|
271019
|
+
const ndjson = options.streamJson ? ndjsonWriter() : null;
|
|
270231
271020
|
let app;
|
|
270232
271021
|
try {
|
|
270233
271022
|
app = await runTask(options.import ? "Importing the repository" : "Creating your app", () => createAndLinkApp({
|
|
@@ -270236,18 +271025,33 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270236
271025
|
importRepo: options.import,
|
|
270237
271026
|
mode: options.mode,
|
|
270238
271027
|
repoName: options.repoName,
|
|
270239
|
-
fromBranch: options.fromBranch
|
|
271028
|
+
fromBranch: options.fromBranch,
|
|
271029
|
+
path: options.path,
|
|
271030
|
+
wixInstance
|
|
270240
271031
|
}));
|
|
270241
271032
|
} catch (error) {
|
|
270242
271033
|
for (const line of await githubReauthLines(error) ?? [])
|
|
270243
271034
|
log.message(line);
|
|
270244
271035
|
throw error;
|
|
270245
271036
|
}
|
|
270246
|
-
if (
|
|
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) {
|
|
270247
271048
|
if (app.repoUrl)
|
|
270248
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
|
+
}
|
|
270249
271053
|
log.message(source_default.dim(`editor ${app.editorUrl}`));
|
|
270250
|
-
log.message(source_default.dim(`linked
|
|
271054
|
+
log.message(source_default.dim(`linked ${app.here ? "./ (this directory)" : `./${app.dirName}`}`));
|
|
270251
271055
|
}
|
|
270252
271056
|
let finalState;
|
|
270253
271057
|
let previewUrl;
|
|
@@ -270256,10 +271060,16 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270256
271060
|
const branchId = await resolveActiveBranchId().catch(() => {
|
|
270257
271061
|
return;
|
|
270258
271062
|
});
|
|
270259
|
-
const stream = createTurnStream(process.stdout.isTTY === true && !jsonMode, undefined, {
|
|
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
|
+
});
|
|
270260
271067
|
try {
|
|
270261
271068
|
const settled = await streamConversationUntilSettled((event) => {
|
|
270262
|
-
if (
|
|
271069
|
+
if (ndjson) {
|
|
271070
|
+
const { kind, ...rest } = event;
|
|
271071
|
+
ndjson({ type: kind, ...rest });
|
|
271072
|
+
} else if (!jsonMode)
|
|
270263
271073
|
stream.onEvent(event);
|
|
270264
271074
|
}, { branchId, timeoutMs: POLL_TIMEOUT_MS });
|
|
270265
271075
|
finalState = settled === "timeout" ? "processing" : (await getAppState(app.id)).status?.state ?? "ready";
|
|
@@ -270272,6 +271082,15 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270272
271082
|
stream.stop();
|
|
270273
271083
|
}
|
|
270274
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
|
+
}
|
|
270275
271094
|
if (jsonMode) {
|
|
270276
271095
|
return {
|
|
270277
271096
|
stdout: `${JSON.stringify({
|
|
@@ -270279,102 +271098,39 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270279
271098
|
repo_url: app.repoUrl ?? null,
|
|
270280
271099
|
editor_url: app.editorUrl,
|
|
270281
271100
|
preview_url: previewUrl ?? null,
|
|
270282
|
-
|
|
271101
|
+
dir: app.dirName,
|
|
271102
|
+
path: app.targetDir,
|
|
271103
|
+
status: finalState ?? "created",
|
|
271104
|
+
...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
|
|
270283
271105
|
})}
|
|
270284
271106
|
`
|
|
270285
271107
|
};
|
|
270286
271108
|
}
|
|
270287
271109
|
if (previewUrl)
|
|
270288
271110
|
log.message(`preview ${previewUrl}`);
|
|
270289
|
-
const
|
|
271111
|
+
for (const line of nextStepsLines(app))
|
|
271112
|
+
log.message(line);
|
|
270290
271113
|
if (finalState === "error") {
|
|
270291
271114
|
return {
|
|
270292
|
-
outroMessage: `The first build reported an error — open the editor for details
|
|
271115
|
+
outroMessage: `The first build reported an error — open the editor for details.`
|
|
270293
271116
|
};
|
|
270294
271117
|
}
|
|
270295
271118
|
if (finalState === "processing") {
|
|
270296
271119
|
return {
|
|
270297
|
-
outroMessage: `Still building — follow it with \`base44
|
|
271120
|
+
outroMessage: `Still building — follow it with \`base44 builder status\`.`
|
|
270298
271121
|
};
|
|
270299
271122
|
}
|
|
270300
271123
|
return {
|
|
270301
|
-
outroMessage: prompt ? `First build finished · ${formatDuration2(Date.now() - startedAt)}
|
|
271124
|
+
outroMessage: prompt ? `First build finished · ${formatDuration2(Date.now() - startedAt)}.` : "App created."
|
|
270302
271125
|
};
|
|
270303
271126
|
}
|
|
270304
271127
|
function getNewCommand() {
|
|
270305
271128
|
const command = new Base44Command("new", { requireAppContext: false });
|
|
270306
|
-
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);
|
|
270307
|
-
return command;
|
|
270308
|
-
}
|
|
270309
|
-
|
|
270310
|
-
// src/cli/commands/app/preview.ts
|
|
270311
|
-
async function previewAction(ctx) {
|
|
270312
|
-
const url = await ctx.runTask("Resolving preview URL (boots the sandbox if needed)", () => getPreviewUrl());
|
|
270313
|
-
if (ctx.jsonMode)
|
|
270314
|
-
return { stdout: `${JSON.stringify({ preview_url: url })}
|
|
270315
|
-
` };
|
|
270316
|
-
ctx.log.message(url);
|
|
270317
|
-
return { outroMessage: "Preview is live." };
|
|
270318
|
-
}
|
|
270319
|
-
function getPreviewCommand() {
|
|
270320
|
-
const command = new Base44Command("preview");
|
|
270321
|
-
command.description("Print the app's live preview URL").action(previewAction);
|
|
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);
|
|
270322
271130
|
return command;
|
|
270323
271131
|
}
|
|
270324
271132
|
|
|
270325
|
-
// src/cli/commands/
|
|
270326
|
-
function lastAssistantReply(turn) {
|
|
270327
|
-
const messages = turn.conversation?.messages ?? [];
|
|
270328
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
270329
|
-
const { role, content } = messages[i];
|
|
270330
|
-
if (role === "assistant" && typeof content === "string" && content.trim()) {
|
|
270331
|
-
return content.trim();
|
|
270332
|
-
}
|
|
270333
|
-
}
|
|
270334
|
-
return;
|
|
270335
|
-
}
|
|
270336
|
-
async function sendAction(ctx, message) {
|
|
270337
|
-
const branchId = await resolveBranchId(ctx);
|
|
270338
|
-
if (ctx.jsonMode) {
|
|
270339
|
-
const turn = await ctx.runTask("Agent working (a turn can take minutes)", () => sendTurn(message, branchId));
|
|
270340
|
-
if (turn.queued)
|
|
270341
|
-
return { stdout: `${JSON.stringify({ queued: true })}
|
|
270342
|
-
` };
|
|
270343
|
-
return {
|
|
270344
|
-
stdout: `${JSON.stringify({
|
|
270345
|
-
status: turn.status?.state ?? "ready",
|
|
270346
|
-
error_source: turn.status?.error_source ?? null,
|
|
270347
|
-
reply: lastAssistantReply(turn) ?? null
|
|
270348
|
-
})}
|
|
270349
|
-
`
|
|
270350
|
-
};
|
|
270351
|
-
}
|
|
270352
|
-
const stream = createTurnStream(process.stdout.isTTY === true);
|
|
270353
|
-
let turn;
|
|
270354
|
-
try {
|
|
270355
|
-
turn = await streamConversationDuring(() => sendTurn(message, branchId), stream.onEvent, { branchId });
|
|
270356
|
-
} finally {
|
|
270357
|
-
stream.stop();
|
|
270358
|
-
}
|
|
270359
|
-
if (turn.queued) {
|
|
270360
|
-
return {
|
|
270361
|
-
outroMessage: "The agent is busy with an earlier message — yours was queued and runs next."
|
|
270362
|
-
};
|
|
270363
|
-
}
|
|
270364
|
-
if (turn.status?.state === "error") {
|
|
270365
|
-
return {
|
|
270366
|
-
outroMessage: `Turn failed (${turn.status.error_source ?? "unknown"}) — see the editor for details.`
|
|
270367
|
-
};
|
|
270368
|
-
}
|
|
270369
|
-
return { outroMessage: "Turn finished." };
|
|
270370
|
-
}
|
|
270371
|
-
function getSendCommand() {
|
|
270372
|
-
const command = new Base44Command("send", { supportsBranch: true });
|
|
270373
|
-
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);
|
|
270374
|
-
return command;
|
|
270375
|
-
}
|
|
270376
|
-
|
|
270377
|
-
// src/cli/commands/app/status.ts
|
|
271133
|
+
// src/cli/commands/builder/status.ts
|
|
270378
271134
|
async function statusAction(ctx) {
|
|
270379
271135
|
const id = ctx.app?.id;
|
|
270380
271136
|
const app = await ctx.runTask("Reading app status", () => getAppState(id));
|
|
@@ -270396,7 +271152,7 @@ function getStatusCommand() {
|
|
|
270396
271152
|
return command;
|
|
270397
271153
|
}
|
|
270398
271154
|
|
|
270399
|
-
// src/cli/commands/
|
|
271155
|
+
// src/cli/commands/builder/stop.ts
|
|
270400
271156
|
async function stopAction(ctx) {
|
|
270401
271157
|
const branchId = await resolveBranchId(ctx);
|
|
270402
271158
|
await ctx.runTask("Stopping the running turn", () => stopTurn(branchId));
|
|
@@ -270411,470 +271167,9 @@ function getStopCommand() {
|
|
|
270411
271167
|
return command;
|
|
270412
271168
|
}
|
|
270413
271169
|
|
|
270414
|
-
// src/cli/commands/
|
|
270415
|
-
function
|
|
270416
|
-
return new Command2("
|
|
270417
|
-
}
|
|
270418
|
-
|
|
270419
|
-
// src/cli/commands/auth/password-login.ts
|
|
270420
|
-
import { dirname as dirname15, join as join22 } from "node:path";
|
|
270421
|
-
async function passwordLoginAction({ log, runTask }, action) {
|
|
270422
|
-
const shouldEnable = action === "enable";
|
|
270423
|
-
const { project } = await readProjectConfig();
|
|
270424
|
-
const configDir = dirname15(project.configPath);
|
|
270425
|
-
const authDir = join22(configDir, project.authDir);
|
|
270426
|
-
const updated = await runTask("Updating local auth config", async () => {
|
|
270427
|
-
const current = await readAuthConfig(authDir) ?? DEFAULT_AUTH_CONFIG;
|
|
270428
|
-
const merged = { ...current, enableUsernamePassword: shouldEnable };
|
|
270429
|
-
await writeAuthConfig(authDir, merged);
|
|
270430
|
-
return merged;
|
|
270431
|
-
});
|
|
270432
|
-
if (!shouldEnable && !hasAnyLoginMethod(updated)) {
|
|
270433
|
-
log.warn("Disabling password auth will leave no login methods enabled. Users will be locked out.");
|
|
270434
|
-
}
|
|
270435
|
-
const newStatus = shouldEnable ? "enabled" : "disabled";
|
|
270436
|
-
return {
|
|
270437
|
-
outroMessage: `Username & password authentication ${newStatus} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
|
|
270438
|
-
};
|
|
270439
|
-
}
|
|
270440
|
-
function getPasswordLoginCommand() {
|
|
270441
|
-
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);
|
|
270442
|
-
}
|
|
270443
|
-
|
|
270444
|
-
// src/cli/commands/auth/pull.ts
|
|
270445
|
-
import { dirname as dirname16, join as join23 } from "node:path";
|
|
270446
|
-
async function pullAuthAction({
|
|
270447
|
-
log,
|
|
270448
|
-
runTask
|
|
270449
|
-
}) {
|
|
270450
|
-
const { project } = await readProjectConfig();
|
|
270451
|
-
const configDir = dirname16(project.configPath);
|
|
270452
|
-
const authDir = join23(configDir, project.authDir);
|
|
270453
|
-
const remoteConfig = await runTask("Fetching auth config from Base44", async () => {
|
|
270454
|
-
return await pullAuthConfig();
|
|
270455
|
-
}, {
|
|
270456
|
-
successMessage: "Auth config fetched successfully",
|
|
270457
|
-
errorMessage: "Failed to fetch auth config"
|
|
270458
|
-
});
|
|
270459
|
-
const { written } = await runTask("Syncing auth config file", async () => {
|
|
270460
|
-
return await writeAuthConfig(authDir, remoteConfig);
|
|
270461
|
-
}, {
|
|
270462
|
-
successMessage: "Auth config file synced successfully",
|
|
270463
|
-
errorMessage: "Failed to sync auth config file"
|
|
270464
|
-
});
|
|
270465
|
-
if (written) {
|
|
270466
|
-
log.success("Auth config written to local file");
|
|
270467
|
-
} else {
|
|
270468
|
-
log.info("Auth config is already up to date");
|
|
270469
|
-
}
|
|
270470
|
-
return {
|
|
270471
|
-
outroMessage: `Pulled auth config to ${authDir} (overwrites local file)`
|
|
270472
|
-
};
|
|
270473
|
-
}
|
|
270474
|
-
function getAuthPullCommand() {
|
|
270475
|
-
return new Base44Command("pull").description("Pull auth config from Base44 to local file").action(pullAuthAction);
|
|
270476
|
-
}
|
|
270477
|
-
|
|
270478
|
-
// src/cli/commands/auth/push.ts
|
|
270479
|
-
async function pushAuthAction({ isNonInteractive, log, runTask }, options) {
|
|
270480
|
-
const { authConfig } = await readProjectConfig();
|
|
270481
|
-
if (authConfig.length === 0) {
|
|
270482
|
-
log.info("No local auth config found");
|
|
270483
|
-
return {
|
|
270484
|
-
outroMessage: "No auth config to push. Run `base44 auth pull` to fetch the remote config first."
|
|
270485
|
-
};
|
|
270486
|
-
}
|
|
270487
|
-
if (!hasAnyLoginMethod(authConfig[0])) {
|
|
270488
|
-
log.warn("This config has no login methods enabled. Pushing it will lock out all users.");
|
|
270489
|
-
}
|
|
270490
|
-
if (!options.yes) {
|
|
270491
|
-
if (isNonInteractive) {
|
|
270492
|
-
throw new InvalidInputError("--yes is required in non-interactive mode");
|
|
270493
|
-
}
|
|
270494
|
-
const shouldPush = await Re({
|
|
270495
|
-
message: "Push auth config to Base44?"
|
|
270496
|
-
});
|
|
270497
|
-
if (Ct(shouldPush) || !shouldPush) {
|
|
270498
|
-
return { outroMessage: "Push cancelled" };
|
|
270499
|
-
}
|
|
270500
|
-
}
|
|
270501
|
-
await runTask("Pushing auth config to Base44", async () => {
|
|
270502
|
-
return await pushAuthConfig(authConfig[0] ?? null);
|
|
270503
|
-
}, {
|
|
270504
|
-
successMessage: "Auth config pushed successfully",
|
|
270505
|
-
errorMessage: "Failed to push auth config"
|
|
270506
|
-
});
|
|
270507
|
-
return {
|
|
270508
|
-
outroMessage: "Auth config pushed to Base44"
|
|
270509
|
-
};
|
|
270510
|
-
}
|
|
270511
|
-
function getAuthPushCommand() {
|
|
270512
|
-
return new Base44Command("push").description("Push local auth config to Base44").option("-y, --yes", "Skip confirmation prompt").action(pushAuthAction);
|
|
270513
|
-
}
|
|
270514
|
-
|
|
270515
|
-
// src/cli/commands/auth/social-login.ts
|
|
270516
|
-
import { dirname as dirname17, join as join24, resolve as resolve6 } from "node:path";
|
|
270517
|
-
var PROVIDER_LABELS = {
|
|
270518
|
-
google: "Google",
|
|
270519
|
-
microsoft: "Microsoft",
|
|
270520
|
-
facebook: "Facebook",
|
|
270521
|
-
apple: "Apple"
|
|
270522
|
-
};
|
|
270523
|
-
var VALID_PROVIDER_NAMES = Object.keys(SOCIAL_PROVIDERS);
|
|
270524
|
-
var PROVIDER_OAUTH_CLI = {
|
|
270525
|
-
google: {
|
|
270526
|
-
envVar: "google_oauth_client_secret",
|
|
270527
|
-
promptMessage: "Enter Google OAuth client secret"
|
|
270528
|
-
}
|
|
270529
|
-
};
|
|
270530
|
-
function hasSecretOptions(options) {
|
|
270531
|
-
return Boolean(options.clientSecret || options.clientSecretStdin || options.envFile);
|
|
270532
|
-
}
|
|
270533
|
-
function hasCustomOAuthOptions(options) {
|
|
270534
|
-
return Boolean(options.clientId || hasSecretOptions(options));
|
|
270535
|
-
}
|
|
270536
|
-
async function socialLoginAction({ log, isNonInteractive, runTask }, provider, action, options) {
|
|
270537
|
-
const shouldEnable = action === "enable";
|
|
270538
|
-
const providerInfo = SOCIAL_PROVIDERS[provider];
|
|
270539
|
-
const label = PROVIDER_LABELS[provider];
|
|
270540
|
-
const hasOAuthOptions = hasCustomOAuthOptions(options);
|
|
270541
|
-
if (hasOAuthOptions && !providerInfo.customOAuth) {
|
|
270542
|
-
throw new InvalidInputError(`Custom OAuth options are only supported for providers with custom OAuth (e.g., google). Use: base44 auth social-login ${provider} ${action}`);
|
|
270543
|
-
}
|
|
270544
|
-
if (hasOAuthOptions && !shouldEnable) {
|
|
270545
|
-
throw new InvalidInputError(`Custom OAuth options cannot be used with disable. To disable ${label} login: base44 auth social-login ${provider} disable`);
|
|
270546
|
-
}
|
|
270547
|
-
if (hasSecretOptions(options) && !options.clientId) {
|
|
270548
|
-
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>`);
|
|
270549
|
-
}
|
|
270550
|
-
const oauth = providerInfo.customOAuth;
|
|
270551
|
-
const oauthCli = PROVIDER_OAUTH_CLI[provider];
|
|
270552
|
-
const useCustomOAuth = shouldEnable && hasOAuthOptions && oauth != null;
|
|
270553
|
-
let clientSecret;
|
|
270554
|
-
if (useCustomOAuth && oauth && oauthCli && hasSecretOptions(options)) {
|
|
270555
|
-
if (options.envFile) {
|
|
270556
|
-
const secrets = await parseEnvFile(resolve6(options.envFile));
|
|
270557
|
-
const value = secrets[oauthCli.envVar];
|
|
270558
|
-
if (!value) {
|
|
270559
|
-
throw new InvalidInputError(`Key "${oauthCli.envVar}" not found in ${options.envFile}.`);
|
|
270560
|
-
}
|
|
270561
|
-
clientSecret = value;
|
|
270562
|
-
} else {
|
|
270563
|
-
clientSecret = await resolveSecret({
|
|
270564
|
-
flagValue: options.clientSecret,
|
|
270565
|
-
fromStdin: options.clientSecretStdin,
|
|
270566
|
-
envVar: oauthCli.envVar,
|
|
270567
|
-
promptMessage: oauthCli.promptMessage,
|
|
270568
|
-
isNonInteractive,
|
|
270569
|
-
name: "client secret",
|
|
270570
|
-
hints: [
|
|
270571
|
-
{
|
|
270572
|
-
message: `Provide via flag: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`,
|
|
270573
|
-
command: `base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`
|
|
270574
|
-
},
|
|
270575
|
-
{
|
|
270576
|
-
message: `Provide via stdin: echo <secret> | base44 auth social-login ${provider} enable --client-id <id> --client-secret-stdin`
|
|
270577
|
-
},
|
|
270578
|
-
{
|
|
270579
|
-
message: `Provide via env: ${oauthCli.envVar}=<secret> base44 auth social-login ${provider} enable --client-id <id>`
|
|
270580
|
-
}
|
|
270581
|
-
]
|
|
270582
|
-
});
|
|
270583
|
-
}
|
|
270584
|
-
}
|
|
270585
|
-
const { project } = await readProjectConfig();
|
|
270586
|
-
const configDir = dirname17(project.configPath);
|
|
270587
|
-
const authDir = join24(configDir, project.authDir);
|
|
270588
|
-
const { config: updated } = await runTask("Updating local auth config", async () => updateSocialLoginConfig(authDir, provider, shouldEnable, useCustomOAuth && options.clientId ? { clientId: options.clientId } : undefined));
|
|
270589
|
-
if (clientSecret) {
|
|
270590
|
-
await runTask("Saving client secret", async () => pushCustomOAuthSecret(provider, clientSecret));
|
|
270591
|
-
}
|
|
270592
|
-
if (!shouldEnable && !hasAnyLoginMethod(updated)) {
|
|
270593
|
-
log.warn(`Disabling ${label} login will leave no login methods enabled. Users will be locked out.`);
|
|
270594
|
-
}
|
|
270595
|
-
const newStatus = shouldEnable ? "enabled" : "disabled";
|
|
270596
|
-
const oauthNote = useCustomOAuth ? " with custom OAuth" : "";
|
|
270597
|
-
let outroMessage = `${label} login ${newStatus}${oauthNote} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`;
|
|
270598
|
-
if (useCustomOAuth && !clientSecret) {
|
|
270599
|
-
outroMessage += `
|
|
270600
|
-
Remember to push the client secret separately: base44 secrets set --env-file <path>`;
|
|
270601
|
-
}
|
|
270602
|
-
return { outroMessage };
|
|
270603
|
-
}
|
|
270604
|
-
function getSocialLoginCommand() {
|
|
270605
|
-
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([
|
|
270606
|
-
"enable",
|
|
270607
|
-
"disable"
|
|
270608
|
-
])).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);
|
|
270609
|
-
}
|
|
270610
|
-
|
|
270611
|
-
// src/cli/commands/auth/sso.ts
|
|
270612
|
-
import { dirname as dirname18, join as join25, resolve as resolve7 } from "node:path";
|
|
270613
|
-
var SSOConfigFileSchema = object({
|
|
270614
|
-
provider: _enum(Object.values(KNOWN_SSO_PROVIDERS)),
|
|
270615
|
-
clientId: string2(),
|
|
270616
|
-
clientSecret: string2(),
|
|
270617
|
-
scope: string2().optional(),
|
|
270618
|
-
discoveryUrl: string2().optional(),
|
|
270619
|
-
tenantId: string2().optional(),
|
|
270620
|
-
oktaDomain: string2().optional(),
|
|
270621
|
-
authEndpoint: string2().optional(),
|
|
270622
|
-
tokenEndpoint: string2().optional(),
|
|
270623
|
-
userinfoEndpoint: string2().optional(),
|
|
270624
|
-
jwksUri: string2().optional(),
|
|
270625
|
-
ssoName: string2().optional()
|
|
270626
|
-
});
|
|
270627
|
-
async function loadSSOConfigFile(filePath) {
|
|
270628
|
-
const resolved = resolve7(filePath);
|
|
270629
|
-
const raw = await readJsonFile(resolved);
|
|
270630
|
-
const result = SSOConfigFileSchema.safeParse(raw);
|
|
270631
|
-
if (!result.success) {
|
|
270632
|
-
throw new SchemaValidationError("Invalid SSO config file", result.error, filePath);
|
|
270633
|
-
}
|
|
270634
|
-
return result.data;
|
|
270635
|
-
}
|
|
270636
|
-
function mergeFileWithFlags(fileConfig, options) {
|
|
270637
|
-
return {
|
|
270638
|
-
provider: options.provider ?? fileConfig.provider,
|
|
270639
|
-
clientId: options.clientId ?? fileConfig.clientId,
|
|
270640
|
-
clientSecret: options.clientSecret ?? fileConfig.clientSecret,
|
|
270641
|
-
clientSecretStdin: options.clientSecretStdin,
|
|
270642
|
-
envFile: options.envFile,
|
|
270643
|
-
scope: options.scope ?? fileConfig.scope,
|
|
270644
|
-
discoveryUrl: options.discoveryUrl ?? fileConfig.discoveryUrl,
|
|
270645
|
-
tenantId: options.tenantId ?? fileConfig.tenantId,
|
|
270646
|
-
oktaDomain: options.oktaDomain ?? fileConfig.oktaDomain,
|
|
270647
|
-
authEndpoint: options.authEndpoint ?? fileConfig.authEndpoint,
|
|
270648
|
-
tokenEndpoint: options.tokenEndpoint ?? fileConfig.tokenEndpoint,
|
|
270649
|
-
userinfoEndpoint: options.userinfoEndpoint ?? fileConfig.userinfoEndpoint,
|
|
270650
|
-
jwksUri: options.jwksUri ?? fileConfig.jwksUri,
|
|
270651
|
-
ssoName: options.ssoName ?? fileConfig.ssoName
|
|
270652
|
-
};
|
|
270653
|
-
}
|
|
270654
|
-
var providerNames = Object.keys(KNOWN_SSO_PROVIDERS);
|
|
270655
|
-
var SECRET_KEY_TO_FLAG = {
|
|
270656
|
-
["sso_name" /* Name */]: "--sso-name",
|
|
270657
|
-
["sso_client_id" /* ClientId */]: "--client-id",
|
|
270658
|
-
["sso_client_secret" /* ClientSecret */]: "--client-secret",
|
|
270659
|
-
["sso_scope" /* Scope */]: "--scope",
|
|
270660
|
-
["sso_discovery_url" /* DiscoveryUrl */]: "--discovery-url",
|
|
270661
|
-
["sso_tenant_id" /* TenantId */]: "--tenant-id",
|
|
270662
|
-
["sso_auth_endpoint" /* AuthEndpoint */]: "--auth-endpoint",
|
|
270663
|
-
["sso_token_endpoint" /* TokenEndpoint */]: "--token-endpoint",
|
|
270664
|
-
["sso_userinfo_endpoint" /* UserinfoEndpoint */]: "--userinfo-endpoint",
|
|
270665
|
-
["sso_okta_domain" /* OktaDomain */]: "--okta-domain",
|
|
270666
|
-
["sso_jwks_uri" /* JwksUri */]: "--jwks-uri"
|
|
270667
|
-
};
|
|
270668
|
-
function secretKeyToFlag(key) {
|
|
270669
|
-
return SECRET_KEY_TO_FLAG[key];
|
|
270670
|
-
}
|
|
270671
|
-
function exampleCommand(provider) {
|
|
270672
|
-
let cmd = `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`;
|
|
270673
|
-
if (provider === KNOWN_SSO_PROVIDERS.microsoft)
|
|
270674
|
-
cmd += " --tenant-id <id>";
|
|
270675
|
-
if (provider === KNOWN_SSO_PROVIDERS.okta)
|
|
270676
|
-
cmd += " --okta-domain <domain>";
|
|
270677
|
-
if (provider === KNOWN_SSO_PROVIDERS.custom)
|
|
270678
|
-
cmd += " --sso-name <name> --auth-endpoint <url> --token-endpoint <url> --userinfo-endpoint <url> --jwks-uri <url>";
|
|
270679
|
-
return cmd;
|
|
270680
|
-
}
|
|
270681
|
-
function validateProvider(provider) {
|
|
270682
|
-
if (!provider) {
|
|
270683
|
-
throw new InvalidInputError("Missing --provider.", {
|
|
270684
|
-
hints: [
|
|
270685
|
-
{
|
|
270686
|
-
message: `Valid providers: ${providerNames.join(", ")}`,
|
|
270687
|
-
command: "base44 auth sso enable --provider <provider> --client-id <id> --client-secret <secret>"
|
|
270688
|
-
}
|
|
270689
|
-
]
|
|
270690
|
-
});
|
|
270691
|
-
}
|
|
270692
|
-
return provider;
|
|
270693
|
-
}
|
|
270694
|
-
async function ssoEnableAction({ isNonInteractive, runTask }, options) {
|
|
270695
|
-
if (options.file && options.envFile) {
|
|
270696
|
-
throw new InvalidInputError("--file and --env-file cannot be used together. Provide the client secret either inside --file or via --env-file.");
|
|
270697
|
-
}
|
|
270698
|
-
let merged = options;
|
|
270699
|
-
if (options.file) {
|
|
270700
|
-
const fileConfig = await loadSSOConfigFile(options.file);
|
|
270701
|
-
merged = mergeFileWithFlags(fileConfig, options);
|
|
270702
|
-
}
|
|
270703
|
-
const provider = validateProvider(merged.provider);
|
|
270704
|
-
if (!merged.clientId) {
|
|
270705
|
-
throw new InvalidInputError("Missing --client-id.", {
|
|
270706
|
-
hints: [
|
|
270707
|
-
{
|
|
270708
|
-
message: `Example: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
|
|
270709
|
-
command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
|
|
270710
|
-
}
|
|
270711
|
-
]
|
|
270712
|
-
});
|
|
270713
|
-
}
|
|
270714
|
-
let clientSecret;
|
|
270715
|
-
if (merged.envFile && !merged.clientSecret) {
|
|
270716
|
-
const secrets = await parseEnvFile(resolve7(merged.envFile));
|
|
270717
|
-
const value = secrets.sso_client_secret;
|
|
270718
|
-
if (!value) {
|
|
270719
|
-
throw new InvalidInputError(`Key "sso_client_secret" not found in ${merged.envFile}.`);
|
|
270720
|
-
}
|
|
270721
|
-
clientSecret = value;
|
|
270722
|
-
} else {
|
|
270723
|
-
clientSecret = await resolveSecret({
|
|
270724
|
-
flagValue: merged.clientSecret,
|
|
270725
|
-
fromStdin: merged.clientSecretStdin,
|
|
270726
|
-
envVar: "sso_client_secret",
|
|
270727
|
-
promptMessage: "Enter SSO client secret",
|
|
270728
|
-
isNonInteractive,
|
|
270729
|
-
name: "client secret",
|
|
270730
|
-
hints: [
|
|
270731
|
-
{
|
|
270732
|
-
message: `Provide via flag: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
|
|
270733
|
-
command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
|
|
270734
|
-
},
|
|
270735
|
-
{
|
|
270736
|
-
message: `Provide via stdin: echo <secret> | base44 auth sso enable --provider ${provider} --client-id <id> --client-secret-stdin`
|
|
270737
|
-
},
|
|
270738
|
-
{
|
|
270739
|
-
message: `Provide via env: sso_client_secret=<secret> base44 auth sso enable --provider ${provider} --client-id <id>`
|
|
270740
|
-
}
|
|
270741
|
-
]
|
|
270742
|
-
});
|
|
270743
|
-
}
|
|
270744
|
-
const secretOptions = {
|
|
270745
|
-
clientId: merged.clientId,
|
|
270746
|
-
clientSecret,
|
|
270747
|
-
scope: merged.scope,
|
|
270748
|
-
discoveryUrl: merged.discoveryUrl,
|
|
270749
|
-
tenantId: merged.tenantId,
|
|
270750
|
-
oktaDomain: merged.oktaDomain,
|
|
270751
|
-
authEndpoint: merged.authEndpoint,
|
|
270752
|
-
tokenEndpoint: merged.tokenEndpoint,
|
|
270753
|
-
userinfoEndpoint: merged.userinfoEndpoint,
|
|
270754
|
-
jwksUri: merged.jwksUri,
|
|
270755
|
-
ssoName: merged.ssoName
|
|
270756
|
-
};
|
|
270757
|
-
let secrets;
|
|
270758
|
-
try {
|
|
270759
|
-
secrets = buildSSOSecrets(provider, secretOptions);
|
|
270760
|
-
} catch (error) {
|
|
270761
|
-
if (error instanceof MissingSSOFieldsError) {
|
|
270762
|
-
const flagNames = error.missingKeys.map(secretKeyToFlag);
|
|
270763
|
-
throw new InvalidInputError(`Missing required fields for ${error.provider}: ${flagNames.join(", ")}`, {
|
|
270764
|
-
hints: [
|
|
270765
|
-
{
|
|
270766
|
-
message: `Example: ${exampleCommand(provider)}`,
|
|
270767
|
-
command: exampleCommand(provider)
|
|
270768
|
-
}
|
|
270769
|
-
]
|
|
270770
|
-
});
|
|
270771
|
-
}
|
|
270772
|
-
throw error;
|
|
270773
|
-
}
|
|
270774
|
-
const { project } = await readProjectConfig();
|
|
270775
|
-
const configDir = dirname18(project.configPath);
|
|
270776
|
-
const authDir = join25(configDir, project.authDir);
|
|
270777
|
-
await runTask("Updating local auth config", async () => updateSSOConfig(authDir, provider, true));
|
|
270778
|
-
await runTask("Saving SSO credentials", async () => pushSSOSecrets(secrets));
|
|
270779
|
-
return {
|
|
270780
|
-
outroMessage: `SSO configured with ${provider} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
|
|
270781
|
-
};
|
|
270782
|
-
}
|
|
270783
|
-
function hasEnableOnlyOptions(options) {
|
|
270784
|
-
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);
|
|
270785
|
-
}
|
|
270786
|
-
async function ssoDisableAction({ log, runTask }, options) {
|
|
270787
|
-
if (hasEnableOnlyOptions(options)) {
|
|
270788
|
-
throw new InvalidInputError("Configuration options cannot be used with disable. To disable SSO: base44 auth sso disable");
|
|
270789
|
-
}
|
|
270790
|
-
const { project } = await readProjectConfig();
|
|
270791
|
-
const configDir = dirname18(project.configPath);
|
|
270792
|
-
const authDir = join25(configDir, project.authDir);
|
|
270793
|
-
const updated = await runTask("Updating local auth config", async () => updateSSOConfig(authDir, null, false));
|
|
270794
|
-
await runTask("Removing SSO credentials", async () => deleteSSOSecrets());
|
|
270795
|
-
if (!hasAnyLoginMethod(updated)) {
|
|
270796
|
-
log.warn("Disabling SSO will leave no login methods enabled. Users will be locked out.");
|
|
270797
|
-
}
|
|
270798
|
-
return {
|
|
270799
|
-
outroMessage: "SSO disabled in local config and credentials removed. Run `base44 auth push` or `base44 deploy` to apply."
|
|
270800
|
-
};
|
|
270801
|
-
}
|
|
270802
|
-
async function ssoAction(context, action, options) {
|
|
270803
|
-
if (action === "disable") {
|
|
270804
|
-
return ssoDisableAction(context, options);
|
|
270805
|
-
}
|
|
270806
|
-
return ssoEnableAction(context, options);
|
|
270807
|
-
}
|
|
270808
|
-
function getSSOCommand() {
|
|
270809
|
-
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([
|
|
270810
|
-
"enable",
|
|
270811
|
-
"disable"
|
|
270812
|
-
])).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);
|
|
270813
|
-
}
|
|
270814
|
-
|
|
270815
|
-
// src/cli/commands/auth/index.ts
|
|
270816
|
-
function getAuthCommand() {
|
|
270817
|
-
return new Command2("auth").description("Manage app authentication settings").addCommand(getPasswordLoginCommand()).addCommand(getSocialLoginCommand()).addCommand(getSSOCommand()).addCommand(getAuthPullCommand()).addCommand(getAuthPushCommand());
|
|
270818
|
-
}
|
|
270819
|
-
|
|
270820
|
-
// src/cli/commands/auth/login.ts
|
|
270821
|
-
function getLoginCommand() {
|
|
270822
|
-
return new Base44Command("login", {
|
|
270823
|
-
requireAuth: false,
|
|
270824
|
-
requireAppContext: false
|
|
270825
|
-
}).description("Authenticate with Base44").action(login);
|
|
270826
|
-
}
|
|
270827
|
-
|
|
270828
|
-
// src/cli/commands/auth/logout.ts
|
|
270829
|
-
async function logout(_ctx) {
|
|
270830
|
-
await deleteAuth();
|
|
270831
|
-
return { outroMessage: "Logged out successfully" };
|
|
270832
|
-
}
|
|
270833
|
-
function getLogoutCommand() {
|
|
270834
|
-
return new Base44Command("logout", {
|
|
270835
|
-
requireAuth: false,
|
|
270836
|
-
requireAppContext: false
|
|
270837
|
-
}).description("Logout from current device").action(logout);
|
|
270838
|
-
}
|
|
270839
|
-
|
|
270840
|
-
// src/cli/commands/auth/whoami.ts
|
|
270841
|
-
async function whoami(_ctx) {
|
|
270842
|
-
const workspaceApiKey = getWorkspaceApiKeyFromEnv();
|
|
270843
|
-
if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
|
|
270844
|
-
return {
|
|
270845
|
-
outroMessage: `Using workspace API key: ${theme.styles.bold(workspaceApiKey.slice(0, 10))}`
|
|
270846
|
-
};
|
|
270847
|
-
}
|
|
270848
|
-
const auth = await readAuth();
|
|
270849
|
-
return { outroMessage: `Logged in as: ${theme.styles.bold(auth.email)}` };
|
|
270850
|
-
}
|
|
270851
|
-
function getWhoamiCommand() {
|
|
270852
|
-
return new Base44Command("whoami", { requireAppContext: false }).description("Display current authenticated user").action(whoami);
|
|
270853
|
-
}
|
|
270854
|
-
|
|
270855
|
-
// src/cli/commands/branches/index.ts
|
|
270856
|
-
async function listBranchesAction({
|
|
270857
|
-
log,
|
|
270858
|
-
runTask,
|
|
270859
|
-
jsonMode
|
|
270860
|
-
}) {
|
|
270861
|
-
const remote = await runTask("Fetching branches", () => listBranches());
|
|
270862
|
-
const branches = [
|
|
270863
|
-
{ name: "main", status: "active" },
|
|
270864
|
-
...remote.map((branch) => ({
|
|
270865
|
-
name: branch.branch_name,
|
|
270866
|
-
status: branch.status
|
|
270867
|
-
}))
|
|
270868
|
-
];
|
|
270869
|
-
if (jsonMode)
|
|
270870
|
-
return { stdout: `${JSON.stringify({ branches })}
|
|
270871
|
-
` };
|
|
270872
|
-
for (const branch of branches)
|
|
270873
|
-
log.message(`${branch.name} (${branch.status})`);
|
|
270874
|
-
return { outroMessage: `${branches.length} branches` };
|
|
270875
|
-
}
|
|
270876
|
-
function getBranchesCommand() {
|
|
270877
|
-
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());
|
|
270878
271173
|
}
|
|
270879
271174
|
|
|
270880
271175
|
// ../../node_modules/ink/build/render.js
|
|
@@ -275840,6 +276135,119 @@ var build_default = TextInput;
|
|
|
275840
276135
|
// src/cli/commands/code/session.tsx
|
|
275841
276136
|
var import_react23 = __toESM(require_react(), 1);
|
|
275842
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
|
+
|
|
275843
276251
|
// src/cli/commands/code/paste.ts
|
|
275844
276252
|
import { PassThrough as PassThrough3 } from "node:stream";
|
|
275845
276253
|
var START = "\x1B[200~";
|
|
@@ -275913,6 +276321,393 @@ function createPasteFriendlyStdin(real) {
|
|
|
275913
276321
|
return proxy;
|
|
275914
276322
|
}
|
|
275915
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
|
+
|
|
275916
276711
|
// src/cli/commands/code/session-engine.ts
|
|
275917
276712
|
var POLL_MS = 1000;
|
|
275918
276713
|
function createSessionEngine(options) {
|
|
@@ -275932,6 +276727,28 @@ function createSessionEngine(options) {
|
|
|
275932
276727
|
let awaitingTurn = options.awaitingTurnLabel ?? null;
|
|
275933
276728
|
const awaitingSince = Date.now();
|
|
275934
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
|
+
};
|
|
275935
276752
|
const submit = (raw) => {
|
|
275936
276753
|
const typed = raw.trim();
|
|
275937
276754
|
if (!typed)
|
|
@@ -275972,25 +276789,38 @@ function createSessionEngine(options) {
|
|
|
275972
276789
|
return;
|
|
275973
276790
|
}
|
|
275974
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));
|
|
275975
276798
|
if (!prime) {
|
|
275976
276799
|
for (const event of events) {
|
|
275977
276800
|
if (event.kind === "tool_start") {
|
|
276801
|
+
const startedAt = Date.now();
|
|
275978
276802
|
running.set(event.id, {
|
|
275979
276803
|
alias: toolAlias(event.name),
|
|
275980
276804
|
label: event.label,
|
|
275981
276805
|
summary: event.summary,
|
|
275982
|
-
startedAt
|
|
276806
|
+
startedAt
|
|
275983
276807
|
});
|
|
276808
|
+
options.onLine({ running: event, startedAt });
|
|
275984
276809
|
continue;
|
|
275985
276810
|
}
|
|
275986
|
-
let elapsedMs;
|
|
275987
276811
|
if (event.kind === "tool_end") {
|
|
275988
276812
|
const started = running.get(event.id)?.startedAt;
|
|
275989
|
-
if (started != null)
|
|
275990
|
-
elapsedMs = Date.now() - started;
|
|
275991
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;
|
|
275992
276820
|
}
|
|
275993
|
-
const line = eventLine(event,
|
|
276821
|
+
const line = eventLine(event, undefined, {
|
|
276822
|
+
waitingHint: "answer in the card below"
|
|
276823
|
+
});
|
|
275994
276824
|
if (line != null) {
|
|
275995
276825
|
lastEventAt = Date.now();
|
|
275996
276826
|
options.onLine(line);
|
|
@@ -276057,6 +276887,7 @@ function createSessionEngine(options) {
|
|
|
276057
276887
|
});
|
|
276058
276888
|
},
|
|
276059
276889
|
submit,
|
|
276890
|
+
answer,
|
|
276060
276891
|
status() {
|
|
276061
276892
|
let phase = "idle";
|
|
276062
276893
|
if (awaitingTurn != null)
|
|
@@ -276079,7 +276910,8 @@ function createSessionEngine(options) {
|
|
|
276079
276910
|
turnStartedAt,
|
|
276080
276911
|
runningTool,
|
|
276081
276912
|
lastTurnMs,
|
|
276082
|
-
lastTurnOk
|
|
276913
|
+
lastTurnOk,
|
|
276914
|
+
pending
|
|
276083
276915
|
};
|
|
276084
276916
|
},
|
|
276085
276917
|
turnRunning() {
|
|
@@ -276111,6 +276943,17 @@ function exitAltScreen() {
|
|
|
276111
276943
|
altScreenActive = false;
|
|
276112
276944
|
process.stdout.write("\x1B[?1007l\x1B[?1049l");
|
|
276113
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
|
+
}
|
|
276114
276957
|
function statusText(status, musingSeed) {
|
|
276115
276958
|
const frame = FRAMES2[Math.floor(Date.now() / 120) % FRAMES2.length];
|
|
276116
276959
|
switch (status.phase) {
|
|
@@ -276118,15 +276961,8 @@ function statusText(status, musingSeed) {
|
|
|
276118
276961
|
return source_default.dim(`${frame} ${status.awaitingLabel} (${formatDuration2(Date.now() - status.awaitingSince)})`);
|
|
276119
276962
|
case "running": {
|
|
276120
276963
|
const turnFor = formatDuration2(Date.now() - (status.turnStartedAt ?? Date.now()));
|
|
276121
|
-
const tool = status.runningTool;
|
|
276122
|
-
if (tool) {
|
|
276123
|
-
const toolFor = Math.round((Date.now() - tool.startedAt) / 1000);
|
|
276124
|
-
const others = tool.others > 0 ? ` (+${tool.others})` : "";
|
|
276125
|
-
const what = tool.label || `${tool.alias}${tool.summary ? ` ${tool.summary}` : ""}`;
|
|
276126
|
-
return source_default.dim(`${frame} ${what}${others} · ${toolFor}s (turn ${turnFor})`);
|
|
276127
|
-
}
|
|
276128
276964
|
const quiet = status.quietForMs > 60000 ? " · a long private step — details render in the editor" : "";
|
|
276129
|
-
return `${source_default.magenta(
|
|
276965
|
+
return `${source_default.magenta(shimmer())} ${source_default.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`;
|
|
276130
276966
|
}
|
|
276131
276967
|
case "sending":
|
|
276132
276968
|
return source_default.dim(`${frame} sending…`);
|
|
@@ -276140,18 +276976,19 @@ function statusText(status, musingSeed) {
|
|
|
276140
276976
|
function SessionView({ engine, footer, subscribe }) {
|
|
276141
276977
|
const { exit } = use_app_default();
|
|
276142
276978
|
const [items, setItems] = import_react23.useState([]);
|
|
276979
|
+
const [verbose, setVerbose] = import_react23.useState(false);
|
|
276143
276980
|
const [input, setInput] = import_react23.useState("");
|
|
276144
276981
|
const [scroll, setScroll] = import_react23.useState(0);
|
|
276145
276982
|
const [, tick] = import_react23.useReducer((x) => x + 1, 0);
|
|
276146
276983
|
const [musingSeed] = import_react23.useState(() => Math.floor(Math.random() * 97));
|
|
276147
276984
|
const [currentModel, setCurrentModel] = import_react23.useState(null);
|
|
276148
276985
|
const [pickerIndex, setPickerIndex] = import_react23.useState(null);
|
|
276986
|
+
const [card, setCard] = import_react23.useState(null);
|
|
276987
|
+
const dismissedRef = import_react23.useRef(new Set);
|
|
276149
276988
|
const maxScrollRef = import_react23.useRef(0);
|
|
276150
276989
|
const meIdRef = import_react23.useRef(null);
|
|
276151
|
-
const emit = (
|
|
276152
|
-
|
|
276153
|
-
import_react23.useEffect(() => subscribe((line) => setItems((h) => [...h, `${line}
|
|
276154
|
-
`])), [subscribe]);
|
|
276990
|
+
const emit = (entry) => setItems((h) => withEntry(h, entry));
|
|
276991
|
+
import_react23.useEffect(() => subscribe((entry) => setItems((h) => withEntry(h, entry))), [subscribe]);
|
|
276155
276992
|
import_react23.useEffect(() => {
|
|
276156
276993
|
const timer = setInterval(tick, 120);
|
|
276157
276994
|
return () => clearInterval(timer);
|
|
@@ -276193,6 +277030,29 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276193
277030
|
emit(source_default.red(` /model: ${error instanceof Error ? error.message : String(error)}`));
|
|
276194
277031
|
}
|
|
276195
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]);
|
|
276196
277056
|
use_input_default((char, key) => {
|
|
276197
277057
|
if (pickerIndex !== null) {
|
|
276198
277058
|
if (key.upArrow)
|
|
@@ -276208,6 +277068,23 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276208
277068
|
}
|
|
276209
277069
|
return;
|
|
276210
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
|
+
}
|
|
276211
277088
|
if (key.ctrl && char === "c") {
|
|
276212
277089
|
if (input)
|
|
276213
277090
|
setInput("");
|
|
@@ -276219,6 +277096,10 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276219
277096
|
exit();
|
|
276220
277097
|
return;
|
|
276221
277098
|
}
|
|
277099
|
+
if (key.ctrl && char === "o") {
|
|
277100
|
+
setVerbose((v) => !v);
|
|
277101
|
+
return;
|
|
277102
|
+
}
|
|
276222
277103
|
if (key.upArrow) {
|
|
276223
277104
|
setScroll((s) => Math.min(s + 3, maxScrollRef.current));
|
|
276224
277105
|
return;
|
|
@@ -276244,14 +277125,17 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276244
277125
|
});
|
|
276245
277126
|
const columns = process.stdout.columns || 80;
|
|
276246
277127
|
const rows = process.stdout.rows || 24;
|
|
276247
|
-
const width =
|
|
277128
|
+
const width = columns;
|
|
276248
277129
|
const innerWidth = Math.max(10, width - 4);
|
|
276249
277130
|
const inputRows = Math.max(1, Math.ceil((input.length + 3) / innerWidth));
|
|
276250
277131
|
const pickerOpen = pickerIndex !== null;
|
|
276251
|
-
const
|
|
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);
|
|
276252
277135
|
const widgetHeight = inputBlockHeight + 3 + (footer.length ? 1 : 0);
|
|
276253
277136
|
const viewHeight = Math.max(3, rows - widgetHeight - 1);
|
|
276254
|
-
const lines = items.flatMap((item) => hardWrapAnsi(item,
|
|
277137
|
+
const lines = items.flatMap((item) => hardWrapAnsi(`${renderEntry(item, { verbose, foldHint: "Ctrl+O to expand" })}
|
|
277138
|
+
`, columns));
|
|
276255
277139
|
const maxScroll = Math.max(0, lines.length - viewHeight);
|
|
276256
277140
|
maxScrollRef.current = maxScroll;
|
|
276257
277141
|
const clamped = Math.min(scroll, maxScroll);
|
|
@@ -276298,29 +277182,64 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276298
277182
|
}, m.name, false, undefined, this);
|
|
276299
277183
|
})
|
|
276300
277184
|
]
|
|
276301
|
-
}, 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",
|
|
276302
277187
|
borderStyle: "round",
|
|
276303
|
-
borderColor: "
|
|
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",
|
|
277197
|
+
borderStyle: "round",
|
|
277198
|
+
borderColor: card?.typing ? "yellow" : "gray",
|
|
276304
277199
|
paddingX: 1,
|
|
276305
277200
|
width,
|
|
276306
277201
|
children: [
|
|
276307
|
-
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
276308
|
-
|
|
276309
|
-
children:
|
|
276310
|
-
|
|
276311
|
-
|
|
276312
|
-
|
|
276313
|
-
|
|
276314
|
-
|
|
276315
|
-
|
|
276316
|
-
|
|
276317
|
-
|
|
276318
|
-
|
|
276319
|
-
|
|
276320
|
-
|
|
276321
|
-
|
|
276322
|
-
|
|
276323
|
-
|
|
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)
|
|
276324
277243
|
]
|
|
276325
277244
|
}, undefined, true, undefined, this),
|
|
276326
277245
|
footer.length > 0 && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
@@ -276334,48 +277253,16 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276334
277253
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
276335
277254
|
dimColor: true,
|
|
276336
277255
|
wrap: "truncate-end",
|
|
276337
|
-
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"
|
|
276338
277257
|
}, undefined, false, undefined, this)
|
|
276339
277258
|
]
|
|
276340
277259
|
}, undefined, true, undefined, this);
|
|
276341
277260
|
}
|
|
276342
|
-
var LOGO_ROWS = 6;
|
|
276343
|
-
var LOGO_GAP_SUBROW = 8;
|
|
276344
|
-
var QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█";
|
|
276345
|
-
function buildLogoRows() {
|
|
276346
|
-
const sy = 2 * LOGO_ROWS;
|
|
276347
|
-
const sx = 2 * sy;
|
|
276348
|
-
const cx = (sx - 1) / 2;
|
|
276349
|
-
const cy = (sy - 1) / 2;
|
|
276350
|
-
const rad = sy / 2 - 0.5;
|
|
276351
|
-
const on = (px, py) => {
|
|
276352
|
-
if (py === LOGO_GAP_SUBROW)
|
|
276353
|
-
return false;
|
|
276354
|
-
const dx = (px - cx) * 0.5;
|
|
276355
|
-
const dy = py - cy;
|
|
276356
|
-
return dx * dx + dy * dy <= rad * rad;
|
|
276357
|
-
};
|
|
276358
|
-
const rows = [];
|
|
276359
|
-
for (let ty = 0;ty < sy; ty += 2) {
|
|
276360
|
-
let row = "";
|
|
276361
|
-
for (let tx = 0;tx < sx; tx += 2) {
|
|
276362
|
-
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);
|
|
276363
|
-
row += QUAD[bits];
|
|
276364
|
-
}
|
|
276365
|
-
rows.push(row.trim());
|
|
276366
|
-
}
|
|
276367
|
-
return rows.map((row) => row.trim());
|
|
276368
|
-
}
|
|
276369
277261
|
function renderHeader(who, mode) {
|
|
276370
277262
|
const orange = source_default.hex(BRAND_ORANGE);
|
|
276371
277263
|
const cwd = process.cwd().replace(process.env.HOME ?? "", "~");
|
|
276372
|
-
const logo =
|
|
276373
|
-
const logoW =
|
|
276374
|
-
const center = (s) => {
|
|
276375
|
-
const total = Math.max(0, logoW - s.length);
|
|
276376
|
-
const left = Math.floor(total / 2);
|
|
276377
|
-
return " ".repeat(left) + s + " ".repeat(total - left);
|
|
276378
|
-
};
|
|
277264
|
+
const logo = logoRows(BRAND_ORANGE);
|
|
277265
|
+
const logoW = LOGO_COLS;
|
|
276379
277266
|
const text = [
|
|
276380
277267
|
`${orange.bold("Base44 Code")} ${source_default.dim(`v${package_default.version}`)}`,
|
|
276381
277268
|
source_default.bold(who ? `Welcome back, ${who}!` : "Welcome!"),
|
|
@@ -276387,7 +277274,7 @@ function renderHeader(who, mode) {
|
|
|
276387
277274
|
const textTop = Math.max(0, Math.floor((logo.length - text.length) / 2));
|
|
276388
277275
|
const out = [];
|
|
276389
277276
|
for (let i = 0;i < height; i++) {
|
|
276390
|
-
const left = i < logo.length ?
|
|
277277
|
+
const left = i < logo.length ? logo[i] : " ".repeat(logoW);
|
|
276391
277278
|
const right = text[i - textTop] ?? "";
|
|
276392
277279
|
out.push(` ${left} ${right}`.trimEnd());
|
|
276393
277280
|
}
|
|
@@ -276491,6 +277378,7 @@ async function runGenesisSession(options) {
|
|
|
276491
277378
|
runningTool: null,
|
|
276492
277379
|
lastTurnMs: null,
|
|
276493
277380
|
lastTurnOk: true,
|
|
277381
|
+
pending: [],
|
|
276494
277382
|
quietForMs: 0
|
|
276495
277383
|
};
|
|
276496
277384
|
const genesis = {
|
|
@@ -276546,6 +277434,9 @@ async function runGenesisSession(options) {
|
|
|
276546
277434
|
}
|
|
276547
277435
|
return IDLE_STATUS;
|
|
276548
277436
|
},
|
|
277437
|
+
answer(p, action, input) {
|
|
277438
|
+
inner?.answer(p, action, input);
|
|
277439
|
+
},
|
|
276549
277440
|
turnRunning() {
|
|
276550
277441
|
return inner?.turnRunning() ?? creating;
|
|
276551
277442
|
}
|
|
@@ -276578,19 +277469,21 @@ async function runGenesisSession(options) {
|
|
|
276578
277469
|
|
|
276579
277470
|
// src/cli/commands/code/index.ts
|
|
276580
277471
|
var BRAND_ORANGE2 = "#E86B3C";
|
|
276581
|
-
async function bootstrapApp(prompt, footer, emit, importRepo) {
|
|
277472
|
+
async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path, wixInstance) {
|
|
276582
277473
|
let app;
|
|
276583
277474
|
try {
|
|
276584
|
-
app = await createAndLinkApp({ prompt, importRepo });
|
|
277475
|
+
app = await createAndLinkApp({ prompt, importRepo, path, wixInstance });
|
|
276585
277476
|
} catch (error) {
|
|
276586
277477
|
for (const line of await githubReauthLines(error) ?? [])
|
|
276587
277478
|
emit(line);
|
|
276588
277479
|
throw error;
|
|
276589
277480
|
}
|
|
277481
|
+
onCreated(app);
|
|
277482
|
+
footer.push(source_default.dim(`dir ${app.here ? "./" : `./${app.dirName}`}`));
|
|
276590
277483
|
if (app.repoUrl)
|
|
276591
277484
|
footer.push(terminalLink("repo", app.repoUrl));
|
|
276592
277485
|
footer.push(terminalLink("editor", app.editorUrl));
|
|
276593
|
-
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)`));
|
|
276594
277487
|
const branchId = await resolveActiveBranchId().catch(() => {
|
|
276595
277488
|
return;
|
|
276596
277489
|
});
|
|
@@ -276614,46 +277507,73 @@ async function bootstrapApp(prompt, footer, emit, importRepo) {
|
|
|
276614
277507
|
}
|
|
276615
277508
|
};
|
|
276616
277509
|
}
|
|
276617
|
-
async function codeAction(
|
|
277510
|
+
async function codeAction({ log }, options, appId) {
|
|
277511
|
+
const orange = source_default.hex(BRAND_ORANGE2);
|
|
277512
|
+
const chip = (label) => source_default.dim(`${orange("●")} ${label}`);
|
|
276618
277513
|
if (process.stdout.isTTY !== true) {
|
|
276619
277514
|
throw new InvalidInputError("base44 code is an interactive session and needs a terminal.");
|
|
276620
277515
|
}
|
|
276621
277516
|
let linked = false;
|
|
276622
277517
|
try {
|
|
276623
|
-
await initAppContext();
|
|
277518
|
+
await initAppContext(appId ? { appId } : {});
|
|
276624
277519
|
linked = true;
|
|
276625
277520
|
} catch {}
|
|
276626
277521
|
if (linked) {
|
|
276627
|
-
if (options.import) {
|
|
276628
|
-
throw new InvalidInputError("--import
|
|
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.");
|
|
276629
277524
|
}
|
|
277525
|
+
const { id, projectRoot } = getAppContext();
|
|
277526
|
+
const state = await assertBuilderApp(id);
|
|
276630
277527
|
const branchId = await resolveActiveBranchId().catch(() => {
|
|
276631
277528
|
return;
|
|
276632
277529
|
});
|
|
276633
277530
|
await runInteractiveSession({
|
|
276634
277531
|
branchId,
|
|
276635
|
-
footer: [],
|
|
277532
|
+
footer: [chip(appTypeChip(state))],
|
|
276636
277533
|
primeFirstPoll: true,
|
|
276637
277534
|
idleHint: "what should the agent do next?"
|
|
276638
277535
|
});
|
|
276639
|
-
|
|
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
|
+
}
|
|
277542
|
+
return {
|
|
277543
|
+
outroMessage: `Session closed. Resume with \`base44 code --app-id ${id}\`.`
|
|
277544
|
+
};
|
|
276640
277545
|
}
|
|
276641
|
-
|
|
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;
|
|
277553
|
+
let created;
|
|
276642
277554
|
const footer = [
|
|
276643
|
-
|
|
277555
|
+
chip(options.import ? repoLabel(options.import) : wixInstance ? "web app · wix" : "web app")
|
|
276644
277556
|
];
|
|
276645
277557
|
await runGenesisSession({
|
|
276646
|
-
idleHint: options.import ? "describe what to build over the
|
|
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",
|
|
276647
277559
|
creatingLabel: options.import ? "importing the repository" : "creating your app",
|
|
276648
|
-
modeLabel: options.import ? `
|
|
277560
|
+
modeLabel: options.import ? `Repository — ${repoLabel(options.import)}` : wixInstance ? "Web app — Wix launch (connector connected first)" : "Web app — Base44 template + builder agent",
|
|
276649
277561
|
footer,
|
|
276650
|
-
createApp: (prompt, emit) => bootstrapApp(prompt, footer, emit,
|
|
277562
|
+
createApp: (prompt, emit) => bootstrapApp(prompt, footer, emit, (app) => {
|
|
277563
|
+
created = app;
|
|
277564
|
+
}, options.import, options.path, wixInstance)
|
|
276651
277565
|
});
|
|
276652
|
-
|
|
277566
|
+
if (!created) {
|
|
277567
|
+
return { outroMessage: "Session closed. No app was created." };
|
|
277568
|
+
}
|
|
277569
|
+
for (const line of nextStepsLines(created))
|
|
277570
|
+
log.message(line);
|
|
277571
|
+
log.message(source_default.dim(` editor ${created.editorUrl}`));
|
|
277572
|
+
return { outroMessage: "Session closed." };
|
|
276653
277573
|
}
|
|
276654
277574
|
function getCodeCommand() {
|
|
276655
277575
|
const command = new Base44Command("code", { requireAppContext: false });
|
|
276656
|
-
command.description("Open Base44 Code
|
|
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));
|
|
276657
277577
|
return command;
|
|
276658
277578
|
}
|
|
276659
277579
|
|
|
@@ -277451,10 +278371,10 @@ function getConnectorsListAvailableCommand() {
|
|
|
277451
278371
|
}
|
|
277452
278372
|
|
|
277453
278373
|
// src/cli/commands/connectors/pull.ts
|
|
277454
|
-
import { dirname as dirname19, join as join27, resolve as
|
|
278374
|
+
import { dirname as dirname19, join as join27, resolve as resolve9 } from "node:path";
|
|
277455
278375
|
async function resolveConnectorsDir(options) {
|
|
277456
278376
|
if (!getAppContext().projectRoot) {
|
|
277457
|
-
return
|
|
278377
|
+
return resolve9(options.dir ?? "connectors");
|
|
277458
278378
|
}
|
|
277459
278379
|
const { project } = await readProjectConfig();
|
|
277460
278380
|
return join27(dirname19(project.configPath), project.connectorsDir);
|
|
@@ -277498,10 +278418,10 @@ function getConnectorsPullCommand() {
|
|
|
277498
278418
|
}
|
|
277499
278419
|
|
|
277500
278420
|
// src/cli/commands/connectors/push.ts
|
|
277501
|
-
import { resolve as
|
|
278421
|
+
import { resolve as resolve10 } from "node:path";
|
|
277502
278422
|
async function readConnectorsToPush(options) {
|
|
277503
278423
|
if (!getAppContext().projectRoot) {
|
|
277504
|
-
return readAllConnectors(
|
|
278424
|
+
return readAllConnectors(resolve10(options.dir ?? "connectors"));
|
|
277505
278425
|
}
|
|
277506
278426
|
const { connectors } = await readProjectConfig();
|
|
277507
278427
|
return connectors;
|
|
@@ -277970,7 +278890,7 @@ function getBuildCommand() {
|
|
|
277970
278890
|
}
|
|
277971
278891
|
|
|
277972
278892
|
// src/cli/commands/project/create.ts
|
|
277973
|
-
import { basename as
|
|
278893
|
+
import { basename as basename7, resolve as resolve11 } from "node:path";
|
|
277974
278894
|
var import_kebabCase2 = __toESM(require_kebabCase(), 1);
|
|
277975
278895
|
|
|
277976
278896
|
// src/cli/commands/project/scaffold-shared.ts
|
|
@@ -278135,8 +279055,8 @@ async function createInteractive(options, ctx) {
|
|
|
278135
279055
|
name: () => {
|
|
278136
279056
|
return options.name ? Promise.resolve(options.name) : Ze({
|
|
278137
279057
|
message: "What is the name of your project?",
|
|
278138
|
-
placeholder:
|
|
278139
|
-
initialValue:
|
|
279058
|
+
placeholder: basename7(process.cwd()),
|
|
279059
|
+
initialValue: basename7(process.cwd()),
|
|
278140
279060
|
validate: (value) => {
|
|
278141
279061
|
if (!value || value.trim().length === 0) {
|
|
278142
279062
|
return "Every project deserves a name";
|
|
@@ -278166,7 +279086,7 @@ async function createInteractive(options, ctx) {
|
|
|
278166
279086
|
}, ctx);
|
|
278167
279087
|
}
|
|
278168
279088
|
async function createNonInteractive(options, ctx) {
|
|
278169
|
-
ctx.log.info(`Creating a new project at ${
|
|
279089
|
+
ctx.log.info(`Creating a new project at ${resolve11(options.path)}`);
|
|
278170
279090
|
const template = await getTemplateById(options.template ?? DEFAULT_TEMPLATE_ID);
|
|
278171
279091
|
return await executeCreate({
|
|
278172
279092
|
template,
|
|
@@ -278190,7 +279110,7 @@ async function executeCreate({
|
|
|
278190
279110
|
}, ctx) {
|
|
278191
279111
|
const { log, runTask } = ctx;
|
|
278192
279112
|
const name = rawName.trim();
|
|
278193
|
-
const resolvedPath =
|
|
279113
|
+
const resolvedPath = resolve11(projectPath);
|
|
278194
279114
|
const organizationId = await resolveWorkspaceId(ctx, flagWorkspaceId, isInteractive);
|
|
278195
279115
|
const { projectId } = await runTask("Setting up your project...", async () => {
|
|
278196
279116
|
return await createProjectFiles({
|
|
@@ -278831,7 +279751,7 @@ function getLogsCommand() {
|
|
|
278831
279751
|
}
|
|
278832
279752
|
|
|
278833
279753
|
// src/cli/commands/project/scaffold.ts
|
|
278834
|
-
import { basename as
|
|
279754
|
+
import { basename as basename8, resolve as resolve12 } from "node:path";
|
|
278835
279755
|
function resolveAppId(options) {
|
|
278836
279756
|
const appId = options.appId;
|
|
278837
279757
|
if (!appId) {
|
|
@@ -278847,8 +279767,8 @@ function resolveAppId(options) {
|
|
|
278847
279767
|
async function scaffoldAction(ctx, name, options, command) {
|
|
278848
279768
|
const { log, runTask } = ctx;
|
|
278849
279769
|
const appId = resolveAppId(command.optsWithGlobals());
|
|
278850
|
-
const resolvedPath =
|
|
278851
|
-
const projectName = (name ??
|
|
279770
|
+
const resolvedPath = resolve12("./");
|
|
279771
|
+
const projectName = (name ?? basename8(resolvedPath)).trim();
|
|
278852
279772
|
const template = await getTemplateById("backend-only");
|
|
278853
279773
|
log.info(`Scaffolding project at ${resolvedPath}`);
|
|
278854
279774
|
const { projectId } = await runTask("Setting up your project...", async () => {
|
|
@@ -279165,6 +280085,21 @@ function getSandboxListDirectoryCommand() {
|
|
|
279165
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);
|
|
279166
280086
|
}
|
|
279167
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
|
+
|
|
279168
280103
|
// src/cli/commands/sandbox/read-file.ts
|
|
279169
280104
|
async function readFileAction({ runTask, branchId }, paths, options) {
|
|
279170
280105
|
const { id: appId } = getAppContext();
|
|
@@ -279218,7 +280153,7 @@ Examples:
|
|
|
279218
280153
|
|
|
279219
280154
|
// src/cli/commands/sandbox/index.ts
|
|
279220
280155
|
function getSandboxCommand() {
|
|
279221
|
-
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());
|
|
279222
280157
|
}
|
|
279223
280158
|
|
|
279224
280159
|
// src/cli/commands/secrets/delete.ts
|
|
@@ -279264,7 +280199,7 @@ function getSecretsListCommand() {
|
|
|
279264
280199
|
}
|
|
279265
280200
|
|
|
279266
280201
|
// src/cli/commands/secrets/set.ts
|
|
279267
|
-
import { resolve as
|
|
280202
|
+
import { resolve as resolve13 } from "node:path";
|
|
279268
280203
|
function parseEntries(entries) {
|
|
279269
280204
|
const secrets = {};
|
|
279270
280205
|
for (const entry of entries) {
|
|
@@ -279295,7 +280230,7 @@ async function setSecretsAction({ log, runTask }, entries, options) {
|
|
|
279295
280230
|
validateInput(entries, options);
|
|
279296
280231
|
let secrets;
|
|
279297
280232
|
if (options.envFile) {
|
|
279298
|
-
secrets = await parseEnvFile(
|
|
280233
|
+
secrets = await parseEnvFile(resolve13(options.envFile));
|
|
279299
280234
|
if (Object.keys(secrets).length === 0) {
|
|
279300
280235
|
throw new InvalidInputError("The env file contains no valid KEY=VALUE entries.");
|
|
279301
280236
|
}
|
|
@@ -279324,7 +280259,7 @@ function getSecretsCommand() {
|
|
|
279324
280259
|
}
|
|
279325
280260
|
|
|
279326
280261
|
// src/cli/commands/site/deploy.ts
|
|
279327
|
-
import { resolve as
|
|
280262
|
+
import { resolve as resolve14 } from "node:path";
|
|
279328
280263
|
async function deployAction2(ctx, options) {
|
|
279329
280264
|
const { isNonInteractive } = ctx;
|
|
279330
280265
|
if (isNonInteractive && !options.yes) {
|
|
@@ -279402,7 +280337,7 @@ async function deployTarball({ runTask }, project) {
|
|
|
279402
280337
|
}
|
|
279403
280338
|
function siteOutputDir(project) {
|
|
279404
280339
|
const outputDirectory = project.site?.outputDirectory;
|
|
279405
|
-
return outputDirectory ?
|
|
280340
|
+
return outputDirectory ? resolve14(project.root, outputDirectory) : null;
|
|
279406
280341
|
}
|
|
279407
280342
|
function getSiteDeployCommand() {
|
|
279408
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)");
|
|
@@ -281904,7 +282839,7 @@ function createCustomIntegrationRoutes(remoteProxy, logger) {
|
|
|
281904
282839
|
|
|
281905
282840
|
// src/cli/dev/dev-server/watcher.ts
|
|
281906
282841
|
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
281907
|
-
import { relative as
|
|
282842
|
+
import { relative as relative10 } from "node:path";
|
|
281908
282843
|
|
|
281909
282844
|
// ../../node_modules/chokidar/index.js
|
|
281910
282845
|
import { EventEmitter as EventEmitter5 } from "node:events";
|
|
@@ -283589,7 +284524,7 @@ class WatchBase44 extends EventEmitter6 {
|
|
|
283589
284524
|
ignoreInitial: true
|
|
283590
284525
|
});
|
|
283591
284526
|
watcher.on("all", import_debounce4.default(async (_event, path) => {
|
|
283592
|
-
this.emit("change", name,
|
|
284527
|
+
this.emit("change", name, relative10(targetPath, path));
|
|
283593
284528
|
}, WATCH_DEBOUNCE_MS));
|
|
283594
284529
|
watcher.on("error", (err) => {
|
|
283595
284530
|
this.logger.error(`Watch handler failed for ${targetPath}`, err);
|
|
@@ -284021,7 +284956,7 @@ async function runScript(options) {
|
|
|
284021
284956
|
}
|
|
284022
284957
|
}
|
|
284023
284958
|
// src/cli/commands/exec.ts
|
|
284024
|
-
function
|
|
284959
|
+
function readStdin3() {
|
|
284025
284960
|
return new Promise((resolve, reject) => {
|
|
284026
284961
|
let data = "";
|
|
284027
284962
|
process.stdin.setEncoding("utf-8");
|
|
@@ -284065,7 +285000,7 @@ async function execAction({ app, isNonInteractive }, options) {
|
|
|
284065
285000
|
if (!isNonInteractive) {
|
|
284066
285001
|
throw noInputError;
|
|
284067
285002
|
}
|
|
284068
|
-
const code = await
|
|
285003
|
+
const code = await readStdin3();
|
|
284069
285004
|
if (!code.trim()) {
|
|
284070
285005
|
throw noInputError;
|
|
284071
285006
|
}
|
|
@@ -284099,7 +285034,7 @@ Examples:
|
|
|
284099
285034
|
}
|
|
284100
285035
|
|
|
284101
285036
|
// src/cli/commands/project/eject.ts
|
|
284102
|
-
import { resolve as
|
|
285037
|
+
import { resolve as resolve18 } from "node:path";
|
|
284103
285038
|
var import_kebabCase3 = __toESM(require_kebabCase(), 1);
|
|
284104
285039
|
async function eject(ctx, options, command) {
|
|
284105
285040
|
const { log, runTask, isNonInteractive } = ctx;
|
|
@@ -284163,7 +285098,7 @@ async function eject(ctx, options, command) {
|
|
|
284163
285098
|
Ne("Operation cancelled.");
|
|
284164
285099
|
throw new CLIExitError(0);
|
|
284165
285100
|
}
|
|
284166
|
-
const resolvedPath =
|
|
285101
|
+
const resolvedPath = resolve18(selectedPath);
|
|
284167
285102
|
await runTask("Downloading your project's code...", async (updateMessage) => {
|
|
284168
285103
|
await createProjectFilesForExistingProject({
|
|
284169
285104
|
projectId,
|
|
@@ -284245,7 +285180,7 @@ function createProgram(context) {
|
|
|
284245
285180
|
program.addCommand(getSecretsCommand());
|
|
284246
285181
|
program.addCommand(getSandboxCommand());
|
|
284247
285182
|
program.addCommand(getBranchesCommand());
|
|
284248
|
-
program.addCommand(
|
|
285183
|
+
program.addCommand(getBuilderCommand());
|
|
284249
285184
|
program.addCommand(getCodeCommand());
|
|
284250
285185
|
program.addCommand(getAuthCommand());
|
|
284251
285186
|
program.addCommand(getSiteCommand());
|
|
@@ -288246,6 +289181,13 @@ function getFullCommandName(command) {
|
|
|
288246
289181
|
}
|
|
288247
289182
|
return parts.join(" ");
|
|
288248
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
|
+
}
|
|
288249
289191
|
function addCommandInfoToErrorReporter(program, errorReporter) {
|
|
288250
289192
|
program.hook("preAction", (_, actionCommand) => {
|
|
288251
289193
|
const fullCommandName = getFullCommandName(actionCommand);
|
|
@@ -288253,7 +289195,7 @@ function addCommandInfoToErrorReporter(program, errorReporter) {
|
|
|
288253
289195
|
command: {
|
|
288254
289196
|
name: fullCommandName,
|
|
288255
289197
|
args: actionCommand.args,
|
|
288256
|
-
options: actionCommand.opts()
|
|
289198
|
+
options: redactSensitiveOptions(actionCommand.opts())
|
|
288257
289199
|
}
|
|
288258
289200
|
});
|
|
288259
289201
|
});
|
|
@@ -288300,4 +289242,4 @@ export {
|
|
|
288300
289242
|
runCLI
|
|
288301
289243
|
};
|
|
288302
289244
|
|
|
288303
|
-
//# debugId=
|
|
289245
|
+
//# debugId=F9AF4FAF927903AE64756E2164756E21
|