@base44-preview/cli 0.1.15-pr.630.a207c26 → 0.1.15-pr.630.ab1ebad
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 +1045 -679
- package/dist/cli/index.js.map +22 -21
- 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
|
|
@@ -269450,6 +269450,467 @@ function getAgentsCommand() {
|
|
|
269450
269450
|
return new Command2("agents").description("Manage project agents").addCommand(getAgentsPushCommand()).addCommand(getAgentsPullCommand());
|
|
269451
269451
|
}
|
|
269452
269452
|
|
|
269453
|
+
// src/cli/commands/auth/password-login.ts
|
|
269454
|
+
import { dirname as dirname15, join as join21 } from "node:path";
|
|
269455
|
+
async function passwordLoginAction({ log, runTask }, action) {
|
|
269456
|
+
const shouldEnable = action === "enable";
|
|
269457
|
+
const { project } = await readProjectConfig();
|
|
269458
|
+
const configDir = dirname15(project.configPath);
|
|
269459
|
+
const authDir = join21(configDir, project.authDir);
|
|
269460
|
+
const updated = await runTask("Updating local auth config", async () => {
|
|
269461
|
+
const current = await readAuthConfig(authDir) ?? DEFAULT_AUTH_CONFIG;
|
|
269462
|
+
const merged = { ...current, enableUsernamePassword: shouldEnable };
|
|
269463
|
+
await writeAuthConfig(authDir, merged);
|
|
269464
|
+
return merged;
|
|
269465
|
+
});
|
|
269466
|
+
if (!shouldEnable && !hasAnyLoginMethod(updated)) {
|
|
269467
|
+
log.warn("Disabling password auth will leave no login methods enabled. Users will be locked out.");
|
|
269468
|
+
}
|
|
269469
|
+
const newStatus = shouldEnable ? "enabled" : "disabled";
|
|
269470
|
+
return {
|
|
269471
|
+
outroMessage: `Username & password authentication ${newStatus} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
|
|
269472
|
+
};
|
|
269473
|
+
}
|
|
269474
|
+
function getPasswordLoginCommand() {
|
|
269475
|
+
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);
|
|
269476
|
+
}
|
|
269477
|
+
|
|
269478
|
+
// src/cli/commands/auth/pull.ts
|
|
269479
|
+
import { dirname as dirname16, join as join22 } from "node:path";
|
|
269480
|
+
async function pullAuthAction({
|
|
269481
|
+
log,
|
|
269482
|
+
runTask
|
|
269483
|
+
}) {
|
|
269484
|
+
const { project } = await readProjectConfig();
|
|
269485
|
+
const configDir = dirname16(project.configPath);
|
|
269486
|
+
const authDir = join22(configDir, project.authDir);
|
|
269487
|
+
const remoteConfig = await runTask("Fetching auth config from Base44", async () => {
|
|
269488
|
+
return await pullAuthConfig();
|
|
269489
|
+
}, {
|
|
269490
|
+
successMessage: "Auth config fetched successfully",
|
|
269491
|
+
errorMessage: "Failed to fetch auth config"
|
|
269492
|
+
});
|
|
269493
|
+
const { written } = await runTask("Syncing auth config file", async () => {
|
|
269494
|
+
return await writeAuthConfig(authDir, remoteConfig);
|
|
269495
|
+
}, {
|
|
269496
|
+
successMessage: "Auth config file synced successfully",
|
|
269497
|
+
errorMessage: "Failed to sync auth config file"
|
|
269498
|
+
});
|
|
269499
|
+
if (written) {
|
|
269500
|
+
log.success("Auth config written to local file");
|
|
269501
|
+
} else {
|
|
269502
|
+
log.info("Auth config is already up to date");
|
|
269503
|
+
}
|
|
269504
|
+
return {
|
|
269505
|
+
outroMessage: `Pulled auth config to ${authDir} (overwrites local file)`
|
|
269506
|
+
};
|
|
269507
|
+
}
|
|
269508
|
+
function getAuthPullCommand() {
|
|
269509
|
+
return new Base44Command("pull").description("Pull auth config from Base44 to local file").action(pullAuthAction);
|
|
269510
|
+
}
|
|
269511
|
+
|
|
269512
|
+
// src/cli/commands/auth/push.ts
|
|
269513
|
+
async function pushAuthAction({ isNonInteractive, log, runTask }, options) {
|
|
269514
|
+
const { authConfig } = await readProjectConfig();
|
|
269515
|
+
if (authConfig.length === 0) {
|
|
269516
|
+
log.info("No local auth config found");
|
|
269517
|
+
return {
|
|
269518
|
+
outroMessage: "No auth config to push. Run `base44 auth pull` to fetch the remote config first."
|
|
269519
|
+
};
|
|
269520
|
+
}
|
|
269521
|
+
if (!hasAnyLoginMethod(authConfig[0])) {
|
|
269522
|
+
log.warn("This config has no login methods enabled. Pushing it will lock out all users.");
|
|
269523
|
+
}
|
|
269524
|
+
if (!options.yes) {
|
|
269525
|
+
if (isNonInteractive) {
|
|
269526
|
+
throw new InvalidInputError("--yes is required in non-interactive mode");
|
|
269527
|
+
}
|
|
269528
|
+
const shouldPush = await Re({
|
|
269529
|
+
message: "Push auth config to Base44?"
|
|
269530
|
+
});
|
|
269531
|
+
if (Ct(shouldPush) || !shouldPush) {
|
|
269532
|
+
return { outroMessage: "Push cancelled" };
|
|
269533
|
+
}
|
|
269534
|
+
}
|
|
269535
|
+
await runTask("Pushing auth config to Base44", async () => {
|
|
269536
|
+
return await pushAuthConfig(authConfig[0] ?? null);
|
|
269537
|
+
}, {
|
|
269538
|
+
successMessage: "Auth config pushed successfully",
|
|
269539
|
+
errorMessage: "Failed to push auth config"
|
|
269540
|
+
});
|
|
269541
|
+
return {
|
|
269542
|
+
outroMessage: "Auth config pushed to Base44"
|
|
269543
|
+
};
|
|
269544
|
+
}
|
|
269545
|
+
function getAuthPushCommand() {
|
|
269546
|
+
return new Base44Command("push").description("Push local auth config to Base44").option("-y, --yes", "Skip confirmation prompt").action(pushAuthAction);
|
|
269547
|
+
}
|
|
269548
|
+
|
|
269549
|
+
// src/cli/commands/auth/social-login.ts
|
|
269550
|
+
import { dirname as dirname17, join as join23, resolve as resolve6 } from "node:path";
|
|
269551
|
+
var PROVIDER_LABELS = {
|
|
269552
|
+
google: "Google",
|
|
269553
|
+
microsoft: "Microsoft",
|
|
269554
|
+
facebook: "Facebook",
|
|
269555
|
+
apple: "Apple"
|
|
269556
|
+
};
|
|
269557
|
+
var VALID_PROVIDER_NAMES = Object.keys(SOCIAL_PROVIDERS);
|
|
269558
|
+
var PROVIDER_OAUTH_CLI = {
|
|
269559
|
+
google: {
|
|
269560
|
+
envVar: "google_oauth_client_secret",
|
|
269561
|
+
promptMessage: "Enter Google OAuth client secret"
|
|
269562
|
+
}
|
|
269563
|
+
};
|
|
269564
|
+
function hasSecretOptions(options) {
|
|
269565
|
+
return Boolean(options.clientSecret || options.clientSecretStdin || options.envFile);
|
|
269566
|
+
}
|
|
269567
|
+
function hasCustomOAuthOptions(options) {
|
|
269568
|
+
return Boolean(options.clientId || hasSecretOptions(options));
|
|
269569
|
+
}
|
|
269570
|
+
async function socialLoginAction({ log, isNonInteractive, runTask }, provider, action, options) {
|
|
269571
|
+
const shouldEnable = action === "enable";
|
|
269572
|
+
const providerInfo = SOCIAL_PROVIDERS[provider];
|
|
269573
|
+
const label = PROVIDER_LABELS[provider];
|
|
269574
|
+
const hasOAuthOptions = hasCustomOAuthOptions(options);
|
|
269575
|
+
if (hasOAuthOptions && !providerInfo.customOAuth) {
|
|
269576
|
+
throw new InvalidInputError(`Custom OAuth options are only supported for providers with custom OAuth (e.g., google). Use: base44 auth social-login ${provider} ${action}`);
|
|
269577
|
+
}
|
|
269578
|
+
if (hasOAuthOptions && !shouldEnable) {
|
|
269579
|
+
throw new InvalidInputError(`Custom OAuth options cannot be used with disable. To disable ${label} login: base44 auth social-login ${provider} disable`);
|
|
269580
|
+
}
|
|
269581
|
+
if (hasSecretOptions(options) && !options.clientId) {
|
|
269582
|
+
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>`);
|
|
269583
|
+
}
|
|
269584
|
+
const oauth = providerInfo.customOAuth;
|
|
269585
|
+
const oauthCli = PROVIDER_OAUTH_CLI[provider];
|
|
269586
|
+
const useCustomOAuth = shouldEnable && hasOAuthOptions && oauth != null;
|
|
269587
|
+
let clientSecret;
|
|
269588
|
+
if (useCustomOAuth && oauth && oauthCli && hasSecretOptions(options)) {
|
|
269589
|
+
if (options.envFile) {
|
|
269590
|
+
const secrets = await parseEnvFile(resolve6(options.envFile));
|
|
269591
|
+
const value = secrets[oauthCli.envVar];
|
|
269592
|
+
if (!value) {
|
|
269593
|
+
throw new InvalidInputError(`Key "${oauthCli.envVar}" not found in ${options.envFile}.`);
|
|
269594
|
+
}
|
|
269595
|
+
clientSecret = value;
|
|
269596
|
+
} else {
|
|
269597
|
+
clientSecret = await resolveSecret({
|
|
269598
|
+
flagValue: options.clientSecret,
|
|
269599
|
+
fromStdin: options.clientSecretStdin,
|
|
269600
|
+
envVar: oauthCli.envVar,
|
|
269601
|
+
promptMessage: oauthCli.promptMessage,
|
|
269602
|
+
isNonInteractive,
|
|
269603
|
+
name: "client secret",
|
|
269604
|
+
hints: [
|
|
269605
|
+
{
|
|
269606
|
+
message: `Provide via flag: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`,
|
|
269607
|
+
command: `base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`
|
|
269608
|
+
},
|
|
269609
|
+
{
|
|
269610
|
+
message: `Provide via stdin: echo <secret> | base44 auth social-login ${provider} enable --client-id <id> --client-secret-stdin`
|
|
269611
|
+
},
|
|
269612
|
+
{
|
|
269613
|
+
message: `Provide via env: ${oauthCli.envVar}=<secret> base44 auth social-login ${provider} enable --client-id <id>`
|
|
269614
|
+
}
|
|
269615
|
+
]
|
|
269616
|
+
});
|
|
269617
|
+
}
|
|
269618
|
+
}
|
|
269619
|
+
const { project } = await readProjectConfig();
|
|
269620
|
+
const configDir = dirname17(project.configPath);
|
|
269621
|
+
const authDir = join23(configDir, project.authDir);
|
|
269622
|
+
const { config: updated } = await runTask("Updating local auth config", async () => updateSocialLoginConfig(authDir, provider, shouldEnable, useCustomOAuth && options.clientId ? { clientId: options.clientId } : undefined));
|
|
269623
|
+
if (clientSecret) {
|
|
269624
|
+
await runTask("Saving client secret", async () => pushCustomOAuthSecret(provider, clientSecret));
|
|
269625
|
+
}
|
|
269626
|
+
if (!shouldEnable && !hasAnyLoginMethod(updated)) {
|
|
269627
|
+
log.warn(`Disabling ${label} login will leave no login methods enabled. Users will be locked out.`);
|
|
269628
|
+
}
|
|
269629
|
+
const newStatus = shouldEnable ? "enabled" : "disabled";
|
|
269630
|
+
const oauthNote = useCustomOAuth ? " with custom OAuth" : "";
|
|
269631
|
+
let outroMessage = `${label} login ${newStatus}${oauthNote} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`;
|
|
269632
|
+
if (useCustomOAuth && !clientSecret) {
|
|
269633
|
+
outroMessage += `
|
|
269634
|
+
Remember to push the client secret separately: base44 secrets set --env-file <path>`;
|
|
269635
|
+
}
|
|
269636
|
+
return { outroMessage };
|
|
269637
|
+
}
|
|
269638
|
+
function getSocialLoginCommand() {
|
|
269639
|
+
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([
|
|
269640
|
+
"enable",
|
|
269641
|
+
"disable"
|
|
269642
|
+
])).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);
|
|
269643
|
+
}
|
|
269644
|
+
|
|
269645
|
+
// src/cli/commands/auth/sso.ts
|
|
269646
|
+
import { dirname as dirname18, join as join24, resolve as resolve7 } from "node:path";
|
|
269647
|
+
var SSOConfigFileSchema = object({
|
|
269648
|
+
provider: _enum(Object.values(KNOWN_SSO_PROVIDERS)),
|
|
269649
|
+
clientId: string2(),
|
|
269650
|
+
clientSecret: string2(),
|
|
269651
|
+
scope: string2().optional(),
|
|
269652
|
+
discoveryUrl: string2().optional(),
|
|
269653
|
+
tenantId: string2().optional(),
|
|
269654
|
+
oktaDomain: string2().optional(),
|
|
269655
|
+
authEndpoint: string2().optional(),
|
|
269656
|
+
tokenEndpoint: string2().optional(),
|
|
269657
|
+
userinfoEndpoint: string2().optional(),
|
|
269658
|
+
jwksUri: string2().optional(),
|
|
269659
|
+
ssoName: string2().optional()
|
|
269660
|
+
});
|
|
269661
|
+
async function loadSSOConfigFile(filePath) {
|
|
269662
|
+
const resolved = resolve7(filePath);
|
|
269663
|
+
const raw = await readJsonFile(resolved);
|
|
269664
|
+
const result = SSOConfigFileSchema.safeParse(raw);
|
|
269665
|
+
if (!result.success) {
|
|
269666
|
+
throw new SchemaValidationError("Invalid SSO config file", result.error, filePath);
|
|
269667
|
+
}
|
|
269668
|
+
return result.data;
|
|
269669
|
+
}
|
|
269670
|
+
function mergeFileWithFlags(fileConfig, options) {
|
|
269671
|
+
return {
|
|
269672
|
+
provider: options.provider ?? fileConfig.provider,
|
|
269673
|
+
clientId: options.clientId ?? fileConfig.clientId,
|
|
269674
|
+
clientSecret: options.clientSecret ?? fileConfig.clientSecret,
|
|
269675
|
+
clientSecretStdin: options.clientSecretStdin,
|
|
269676
|
+
envFile: options.envFile,
|
|
269677
|
+
scope: options.scope ?? fileConfig.scope,
|
|
269678
|
+
discoveryUrl: options.discoveryUrl ?? fileConfig.discoveryUrl,
|
|
269679
|
+
tenantId: options.tenantId ?? fileConfig.tenantId,
|
|
269680
|
+
oktaDomain: options.oktaDomain ?? fileConfig.oktaDomain,
|
|
269681
|
+
authEndpoint: options.authEndpoint ?? fileConfig.authEndpoint,
|
|
269682
|
+
tokenEndpoint: options.tokenEndpoint ?? fileConfig.tokenEndpoint,
|
|
269683
|
+
userinfoEndpoint: options.userinfoEndpoint ?? fileConfig.userinfoEndpoint,
|
|
269684
|
+
jwksUri: options.jwksUri ?? fileConfig.jwksUri,
|
|
269685
|
+
ssoName: options.ssoName ?? fileConfig.ssoName
|
|
269686
|
+
};
|
|
269687
|
+
}
|
|
269688
|
+
var providerNames = Object.keys(KNOWN_SSO_PROVIDERS);
|
|
269689
|
+
var SECRET_KEY_TO_FLAG = {
|
|
269690
|
+
["sso_name" /* Name */]: "--sso-name",
|
|
269691
|
+
["sso_client_id" /* ClientId */]: "--client-id",
|
|
269692
|
+
["sso_client_secret" /* ClientSecret */]: "--client-secret",
|
|
269693
|
+
["sso_scope" /* Scope */]: "--scope",
|
|
269694
|
+
["sso_discovery_url" /* DiscoveryUrl */]: "--discovery-url",
|
|
269695
|
+
["sso_tenant_id" /* TenantId */]: "--tenant-id",
|
|
269696
|
+
["sso_auth_endpoint" /* AuthEndpoint */]: "--auth-endpoint",
|
|
269697
|
+
["sso_token_endpoint" /* TokenEndpoint */]: "--token-endpoint",
|
|
269698
|
+
["sso_userinfo_endpoint" /* UserinfoEndpoint */]: "--userinfo-endpoint",
|
|
269699
|
+
["sso_okta_domain" /* OktaDomain */]: "--okta-domain",
|
|
269700
|
+
["sso_jwks_uri" /* JwksUri */]: "--jwks-uri"
|
|
269701
|
+
};
|
|
269702
|
+
function secretKeyToFlag(key) {
|
|
269703
|
+
return SECRET_KEY_TO_FLAG[key];
|
|
269704
|
+
}
|
|
269705
|
+
function exampleCommand(provider) {
|
|
269706
|
+
let cmd = `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`;
|
|
269707
|
+
if (provider === KNOWN_SSO_PROVIDERS.microsoft)
|
|
269708
|
+
cmd += " --tenant-id <id>";
|
|
269709
|
+
if (provider === KNOWN_SSO_PROVIDERS.okta)
|
|
269710
|
+
cmd += " --okta-domain <domain>";
|
|
269711
|
+
if (provider === KNOWN_SSO_PROVIDERS.custom)
|
|
269712
|
+
cmd += " --sso-name <name> --auth-endpoint <url> --token-endpoint <url> --userinfo-endpoint <url> --jwks-uri <url>";
|
|
269713
|
+
return cmd;
|
|
269714
|
+
}
|
|
269715
|
+
function validateProvider(provider) {
|
|
269716
|
+
if (!provider) {
|
|
269717
|
+
throw new InvalidInputError("Missing --provider.", {
|
|
269718
|
+
hints: [
|
|
269719
|
+
{
|
|
269720
|
+
message: `Valid providers: ${providerNames.join(", ")}`,
|
|
269721
|
+
command: "base44 auth sso enable --provider <provider> --client-id <id> --client-secret <secret>"
|
|
269722
|
+
}
|
|
269723
|
+
]
|
|
269724
|
+
});
|
|
269725
|
+
}
|
|
269726
|
+
return provider;
|
|
269727
|
+
}
|
|
269728
|
+
async function ssoEnableAction({ isNonInteractive, runTask }, options) {
|
|
269729
|
+
if (options.file && options.envFile) {
|
|
269730
|
+
throw new InvalidInputError("--file and --env-file cannot be used together. Provide the client secret either inside --file or via --env-file.");
|
|
269731
|
+
}
|
|
269732
|
+
let merged = options;
|
|
269733
|
+
if (options.file) {
|
|
269734
|
+
const fileConfig = await loadSSOConfigFile(options.file);
|
|
269735
|
+
merged = mergeFileWithFlags(fileConfig, options);
|
|
269736
|
+
}
|
|
269737
|
+
const provider = validateProvider(merged.provider);
|
|
269738
|
+
if (!merged.clientId) {
|
|
269739
|
+
throw new InvalidInputError("Missing --client-id.", {
|
|
269740
|
+
hints: [
|
|
269741
|
+
{
|
|
269742
|
+
message: `Example: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
|
|
269743
|
+
command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
|
|
269744
|
+
}
|
|
269745
|
+
]
|
|
269746
|
+
});
|
|
269747
|
+
}
|
|
269748
|
+
let clientSecret;
|
|
269749
|
+
if (merged.envFile && !merged.clientSecret) {
|
|
269750
|
+
const secrets = await parseEnvFile(resolve7(merged.envFile));
|
|
269751
|
+
const value = secrets.sso_client_secret;
|
|
269752
|
+
if (!value) {
|
|
269753
|
+
throw new InvalidInputError(`Key "sso_client_secret" not found in ${merged.envFile}.`);
|
|
269754
|
+
}
|
|
269755
|
+
clientSecret = value;
|
|
269756
|
+
} else {
|
|
269757
|
+
clientSecret = await resolveSecret({
|
|
269758
|
+
flagValue: merged.clientSecret,
|
|
269759
|
+
fromStdin: merged.clientSecretStdin,
|
|
269760
|
+
envVar: "sso_client_secret",
|
|
269761
|
+
promptMessage: "Enter SSO client secret",
|
|
269762
|
+
isNonInteractive,
|
|
269763
|
+
name: "client secret",
|
|
269764
|
+
hints: [
|
|
269765
|
+
{
|
|
269766
|
+
message: `Provide via flag: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
|
|
269767
|
+
command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
|
|
269768
|
+
},
|
|
269769
|
+
{
|
|
269770
|
+
message: `Provide via stdin: echo <secret> | base44 auth sso enable --provider ${provider} --client-id <id> --client-secret-stdin`
|
|
269771
|
+
},
|
|
269772
|
+
{
|
|
269773
|
+
message: `Provide via env: sso_client_secret=<secret> base44 auth sso enable --provider ${provider} --client-id <id>`
|
|
269774
|
+
}
|
|
269775
|
+
]
|
|
269776
|
+
});
|
|
269777
|
+
}
|
|
269778
|
+
const secretOptions = {
|
|
269779
|
+
clientId: merged.clientId,
|
|
269780
|
+
clientSecret,
|
|
269781
|
+
scope: merged.scope,
|
|
269782
|
+
discoveryUrl: merged.discoveryUrl,
|
|
269783
|
+
tenantId: merged.tenantId,
|
|
269784
|
+
oktaDomain: merged.oktaDomain,
|
|
269785
|
+
authEndpoint: merged.authEndpoint,
|
|
269786
|
+
tokenEndpoint: merged.tokenEndpoint,
|
|
269787
|
+
userinfoEndpoint: merged.userinfoEndpoint,
|
|
269788
|
+
jwksUri: merged.jwksUri,
|
|
269789
|
+
ssoName: merged.ssoName
|
|
269790
|
+
};
|
|
269791
|
+
let secrets;
|
|
269792
|
+
try {
|
|
269793
|
+
secrets = buildSSOSecrets(provider, secretOptions);
|
|
269794
|
+
} catch (error) {
|
|
269795
|
+
if (error instanceof MissingSSOFieldsError) {
|
|
269796
|
+
const flagNames = error.missingKeys.map(secretKeyToFlag);
|
|
269797
|
+
throw new InvalidInputError(`Missing required fields for ${error.provider}: ${flagNames.join(", ")}`, {
|
|
269798
|
+
hints: [
|
|
269799
|
+
{
|
|
269800
|
+
message: `Example: ${exampleCommand(provider)}`,
|
|
269801
|
+
command: exampleCommand(provider)
|
|
269802
|
+
}
|
|
269803
|
+
]
|
|
269804
|
+
});
|
|
269805
|
+
}
|
|
269806
|
+
throw error;
|
|
269807
|
+
}
|
|
269808
|
+
const { project } = await readProjectConfig();
|
|
269809
|
+
const configDir = dirname18(project.configPath);
|
|
269810
|
+
const authDir = join24(configDir, project.authDir);
|
|
269811
|
+
await runTask("Updating local auth config", async () => updateSSOConfig(authDir, provider, true));
|
|
269812
|
+
await runTask("Saving SSO credentials", async () => pushSSOSecrets(secrets));
|
|
269813
|
+
return {
|
|
269814
|
+
outroMessage: `SSO configured with ${provider} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
|
|
269815
|
+
};
|
|
269816
|
+
}
|
|
269817
|
+
function hasEnableOnlyOptions(options) {
|
|
269818
|
+
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);
|
|
269819
|
+
}
|
|
269820
|
+
async function ssoDisableAction({ log, runTask }, options) {
|
|
269821
|
+
if (hasEnableOnlyOptions(options)) {
|
|
269822
|
+
throw new InvalidInputError("Configuration options cannot be used with disable. To disable SSO: base44 auth sso disable");
|
|
269823
|
+
}
|
|
269824
|
+
const { project } = await readProjectConfig();
|
|
269825
|
+
const configDir = dirname18(project.configPath);
|
|
269826
|
+
const authDir = join24(configDir, project.authDir);
|
|
269827
|
+
const updated = await runTask("Updating local auth config", async () => updateSSOConfig(authDir, null, false));
|
|
269828
|
+
await runTask("Removing SSO credentials", async () => deleteSSOSecrets());
|
|
269829
|
+
if (!hasAnyLoginMethod(updated)) {
|
|
269830
|
+
log.warn("Disabling SSO will leave no login methods enabled. Users will be locked out.");
|
|
269831
|
+
}
|
|
269832
|
+
return {
|
|
269833
|
+
outroMessage: "SSO disabled in local config and credentials removed. Run `base44 auth push` or `base44 deploy` to apply."
|
|
269834
|
+
};
|
|
269835
|
+
}
|
|
269836
|
+
async function ssoAction(context, action, options) {
|
|
269837
|
+
if (action === "disable") {
|
|
269838
|
+
return ssoDisableAction(context, options);
|
|
269839
|
+
}
|
|
269840
|
+
return ssoEnableAction(context, options);
|
|
269841
|
+
}
|
|
269842
|
+
function getSSOCommand() {
|
|
269843
|
+
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([
|
|
269844
|
+
"enable",
|
|
269845
|
+
"disable"
|
|
269846
|
+
])).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);
|
|
269847
|
+
}
|
|
269848
|
+
|
|
269849
|
+
// src/cli/commands/auth/index.ts
|
|
269850
|
+
function getAuthCommand() {
|
|
269851
|
+
return new Command2("auth").description("Manage app authentication settings").addCommand(getPasswordLoginCommand()).addCommand(getSocialLoginCommand()).addCommand(getSSOCommand()).addCommand(getAuthPullCommand()).addCommand(getAuthPushCommand());
|
|
269852
|
+
}
|
|
269853
|
+
|
|
269854
|
+
// src/cli/commands/auth/login.ts
|
|
269855
|
+
function getLoginCommand() {
|
|
269856
|
+
return new Base44Command("login", {
|
|
269857
|
+
requireAuth: false,
|
|
269858
|
+
requireAppContext: false
|
|
269859
|
+
}).description("Authenticate with Base44").action(login);
|
|
269860
|
+
}
|
|
269861
|
+
|
|
269862
|
+
// src/cli/commands/auth/logout.ts
|
|
269863
|
+
async function logout(_ctx) {
|
|
269864
|
+
await deleteAuth();
|
|
269865
|
+
return { outroMessage: "Logged out successfully" };
|
|
269866
|
+
}
|
|
269867
|
+
function getLogoutCommand() {
|
|
269868
|
+
return new Base44Command("logout", {
|
|
269869
|
+
requireAuth: false,
|
|
269870
|
+
requireAppContext: false
|
|
269871
|
+
}).description("Logout from current device").action(logout);
|
|
269872
|
+
}
|
|
269873
|
+
|
|
269874
|
+
// src/cli/commands/auth/whoami.ts
|
|
269875
|
+
async function whoami(_ctx) {
|
|
269876
|
+
const workspaceApiKey = getWorkspaceApiKeyFromEnv();
|
|
269877
|
+
if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
|
|
269878
|
+
return {
|
|
269879
|
+
outroMessage: `Using workspace API key: ${theme.styles.bold(workspaceApiKey.slice(0, 10))}`
|
|
269880
|
+
};
|
|
269881
|
+
}
|
|
269882
|
+
const auth = await readAuth();
|
|
269883
|
+
return { outroMessage: `Logged in as: ${theme.styles.bold(auth.email)}` };
|
|
269884
|
+
}
|
|
269885
|
+
function getWhoamiCommand() {
|
|
269886
|
+
return new Base44Command("whoami", { requireAppContext: false }).description("Display current authenticated user").action(whoami);
|
|
269887
|
+
}
|
|
269888
|
+
|
|
269889
|
+
// src/cli/commands/branches/index.ts
|
|
269890
|
+
async function listBranchesAction({
|
|
269891
|
+
log,
|
|
269892
|
+
runTask,
|
|
269893
|
+
jsonMode
|
|
269894
|
+
}) {
|
|
269895
|
+
const remote = await runTask("Fetching branches", () => listBranches());
|
|
269896
|
+
const branches = [
|
|
269897
|
+
{ name: "main", status: "active" },
|
|
269898
|
+
...remote.map((branch) => ({
|
|
269899
|
+
name: branch.branch_name,
|
|
269900
|
+
status: branch.status
|
|
269901
|
+
}))
|
|
269902
|
+
];
|
|
269903
|
+
if (jsonMode)
|
|
269904
|
+
return { stdout: `${JSON.stringify({ branches })}
|
|
269905
|
+
` };
|
|
269906
|
+
for (const branch of branches)
|
|
269907
|
+
log.message(`${branch.name} (${branch.status})`);
|
|
269908
|
+
return { outroMessage: `${branches.length} branches` };
|
|
269909
|
+
}
|
|
269910
|
+
function getBranchesCommand() {
|
|
269911
|
+
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));
|
|
269912
|
+
}
|
|
269913
|
+
|
|
269453
269914
|
// src/core/model.ts
|
|
269454
269915
|
var MODELS = [
|
|
269455
269916
|
{
|
|
@@ -269502,7 +269963,7 @@ async function saveBuilderModel(userId, modelId) {
|
|
|
269502
269963
|
}
|
|
269503
269964
|
var displayName = (id) => MODELS.find((m) => m.id === id)?.name ?? id ?? "Automatic";
|
|
269504
269965
|
|
|
269505
|
-
// src/cli/commands/
|
|
269966
|
+
// src/cli/commands/builder/model.ts
|
|
269506
269967
|
async function modelAction({ log, jsonMode }, input) {
|
|
269507
269968
|
const me = await getMe();
|
|
269508
269969
|
const current = me.builder_model ?? null;
|
|
@@ -269523,7 +269984,7 @@ async function modelAction({ log, jsonMode }, input) {
|
|
|
269523
269984
|
log.message(`${marker} ${active ? theme.styles.bold(m.name) : m.name}${note}`);
|
|
269524
269985
|
}
|
|
269525
269986
|
return {
|
|
269526
|
-
outroMessage: `Current: ${theme.styles.bold(displayName(current))}. Set with \`base44
|
|
269987
|
+
outroMessage: `Current: ${theme.styles.bold(displayName(current))}. Set with \`base44 builder model <name>\`.`
|
|
269527
269988
|
};
|
|
269528
269989
|
}
|
|
269529
269990
|
const pick = resolvePick(input);
|
|
@@ -269544,9 +270005,9 @@ function getModelCommand() {
|
|
|
269544
270005
|
return command;
|
|
269545
270006
|
}
|
|
269546
270007
|
|
|
269547
|
-
// src/cli/commands/
|
|
270008
|
+
// src/cli/commands/builder/shared.ts
|
|
269548
270009
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
269549
|
-
import { basename as basename6, join as
|
|
270010
|
+
import { basename as basename6, join as join25, relative as relative6, resolve as resolve8 } from "node:path";
|
|
269550
270011
|
|
|
269551
270012
|
// src/cli/commands/code/render.ts
|
|
269552
270013
|
var TOOL_ALIASES = {
|
|
@@ -269554,6 +270015,10 @@ var TOOL_ALIASES = {
|
|
|
269554
270015
|
read_repo_file: "read",
|
|
269555
270016
|
write_repo_file: "write",
|
|
269556
270017
|
edit_repo_file: "edit",
|
|
270018
|
+
read_file: "read",
|
|
270019
|
+
write_file: "write",
|
|
270020
|
+
find_replace: "edit",
|
|
270021
|
+
delete_file: "delete",
|
|
269557
270022
|
set_secrets: "secrets",
|
|
269558
270023
|
generate_development_secrets: "secrets",
|
|
269559
270024
|
create_pull_request: "pr",
|
|
@@ -269567,7 +270032,55 @@ var TOOL_ALIASES = {
|
|
|
269567
270032
|
preview_screenshot: "screenshot",
|
|
269568
270033
|
connect_github_account: "github"
|
|
269569
270034
|
};
|
|
269570
|
-
var QUIET_OK_RESULTS = new Set(["read", "write", "edit", "reload"]);
|
|
270035
|
+
var QUIET_OK_RESULTS = new Set(["read", "write", "edit", "reload", "delete"]);
|
|
270036
|
+
var RESULT_LINE_MAX = 110;
|
|
270037
|
+
var DIFF_LINES_MAX = 12;
|
|
270038
|
+
var ERROR_LINES_MAX = 8;
|
|
270039
|
+
function renderEntry(entry, options = {}) {
|
|
270040
|
+
if (typeof entry === "string")
|
|
270041
|
+
return entry;
|
|
270042
|
+
if ("running" in entry)
|
|
270043
|
+
return runningLine(entry.running, entry.startedAt);
|
|
270044
|
+
return eventLine(entry.event, entry.elapsedMs, options) ?? "";
|
|
270045
|
+
}
|
|
270046
|
+
var RUNNING_DOT = "#E86B3C";
|
|
270047
|
+
var PULSE_MS = 500;
|
|
270048
|
+
function runningLine(event, startedAt, now = Date.now()) {
|
|
270049
|
+
const lit = Math.floor(now / PULSE_MS) % 2 === 0;
|
|
270050
|
+
const dot = lit ? source_default.hex(RUNNING_DOT)("●") : source_default.dim("●");
|
|
270051
|
+
const alias = toolAlias(event.name);
|
|
270052
|
+
const title = source_default.bold(event.label || alias);
|
|
270053
|
+
const inline = !event.label && event.summary ? ` ${source_default.dim(event.summary)}` : "";
|
|
270054
|
+
const seconds = Math.round((now - startedAt) / 1000);
|
|
270055
|
+
const took = seconds >= 3 ? ` ${source_default.dim(`· ${seconds}s`)}` : "";
|
|
270056
|
+
const detail = event.label && event.summary ? `
|
|
270057
|
+
${source_default.dim(`└ ${alias}: ${event.summary}`)}` : "";
|
|
270058
|
+
return `${dot} ${title}${inline}${took}${detail}`;
|
|
270059
|
+
}
|
|
270060
|
+
function fold2(lines, max, options) {
|
|
270061
|
+
if (options.verbose || lines.length <= max)
|
|
270062
|
+
return lines;
|
|
270063
|
+
const hint = options.foldHint ? ` (${options.foldHint})` : "";
|
|
270064
|
+
return [
|
|
270065
|
+
...lines.slice(0, max),
|
|
270066
|
+
source_default.dim(`… +${lines.length - max} lines${hint}`)
|
|
270067
|
+
];
|
|
270068
|
+
}
|
|
270069
|
+
var str = (v) => typeof v === "string" ? v : undefined;
|
|
270070
|
+
function diffLines(args) {
|
|
270071
|
+
const find = str(args.find);
|
|
270072
|
+
const replace = str(args.replace);
|
|
270073
|
+
if (find == null && replace == null)
|
|
270074
|
+
return null;
|
|
270075
|
+
return [
|
|
270076
|
+
...(find ?? "").split(`
|
|
270077
|
+
`).map((l) => source_default.red(`- ${l}`)),
|
|
270078
|
+
...(replace ?? "").split(`
|
|
270079
|
+
`).map((l) => source_default.green(`+ ${l}`))
|
|
270080
|
+
];
|
|
270081
|
+
}
|
|
270082
|
+
var indent = (lines) => lines.map((l) => ` ${l}`).join(`
|
|
270083
|
+
`);
|
|
269571
270084
|
function toolAlias(name) {
|
|
269572
270085
|
return TOOL_ALIASES[name] ?? name;
|
|
269573
270086
|
}
|
|
@@ -269632,7 +270145,7 @@ function formatDuration2(ms) {
|
|
|
269632
270145
|
return `${seconds}s`;
|
|
269633
270146
|
return `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
|
|
269634
270147
|
}
|
|
269635
|
-
function eventLine(event, elapsedMs) {
|
|
270148
|
+
function eventLine(event, elapsedMs, options = {}) {
|
|
269636
270149
|
switch (event.kind) {
|
|
269637
270150
|
case "thinking":
|
|
269638
270151
|
return source_default.dim(`✻ ${event.text}`);
|
|
@@ -269653,12 +270166,40 @@ function eventLine(event, elapsedMs) {
|
|
|
269653
270166
|
const paramsLine = event.label && event.summary ? `
|
|
269654
270167
|
${source_default.dim(`${alias}: ${event.summary}`)}` : "";
|
|
269655
270168
|
const head = `${mark} ${title}${inlineDetail}${took}${paramsLine}`;
|
|
270169
|
+
const args = event.args ?? null;
|
|
270170
|
+
if (event.ok && alias === "edit" && args) {
|
|
270171
|
+
const diff = diffLines(args);
|
|
270172
|
+
if (diff)
|
|
270173
|
+
return `${head}
|
|
270174
|
+
${indent(fold2(diff, DIFF_LINES_MAX, options))}`;
|
|
270175
|
+
}
|
|
270176
|
+
if (event.ok && alias === "write" && args && str(args.content) != null) {
|
|
270177
|
+
const lines = str(args.content).split(`
|
|
270178
|
+
`);
|
|
270179
|
+
const count = source_default.dim(`+${lines.length} lines`);
|
|
270180
|
+
if (!options.verbose)
|
|
270181
|
+
return `${head} ${count}`;
|
|
270182
|
+
return `${head} ${count}
|
|
270183
|
+
${indent(lines.map((l) => source_default.dim(l)))}`;
|
|
270184
|
+
}
|
|
269656
270185
|
if (event.ok && (QUIET_OK_RESULTS.has(alias) || !event.result)) {
|
|
269657
270186
|
return head;
|
|
269658
270187
|
}
|
|
269659
|
-
const
|
|
269660
|
-
|
|
269661
|
-
|
|
270188
|
+
const paint = event.ok ? source_default.dim : source_default.red;
|
|
270189
|
+
const lines = event.result.split(`
|
|
270190
|
+
`).map((l) => l.trimEnd()).filter((l, i, all) => l.length > 0 || i > 0 && i < all.length - 1);
|
|
270191
|
+
if (event.ok && !options.verbose) {
|
|
270192
|
+
const first = lines[0] ?? "";
|
|
270193
|
+
const cut = first.length > RESULT_LINE_MAX ? `${first.slice(0, RESULT_LINE_MAX)}…` : first;
|
|
270194
|
+
const more = lines.length - 1;
|
|
270195
|
+
const hint = options.foldHint ? ` (${options.foldHint})` : "";
|
|
270196
|
+
const tail = more > 0 ? source_default.dim(` … +${more} lines${hint}`) : "";
|
|
270197
|
+
return `${head}
|
|
270198
|
+
${paint(linkifyUrls(cut))}${tail}`;
|
|
270199
|
+
}
|
|
270200
|
+
const shown = fold2(lines.map((l) => paint(linkifyUrls(l))), event.ok ? Number.POSITIVE_INFINITY : ERROR_LINES_MAX, options);
|
|
270201
|
+
return `${head}
|
|
270202
|
+
${indent(shown)}`;
|
|
269662
270203
|
}
|
|
269663
270204
|
}
|
|
269664
270205
|
}
|
|
@@ -269764,7 +270305,10 @@ function createTurnStream(interactive, write = (text) => process.stdout.write(te
|
|
|
269764
270305
|
elapsedMs = Date.now() - started;
|
|
269765
270306
|
running.delete(event.id);
|
|
269766
270307
|
}
|
|
269767
|
-
const line = eventLine(event, elapsedMs
|
|
270308
|
+
const line = eventLine(event, elapsedMs, {
|
|
270309
|
+
verbose: options.verbose,
|
|
270310
|
+
foldHint: "--verbose shows everything"
|
|
270311
|
+
});
|
|
269768
270312
|
if (line == null)
|
|
269769
270313
|
return;
|
|
269770
270314
|
if (interactive) {
|
|
@@ -269799,8 +270343,15 @@ var CreatedAppSchema = object({
|
|
|
269799
270343
|
name: string2().nullish(),
|
|
269800
270344
|
imported_repo_url: string2().nullish()
|
|
269801
270345
|
});
|
|
270346
|
+
var WixCreatedAppSchema = object({
|
|
270347
|
+
app_id: string2().min(1),
|
|
270348
|
+
client_creation_id: string2().min(1)
|
|
270349
|
+
});
|
|
269802
270350
|
var AppStateSchema = object({
|
|
269803
270351
|
id: string2(),
|
|
270352
|
+
app_type: string2().nullish(),
|
|
270353
|
+
is_managed_source_code: boolean2().nullish(),
|
|
270354
|
+
imported_repo_url: string2().nullish(),
|
|
269804
270355
|
status: object({
|
|
269805
270356
|
state: string2().nullish(),
|
|
269806
270357
|
message: string2().nullish()
|
|
@@ -269852,6 +270403,23 @@ function parseOrThrow(schema, payload, what) {
|
|
|
269852
270403
|
function branchScope(branchId) {
|
|
269853
270404
|
return branchId ? { branch_id: branchId } : {};
|
|
269854
270405
|
}
|
|
270406
|
+
async function createWixLaunchedApp(options) {
|
|
270407
|
+
let response;
|
|
270408
|
+
try {
|
|
270409
|
+
response = await base44Client.post("api/wix/create-app", {
|
|
270410
|
+
timeout: false,
|
|
270411
|
+
json: {
|
|
270412
|
+
prompt: options.prompt,
|
|
270413
|
+
signed_instance: options.signedInstance,
|
|
270414
|
+
...options.wixClientId ? { wix_client_id: options.wixClientId } : {}
|
|
270415
|
+
}
|
|
270416
|
+
});
|
|
270417
|
+
} catch (error) {
|
|
270418
|
+
throw await ApiError.fromHttpError(error, "creating app via Wix launch");
|
|
270419
|
+
}
|
|
270420
|
+
const created = parseOrThrow(WixCreatedAppSchema, await response.json(), "app");
|
|
270421
|
+
return { id: created.app_id, client_creation_id: created.client_creation_id };
|
|
270422
|
+
}
|
|
269855
270423
|
async function createApp(options) {
|
|
269856
270424
|
let response;
|
|
269857
270425
|
try {
|
|
@@ -269900,7 +270468,9 @@ async function getAppState(appId) {
|
|
|
269900
270468
|
let response;
|
|
269901
270469
|
try {
|
|
269902
270470
|
response = await base44Client.get(`api/apps/${appId}`, {
|
|
269903
|
-
searchParams: {
|
|
270471
|
+
searchParams: {
|
|
270472
|
+
fields: "id,status,app_type,is_managed_source_code,imported_repo_url"
|
|
270473
|
+
}
|
|
269904
270474
|
});
|
|
269905
270475
|
} catch (error) {
|
|
269906
270476
|
throw await ApiError.fromHttpError(error, "reading app status");
|
|
@@ -269961,7 +270531,7 @@ async function getPreviewUrl() {
|
|
|
269961
270531
|
return /^https?:\/\//.test(url) ? url : `https://${url}`;
|
|
269962
270532
|
}
|
|
269963
270533
|
|
|
269964
|
-
// src/cli/commands/
|
|
270534
|
+
// src/cli/commands/builder/shared.ts
|
|
269965
270535
|
var APP_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
269966
270536
|
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(" "));
|
|
269967
270537
|
var FALLBACK_WORDS = [
|
|
@@ -269979,23 +270549,51 @@ function inventAppName(prompt) {
|
|
|
269979
270549
|
function repoBasename(repoUrl) {
|
|
269980
270550
|
return repoUrl.replace(/\/+$/, "").replace(/\.git$/, "").split("/").pop() ?? "app";
|
|
269981
270551
|
}
|
|
270552
|
+
function parseWixLaunchUrl(raw) {
|
|
270553
|
+
let url;
|
|
270554
|
+
try {
|
|
270555
|
+
url = new URL(raw.trim());
|
|
270556
|
+
} catch {
|
|
270557
|
+
throw new InvalidInputError("--wix-launch expects the full launch URL.");
|
|
270558
|
+
}
|
|
270559
|
+
const fragment = new URLSearchParams(url.hash.replace(/^#/, ""));
|
|
270560
|
+
const signedInstance = fragment.get("wix-signed-instance")?.trim();
|
|
270561
|
+
if (!signedInstance) {
|
|
270562
|
+
throw new InvalidInputError("The launch URL has no #wix-signed-instance — copy the whole URL the funnel printed.");
|
|
270563
|
+
}
|
|
270564
|
+
const prompt = url.searchParams.get("prompt")?.trim();
|
|
270565
|
+
const wixClientId = fragment.get("wix-client-id")?.trim();
|
|
270566
|
+
return {
|
|
270567
|
+
...prompt ? { prompt } : {},
|
|
270568
|
+
signedInstance,
|
|
270569
|
+
...wixClientId ? { wixClientId } : {}
|
|
270570
|
+
};
|
|
270571
|
+
}
|
|
269982
270572
|
async function createAndLinkApp(options) {
|
|
269983
270573
|
if (options.name && !APP_NAME_RE.test(options.name)) {
|
|
269984
270574
|
throw new InvalidInputError("The name becomes a directory — letters, digits, dots, dashes and underscores only.");
|
|
269985
270575
|
}
|
|
269986
270576
|
const cwd = process.cwd();
|
|
269987
|
-
const fallbackName = () => options.importRepo ? repoBasename(options.importRepo) : inventAppName(options.prompt);
|
|
269988
|
-
const chosenDir = options.path ?
|
|
270577
|
+
const fallbackName = () => options.importRepo ? repoBasename(options.importRepo) : inventAppName(options.prompt ?? options.wixLaunch?.prompt);
|
|
270578
|
+
const chosenDir = options.path ? resolve8(cwd, options.path) : await isDirEmpty(cwd) ? cwd : undefined;
|
|
269989
270579
|
const dirBase = chosenDir ? basename6(chosenDir) : undefined;
|
|
269990
270580
|
const name = options.name ?? (dirBase && APP_NAME_RE.test(dirBase) ? dirBase : fallbackName());
|
|
269991
|
-
const targetDir = chosenDir ??
|
|
270581
|
+
const targetDir = chosenDir ?? join25(cwd, name);
|
|
269992
270582
|
const here = targetDir === cwd;
|
|
269993
270583
|
const dirName = here ? "." : relative6(cwd, targetDir) || name;
|
|
269994
270584
|
await mkdir3(targetDir, { recursive: true });
|
|
269995
270585
|
if (await appConfigExists(targetDir)) {
|
|
269996
270586
|
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.`);
|
|
269997
270587
|
}
|
|
269998
|
-
|
|
270588
|
+
let clientCreationId;
|
|
270589
|
+
const created = options.wixLaunch ? await createWixLaunchedApp({
|
|
270590
|
+
prompt: options.prompt ?? options.wixLaunch.prompt ?? "",
|
|
270591
|
+
signedInstance: options.wixLaunch.signedInstance,
|
|
270592
|
+
wixClientId: options.wixLaunch.wixClientId
|
|
270593
|
+
}).then((c) => {
|
|
270594
|
+
clientCreationId = c.client_creation_id;
|
|
270595
|
+
return c;
|
|
270596
|
+
}) : options.importRepo ? await createImportedApp({
|
|
269999
270597
|
appName: name,
|
|
270000
270598
|
repoUrl: options.importRepo,
|
|
270001
270599
|
sourceMode: options.mode ?? "direct",
|
|
@@ -270004,9 +270602,9 @@ async function createAndLinkApp(options) {
|
|
|
270004
270602
|
prompt: options.prompt
|
|
270005
270603
|
}) : await createApp({ appName: name, prompt: options.prompt });
|
|
270006
270604
|
await writeAppConfig(targetDir, created.id);
|
|
270007
|
-
await mkdir3(
|
|
270605
|
+
await mkdir3(join25(targetDir, "base44"), { recursive: true });
|
|
270008
270606
|
try {
|
|
270009
|
-
await writeFile2(
|
|
270607
|
+
await writeFile2(join25(targetDir, "base44", "config.jsonc"), `// Base44 project configuration.
|
|
270010
270608
|
{
|
|
270011
270609
|
"name": ${JSON.stringify(name)}
|
|
270012
270610
|
}
|
|
@@ -270019,8 +270617,38 @@ async function createAndLinkApp(options) {
|
|
|
270019
270617
|
repoUrl: created.imported_repo_url ?? undefined,
|
|
270020
270618
|
dirName,
|
|
270021
270619
|
targetDir,
|
|
270022
|
-
here
|
|
270023
|
-
|
|
270620
|
+
here,
|
|
270621
|
+
...clientCreationId ? { clientCreationId } : {}
|
|
270622
|
+
};
|
|
270623
|
+
}
|
|
270624
|
+
function repoLabel(url) {
|
|
270625
|
+
return url.replace(/^https?:\/\//, "").replace(/\.git$/, "").replace(/\/$/, "");
|
|
270626
|
+
}
|
|
270627
|
+
function appTypeChip(state) {
|
|
270628
|
+
switch (state.app_type ?? "user_app") {
|
|
270629
|
+
case "imported_app":
|
|
270630
|
+
return state.imported_repo_url ? repoLabel(state.imported_repo_url) : "repository app";
|
|
270631
|
+
case "user_game":
|
|
270632
|
+
return "game";
|
|
270633
|
+
case "mobile_app":
|
|
270634
|
+
return "mobile app";
|
|
270635
|
+
case "slide":
|
|
270636
|
+
return "slides";
|
|
270637
|
+
case "user_agent":
|
|
270638
|
+
return "superagent";
|
|
270639
|
+
default:
|
|
270640
|
+
return "web app";
|
|
270641
|
+
}
|
|
270642
|
+
}
|
|
270643
|
+
async function assertBuilderApp(appId) {
|
|
270644
|
+
const state = await getAppState(appId);
|
|
270645
|
+
if (state.is_managed_source_code === false) {
|
|
270646
|
+
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.");
|
|
270647
|
+
}
|
|
270648
|
+
if (state.app_type === "user_agent") {
|
|
270649
|
+
throw new InvalidInputError("This is a Superagent, not an app the builder works on. Superagent has no CLI surface yet.");
|
|
270650
|
+
}
|
|
270651
|
+
return state;
|
|
270024
270652
|
}
|
|
270025
270653
|
function nextStepsLines(app) {
|
|
270026
270654
|
const cd = app.here ? "" : `cd ${app.dirName} && `;
|
|
@@ -270028,7 +270656,7 @@ function nextStepsLines(app) {
|
|
|
270028
270656
|
"",
|
|
270029
270657
|
`${source_default.bold("Your app lives in")} ${app.here ? "./ (this directory)" : `./${app.dirName}`}`,
|
|
270030
270658
|
source_default.dim(` ${cd}base44 code # keep building with the agent`),
|
|
270031
|
-
source_default.dim(` ${cd}base44
|
|
270659
|
+
source_default.dim(` ${cd}base44 builder send "…" # one non-interactive turn`),
|
|
270032
270660
|
source_default.dim(` files live remotely — ${cd}base44 sandbox ls to look, base44 eject for a copy`)
|
|
270033
270661
|
];
|
|
270034
270662
|
}
|
|
@@ -270052,12 +270680,27 @@ function newStreamState() {
|
|
|
270052
270680
|
return { perMessage: new Map };
|
|
270053
270681
|
}
|
|
270054
270682
|
var TOOL_SETTLED = new Set(["success", "error", "stopped"]);
|
|
270683
|
+
function fullText(value) {
|
|
270684
|
+
if (typeof value === "string")
|
|
270685
|
+
return value.trimEnd();
|
|
270686
|
+
if (value == null)
|
|
270687
|
+
return "";
|
|
270688
|
+
return JSON.stringify(value);
|
|
270689
|
+
}
|
|
270690
|
+
function toolArgs(argumentsString) {
|
|
270691
|
+
try {
|
|
270692
|
+
const parsed = JSON.parse(argumentsString ?? "");
|
|
270693
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
270694
|
+
} catch {
|
|
270695
|
+
return null;
|
|
270696
|
+
}
|
|
270697
|
+
}
|
|
270055
270698
|
function oneLine(value, max) {
|
|
270056
270699
|
const text = typeof value === "string" ? value : value == null ? "" : JSON.stringify(value);
|
|
270057
270700
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
270058
270701
|
return flat.length > max ? `${flat.slice(0, max)}…` : flat;
|
|
270059
270702
|
}
|
|
270060
|
-
var SALIENT_KEYS = ["command", "path", "file_path", "title"];
|
|
270703
|
+
var SALIENT_KEYS = ["command", "path", "file_path", "title", "find"];
|
|
270061
270704
|
function salvageKey(raw, keys) {
|
|
270062
270705
|
for (const key of keys) {
|
|
270063
270706
|
const match = raw.match(new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`));
|
|
@@ -270086,6 +270729,10 @@ function toolMeta(name, argumentsString) {
|
|
|
270086
270729
|
read_repo_file: pick("path") ?? pick("file_path"),
|
|
270087
270730
|
write_repo_file: pick("path") ?? pick("file_path"),
|
|
270088
270731
|
edit_repo_file: pick("path") ?? pick("file_path"),
|
|
270732
|
+
read_file: pick("file_path") ?? pick("path"),
|
|
270733
|
+
write_file: pick("file_path") ?? pick("path"),
|
|
270734
|
+
find_replace: pick("file_path") ?? pick("path"),
|
|
270735
|
+
delete_file: pick("file_path") ?? pick("path"),
|
|
270089
270736
|
create_pull_request: pick("title"),
|
|
270090
270737
|
reload_preview: ""
|
|
270091
270738
|
};
|
|
@@ -270136,7 +270783,10 @@ function diffConversation(state, messages) {
|
|
|
270136
270783
|
}
|
|
270137
270784
|
for (const tool of message.tool_calls ?? []) {
|
|
270138
270785
|
if (!progress.announcedTools.has(tool.id)) {
|
|
270139
|
-
progress.announcedTools.set(tool.id,
|
|
270786
|
+
progress.announcedTools.set(tool.id, {
|
|
270787
|
+
...toolMeta(tool.name, tool.arguments_string),
|
|
270788
|
+
args: toolArgs(tool.arguments_string)
|
|
270789
|
+
});
|
|
270140
270790
|
const meta = progress.announcedTools.get(tool.id);
|
|
270141
270791
|
events.push({
|
|
270142
270792
|
kind: "tool_start",
|
|
@@ -270167,7 +270817,8 @@ function diffConversation(state, messages) {
|
|
|
270167
270817
|
label: labelTense(meta.label, "done"),
|
|
270168
270818
|
summary: meta.summary,
|
|
270169
270819
|
ok: status === "success",
|
|
270170
|
-
result:
|
|
270820
|
+
result: fullText(tool.results),
|
|
270821
|
+
args: meta.args
|
|
270171
270822
|
});
|
|
270172
270823
|
}
|
|
270173
270824
|
}
|
|
@@ -270241,9 +270892,90 @@ async function streamConversationUntilSettled(onEvent, options = {}) {
|
|
|
270241
270892
|
return "timeout";
|
|
270242
270893
|
}
|
|
270243
270894
|
|
|
270244
|
-
// src/cli/commands/
|
|
270895
|
+
// src/cli/commands/builder/send.ts
|
|
270896
|
+
function lastAssistantReply(turn) {
|
|
270897
|
+
const messages = turn.conversation?.messages ?? [];
|
|
270898
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
270899
|
+
const { role, content } = messages[i];
|
|
270900
|
+
if (role === "assistant" && typeof content === "string" && content.trim()) {
|
|
270901
|
+
return content.trim();
|
|
270902
|
+
}
|
|
270903
|
+
}
|
|
270904
|
+
return;
|
|
270905
|
+
}
|
|
270906
|
+
function ndjsonWriter() {
|
|
270907
|
+
return (record) => process.stdout.write(`${JSON.stringify(record)}
|
|
270908
|
+
`);
|
|
270909
|
+
}
|
|
270910
|
+
async function sendAction(ctx, message, options) {
|
|
270911
|
+
if (options.streamJson && ctx.jsonMode) {
|
|
270912
|
+
throw new InvalidInputError("--stream-json and --json are exclusive.");
|
|
270913
|
+
}
|
|
270914
|
+
if (ctx.app)
|
|
270915
|
+
await assertBuilderApp(ctx.app.id);
|
|
270916
|
+
const branchId = await resolveBranchId(ctx);
|
|
270917
|
+
if (options.streamJson) {
|
|
270918
|
+
const write = ndjsonWriter();
|
|
270919
|
+
const turn = await streamConversationDuring(() => sendTurn(message, branchId), ({ kind, ...event }) => write({ type: kind, ...event }), { branchId });
|
|
270920
|
+
write({
|
|
270921
|
+
type: "result",
|
|
270922
|
+
queued: turn.queued === true,
|
|
270923
|
+
status: turn.status?.state ?? "ready",
|
|
270924
|
+
error_source: turn.status?.error_source ?? null,
|
|
270925
|
+
reply: lastAssistantReply(turn) ?? null
|
|
270926
|
+
});
|
|
270927
|
+
return {};
|
|
270928
|
+
}
|
|
270929
|
+
if (ctx.jsonMode) {
|
|
270930
|
+
const turn = await ctx.runTask("Agent working (a turn can take minutes)", () => sendTurn(message, branchId));
|
|
270931
|
+
if (turn.queued)
|
|
270932
|
+
return { stdout: `${JSON.stringify({ queued: true })}
|
|
270933
|
+
` };
|
|
270934
|
+
return {
|
|
270935
|
+
stdout: `${JSON.stringify({
|
|
270936
|
+
status: turn.status?.state ?? "ready",
|
|
270937
|
+
error_source: turn.status?.error_source ?? null,
|
|
270938
|
+
reply: lastAssistantReply(turn) ?? null
|
|
270939
|
+
})}
|
|
270940
|
+
`
|
|
270941
|
+
};
|
|
270942
|
+
}
|
|
270943
|
+
const stream = createTurnStream(process.stdout.isTTY === true, undefined, {
|
|
270944
|
+
verbose: options.verbose
|
|
270945
|
+
});
|
|
270946
|
+
let turn;
|
|
270947
|
+
try {
|
|
270948
|
+
turn = await streamConversationDuring(() => sendTurn(message, branchId), stream.onEvent, { branchId });
|
|
270949
|
+
} finally {
|
|
270950
|
+
stream.stop();
|
|
270951
|
+
}
|
|
270952
|
+
if (turn.queued) {
|
|
270953
|
+
return {
|
|
270954
|
+
outroMessage: "The agent is busy with an earlier message — yours was queued and runs next."
|
|
270955
|
+
};
|
|
270956
|
+
}
|
|
270957
|
+
if (turn.status?.state === "error") {
|
|
270958
|
+
return {
|
|
270959
|
+
outroMessage: `Turn failed (${turn.status.error_source ?? "unknown"}) — see the editor for details.`
|
|
270960
|
+
};
|
|
270961
|
+
}
|
|
270962
|
+
return { outroMessage: "Turn finished." };
|
|
270963
|
+
}
|
|
270964
|
+
function getSendCommand() {
|
|
270965
|
+
const command = new Base44Command("send", { supportsBranch: true });
|
|
270966
|
+
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);
|
|
270967
|
+
return command;
|
|
270968
|
+
}
|
|
270969
|
+
|
|
270970
|
+
// src/cli/commands/builder/new.ts
|
|
270245
270971
|
var POLL_TIMEOUT_MS = 20 * 60000;
|
|
270246
270972
|
var MODES = ["direct", "fork", "copy"];
|
|
270973
|
+
async function readStdin2() {
|
|
270974
|
+
const chunks = [];
|
|
270975
|
+
for await (const chunk of process.stdin)
|
|
270976
|
+
chunks.push(chunk);
|
|
270977
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
270978
|
+
}
|
|
270247
270979
|
async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
270248
270980
|
if (options.mode && !MODES.includes(options.mode)) {
|
|
270249
270981
|
throw new InvalidInputError("--mode must be direct, fork, or copy.");
|
|
@@ -270251,9 +270983,20 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270251
270983
|
if ((options.mode || options.repoName || options.fromBranch) && !options.import) {
|
|
270252
270984
|
throw new InvalidInputError("--mode, --repo-name and --from-branch apply only with --import <repo>.");
|
|
270253
270985
|
}
|
|
270254
|
-
if (
|
|
270255
|
-
throw new InvalidInputError(
|
|
270986
|
+
if (options.wixLaunch && options.import) {
|
|
270987
|
+
throw new InvalidInputError("--wix-launch and --import are exclusive.");
|
|
270988
|
+
}
|
|
270989
|
+
const wixLaunch = options.wixLaunch ? parseWixLaunchUrl(options.wixLaunch === "-" ? await readStdin2() : options.wixLaunch) : undefined;
|
|
270990
|
+
if (wixLaunch && !prompt && !wixLaunch.prompt) {
|
|
270991
|
+
throw new InvalidInputError("The launch URL has no ?prompt= — pass the prompt as the argument.");
|
|
270256
270992
|
}
|
|
270993
|
+
if (!prompt && !options.import && !wixLaunch) {
|
|
270994
|
+
throw new InvalidInputError('Describe the app ("<prompt>") or pass --import <repo> / --wix-launch <url>.');
|
|
270995
|
+
}
|
|
270996
|
+
if (options.streamJson && jsonMode) {
|
|
270997
|
+
throw new InvalidInputError("--stream-json and --json are exclusive.");
|
|
270998
|
+
}
|
|
270999
|
+
const ndjson = options.streamJson ? ndjsonWriter() : null;
|
|
270257
271000
|
let app;
|
|
270258
271001
|
try {
|
|
270259
271002
|
app = await runTask(options.import ? "Importing the repository" : "Creating your app", () => createAndLinkApp({
|
|
@@ -270263,16 +271006,30 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270263
271006
|
mode: options.mode,
|
|
270264
271007
|
repoName: options.repoName,
|
|
270265
271008
|
fromBranch: options.fromBranch,
|
|
270266
|
-
path: options.path
|
|
271009
|
+
path: options.path,
|
|
271010
|
+
wixLaunch
|
|
270267
271011
|
}));
|
|
270268
271012
|
} catch (error) {
|
|
270269
271013
|
for (const line of await githubReauthLines(error) ?? [])
|
|
270270
271014
|
log.message(line);
|
|
270271
271015
|
throw error;
|
|
270272
271016
|
}
|
|
270273
|
-
if (
|
|
271017
|
+
if (ndjson) {
|
|
271018
|
+
ndjson({
|
|
271019
|
+
type: "created",
|
|
271020
|
+
id: app.id,
|
|
271021
|
+
repo_url: app.repoUrl ?? null,
|
|
271022
|
+
editor_url: app.editorUrl,
|
|
271023
|
+
dir: app.dirName,
|
|
271024
|
+
path: app.targetDir,
|
|
271025
|
+
...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
|
|
271026
|
+
});
|
|
271027
|
+
} else if (!jsonMode) {
|
|
270274
271028
|
if (app.repoUrl)
|
|
270275
271029
|
log.message(source_default.dim(`repo ${app.repoUrl}`));
|
|
271030
|
+
if (app.clientCreationId) {
|
|
271031
|
+
log.message(source_default.dim("wix launched — connector connected before the first turn"));
|
|
271032
|
+
}
|
|
270276
271033
|
log.message(source_default.dim(`editor ${app.editorUrl}`));
|
|
270277
271034
|
log.message(source_default.dim(`linked ${app.here ? "./ (this directory)" : `./${app.dirName}`}`));
|
|
270278
271035
|
}
|
|
@@ -270283,10 +271040,16 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270283
271040
|
const branchId = await resolveActiveBranchId().catch(() => {
|
|
270284
271041
|
return;
|
|
270285
271042
|
});
|
|
270286
|
-
const stream = createTurnStream(process.stdout.isTTY === true && !jsonMode, undefined, {
|
|
271043
|
+
const stream = createTurnStream(process.stdout.isTTY === true && !jsonMode && !ndjson, undefined, {
|
|
271044
|
+
idleLabel: "provisioning the sandbox and starting the build",
|
|
271045
|
+
verbose: options.verbose
|
|
271046
|
+
});
|
|
270287
271047
|
try {
|
|
270288
271048
|
const settled = await streamConversationUntilSettled((event) => {
|
|
270289
|
-
if (
|
|
271049
|
+
if (ndjson) {
|
|
271050
|
+
const { kind, ...rest } = event;
|
|
271051
|
+
ndjson({ type: kind, ...rest });
|
|
271052
|
+
} else if (!jsonMode)
|
|
270290
271053
|
stream.onEvent(event);
|
|
270291
271054
|
}, { branchId, timeoutMs: POLL_TIMEOUT_MS });
|
|
270292
271055
|
finalState = settled === "timeout" ? "processing" : (await getAppState(app.id)).status?.state ?? "ready";
|
|
@@ -270299,6 +271062,15 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270299
271062
|
stream.stop();
|
|
270300
271063
|
}
|
|
270301
271064
|
}
|
|
271065
|
+
if (ndjson) {
|
|
271066
|
+
ndjson({
|
|
271067
|
+
type: "result",
|
|
271068
|
+
id: app.id,
|
|
271069
|
+
preview_url: previewUrl ?? null,
|
|
271070
|
+
status: finalState ?? "created"
|
|
271071
|
+
});
|
|
271072
|
+
return {};
|
|
271073
|
+
}
|
|
270302
271074
|
if (jsonMode) {
|
|
270303
271075
|
return {
|
|
270304
271076
|
stdout: `${JSON.stringify({
|
|
@@ -270308,7 +271080,8 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270308
271080
|
preview_url: previewUrl ?? null,
|
|
270309
271081
|
dir: app.dirName,
|
|
270310
271082
|
path: app.targetDir,
|
|
270311
|
-
status: finalState ?? "created"
|
|
271083
|
+
status: finalState ?? "created",
|
|
271084
|
+
...app.clientCreationId ? { client_creation_id: app.clientCreationId } : {}
|
|
270312
271085
|
})}
|
|
270313
271086
|
`
|
|
270314
271087
|
};
|
|
@@ -270324,7 +271097,7 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270324
271097
|
}
|
|
270325
271098
|
if (finalState === "processing") {
|
|
270326
271099
|
return {
|
|
270327
|
-
outroMessage: `Still building — follow it with \`base44
|
|
271100
|
+
outroMessage: `Still building — follow it with \`base44 builder status\`.`
|
|
270328
271101
|
};
|
|
270329
271102
|
}
|
|
270330
271103
|
return {
|
|
@@ -270333,78 +271106,11 @@ async function newAction({ log, runTask, jsonMode }, prompt, options) {
|
|
|
270333
271106
|
}
|
|
270334
271107
|
function getNewCommand() {
|
|
270335
271108
|
const command = new Base44Command("new", { requireAppContext: false });
|
|
270336
|
-
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").action(newAction);
|
|
270337
|
-
return command;
|
|
270338
|
-
}
|
|
270339
|
-
|
|
270340
|
-
// src/cli/commands/app/preview.ts
|
|
270341
|
-
async function previewAction(ctx) {
|
|
270342
|
-
const url = await ctx.runTask("Resolving preview URL (boots the sandbox if needed)", () => getPreviewUrl());
|
|
270343
|
-
if (ctx.jsonMode)
|
|
270344
|
-
return { stdout: `${JSON.stringify({ preview_url: url })}
|
|
270345
|
-
` };
|
|
270346
|
-
ctx.log.message(url);
|
|
270347
|
-
return { outroMessage: "Preview is live." };
|
|
270348
|
-
}
|
|
270349
|
-
function getPreviewCommand() {
|
|
270350
|
-
const command = new Base44Command("preview");
|
|
270351
|
-
command.description("Print the app's live preview URL").action(previewAction);
|
|
271109
|
+
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-launch <url>", `Create through a Wix launch URL (the funnel's ?prompt= and #wix-signed-instance); "-" reads it from stdin. A prompt argument is what the agent runs instead of the URL's`).env("BASE44_WIX_LAUNCH_URL")).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);
|
|
270352
271110
|
return command;
|
|
270353
271111
|
}
|
|
270354
271112
|
|
|
270355
|
-
// src/cli/commands/
|
|
270356
|
-
function lastAssistantReply(turn) {
|
|
270357
|
-
const messages = turn.conversation?.messages ?? [];
|
|
270358
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
270359
|
-
const { role, content } = messages[i];
|
|
270360
|
-
if (role === "assistant" && typeof content === "string" && content.trim()) {
|
|
270361
|
-
return content.trim();
|
|
270362
|
-
}
|
|
270363
|
-
}
|
|
270364
|
-
return;
|
|
270365
|
-
}
|
|
270366
|
-
async function sendAction(ctx, message) {
|
|
270367
|
-
const branchId = await resolveBranchId(ctx);
|
|
270368
|
-
if (ctx.jsonMode) {
|
|
270369
|
-
const turn = await ctx.runTask("Agent working (a turn can take minutes)", () => sendTurn(message, branchId));
|
|
270370
|
-
if (turn.queued)
|
|
270371
|
-
return { stdout: `${JSON.stringify({ queued: true })}
|
|
270372
|
-
` };
|
|
270373
|
-
return {
|
|
270374
|
-
stdout: `${JSON.stringify({
|
|
270375
|
-
status: turn.status?.state ?? "ready",
|
|
270376
|
-
error_source: turn.status?.error_source ?? null,
|
|
270377
|
-
reply: lastAssistantReply(turn) ?? null
|
|
270378
|
-
})}
|
|
270379
|
-
`
|
|
270380
|
-
};
|
|
270381
|
-
}
|
|
270382
|
-
const stream = createTurnStream(process.stdout.isTTY === true);
|
|
270383
|
-
let turn;
|
|
270384
|
-
try {
|
|
270385
|
-
turn = await streamConversationDuring(() => sendTurn(message, branchId), stream.onEvent, { branchId });
|
|
270386
|
-
} finally {
|
|
270387
|
-
stream.stop();
|
|
270388
|
-
}
|
|
270389
|
-
if (turn.queued) {
|
|
270390
|
-
return {
|
|
270391
|
-
outroMessage: "The agent is busy with an earlier message — yours was queued and runs next."
|
|
270392
|
-
};
|
|
270393
|
-
}
|
|
270394
|
-
if (turn.status?.state === "error") {
|
|
270395
|
-
return {
|
|
270396
|
-
outroMessage: `Turn failed (${turn.status.error_source ?? "unknown"}) — see the editor for details.`
|
|
270397
|
-
};
|
|
270398
|
-
}
|
|
270399
|
-
return { outroMessage: "Turn finished." };
|
|
270400
|
-
}
|
|
270401
|
-
function getSendCommand() {
|
|
270402
|
-
const command = new Base44Command("send", { supportsBranch: true });
|
|
270403
|
-
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);
|
|
270404
|
-
return command;
|
|
270405
|
-
}
|
|
270406
|
-
|
|
270407
|
-
// src/cli/commands/app/status.ts
|
|
271113
|
+
// src/cli/commands/builder/status.ts
|
|
270408
271114
|
async function statusAction(ctx) {
|
|
270409
271115
|
const id = ctx.app?.id;
|
|
270410
271116
|
const app = await ctx.runTask("Reading app status", () => getAppState(id));
|
|
@@ -270426,7 +271132,7 @@ function getStatusCommand() {
|
|
|
270426
271132
|
return command;
|
|
270427
271133
|
}
|
|
270428
271134
|
|
|
270429
|
-
// src/cli/commands/
|
|
271135
|
+
// src/cli/commands/builder/stop.ts
|
|
270430
271136
|
async function stopAction(ctx) {
|
|
270431
271137
|
const branchId = await resolveBranchId(ctx);
|
|
270432
271138
|
await ctx.runTask("Stopping the running turn", () => stopTurn(branchId));
|
|
@@ -270441,470 +271147,9 @@ function getStopCommand() {
|
|
|
270441
271147
|
return command;
|
|
270442
271148
|
}
|
|
270443
271149
|
|
|
270444
|
-
// src/cli/commands/
|
|
270445
|
-
function
|
|
270446
|
-
return new Command2("
|
|
270447
|
-
}
|
|
270448
|
-
|
|
270449
|
-
// src/cli/commands/auth/password-login.ts
|
|
270450
|
-
import { dirname as dirname15, join as join22 } from "node:path";
|
|
270451
|
-
async function passwordLoginAction({ log, runTask }, action) {
|
|
270452
|
-
const shouldEnable = action === "enable";
|
|
270453
|
-
const { project } = await readProjectConfig();
|
|
270454
|
-
const configDir = dirname15(project.configPath);
|
|
270455
|
-
const authDir = join22(configDir, project.authDir);
|
|
270456
|
-
const updated = await runTask("Updating local auth config", async () => {
|
|
270457
|
-
const current = await readAuthConfig(authDir) ?? DEFAULT_AUTH_CONFIG;
|
|
270458
|
-
const merged = { ...current, enableUsernamePassword: shouldEnable };
|
|
270459
|
-
await writeAuthConfig(authDir, merged);
|
|
270460
|
-
return merged;
|
|
270461
|
-
});
|
|
270462
|
-
if (!shouldEnable && !hasAnyLoginMethod(updated)) {
|
|
270463
|
-
log.warn("Disabling password auth will leave no login methods enabled. Users will be locked out.");
|
|
270464
|
-
}
|
|
270465
|
-
const newStatus = shouldEnable ? "enabled" : "disabled";
|
|
270466
|
-
return {
|
|
270467
|
-
outroMessage: `Username & password authentication ${newStatus} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
|
|
270468
|
-
};
|
|
270469
|
-
}
|
|
270470
|
-
function getPasswordLoginCommand() {
|
|
270471
|
-
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);
|
|
270472
|
-
}
|
|
270473
|
-
|
|
270474
|
-
// src/cli/commands/auth/pull.ts
|
|
270475
|
-
import { dirname as dirname16, join as join23 } from "node:path";
|
|
270476
|
-
async function pullAuthAction({
|
|
270477
|
-
log,
|
|
270478
|
-
runTask
|
|
270479
|
-
}) {
|
|
270480
|
-
const { project } = await readProjectConfig();
|
|
270481
|
-
const configDir = dirname16(project.configPath);
|
|
270482
|
-
const authDir = join23(configDir, project.authDir);
|
|
270483
|
-
const remoteConfig = await runTask("Fetching auth config from Base44", async () => {
|
|
270484
|
-
return await pullAuthConfig();
|
|
270485
|
-
}, {
|
|
270486
|
-
successMessage: "Auth config fetched successfully",
|
|
270487
|
-
errorMessage: "Failed to fetch auth config"
|
|
270488
|
-
});
|
|
270489
|
-
const { written } = await runTask("Syncing auth config file", async () => {
|
|
270490
|
-
return await writeAuthConfig(authDir, remoteConfig);
|
|
270491
|
-
}, {
|
|
270492
|
-
successMessage: "Auth config file synced successfully",
|
|
270493
|
-
errorMessage: "Failed to sync auth config file"
|
|
270494
|
-
});
|
|
270495
|
-
if (written) {
|
|
270496
|
-
log.success("Auth config written to local file");
|
|
270497
|
-
} else {
|
|
270498
|
-
log.info("Auth config is already up to date");
|
|
270499
|
-
}
|
|
270500
|
-
return {
|
|
270501
|
-
outroMessage: `Pulled auth config to ${authDir} (overwrites local file)`
|
|
270502
|
-
};
|
|
270503
|
-
}
|
|
270504
|
-
function getAuthPullCommand() {
|
|
270505
|
-
return new Base44Command("pull").description("Pull auth config from Base44 to local file").action(pullAuthAction);
|
|
270506
|
-
}
|
|
270507
|
-
|
|
270508
|
-
// src/cli/commands/auth/push.ts
|
|
270509
|
-
async function pushAuthAction({ isNonInteractive, log, runTask }, options) {
|
|
270510
|
-
const { authConfig } = await readProjectConfig();
|
|
270511
|
-
if (authConfig.length === 0) {
|
|
270512
|
-
log.info("No local auth config found");
|
|
270513
|
-
return {
|
|
270514
|
-
outroMessage: "No auth config to push. Run `base44 auth pull` to fetch the remote config first."
|
|
270515
|
-
};
|
|
270516
|
-
}
|
|
270517
|
-
if (!hasAnyLoginMethod(authConfig[0])) {
|
|
270518
|
-
log.warn("This config has no login methods enabled. Pushing it will lock out all users.");
|
|
270519
|
-
}
|
|
270520
|
-
if (!options.yes) {
|
|
270521
|
-
if (isNonInteractive) {
|
|
270522
|
-
throw new InvalidInputError("--yes is required in non-interactive mode");
|
|
270523
|
-
}
|
|
270524
|
-
const shouldPush = await Re({
|
|
270525
|
-
message: "Push auth config to Base44?"
|
|
270526
|
-
});
|
|
270527
|
-
if (Ct(shouldPush) || !shouldPush) {
|
|
270528
|
-
return { outroMessage: "Push cancelled" };
|
|
270529
|
-
}
|
|
270530
|
-
}
|
|
270531
|
-
await runTask("Pushing auth config to Base44", async () => {
|
|
270532
|
-
return await pushAuthConfig(authConfig[0] ?? null);
|
|
270533
|
-
}, {
|
|
270534
|
-
successMessage: "Auth config pushed successfully",
|
|
270535
|
-
errorMessage: "Failed to push auth config"
|
|
270536
|
-
});
|
|
270537
|
-
return {
|
|
270538
|
-
outroMessage: "Auth config pushed to Base44"
|
|
270539
|
-
};
|
|
270540
|
-
}
|
|
270541
|
-
function getAuthPushCommand() {
|
|
270542
|
-
return new Base44Command("push").description("Push local auth config to Base44").option("-y, --yes", "Skip confirmation prompt").action(pushAuthAction);
|
|
270543
|
-
}
|
|
270544
|
-
|
|
270545
|
-
// src/cli/commands/auth/social-login.ts
|
|
270546
|
-
import { dirname as dirname17, join as join24, resolve as resolve7 } from "node:path";
|
|
270547
|
-
var PROVIDER_LABELS = {
|
|
270548
|
-
google: "Google",
|
|
270549
|
-
microsoft: "Microsoft",
|
|
270550
|
-
facebook: "Facebook",
|
|
270551
|
-
apple: "Apple"
|
|
270552
|
-
};
|
|
270553
|
-
var VALID_PROVIDER_NAMES = Object.keys(SOCIAL_PROVIDERS);
|
|
270554
|
-
var PROVIDER_OAUTH_CLI = {
|
|
270555
|
-
google: {
|
|
270556
|
-
envVar: "google_oauth_client_secret",
|
|
270557
|
-
promptMessage: "Enter Google OAuth client secret"
|
|
270558
|
-
}
|
|
270559
|
-
};
|
|
270560
|
-
function hasSecretOptions(options) {
|
|
270561
|
-
return Boolean(options.clientSecret || options.clientSecretStdin || options.envFile);
|
|
270562
|
-
}
|
|
270563
|
-
function hasCustomOAuthOptions(options) {
|
|
270564
|
-
return Boolean(options.clientId || hasSecretOptions(options));
|
|
270565
|
-
}
|
|
270566
|
-
async function socialLoginAction({ log, isNonInteractive, runTask }, provider, action, options) {
|
|
270567
|
-
const shouldEnable = action === "enable";
|
|
270568
|
-
const providerInfo = SOCIAL_PROVIDERS[provider];
|
|
270569
|
-
const label = PROVIDER_LABELS[provider];
|
|
270570
|
-
const hasOAuthOptions = hasCustomOAuthOptions(options);
|
|
270571
|
-
if (hasOAuthOptions && !providerInfo.customOAuth) {
|
|
270572
|
-
throw new InvalidInputError(`Custom OAuth options are only supported for providers with custom OAuth (e.g., google). Use: base44 auth social-login ${provider} ${action}`);
|
|
270573
|
-
}
|
|
270574
|
-
if (hasOAuthOptions && !shouldEnable) {
|
|
270575
|
-
throw new InvalidInputError(`Custom OAuth options cannot be used with disable. To disable ${label} login: base44 auth social-login ${provider} disable`);
|
|
270576
|
-
}
|
|
270577
|
-
if (hasSecretOptions(options) && !options.clientId) {
|
|
270578
|
-
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>`);
|
|
270579
|
-
}
|
|
270580
|
-
const oauth = providerInfo.customOAuth;
|
|
270581
|
-
const oauthCli = PROVIDER_OAUTH_CLI[provider];
|
|
270582
|
-
const useCustomOAuth = shouldEnable && hasOAuthOptions && oauth != null;
|
|
270583
|
-
let clientSecret;
|
|
270584
|
-
if (useCustomOAuth && oauth && oauthCli && hasSecretOptions(options)) {
|
|
270585
|
-
if (options.envFile) {
|
|
270586
|
-
const secrets = await parseEnvFile(resolve7(options.envFile));
|
|
270587
|
-
const value = secrets[oauthCli.envVar];
|
|
270588
|
-
if (!value) {
|
|
270589
|
-
throw new InvalidInputError(`Key "${oauthCli.envVar}" not found in ${options.envFile}.`);
|
|
270590
|
-
}
|
|
270591
|
-
clientSecret = value;
|
|
270592
|
-
} else {
|
|
270593
|
-
clientSecret = await resolveSecret({
|
|
270594
|
-
flagValue: options.clientSecret,
|
|
270595
|
-
fromStdin: options.clientSecretStdin,
|
|
270596
|
-
envVar: oauthCli.envVar,
|
|
270597
|
-
promptMessage: oauthCli.promptMessage,
|
|
270598
|
-
isNonInteractive,
|
|
270599
|
-
name: "client secret",
|
|
270600
|
-
hints: [
|
|
270601
|
-
{
|
|
270602
|
-
message: `Provide via flag: base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`,
|
|
270603
|
-
command: `base44 auth social-login ${provider} enable --client-id <id> --client-secret <secret>`
|
|
270604
|
-
},
|
|
270605
|
-
{
|
|
270606
|
-
message: `Provide via stdin: echo <secret> | base44 auth social-login ${provider} enable --client-id <id> --client-secret-stdin`
|
|
270607
|
-
},
|
|
270608
|
-
{
|
|
270609
|
-
message: `Provide via env: ${oauthCli.envVar}=<secret> base44 auth social-login ${provider} enable --client-id <id>`
|
|
270610
|
-
}
|
|
270611
|
-
]
|
|
270612
|
-
});
|
|
270613
|
-
}
|
|
270614
|
-
}
|
|
270615
|
-
const { project } = await readProjectConfig();
|
|
270616
|
-
const configDir = dirname17(project.configPath);
|
|
270617
|
-
const authDir = join24(configDir, project.authDir);
|
|
270618
|
-
const { config: updated } = await runTask("Updating local auth config", async () => updateSocialLoginConfig(authDir, provider, shouldEnable, useCustomOAuth && options.clientId ? { clientId: options.clientId } : undefined));
|
|
270619
|
-
if (clientSecret) {
|
|
270620
|
-
await runTask("Saving client secret", async () => pushCustomOAuthSecret(provider, clientSecret));
|
|
270621
|
-
}
|
|
270622
|
-
if (!shouldEnable && !hasAnyLoginMethod(updated)) {
|
|
270623
|
-
log.warn(`Disabling ${label} login will leave no login methods enabled. Users will be locked out.`);
|
|
270624
|
-
}
|
|
270625
|
-
const newStatus = shouldEnable ? "enabled" : "disabled";
|
|
270626
|
-
const oauthNote = useCustomOAuth ? " with custom OAuth" : "";
|
|
270627
|
-
let outroMessage = `${label} login ${newStatus}${oauthNote} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`;
|
|
270628
|
-
if (useCustomOAuth && !clientSecret) {
|
|
270629
|
-
outroMessage += `
|
|
270630
|
-
Remember to push the client secret separately: base44 secrets set --env-file <path>`;
|
|
270631
|
-
}
|
|
270632
|
-
return { outroMessage };
|
|
270633
|
-
}
|
|
270634
|
-
function getSocialLoginCommand() {
|
|
270635
|
-
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([
|
|
270636
|
-
"enable",
|
|
270637
|
-
"disable"
|
|
270638
|
-
])).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);
|
|
270639
|
-
}
|
|
270640
|
-
|
|
270641
|
-
// src/cli/commands/auth/sso.ts
|
|
270642
|
-
import { dirname as dirname18, join as join25, resolve as resolve8 } from "node:path";
|
|
270643
|
-
var SSOConfigFileSchema = object({
|
|
270644
|
-
provider: _enum(Object.values(KNOWN_SSO_PROVIDERS)),
|
|
270645
|
-
clientId: string2(),
|
|
270646
|
-
clientSecret: string2(),
|
|
270647
|
-
scope: string2().optional(),
|
|
270648
|
-
discoveryUrl: string2().optional(),
|
|
270649
|
-
tenantId: string2().optional(),
|
|
270650
|
-
oktaDomain: string2().optional(),
|
|
270651
|
-
authEndpoint: string2().optional(),
|
|
270652
|
-
tokenEndpoint: string2().optional(),
|
|
270653
|
-
userinfoEndpoint: string2().optional(),
|
|
270654
|
-
jwksUri: string2().optional(),
|
|
270655
|
-
ssoName: string2().optional()
|
|
270656
|
-
});
|
|
270657
|
-
async function loadSSOConfigFile(filePath) {
|
|
270658
|
-
const resolved = resolve8(filePath);
|
|
270659
|
-
const raw = await readJsonFile(resolved);
|
|
270660
|
-
const result = SSOConfigFileSchema.safeParse(raw);
|
|
270661
|
-
if (!result.success) {
|
|
270662
|
-
throw new SchemaValidationError("Invalid SSO config file", result.error, filePath);
|
|
270663
|
-
}
|
|
270664
|
-
return result.data;
|
|
270665
|
-
}
|
|
270666
|
-
function mergeFileWithFlags(fileConfig, options) {
|
|
270667
|
-
return {
|
|
270668
|
-
provider: options.provider ?? fileConfig.provider,
|
|
270669
|
-
clientId: options.clientId ?? fileConfig.clientId,
|
|
270670
|
-
clientSecret: options.clientSecret ?? fileConfig.clientSecret,
|
|
270671
|
-
clientSecretStdin: options.clientSecretStdin,
|
|
270672
|
-
envFile: options.envFile,
|
|
270673
|
-
scope: options.scope ?? fileConfig.scope,
|
|
270674
|
-
discoveryUrl: options.discoveryUrl ?? fileConfig.discoveryUrl,
|
|
270675
|
-
tenantId: options.tenantId ?? fileConfig.tenantId,
|
|
270676
|
-
oktaDomain: options.oktaDomain ?? fileConfig.oktaDomain,
|
|
270677
|
-
authEndpoint: options.authEndpoint ?? fileConfig.authEndpoint,
|
|
270678
|
-
tokenEndpoint: options.tokenEndpoint ?? fileConfig.tokenEndpoint,
|
|
270679
|
-
userinfoEndpoint: options.userinfoEndpoint ?? fileConfig.userinfoEndpoint,
|
|
270680
|
-
jwksUri: options.jwksUri ?? fileConfig.jwksUri,
|
|
270681
|
-
ssoName: options.ssoName ?? fileConfig.ssoName
|
|
270682
|
-
};
|
|
270683
|
-
}
|
|
270684
|
-
var providerNames = Object.keys(KNOWN_SSO_PROVIDERS);
|
|
270685
|
-
var SECRET_KEY_TO_FLAG = {
|
|
270686
|
-
["sso_name" /* Name */]: "--sso-name",
|
|
270687
|
-
["sso_client_id" /* ClientId */]: "--client-id",
|
|
270688
|
-
["sso_client_secret" /* ClientSecret */]: "--client-secret",
|
|
270689
|
-
["sso_scope" /* Scope */]: "--scope",
|
|
270690
|
-
["sso_discovery_url" /* DiscoveryUrl */]: "--discovery-url",
|
|
270691
|
-
["sso_tenant_id" /* TenantId */]: "--tenant-id",
|
|
270692
|
-
["sso_auth_endpoint" /* AuthEndpoint */]: "--auth-endpoint",
|
|
270693
|
-
["sso_token_endpoint" /* TokenEndpoint */]: "--token-endpoint",
|
|
270694
|
-
["sso_userinfo_endpoint" /* UserinfoEndpoint */]: "--userinfo-endpoint",
|
|
270695
|
-
["sso_okta_domain" /* OktaDomain */]: "--okta-domain",
|
|
270696
|
-
["sso_jwks_uri" /* JwksUri */]: "--jwks-uri"
|
|
270697
|
-
};
|
|
270698
|
-
function secretKeyToFlag(key) {
|
|
270699
|
-
return SECRET_KEY_TO_FLAG[key];
|
|
270700
|
-
}
|
|
270701
|
-
function exampleCommand(provider) {
|
|
270702
|
-
let cmd = `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`;
|
|
270703
|
-
if (provider === KNOWN_SSO_PROVIDERS.microsoft)
|
|
270704
|
-
cmd += " --tenant-id <id>";
|
|
270705
|
-
if (provider === KNOWN_SSO_PROVIDERS.okta)
|
|
270706
|
-
cmd += " --okta-domain <domain>";
|
|
270707
|
-
if (provider === KNOWN_SSO_PROVIDERS.custom)
|
|
270708
|
-
cmd += " --sso-name <name> --auth-endpoint <url> --token-endpoint <url> --userinfo-endpoint <url> --jwks-uri <url>";
|
|
270709
|
-
return cmd;
|
|
270710
|
-
}
|
|
270711
|
-
function validateProvider(provider) {
|
|
270712
|
-
if (!provider) {
|
|
270713
|
-
throw new InvalidInputError("Missing --provider.", {
|
|
270714
|
-
hints: [
|
|
270715
|
-
{
|
|
270716
|
-
message: `Valid providers: ${providerNames.join(", ")}`,
|
|
270717
|
-
command: "base44 auth sso enable --provider <provider> --client-id <id> --client-secret <secret>"
|
|
270718
|
-
}
|
|
270719
|
-
]
|
|
270720
|
-
});
|
|
270721
|
-
}
|
|
270722
|
-
return provider;
|
|
270723
|
-
}
|
|
270724
|
-
async function ssoEnableAction({ isNonInteractive, runTask }, options) {
|
|
270725
|
-
if (options.file && options.envFile) {
|
|
270726
|
-
throw new InvalidInputError("--file and --env-file cannot be used together. Provide the client secret either inside --file or via --env-file.");
|
|
270727
|
-
}
|
|
270728
|
-
let merged = options;
|
|
270729
|
-
if (options.file) {
|
|
270730
|
-
const fileConfig = await loadSSOConfigFile(options.file);
|
|
270731
|
-
merged = mergeFileWithFlags(fileConfig, options);
|
|
270732
|
-
}
|
|
270733
|
-
const provider = validateProvider(merged.provider);
|
|
270734
|
-
if (!merged.clientId) {
|
|
270735
|
-
throw new InvalidInputError("Missing --client-id.", {
|
|
270736
|
-
hints: [
|
|
270737
|
-
{
|
|
270738
|
-
message: `Example: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
|
|
270739
|
-
command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
|
|
270740
|
-
}
|
|
270741
|
-
]
|
|
270742
|
-
});
|
|
270743
|
-
}
|
|
270744
|
-
let clientSecret;
|
|
270745
|
-
if (merged.envFile && !merged.clientSecret) {
|
|
270746
|
-
const secrets = await parseEnvFile(resolve8(merged.envFile));
|
|
270747
|
-
const value = secrets.sso_client_secret;
|
|
270748
|
-
if (!value) {
|
|
270749
|
-
throw new InvalidInputError(`Key "sso_client_secret" not found in ${merged.envFile}.`);
|
|
270750
|
-
}
|
|
270751
|
-
clientSecret = value;
|
|
270752
|
-
} else {
|
|
270753
|
-
clientSecret = await resolveSecret({
|
|
270754
|
-
flagValue: merged.clientSecret,
|
|
270755
|
-
fromStdin: merged.clientSecretStdin,
|
|
270756
|
-
envVar: "sso_client_secret",
|
|
270757
|
-
promptMessage: "Enter SSO client secret",
|
|
270758
|
-
isNonInteractive,
|
|
270759
|
-
name: "client secret",
|
|
270760
|
-
hints: [
|
|
270761
|
-
{
|
|
270762
|
-
message: `Provide via flag: base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`,
|
|
270763
|
-
command: `base44 auth sso enable --provider ${provider} --client-id <id> --client-secret <secret>`
|
|
270764
|
-
},
|
|
270765
|
-
{
|
|
270766
|
-
message: `Provide via stdin: echo <secret> | base44 auth sso enable --provider ${provider} --client-id <id> --client-secret-stdin`
|
|
270767
|
-
},
|
|
270768
|
-
{
|
|
270769
|
-
message: `Provide via env: sso_client_secret=<secret> base44 auth sso enable --provider ${provider} --client-id <id>`
|
|
270770
|
-
}
|
|
270771
|
-
]
|
|
270772
|
-
});
|
|
270773
|
-
}
|
|
270774
|
-
const secretOptions = {
|
|
270775
|
-
clientId: merged.clientId,
|
|
270776
|
-
clientSecret,
|
|
270777
|
-
scope: merged.scope,
|
|
270778
|
-
discoveryUrl: merged.discoveryUrl,
|
|
270779
|
-
tenantId: merged.tenantId,
|
|
270780
|
-
oktaDomain: merged.oktaDomain,
|
|
270781
|
-
authEndpoint: merged.authEndpoint,
|
|
270782
|
-
tokenEndpoint: merged.tokenEndpoint,
|
|
270783
|
-
userinfoEndpoint: merged.userinfoEndpoint,
|
|
270784
|
-
jwksUri: merged.jwksUri,
|
|
270785
|
-
ssoName: merged.ssoName
|
|
270786
|
-
};
|
|
270787
|
-
let secrets;
|
|
270788
|
-
try {
|
|
270789
|
-
secrets = buildSSOSecrets(provider, secretOptions);
|
|
270790
|
-
} catch (error) {
|
|
270791
|
-
if (error instanceof MissingSSOFieldsError) {
|
|
270792
|
-
const flagNames = error.missingKeys.map(secretKeyToFlag);
|
|
270793
|
-
throw new InvalidInputError(`Missing required fields for ${error.provider}: ${flagNames.join(", ")}`, {
|
|
270794
|
-
hints: [
|
|
270795
|
-
{
|
|
270796
|
-
message: `Example: ${exampleCommand(provider)}`,
|
|
270797
|
-
command: exampleCommand(provider)
|
|
270798
|
-
}
|
|
270799
|
-
]
|
|
270800
|
-
});
|
|
270801
|
-
}
|
|
270802
|
-
throw error;
|
|
270803
|
-
}
|
|
270804
|
-
const { project } = await readProjectConfig();
|
|
270805
|
-
const configDir = dirname18(project.configPath);
|
|
270806
|
-
const authDir = join25(configDir, project.authDir);
|
|
270807
|
-
await runTask("Updating local auth config", async () => updateSSOConfig(authDir, provider, true));
|
|
270808
|
-
await runTask("Saving SSO credentials", async () => pushSSOSecrets(secrets));
|
|
270809
|
-
return {
|
|
270810
|
-
outroMessage: `SSO configured with ${provider} in local config. Run \`base44 auth push\` or \`base44 deploy\` to apply.`
|
|
270811
|
-
};
|
|
270812
|
-
}
|
|
270813
|
-
function hasEnableOnlyOptions(options) {
|
|
270814
|
-
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);
|
|
270815
|
-
}
|
|
270816
|
-
async function ssoDisableAction({ log, runTask }, options) {
|
|
270817
|
-
if (hasEnableOnlyOptions(options)) {
|
|
270818
|
-
throw new InvalidInputError("Configuration options cannot be used with disable. To disable SSO: base44 auth sso disable");
|
|
270819
|
-
}
|
|
270820
|
-
const { project } = await readProjectConfig();
|
|
270821
|
-
const configDir = dirname18(project.configPath);
|
|
270822
|
-
const authDir = join25(configDir, project.authDir);
|
|
270823
|
-
const updated = await runTask("Updating local auth config", async () => updateSSOConfig(authDir, null, false));
|
|
270824
|
-
await runTask("Removing SSO credentials", async () => deleteSSOSecrets());
|
|
270825
|
-
if (!hasAnyLoginMethod(updated)) {
|
|
270826
|
-
log.warn("Disabling SSO will leave no login methods enabled. Users will be locked out.");
|
|
270827
|
-
}
|
|
270828
|
-
return {
|
|
270829
|
-
outroMessage: "SSO disabled in local config and credentials removed. Run `base44 auth push` or `base44 deploy` to apply."
|
|
270830
|
-
};
|
|
270831
|
-
}
|
|
270832
|
-
async function ssoAction(context, action, options) {
|
|
270833
|
-
if (action === "disable") {
|
|
270834
|
-
return ssoDisableAction(context, options);
|
|
270835
|
-
}
|
|
270836
|
-
return ssoEnableAction(context, options);
|
|
270837
|
-
}
|
|
270838
|
-
function getSSOCommand() {
|
|
270839
|
-
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([
|
|
270840
|
-
"enable",
|
|
270841
|
-
"disable"
|
|
270842
|
-
])).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);
|
|
270843
|
-
}
|
|
270844
|
-
|
|
270845
|
-
// src/cli/commands/auth/index.ts
|
|
270846
|
-
function getAuthCommand() {
|
|
270847
|
-
return new Command2("auth").description("Manage app authentication settings").addCommand(getPasswordLoginCommand()).addCommand(getSocialLoginCommand()).addCommand(getSSOCommand()).addCommand(getAuthPullCommand()).addCommand(getAuthPushCommand());
|
|
270848
|
-
}
|
|
270849
|
-
|
|
270850
|
-
// src/cli/commands/auth/login.ts
|
|
270851
|
-
function getLoginCommand() {
|
|
270852
|
-
return new Base44Command("login", {
|
|
270853
|
-
requireAuth: false,
|
|
270854
|
-
requireAppContext: false
|
|
270855
|
-
}).description("Authenticate with Base44").action(login);
|
|
270856
|
-
}
|
|
270857
|
-
|
|
270858
|
-
// src/cli/commands/auth/logout.ts
|
|
270859
|
-
async function logout(_ctx) {
|
|
270860
|
-
await deleteAuth();
|
|
270861
|
-
return { outroMessage: "Logged out successfully" };
|
|
270862
|
-
}
|
|
270863
|
-
function getLogoutCommand() {
|
|
270864
|
-
return new Base44Command("logout", {
|
|
270865
|
-
requireAuth: false,
|
|
270866
|
-
requireAppContext: false
|
|
270867
|
-
}).description("Logout from current device").action(logout);
|
|
270868
|
-
}
|
|
270869
|
-
|
|
270870
|
-
// src/cli/commands/auth/whoami.ts
|
|
270871
|
-
async function whoami(_ctx) {
|
|
270872
|
-
const workspaceApiKey = getWorkspaceApiKeyFromEnv();
|
|
270873
|
-
if (workspaceApiKey && isWorkspaceApiKey(workspaceApiKey)) {
|
|
270874
|
-
return {
|
|
270875
|
-
outroMessage: `Using workspace API key: ${theme.styles.bold(workspaceApiKey.slice(0, 10))}`
|
|
270876
|
-
};
|
|
270877
|
-
}
|
|
270878
|
-
const auth = await readAuth();
|
|
270879
|
-
return { outroMessage: `Logged in as: ${theme.styles.bold(auth.email)}` };
|
|
270880
|
-
}
|
|
270881
|
-
function getWhoamiCommand() {
|
|
270882
|
-
return new Base44Command("whoami", { requireAppContext: false }).description("Display current authenticated user").action(whoami);
|
|
270883
|
-
}
|
|
270884
|
-
|
|
270885
|
-
// src/cli/commands/branches/index.ts
|
|
270886
|
-
async function listBranchesAction({
|
|
270887
|
-
log,
|
|
270888
|
-
runTask,
|
|
270889
|
-
jsonMode
|
|
270890
|
-
}) {
|
|
270891
|
-
const remote = await runTask("Fetching branches", () => listBranches());
|
|
270892
|
-
const branches = [
|
|
270893
|
-
{ name: "main", status: "active" },
|
|
270894
|
-
...remote.map((branch) => ({
|
|
270895
|
-
name: branch.branch_name,
|
|
270896
|
-
status: branch.status
|
|
270897
|
-
}))
|
|
270898
|
-
];
|
|
270899
|
-
if (jsonMode)
|
|
270900
|
-
return { stdout: `${JSON.stringify({ branches })}
|
|
270901
|
-
` };
|
|
270902
|
-
for (const branch of branches)
|
|
270903
|
-
log.message(`${branch.name} (${branch.status})`);
|
|
270904
|
-
return { outroMessage: `${branches.length} branches` };
|
|
270905
|
-
}
|
|
270906
|
-
function getBranchesCommand() {
|
|
270907
|
-
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));
|
|
271150
|
+
// src/cli/commands/builder/index.ts
|
|
271151
|
+
function getBuilderCommand() {
|
|
271152
|
+
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());
|
|
270908
271153
|
}
|
|
270909
271154
|
|
|
270910
271155
|
// ../../node_modules/ink/build/render.js
|
|
@@ -275870,6 +276115,119 @@ var build_default = TextInput;
|
|
|
275870
276115
|
// src/cli/commands/code/session.tsx
|
|
275871
276116
|
var import_react23 = __toESM(require_react(), 1);
|
|
275872
276117
|
|
|
276118
|
+
// src/cli/commands/code/logo.ts
|
|
276119
|
+
var ROWS = 6;
|
|
276120
|
+
var ASPECT = 0.44;
|
|
276121
|
+
var SAMPLES = 4;
|
|
276122
|
+
var FILL = 0.6;
|
|
276123
|
+
var GAP_ROW = 3.5 + 0.5;
|
|
276124
|
+
var GAP_HEIGHT_ROWS = 0.8;
|
|
276125
|
+
var LOGO_COLS = Math.max(2, Math.floor(ROWS / ASPECT + 0.5));
|
|
276126
|
+
var OCTANT = {
|
|
276127
|
+
rx: 2,
|
|
276128
|
+
ry: 4,
|
|
276129
|
+
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█")
|
|
276130
|
+
};
|
|
276131
|
+
var QUAD = {
|
|
276132
|
+
rx: 2,
|
|
276133
|
+
ry: 2,
|
|
276134
|
+
table: Array.from(" ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█")
|
|
276135
|
+
};
|
|
276136
|
+
function detectTier(env = process.env) {
|
|
276137
|
+
const term = env.TERM ?? "";
|
|
276138
|
+
const prog = env.TERM_PROGRAM ?? "";
|
|
276139
|
+
if (prog === "ghostty" || term.includes("ghostty"))
|
|
276140
|
+
return "octant";
|
|
276141
|
+
if (env.KITTY_WINDOW_ID || term.includes("kitty"))
|
|
276142
|
+
return "octant";
|
|
276143
|
+
if (env.WEZTERM_PANE || env.WEZTERM_EXECUTABLE)
|
|
276144
|
+
return "octant";
|
|
276145
|
+
if (term.startsWith("foot") || prog === "contour")
|
|
276146
|
+
return "octant";
|
|
276147
|
+
return "quad";
|
|
276148
|
+
}
|
|
276149
|
+
function coverageGrid(rows, cols, aspect, pixelH) {
|
|
276150
|
+
const worldH = rows * pixelH;
|
|
276151
|
+
const worldW = cols * aspect;
|
|
276152
|
+
const radius = Math.min(worldH, worldW) / 2;
|
|
276153
|
+
const cx = worldW / 2;
|
|
276154
|
+
const cy = worldH / 2;
|
|
276155
|
+
const step = 1 / SAMPLES;
|
|
276156
|
+
const grid = [];
|
|
276157
|
+
for (let py = 0;py < rows; py++) {
|
|
276158
|
+
const y0 = py * pixelH;
|
|
276159
|
+
const y1 = y0 + pixelH;
|
|
276160
|
+
const dyLo = y0 <= cy && cy <= y1 ? 0 : Math.min(Math.abs(y0 - cy), Math.abs(y1 - cy));
|
|
276161
|
+
const dyHi = Math.max(Math.abs(y0 - cy), Math.abs(y1 - cy));
|
|
276162
|
+
const line = [];
|
|
276163
|
+
for (let px = 0;px < cols; px++) {
|
|
276164
|
+
const x0 = px * aspect;
|
|
276165
|
+
const x1 = x0 + aspect;
|
|
276166
|
+
const dxLo = x0 <= cx && cx <= x1 ? 0 : Math.min(Math.abs(x0 - cx), Math.abs(x1 - cx));
|
|
276167
|
+
const dxHi = Math.max(Math.abs(x0 - cx), Math.abs(x1 - cx));
|
|
276168
|
+
if (Math.hypot(dxLo, dyLo) >= radius) {
|
|
276169
|
+
line.push(0);
|
|
276170
|
+
continue;
|
|
276171
|
+
}
|
|
276172
|
+
if (Math.hypot(dxHi, dyHi) <= radius) {
|
|
276173
|
+
line.push(1);
|
|
276174
|
+
continue;
|
|
276175
|
+
}
|
|
276176
|
+
let hits = 0;
|
|
276177
|
+
for (let j = 0;j < SAMPLES; j++) {
|
|
276178
|
+
const dy = y0 + (j + 0.5) * step * pixelH - cy;
|
|
276179
|
+
for (let i = 0;i < SAMPLES; i++) {
|
|
276180
|
+
const dx = x0 + (i + 0.5) * step * aspect - cx;
|
|
276181
|
+
if (Math.hypot(dx, dy) <= radius)
|
|
276182
|
+
hits++;
|
|
276183
|
+
}
|
|
276184
|
+
}
|
|
276185
|
+
line.push(hits / (SAMPLES * SAMPLES));
|
|
276186
|
+
}
|
|
276187
|
+
grid.push(line);
|
|
276188
|
+
}
|
|
276189
|
+
return grid;
|
|
276190
|
+
}
|
|
276191
|
+
function carveGap(grid, ry) {
|
|
276192
|
+
const n = grid.length;
|
|
276193
|
+
const thick = Math.max(1, Math.round(GAP_HEIGHT_ROWS * ry));
|
|
276194
|
+
const centre = GAP_ROW >= 0 ? GAP_ROW * ry : n + GAP_ROW * ry;
|
|
276195
|
+
const start = Math.max(0, Math.min(n - thick, centre - thick / 2));
|
|
276196
|
+
for (let i = Math.trunc(start);i < Math.trunc(start + thick); i++) {
|
|
276197
|
+
grid[i].fill(0);
|
|
276198
|
+
}
|
|
276199
|
+
}
|
|
276200
|
+
function logoRows(color, tier = detectTier()) {
|
|
276201
|
+
const { rx, ry, table } = tier === "octant" ? OCTANT : QUAD;
|
|
276202
|
+
const grid = coverageGrid(ROWS * ry, LOGO_COLS * rx, ASPECT / rx, 1 / ry);
|
|
276203
|
+
carveGap(grid, ry);
|
|
276204
|
+
const fg = color ? source_default.hex(color) : (s) => s;
|
|
276205
|
+
const bg = color ? source_default.bgHex(color) : (s) => s;
|
|
276206
|
+
const full = (1 << rx * ry) - 1;
|
|
276207
|
+
const out = [];
|
|
276208
|
+
for (let r = 0;r < ROWS; r++) {
|
|
276209
|
+
let row = "";
|
|
276210
|
+
for (let c = 0;c < LOGO_COLS; c++) {
|
|
276211
|
+
let mask = 0;
|
|
276212
|
+
for (let sr = 0;sr < ry; sr++) {
|
|
276213
|
+
for (let sc = 0;sc < rx; sc++) {
|
|
276214
|
+
if (grid[r * ry + sr][c * rx + sc] >= FILL)
|
|
276215
|
+
mask |= 1 << sr * rx + sc;
|
|
276216
|
+
}
|
|
276217
|
+
}
|
|
276218
|
+
const glyph = table[mask];
|
|
276219
|
+
if (glyph === " ")
|
|
276220
|
+
row += " ";
|
|
276221
|
+
else if (mask === full && color)
|
|
276222
|
+
row += bg(" ");
|
|
276223
|
+
else
|
|
276224
|
+
row += fg(glyph);
|
|
276225
|
+
}
|
|
276226
|
+
out.push(row);
|
|
276227
|
+
}
|
|
276228
|
+
return out;
|
|
276229
|
+
}
|
|
276230
|
+
|
|
275873
276231
|
// src/cli/commands/code/paste.ts
|
|
275874
276232
|
import { PassThrough as PassThrough3 } from "node:stream";
|
|
275875
276233
|
var START = "\x1B[200~";
|
|
@@ -276005,22 +276363,27 @@ function createSessionEngine(options) {
|
|
|
276005
276363
|
if (!prime) {
|
|
276006
276364
|
for (const event of events) {
|
|
276007
276365
|
if (event.kind === "tool_start") {
|
|
276366
|
+
const startedAt = Date.now();
|
|
276008
276367
|
running.set(event.id, {
|
|
276009
276368
|
alias: toolAlias(event.name),
|
|
276010
276369
|
label: event.label,
|
|
276011
276370
|
summary: event.summary,
|
|
276012
|
-
startedAt
|
|
276371
|
+
startedAt
|
|
276013
276372
|
});
|
|
276373
|
+
options.onLine({ running: event, startedAt });
|
|
276014
276374
|
continue;
|
|
276015
276375
|
}
|
|
276016
|
-
let elapsedMs;
|
|
276017
276376
|
if (event.kind === "tool_end") {
|
|
276018
276377
|
const started = running.get(event.id)?.startedAt;
|
|
276019
|
-
if (started != null)
|
|
276020
|
-
elapsedMs = Date.now() - started;
|
|
276021
276378
|
running.delete(event.id);
|
|
276379
|
+
lastEventAt = Date.now();
|
|
276380
|
+
options.onLine({
|
|
276381
|
+
event,
|
|
276382
|
+
elapsedMs: started != null ? Date.now() - started : undefined
|
|
276383
|
+
});
|
|
276384
|
+
continue;
|
|
276022
276385
|
}
|
|
276023
|
-
const line = eventLine(event
|
|
276386
|
+
const line = eventLine(event);
|
|
276024
276387
|
if (line != null) {
|
|
276025
276388
|
lastEventAt = Date.now();
|
|
276026
276389
|
options.onLine(line);
|
|
@@ -276141,6 +276504,17 @@ function exitAltScreen() {
|
|
|
276141
276504
|
altScreenActive = false;
|
|
276142
276505
|
process.stdout.write("\x1B[?1007l\x1B[?1049l");
|
|
276143
276506
|
}
|
|
276507
|
+
function withEntry(items, entry) {
|
|
276508
|
+
if (typeof entry !== "string" && "event" in entry) {
|
|
276509
|
+
const i = items.findIndex((e) => typeof e !== "string" && ("running" in e) && e.running.id === entry.event.id);
|
|
276510
|
+
if (i >= 0) {
|
|
276511
|
+
const next = [...items];
|
|
276512
|
+
next[i] = entry;
|
|
276513
|
+
return next;
|
|
276514
|
+
}
|
|
276515
|
+
}
|
|
276516
|
+
return [...items, entry];
|
|
276517
|
+
}
|
|
276144
276518
|
function statusText(status, musingSeed) {
|
|
276145
276519
|
const frame = FRAMES2[Math.floor(Date.now() / 120) % FRAMES2.length];
|
|
276146
276520
|
switch (status.phase) {
|
|
@@ -276148,13 +276522,6 @@ function statusText(status, musingSeed) {
|
|
|
276148
276522
|
return source_default.dim(`${frame} ${status.awaitingLabel} (${formatDuration2(Date.now() - status.awaitingSince)})`);
|
|
276149
276523
|
case "running": {
|
|
276150
276524
|
const turnFor = formatDuration2(Date.now() - (status.turnStartedAt ?? Date.now()));
|
|
276151
|
-
const tool = status.runningTool;
|
|
276152
|
-
if (tool) {
|
|
276153
|
-
const toolFor = Math.round((Date.now() - tool.startedAt) / 1000);
|
|
276154
|
-
const others = tool.others > 0 ? ` (+${tool.others})` : "";
|
|
276155
|
-
const what = tool.label || `${tool.alias}${tool.summary ? ` ${tool.summary}` : ""}`;
|
|
276156
|
-
return source_default.dim(`${frame} ${what}${others} · ${toolFor}s (turn ${turnFor})`);
|
|
276157
|
-
}
|
|
276158
276525
|
const quiet = status.quietForMs > 60000 ? " · a long private step — details render in the editor" : "";
|
|
276159
276526
|
return `${source_default.magenta("✻")} ${source_default.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`;
|
|
276160
276527
|
}
|
|
@@ -276170,6 +276537,7 @@ function statusText(status, musingSeed) {
|
|
|
276170
276537
|
function SessionView({ engine, footer, subscribe }) {
|
|
276171
276538
|
const { exit } = use_app_default();
|
|
276172
276539
|
const [items, setItems] = import_react23.useState([]);
|
|
276540
|
+
const [verbose, setVerbose] = import_react23.useState(false);
|
|
276173
276541
|
const [input, setInput] = import_react23.useState("");
|
|
276174
276542
|
const [scroll, setScroll] = import_react23.useState(0);
|
|
276175
276543
|
const [, tick] = import_react23.useReducer((x) => x + 1, 0);
|
|
@@ -276178,10 +276546,8 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276178
276546
|
const [pickerIndex, setPickerIndex] = import_react23.useState(null);
|
|
276179
276547
|
const maxScrollRef = import_react23.useRef(0);
|
|
276180
276548
|
const meIdRef = import_react23.useRef(null);
|
|
276181
|
-
const emit = (
|
|
276182
|
-
|
|
276183
|
-
import_react23.useEffect(() => subscribe((line) => setItems((h) => [...h, `${line}
|
|
276184
|
-
`])), [subscribe]);
|
|
276549
|
+
const emit = (entry) => setItems((h) => withEntry(h, entry));
|
|
276550
|
+
import_react23.useEffect(() => subscribe((entry) => setItems((h) => withEntry(h, entry))), [subscribe]);
|
|
276185
276551
|
import_react23.useEffect(() => {
|
|
276186
276552
|
const timer = setInterval(tick, 120);
|
|
276187
276553
|
return () => clearInterval(timer);
|
|
@@ -276249,6 +276615,10 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276249
276615
|
exit();
|
|
276250
276616
|
return;
|
|
276251
276617
|
}
|
|
276618
|
+
if (key.ctrl && char === "o") {
|
|
276619
|
+
setVerbose((v) => !v);
|
|
276620
|
+
return;
|
|
276621
|
+
}
|
|
276252
276622
|
if (key.upArrow) {
|
|
276253
276623
|
setScroll((s) => Math.min(s + 3, maxScrollRef.current));
|
|
276254
276624
|
return;
|
|
@@ -276274,14 +276644,15 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276274
276644
|
});
|
|
276275
276645
|
const columns = process.stdout.columns || 80;
|
|
276276
276646
|
const rows = process.stdout.rows || 24;
|
|
276277
|
-
const width =
|
|
276647
|
+
const width = columns;
|
|
276278
276648
|
const innerWidth = Math.max(10, width - 4);
|
|
276279
276649
|
const inputRows = Math.max(1, Math.ceil((input.length + 3) / innerWidth));
|
|
276280
276650
|
const pickerOpen = pickerIndex !== null;
|
|
276281
276651
|
const inputBlockHeight = pickerOpen ? MODELS.length + 3 : inputRows + 2;
|
|
276282
276652
|
const widgetHeight = inputBlockHeight + 3 + (footer.length ? 1 : 0);
|
|
276283
276653
|
const viewHeight = Math.max(3, rows - widgetHeight - 1);
|
|
276284
|
-
const lines = items.flatMap((item) => hardWrapAnsi(item,
|
|
276654
|
+
const lines = items.flatMap((item) => hardWrapAnsi(`${renderEntry(item, { verbose, foldHint: "Ctrl+O to expand" })}
|
|
276655
|
+
`, columns));
|
|
276285
276656
|
const maxScroll = Math.max(0, lines.length - viewHeight);
|
|
276286
276657
|
maxScrollRef.current = maxScroll;
|
|
276287
276658
|
const clamped = Math.min(scroll, maxScroll);
|
|
@@ -276364,48 +276735,16 @@ function SessionView({ engine, footer, subscribe }) {
|
|
|
276364
276735
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
276365
276736
|
dimColor: true,
|
|
276366
276737
|
wrap: "truncate-end",
|
|
276367
|
-
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"
|
|
276738
|
+
children: pickerOpen ? " ↑↓ to move · Enter to select · Esc to cancel" : engine.turnRunning() ? " Esc to stop · type to queue · scroll to read · Ctrl+O to expand · Ctrl+C to exit" : " Enter to send · /model to switch model · Esc for live · Ctrl+O to expand · Ctrl+C to exit"
|
|
276368
276739
|
}, undefined, false, undefined, this)
|
|
276369
276740
|
]
|
|
276370
276741
|
}, undefined, true, undefined, this);
|
|
276371
276742
|
}
|
|
276372
|
-
var LOGO_ROWS = 6;
|
|
276373
|
-
var LOGO_GAP_SUBROW = 8;
|
|
276374
|
-
var QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█";
|
|
276375
|
-
function buildLogoRows() {
|
|
276376
|
-
const sy = 2 * LOGO_ROWS;
|
|
276377
|
-
const sx = 2 * sy;
|
|
276378
|
-
const cx = (sx - 1) / 2;
|
|
276379
|
-
const cy = (sy - 1) / 2;
|
|
276380
|
-
const rad = sy / 2 - 0.5;
|
|
276381
|
-
const on = (px, py) => {
|
|
276382
|
-
if (py === LOGO_GAP_SUBROW)
|
|
276383
|
-
return false;
|
|
276384
|
-
const dx = (px - cx) * 0.5;
|
|
276385
|
-
const dy = py - cy;
|
|
276386
|
-
return dx * dx + dy * dy <= rad * rad;
|
|
276387
|
-
};
|
|
276388
|
-
const rows = [];
|
|
276389
|
-
for (let ty = 0;ty < sy; ty += 2) {
|
|
276390
|
-
let row = "";
|
|
276391
|
-
for (let tx = 0;tx < sx; tx += 2) {
|
|
276392
|
-
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);
|
|
276393
|
-
row += QUAD[bits];
|
|
276394
|
-
}
|
|
276395
|
-
rows.push(row.trim());
|
|
276396
|
-
}
|
|
276397
|
-
return rows.map((row) => row.trim());
|
|
276398
|
-
}
|
|
276399
276743
|
function renderHeader(who, mode) {
|
|
276400
276744
|
const orange = source_default.hex(BRAND_ORANGE);
|
|
276401
276745
|
const cwd = process.cwd().replace(process.env.HOME ?? "", "~");
|
|
276402
|
-
const logo =
|
|
276403
|
-
const logoW =
|
|
276404
|
-
const center = (s) => {
|
|
276405
|
-
const total = Math.max(0, logoW - s.length);
|
|
276406
|
-
const left = Math.floor(total / 2);
|
|
276407
|
-
return " ".repeat(left) + s + " ".repeat(total - left);
|
|
276408
|
-
};
|
|
276746
|
+
const logo = logoRows(BRAND_ORANGE);
|
|
276747
|
+
const logoW = LOGO_COLS;
|
|
276409
276748
|
const text = [
|
|
276410
276749
|
`${orange.bold("Base44 Code")} ${source_default.dim(`v${package_default.version}`)}`,
|
|
276411
276750
|
source_default.bold(who ? `Welcome back, ${who}!` : "Welcome!"),
|
|
@@ -276417,7 +276756,7 @@ function renderHeader(who, mode) {
|
|
|
276417
276756
|
const textTop = Math.max(0, Math.floor((logo.length - text.length) / 2));
|
|
276418
276757
|
const out = [];
|
|
276419
276758
|
for (let i = 0;i < height; i++) {
|
|
276420
|
-
const left = i < logo.length ?
|
|
276759
|
+
const left = i < logo.length ? logo[i] : " ".repeat(logoW);
|
|
276421
276760
|
const right = text[i - textTop] ?? "";
|
|
276422
276761
|
out.push(` ${left} ${right}`.trimEnd());
|
|
276423
276762
|
}
|
|
@@ -276608,10 +276947,10 @@ async function runGenesisSession(options) {
|
|
|
276608
276947
|
|
|
276609
276948
|
// src/cli/commands/code/index.ts
|
|
276610
276949
|
var BRAND_ORANGE2 = "#E86B3C";
|
|
276611
|
-
async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path) {
|
|
276950
|
+
async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path, wixLaunch) {
|
|
276612
276951
|
let app;
|
|
276613
276952
|
try {
|
|
276614
|
-
app = await createAndLinkApp({ prompt, importRepo, path });
|
|
276953
|
+
app = await createAndLinkApp({ prompt, importRepo, path, wixLaunch });
|
|
276615
276954
|
} catch (error) {
|
|
276616
276955
|
for (const line of await githubReauthLines(error) ?? [])
|
|
276617
276956
|
emit(line);
|
|
@@ -276646,46 +276985,58 @@ async function bootstrapApp(prompt, footer, emit, onCreated, importRepo, path) {
|
|
|
276646
276985
|
}
|
|
276647
276986
|
};
|
|
276648
276987
|
}
|
|
276649
|
-
async function codeAction({ log }, options) {
|
|
276988
|
+
async function codeAction({ log }, options, appId) {
|
|
276989
|
+
const orange = source_default.hex(BRAND_ORANGE2);
|
|
276990
|
+
const chip = (label) => source_default.dim(`${orange("●")} ${label}`);
|
|
276650
276991
|
if (process.stdout.isTTY !== true) {
|
|
276651
276992
|
throw new InvalidInputError("base44 code is an interactive session and needs a terminal.");
|
|
276652
276993
|
}
|
|
276653
276994
|
let linked = false;
|
|
276654
276995
|
try {
|
|
276655
|
-
await initAppContext();
|
|
276996
|
+
await initAppContext(appId ? { appId } : {});
|
|
276656
276997
|
linked = true;
|
|
276657
276998
|
} catch {}
|
|
276658
276999
|
if (linked) {
|
|
276659
|
-
if (options.import || options.path) {
|
|
276660
|
-
throw new InvalidInputError("--import and --
|
|
277000
|
+
if (options.import || options.path || options.wixLaunch) {
|
|
277001
|
+
throw new InvalidInputError("--import, --path and --wix-launch create a new app; run them outside a linked project, without --app-id.");
|
|
276661
277002
|
}
|
|
277003
|
+
const { id, projectRoot } = getAppContext();
|
|
277004
|
+
const state = await assertBuilderApp(id);
|
|
276662
277005
|
const branchId = await resolveActiveBranchId().catch(() => {
|
|
276663
277006
|
return;
|
|
276664
277007
|
});
|
|
276665
277008
|
await runInteractiveSession({
|
|
276666
277009
|
branchId,
|
|
276667
|
-
footer: [],
|
|
277010
|
+
footer: [chip(appTypeChip(state))],
|
|
276668
277011
|
primeFirstPoll: true,
|
|
276669
277012
|
idleHint: "what should the agent do next?"
|
|
276670
277013
|
});
|
|
276671
|
-
|
|
277014
|
+
if (projectRoot) {
|
|
277015
|
+
log.message(source_default.dim(`app dir ${projectRoot}`));
|
|
277016
|
+
return {
|
|
277017
|
+
outroMessage: "Session closed. Run `base44 code` here to resume."
|
|
277018
|
+
};
|
|
277019
|
+
}
|
|
276672
277020
|
return {
|
|
276673
|
-
outroMessage:
|
|
277021
|
+
outroMessage: `Session closed. Resume with \`base44 code --app-id ${id}\`.`
|
|
276674
277022
|
};
|
|
276675
277023
|
}
|
|
277024
|
+
if (options.wixLaunch && options.import) {
|
|
277025
|
+
throw new InvalidInputError("--wix-launch and --import are exclusive.");
|
|
277026
|
+
}
|
|
277027
|
+
const wixLaunch = options.wixLaunch ? parseWixLaunchUrl(options.wixLaunch) : undefined;
|
|
276676
277028
|
let created;
|
|
276677
|
-
const orange = source_default.hex(BRAND_ORANGE2);
|
|
276678
277029
|
const footer = [
|
|
276679
|
-
|
|
277030
|
+
chip(options.import ? repoLabel(options.import) : wixLaunch ? "web app · wix" : "web app")
|
|
276680
277031
|
];
|
|
276681
277032
|
await runGenesisSession({
|
|
276682
|
-
idleHint: options.import ? "describe what to build over the
|
|
277033
|
+
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",
|
|
276683
277034
|
creatingLabel: options.import ? "importing the repository" : "creating your app",
|
|
276684
|
-
modeLabel: options.import ? `
|
|
277035
|
+
modeLabel: options.import ? `Repository — ${repoLabel(options.import)}` : wixLaunch ? "Web app — Wix launch (connector connected first)" : "Web app — Base44 template + builder agent",
|
|
276685
277036
|
footer,
|
|
276686
277037
|
createApp: (prompt, emit) => bootstrapApp(prompt, footer, emit, (app) => {
|
|
276687
277038
|
created = app;
|
|
276688
|
-
}, options.import, options.path)
|
|
277039
|
+
}, options.import, options.path, wixLaunch)
|
|
276689
277040
|
});
|
|
276690
277041
|
if (!created) {
|
|
276691
277042
|
return { outroMessage: "Session closed. No app was created." };
|
|
@@ -276697,7 +277048,7 @@ async function codeAction({ log }, options) {
|
|
|
276697
277048
|
}
|
|
276698
277049
|
function getCodeCommand() {
|
|
276699
277050
|
const command = new Base44Command("code", { requireAppContext: false });
|
|
276700
|
-
command.description("Open Base44 Code
|
|
277051
|
+
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-launch <url>", "Create the new app through a Wix launch URL (your first prompt is what the agent runs)").action((ctx, options) => codeAction(ctx, options, command.optsWithGlobals().appId));
|
|
276701
277052
|
return command;
|
|
276702
277053
|
}
|
|
276703
277054
|
|
|
@@ -279209,6 +279560,21 @@ function getSandboxListDirectoryCommand() {
|
|
|
279209
279560
|
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);
|
|
279210
279561
|
}
|
|
279211
279562
|
|
|
279563
|
+
// src/cli/commands/sandbox/preview.ts
|
|
279564
|
+
async function previewAction(ctx) {
|
|
279565
|
+
const url = await ctx.runTask("Resolving preview URL (boots the sandbox if needed)", () => getPreviewUrl());
|
|
279566
|
+
if (ctx.jsonMode)
|
|
279567
|
+
return { stdout: `${JSON.stringify({ preview_url: url })}
|
|
279568
|
+
` };
|
|
279569
|
+
ctx.log.message(url);
|
|
279570
|
+
return { outroMessage: "Preview is live." };
|
|
279571
|
+
}
|
|
279572
|
+
function getSandboxPreviewCommand() {
|
|
279573
|
+
const command = new Base44Command("preview");
|
|
279574
|
+
command.description("Print the app's live preview URL").action(previewAction);
|
|
279575
|
+
return command;
|
|
279576
|
+
}
|
|
279577
|
+
|
|
279212
279578
|
// src/cli/commands/sandbox/read-file.ts
|
|
279213
279579
|
async function readFileAction({ runTask, branchId }, paths, options) {
|
|
279214
279580
|
const { id: appId } = getAppContext();
|
|
@@ -279262,7 +279628,7 @@ Examples:
|
|
|
279262
279628
|
|
|
279263
279629
|
// src/cli/commands/sandbox/index.ts
|
|
279264
279630
|
function getSandboxCommand() {
|
|
279265
|
-
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());
|
|
279631
|
+
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());
|
|
279266
279632
|
}
|
|
279267
279633
|
|
|
279268
279634
|
// src/cli/commands/secrets/delete.ts
|
|
@@ -284065,7 +284431,7 @@ async function runScript(options) {
|
|
|
284065
284431
|
}
|
|
284066
284432
|
}
|
|
284067
284433
|
// src/cli/commands/exec.ts
|
|
284068
|
-
function
|
|
284434
|
+
function readStdin3() {
|
|
284069
284435
|
return new Promise((resolve, reject) => {
|
|
284070
284436
|
let data = "";
|
|
284071
284437
|
process.stdin.setEncoding("utf-8");
|
|
@@ -284109,7 +284475,7 @@ async function execAction({ app, isNonInteractive }, options) {
|
|
|
284109
284475
|
if (!isNonInteractive) {
|
|
284110
284476
|
throw noInputError;
|
|
284111
284477
|
}
|
|
284112
|
-
const code = await
|
|
284478
|
+
const code = await readStdin3();
|
|
284113
284479
|
if (!code.trim()) {
|
|
284114
284480
|
throw noInputError;
|
|
284115
284481
|
}
|
|
@@ -284289,7 +284655,7 @@ function createProgram(context) {
|
|
|
284289
284655
|
program.addCommand(getSecretsCommand());
|
|
284290
284656
|
program.addCommand(getSandboxCommand());
|
|
284291
284657
|
program.addCommand(getBranchesCommand());
|
|
284292
|
-
program.addCommand(
|
|
284658
|
+
program.addCommand(getBuilderCommand());
|
|
284293
284659
|
program.addCommand(getCodeCommand());
|
|
284294
284660
|
program.addCommand(getAuthCommand());
|
|
284295
284661
|
program.addCommand(getSiteCommand());
|
|
@@ -288344,4 +288710,4 @@ export {
|
|
|
288344
288710
|
runCLI
|
|
288345
288711
|
};
|
|
288346
288712
|
|
|
288347
|
-
//# debugId=
|
|
288713
|
+
//# debugId=9A8433398282BCF064756E2164756E21
|