@bobsworkshop/cli 0.6.0 → 0.7.0
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/analyse-auto-AU5STJV3.js +529 -0
- package/dist/analyse-auto-GCWXNMNZ.js +529 -0
- package/dist/analyse-auto-PIMZNIOW.js +529 -0
- package/dist/analyse-auto-WJAYO6EW.js +529 -0
- package/dist/analyse-auto-XTPO6UCM.js +529 -0
- package/dist/analyse-results-DPQN332P.js +8 -0
- package/dist/analyse-results-I2QBXA5F.js +8 -0
- package/dist/analyse-results-PFM4IQKY.js +8 -0
- package/dist/analyse-results-TG74PQW7.js +8 -0
- package/dist/analyse-results-TIVOJ5SC.js +8 -0
- package/dist/bob.js +1178 -1236
- package/dist/chunk-7NRJGYXJ.js +1084 -0
- package/dist/chunk-DEYW6QHQ.js +1023 -0
- package/dist/chunk-M4GXVZEK.js +1027 -0
- package/dist/chunk-S7UZ5OW3.js +1016 -0
- package/dist/chunk-WXLT3UL7.js +1036 -0
- package/package.json +3 -1
package/dist/bob.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import {
|
|
2
3
|
buildLocalContext,
|
|
3
4
|
callCloudFunction,
|
|
4
|
-
callHTTPFunction,
|
|
5
5
|
callLocalModel,
|
|
6
6
|
extractAllProposedFiles,
|
|
7
7
|
extractProposedFile,
|
|
@@ -16,11 +16,11 @@ import {
|
|
|
16
16
|
registerLoginCommand,
|
|
17
17
|
setConfigValue,
|
|
18
18
|
stripCodeBlockFromResponse
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WXLT3UL7.js";
|
|
20
20
|
|
|
21
21
|
// bin/bob.ts
|
|
22
22
|
import { Command } from "commander";
|
|
23
|
-
import
|
|
23
|
+
import chalk25 from "chalk";
|
|
24
24
|
import * as path14 from "path";
|
|
25
25
|
|
|
26
26
|
// src/commands/config.ts
|
|
@@ -292,29 +292,6 @@ async function getTodayMessages(projectName) {
|
|
|
292
292
|
return msg.timestamp >= twentyFourHoursAgo;
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
|
-
function buildDNAString() {
|
|
296
|
-
const dna = loadCurrentDNA();
|
|
297
|
-
if (!dna) return null;
|
|
298
|
-
const weekly = loadWeeklyProfiles(1)[0] || null;
|
|
299
|
-
const lines = [
|
|
300
|
-
"### USER BEHAVIORAL DNA ###",
|
|
301
|
-
`Archetype: ${dna.archetype || "Unknown"}`,
|
|
302
|
-
`Communication Style: ${dna.communicationStyle || "Unknown"}`,
|
|
303
|
-
`Work Rhythm: ${dna.workRhythm || "Unknown"}`,
|
|
304
|
-
`Decision Making: ${dna.decisionMaking || "Unknown"}`,
|
|
305
|
-
`Emotional State: ${dna.emotionalState || "Unknown"}`
|
|
306
|
-
];
|
|
307
|
-
if (dna.growth) lines.push(`Growth Focus: ${dna.growth}`);
|
|
308
|
-
if (dna.source) lines.push(`Profile Source: ${dna.source} (${dna.lastUpdated || "unknown date"})`);
|
|
309
|
-
if (weekly) {
|
|
310
|
-
lines.push("");
|
|
311
|
-
lines.push("--- WEEKLY TRAJECTORY ---");
|
|
312
|
-
if (weekly.trajectory) lines.push(`Weekly Arc: ${weekly.trajectory}`);
|
|
313
|
-
if (weekly.moodShift) lines.push(`Mood Shift: ${weekly.moodShift}`);
|
|
314
|
-
if (weekly.focusEvolution) lines.push(`Focus Evolution: ${weekly.focusEvolution}`);
|
|
315
|
-
}
|
|
316
|
-
return lines.join("\n");
|
|
317
|
-
}
|
|
318
295
|
|
|
319
296
|
// src/ai/persona.ts
|
|
320
297
|
var STANDARD_STYLE_PROMPT = `You are Bob: friendly, direct, senior-level engineering partner.
|
|
@@ -670,7 +647,7 @@ function renderFrame(frame, frameHeight, statusText) {
|
|
|
670
647
|
}
|
|
671
648
|
}
|
|
672
649
|
function sleep(ms) {
|
|
673
|
-
return new Promise((
|
|
650
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
674
651
|
}
|
|
675
652
|
|
|
676
653
|
// src/ui/animations/deep-dive.ts
|
|
@@ -1153,7 +1130,7 @@ async function typewriterCharByChar(bgColor, textColor, line, maxWidth) {
|
|
|
1153
1130
|
process.stdout.write("\n");
|
|
1154
1131
|
}
|
|
1155
1132
|
function sleep2(ms) {
|
|
1156
|
-
return new Promise((
|
|
1133
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1157
1134
|
}
|
|
1158
1135
|
|
|
1159
1136
|
// src/commands/deepdive.ts
|
|
@@ -1253,8 +1230,8 @@ function registerDeepDiveCommand(program2) {
|
|
|
1253
1230
|
console.log(MODE_DEEPDIVE2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
1254
1231
|
console.log("");
|
|
1255
1232
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1256
|
-
const answer = await new Promise((
|
|
1257
|
-
rl.question(MODE_DEEPDIVE2(" Select (1-" + dives.length + ") or 0 to cancel: "),
|
|
1233
|
+
const answer = await new Promise((resolve3) => {
|
|
1234
|
+
rl.question(MODE_DEEPDIVE2(" Select (1-" + dives.length + ") or 0 to cancel: "), resolve3);
|
|
1258
1235
|
});
|
|
1259
1236
|
const selection = parseInt(answer.trim());
|
|
1260
1237
|
if (isNaN(selection) || selection === 0 || selection < 1 || selection > dives.length) {
|
|
@@ -1266,9 +1243,9 @@ function registerDeepDiveCommand(program2) {
|
|
|
1266
1243
|
const parentMessageId = selectedDive.parentMessageId;
|
|
1267
1244
|
const initiatingPrompt = selectedDive.initiatingPrompt || "Deep dive session";
|
|
1268
1245
|
const animation = startDeepDiveAnimation();
|
|
1269
|
-
await new Promise((
|
|
1246
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
1270
1247
|
animation.stop();
|
|
1271
|
-
await new Promise((
|
|
1248
|
+
await new Promise((resolve3) => setTimeout(resolve3, 300));
|
|
1272
1249
|
await runDeepDiveSession(config, config.conversationId, parentMessageId, initiatingPrompt, rl);
|
|
1273
1250
|
rl.close();
|
|
1274
1251
|
} catch (error) {
|
|
@@ -1329,8 +1306,8 @@ async function enterDeepDive(config, conversationId, rl) {
|
|
|
1329
1306
|
}
|
|
1330
1307
|
console.log(MODE_DEEPDIVE2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
1331
1308
|
console.log("");
|
|
1332
|
-
const answer = await new Promise((
|
|
1333
|
-
rl.question(MODE_DEEPDIVE2(" Select (1-" + messages.length + ") or 0 to cancel: "),
|
|
1309
|
+
const answer = await new Promise((resolve3) => {
|
|
1310
|
+
rl.question(MODE_DEEPDIVE2(" Select (1-" + messages.length + ") or 0 to cancel: "), resolve3);
|
|
1334
1311
|
});
|
|
1335
1312
|
const selection = parseInt(answer.trim());
|
|
1336
1313
|
if (isNaN(selection) || selection === 0 || selection < 1 || selection > messages.length) {
|
|
@@ -1343,13 +1320,13 @@ async function enterDeepDive(config, conversationId, rl) {
|
|
|
1343
1320
|
const animation = startDeepDiveAnimation();
|
|
1344
1321
|
const divePromise = callCloudFunction("initiateCLIDeepDive", { conversationId, parentMessageId, initiatingPrompt });
|
|
1345
1322
|
try {
|
|
1346
|
-
await Promise.all([divePromise, new Promise((
|
|
1323
|
+
await Promise.all([divePromise, new Promise((resolve3) => setTimeout(resolve3, 3e3))]);
|
|
1347
1324
|
animation.stop();
|
|
1348
|
-
await new Promise((
|
|
1325
|
+
await new Promise((resolve3) => setTimeout(resolve3, 300));
|
|
1349
1326
|
await runDeepDiveSession(config, conversationId, parentMessageId, initiatingPrompt, rl);
|
|
1350
1327
|
} catch (error) {
|
|
1351
1328
|
animation.stop();
|
|
1352
|
-
await new Promise((
|
|
1329
|
+
await new Promise((resolve3) => setTimeout(resolve3, 200));
|
|
1353
1330
|
console.log(ERROR2(` \u274C Could not initiate deep dive: ${error.message}`));
|
|
1354
1331
|
}
|
|
1355
1332
|
}
|
|
@@ -1369,7 +1346,7 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
|
|
|
1369
1346
|
console.log("");
|
|
1370
1347
|
let lastBobResponse = "";
|
|
1371
1348
|
let lastConstraints3 = [];
|
|
1372
|
-
return new Promise((
|
|
1349
|
+
return new Promise((resolve3) => {
|
|
1373
1350
|
const deepDivePrompt = () => {
|
|
1374
1351
|
rl.question(MODE_DEEPDIVE2(" \u{1F93F} You: "), async (input) => {
|
|
1375
1352
|
const trimmed = input.trim();
|
|
@@ -1384,7 +1361,7 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
|
|
|
1384
1361
|
console.log(MODE_DEEPDIVE2(" \u2551") + MUTED2(` Back in: ${conversationId.slice(0, 24)}...`));
|
|
1385
1362
|
console.log(MODE_DEEPDIVE2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
1386
1363
|
console.log("");
|
|
1387
|
-
|
|
1364
|
+
resolve3();
|
|
1388
1365
|
return;
|
|
1389
1366
|
}
|
|
1390
1367
|
if (trimmed === "/promote") {
|
|
@@ -1400,7 +1377,7 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
|
|
|
1400
1377
|
console.log(ERROR2(` \u274C Promote failed: ${error.message}`));
|
|
1401
1378
|
console.log("");
|
|
1402
1379
|
}
|
|
1403
|
-
|
|
1380
|
+
resolve3();
|
|
1404
1381
|
return;
|
|
1405
1382
|
}
|
|
1406
1383
|
if (trimmed === "/clear") {
|
|
@@ -1431,14 +1408,14 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
|
|
|
1431
1408
|
console.log(BORDER(" \u2502"));
|
|
1432
1409
|
console.log(BORDER(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
|
|
1433
1410
|
console.log("");
|
|
1434
|
-
const answer = await new Promise((
|
|
1435
|
-
rl.question(MODE_DEEPDIVE2(" Leave deep dive for main conversation? (y/N): "),
|
|
1411
|
+
const answer = await new Promise((resolve4) => {
|
|
1412
|
+
rl.question(MODE_DEEPDIVE2(" Leave deep dive for main conversation? (y/N): "), resolve4);
|
|
1436
1413
|
});
|
|
1437
1414
|
if (answer.trim().toLowerCase() === "y") {
|
|
1438
1415
|
console.log("");
|
|
1439
1416
|
console.log(SUCCESS2(" \u2705 Surfacing from deep dive. Personalization Mode will activate in main conversation."));
|
|
1440
1417
|
console.log("");
|
|
1441
|
-
|
|
1418
|
+
resolve3();
|
|
1442
1419
|
return;
|
|
1443
1420
|
}
|
|
1444
1421
|
console.log(MUTED2(" Staying in deep dive."));
|
|
@@ -1641,12 +1618,17 @@ async function playWelcomeAnimation() {
|
|
|
1641
1618
|
console.log(MUTED4(" \u2551") + pad2(" " + BRAND_SECONDARY4('\u25B8 bob push "msg"') + MUTED4(" \u2014 Git commit + push"), 41));
|
|
1642
1619
|
console.log(MUTED4(" \u2551") + pad2(" " + BRAND_SECONDARY4("\u25B8 bob --help") + MUTED4(" \u2014 See all commands"), 40));
|
|
1643
1620
|
console.log(MUTED4(" \u2551") + pad2("", 0));
|
|
1621
|
+
console.log(hRule2());
|
|
1622
|
+
console.log(MUTED4(" \u2551") + pad2("", 0));
|
|
1623
|
+
console.log(MUTED4(" \u2551") + pad2(" " + SUCCESS4("\u{1F331} Join 1,700+ builders in our community:"), 43));
|
|
1624
|
+
console.log(MUTED4(" \u2551") + pad2(" " + INFO4("https://discord.gg/wM9ZBXdd"), 21));
|
|
1625
|
+
console.log(MUTED4(" \u2551") + pad2("", 0));
|
|
1644
1626
|
console.log(MUTED4(" \u255A" + "\u2550".repeat(BOX_WIDTH2) + "\u255D"));
|
|
1645
1627
|
console.log("");
|
|
1646
1628
|
await sleep3(800);
|
|
1647
1629
|
}
|
|
1648
1630
|
function sleep3(ms) {
|
|
1649
|
-
return new Promise((
|
|
1631
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1650
1632
|
}
|
|
1651
1633
|
|
|
1652
1634
|
// src/core/provider-detect.ts
|
|
@@ -1939,7 +1921,7 @@ ${content}
|
|
|
1939
1921
|
}
|
|
1940
1922
|
rl.pause();
|
|
1941
1923
|
const confirmPromptText = ERROR4(` \u{1F5D1}\uFE0F Delete ${filePath}? (y/n): `);
|
|
1942
|
-
const confirm = await new Promise((
|
|
1924
|
+
const confirm = await new Promise((resolve3) => {
|
|
1943
1925
|
process.stdout.write(confirmPromptText);
|
|
1944
1926
|
process.stdin.resume();
|
|
1945
1927
|
process.stdin.setEncoding("utf-8");
|
|
@@ -1950,7 +1932,7 @@ ${content}
|
|
|
1950
1932
|
inputBuffer += chunk.slice(0, newlineIdx);
|
|
1951
1933
|
process.stdin.removeListener("data", onData);
|
|
1952
1934
|
process.stdin.pause();
|
|
1953
|
-
|
|
1935
|
+
resolve3(inputBuffer.replace(/\r/g, "").trim());
|
|
1954
1936
|
} else {
|
|
1955
1937
|
inputBuffer += chunk;
|
|
1956
1938
|
}
|
|
@@ -2559,8 +2541,8 @@ function registerByokCommand(program2) {
|
|
|
2559
2541
|
return;
|
|
2560
2542
|
}
|
|
2561
2543
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
|
|
2562
|
-
const answer = await new Promise((
|
|
2563
|
-
rl.question(WARNING9(` Remove ${provider} key? (y/n): `),
|
|
2544
|
+
const answer = await new Promise((resolve3) => {
|
|
2545
|
+
rl.question(WARNING9(` Remove ${provider} key? (y/n): `), resolve3);
|
|
2564
2546
|
});
|
|
2565
2547
|
rl.close();
|
|
2566
2548
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
@@ -2740,8 +2722,8 @@ function registerConversationsCommand(program2) {
|
|
|
2740
2722
|
}
|
|
2741
2723
|
renderConversationList(conversations, config.conversationId, options.search, result, true);
|
|
2742
2724
|
const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
|
|
2743
|
-
const answer = await new Promise((
|
|
2744
|
-
rl.question(INFO11(" Select (1-" + conversations.length + ") or 0 to cancel: "),
|
|
2725
|
+
const answer = await new Promise((resolve3) => {
|
|
2726
|
+
rl.question(INFO11(" Select (1-" + conversations.length + ") or 0 to cancel: "), resolve3);
|
|
2745
2727
|
});
|
|
2746
2728
|
rl.close();
|
|
2747
2729
|
const selection = parseInt(answer.trim());
|
|
@@ -2978,7 +2960,7 @@ function truncate(text, max) {
|
|
|
2978
2960
|
return text.length > max ? text.slice(0, max - 3) + "..." : text;
|
|
2979
2961
|
}
|
|
2980
2962
|
function sleep4(ms) {
|
|
2981
|
-
return new Promise((
|
|
2963
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2982
2964
|
}
|
|
2983
2965
|
|
|
2984
2966
|
// src/commands/fork.ts
|
|
@@ -3022,7 +3004,7 @@ function registerForkCommand(program2) {
|
|
|
3022
3004
|
try {
|
|
3023
3005
|
const result = await forkPromise;
|
|
3024
3006
|
animation.stop();
|
|
3025
|
-
await new Promise((
|
|
3007
|
+
await new Promise((resolve3) => setTimeout(resolve3, 200));
|
|
3026
3008
|
if (result?.conversationId) {
|
|
3027
3009
|
setConfigValue("conversationId", result.conversationId);
|
|
3028
3010
|
console.log("");
|
|
@@ -3056,7 +3038,7 @@ function registerForkCommand(program2) {
|
|
|
3056
3038
|
}
|
|
3057
3039
|
} catch (error) {
|
|
3058
3040
|
animation.stop();
|
|
3059
|
-
await new Promise((
|
|
3041
|
+
await new Promise((resolve3) => setTimeout(resolve3, 200));
|
|
3060
3042
|
console.log("");
|
|
3061
3043
|
console.log(ERROR10(` \u274C Fork failed: ${error.message}`));
|
|
3062
3044
|
console.log("");
|
|
@@ -3126,7 +3108,7 @@ function registerAnalyseCommand(program2) {
|
|
|
3126
3108
|
program2.command("analyse").description("Analyse the current project for bugs, features, improvements, and upgrades").option("--results", "Show analysis dashboard or filtered list").option("--bugs", "Show bugs list (interactive)").option("--features", "Show features list (interactive)").option("--improvements", "Show improvements list (interactive)").option("--upgrades", "Show upgrades list (interactive)").option("--sort <method>", "Sort by: priority (default) or file").option("--search <query>", "Filter results by keyword").option("--status", "Show current analysis job status").option("--auto", "Auto-fix mode: Bob triages and MiniBob implements").option("--confidence <number>", "Confidence gate for auto-fix (default: 90)", "90").option("--priority <level>", "Priority gate for auto-fix: critical, high, medium, low (default: critical)", "critical").action(async (options) => {
|
|
3127
3109
|
const config = getConfig();
|
|
3128
3110
|
if (options.auto) {
|
|
3129
|
-
const { runAutoFix } = await import("./analyse-auto-
|
|
3111
|
+
const { runAutoFix } = await import("./analyse-auto-GCWXNMNZ.js");
|
|
3130
3112
|
const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : options.upgrades ? "upgrades" : void 0;
|
|
3131
3113
|
await runAutoFix({
|
|
3132
3114
|
category,
|
|
@@ -3136,7 +3118,7 @@ function registerAnalyseCommand(program2) {
|
|
|
3136
3118
|
return;
|
|
3137
3119
|
}
|
|
3138
3120
|
if (options.bugs || options.features || options.improvements || options.upgrades) {
|
|
3139
|
-
const { showInteractiveResults } = await import("./analyse-results-
|
|
3121
|
+
const { showInteractiveResults } = await import("./analyse-results-TIVOJ5SC.js");
|
|
3140
3122
|
const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : "upgrades";
|
|
3141
3123
|
await showInteractiveResults(config, category, options.sort, options.search);
|
|
3142
3124
|
return;
|
|
@@ -3591,8 +3573,8 @@ async function runTier3Autonomy(config) {
|
|
|
3591
3573
|
console.log(GREEN(" \u2705 All tasks complete!"));
|
|
3592
3574
|
console.log(AMBER2(" \u{1F4E4} MiniBob wants to push to GitHub."));
|
|
3593
3575
|
const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
|
|
3594
|
-
const answer = await new Promise((
|
|
3595
|
-
rl.question(CYAN(" Approve push? (y/n): "),
|
|
3576
|
+
const answer = await new Promise((resolve3) => {
|
|
3577
|
+
rl.question(CYAN(" Approve push? (y/n): "), resolve3);
|
|
3596
3578
|
});
|
|
3597
3579
|
rl.close();
|
|
3598
3580
|
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
@@ -3622,7 +3604,7 @@ async function runTier3Autonomy(config) {
|
|
|
3622
3604
|
} catch (pollError) {
|
|
3623
3605
|
}
|
|
3624
3606
|
if (running) {
|
|
3625
|
-
await new Promise((
|
|
3607
|
+
await new Promise((resolve3) => setTimeout(resolve3, 2500));
|
|
3626
3608
|
}
|
|
3627
3609
|
}
|
|
3628
3610
|
console.log("");
|
|
@@ -3940,48 +3922,99 @@ function renderLocalTodoList(queue) {
|
|
|
3940
3922
|
|
|
3941
3923
|
// src/commands/serve.ts
|
|
3942
3924
|
import chalk19 from "chalk";
|
|
3925
|
+
import * as fs10 from "fs";
|
|
3943
3926
|
import * as os3 from "os";
|
|
3944
3927
|
import * as path11 from "path";
|
|
3928
|
+
import * as crypto2 from "crypto";
|
|
3929
|
+
import axios from "axios";
|
|
3945
3930
|
var GREEN2 = chalk19.hex("#66BB6A");
|
|
3946
3931
|
var AMBER3 = chalk19.hex("#FFAB00");
|
|
3947
|
-
var BLUE2 = chalk19.hex("#42A5F5");
|
|
3948
3932
|
var RED2 = chalk19.hex("#EF5350");
|
|
3949
3933
|
var GRAY2 = chalk19.gray;
|
|
3950
3934
|
var CYAN2 = chalk19.cyan;
|
|
3951
3935
|
var BORDER5 = chalk19.hex("#455A64");
|
|
3936
|
+
var BOB_DIR3 = path11.join(os3.homedir(), ".bob");
|
|
3937
|
+
var ALGORITHM = "aes-256-cbc";
|
|
3952
3938
|
var TIER_CONFIGS = {
|
|
3953
3939
|
"Power": {
|
|
3954
3940
|
activeInterval: 2e3,
|
|
3955
|
-
// 2 seconds
|
|
3956
3941
|
sleepInterval: 12e4,
|
|
3957
|
-
// 120 seconds (2 minutes) when sleeping
|
|
3958
3942
|
idleThreshold: 5 * 6e4,
|
|
3959
|
-
// 5 min → enter sleep
|
|
3960
3943
|
extendedIdleTimeout: null
|
|
3961
|
-
// Never auto-exit
|
|
3962
3944
|
},
|
|
3963
3945
|
"Pro": {
|
|
3964
3946
|
activeInterval: 1e4,
|
|
3965
|
-
// 10 seconds
|
|
3966
3947
|
sleepInterval: 3e4,
|
|
3967
|
-
// 30 seconds when sleeping
|
|
3968
3948
|
idleThreshold: 5 * 6e4,
|
|
3969
|
-
// 5 min → enter sleep
|
|
3970
3949
|
extendedIdleTimeout: 60 * 6e4
|
|
3971
|
-
// 1 hour → auto-exit
|
|
3972
3950
|
},
|
|
3973
3951
|
"Starter": {
|
|
3974
3952
|
activeInterval: 15e3,
|
|
3975
|
-
// 15 seconds
|
|
3976
3953
|
sleepInterval: null,
|
|
3977
|
-
// No sleep mode — goes straight to auto-exit
|
|
3978
3954
|
idleThreshold: null,
|
|
3979
|
-
// No idle detection
|
|
3980
3955
|
extendedIdleTimeout: 15 * 6e4
|
|
3981
|
-
// 15 minutes → auto-exit
|
|
3982
3956
|
}
|
|
3983
3957
|
};
|
|
3984
3958
|
var BLOCKED_TIERS = ["Explore", "Free", "free", "explore"];
|
|
3959
|
+
function deriveKey(uid, salt) {
|
|
3960
|
+
return crypto2.pbkdf2Sync(uid, salt, 1e5, 32, "sha256");
|
|
3961
|
+
}
|
|
3962
|
+
function encrypt(inputPath, outputPath, uid) {
|
|
3963
|
+
const salt = crypto2.randomBytes(16).toString("hex");
|
|
3964
|
+
const key = deriveKey(uid, salt);
|
|
3965
|
+
const iv = crypto2.randomBytes(16);
|
|
3966
|
+
const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
|
|
3967
|
+
const input = fs10.readFileSync(inputPath);
|
|
3968
|
+
const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
|
|
3969
|
+
const header = Buffer.from(salt + iv.toString("hex"), "utf-8");
|
|
3970
|
+
fs10.writeFileSync(outputPath, Buffer.concat([header, encrypted]));
|
|
3971
|
+
}
|
|
3972
|
+
function decrypt(inputPath, outputPath, uid) {
|
|
3973
|
+
const data = fs10.readFileSync(inputPath);
|
|
3974
|
+
const header = data.slice(0, 64).toString("utf-8");
|
|
3975
|
+
const salt = header.slice(0, 32);
|
|
3976
|
+
const iv = Buffer.from(header.slice(32, 64), "hex");
|
|
3977
|
+
const encrypted = data.slice(64);
|
|
3978
|
+
const key = deriveKey(uid, salt);
|
|
3979
|
+
const decipher = crypto2.createDecipheriv(ALGORITHM, key, iv);
|
|
3980
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
3981
|
+
fs10.writeFileSync(outputPath, decrypted);
|
|
3982
|
+
}
|
|
3983
|
+
function getTempDir() {
|
|
3984
|
+
const tmpDir = path11.join(os3.tmpdir(), `bob-remote-${Date.now()}`);
|
|
3985
|
+
fs10.mkdirSync(tmpDir, { recursive: true });
|
|
3986
|
+
return tmpDir;
|
|
3987
|
+
}
|
|
3988
|
+
function cleanupTemp(tmpDir) {
|
|
3989
|
+
try {
|
|
3990
|
+
fs10.rmSync(tmpDir, { recursive: true, force: true });
|
|
3991
|
+
} catch {
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
var IGNORE_DIRS2 = ["node_modules", ".git", "dist", "build", ".dart_tool", ".idea", ".gradle", ".pub-cache", ".bob"];
|
|
3995
|
+
var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".dart", ".js", ".ts", ".html", ".css", ".json", ".yaml", ".yml", ".xml", ".sh", ".md"]);
|
|
3996
|
+
function scanProjectFiles2(rootDir, currentDir, depth = 0) {
|
|
3997
|
+
if (depth > 6) return [];
|
|
3998
|
+
const dir = currentDir || rootDir;
|
|
3999
|
+
const files = [];
|
|
4000
|
+
try {
|
|
4001
|
+
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
4002
|
+
for (const entry of entries) {
|
|
4003
|
+
if (IGNORE_DIRS2.includes(entry.name)) continue;
|
|
4004
|
+
if (entry.name.startsWith(".")) continue;
|
|
4005
|
+
const fullPath = path11.join(dir, entry.name);
|
|
4006
|
+
const relativePath = path11.relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
4007
|
+
if (entry.isDirectory()) {
|
|
4008
|
+
files.push(...scanProjectFiles2(rootDir, fullPath, depth + 1));
|
|
4009
|
+
} else {
|
|
4010
|
+
const ext = path11.extname(entry.name).toLowerCase();
|
|
4011
|
+
if (CODE_EXTENSIONS2.has(ext)) files.push(relativePath);
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
} catch {
|
|
4015
|
+
}
|
|
4016
|
+
return files;
|
|
4017
|
+
}
|
|
3985
4018
|
function registerServeCommand(program2) {
|
|
3986
4019
|
program2.command("serve").description("Start an Active Bob \u2014 receive and execute commands from the web app or another device").action(async () => {
|
|
3987
4020
|
const config = getConfig();
|
|
@@ -4062,14 +4095,15 @@ async function startActiveBob(config, sessionId, machineId, projectName, tierCon
|
|
|
4062
4095
|
if (tierConfig.sleepInterval) {
|
|
4063
4096
|
console.log(BORDER5(" \u2551") + GRAY2(` Sleep: every ${tierConfig.sleepInterval / 1e3}s after ${tierConfig.idleThreshold / 6e4} min idle`));
|
|
4064
4097
|
} else {
|
|
4065
|
-
console.log(BORDER5(" \u2551") + GRAY2(
|
|
4098
|
+
console.log(BORDER5(" \u2551") + GRAY2(" Sleep: disabled"));
|
|
4066
4099
|
}
|
|
4067
4100
|
if (tierConfig.extendedIdleTimeout) {
|
|
4068
4101
|
console.log(BORDER5(" \u2551") + GRAY2(` Auto-exit: after ${tierConfig.extendedIdleTimeout / 6e4} min idle`));
|
|
4069
4102
|
} else {
|
|
4070
|
-
console.log(BORDER5(" \u2551") + GREEN2(
|
|
4103
|
+
console.log(BORDER5(" \u2551") + GREEN2(" Auto-exit: never (Power tier)"));
|
|
4071
4104
|
}
|
|
4072
4105
|
console.log(BORDER5(" \u2551"));
|
|
4106
|
+
console.log(BORDER5(" \u2551") + GRAY2(" Capabilities: chat, consult, push, index, analyse, backup, restore"));
|
|
4073
4107
|
console.log(BORDER5(" \u2551") + GRAY2(" Press Ctrl+C to stop."));
|
|
4074
4108
|
console.log(BORDER5(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
4075
4109
|
console.log("");
|
|
@@ -4089,7 +4123,9 @@ async function startActiveBob(config, sessionId, machineId, projectName, tierCon
|
|
|
4089
4123
|
return;
|
|
4090
4124
|
}
|
|
4091
4125
|
let running = true;
|
|
4126
|
+
let sigintHandlerAdded = false;
|
|
4092
4127
|
const cleanup = async () => {
|
|
4128
|
+
if (!running) return;
|
|
4093
4129
|
running = false;
|
|
4094
4130
|
console.log("");
|
|
4095
4131
|
console.log(GRAY2(" \u{1F50C} Shutting down Active Bob..."));
|
|
@@ -4105,8 +4141,11 @@ async function startActiveBob(config, sessionId, machineId, projectName, tierCon
|
|
|
4105
4141
|
console.log("");
|
|
4106
4142
|
process.exit(0);
|
|
4107
4143
|
};
|
|
4108
|
-
|
|
4109
|
-
|
|
4144
|
+
if (!sigintHandlerAdded) {
|
|
4145
|
+
process.once("SIGINT", cleanup);
|
|
4146
|
+
process.once("SIGTERM", cleanup);
|
|
4147
|
+
sigintHandlerAdded = true;
|
|
4148
|
+
}
|
|
4110
4149
|
let lastCommandTime = Date.now();
|
|
4111
4150
|
let isSleeping = false;
|
|
4112
4151
|
while (running) {
|
|
@@ -4149,7 +4188,7 @@ async function startActiveBob(config, sessionId, machineId, projectName, tierCon
|
|
|
4149
4188
|
}
|
|
4150
4189
|
lastCommandTime = Date.now();
|
|
4151
4190
|
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
4152
|
-
console.log(AMBER3(` [${timestamp}] \u23F3 Received: ${type}
|
|
4191
|
+
console.log(AMBER3(` [${timestamp}] \u23F3 Received: ${type}${payload.message ? ` "${payload.message.slice(0, 40)}${payload.message.length > 40 ? "..." : ""}"` : ""}`));
|
|
4153
4192
|
const commandResult = await executeRemoteCommand(type, payload, config);
|
|
4154
4193
|
await callCloudFunction("completeRemoteCommand", {
|
|
4155
4194
|
conversationId: config.conversationId,
|
|
@@ -4172,7 +4211,7 @@ async function startActiveBob(config, sessionId, machineId, projectName, tierCon
|
|
|
4172
4211
|
}
|
|
4173
4212
|
}
|
|
4174
4213
|
if (running) {
|
|
4175
|
-
await new Promise((
|
|
4214
|
+
await new Promise((resolve3) => setTimeout(resolve3, currentInterval));
|
|
4176
4215
|
}
|
|
4177
4216
|
}
|
|
4178
4217
|
}
|
|
@@ -4185,9 +4224,13 @@ async function executeRemoteCommand(type, payload, config) {
|
|
|
4185
4224
|
case "push":
|
|
4186
4225
|
return await executePush(payload);
|
|
4187
4226
|
case "index":
|
|
4188
|
-
return
|
|
4227
|
+
return await executeIndex(payload, config);
|
|
4189
4228
|
case "analyse":
|
|
4190
|
-
return
|
|
4229
|
+
return await executeAnalyse(payload, config);
|
|
4230
|
+
case "backup":
|
|
4231
|
+
return await executeBackup(payload, config);
|
|
4232
|
+
case "restore":
|
|
4233
|
+
return await executeRestore(payload, config);
|
|
4191
4234
|
case "autonomy":
|
|
4192
4235
|
return { success: true, message: "Autonomy command received. Feature in progress." };
|
|
4193
4236
|
default:
|
|
@@ -4219,9 +4262,7 @@ ${fullContext}` : "") },
|
|
|
4219
4262
|
];
|
|
4220
4263
|
const response = await callLocalModel(config.localEndpoint, messages);
|
|
4221
4264
|
const proposed = extractProposedFile(response);
|
|
4222
|
-
if (proposed)
|
|
4223
|
-
await proposeAndWriteFile(proposed, true);
|
|
4224
|
-
}
|
|
4265
|
+
if (proposed) await proposeAndWriteFile(proposed, true);
|
|
4225
4266
|
return {
|
|
4226
4267
|
success: true,
|
|
4227
4268
|
text: response,
|
|
@@ -4256,11 +4297,7 @@ ${fullContext}` : "") },
|
|
|
4256
4297
|
{ role: "user", content: message }
|
|
4257
4298
|
];
|
|
4258
4299
|
const response = await callLocalModel(config.localEndpoint, messages);
|
|
4259
|
-
return {
|
|
4260
|
-
success: true,
|
|
4261
|
-
text: response,
|
|
4262
|
-
referencedFiles: selectedFiles
|
|
4263
|
-
};
|
|
4300
|
+
return { success: true, text: response, referencedFiles: selectedFiles };
|
|
4264
4301
|
} catch (error) {
|
|
4265
4302
|
return { success: false, error: error.message };
|
|
4266
4303
|
}
|
|
@@ -4274,7 +4311,9 @@ async function executePush(payload) {
|
|
|
4274
4311
|
const isRepo = await git.checkIsRepo();
|
|
4275
4312
|
if (!isRepo) return { success: false, error: "Not a git repository." };
|
|
4276
4313
|
const status = await git.status();
|
|
4277
|
-
if (status.files.length === 0)
|
|
4314
|
+
if (status.files.length === 0) {
|
|
4315
|
+
return { success: true, message: "Nothing to commit. Working tree clean." };
|
|
4316
|
+
}
|
|
4278
4317
|
await git.add(".");
|
|
4279
4318
|
const commitResult = await git.commit(message);
|
|
4280
4319
|
const branch = (await git.branchLocal()).current;
|
|
@@ -4297,6 +4336,336 @@ async function executePush(payload) {
|
|
|
4297
4336
|
return { success: false, error: error.message };
|
|
4298
4337
|
}
|
|
4299
4338
|
}
|
|
4339
|
+
async function executeIndex(payload, config) {
|
|
4340
|
+
if (!config.localEndpoint) {
|
|
4341
|
+
return { success: false, error: "No local endpoint configured. Index requires a local model." };
|
|
4342
|
+
}
|
|
4343
|
+
const cwd = process.cwd();
|
|
4344
|
+
const projectName = getProjectName(cwd);
|
|
4345
|
+
try {
|
|
4346
|
+
const files = scanProjectFiles2(cwd);
|
|
4347
|
+
if (files.length === 0) {
|
|
4348
|
+
return { success: false, error: "No code files found to index." };
|
|
4349
|
+
}
|
|
4350
|
+
const { runId, runDir, tasksDir } = createAnalysisRun(cwd, files);
|
|
4351
|
+
const summaries = {};
|
|
4352
|
+
let completed = 0;
|
|
4353
|
+
for (const filePath of files) {
|
|
4354
|
+
const absolutePath = path11.join(cwd, filePath);
|
|
4355
|
+
let content;
|
|
4356
|
+
try {
|
|
4357
|
+
content = fs10.readFileSync(absolutePath, "utf-8");
|
|
4358
|
+
} catch {
|
|
4359
|
+
completed++;
|
|
4360
|
+
continue;
|
|
4361
|
+
}
|
|
4362
|
+
if (content.length > 5e4) {
|
|
4363
|
+
summaries[filePath] = `Large file (${Math.round(content.length / 1e3)}KB). Skipped detailed analysis.`;
|
|
4364
|
+
completeTask(tasksDir, filePath, summaries[filePath]);
|
|
4365
|
+
completed++;
|
|
4366
|
+
updateManifestProgress(runDir, completed);
|
|
4367
|
+
continue;
|
|
4368
|
+
}
|
|
4369
|
+
try {
|
|
4370
|
+
const messages = [
|
|
4371
|
+
{
|
|
4372
|
+
role: "system",
|
|
4373
|
+
content: "You are a code analyst. Respond with ONLY a 2-3 sentence summary. No formatting, no headers, no bullets. Just plain sentences."
|
|
4374
|
+
},
|
|
4375
|
+
{
|
|
4376
|
+
role: "user",
|
|
4377
|
+
content: `Summarize this file. What does it do, what does it export, and what does it depend on?
|
|
4378
|
+
|
|
4379
|
+
File: ${filePath}
|
|
4380
|
+
|
|
4381
|
+
${content}`
|
|
4382
|
+
}
|
|
4383
|
+
];
|
|
4384
|
+
const summary = await callLocalModel(config.localEndpoint, messages);
|
|
4385
|
+
summaries[filePath] = summary.trim();
|
|
4386
|
+
completeTask(tasksDir, filePath, summary.trim());
|
|
4387
|
+
} catch {
|
|
4388
|
+
summaries[filePath] = "Could not summarize.";
|
|
4389
|
+
}
|
|
4390
|
+
completed++;
|
|
4391
|
+
updateManifestProgress(runDir, completed);
|
|
4392
|
+
}
|
|
4393
|
+
let dependencies = {};
|
|
4394
|
+
try {
|
|
4395
|
+
const summaryContext = Object.entries(summaries).map(([fp, summary]) => `[${fp}]: ${summary}`).join("\n\n");
|
|
4396
|
+
const messages = [
|
|
4397
|
+
{
|
|
4398
|
+
role: "system",
|
|
4399
|
+
content: "You are a senior software architect. Respond with ONLY a valid JSON object. No explanation, no markdown."
|
|
4400
|
+
},
|
|
4401
|
+
{
|
|
4402
|
+
role: "user",
|
|
4403
|
+
content: `Based on these file summaries, generate a JSON dependency map. Each key is a file path, each value is an array of file paths it depends on.
|
|
4404
|
+
|
|
4405
|
+
FILE SUMMARIES:
|
|
4406
|
+
${summaryContext}
|
|
4407
|
+
|
|
4408
|
+
Respond with ONLY the JSON object:`
|
|
4409
|
+
}
|
|
4410
|
+
];
|
|
4411
|
+
const depResponse = await callLocalModel(config.localEndpoint, messages);
|
|
4412
|
+
const jsonMatch = depResponse.match(/\{[\s\S]*\}/);
|
|
4413
|
+
if (jsonMatch) {
|
|
4414
|
+
dependencies = JSON.parse(jsonMatch[0]);
|
|
4415
|
+
}
|
|
4416
|
+
} catch {
|
|
4417
|
+
dependencies = {};
|
|
4418
|
+
}
|
|
4419
|
+
saveSummaries(cwd, summaries);
|
|
4420
|
+
saveDependencies(cwd, dependencies);
|
|
4421
|
+
updateManifestProgress(runDir, completed, "completed");
|
|
4422
|
+
return {
|
|
4423
|
+
success: true,
|
|
4424
|
+
message: `Indexed ${Object.keys(summaries).length} files in ${projectName}. Dependency map generated.`,
|
|
4425
|
+
fileCount: Object.keys(summaries).length,
|
|
4426
|
+
projectName
|
|
4427
|
+
};
|
|
4428
|
+
} catch (error) {
|
|
4429
|
+
return { success: false, error: error.message };
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
async function executeAnalyse(payload, config) {
|
|
4433
|
+
if (!config.localEndpoint) {
|
|
4434
|
+
return { success: false, error: "No local endpoint configured. Analysis requires a local model." };
|
|
4435
|
+
}
|
|
4436
|
+
const cwd = process.cwd();
|
|
4437
|
+
const projectName = getProjectName(cwd);
|
|
4438
|
+
try {
|
|
4439
|
+
const summaries = loadSummaries(cwd);
|
|
4440
|
+
if (!summaries || Object.keys(summaries).length === 0) {
|
|
4441
|
+
return {
|
|
4442
|
+
success: false,
|
|
4443
|
+
error: `Project not indexed. Run \`bob remote index\` first, or \`bob index\` locally.`
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4446
|
+
const dependencies = loadDependencies(cwd) || {};
|
|
4447
|
+
const files = Object.keys(summaries);
|
|
4448
|
+
const { analysisDir } = ensureProjectStructure(cwd);
|
|
4449
|
+
const resultsDir = path11.join(analysisDir, "results");
|
|
4450
|
+
if (!fs10.existsSync(resultsDir)) fs10.mkdirSync(resultsDir, { recursive: true });
|
|
4451
|
+
const allResults = {};
|
|
4452
|
+
let totalBugs = 0;
|
|
4453
|
+
let totalFeatures = 0;
|
|
4454
|
+
let totalImprovements = 0;
|
|
4455
|
+
let totalUpgrades = 0;
|
|
4456
|
+
for (const filePath of files) {
|
|
4457
|
+
const absolutePath = path11.join(cwd, filePath);
|
|
4458
|
+
let content;
|
|
4459
|
+
try {
|
|
4460
|
+
content = fs10.readFileSync(absolutePath, "utf-8");
|
|
4461
|
+
} catch {
|
|
4462
|
+
continue;
|
|
4463
|
+
}
|
|
4464
|
+
if (content.length > 3e4) continue;
|
|
4465
|
+
const fileDeps = dependencies[filePath] || [];
|
|
4466
|
+
let depContext = "";
|
|
4467
|
+
if (fileDeps.length > 0) {
|
|
4468
|
+
depContext = `
|
|
4469
|
+
RELATED FILES:
|
|
4470
|
+
${fileDeps.map((d) => `- ${d}: ${summaries[d] || "unknown"}`).join("\n")}
|
|
4471
|
+
`;
|
|
4472
|
+
}
|
|
4473
|
+
const analysisPrompt = `You are the Lead QA Engineer on this project. Perform a thorough code review.
|
|
4474
|
+
|
|
4475
|
+
For each issue provide: title, description (why it's a problem), priority (critical/high/medium/low), implementation (exact fix steps).
|
|
4476
|
+
|
|
4477
|
+
Respond with ONLY a JSON object:
|
|
4478
|
+
{
|
|
4479
|
+
"bugs": [{"title": "...", "description": "...", "priority": "...", "implementation": "..."}],
|
|
4480
|
+
"features": [{"title": "...", "description": "...", "priority": "...", "implementation": "..."}],
|
|
4481
|
+
"improvements": [{"title": "...", "description": "...", "priority": "...", "implementation": "..."}],
|
|
4482
|
+
"upgrades": [{"title": "...", "description": "...", "priority": "...", "implementation": "..."}]
|
|
4483
|
+
}
|
|
4484
|
+
${depContext}
|
|
4485
|
+
FILE: ${filePath}
|
|
4486
|
+
${content}`;
|
|
4487
|
+
try {
|
|
4488
|
+
const messages = [
|
|
4489
|
+
{ role: "system", content: "You are the Lead QA Engineer. Respond with ONLY valid JSON. Quality over quantity." },
|
|
4490
|
+
{ role: "user", content: analysisPrompt }
|
|
4491
|
+
];
|
|
4492
|
+
const responseText = await callLocalModel(config.localEndpoint, messages);
|
|
4493
|
+
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
|
4494
|
+
if (jsonMatch) {
|
|
4495
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
4496
|
+
for (const cat of ["bugs", "features", "improvements", "upgrades"]) {
|
|
4497
|
+
if (parsed[cat]) {
|
|
4498
|
+
parsed[cat] = parsed[cat].map((item) => ({ ...item, filePath }));
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
allResults[filePath] = parsed;
|
|
4502
|
+
totalBugs += parsed.bugs?.length || 0;
|
|
4503
|
+
totalFeatures += parsed.features?.length || 0;
|
|
4504
|
+
totalImprovements += parsed.improvements?.length || 0;
|
|
4505
|
+
totalUpgrades += parsed.upgrades?.length || 0;
|
|
4506
|
+
}
|
|
4507
|
+
} catch {
|
|
4508
|
+
continue;
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
fs10.writeFileSync(
|
|
4512
|
+
path11.join(resultsDir, "analysis.json"),
|
|
4513
|
+
JSON.stringify(allResults, null, 2)
|
|
4514
|
+
);
|
|
4515
|
+
fs10.writeFileSync(
|
|
4516
|
+
path11.join(resultsDir, "counts.json"),
|
|
4517
|
+
JSON.stringify({ bugs: totalBugs, features: totalFeatures, improvements: totalImprovements, upgrades: totalUpgrades }, null, 2)
|
|
4518
|
+
);
|
|
4519
|
+
return {
|
|
4520
|
+
success: true,
|
|
4521
|
+
message: `Analysis complete for ${projectName}. Found: ${totalBugs} bugs, ${totalFeatures} features, ${totalImprovements} improvements, ${totalUpgrades} upgrades.`,
|
|
4522
|
+
counts: { bugs: totalBugs, features: totalFeatures, improvements: totalImprovements, upgrades: totalUpgrades },
|
|
4523
|
+
projectName
|
|
4524
|
+
};
|
|
4525
|
+
} catch (error) {
|
|
4526
|
+
return { success: false, error: error.message };
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
async function executeBackup(payload, config) {
|
|
4530
|
+
const { isSource = false, isGlobal = false, archiveName } = payload;
|
|
4531
|
+
if (!config.uid) {
|
|
4532
|
+
return { success: false, error: "User UID not available. Re-login required." };
|
|
4533
|
+
}
|
|
4534
|
+
const cwd = process.cwd();
|
|
4535
|
+
const projectName = path11.basename(cwd);
|
|
4536
|
+
let sourceDir;
|
|
4537
|
+
let displayLabel;
|
|
4538
|
+
if (isGlobal) {
|
|
4539
|
+
sourceDir = BOB_DIR3;
|
|
4540
|
+
displayLabel = "global (~/.bob/)";
|
|
4541
|
+
} else if (isSource) {
|
|
4542
|
+
sourceDir = cwd;
|
|
4543
|
+
displayLabel = `source: ${projectName}`;
|
|
4544
|
+
} else {
|
|
4545
|
+
sourceDir = path11.join(BOB_DIR3, "projects", projectName);
|
|
4546
|
+
displayLabel = `context: ${projectName}`;
|
|
4547
|
+
}
|
|
4548
|
+
if (!fs10.existsSync(sourceDir)) {
|
|
4549
|
+
return { success: false, error: `Source directory not found: ${sourceDir}` };
|
|
4550
|
+
}
|
|
4551
|
+
const tmpDir = getTempDir();
|
|
4552
|
+
const archivePath = path11.join(tmpDir, "bob-backup.tar.gz");
|
|
4553
|
+
const encryptedPath = path11.join(tmpDir, "bob-backup.bob.enc");
|
|
4554
|
+
try {
|
|
4555
|
+
const tar = await import("tar");
|
|
4556
|
+
const relativeSource = path11.relative(os3.homedir(), sourceDir);
|
|
4557
|
+
await tar.create(
|
|
4558
|
+
{ gzip: true, file: archivePath, cwd: os3.homedir() },
|
|
4559
|
+
[relativeSource]
|
|
4560
|
+
);
|
|
4561
|
+
const archiveStats = fs10.statSync(archivePath);
|
|
4562
|
+
const estimatedSizeGB = archiveStats.size / (1024 * 1024 * 1024);
|
|
4563
|
+
const sizeLabel = archiveStats.size < 1024 * 1024 ? `${(archiveStats.size / 1024).toFixed(1)} KB` : `${(archiveStats.size / (1024 * 1024)).toFixed(1)} MB`;
|
|
4564
|
+
encrypt(archivePath, encryptedPath, config.uid);
|
|
4565
|
+
let uploadResult;
|
|
4566
|
+
const action = archiveName ? isSource ? "requestSourceArchiveUpload" : "requestArchiveUpload" : isSource ? "requestSourceUpload" : "requestUpload";
|
|
4567
|
+
uploadResult = await callCloudFunction("cliBackupLicense", {
|
|
4568
|
+
action,
|
|
4569
|
+
projectName,
|
|
4570
|
+
isGlobal,
|
|
4571
|
+
isSource,
|
|
4572
|
+
archiveName: archiveName || null,
|
|
4573
|
+
estimatedSizeGB
|
|
4574
|
+
});
|
|
4575
|
+
const encryptedData = fs10.readFileSync(encryptedPath);
|
|
4576
|
+
await axios.put(uploadResult.uploadUrl, encryptedData, {
|
|
4577
|
+
headers: {
|
|
4578
|
+
"Content-Type": "application/octet-stream",
|
|
4579
|
+
"Content-Length": encryptedData.length
|
|
4580
|
+
},
|
|
4581
|
+
maxBodyLength: Infinity,
|
|
4582
|
+
maxContentLength: Infinity
|
|
4583
|
+
});
|
|
4584
|
+
const recordAction = archiveName ? isSource ? "recordSourceArchiveUsage" : "recordArchiveUsage" : isSource ? "recordSourceUsage" : "recordUsage";
|
|
4585
|
+
try {
|
|
4586
|
+
await callCloudFunction("cliBackupLicense", {
|
|
4587
|
+
action: recordAction,
|
|
4588
|
+
projectName,
|
|
4589
|
+
isGlobal,
|
|
4590
|
+
isSource,
|
|
4591
|
+
archiveId: uploadResult.archiveId || null
|
|
4592
|
+
});
|
|
4593
|
+
} catch {
|
|
4594
|
+
}
|
|
4595
|
+
const label = archiveName ? `archive "${archiveName}"` : "backup";
|
|
4596
|
+
return {
|
|
4597
|
+
success: true,
|
|
4598
|
+
message: `Remote ${label} complete for ${displayLabel}. Size: ${sizeLabel}.`,
|
|
4599
|
+
sizeBytes: archiveStats.size,
|
|
4600
|
+
projectName,
|
|
4601
|
+
isSource,
|
|
4602
|
+
isGlobal
|
|
4603
|
+
};
|
|
4604
|
+
} catch (error) {
|
|
4605
|
+
return { success: false, error: `Backup failed: ${error.message}` };
|
|
4606
|
+
} finally {
|
|
4607
|
+
cleanupTemp(tmpDir);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
async function executeRestore(payload, config) {
|
|
4611
|
+
const { isSource = false, isGlobal = false } = payload;
|
|
4612
|
+
if (!config.uid) {
|
|
4613
|
+
return { success: false, error: "User UID not available. Re-login required." };
|
|
4614
|
+
}
|
|
4615
|
+
const cwd = process.cwd();
|
|
4616
|
+
const projectName = path11.basename(cwd);
|
|
4617
|
+
const tmpDir = getTempDir();
|
|
4618
|
+
const downloadPath = path11.join(tmpDir, "bob-backup.bob.enc");
|
|
4619
|
+
const decryptedPath = path11.join(tmpDir, "bob-backup.tar.gz");
|
|
4620
|
+
try {
|
|
4621
|
+
const downloadResult = await callCloudFunction("cliBackupLicense", {
|
|
4622
|
+
action: isSource ? "requestSourceDownload" : "requestDownload",
|
|
4623
|
+
projectName,
|
|
4624
|
+
isGlobal,
|
|
4625
|
+
isSource,
|
|
4626
|
+
s3VersionId: null
|
|
4627
|
+
// always latest in headless mode
|
|
4628
|
+
});
|
|
4629
|
+
const response = await axios.get(downloadResult.downloadUrl, {
|
|
4630
|
+
responseType: "arraybuffer",
|
|
4631
|
+
maxContentLength: Infinity
|
|
4632
|
+
});
|
|
4633
|
+
fs10.writeFileSync(downloadPath, Buffer.from(response.data));
|
|
4634
|
+
decrypt(downloadPath, decryptedPath, config.uid);
|
|
4635
|
+
let restoreTarget;
|
|
4636
|
+
if (isGlobal) {
|
|
4637
|
+
restoreTarget = BOB_DIR3;
|
|
4638
|
+
} else if (isSource) {
|
|
4639
|
+
restoreTarget = cwd;
|
|
4640
|
+
} else {
|
|
4641
|
+
restoreTarget = path11.join(BOB_DIR3, "projects", projectName);
|
|
4642
|
+
}
|
|
4643
|
+
const preRestoreBackup = `${restoreTarget}-pre-restore-${Date.now()}`;
|
|
4644
|
+
if (fs10.existsSync(restoreTarget)) {
|
|
4645
|
+
fs10.cpSync(restoreTarget, preRestoreBackup, { recursive: true });
|
|
4646
|
+
}
|
|
4647
|
+
const tar = await import("tar");
|
|
4648
|
+
if (isSource) {
|
|
4649
|
+
const parentDir = path11.dirname(cwd);
|
|
4650
|
+
await tar.extract({ file: decryptedPath, cwd: parentDir });
|
|
4651
|
+
} else {
|
|
4652
|
+
await tar.extract({ file: decryptedPath, cwd: os3.homedir() });
|
|
4653
|
+
}
|
|
4654
|
+
const scopeLabel = isGlobal ? "global (~/.bob/)" : isSource ? `source: ${projectName}` : `context: ${projectName}`;
|
|
4655
|
+
return {
|
|
4656
|
+
success: true,
|
|
4657
|
+
message: `Remote restore complete for ${scopeLabel}. Pre-restore backup saved locally.`,
|
|
4658
|
+
projectName,
|
|
4659
|
+
isSource,
|
|
4660
|
+
isGlobal,
|
|
4661
|
+
preRestoreBackup: path11.basename(preRestoreBackup)
|
|
4662
|
+
};
|
|
4663
|
+
} catch (error) {
|
|
4664
|
+
return { success: false, error: `Restore failed: ${error.message}` };
|
|
4665
|
+
} finally {
|
|
4666
|
+
cleanupTemp(tmpDir);
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4300
4669
|
|
|
4301
4670
|
// src/commands/remote.ts
|
|
4302
4671
|
import chalk20 from "chalk";
|
|
@@ -4311,7 +4680,7 @@ var ERROR12 = chalk20.hex("#EF5350");
|
|
|
4311
4680
|
var MUTED14 = chalk20.hex("#78909C");
|
|
4312
4681
|
var BORDER6 = chalk20.hex("#455A64");
|
|
4313
4682
|
function registerRemoteCommand(program2) {
|
|
4314
|
-
program2.command("remote [type] [message]").description("Send commands to an Active Bob on a remote machine").option("--new", "Discover and connect to a different Active Bob").option("--auto", "For analyse: run auto-fix mode").option("-i, --interactive", "Enter interactive remote session").option("--session <id>", "Target a specific Active Bob session").action(async (type, message, options) => {
|
|
4683
|
+
program2.command("remote [type] [message]").description("Send commands to an Active Bob on a remote machine").option("--new", "Discover and connect to a different Active Bob").option("--auto", "For analyse: run auto-fix mode").option("-i, --interactive", "Enter interactive remote session").option("--session <id>", "Target a specific Active Bob session").option("--source", "For backup/restore: use source code mode").option("--global", "For backup/restore: use global mode (Grid only)").option("--archive <name>", "For backup: save as named archive").action(async (type, message, options) => {
|
|
4315
4684
|
const config = getConfig();
|
|
4316
4685
|
if (!config.loggedIn || !config.authToken) {
|
|
4317
4686
|
console.log("");
|
|
@@ -4332,7 +4701,16 @@ function registerRemoteCommand(program2) {
|
|
|
4332
4701
|
await showConnectionStatus(config);
|
|
4333
4702
|
return;
|
|
4334
4703
|
}
|
|
4335
|
-
const validTypes = [
|
|
4704
|
+
const validTypes = [
|
|
4705
|
+
"chat",
|
|
4706
|
+
"consult",
|
|
4707
|
+
"index",
|
|
4708
|
+
"analyse",
|
|
4709
|
+
"push",
|
|
4710
|
+
"autonomy",
|
|
4711
|
+
"backup",
|
|
4712
|
+
"restore"
|
|
4713
|
+
];
|
|
4336
4714
|
if (!validTypes.includes(type)) {
|
|
4337
4715
|
console.log("");
|
|
4338
4716
|
console.log(ERROR12(` \u274C Invalid command type: "${type}"`));
|
|
@@ -4343,6 +4721,9 @@ function registerRemoteCommand(program2) {
|
|
|
4343
4721
|
const payload = { conversationId: config.conversationId };
|
|
4344
4722
|
if (message) payload.message = message;
|
|
4345
4723
|
if (options.auto) payload.auto = true;
|
|
4724
|
+
if (options.source) payload.isSource = true;
|
|
4725
|
+
if (options.global) payload.isGlobal = true;
|
|
4726
|
+
if (options.archive) payload.archiveName = options.archive;
|
|
4346
4727
|
if ((type === "chat" || type === "consult" || type === "push") && !message) {
|
|
4347
4728
|
console.log("");
|
|
4348
4729
|
console.log(ERROR12(` \u274C ${type} requires a message.`));
|
|
@@ -4378,17 +4759,22 @@ async function runInteractiveRemote(config, targetSession) {
|
|
|
4378
4759
|
}
|
|
4379
4760
|
console.log("");
|
|
4380
4761
|
console.log(BORDER6(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
4381
|
-
console.log(BORDER6(" \u2551") + INFO14(
|
|
4762
|
+
console.log(BORDER6(" \u2551") + INFO14(" \u{1F310} Active Bob \u2014 Remote Session") + MUTED14(` (${activeBobName})`));
|
|
4382
4763
|
console.log(BORDER6(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
4383
4764
|
console.log(BORDER6(" \u2551") + MUTED14(` Conversation: ${config.conversationId?.slice(0, 28)}...`));
|
|
4384
4765
|
console.log(BORDER6(" \u2551") + MUTED14(" Commands dispatched to the remote machine."));
|
|
4385
4766
|
console.log(BORDER6(" \u2551"));
|
|
4386
4767
|
console.log(BORDER6(" \u2551") + chalk20.white(" Slash Commands:"));
|
|
4387
|
-
console.log(BORDER6(" \u2551") + MUTED14(' \u25B8 /consult "msg"
|
|
4388
|
-
console.log(BORDER6(" \u2551") + MUTED14(' \u25B8 /push "msg"
|
|
4389
|
-
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /index
|
|
4390
|
-
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /analyse
|
|
4391
|
-
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /
|
|
4768
|
+
console.log(BORDER6(" \u2551") + MUTED14(' \u25B8 /consult "msg" \u2014 Strategic advice'));
|
|
4769
|
+
console.log(BORDER6(" \u2551") + MUTED14(' \u25B8 /push "msg" \u2014 Git commit & push'));
|
|
4770
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /index \u2014 Re-index project"));
|
|
4771
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /analyse \u2014 Run QA analysis"));
|
|
4772
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /backup \u2014 Backup context"));
|
|
4773
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /backup source \u2014 Backup source code"));
|
|
4774
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /backup global \u2014 Backup ~/.bob/ (Grid)"));
|
|
4775
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /restore \u2014 Restore latest context"));
|
|
4776
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /restore source \u2014 Restore latest source"));
|
|
4777
|
+
console.log(BORDER6(" \u2551") + MUTED14(" \u25B8 /exit \u2014 Disconnect"));
|
|
4392
4778
|
console.log(BORDER6(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
4393
4779
|
console.log("");
|
|
4394
4780
|
const rl = readline7.createInterface({
|
|
@@ -4412,7 +4798,7 @@ async function runInteractiveRemote(config, targetSession) {
|
|
|
4412
4798
|
if (trimmed.startsWith("/consult ")) {
|
|
4413
4799
|
const msg = trimmed.slice(9).trim().replace(/^["']|["']$/g, "");
|
|
4414
4800
|
if (msg) {
|
|
4415
|
-
await dispatchAndShow(config, "consult", { message: msg
|
|
4801
|
+
await dispatchAndShow(config, "consult", { message: msg }, targetSession);
|
|
4416
4802
|
} else {
|
|
4417
4803
|
console.log(ERROR12(' \u274C Provide a message: /consult "your question"'));
|
|
4418
4804
|
}
|
|
@@ -4422,7 +4808,7 @@ async function runInteractiveRemote(config, targetSession) {
|
|
|
4422
4808
|
if (trimmed.startsWith("/push ")) {
|
|
4423
4809
|
const msg = trimmed.slice(6).trim().replace(/^["']|["']$/g, "");
|
|
4424
4810
|
if (msg) {
|
|
4425
|
-
await dispatchAndShow(config, "push", { message: msg
|
|
4811
|
+
await dispatchAndShow(config, "push", { message: msg }, targetSession);
|
|
4426
4812
|
} else {
|
|
4427
4813
|
console.log(ERROR12(' \u274C Provide a commit message: /push "your message"'));
|
|
4428
4814
|
}
|
|
@@ -4430,28 +4816,53 @@ async function runInteractiveRemote(config, targetSession) {
|
|
|
4430
4816
|
return;
|
|
4431
4817
|
}
|
|
4432
4818
|
if (trimmed === "/index") {
|
|
4433
|
-
await dispatchAndShow(config, "index", {
|
|
4819
|
+
await dispatchAndShow(config, "index", {}, targetSession);
|
|
4434
4820
|
prompt();
|
|
4435
4821
|
return;
|
|
4436
4822
|
}
|
|
4437
4823
|
if (trimmed === "/analyse" || trimmed === "/analyze") {
|
|
4438
|
-
await dispatchAndShow(config, "analyse", {
|
|
4824
|
+
await dispatchAndShow(config, "analyse", {}, targetSession);
|
|
4825
|
+
prompt();
|
|
4826
|
+
return;
|
|
4827
|
+
}
|
|
4828
|
+
if (trimmed.startsWith("/backup")) {
|
|
4829
|
+
const parts = trimmed.split(" ");
|
|
4830
|
+
const flag = parts[1]?.toLowerCase();
|
|
4831
|
+
const backupPayload = {};
|
|
4832
|
+
if (flag === "source") backupPayload.isSource = true;
|
|
4833
|
+
if (flag === "global") backupPayload.isGlobal = true;
|
|
4834
|
+
await dispatchAndShow(config, "backup", backupPayload, targetSession);
|
|
4835
|
+
prompt();
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
if (trimmed.startsWith("/restore")) {
|
|
4839
|
+
const parts = trimmed.split(" ");
|
|
4840
|
+
const flag = parts[1]?.toLowerCase();
|
|
4841
|
+
const restorePayload = {};
|
|
4842
|
+
if (flag === "source") restorePayload.isSource = true;
|
|
4843
|
+
if (flag === "global") restorePayload.isGlobal = true;
|
|
4844
|
+
await dispatchAndShow(config, "restore", restorePayload, targetSession);
|
|
4439
4845
|
prompt();
|
|
4440
4846
|
return;
|
|
4441
4847
|
}
|
|
4442
|
-
await dispatchAndShow(config, "chat", { message: trimmed
|
|
4848
|
+
await dispatchAndShow(config, "chat", { message: trimmed }, targetSession);
|
|
4443
4849
|
prompt();
|
|
4444
4850
|
});
|
|
4445
4851
|
};
|
|
4446
4852
|
prompt();
|
|
4447
4853
|
}
|
|
4448
4854
|
async function dispatchAndShow(config, type, payload, targetSession) {
|
|
4449
|
-
const spinner = ora7({
|
|
4855
|
+
const spinner = ora7({
|
|
4856
|
+
text: BRAND_SECONDARY13(` \u{1F4E1} Active Bob executing: ${type}...`),
|
|
4857
|
+
spinner: "dots"
|
|
4858
|
+
}).start();
|
|
4859
|
+
let pollCount = 0;
|
|
4860
|
+
const MAX_POLLS = 300;
|
|
4450
4861
|
try {
|
|
4451
4862
|
const result = await callCloudFunction("sendRemoteCommand", {
|
|
4452
4863
|
conversationId: config.conversationId,
|
|
4453
4864
|
type,
|
|
4454
|
-
payload,
|
|
4865
|
+
payload: { ...payload, conversationId: config.conversationId },
|
|
4455
4866
|
targetSession: targetSession || null
|
|
4456
4867
|
});
|
|
4457
4868
|
if (!result?.success) {
|
|
@@ -4461,7 +4872,8 @@ async function dispatchAndShow(config, type, payload, targetSession) {
|
|
|
4461
4872
|
return;
|
|
4462
4873
|
}
|
|
4463
4874
|
const commandId = result.commandId;
|
|
4464
|
-
while (
|
|
4875
|
+
while (pollCount < MAX_POLLS) {
|
|
4876
|
+
pollCount++;
|
|
4465
4877
|
try {
|
|
4466
4878
|
const pollResult = await callCloudFunction("getRemoteCommandResult", {
|
|
4467
4879
|
conversationId: config.conversationId,
|
|
@@ -4486,6 +4898,14 @@ async function dispatchAndShow(config, type, payload, targetSession) {
|
|
|
4486
4898
|
} else if (pollResult.result?.message) {
|
|
4487
4899
|
console.log("");
|
|
4488
4900
|
console.log(SUCCESS14(` \u2705 ${pollResult.result.message}`));
|
|
4901
|
+
if (pollResult.result?.counts) {
|
|
4902
|
+
const c = pollResult.result.counts;
|
|
4903
|
+
console.log(MUTED14(` \u{1F41B} ${c.bugs} bugs \u2B50 ${c.features} features \u{1F527} ${c.improvements} improvements \u2B06\uFE0F ${c.upgrades} upgrades`));
|
|
4904
|
+
}
|
|
4905
|
+
if (pollResult.result?.sizeBytes) {
|
|
4906
|
+
const sizeLabel = pollResult.result.sizeBytes < 1024 * 1024 ? `${(pollResult.result.sizeBytes / 1024).toFixed(1)} KB` : `${(pollResult.result.sizeBytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4907
|
+
console.log(MUTED14(` \u{1F4E6} Size: ${sizeLabel}`));
|
|
4908
|
+
}
|
|
4489
4909
|
} else if (pollResult.result?.error) {
|
|
4490
4910
|
console.log("");
|
|
4491
4911
|
console.log(ERROR12(` \u274C ${pollResult.result.error}`));
|
|
@@ -4502,8 +4922,13 @@ async function dispatchAndShow(config, type, payload, targetSession) {
|
|
|
4502
4922
|
}
|
|
4503
4923
|
} catch {
|
|
4504
4924
|
}
|
|
4505
|
-
await new Promise((
|
|
4925
|
+
await new Promise((resolve3) => setTimeout(resolve3, 2e3));
|
|
4506
4926
|
}
|
|
4927
|
+
spinner.stop();
|
|
4928
|
+
console.log("");
|
|
4929
|
+
console.log(WARNING13(" \u26A0\uFE0F Command timed out after 10 minutes."));
|
|
4930
|
+
console.log(MUTED14(" The command may still be running on the remote machine."));
|
|
4931
|
+
console.log("");
|
|
4507
4932
|
} catch (error) {
|
|
4508
4933
|
spinner.stop();
|
|
4509
4934
|
console.log(ERROR12(` \u274C ${error.message}`));
|
|
@@ -4546,12 +4971,17 @@ async function showConnectionStatus(config) {
|
|
|
4546
4971
|
console.log("");
|
|
4547
4972
|
if (activeSessions.length > 0) {
|
|
4548
4973
|
console.log(MUTED14(" Commands:"));
|
|
4549
|
-
console.log(MUTED14(" \u25B8 bob remote --interactive
|
|
4550
|
-
console.log(MUTED14(' \u25B8 bob remote chat "message"
|
|
4551
|
-
console.log(MUTED14(' \u25B8 bob remote consult "msg"
|
|
4552
|
-
console.log(MUTED14(' \u25B8 bob remote push "msg"
|
|
4553
|
-
console.log(MUTED14(" \u25B8 bob remote index
|
|
4554
|
-
console.log(MUTED14(" \u25B8 bob remote analyse
|
|
4974
|
+
console.log(MUTED14(" \u25B8 bob remote --interactive \u2014 Persistent session"));
|
|
4975
|
+
console.log(MUTED14(' \u25B8 bob remote chat "message" \u2014 One-shot chat'));
|
|
4976
|
+
console.log(MUTED14(' \u25B8 bob remote consult "msg" \u2014 Strategic advice'));
|
|
4977
|
+
console.log(MUTED14(' \u25B8 bob remote push "msg" \u2014 Git push'));
|
|
4978
|
+
console.log(MUTED14(" \u25B8 bob remote index \u2014 Re-index project"));
|
|
4979
|
+
console.log(MUTED14(" \u25B8 bob remote analyse \u2014 Run QA analysis"));
|
|
4980
|
+
console.log(MUTED14(" \u25B8 bob remote backup \u2014 Backup context"));
|
|
4981
|
+
console.log(MUTED14(" \u25B8 bob remote backup --source \u2014 Backup source code"));
|
|
4982
|
+
console.log(MUTED14(" \u25B8 bob remote backup --global \u2014 Backup ~/.bob/ (Grid)"));
|
|
4983
|
+
console.log(MUTED14(" \u25B8 bob remote restore \u2014 Restore latest"));
|
|
4984
|
+
console.log(MUTED14(" \u25B8 bob remote restore --source \u2014 Restore latest source"));
|
|
4555
4985
|
console.log("");
|
|
4556
4986
|
}
|
|
4557
4987
|
} catch (error) {
|
|
@@ -4586,8 +5016,8 @@ async function discoverAndConnect(config) {
|
|
|
4586
5016
|
console.log(BORDER6(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
4587
5017
|
console.log("");
|
|
4588
5018
|
const rl = readline7.createInterface({ input: process.stdin, output: process.stdout });
|
|
4589
|
-
const answer = await new Promise((
|
|
4590
|
-
rl.question(INFO14(" Select (1-" + bobs.length + ") or 0 to cancel: "),
|
|
5019
|
+
const answer = await new Promise((resolve3) => {
|
|
5020
|
+
rl.question(INFO14(" Select (1-" + bobs.length + ") or 0 to cancel: "), resolve3);
|
|
4591
5021
|
});
|
|
4592
5022
|
rl.close();
|
|
4593
5023
|
const selection = parseInt(answer.trim());
|
|
@@ -5011,7 +5441,7 @@ async function runCloudProfiler(options) {
|
|
|
5011
5441
|
}
|
|
5012
5442
|
}
|
|
5013
5443
|
function sleep5(ms) {
|
|
5014
|
-
return new Promise((
|
|
5444
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5015
5445
|
}
|
|
5016
5446
|
|
|
5017
5447
|
// src/ui/profile-dashboard.ts
|
|
@@ -5293,7 +5723,7 @@ async function renderProfileDashboard() {
|
|
|
5293
5723
|
import * as path12 from "path";
|
|
5294
5724
|
var AMBER5 = chalk23.hex("#FFAB00");
|
|
5295
5725
|
var GREEN4 = chalk23.hex("#66BB6A");
|
|
5296
|
-
var
|
|
5726
|
+
var BLUE2 = chalk23.hex("#42A5F5");
|
|
5297
5727
|
var GRAY4 = chalk23.gray;
|
|
5298
5728
|
var CYAN4 = chalk23.cyan;
|
|
5299
5729
|
var RED4 = chalk23.hex("#EF5350");
|
|
@@ -5744,1187 +6174,720 @@ function getWeekNumber(date) {
|
|
|
5744
6174
|
return Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
5745
6175
|
}
|
|
5746
6176
|
|
|
5747
|
-
// src/commands/
|
|
6177
|
+
// src/commands/backup.ts
|
|
5748
6178
|
import chalk24 from "chalk";
|
|
5749
|
-
import
|
|
5750
|
-
import * as
|
|
6179
|
+
import ora11 from "ora";
|
|
6180
|
+
import * as fs11 from "fs";
|
|
5751
6181
|
import * as path13 from "path";
|
|
5752
6182
|
import * as os4 from "os";
|
|
5753
|
-
|
|
5754
|
-
|
|
6183
|
+
import * as crypto3 from "crypto";
|
|
6184
|
+
import axios2 from "axios";
|
|
6185
|
+
import inquirer from "inquirer";
|
|
6186
|
+
var BRAND = chalk24.hex("#E66F24");
|
|
6187
|
+
var CYAN5 = chalk24.cyan;
|
|
5755
6188
|
var GREEN5 = chalk24.hex("#66BB6A");
|
|
5756
|
-
var CYAN5 = chalk24.hex("#26C6DA");
|
|
5757
6189
|
var RED5 = chalk24.hex("#EF5350");
|
|
6190
|
+
var AMBER6 = chalk24.hex("#FFAB00");
|
|
5758
6191
|
var GRAY5 = chalk24.gray;
|
|
5759
|
-
var BLUE4 = chalk24.hex("#42A5F5");
|
|
5760
|
-
var BOB_COLOR = chalk24.hex("#E66F24");
|
|
5761
6192
|
var BORDER10 = chalk24.hex("#455A64");
|
|
5762
|
-
var
|
|
5763
|
-
var
|
|
5764
|
-
function
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
const
|
|
5770
|
-
const
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
6193
|
+
var BOB_DIR4 = path13.join(os4.homedir(), ".bob");
|
|
6194
|
+
var ALGORITHM2 = "aes-256-cbc";
|
|
6195
|
+
function deriveKey2(uid, salt) {
|
|
6196
|
+
return crypto3.pbkdf2Sync(uid, salt, 1e5, 32, "sha256");
|
|
6197
|
+
}
|
|
6198
|
+
function encrypt2(inputPath, outputPath, uid) {
|
|
6199
|
+
const salt = crypto3.randomBytes(16).toString("hex");
|
|
6200
|
+
const key = deriveKey2(uid, salt);
|
|
6201
|
+
const iv = crypto3.randomBytes(16);
|
|
6202
|
+
const cipher = crypto3.createCipheriv(ALGORITHM2, key, iv);
|
|
6203
|
+
const input = fs11.readFileSync(inputPath);
|
|
6204
|
+
const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
|
|
6205
|
+
const header = Buffer.from(salt + iv.toString("hex"), "utf-8");
|
|
6206
|
+
fs11.writeFileSync(outputPath, Buffer.concat([header, encrypted]));
|
|
6207
|
+
}
|
|
6208
|
+
function decrypt2(inputPath, outputPath, uid) {
|
|
6209
|
+
const data = fs11.readFileSync(inputPath);
|
|
6210
|
+
const header = data.slice(0, 64).toString("utf-8");
|
|
6211
|
+
const salt = header.slice(0, 32);
|
|
6212
|
+
const iv = Buffer.from(header.slice(32, 64), "hex");
|
|
6213
|
+
const encrypted = data.slice(64);
|
|
6214
|
+
const key = deriveKey2(uid, salt);
|
|
6215
|
+
const decipher = crypto3.createDecipheriv(ALGORITHM2, key, iv);
|
|
6216
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
6217
|
+
fs11.writeFileSync(outputPath, decrypted);
|
|
6218
|
+
}
|
|
6219
|
+
function formatBytes(bytes) {
|
|
6220
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
6221
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
6222
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
6223
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
6224
|
+
}
|
|
6225
|
+
function getTempDir2() {
|
|
6226
|
+
const tmpDir = path13.join(os4.tmpdir(), `bob-backup-${Date.now()}`);
|
|
6227
|
+
fs11.mkdirSync(tmpDir, { recursive: true });
|
|
6228
|
+
return tmpDir;
|
|
6229
|
+
}
|
|
6230
|
+
function cleanupTemp2(tmpDir) {
|
|
5777
6231
|
try {
|
|
5778
|
-
|
|
6232
|
+
fs11.rmSync(tmpDir, { recursive: true, force: true });
|
|
5779
6233
|
} catch {
|
|
5780
|
-
return null;
|
|
5781
6234
|
}
|
|
5782
6235
|
}
|
|
5783
|
-
function
|
|
5784
|
-
|
|
5785
|
-
|
|
6236
|
+
function requireAuth(config) {
|
|
6237
|
+
if (!config.loggedIn || !config.authToken) {
|
|
6238
|
+
console.log("");
|
|
6239
|
+
console.log(RED5(" \u274C Backup requires authentication."));
|
|
6240
|
+
console.log(GRAY5(" Run `bob login` to authenticate."));
|
|
6241
|
+
console.log("");
|
|
6242
|
+
return false;
|
|
6243
|
+
}
|
|
6244
|
+
return true;
|
|
6245
|
+
}
|
|
6246
|
+
function getCurrentProjectName() {
|
|
6247
|
+
return path13.basename(process.cwd());
|
|
5786
6248
|
}
|
|
5787
|
-
function
|
|
5788
|
-
|
|
5789
|
-
console.log("");
|
|
5790
|
-
console.log(BORDER10(" \u2500\u2500\u2500 MISSION CONTROL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5791
|
-
console.log(
|
|
5792
|
-
` SAT: ${satBar} \u2192 ${target}% \u2502 STAG: ${stag}/${stagTarget > 0 ? stagTarget : "\u221E"} \u2502 DIV: ${div}/${divTarget > 0 ? divTarget : "\u221E"} \u2502 GRADE: ${grading}`
|
|
5793
|
-
);
|
|
5794
|
-
console.log(BORDER10(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5795
|
-
console.log("");
|
|
6249
|
+
function getProjectBackupDir(projectName) {
|
|
6250
|
+
return path13.join(BOB_DIR4, "projects", projectName);
|
|
5796
6251
|
}
|
|
5797
|
-
function
|
|
5798
|
-
return
|
|
6252
|
+
function normalizeFilePath(filePath) {
|
|
6253
|
+
return filePath.replace(/\\/g, "/");
|
|
5799
6254
|
}
|
|
5800
|
-
function
|
|
5801
|
-
|
|
5802
|
-
const maxWidth = 70;
|
|
5803
|
-
const lines = wrapText2(cleanMsg, maxWidth - 4);
|
|
5804
|
-
if (sender === "userBob") {
|
|
5805
|
-
const topBar = PURPLE(` \u250C\u2500 UserBob ${"\u2500".repeat(maxWidth - 13)}\u2510`);
|
|
5806
|
-
const bottomBar = PURPLE(` \u2514${"\u2500".repeat(maxWidth - 2)}\u2518`);
|
|
6255
|
+
function handleBackupError(error) {
|
|
6256
|
+
if (error.message?.includes("BOB_BACKUP_LICENSE_REQUIRED")) {
|
|
5807
6257
|
console.log("");
|
|
5808
|
-
console.log(
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
console.log(PURPLE(" \u2502") + ` ${padded}` + PURPLE(" \u2502"));
|
|
5812
|
-
}
|
|
5813
|
-
console.log(bottomBar);
|
|
5814
|
-
if (audit) {
|
|
5815
|
-
const chips = [];
|
|
5816
|
-
if (audit.satisfactionScore !== void 0) chips.push(CYAN5(`[SAT: ${audit.satisfactionScore}%]`));
|
|
5817
|
-
if (audit.resemblanceScore !== void 0) chips.push(BLUE4(`[RES: ${audit.resemblanceScore}%]`));
|
|
5818
|
-
if (audit.reasoning) chips.push(GRAY5(`[${String(audit.reasoning).slice(0, 50)}...]`));
|
|
5819
|
-
if (chips.length > 0) console.log(" " + chips.join(" "));
|
|
5820
|
-
}
|
|
5821
|
-
} else if (sender === "bob") {
|
|
5822
|
-
const indent = " ";
|
|
5823
|
-
const topBar = BOB_COLOR(`${indent}\u250C${"\u2500".repeat(maxWidth - 12)}\u2500 Bob \u2500\u2510`);
|
|
5824
|
-
const bottomBar = BOB_COLOR(`${indent}\u2514${"\u2500".repeat(maxWidth - 2)}\u2518`);
|
|
6258
|
+
console.log(AMBER6(" \u26A0\uFE0F No active backup license found."));
|
|
6259
|
+
console.log(GRAY5(" Purchase one at: app.bobsworkshop.com/iap \u2192 Bob Backup"));
|
|
6260
|
+
} else if (error.message?.includes("STORAGE_QUOTA_EXCEEDED")) {
|
|
5825
6261
|
console.log("");
|
|
5826
|
-
console.log(
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
console.log(BOB_COLOR(`${indent}\u2502`) + ` ${padded}` + BOB_COLOR(" \u2502"));
|
|
5830
|
-
}
|
|
5831
|
-
console.log(bottomBar);
|
|
5832
|
-
} else if (sender === "system") {
|
|
6262
|
+
console.log(AMBER6(" \u26A0\uFE0F Storage quota exceeded."));
|
|
6263
|
+
console.log(GRAY5(" Purchase a storage pack: app.bobsworkshop.com/iap \u2192 Storage Packs"));
|
|
6264
|
+
} else if (error.message?.includes("ARCHIVE_SLOTS_EXHAUSTED")) {
|
|
5833
6265
|
console.log("");
|
|
5834
|
-
console.log(
|
|
5835
|
-
console.log(GRAY5(
|
|
5836
|
-
|
|
6266
|
+
console.log(AMBER6(" \u26A0\uFE0F All archive slots are used."));
|
|
6267
|
+
console.log(GRAY5(" Upgrade your Workshop SKU for more archive slots."));
|
|
6268
|
+
} else if (error.message?.includes("GRID_REQUIRED")) {
|
|
6269
|
+
console.log("");
|
|
6270
|
+
console.log(AMBER6(" \u26A0\uFE0F Global backup requires the Grid Workshop plan."));
|
|
6271
|
+
console.log(GRAY5(" Upgrade at: app.bobsworkshop.com/iap \u2192 Workshop"));
|
|
5837
6272
|
} else {
|
|
5838
6273
|
console.log("");
|
|
5839
|
-
console.log(
|
|
6274
|
+
console.log(RED5(` \u274C ${error.message}`));
|
|
5840
6275
|
}
|
|
6276
|
+
console.log("");
|
|
5841
6277
|
}
|
|
5842
|
-
function
|
|
5843
|
-
const
|
|
5844
|
-
const
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
6278
|
+
function loadGitignorePatterns(projectDir) {
|
|
6279
|
+
const gitignorePath = path13.join(projectDir, ".gitignore");
|
|
6280
|
+
const defaultIgnore = [
|
|
6281
|
+
"node_modules",
|
|
6282
|
+
".git",
|
|
6283
|
+
"dist",
|
|
6284
|
+
"build",
|
|
6285
|
+
".dart_tool",
|
|
6286
|
+
".idea",
|
|
6287
|
+
".gradle",
|
|
6288
|
+
".pub-cache",
|
|
6289
|
+
"*.log",
|
|
6290
|
+
".env",
|
|
6291
|
+
".env.local",
|
|
6292
|
+
".env.*",
|
|
6293
|
+
"coverage",
|
|
6294
|
+
".nyc_output"
|
|
6295
|
+
];
|
|
6296
|
+
if (!fs11.existsSync(gitignorePath)) return defaultIgnore;
|
|
6297
|
+
try {
|
|
6298
|
+
const lines = fs11.readFileSync(gitignorePath, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
6299
|
+
return [...defaultIgnore, ...lines];
|
|
6300
|
+
} catch {
|
|
6301
|
+
return defaultIgnore;
|
|
5861
6302
|
}
|
|
5862
|
-
return lines;
|
|
5863
6303
|
}
|
|
5864
|
-
|
|
5865
|
-
const
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
6304
|
+
function shouldIgnore(filePath, patterns) {
|
|
6305
|
+
const parts = filePath.split(/[/\\]/);
|
|
6306
|
+
return patterns.some((pattern) => {
|
|
6307
|
+
const clean = pattern.replace(/^\//, "").replace(/\/$/, "");
|
|
6308
|
+
return parts.some((part) => {
|
|
6309
|
+
if (clean.includes("*")) {
|
|
6310
|
+
const regex = new RegExp("^" + clean.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$");
|
|
6311
|
+
return regex.test(part);
|
|
6312
|
+
}
|
|
6313
|
+
return part === clean;
|
|
6314
|
+
});
|
|
6315
|
+
});
|
|
6316
|
+
}
|
|
6317
|
+
async function runBackup(options) {
|
|
6318
|
+
const { config, projectName, isGlobal, archiveName, sourceDir, displayName } = options;
|
|
6319
|
+
console.log("");
|
|
6320
|
+
console.log(BORDER10(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
6321
|
+
console.log(BORDER10(" \u2551") + BRAND(" \u2601\uFE0F Bob Backup ") + BORDER10("\u2551"));
|
|
6322
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6323
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Scope: ${isGlobal ? "\u{1F310} Global (~/.bob/)" : `\u{1F4C1} Project: ${projectName}`}`));
|
|
6324
|
+
if (archiveName) console.log(BORDER10(" \u2551") + GRAY5(` Archive: "${archiveName}"`));
|
|
6325
|
+
console.log(BORDER10(" \u2551"));
|
|
6326
|
+
if (!fs11.existsSync(sourceDir)) {
|
|
6327
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6328
|
+
console.log("");
|
|
6329
|
+
console.log(RED5(` \u274C Source directory not found: ${sourceDir}`));
|
|
6330
|
+
console.log(GRAY5(` Run \`bob index\` first to initialize this project.`));
|
|
6331
|
+
console.log("");
|
|
5883
6332
|
return;
|
|
5884
6333
|
}
|
|
5885
|
-
const
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
const
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
6334
|
+
const tmpDir = getTempDir2();
|
|
6335
|
+
const archivePath = path13.join(tmpDir, "bob-backup.tar.gz");
|
|
6336
|
+
const encryptedPath = path13.join(tmpDir, "bob-backup.bob.enc");
|
|
6337
|
+
try {
|
|
6338
|
+
const compressSpinner = ora11({ text: GRAY5(" Compressing ..."), spinner: "dots" }).start();
|
|
6339
|
+
const tar = await import("tar");
|
|
6340
|
+
const relativeSource = path13.relative(os4.homedir(), sourceDir);
|
|
6341
|
+
await tar.create(
|
|
6342
|
+
{ gzip: true, file: archivePath, cwd: os4.homedir() },
|
|
6343
|
+
[relativeSource]
|
|
6344
|
+
);
|
|
6345
|
+
const archiveStats = fs11.statSync(archivePath);
|
|
6346
|
+
const estimatedSizeGB = archiveStats.size / (1024 * 1024 * 1024);
|
|
6347
|
+
compressSpinner.succeed(GREEN5(` Compressing ${displayName} ...`) + GRAY5(` ${formatBytes(archiveStats.size)}`));
|
|
6348
|
+
const encryptSpinner = ora11({ text: GRAY5(" Encrypting archive ..."), spinner: "dots" }).start();
|
|
6349
|
+
encrypt2(archivePath, encryptedPath, config.uid);
|
|
6350
|
+
const encryptedStats = fs11.statSync(encryptedPath);
|
|
6351
|
+
encryptSpinner.succeed(GREEN5(" Encrypting archive ...") + GRAY5(` ${formatBytes(encryptedStats.size)}`));
|
|
6352
|
+
const urlSpinner = ora11({ text: GRAY5(" Requesting upload authorization ..."), spinner: "dots" }).start();
|
|
6353
|
+
let uploadResult;
|
|
5895
6354
|
try {
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
try {
|
|
5913
|
-
await callHTTPFunction("userSimManagerService", {
|
|
5914
|
-
action: "injectNote",
|
|
5915
|
-
conversationId,
|
|
5916
|
-
uid: config.uid,
|
|
5917
|
-
email: config.email,
|
|
5918
|
-
note
|
|
5919
|
-
});
|
|
5920
|
-
console.log(GREEN5(` \u2705 Director's note injected.`));
|
|
5921
|
-
} catch (e) {
|
|
5922
|
-
console.log(RED5(` \u274C Failed to inject note: ${e.message}`));
|
|
5923
|
-
}
|
|
5924
|
-
return;
|
|
5925
|
-
}
|
|
5926
|
-
console.log(GRAY5(' Commands: /set grading|target|stag|div <n> /inject "note" /status /abort'));
|
|
5927
|
-
}
|
|
5928
|
-
async function runPlatformSimulation(config, conversationId, mission, params) {
|
|
5929
|
-
await callHTTPFunction("userSimManagerService", {
|
|
5930
|
-
action: "updateParameters",
|
|
5931
|
-
conversationId,
|
|
5932
|
-
uid: config.uid,
|
|
5933
|
-
email: config.email,
|
|
5934
|
-
params: {
|
|
5935
|
-
targetSatisfaction: params.target,
|
|
5936
|
-
gradingStandard: params.grading,
|
|
5937
|
-
stalemateZone: params.stag,
|
|
5938
|
-
divergenceThreshold: params.div
|
|
5939
|
-
}
|
|
5940
|
-
});
|
|
5941
|
-
await callHTTPFunction("userSimManagerService", {
|
|
5942
|
-
action: "injectNote",
|
|
5943
|
-
conversationId,
|
|
5944
|
-
uid: config.uid,
|
|
5945
|
-
email: config.email,
|
|
5946
|
-
note: mission
|
|
5947
|
-
});
|
|
5948
|
-
console.log(GREEN5(" \u2705 Mission injected. Simulation is running."));
|
|
5949
|
-
console.log("");
|
|
5950
|
-
console.log(BORDER10(" \u2500\u2500\u2500 LIVE SIMULATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5951
|
-
console.log(GRAY5(" Messages will stream below as Bob and UserBob interact."));
|
|
5952
|
-
console.log(GRAY5(" You can type commands at any time:"));
|
|
5953
|
-
console.log("");
|
|
5954
|
-
console.log(AMBER6(" /abort") + GRAY5(" \u2014 Stop the simulation immediately"));
|
|
5955
|
-
console.log(AMBER6(" /set target 90") + GRAY5(" \u2014 Update satisfaction target"));
|
|
5956
|
-
console.log(AMBER6(" /set grading 70") + GRAY5(" \u2014 Update Teacher's Curve"));
|
|
5957
|
-
console.log(AMBER6(" /set stag 5") + GRAY5(" \u2014 Update stalemate threshold"));
|
|
5958
|
-
console.log(AMBER6(" /set div 3") + GRAY5(" \u2014 Update divergence threshold"));
|
|
5959
|
-
console.log(AMBER6(' /inject "note"') + GRAY5(" \u2014 Inject a director's note mid-session"));
|
|
5960
|
-
console.log(AMBER6(" /status") + GRAY5(" \u2014 Show current simulation parameters"));
|
|
5961
|
-
console.log("");
|
|
5962
|
-
console.log(BORDER10(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5963
|
-
console.log("");
|
|
5964
|
-
let running = true;
|
|
5965
|
-
let lastMessageTimestamp = 0;
|
|
5966
|
-
let hudState = { sat: 0, target: params.target, stag: 0, stagTarget: params.stag, div: 0, divTarget: params.div, grading: params.grading };
|
|
5967
|
-
const sigintHandler = async () => {
|
|
5968
|
-
if (!running) return;
|
|
5969
|
-
running = false;
|
|
5970
|
-
console.log("\n");
|
|
5971
|
-
console.log(AMBER6(" \u{1F6D1} Aborting simulation..."));
|
|
5972
|
-
try {
|
|
5973
|
-
await callHTTPFunction("userSimManagerService", {
|
|
5974
|
-
action: "abortMission",
|
|
5975
|
-
conversationId,
|
|
5976
|
-
uid: config.uid,
|
|
5977
|
-
email: config.email
|
|
5978
|
-
});
|
|
5979
|
-
console.log(GREEN5(" \u2705 Simulation aborted."));
|
|
5980
|
-
} catch {
|
|
5981
|
-
}
|
|
5982
|
-
process.exit(0);
|
|
5983
|
-
};
|
|
5984
|
-
process.on("SIGINT", sigintHandler);
|
|
5985
|
-
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
5986
|
-
rl.setPrompt("");
|
|
5987
|
-
rl.on("line", async (line) => {
|
|
5988
|
-
const trimmed = line.trim();
|
|
5989
|
-
if (!trimmed) return;
|
|
5990
|
-
if (trimmed === "/abort" || trimmed === "abort") {
|
|
5991
|
-
running = false;
|
|
5992
|
-
console.log(AMBER6(" \u{1F6D1} Aborting simulation..."));
|
|
5993
|
-
try {
|
|
5994
|
-
await callHTTPFunction("userSimManagerService", {
|
|
5995
|
-
action: "abortMission",
|
|
5996
|
-
conversationId,
|
|
5997
|
-
uid: config.uid,
|
|
5998
|
-
email: config.email
|
|
6355
|
+
if (archiveName) {
|
|
6356
|
+
uploadResult = await callCloudFunction("cliBackupLicense", {
|
|
6357
|
+
action: "requestArchiveUpload",
|
|
6358
|
+
projectName,
|
|
6359
|
+
isGlobal,
|
|
6360
|
+
isSource: false,
|
|
6361
|
+
archiveName,
|
|
6362
|
+
estimatedSizeGB
|
|
6363
|
+
});
|
|
6364
|
+
} else {
|
|
6365
|
+
uploadResult = await callCloudFunction("cliBackupLicense", {
|
|
6366
|
+
action: "requestUpload",
|
|
6367
|
+
projectName,
|
|
6368
|
+
isGlobal,
|
|
6369
|
+
isSource: false,
|
|
6370
|
+
estimatedSizeGB
|
|
5999
6371
|
});
|
|
6000
|
-
console.log(GREEN5(" \u2705 Simulation aborted."));
|
|
6001
|
-
} catch {
|
|
6002
6372
|
}
|
|
6003
|
-
|
|
6004
|
-
|
|
6373
|
+
urlSpinner.succeed(GREEN5(" Requesting upload authorization ..."));
|
|
6374
|
+
} catch (error) {
|
|
6375
|
+
urlSpinner.fail(RED5(" \u274C Authorization failed."));
|
|
6376
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6377
|
+
handleBackupError(error);
|
|
6378
|
+
return;
|
|
6005
6379
|
}
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6380
|
+
const uploadSpinner = ora11({ text: GRAY5(" Uploading to S3 ..."), spinner: "dots" }).start();
|
|
6381
|
+
const encryptedData = fs11.readFileSync(encryptedPath);
|
|
6382
|
+
await axios2.put(uploadResult.uploadUrl, encryptedData, {
|
|
6383
|
+
headers: { "Content-Type": "application/octet-stream", "Content-Length": encryptedData.length },
|
|
6384
|
+
maxBodyLength: Infinity,
|
|
6385
|
+
maxContentLength: Infinity
|
|
6386
|
+
});
|
|
6387
|
+
uploadSpinner.succeed(GREEN5(" Uploading to S3 ..."));
|
|
6388
|
+
const recordSpinner = ora11({ text: GRAY5(" Recording usage ..."), spinner: "dots" }).start();
|
|
6010
6389
|
try {
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
if (state.stalemateState) {
|
|
6027
|
-
hudState.stag = state.stalemateState.current ?? hudState.stag;
|
|
6028
|
-
hudState.stagTarget = state.stalemateState.target ?? hudState.stagTarget;
|
|
6029
|
-
}
|
|
6030
|
-
if (state.divergenceState) {
|
|
6031
|
-
hudState.div = state.divergenceState.current ?? hudState.div;
|
|
6032
|
-
hudState.divTarget = state.divergenceState.target ?? hudState.divTarget;
|
|
6033
|
-
}
|
|
6034
|
-
if (state.userBobActive === false || state.simulationStatus && state.simulationStatus !== "RUNNING") {
|
|
6035
|
-
if (messages.length > 0) {
|
|
6036
|
-
renderHUD(hudState.sat, hudState.target, hudState.stag, hudState.stagTarget, hudState.div, hudState.divTarget, hudState.grading);
|
|
6037
|
-
}
|
|
6038
|
-
console.log("");
|
|
6039
|
-
console.log(AMBER6(` \u{1F3C1} Simulation ended: ${state.simulationStatus || "INACTIVE"}`));
|
|
6040
|
-
console.log("");
|
|
6041
|
-
running = false;
|
|
6042
|
-
break;
|
|
6043
|
-
}
|
|
6044
|
-
if (messages.length > 0) {
|
|
6045
|
-
renderHUD(hudState.sat, hudState.target, hudState.stag, hudState.stagTarget, hudState.div, hudState.divTarget, hudState.grading);
|
|
6390
|
+
if (archiveName && uploadResult.archiveId) {
|
|
6391
|
+
await callCloudFunction("cliBackupLicense", {
|
|
6392
|
+
action: "recordArchiveUsage",
|
|
6393
|
+
projectName,
|
|
6394
|
+
isGlobal,
|
|
6395
|
+
isSource: false,
|
|
6396
|
+
archiveId: uploadResult.archiveId
|
|
6397
|
+
});
|
|
6398
|
+
} else {
|
|
6399
|
+
await callCloudFunction("cliBackupLicense", {
|
|
6400
|
+
action: "recordUsage",
|
|
6401
|
+
projectName,
|
|
6402
|
+
isGlobal,
|
|
6403
|
+
isSource: false
|
|
6404
|
+
});
|
|
6046
6405
|
}
|
|
6047
|
-
|
|
6048
|
-
|
|
6406
|
+
recordSpinner.succeed(GREEN5(" Recording usage ..."));
|
|
6407
|
+
} catch {
|
|
6408
|
+
recordSpinner.warn(AMBER6(" Usage recording failed (non-fatal). Backup was saved."));
|
|
6049
6409
|
}
|
|
6410
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
6411
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6412
|
+
if (archiveName) {
|
|
6413
|
+
console.log(BORDER10(" \u2551") + GREEN5(` \u2705 Archive saved: "${archiveName}" \u2014 ${now}`));
|
|
6414
|
+
} else {
|
|
6415
|
+
console.log(BORDER10(" \u2551") + GREEN5(` \u2705 Backup complete \u2014 ${now}`));
|
|
6416
|
+
}
|
|
6417
|
+
console.log(BORDER10(" \u2551") + GRAY5(" Run `bob backup list` to see all revisions."));
|
|
6418
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6419
|
+
console.log("");
|
|
6420
|
+
} catch (error) {
|
|
6421
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6422
|
+
handleBackupError(error);
|
|
6423
|
+
} finally {
|
|
6424
|
+
cleanupTemp2(tmpDir);
|
|
6050
6425
|
}
|
|
6051
|
-
rl.close();
|
|
6052
|
-
process.removeListener("SIGINT", sigintHandler);
|
|
6053
6426
|
}
|
|
6054
|
-
async function
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
process.on("SIGINT", sigintHandler);
|
|
6071
|
-
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
6072
|
-
rl.setPrompt("");
|
|
6073
|
-
rl.on("line", (line) => {
|
|
6074
|
-
const t = line.trim();
|
|
6075
|
-
if (t === "/abort" || t === "abort") {
|
|
6076
|
-
running = false;
|
|
6077
|
-
writeSessionFile({ active: false });
|
|
6078
|
-
clearSessionFile();
|
|
6079
|
-
console.log(AMBER6(" \u{1F6D1} Simulation stopped."));
|
|
6080
|
-
rl.close();
|
|
6081
|
-
process.exit(0);
|
|
6082
|
-
}
|
|
6083
|
-
if (t.startsWith("/set ")) {
|
|
6084
|
-
const m = t.match(/^\/set\s+(grading|target|stag|div)\s+(\d+)$/i);
|
|
6085
|
-
if (m) {
|
|
6086
|
-
const val = parseInt(m[2], 10);
|
|
6087
|
-
if (m[1] === "grading") params.grading = val;
|
|
6088
|
-
if (m[1] === "target") params.target = val;
|
|
6089
|
-
if (m[1] === "stag") params.stag = val;
|
|
6090
|
-
if (m[1] === "div") params.div = val;
|
|
6091
|
-
console.log(GREEN5(` \u2705 ${m[1]} updated to ${val} (local)`));
|
|
6092
|
-
}
|
|
6093
|
-
}
|
|
6094
|
-
if (t === "/status") {
|
|
6427
|
+
async function runSourceBackup(options) {
|
|
6428
|
+
const { config, projectName, projectDir, archiveName } = options;
|
|
6429
|
+
const filePath = options.filePath ? normalizeFilePath(options.filePath) : void 0;
|
|
6430
|
+
const isFileMode = !!filePath;
|
|
6431
|
+
const scopeLabel = isFileMode ? `\u{1F4C4} File: ${filePath}` : `\u{1F4BE} Source: ${projectName}`;
|
|
6432
|
+
console.log("");
|
|
6433
|
+
console.log(BORDER10(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
6434
|
+
console.log(BORDER10(" \u2551") + BRAND(" \u2601\uFE0F Bob Source Backup ") + BORDER10("\u2551"));
|
|
6435
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6436
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Scope: ${scopeLabel}`));
|
|
6437
|
+
if (archiveName) console.log(BORDER10(" \u2551") + GRAY5(` Archive: "${archiveName}"`));
|
|
6438
|
+
console.log(BORDER10(" \u2551"));
|
|
6439
|
+
if (isFileMode) {
|
|
6440
|
+
const absoluteFilePath = path13.resolve(projectDir, filePath);
|
|
6441
|
+
if (!fs11.existsSync(absoluteFilePath)) {
|
|
6442
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6095
6443
|
console.log("");
|
|
6096
|
-
console.log(
|
|
6097
|
-
console.log(GRAY5(` Target: ${params.target} \u2502 Grading: ${params.grading} \u2502 Stag Limit: ${params.stag} \u2502 Div Limit: ${params.div}`));
|
|
6098
|
-
console.log(GRAY5(` Current SAT: ${sat} \u2502 Turns: ${turns} \u2502 Stag: ${stalemateCurrent} \u2502 Div: ${divergenceCurrent}`));
|
|
6444
|
+
console.log(RED5(` \u274C File not found: ${filePath}`));
|
|
6099
6445
|
console.log("");
|
|
6446
|
+
return;
|
|
6100
6447
|
}
|
|
6101
|
-
}
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
After each Bob response, evaluate it 0-100 on how well it advances YOUR mission. Reply with your natural reaction, then append exactly one JSON footer on its own line:
|
|
6110
|
-
{"satisfactionScore": <0-100>, "status": "CONVERGING|STAGNATING|DIVERGING"}` : `You are a digital twin of a software engineer. You have no personal profile loaded \u2014 respond based on the mission context only.
|
|
6111
|
-
|
|
6112
|
-
Mission: ${mission}
|
|
6113
|
-
|
|
6114
|
-
After each Bob response, evaluate it 0-100 on mission alignment. Reply with your reaction, then append exactly one JSON footer on its own line:
|
|
6115
|
-
{"satisfactionScore": <0-100>, "status": "CONVERGING|STAGNATING|DIVERGING"}`;
|
|
6116
|
-
console.log(BORDER10(" \u2500\u2500\u2500 LIVE LOCAL SIMULATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6117
|
-
console.log(GRAY5(" Bob and UserBob will converse autonomously below."));
|
|
6118
|
-
console.log(GRAY5(" Commands:"));
|
|
6119
|
-
console.log(AMBER6(" /abort") + GRAY5(" \u2014 Stop the simulation"));
|
|
6120
|
-
console.log(AMBER6(" /set target 90") + GRAY5(" \u2014 Update satisfaction target"));
|
|
6121
|
-
console.log(AMBER6(" /set grading 70") + GRAY5(" \u2014 Update Teacher's Curve"));
|
|
6122
|
-
console.log(AMBER6(" /set stag 5") + GRAY5(" \u2014 Update stalemate threshold"));
|
|
6123
|
-
console.log(AMBER6(" /set div 3") + GRAY5(" \u2014 Update divergence threshold"));
|
|
6124
|
-
console.log(AMBER6(" /status") + GRAY5(" \u2014 Show current parameters"));
|
|
6125
|
-
console.log(BORDER10(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6126
|
-
console.log("");
|
|
6127
|
-
const kickstart = `Mission received: "${mission}". Bob, what's your first move?`;
|
|
6128
|
-
console.log(PURPLE(" UserBob > ") + WHITE3(kickstart));
|
|
6129
|
-
conversationHistory.push({ role: "user", content: kickstart });
|
|
6130
|
-
while (running) {
|
|
6131
|
-
const session = readSessionFile();
|
|
6132
|
-
if (!session?.active) {
|
|
6133
|
-
running = false;
|
|
6134
|
-
break;
|
|
6448
|
+
} else {
|
|
6449
|
+
if (!fs11.existsSync(projectDir)) {
|
|
6450
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6451
|
+
console.log("");
|
|
6452
|
+
console.log(RED5(` \u274C Project directory not found: ${projectDir}`));
|
|
6453
|
+
console.log("");
|
|
6454
|
+
return;
|
|
6135
6455
|
}
|
|
6136
|
-
|
|
6456
|
+
}
|
|
6457
|
+
const tmpDir = getTempDir2();
|
|
6458
|
+
const archivePath = path13.join(tmpDir, "bob-source.tar.gz");
|
|
6459
|
+
const encryptedPath = path13.join(tmpDir, "bob-source.bob.enc");
|
|
6460
|
+
try {
|
|
6461
|
+
const compressSpinner = ora11({ text: GRAY5(" Compressing source ..."), spinner: "dots" }).start();
|
|
6462
|
+
const tar = await import("tar");
|
|
6463
|
+
if (isFileMode) {
|
|
6464
|
+
const relativeFilePath = path13.normalize(filePath).replace(/\\/g, "/");
|
|
6465
|
+
await tar.create(
|
|
6466
|
+
{ gzip: true, file: archivePath, cwd: projectDir },
|
|
6467
|
+
[relativeFilePath]
|
|
6468
|
+
);
|
|
6469
|
+
} else {
|
|
6470
|
+
const ignorePatterns = loadGitignorePatterns(projectDir);
|
|
6471
|
+
const parentDir = path13.dirname(projectDir);
|
|
6472
|
+
const projectDirName = path13.basename(projectDir);
|
|
6473
|
+
await tar.create(
|
|
6474
|
+
{
|
|
6475
|
+
gzip: true,
|
|
6476
|
+
file: archivePath,
|
|
6477
|
+
cwd: parentDir,
|
|
6478
|
+
filter: (fp) => !shouldIgnore(fp, ignorePatterns)
|
|
6479
|
+
},
|
|
6480
|
+
[projectDirName]
|
|
6481
|
+
);
|
|
6482
|
+
}
|
|
6483
|
+
const archiveStats = fs11.statSync(archivePath);
|
|
6484
|
+
const estimatedSizeGB = archiveStats.size / (1024 * 1024 * 1024);
|
|
6485
|
+
compressSpinner.succeed(
|
|
6486
|
+
GREEN5(` Compressing ${isFileMode ? filePath : projectName} ...`) + GRAY5(` ${formatBytes(archiveStats.size)}`)
|
|
6487
|
+
);
|
|
6488
|
+
const encryptSpinner = ora11({ text: GRAY5(" Encrypting ..."), spinner: "dots" }).start();
|
|
6489
|
+
encrypt2(archivePath, encryptedPath, config.uid);
|
|
6490
|
+
const encryptedStats = fs11.statSync(encryptedPath);
|
|
6491
|
+
encryptSpinner.succeed(GREEN5(" Encrypting ...") + GRAY5(` ${formatBytes(encryptedStats.size)}`));
|
|
6492
|
+
const urlSpinner = ora11({ text: GRAY5(" Requesting upload authorization ..."), spinner: "dots" }).start();
|
|
6493
|
+
let uploadResult;
|
|
6494
|
+
const uploadPayload = {
|
|
6495
|
+
action: archiveName ? "requestSourceArchiveUpload" : "requestSourceUpload",
|
|
6496
|
+
projectName,
|
|
6497
|
+
isGlobal: false,
|
|
6498
|
+
isSource: true,
|
|
6499
|
+
filePath: filePath || null,
|
|
6500
|
+
archiveName: archiveName || null,
|
|
6501
|
+
estimatedSizeGB
|
|
6502
|
+
};
|
|
6137
6503
|
try {
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
const ubMessages = [
|
|
6146
|
-
{ role: "system", content: userBobSystem },
|
|
6147
|
-
...conversationHistory
|
|
6148
|
-
];
|
|
6149
|
-
const ubResponse = await callLocalModel(config.localEndpoint, ubMessages);
|
|
6150
|
-
const jsonMatch = ubResponse.match(/\{[^}]*"satisfactionScore"[^}]*\}/);
|
|
6151
|
-
const cleanResponse = ubResponse.replace(/\{[^}]*"satisfactionScore"[^}]*\}/, "").trim();
|
|
6152
|
-
console.log(PURPLE(" UserBob > ") + WHITE3(cleanResponse));
|
|
6153
|
-
let auditChips = [];
|
|
6154
|
-
if (jsonMatch) {
|
|
6155
|
-
try {
|
|
6156
|
-
const audit = JSON.parse(jsonMatch[0]);
|
|
6157
|
-
const rawScore = audit.satisfactionScore || 0;
|
|
6158
|
-
sat = Math.round(rawScore * (params.grading / 100));
|
|
6159
|
-
lastStatus = audit.status || "";
|
|
6160
|
-
auditChips = [CYAN5(`[SAT: ${sat}%]`), BLUE4(`[RAW: ${rawScore}]`), GRAY5(`[${lastStatus}]`)];
|
|
6161
|
-
if (lastStatus === "STAGNATING") {
|
|
6162
|
-
stalemateCurrent++;
|
|
6163
|
-
if (params.stag > 0 && stalemateCurrent >= params.stag) {
|
|
6164
|
-
console.log(" " + auditChips.join(" "));
|
|
6165
|
-
renderHUD(sat, params.target, stalemateCurrent, params.stag, divergenceCurrent, params.div, params.grading);
|
|
6166
|
-
console.log(AMBER6(` \u{1F3C1} Stalemate threshold reached (${stalemateCurrent}/${params.stag}). Simulation ended.`));
|
|
6167
|
-
running = false;
|
|
6168
|
-
break;
|
|
6169
|
-
}
|
|
6170
|
-
} else if (lastStatus === "DIVERGING") {
|
|
6171
|
-
divergenceCurrent++;
|
|
6172
|
-
stalemateCurrent = 0;
|
|
6173
|
-
if (params.div > 0 && divergenceCurrent >= params.div) {
|
|
6174
|
-
console.log(" " + auditChips.join(" "));
|
|
6175
|
-
renderHUD(sat, params.target, stalemateCurrent, params.stag, divergenceCurrent, params.div, params.grading);
|
|
6176
|
-
console.log(AMBER6(` \u{1F3C1} Divergence threshold reached (${divergenceCurrent}/${params.div}). Simulation ended.`));
|
|
6177
|
-
running = false;
|
|
6178
|
-
break;
|
|
6179
|
-
}
|
|
6180
|
-
} else if (lastStatus === "CONVERGING") {
|
|
6181
|
-
stalemateCurrent = 0;
|
|
6182
|
-
divergenceCurrent = 0;
|
|
6183
|
-
}
|
|
6184
|
-
} catch {
|
|
6185
|
-
}
|
|
6186
|
-
}
|
|
6187
|
-
if (auditChips.length) console.log(" " + auditChips.join(" "));
|
|
6188
|
-
conversationHistory.push({ role: "user", content: ubResponse });
|
|
6189
|
-
writeSessionFile({ active: true, turns, mission, sat });
|
|
6190
|
-
renderHUD(sat, params.target, stalemateCurrent, params.stag, divergenceCurrent, params.div, params.grading);
|
|
6191
|
-
if (sat >= params.target) {
|
|
6192
|
-
console.log(GREEN5(` \u{1F3AF} Target satisfaction ${params.target}% reached! Mission complete.`));
|
|
6193
|
-
running = false;
|
|
6194
|
-
break;
|
|
6195
|
-
}
|
|
6196
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
6197
|
-
} catch (e) {
|
|
6198
|
-
console.log(RED5(` \u274C Local model error: ${e.message}`));
|
|
6199
|
-
console.log(GRAY5(" Retrying in 3 seconds..."));
|
|
6200
|
-
await new Promise((r) => setTimeout(r, 3e3));
|
|
6504
|
+
uploadResult = await callCloudFunction("cliBackupLicense", uploadPayload);
|
|
6505
|
+
urlSpinner.succeed(GREEN5(" Requesting upload authorization ..."));
|
|
6506
|
+
} catch (error) {
|
|
6507
|
+
urlSpinner.fail(RED5(" \u274C Authorization failed."));
|
|
6508
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6509
|
+
handleBackupError(error);
|
|
6510
|
+
return;
|
|
6201
6511
|
}
|
|
6202
|
-
}
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6512
|
+
const uploadSpinner = ora11({ text: GRAY5(" Uploading to S3 ..."), spinner: "dots" }).start();
|
|
6513
|
+
const encryptedData = fs11.readFileSync(encryptedPath);
|
|
6514
|
+
await axios2.put(uploadResult.uploadUrl, encryptedData, {
|
|
6515
|
+
headers: { "Content-Type": "application/octet-stream", "Content-Length": encryptedData.length },
|
|
6516
|
+
maxBodyLength: Infinity,
|
|
6517
|
+
maxContentLength: Infinity
|
|
6518
|
+
});
|
|
6519
|
+
uploadSpinner.succeed(GREEN5(" Uploading to S3 ..."));
|
|
6520
|
+
const recordSpinner = ora11({ text: GRAY5(" Recording usage ..."), spinner: "dots" }).start();
|
|
6521
|
+
const recordPayload = {
|
|
6522
|
+
action: archiveName ? "recordSourceArchiveUsage" : "recordSourceUsage",
|
|
6523
|
+
projectName,
|
|
6524
|
+
isGlobal: false,
|
|
6525
|
+
isSource: true,
|
|
6526
|
+
filePath: filePath || null,
|
|
6527
|
+
archiveId: uploadResult.archiveId || null
|
|
6218
6528
|
};
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6225
|
-
console.log("");
|
|
6226
|
-
const dna = buildDNAString();
|
|
6227
|
-
if (dna) {
|
|
6228
|
-
console.log(GREEN5(" \u2705 Behavioral DNA loaded."));
|
|
6229
|
-
} else {
|
|
6230
|
-
console.log(AMBER6(" \u26A0\uFE0F No behavioral profile found."));
|
|
6231
|
-
console.log(GRAY5(" UserBob performs significantly better with your DNA loaded."));
|
|
6232
|
-
console.log("");
|
|
6233
|
-
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
6234
|
-
const answer = await new Promise((resolve2) => rl.question(AMBER6(" Run `bob profile --today` now? (y/n): "), resolve2));
|
|
6235
|
-
rl.close();
|
|
6236
|
-
if (answer.trim().toLowerCase() === "y") {
|
|
6237
|
-
console.log("");
|
|
6238
|
-
console.log(GRAY5(" Run `bob profile --today` in a separate terminal, then re-run `bob userbob`."));
|
|
6239
|
-
process.exit(0);
|
|
6240
|
-
} else {
|
|
6241
|
-
console.log("");
|
|
6242
|
-
console.log(RED5(" \u26A0\uFE0F Running in Generic Mode \u2014 no behavioral profile loaded."));
|
|
6243
|
-
console.log(RED5(" UserBob will respond using project context only."));
|
|
6244
|
-
console.log(RED5(" Responses won't reflect your personal communication style,"));
|
|
6245
|
-
console.log(RED5(" decision patterns, or engineering philosophy."));
|
|
6246
|
-
console.log(GRAY5(" Run `bob profile --today` anytime to unlock full personalization."));
|
|
6247
|
-
console.log("");
|
|
6248
|
-
}
|
|
6529
|
+
try {
|
|
6530
|
+
await callCloudFunction("cliBackupLicense", recordPayload);
|
|
6531
|
+
recordSpinner.succeed(GREEN5(" Recording usage ..."));
|
|
6532
|
+
} catch {
|
|
6533
|
+
recordSpinner.warn(AMBER6(" Usage recording failed (non-fatal). Backup was saved."));
|
|
6249
6534
|
}
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
console.log(RED5(" \u274C Mission cannot be empty. Exiting."));
|
|
6257
|
-
process.exit(1);
|
|
6258
|
-
}
|
|
6259
|
-
mission = mission.trim();
|
|
6535
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
6536
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6537
|
+
if (archiveName) {
|
|
6538
|
+
console.log(BORDER10(" \u2551") + GREEN5(` \u2705 Source archive saved: "${archiveName}" \u2014 ${now}`));
|
|
6539
|
+
} else {
|
|
6540
|
+
console.log(BORDER10(" \u2551") + GREEN5(` \u2705 Source backup complete \u2014 ${now}`));
|
|
6260
6541
|
}
|
|
6542
|
+
console.log(BORDER10(" \u2551") + GRAY5(` ${isFileMode ? `File: ${filePath}` : `Project: ${projectName} (gitignore respected)`}`));
|
|
6543
|
+
console.log(BORDER10(" \u2551") + GRAY5(" Run `bob backup list --source` to see all source revisions."));
|
|
6544
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6261
6545
|
console.log("");
|
|
6262
|
-
|
|
6263
|
-
console.log("");
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
}
|
|
6546
|
+
} catch (error) {
|
|
6547
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6548
|
+
handleBackupError(error);
|
|
6549
|
+
} finally {
|
|
6550
|
+
cleanupTemp2(tmpDir);
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
function registerBackupCommand(program2) {
|
|
6554
|
+
const backupCmd = program2.command("backup").description("Encrypt and upload your Bob CLI data to secure cloud storage");
|
|
6555
|
+
backupCmd.command("create").description("Create a new encrypted backup").option("--archive <name>", "Save as a named archive snapshot").option("--global", "Back up entire ~/.bob/ (Grid Workshop SKU required)").option("--source", "Back up actual project source code (gitignore respected)").option("--file <path>", "Back up a single file (use with --source)").action(async (options) => {
|
|
6556
|
+
const config = getConfig();
|
|
6557
|
+
if (!requireAuth(config)) return;
|
|
6558
|
+
const projectName = getCurrentProjectName();
|
|
6559
|
+
const projectDir = process.cwd();
|
|
6560
|
+
if (options.source) {
|
|
6561
|
+
await runSourceBackup({
|
|
6562
|
+
config,
|
|
6563
|
+
projectName,
|
|
6564
|
+
projectDir,
|
|
6565
|
+
filePath: options.file,
|
|
6566
|
+
archiveName: options.archive
|
|
6567
|
+
});
|
|
6284
6568
|
return;
|
|
6285
6569
|
}
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6570
|
+
const isGlobal = options.global || false;
|
|
6571
|
+
let sourceDir;
|
|
6572
|
+
let displayName;
|
|
6573
|
+
if (isGlobal) {
|
|
6574
|
+
sourceDir = BOB_DIR4;
|
|
6575
|
+
displayName = "~/.bob/ (global)";
|
|
6576
|
+
} else {
|
|
6577
|
+
sourceDir = getProjectBackupDir(projectName);
|
|
6578
|
+
displayName = `~/.bob/projects/${projectName}/`;
|
|
6290
6579
|
}
|
|
6291
|
-
await
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
var AMBER7 = chalk25.hex("#FFAB00");
|
|
6300
|
-
var GREEN6 = chalk25.hex("#66BB6A");
|
|
6301
|
-
var RED6 = chalk25.hex("#EF5350");
|
|
6302
|
-
var BLUE5 = chalk25.hex("#42A5F5");
|
|
6303
|
-
var PURPLE2 = chalk25.hex("#AB47BC");
|
|
6304
|
-
var CYAN6 = chalk25.hex("#26C6DA");
|
|
6305
|
-
var TEAL = chalk25.hex("#26A69A");
|
|
6306
|
-
var GRAY6 = chalk25.gray;
|
|
6307
|
-
var WHITE4 = chalk25.white;
|
|
6308
|
-
var BORDER11 = chalk25.hex("#455A64");
|
|
6309
|
-
function statusColor(status) {
|
|
6310
|
-
switch (status) {
|
|
6311
|
-
case "queued":
|
|
6312
|
-
return GRAY6;
|
|
6313
|
-
case "awaiting_approval":
|
|
6314
|
-
return AMBER7;
|
|
6315
|
-
case "in_progress":
|
|
6316
|
-
return BLUE5;
|
|
6317
|
-
case "completed":
|
|
6318
|
-
return GREEN6;
|
|
6319
|
-
case "failed":
|
|
6320
|
-
return RED6;
|
|
6321
|
-
case "denied":
|
|
6322
|
-
return RED6;
|
|
6323
|
-
default:
|
|
6324
|
-
return GRAY6;
|
|
6325
|
-
}
|
|
6326
|
-
}
|
|
6327
|
-
function statusLabel(status) {
|
|
6328
|
-
switch (status) {
|
|
6329
|
-
case "queued":
|
|
6330
|
-
return "QUEUED";
|
|
6331
|
-
case "awaiting_approval":
|
|
6332
|
-
return "NEEDS APPROVAL";
|
|
6333
|
-
case "in_progress":
|
|
6334
|
-
return "IN PROGRESS";
|
|
6335
|
-
case "completed":
|
|
6336
|
-
return "COMPLETE";
|
|
6337
|
-
case "failed":
|
|
6338
|
-
return "FAILED";
|
|
6339
|
-
case "denied":
|
|
6340
|
-
return "DENIED";
|
|
6341
|
-
default:
|
|
6342
|
-
return status.toUpperCase();
|
|
6343
|
-
}
|
|
6344
|
-
}
|
|
6345
|
-
function categoryColor(category) {
|
|
6346
|
-
const map = {
|
|
6347
|
-
security: RED6,
|
|
6348
|
-
frontend: BLUE5,
|
|
6349
|
-
backend: GREEN6,
|
|
6350
|
-
cloud_functions: PURPLE2,
|
|
6351
|
-
documentation: TEAL,
|
|
6352
|
-
testing: AMBER7,
|
|
6353
|
-
review: CYAN6,
|
|
6354
|
-
fullstack: ORANGE2
|
|
6355
|
-
};
|
|
6356
|
-
return map[category] || GRAY6;
|
|
6357
|
-
}
|
|
6358
|
-
function confidenceColor(confidence) {
|
|
6359
|
-
if (confidence >= 80) return GREEN6;
|
|
6360
|
-
if (confidence >= 50) return AMBER7;
|
|
6361
|
-
return RED6;
|
|
6362
|
-
}
|
|
6363
|
-
function formatTimestamp(ms) {
|
|
6364
|
-
if (!ms) return "\u2014";
|
|
6365
|
-
return new Date(ms).toLocaleString("en-US", {
|
|
6366
|
-
month: "short",
|
|
6367
|
-
day: "numeric",
|
|
6368
|
-
hour: "numeric",
|
|
6369
|
-
minute: "2-digit",
|
|
6370
|
-
hour12: true
|
|
6371
|
-
});
|
|
6372
|
-
}
|
|
6373
|
-
function stripMarkdown2(text) {
|
|
6374
|
-
return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^---+$/gm, "").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*+]\s+/gm, " \u2022 ").replace(/\n{3,}/g, "\n\n").trim();
|
|
6375
|
-
}
|
|
6376
|
-
function renderStats(stats) {
|
|
6377
|
-
console.log("");
|
|
6378
|
-
console.log(BORDER11(" \u2500\u2500\u2500 COMMAND CENTER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6379
|
-
const parts = [];
|
|
6380
|
-
if (stats.awaiting_approval > 0) parts.push(AMBER7(`${stats.awaiting_approval} PENDING`));
|
|
6381
|
-
if (stats.in_progress > 0) parts.push(BLUE5(`${stats.in_progress} RUNNING`));
|
|
6382
|
-
if (stats.completed > 0) parts.push(GREEN6(`${stats.completed} DONE`));
|
|
6383
|
-
if (stats.failed > 0) parts.push(RED6(`${stats.failed} FAILED`));
|
|
6384
|
-
if (stats.denied > 0) parts.push(GRAY6(`${stats.denied} DENIED`));
|
|
6385
|
-
parts.push(GRAY6(`${stats.total} TOTAL`));
|
|
6386
|
-
console.log(" " + parts.join(GRAY6(" \u2502 ")));
|
|
6387
|
-
console.log(BORDER11(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6388
|
-
console.log("");
|
|
6389
|
-
}
|
|
6390
|
-
function renderTaskDetail(task) {
|
|
6391
|
-
const catColor = categoryColor(task.request.category);
|
|
6392
|
-
const confColor = confidenceColor(task.request.confidence);
|
|
6393
|
-
const sColor = statusColor(task.outcome.status);
|
|
6394
|
-
console.log("");
|
|
6395
|
-
console.log(BORDER11(" \u250C\u2500 TASK DETAIL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
|
|
6396
|
-
console.log(BORDER11(" \u2502"));
|
|
6397
|
-
console.log(BORDER11(" \u2502 ") + WHITE4(task.request.description));
|
|
6398
|
-
console.log(BORDER11(" \u2502"));
|
|
6399
|
-
console.log(BORDER11(" \u2502 ") + catColor(`[${task.request.category.toUpperCase()}]`) + " " + WHITE4(`${task.request.taskType.toUpperCase()}`) + " " + confColor(`${task.request.confidence}% confidence`) + " " + sColor(`\u25CF ${statusLabel(task.outcome.status)}`));
|
|
6400
|
-
if (task.request.targetFile) {
|
|
6401
|
-
console.log(BORDER11(" \u2502 ") + GRAY6("File: ") + CYAN6(task.request.targetFile));
|
|
6402
|
-
}
|
|
6403
|
-
console.log(BORDER11(" \u2502"));
|
|
6404
|
-
console.log(BORDER11(" \u2502 ") + GRAY6("Priority: ") + WHITE4(`P${task.priority}`) + GRAY6(" \u2502 Difficulty: ") + WHITE4(task.difficulty.toUpperCase()) + GRAY6(" \u2502 Created: ") + WHITE4(formatTimestamp(task.createdAt)));
|
|
6405
|
-
console.log(BORDER11(" \u2502"));
|
|
6406
|
-
console.log(BORDER11(" \u2502 ") + AMBER7("TRIGGER"));
|
|
6407
|
-
console.log(BORDER11(" \u2502 ") + GRAY6(stripMarkdown2(task.trigger.reasoning).slice(0, 120) + "..."));
|
|
6408
|
-
if (task.trigger.turnSatisfaction !== null) {
|
|
6409
|
-
console.log(BORDER11(" \u2502 ") + GRAY6("SAT at dispatch: ") + CYAN6(`${task.trigger.turnSatisfaction}%`));
|
|
6410
|
-
}
|
|
6411
|
-
if (task.outcome.resultSummary) {
|
|
6412
|
-
console.log(BORDER11(" \u2502"));
|
|
6413
|
-
console.log(BORDER11(" \u2502 ") + GREEN6("RESULT"));
|
|
6414
|
-
console.log(BORDER11(" \u2502 ") + GRAY6(stripMarkdown2(task.outcome.resultSummary).slice(0, 200) + "..."));
|
|
6415
|
-
}
|
|
6416
|
-
if (task.outcome.filesModified && task.outcome.filesModified.length > 0) {
|
|
6417
|
-
console.log(BORDER11(" \u2502"));
|
|
6418
|
-
console.log(BORDER11(" \u2502 ") + AMBER7("FILES MODIFIED"));
|
|
6419
|
-
for (const file of task.outcome.filesModified) {
|
|
6420
|
-
const icon = file.action === "created" ? GREEN6("+") : AMBER7("~");
|
|
6421
|
-
console.log(BORDER11(" \u2502 ") + icon + " " + CYAN6(file.path || file));
|
|
6422
|
-
}
|
|
6423
|
-
}
|
|
6424
|
-
if (task.outcome.error) {
|
|
6425
|
-
console.log(BORDER11(" \u2502"));
|
|
6426
|
-
console.log(BORDER11(" \u2502 ") + RED6("ERROR"));
|
|
6427
|
-
console.log(BORDER11(" \u2502 ") + RED6(task.outcome.error.slice(0, 150)));
|
|
6428
|
-
}
|
|
6429
|
-
if (task.isDenied && task.denyReason) {
|
|
6430
|
-
console.log(BORDER11(" \u2502"));
|
|
6431
|
-
console.log(BORDER11(" \u2502 ") + RED6("DENIED BY: ") + GRAY6(task.deniedBy || "Unknown"));
|
|
6432
|
-
console.log(BORDER11(" \u2502 ") + RED6("REASON: ") + GRAY6(task.denyReason));
|
|
6433
|
-
}
|
|
6434
|
-
if (task.outcome.turnsUsed || task.outcome.tokensConsumed) {
|
|
6435
|
-
console.log(BORDER11(" \u2502"));
|
|
6436
|
-
if (task.outcome.turnsUsed) {
|
|
6437
|
-
console.log(BORDER11(" \u2502 ") + GRAY6(`Turns: ${task.outcome.turnsUsed} \u2502 Tokens: ${task.outcome.tokensConsumed || 0} \u2502 Provider: ${task.outcome.provider || "unknown"}`));
|
|
6438
|
-
}
|
|
6439
|
-
}
|
|
6440
|
-
console.log(BORDER11(" \u2502"));
|
|
6441
|
-
console.log(BORDER11(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
|
|
6442
|
-
console.log("");
|
|
6443
|
-
}
|
|
6444
|
-
async function runDecisionStream(conversationId) {
|
|
6445
|
-
console.log("");
|
|
6446
|
-
console.log(ORANGE2(" \u2500\u2500\u2500 DECISION STREAM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6447
|
-
console.log(GRAY6(" Live feed of all autonomous decisions. Ctrl+C to exit.\n"));
|
|
6448
|
-
let lastCount = 0;
|
|
6449
|
-
let running = true;
|
|
6450
|
-
process.on("SIGINT", () => {
|
|
6451
|
-
running = false;
|
|
6452
|
-
console.log("\n" + AMBER7(" Stream ended."));
|
|
6453
|
-
process.exit(0);
|
|
6580
|
+
await runBackup({
|
|
6581
|
+
config,
|
|
6582
|
+
projectName,
|
|
6583
|
+
isGlobal,
|
|
6584
|
+
archiveName: options.archive,
|
|
6585
|
+
sourceDir,
|
|
6586
|
+
displayName
|
|
6587
|
+
});
|
|
6454
6588
|
});
|
|
6455
|
-
|
|
6589
|
+
backupCmd.command("list").description("List all backup revisions and named archives").option("--global", "Show global backup revisions").option("--source", "Show source code backup revisions").option("--file <path>", "Show revisions for a specific file (use with --source)").action(async (options) => {
|
|
6590
|
+
const config = getConfig();
|
|
6591
|
+
if (!requireAuth(config)) return;
|
|
6592
|
+
const isGlobal = options.global || false;
|
|
6593
|
+
const isSource = options.source || false;
|
|
6594
|
+
const projectName = getCurrentProjectName();
|
|
6595
|
+
const filePath = options.file ? normalizeFilePath(options.file) : null;
|
|
6596
|
+
const spinner = ora11({ text: CYAN5(" Loading backup history..."), spinner: "dots" }).start();
|
|
6456
6597
|
try {
|
|
6457
|
-
const
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6598
|
+
const result = await callCloudFunction("getCLIBackupStatus", {
|
|
6599
|
+
projectName,
|
|
6600
|
+
isGlobal,
|
|
6601
|
+
isSource,
|
|
6602
|
+
filePath
|
|
6603
|
+
});
|
|
6604
|
+
spinner.stop();
|
|
6605
|
+
const { storage, retention, lastBackedUpAt, versions, archives } = result;
|
|
6606
|
+
const scopeLabel = isGlobal ? "\u{1F310} Global" : isSource ? filePath ? `\u{1F4C4} ${filePath}` : `\u{1F4BE} Source: ${projectName}` : `\u{1F4C1} ${projectName}`;
|
|
6607
|
+
console.log("");
|
|
6608
|
+
console.log(BORDER10(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
6609
|
+
console.log(BORDER10(" \u2551") + BRAND(" \u2601\uFE0F Bob Backup Status ") + BORDER10("\u2551"));
|
|
6610
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6611
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Scope: ${scopeLabel}`));
|
|
6612
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Storage: ${storage.usedGB.toFixed(2)} GB / ${storage.totalGB} GB (${storage.usedPercent}% used)`));
|
|
6613
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Retention: ${retention.months} months`));
|
|
6614
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Archives: ${retention.usedArchiveSlots}/${retention.archiveSlots} slots used`));
|
|
6615
|
+
if (lastBackedUpAt) {
|
|
6616
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Last backup: ${new Date(lastBackedUpAt).toLocaleString()}`));
|
|
6617
|
+
}
|
|
6618
|
+
if (versions.length > 0) {
|
|
6619
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6620
|
+
console.log(BORDER10(" \u2551") + CYAN5(" Revisions"));
|
|
6621
|
+
console.log(BORDER10(" \u2551") + GRAY5(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6622
|
+
for (const v of versions) {
|
|
6623
|
+
const date = new Date(v.lastModified).toLocaleString();
|
|
6624
|
+
const label = v.isLatest ? GREEN5(` ${v.label.padEnd(10)}`) : GRAY5(` ${v.label.padEnd(10)}`);
|
|
6625
|
+
console.log(BORDER10(" \u2551") + `${label} ${GRAY5(formatBytes(v.sizeBytes).padEnd(10))} ${GRAY5(date)}`);
|
|
6626
|
+
}
|
|
6627
|
+
} else {
|
|
6628
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6629
|
+
console.log(BORDER10(" \u2551") + GRAY5(` No revisions found.`));
|
|
6630
|
+
}
|
|
6631
|
+
if (archives.length > 0) {
|
|
6632
|
+
console.log(BORDER10(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
6633
|
+
console.log(BORDER10(" \u2551") + AMBER6(" Named Archives"));
|
|
6634
|
+
console.log(BORDER10(" \u2551") + GRAY5(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6635
|
+
for (const a of archives) {
|
|
6636
|
+
const date = new Date(a.createdAt).toLocaleString();
|
|
6637
|
+
const expires = new Date(a.expiresAt).toLocaleString();
|
|
6638
|
+
console.log(BORDER10(" \u2551") + ` \u{1F4CC} ${AMBER6(a.name.padEnd(24))} ${GRAY5(formatBytes(a.sizeGB * 1024 * 1024 * 1024))}`);
|
|
6639
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Created: ${date} | Expires: ${expires}`));
|
|
6467
6640
|
}
|
|
6468
|
-
lastCount = tasks.length;
|
|
6469
6641
|
}
|
|
6470
|
-
|
|
6471
|
-
console.log(
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
const settings = response?.settings || {};
|
|
6485
|
-
console.log("");
|
|
6486
|
-
console.log(ORANGE2(" \u2500\u2500\u2500 AUTONOMY SETTINGS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6487
|
-
console.log(GRAY6(` Current threshold: ${settings.autonomousConfidenceThreshold ?? 75}%`));
|
|
6488
|
-
console.log(GRAY6(" Tasks below this confidence level require your approval."));
|
|
6489
|
-
console.log("");
|
|
6490
|
-
const { threshold } = await inquirer.prompt([
|
|
6491
|
-
{
|
|
6492
|
-
type: "number",
|
|
6493
|
-
name: "threshold",
|
|
6494
|
-
message: AMBER7(" Confidence threshold (0-100):"),
|
|
6495
|
-
default: settings.autonomousConfidenceThreshold ?? 75,
|
|
6496
|
-
validate: (val) => val >= 0 && val <= 100 ? true : "Must be 0-100"
|
|
6497
|
-
}
|
|
6498
|
-
]);
|
|
6499
|
-
const categories = ["security", "frontend", "backend", "cloud_functions", "documentation", "testing", "review", "fullstack"];
|
|
6500
|
-
const overrides = { ...settings.autonomousCategoryOverrides || {} };
|
|
6501
|
-
console.log("");
|
|
6502
|
-
console.log(GRAY6(" Category overrides:"));
|
|
6503
|
-
console.log("");
|
|
6504
|
-
for (const cat of categories) {
|
|
6505
|
-
const current = overrides[cat] || "threshold";
|
|
6506
|
-
const { override } = await inquirer.prompt([
|
|
6507
|
-
{
|
|
6508
|
-
type: "select",
|
|
6509
|
-
name: "override",
|
|
6510
|
-
message: categoryColor(cat)(` ${cat.padEnd(20)}`),
|
|
6511
|
-
default: current,
|
|
6512
|
-
choices: [
|
|
6513
|
-
{ name: GRAY6("Use Threshold"), value: "threshold" },
|
|
6514
|
-
{ name: GREEN6("Always Auto-Execute"), value: "autonomous" },
|
|
6515
|
-
{ name: RED6("Always Require Approval"), value: "approval_required" }
|
|
6516
|
-
]
|
|
6517
|
-
}
|
|
6518
|
-
]);
|
|
6519
|
-
if (override === "threshold") {
|
|
6520
|
-
delete overrides[cat];
|
|
6521
|
-
} else {
|
|
6522
|
-
overrides[cat] = override;
|
|
6642
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6643
|
+
console.log("");
|
|
6644
|
+
console.log(GRAY5(" Commands:"));
|
|
6645
|
+
console.log(GRAY5(" bob backup create \u2014 Context backup"));
|
|
6646
|
+
console.log(GRAY5(" bob backup create --source \u2014 Full source backup"));
|
|
6647
|
+
console.log(GRAY5(" bob backup create --source --file <path> \u2014 Single file backup"));
|
|
6648
|
+
console.log(GRAY5(" bob backup create --global \u2014 Full ~/.bob/ (Grid)"));
|
|
6649
|
+
console.log(GRAY5(" bob backup restore \u2014 Restore context"));
|
|
6650
|
+
console.log(GRAY5(" bob backup restore --source \u2014 Restore source"));
|
|
6651
|
+
console.log(GRAY5(" bob backup restore --source --file <path> \u2014 Restore single file"));
|
|
6652
|
+
console.log("");
|
|
6653
|
+
} catch (error) {
|
|
6654
|
+
spinner.stop();
|
|
6655
|
+
handleBackupError(error);
|
|
6523
6656
|
}
|
|
6524
|
-
}
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
console.log(AMBER7(" \u26A0\uFE0F `updateCLIAutonomySettings` not deployed yet."));
|
|
6536
|
-
console.log(GRAY6(" Update via web app for now, or deploy the CF first."));
|
|
6537
|
-
}
|
|
6538
|
-
console.log("");
|
|
6539
|
-
}
|
|
6540
|
-
async function runTaskBoard(conversationId) {
|
|
6541
|
-
let continueLoop = true;
|
|
6542
|
-
while (continueLoop) {
|
|
6543
|
-
let response;
|
|
6657
|
+
});
|
|
6658
|
+
backupCmd.command("restore").description("Restore from a backup revision or named archive").option("--global", "Restore from global backup").option("--source", "Restore source code backup").option("--file <path>", "Restore a single file (use with --source)").action(async (options) => {
|
|
6659
|
+
const config = getConfig();
|
|
6660
|
+
if (!requireAuth(config)) return;
|
|
6661
|
+
const isGlobal = options.global || false;
|
|
6662
|
+
const isSource = options.source || false;
|
|
6663
|
+
const projectName = getCurrentProjectName();
|
|
6664
|
+
const projectDir = process.cwd();
|
|
6665
|
+
const filePath = options.file ? normalizeFilePath(options.file) : null;
|
|
6666
|
+
const spinner = ora11({ text: CYAN5(" Loading available backups..."), spinner: "dots" }).start();
|
|
6667
|
+
let statusResult;
|
|
6544
6668
|
try {
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6669
|
+
statusResult = await callCloudFunction("getCLIBackupStatus", {
|
|
6670
|
+
projectName,
|
|
6671
|
+
isGlobal,
|
|
6672
|
+
isSource,
|
|
6673
|
+
filePath
|
|
6674
|
+
});
|
|
6675
|
+
spinner.stop();
|
|
6676
|
+
} catch (error) {
|
|
6677
|
+
spinner.stop();
|
|
6678
|
+
handleBackupError(error);
|
|
6548
6679
|
return;
|
|
6549
6680
|
}
|
|
6550
|
-
const
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
console.log(
|
|
6555
|
-
console.log(GRAY6(" Tasks appear here when UserBob dispatches work to Mini Bob."));
|
|
6681
|
+
const { versions, archives } = statusResult;
|
|
6682
|
+
if (versions.length === 0 && archives.length === 0) {
|
|
6683
|
+
console.log("");
|
|
6684
|
+
const scopeMsg = isSource ? filePath ? `file "${filePath}"` : `source of ${projectName}` : isGlobal ? "global" : projectName;
|
|
6685
|
+
console.log(AMBER6(` \u26A0\uFE0F No backups found for ${scopeMsg}.`));
|
|
6556
6686
|
console.log("");
|
|
6557
6687
|
return;
|
|
6558
6688
|
}
|
|
6559
|
-
const
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6689
|
+
const choices = [];
|
|
6690
|
+
if (versions.length > 0) {
|
|
6691
|
+
choices.push(new inquirer.Separator(CYAN5(" \u2500\u2500 Revisions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
|
|
6692
|
+
for (const v of versions) {
|
|
6693
|
+
const date = new Date(v.lastModified).toLocaleString();
|
|
6694
|
+
choices.push({
|
|
6695
|
+
name: ` ${v.isLatest ? GREEN5("\u25CF ") : " "}${v.label.padEnd(12)} ${GRAY5(formatBytes(v.sizeBytes).padEnd(10))} ${GRAY5(date)}`,
|
|
6696
|
+
value: { type: "revision", versionId: v.versionId, label: v.label }
|
|
6697
|
+
});
|
|
6564
6698
|
}
|
|
6565
6699
|
}
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
const
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
};
|
|
6576
|
-
});
|
|
6577
|
-
taskChoices.push({
|
|
6578
|
-
name: BORDER11(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
6579
|
-
value: "__separator__",
|
|
6580
|
-
short: "",
|
|
6581
|
-
disabled: true
|
|
6582
|
-
});
|
|
6583
|
-
taskChoices.push({
|
|
6584
|
-
name: GRAY6(" \u21A9 Exit Command Center"),
|
|
6585
|
-
value: "__exit__",
|
|
6586
|
-
short: "Exit"
|
|
6587
|
-
});
|
|
6588
|
-
const { selectedTaskId } = await inquirer.prompt([
|
|
6589
|
-
{
|
|
6590
|
-
type: "select",
|
|
6591
|
-
name: "selectedTaskId",
|
|
6592
|
-
message: ORANGE2(" Select a task to inspect:"),
|
|
6593
|
-
choices: taskChoices,
|
|
6594
|
-
pageSize: 14
|
|
6595
|
-
}
|
|
6596
|
-
]);
|
|
6597
|
-
if (selectedTaskId === "__exit__" || selectedTaskId === "__separator__") {
|
|
6598
|
-
continueLoop = false;
|
|
6599
|
-
break;
|
|
6700
|
+
if (archives.length > 0) {
|
|
6701
|
+
choices.push(new inquirer.Separator(AMBER6(" \u2500\u2500 Named Archives \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
|
|
6702
|
+
for (const a of archives) {
|
|
6703
|
+
const date = new Date(a.createdAt).toLocaleString();
|
|
6704
|
+
choices.push({
|
|
6705
|
+
name: ` \u{1F4CC} ${AMBER6(a.name.padEnd(24))} ${GRAY5(date)}`,
|
|
6706
|
+
value: { type: "archive", archiveId: a.archiveId, name: a.name }
|
|
6707
|
+
});
|
|
6708
|
+
}
|
|
6600
6709
|
}
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
const
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
if (
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6710
|
+
choices.push(new inquirer.Separator());
|
|
6711
|
+
choices.push({ name: GRAY5(" \u2190 Cancel"), value: null });
|
|
6712
|
+
console.log("");
|
|
6713
|
+
const scopeLabel = isSource ? filePath ? `file "${filePath}"` : `source: ${projectName}` : isGlobal ? "global" : projectName;
|
|
6714
|
+
const { selected } = await inquirer.prompt([{
|
|
6715
|
+
type: "select",
|
|
6716
|
+
name: "selected",
|
|
6717
|
+
message: CYAN5(` Select a backup to restore (${scopeLabel}):`),
|
|
6718
|
+
choices,
|
|
6719
|
+
pageSize: 12
|
|
6720
|
+
}]);
|
|
6721
|
+
if (!selected) {
|
|
6722
|
+
console.log(GRAY5(" Cancelled."));
|
|
6723
|
+
console.log("");
|
|
6724
|
+
return;
|
|
6725
|
+
}
|
|
6726
|
+
const label = selected.type === "archive" ? `archive "${selected.name}"` : selected.label;
|
|
6727
|
+
console.log("");
|
|
6728
|
+
if (isSource && filePath) {
|
|
6729
|
+
console.log(AMBER6(` \u26A0\uFE0F This will restore ${filePath} from ${label}.`));
|
|
6730
|
+
console.log(GRAY5(" Current file will be backed up to .bob-backups/ first."));
|
|
6731
|
+
} else if (isSource) {
|
|
6732
|
+
console.log(AMBER6(` \u26A0\uFE0F This will restore source code of ${projectName} from ${label}.`));
|
|
6733
|
+
console.log(GRAY5(" Current project will be backed up locally first."));
|
|
6734
|
+
} else {
|
|
6735
|
+
console.log(AMBER6(` \u26A0\uFE0F This will restore ${isGlobal ? "~/.bob/" : `~/.bob/projects/${projectName}/`} from ${label}.`));
|
|
6736
|
+
console.log(GRAY5(" Your current data will be backed up locally first."));
|
|
6737
|
+
}
|
|
6738
|
+
console.log("");
|
|
6739
|
+
const { confirmed } = await inquirer.prompt([{
|
|
6740
|
+
type: "confirm",
|
|
6741
|
+
name: "confirmed",
|
|
6742
|
+
message: AMBER6(" Continue with restore?"),
|
|
6743
|
+
default: false
|
|
6744
|
+
}]);
|
|
6745
|
+
if (!confirmed) {
|
|
6746
|
+
console.log(GRAY5(" Cancelled."));
|
|
6747
|
+
console.log("");
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6750
|
+
const tmpDir = getTempDir2();
|
|
6751
|
+
const downloadPath = path13.join(tmpDir, "bob-backup.bob.enc");
|
|
6752
|
+
const decryptedPath = path13.join(tmpDir, "bob-backup.tar.gz");
|
|
6753
|
+
try {
|
|
6754
|
+
const dlSpinner = ora11({
|
|
6755
|
+
text: GRAY5(` Downloading ${label} ...`),
|
|
6756
|
+
spinner: "dots"
|
|
6757
|
+
}).start();
|
|
6758
|
+
let downloadAction;
|
|
6759
|
+
if (isSource) {
|
|
6760
|
+
downloadAction = selected.type === "archive" ? "requestSourceArchiveDownload" : "requestSourceDownload";
|
|
6761
|
+
} else {
|
|
6762
|
+
downloadAction = selected.type === "archive" ? "requestArchiveDownload" : "requestDownload";
|
|
6763
|
+
}
|
|
6764
|
+
const downloadResult = await callCloudFunction("cliBackupLicense", {
|
|
6765
|
+
action: downloadAction,
|
|
6766
|
+
projectName,
|
|
6767
|
+
isGlobal,
|
|
6768
|
+
isSource,
|
|
6769
|
+
filePath,
|
|
6770
|
+
s3VersionId: selected.type === "revision" ? selected.versionId : null,
|
|
6771
|
+
archiveId: selected.type === "archive" ? selected.archiveId : null
|
|
6772
|
+
});
|
|
6773
|
+
const response = await axios2.get(downloadResult.downloadUrl, {
|
|
6774
|
+
responseType: "arraybuffer",
|
|
6775
|
+
maxContentLength: Infinity
|
|
6776
|
+
});
|
|
6777
|
+
fs11.writeFileSync(downloadPath, Buffer.from(response.data));
|
|
6778
|
+
dlSpinner.succeed(GREEN5(` Downloading ${label} ...`));
|
|
6779
|
+
const decryptSpinner = ora11({ text: GRAY5(" Decrypting ..."), spinner: "dots" }).start();
|
|
6780
|
+
decrypt2(downloadPath, decryptedPath, config.uid);
|
|
6781
|
+
decryptSpinner.succeed(GREEN5(" Decrypting ..."));
|
|
6782
|
+
const backupSpinner = ora11({
|
|
6783
|
+
text: GRAY5(" Backing up current state ..."),
|
|
6784
|
+
spinner: "dots"
|
|
6785
|
+
}).start();
|
|
6786
|
+
let preRestoreBackup;
|
|
6787
|
+
if (isSource && filePath) {
|
|
6788
|
+
const absoluteFilePath = path13.resolve(projectDir, filePath);
|
|
6789
|
+
const backupDir = path13.join(projectDir, ".bob-backups");
|
|
6790
|
+
if (!fs11.existsSync(backupDir)) fs11.mkdirSync(backupDir, { recursive: true });
|
|
6791
|
+
const backupFileName = filePath.replace(/[\/\\]/g, "_") + `.${Date.now()}.bak`;
|
|
6792
|
+
preRestoreBackup = path13.join(backupDir, backupFileName);
|
|
6793
|
+
if (fs11.existsSync(absoluteFilePath)) {
|
|
6794
|
+
fs11.copyFileSync(absoluteFilePath, preRestoreBackup);
|
|
6626
6795
|
}
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
console.log(GRAY6(" Mini Bob is executing. Streaming live logs...\n"));
|
|
6632
|
-
callCloudFunction("approveAutonomousTask", {
|
|
6633
|
-
conversationId,
|
|
6634
|
-
taskId: selectedTask.id,
|
|
6635
|
-
action: "approve"
|
|
6636
|
-
}).then(() => {
|
|
6637
|
-
}).catch((e) => {
|
|
6638
|
-
console.log(RED6(`
|
|
6639
|
-
\u274C Execution error: ${e.message}`));
|
|
6640
|
-
});
|
|
6641
|
-
let running = true;
|
|
6642
|
-
let seenLogIds = /* @__PURE__ */ new Set();
|
|
6643
|
-
let pollErrors = 0;
|
|
6644
|
-
const maxPollErrors = 5;
|
|
6645
|
-
const sigintHandler = () => {
|
|
6646
|
-
running = false;
|
|
6647
|
-
console.log("\n" + AMBER7(" Stream ended. Task continues in background."));
|
|
6648
|
-
process.exit(0);
|
|
6649
|
-
};
|
|
6650
|
-
process.on("SIGINT", sigintHandler);
|
|
6651
|
-
while (running) {
|
|
6652
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
6653
|
-
try {
|
|
6654
|
-
const taskResponse = await callCloudFunction("getCLIAutonomousTasks", {
|
|
6655
|
-
conversationId,
|
|
6656
|
-
statusFilter: null
|
|
6657
|
-
});
|
|
6658
|
-
const updatedTask = taskResponse?.tasks?.find((t) => t.id === selectedTask.id);
|
|
6659
|
-
const logResponse = await callCloudFunction("getCLITaskExecutionLog", {
|
|
6660
|
-
conversationId,
|
|
6661
|
-
taskId: selectedTask.id
|
|
6662
|
-
});
|
|
6663
|
-
const logEntries = logResponse?.entries || [];
|
|
6664
|
-
for (const entry of logEntries) {
|
|
6665
|
-
if (seenLogIds.has(entry.id)) continue;
|
|
6666
|
-
seenLogIds.add(entry.id);
|
|
6667
|
-
const stage = entry.stage || "EXECUTION";
|
|
6668
|
-
let prefix;
|
|
6669
|
-
switch (stage) {
|
|
6670
|
-
case "INIT":
|
|
6671
|
-
prefix = CYAN6(" [INIT] ");
|
|
6672
|
-
break;
|
|
6673
|
-
case "TOOL_CALL":
|
|
6674
|
-
prefix = AMBER7(" [TOOL] ");
|
|
6675
|
-
break;
|
|
6676
|
-
case "FALLBACK":
|
|
6677
|
-
prefix = AMBER7(" [FALLBACK] ");
|
|
6678
|
-
break;
|
|
6679
|
-
case "COMPLETE":
|
|
6680
|
-
prefix = GREEN6(" [DONE] ");
|
|
6681
|
-
break;
|
|
6682
|
-
case "ERROR":
|
|
6683
|
-
prefix = RED6(" [ERROR] ");
|
|
6684
|
-
break;
|
|
6685
|
-
case "APPROVED":
|
|
6686
|
-
prefix = GREEN6(" [APPROVED] ");
|
|
6687
|
-
break;
|
|
6688
|
-
case "DENIED":
|
|
6689
|
-
prefix = RED6(" [DENIED] ");
|
|
6690
|
-
break;
|
|
6691
|
-
case "AWAITING_APPROVAL":
|
|
6692
|
-
prefix = AMBER7(" [PENDING] ");
|
|
6693
|
-
break;
|
|
6694
|
-
default:
|
|
6695
|
-
prefix = GRAY6(" [LOG] ");
|
|
6696
|
-
break;
|
|
6697
|
-
}
|
|
6698
|
-
console.log(prefix + WHITE4(entry.text));
|
|
6699
|
-
}
|
|
6700
|
-
if (updatedTask) {
|
|
6701
|
-
const status = updatedTask.outcome.status;
|
|
6702
|
-
if (status === "completed") {
|
|
6703
|
-
console.log("");
|
|
6704
|
-
console.log(GREEN6(" \u2705 Task complete!"));
|
|
6705
|
-
if (updatedTask.outcome.filesModified?.length > 0) {
|
|
6706
|
-
console.log("");
|
|
6707
|
-
console.log(AMBER7(" Files modified:"));
|
|
6708
|
-
for (const file of updatedTask.outcome.filesModified) {
|
|
6709
|
-
const icon = file.action === "created" ? GREEN6("+") : AMBER7("~");
|
|
6710
|
-
console.log(` ${icon} ${CYAN6(file.path || file)}`);
|
|
6711
|
-
}
|
|
6712
|
-
}
|
|
6713
|
-
if (updatedTask.outcome.resultSummary) {
|
|
6714
|
-
console.log("");
|
|
6715
|
-
console.log(AMBER7(" Summary:"));
|
|
6716
|
-
const summary = stripMarkdown2(updatedTask.outcome.resultSummary);
|
|
6717
|
-
const lines = summary.split("\n").slice(0, 8);
|
|
6718
|
-
for (const line of lines) {
|
|
6719
|
-
console.log(GRAY6(` ${line}`));
|
|
6720
|
-
}
|
|
6721
|
-
}
|
|
6722
|
-
if (updatedTask.outcome.turnsUsed) {
|
|
6723
|
-
console.log("");
|
|
6724
|
-
console.log(GRAY6(` Turns: ${updatedTask.outcome.turnsUsed} \u2502 Tokens: ${updatedTask.outcome.tokensConsumed || 0} \u2502 Provider: ${updatedTask.outcome.provider || "unknown"}`));
|
|
6725
|
-
}
|
|
6726
|
-
running = false;
|
|
6727
|
-
} else if (status === "failed") {
|
|
6728
|
-
console.log("");
|
|
6729
|
-
console.log(RED6(` \u274C Task failed: ${updatedTask.outcome.error || "Unknown error"}`));
|
|
6730
|
-
running = false;
|
|
6731
|
-
} else if (status === "denied") {
|
|
6732
|
-
console.log("");
|
|
6733
|
-
console.log(RED6(" \u274C Task was denied."));
|
|
6734
|
-
running = false;
|
|
6735
|
-
}
|
|
6736
|
-
}
|
|
6737
|
-
pollErrors = 0;
|
|
6738
|
-
} catch (e) {
|
|
6739
|
-
pollErrors++;
|
|
6740
|
-
if (pollErrors >= maxPollErrors) {
|
|
6741
|
-
console.log(RED6(` \u274C Lost connection after ${maxPollErrors} errors. Task continues in background.`));
|
|
6742
|
-
running = false;
|
|
6743
|
-
}
|
|
6744
|
-
}
|
|
6796
|
+
} else if (isSource) {
|
|
6797
|
+
preRestoreBackup = `${projectDir}-pre-restore-${Date.now()}`;
|
|
6798
|
+
if (fs11.existsSync(projectDir)) {
|
|
6799
|
+
fs11.cpSync(projectDir, preRestoreBackup, { recursive: true });
|
|
6745
6800
|
}
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
{
|
|
6752
|
-
type: "input",
|
|
6753
|
-
name: "reason",
|
|
6754
|
-
message: AMBER7(" Denial reason (optional):"),
|
|
6755
|
-
default: ""
|
|
6801
|
+
} else {
|
|
6802
|
+
const restoreTarget = isGlobal ? BOB_DIR4 : getProjectBackupDir(projectName);
|
|
6803
|
+
preRestoreBackup = `${restoreTarget}-pre-restore-${Date.now()}`;
|
|
6804
|
+
if (fs11.existsSync(restoreTarget)) {
|
|
6805
|
+
fs11.cpSync(restoreTarget, preRestoreBackup, { recursive: true });
|
|
6756
6806
|
}
|
|
6757
|
-
]);
|
|
6758
|
-
try {
|
|
6759
|
-
console.log(GRAY6(" Denying task..."));
|
|
6760
|
-
await callCloudFunction("approveAutonomousTask", {
|
|
6761
|
-
conversationId,
|
|
6762
|
-
taskId: selectedTask.id,
|
|
6763
|
-
action: "deny",
|
|
6764
|
-
reason: reason.trim() || null
|
|
6765
|
-
});
|
|
6766
|
-
console.log("");
|
|
6767
|
-
console.log(RED6(" \u274C Task denied."));
|
|
6768
|
-
console.log("");
|
|
6769
|
-
} catch (e) {
|
|
6770
|
-
console.log(RED6(` \u274C Failed to deny: ${e.message}`));
|
|
6771
6807
|
}
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6808
|
+
backupSpinner.succeed(
|
|
6809
|
+
GREEN5(" Backing up current state ...") + GRAY5(` \u2192 ${path13.basename(preRestoreBackup)}`)
|
|
6810
|
+
);
|
|
6811
|
+
const extractSpinner = ora11({ text: GRAY5(" Extracting ..."), spinner: "dots" }).start();
|
|
6812
|
+
const tar = await import("tar");
|
|
6813
|
+
if (isSource && filePath) {
|
|
6814
|
+
await tar.extract({ file: decryptedPath, cwd: projectDir });
|
|
6815
|
+
} else if (isSource) {
|
|
6816
|
+
const parentDir = path13.dirname(projectDir);
|
|
6817
|
+
await tar.extract({ file: decryptedPath, cwd: parentDir });
|
|
6818
|
+
} else {
|
|
6819
|
+
await tar.extract({ file: decryptedPath, cwd: os4.homedir() });
|
|
6820
|
+
}
|
|
6821
|
+
extractSpinner.succeed(GREEN5(" Extracting ..."));
|
|
6781
6822
|
console.log("");
|
|
6782
|
-
console.log(
|
|
6783
|
-
console.log(
|
|
6823
|
+
console.log(BORDER10(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
6824
|
+
console.log(BORDER10(" \u2551") + GREEN5(" \u2705 Restore complete. ") + BORDER10("\u2551"));
|
|
6825
|
+
if (isSource && filePath) {
|
|
6826
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Restored: ${filePath}`));
|
|
6827
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Backup: .bob-backups/${path13.basename(preRestoreBackup)}`));
|
|
6828
|
+
} else if (isSource) {
|
|
6829
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Restored: ${projectName}/ source`));
|
|
6830
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Backup: ${path13.basename(preRestoreBackup)}/`));
|
|
6831
|
+
} else {
|
|
6832
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Restored: ${isGlobal ? "~/.bob/" : `~/.bob/projects/${projectName}/`}`));
|
|
6833
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Backup: ${path13.basename(preRestoreBackup)}`));
|
|
6834
|
+
}
|
|
6835
|
+
console.log(BORDER10(" \u2551") + GRAY5(` From: ${label}`));
|
|
6836
|
+
console.log(BORDER10(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6784
6837
|
console.log("");
|
|
6785
|
-
|
|
6786
|
-
}
|
|
6787
|
-
const config = getConfig();
|
|
6788
|
-
const conversationId = config.conversationId;
|
|
6789
|
-
if (!conversationId) {
|
|
6838
|
+
} catch (error) {
|
|
6790
6839
|
console.log("");
|
|
6791
|
-
console.log(
|
|
6792
|
-
console.log(
|
|
6840
|
+
console.log(RED5(` \u274C Restore failed: ${error.message}`));
|
|
6841
|
+
console.log(GRAY5(" Your original data was not modified."));
|
|
6793
6842
|
console.log("");
|
|
6794
|
-
|
|
6843
|
+
} finally {
|
|
6844
|
+
cleanupTemp2(tmpDir);
|
|
6795
6845
|
}
|
|
6846
|
+
});
|
|
6847
|
+
backupCmd.action(async () => {
|
|
6848
|
+
const config = getConfig();
|
|
6849
|
+
if (!requireAuth(config)) return;
|
|
6850
|
+
const projectName = getCurrentProjectName();
|
|
6851
|
+
console.log("");
|
|
6852
|
+
console.log(GRAY5(` Current project: ${projectName}`));
|
|
6853
|
+
console.log("");
|
|
6854
|
+
console.log(GRAY5(" Bob Backup commands:"));
|
|
6855
|
+
console.log("");
|
|
6856
|
+
console.log(GRAY5(" bob backup create \u2014 Context backup (Bob data)"));
|
|
6857
|
+
console.log(GRAY5(" bob backup create --source \u2014 Source code backup"));
|
|
6858
|
+
console.log(GRAY5(" bob backup create --source --file <path> \u2014 Single file backup"));
|
|
6859
|
+
console.log(GRAY5(' bob backup create --archive "name" \u2014 Named archive'));
|
|
6860
|
+
console.log(GRAY5(" bob backup create --global \u2014 Full ~/.bob/ (Grid only)"));
|
|
6861
|
+
console.log(GRAY5(" bob backup list \u2014 List context revisions"));
|
|
6862
|
+
console.log(GRAY5(" bob backup list --source \u2014 List source revisions"));
|
|
6863
|
+
console.log(GRAY5(" bob backup list --source --file <path> \u2014 List file revisions"));
|
|
6864
|
+
console.log(GRAY5(" bob backup restore \u2014 Restore context"));
|
|
6865
|
+
console.log(GRAY5(" bob backup restore --source \u2014 Restore source code"));
|
|
6866
|
+
console.log(GRAY5(" bob backup restore --source --file <path> \u2014 Restore single file"));
|
|
6867
|
+
console.log(GRAY5(" bob backup restore --global \u2014 Restore full ~/.bob/"));
|
|
6796
6868
|
console.log("");
|
|
6797
|
-
console.log(BORDER11(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
6798
|
-
console.log(BORDER11(" \u2551") + ORANGE2(" \u25C9 AUTONOMOUS COMMAND CENTER"));
|
|
6799
|
-
console.log(BORDER11(" \u2551") + GRAY6(` Conversation: ${conversationId.slice(0, 24)}...`));
|
|
6800
|
-
console.log(BORDER11(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
6801
|
-
if (options.stream) {
|
|
6802
|
-
await runDecisionStream(conversationId);
|
|
6803
|
-
return;
|
|
6804
|
-
}
|
|
6805
|
-
if (options.settings) {
|
|
6806
|
-
await runSettings(conversationId);
|
|
6807
|
-
return;
|
|
6808
|
-
}
|
|
6809
|
-
await runTaskBoard(conversationId);
|
|
6810
6869
|
});
|
|
6811
6870
|
}
|
|
6812
6871
|
|
|
6813
6872
|
// bin/bob.ts
|
|
6814
|
-
var BRAND_PRIMARY11 = chalk26.hex("#E66F24");
|
|
6815
|
-
var BRAND_SECONDARY15 = chalk26.hex("#FFAB00");
|
|
6816
|
-
var SUCCESS16 = chalk26.hex("#66BB6A");
|
|
6817
|
-
var INFO16 = chalk26.hex("#26C6DA");
|
|
6818
|
-
var MUTED16 = chalk26.hex("#78909C");
|
|
6819
|
-
var MODE_CONSULTANT9 = chalk26.hex("#AB47BC");
|
|
6820
6873
|
var program = new Command();
|
|
6821
|
-
program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.
|
|
6822
|
-
program.option("-h, --help", "Print this usage information").on("option:help", () => {
|
|
6823
|
-
printCustomHelp();
|
|
6824
|
-
process.exit(0);
|
|
6825
|
-
});
|
|
6826
|
-
program.command("help").description("Display help for Bob's CLI").action(() => {
|
|
6827
|
-
printCustomHelp();
|
|
6828
|
-
});
|
|
6829
|
-
function printCustomHelp() {
|
|
6830
|
-
console.log("");
|
|
6831
|
-
console.log(BRAND_PRIMARY11(" \u25C9 Bob's CLI") + MUTED16(" \u2014 Your AI Engineering Partner, In Your Terminal."));
|
|
6832
|
-
console.log("");
|
|
6833
|
-
console.log(chalk26.white(" Common commands:"));
|
|
6834
|
-
console.log("");
|
|
6835
|
-
console.log(BRAND_SECONDARY15(' bob chat "message"'));
|
|
6836
|
-
console.log(MUTED16(" Chat with Bob \u2014 code-friendly engineering partner with file awareness."));
|
|
6837
|
-
console.log("");
|
|
6838
|
-
console.log(BRAND_SECONDARY15(' bob consult "message"'));
|
|
6839
|
-
console.log(MUTED16(" Strategic advice only \u2014 no code, just architectural guidance."));
|
|
6840
|
-
console.log("");
|
|
6841
|
-
console.log(BRAND_SECONDARY15(" bob index"));
|
|
6842
|
-
console.log(MUTED16(" Index your project \u2014 generates summaries and dependency map for context."));
|
|
6843
|
-
console.log("");
|
|
6844
|
-
console.log(chalk26.white(" Usage: ") + INFO16("bob <command> [arguments]"));
|
|
6845
|
-
console.log("");
|
|
6846
|
-
console.log(chalk26.white(" Global options:"));
|
|
6847
|
-
console.log(MUTED16(" -h, --help Print this usage information."));
|
|
6848
|
-
console.log(MUTED16(" -V, --version Output the version number."));
|
|
6849
|
-
console.log("");
|
|
6850
|
-
console.log(chalk26.white(" Available commands:"));
|
|
6851
|
-
console.log("");
|
|
6852
|
-
console.log(INFO16(" Conversation"));
|
|
6853
|
-
printCmd("chat [message]", "Chat with Bob \u2014 code-friendly engineering partner");
|
|
6854
|
-
printCmd("consult [message]", "Strategic advice only, no code output");
|
|
6855
|
-
printCmd("conversations", "List, search, and join existing conversations");
|
|
6856
|
-
printCmd("fork <title>", "Branch conversation into a focused sub-project");
|
|
6857
|
-
printCmd("forks", "List all forks of the current conversation");
|
|
6858
|
-
printCmd("deepdive", "Sandboxed exploration on a specific Bob message");
|
|
6859
|
-
printCmd("deepdives", "List all deep dives in the current conversation");
|
|
6860
|
-
printCmd("deepdives-join", "Re-enter an existing deep dive");
|
|
6861
|
-
console.log("");
|
|
6862
|
-
console.log(SUCCESS16(" Project Tools"));
|
|
6863
|
-
printCmd("index", "Index your project \u2014 AI-powered summaries + dependency map");
|
|
6864
|
-
printCmd("analyse", "Full QA code review \u2014 bugs, features, improvements, upgrades");
|
|
6865
|
-
printCmd("analyse --auto", "Auto-fix mode \u2014 Bob triages, MiniBob implements");
|
|
6866
|
-
printCmd("autonomy", "Full autonomous repair across entire codebase");
|
|
6867
|
-
printCmd("push <message>", "Git stage + commit + push in one command");
|
|
6868
|
-
console.log("");
|
|
6869
|
-
console.log(BRAND_PRIMARY11(" Remote (SovereignLink)"));
|
|
6870
|
-
printCmd("serve", "Start Active Bob \u2014 receive commands from any device");
|
|
6871
|
-
printCmd("remote [type] [msg]", "Send commands to a remote Active Bob");
|
|
6872
|
-
console.log("");
|
|
6873
|
-
console.log(MODE_CONSULTANT9(" Profile & Identity"));
|
|
6874
|
-
printCmd("profile", "View your behavioral DNA dashboard");
|
|
6875
|
-
printCmd("profile --cloud", "Generate cloud-powered daily profile");
|
|
6876
|
-
printCmd("userbob", "Launch your UserBob digital twin");
|
|
6877
|
-
printCmd("byok", "Manage Bring Your Own Key configuration");
|
|
6878
|
-
printCmd("command-center", "Autonomous Command Center \u2014 inspect and approve tasks");
|
|
6879
|
-
printCmd("cc", "Alias for command-center");
|
|
6880
|
-
console.log("");
|
|
6881
|
-
console.log(MUTED16(" Configuration"));
|
|
6882
|
-
printCmd("config show", "Display current configuration");
|
|
6883
|
-
printCmd("config set <key> <val>", "Update a configuration value");
|
|
6884
|
-
printCmd("login", "Authenticate with Bob's Workshop via browser");
|
|
6885
|
-
printCmd("logout", "Sign out and clear stored credentials");
|
|
6886
|
-
printCmd("whoami", "Show current auth status and project info");
|
|
6887
|
-
console.log("");
|
|
6888
|
-
console.log(chalk26.white(" Interactive slash commands ") + MUTED16("(inside a chat/consult session):"));
|
|
6889
|
-
console.log("");
|
|
6890
|
-
printCmd("/exit", "End the session");
|
|
6891
|
-
printCmd("/new", "Start a fresh conversation");
|
|
6892
|
-
printCmd("/clear", "Clear the terminal");
|
|
6893
|
-
printCmd("/include <path>", "Load a file into active context");
|
|
6894
|
-
printCmd("/delete <path>", "Delete a file (with backup)");
|
|
6895
|
-
printCmd("/deepdive", "Deep dive on the last Bob message");
|
|
6896
|
-
printCmd("/constraints", "View active negative constraints");
|
|
6897
|
-
console.log("");
|
|
6898
|
-
console.log(MUTED16(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6899
|
-
console.log("");
|
|
6900
|
-
console.log(MUTED16(" Run ") + INFO16("bob <command> --help") + MUTED16(" for details on a specific command."));
|
|
6901
|
-
console.log("");
|
|
6902
|
-
console.log(MUTED16(" \u{1F4D6} Full docs:"));
|
|
6903
|
-
console.log(INFO16(" https://seedling-io.gitbook.io/bob-cli/bobs-cli-product-wiki-and-user-guide/command-reference"));
|
|
6904
|
-
console.log("");
|
|
6905
|
-
console.log(MUTED16(" Built by ") + BRAND_PRIMARY11("Bob's Workshop") + MUTED16(" \u2014 A Seedling Company."));
|
|
6906
|
-
console.log("");
|
|
6907
|
-
}
|
|
6908
|
-
function printCmd(cmd, desc) {
|
|
6909
|
-
const padded = cmd.padEnd(24);
|
|
6910
|
-
console.log(BRAND_SECONDARY15(` ${padded}`) + MUTED16(desc));
|
|
6911
|
-
}
|
|
6874
|
+
program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.1.3");
|
|
6912
6875
|
program.command("whoami").description("Show current authentication status and configuration").action(() => {
|
|
6913
6876
|
const config = getConfig();
|
|
6914
6877
|
const projectName = path14.basename(process.cwd());
|
|
6915
6878
|
console.log("");
|
|
6916
|
-
console.log(
|
|
6917
|
-
console.log(
|
|
6918
|
-
console.log(` ${
|
|
6919
|
-
console.log(` ${
|
|
6920
|
-
console.log(` ${
|
|
6921
|
-
console.log(` ${
|
|
6922
|
-
console.log(` ${
|
|
6923
|
-
console.log(` ${
|
|
6924
|
-
console.log(` ${
|
|
6879
|
+
console.log(chalk25.bold(" \u{1F916} Bob's CLI"));
|
|
6880
|
+
console.log(chalk25.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6881
|
+
console.log(` ${chalk25.cyan("Status:")} ${config.loggedIn ? chalk25.green("Logged in as " + config.email) : "Not logged in"}`);
|
|
6882
|
+
console.log(` ${chalk25.cyan("Tier:")} ${config.tier === "platform" ? "Platform (Tier 3)" : "Local-first (Tier 1)"}`);
|
|
6883
|
+
console.log(` ${chalk25.cyan("Provider:")} ${config.provider || "Not configured"}`);
|
|
6884
|
+
console.log(` ${chalk25.cyan("Mode:")} ${config.personalizationMode ? "Personalized" : config.consultantMode ? "Consultant" : "Standard"}`);
|
|
6885
|
+
console.log(` ${chalk25.cyan("IDRP:")} ${config.idrp ? "Enabled" : "Disabled"}`);
|
|
6886
|
+
console.log(` ${chalk25.cyan("Project:")} ${projectName} (${process.cwd()})`);
|
|
6887
|
+
console.log(` ${chalk25.cyan("Session:")} ${config.conversationId ? config.conversationId.slice(0, 20) + "..." : "None"}`);
|
|
6925
6888
|
console.log("");
|
|
6926
6889
|
if (!config.loggedIn) {
|
|
6927
|
-
console.log(
|
|
6890
|
+
console.log(chalk25.gray(" Run `bob login` to authenticate."));
|
|
6928
6891
|
console.log("");
|
|
6929
6892
|
}
|
|
6930
6893
|
});
|
|
@@ -6943,26 +6906,5 @@ registerAutonomyCommand(program);
|
|
|
6943
6906
|
registerServeCommand(program);
|
|
6944
6907
|
registerRemoteCommand(program);
|
|
6945
6908
|
registerProfileCommand(program);
|
|
6946
|
-
|
|
6947
|
-
registerCommandCenterCommand(program);
|
|
6948
|
-
process.on("uncaughtException", (error) => {
|
|
6949
|
-
console.error("");
|
|
6950
|
-
console.error(chalk26.hex("#EF5350")(" \u274C An unexpected error occurred."));
|
|
6951
|
-
console.error(chalk26.hex("#78909C")(` ${error.message || "Unknown error"}`));
|
|
6952
|
-
console.error("");
|
|
6953
|
-
console.error(chalk26.hex("#78909C")(" If this persists, please report it:"));
|
|
6954
|
-
console.error(chalk26.hex("#26C6DA")(" https://github.com/bobsworkshop/bob-cli/issues"));
|
|
6955
|
-
console.error("");
|
|
6956
|
-
process.exit(1);
|
|
6957
|
-
});
|
|
6958
|
-
process.on("unhandledRejection", (reason) => {
|
|
6959
|
-
console.error("");
|
|
6960
|
-
console.error(chalk26.hex("#EF5350")(" \u274C An unexpected error occurred."));
|
|
6961
|
-
console.error(chalk26.hex("#78909C")(` ${reason?.message || reason || "Unknown error"}`));
|
|
6962
|
-
console.error("");
|
|
6963
|
-
console.error(chalk26.hex("#78909C")(" If this persists, please report it:"));
|
|
6964
|
-
console.error(chalk26.hex("#26C6DA")(" https://github.com/bobsworkshop/bob-cli/issues"));
|
|
6965
|
-
console.error("");
|
|
6966
|
-
process.exit(1);
|
|
6967
|
-
});
|
|
6909
|
+
registerBackupCommand(program);
|
|
6968
6910
|
program.parse();
|