offsite 0.0.93 → 0.0.95
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/apps/client/dist/assets/{XIcon-CDJz0EhT.js → XIcon-Tc8nkaoT.js} +4 -4
- package/apps/client/dist/assets/{audio-player-CEvt4CVB.js → audio-player-DosgEDBw.js} +1 -1
- package/apps/client/dist/assets/{button-CWFREsRn.js → button-BnzxfmO3.js} +1 -1
- package/apps/client/dist/assets/diff-panel-DTqI0r9W.js +260 -0
- package/apps/client/dist/assets/{dist-C_Y10kCd.js → dist-DNx_j6Py.js} +1 -1
- package/apps/client/dist/assets/{editor-dialog-CEMtEtdH.js → editor-dialog-Se2DP1E5.js} +36 -36
- package/apps/client/dist/assets/{image-viewer-3S4rNjFt.js → image-viewer-qKLCNUVK.js} +1 -1
- package/apps/client/dist/assets/index-B6yjBLio.js +72 -0
- package/apps/client/dist/assets/index-BFkBeASv.css +2 -0
- package/apps/client/dist/assets/panel-close-button-ByrJIu34.js +1 -0
- package/apps/client/dist/assets/{preview-dialog-BuSSuMeQ.js → preview-dialog-CnvAwgMU.js} +2 -2
- package/apps/client/dist/assets/{react-BgpkckXP.js → react-aDzERmfM.js} +1 -1
- package/apps/client/dist/assets/{tooltip-CKPboHje.js → tooltip-CU6Gh-yj.js} +1 -1
- package/apps/client/dist/assets/{use-editor-shortcut-auobREOP.js → use-editor-shortcut-CGHT7fnL.js} +26 -26
- package/apps/client/dist/assets/{utils-U6QfaJBO.js → utils-Cih_T9uV.js} +1 -1
- package/apps/client/dist/assets/{video-viewer-gcKzQe3N.js → video-viewer-Ctc_XHXE.js} +1 -1
- package/apps/client/dist/assets/zoomable-image-D0X1OvmT.js +1 -0
- package/apps/client/dist/assets/{zoomable-image-BD-kkiyn.js → zoomable-image-D6TeXYQ9.js} +1 -1
- package/apps/client/dist/index.html +10 -10
- package/apps/client/dist/sw.js +1 -1
- package/apps/server/dist/main.mjs +563 -155
- package/apps/server/dist/main.mjs.map +1 -1
- package/package.json +1 -1
- package/apps/client/dist/assets/diff-panel-CeJqpNi8.js +0 -260
- package/apps/client/dist/assets/index-CC5CJi_g.css +0 -2
- package/apps/client/dist/assets/index-CwwKJuU6.js +0 -72
- package/apps/client/dist/assets/panel-close-button-Bf-0TdmN.js +0 -1
- package/apps/client/dist/assets/zoomable-image-_HGrW8wW.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
|
-
import { accessSync, appendFileSync, chmodSync, closeSync, constants, createReadStream, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync, writeSync } from "node:fs";
|
|
2
|
+
import { accessSync, appendFileSync, chmodSync, closeSync, constants, createReadStream, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, unlinkSync, watch, writeFileSync, writeSync } from "node:fs";
|
|
3
3
|
import http, { createServer } from "node:http";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import zlib, { createGzip } from "node:zlib";
|
|
@@ -302,6 +302,29 @@ function notice(text) {
|
|
|
302
302
|
isError: true
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
|
+
function resultNotice(message) {
|
|
306
|
+
if (message.subtype === "success" || message.terminal_reason === "aborted_streaming" || message.terminal_reason === "aborted_tools") return null;
|
|
307
|
+
return notice(message.errors.filter((error) => !error.startsWith("[ede_diagnostic]")).join("\n") || message.subtype);
|
|
308
|
+
}
|
|
309
|
+
const failingReasons = [
|
|
310
|
+
"api_error",
|
|
311
|
+
"malformed_tool_use_exhausted",
|
|
312
|
+
"budget_exhausted",
|
|
313
|
+
"structured_output_retry_exhausted",
|
|
314
|
+
"tool_deferred_unavailable",
|
|
315
|
+
"turn_setup_failed",
|
|
316
|
+
"blocking_limit",
|
|
317
|
+
"rapid_refill_breaker",
|
|
318
|
+
"prompt_too_long",
|
|
319
|
+
"image_error",
|
|
320
|
+
"model_error"
|
|
321
|
+
];
|
|
322
|
+
function turnFailed(message) {
|
|
323
|
+
if (message.terminal_reason === "aborted_tools" || message.terminal_reason === "aborted_streaming") return false;
|
|
324
|
+
if (message.subtype === "success") return message.api_error_status === 429 || message.api_error_status === 529 || message.is_error && message.api_error_status === 403 || failingReasons.includes(message.terminal_reason);
|
|
325
|
+
const errors = message.errors.join("\n").toLowerCase();
|
|
326
|
+
return !errors.includes("interrupt") && !errors.includes("cancel");
|
|
327
|
+
}
|
|
305
328
|
function contextTokens(message) {
|
|
306
329
|
if (!isRecord(message) || !isRecord(message.usage)) return null;
|
|
307
330
|
const usage = message.usage;
|
|
@@ -366,6 +389,35 @@ function toBlocks(content, answers) {
|
|
|
366
389
|
function isRecord(value) {
|
|
367
390
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
368
391
|
}
|
|
392
|
+
function userText$2(value) {
|
|
393
|
+
const command = /<command-name>(.*?)<\/command-name>/s.exec(value);
|
|
394
|
+
if (command !== null) {
|
|
395
|
+
const args = /<command-args>(.*?)<\/command-args>/s.exec(value);
|
|
396
|
+
return {
|
|
397
|
+
format: "command",
|
|
398
|
+
text: `${command[1]} ${args?.[1] ?? ""}`.trim()
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
const output = /^<local-command-(?:stdout|stderr)>(.*)<\/local-command-(?:stdout|stderr)>$/s.exec(value.trim());
|
|
402
|
+
if (output !== null) return {
|
|
403
|
+
format: "output",
|
|
404
|
+
text: output[1]
|
|
405
|
+
};
|
|
406
|
+
const bash = /^<bash-input>(.*)<\/bash-input>$/s.exec(value.trim());
|
|
407
|
+
if (bash !== null) return {
|
|
408
|
+
format: "command",
|
|
409
|
+
text: `$ ${bash[1]}`
|
|
410
|
+
};
|
|
411
|
+
const bashOutput = /^<bash-stdout>(.*)<\/bash-stdout><bash-stderr>(.*)<\/bash-stderr>$/s.exec(value.trim());
|
|
412
|
+
if (bashOutput !== null) return {
|
|
413
|
+
format: "output",
|
|
414
|
+
text: `${bashOutput[1]}${bashOutput[2]}`.trim()
|
|
415
|
+
};
|
|
416
|
+
return {
|
|
417
|
+
format: "text",
|
|
418
|
+
text: value
|
|
419
|
+
};
|
|
420
|
+
}
|
|
369
421
|
// src/storage/storage.ts
|
|
370
422
|
const home = args.dir;
|
|
371
423
|
const lockFile = path.join(home, "server.pid");
|
|
@@ -430,18 +482,26 @@ function readJson(name) {
|
|
|
430
482
|
throw error;
|
|
431
483
|
}
|
|
432
484
|
}
|
|
433
|
-
function readSettings(name, schema, defaults) {
|
|
485
|
+
function readSettings(name, schema, defaults, migrate = (value) => value) {
|
|
434
486
|
const file = path.join(home, name);
|
|
435
|
-
let
|
|
487
|
+
let saved;
|
|
436
488
|
try {
|
|
437
|
-
|
|
489
|
+
saved = readJson(name);
|
|
438
490
|
} catch (error) {
|
|
439
491
|
log$19.warn(`invalid ${file}, using the defaults:`, error);
|
|
440
492
|
return structuredClone(defaults);
|
|
441
493
|
}
|
|
442
|
-
if (
|
|
494
|
+
if (saved === null) return structuredClone(defaults);
|
|
495
|
+
const value = migrate(saved);
|
|
496
|
+
if (value !== saved) {
|
|
497
|
+
writeJson(`${name}.bak`, saved);
|
|
498
|
+
log$19.info(`migrated ${file}, the old file is in ${file}.bak`);
|
|
499
|
+
}
|
|
443
500
|
let result = schema.safeParse(value);
|
|
444
|
-
if (result.success)
|
|
501
|
+
if (result.success) {
|
|
502
|
+
if (value !== saved) writeJson(name, result.data);
|
|
503
|
+
return result.data;
|
|
504
|
+
}
|
|
445
505
|
while (!result.success) {
|
|
446
506
|
const issue = result.error.issues[0];
|
|
447
507
|
const targets = issue.code === "unrecognized_keys" ? issue.keys.map((key) => [...issue.path, key]) : [issue.path];
|
|
@@ -496,11 +556,11 @@ function installOpen(host, port) {
|
|
|
496
556
|
"wslview"
|
|
497
557
|
]) {
|
|
498
558
|
const wrapper = path.join(folder, name);
|
|
499
|
-
writeFileSync(wrapper, `#!/bin/sh\nexec ${quote(process.execPath)} ${quote(script)} ${name} "$@"\n`);
|
|
559
|
+
writeFileSync(wrapper, `#!/bin/sh\nexec ${quote$1(process.execPath)} ${quote$1(script)} ${name} "$@"\n`);
|
|
500
560
|
chmodSync(wrapper, 493);
|
|
501
561
|
}
|
|
502
562
|
const startup = path.join(folder, "env.sh");
|
|
503
|
-
writeFileSync(startup, `export PATH=${quote(folder)}:"$PATH"\n`);
|
|
563
|
+
writeFileSync(startup, `export PATH=${quote$1(folder)}:"$PATH"\n`);
|
|
504
564
|
env = {
|
|
505
565
|
PATH: process.env.PATH === void 0 ? folder : `${folder}${path.delimiter}${process.env.PATH}`,
|
|
506
566
|
BROWSER: path.join(folder, process.platform === "darwin" ? "open" : "xdg-open"),
|
|
@@ -515,7 +575,7 @@ function loopback(host) {
|
|
|
515
575
|
if (host.includes(":")) return `[${host}]`;
|
|
516
576
|
return host;
|
|
517
577
|
}
|
|
518
|
-
function quote(value) {
|
|
578
|
+
function quote$1(value) {
|
|
519
579
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
520
580
|
}
|
|
521
581
|
// src/lib/codex/app-server.ts
|
|
@@ -693,7 +753,11 @@ var AppServer = class {
|
|
|
693
753
|
publishCodexLimits(this.home, params.rateLimits);
|
|
694
754
|
return;
|
|
695
755
|
}
|
|
696
|
-
listener
|
|
756
|
+
if (listener === void 0) {
|
|
757
|
+
owner?.notifyAgent?.(message.method, params);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
listener.notify(message.method, params);
|
|
697
761
|
}
|
|
698
762
|
rootOf(threadId) {
|
|
699
763
|
if (typeof threadId !== "string") return;
|
|
@@ -925,7 +989,7 @@ function toItems$1(turnId, item, parent, time) {
|
|
|
925
989
|
case "userMessage": return [{
|
|
926
990
|
...base,
|
|
927
991
|
kind: "user",
|
|
928
|
-
text: item.content.flatMap(userText$
|
|
992
|
+
text: item.content.flatMap(userText$1).join("\n").trim(),
|
|
929
993
|
images: item.content.flatMap(userImages),
|
|
930
994
|
format: "text"
|
|
931
995
|
}];
|
|
@@ -1035,7 +1099,7 @@ function toItems$1(turnId, item, parent, time) {
|
|
|
1035
1099
|
case "exitedReviewMode": return [];
|
|
1036
1100
|
}
|
|
1037
1101
|
}
|
|
1038
|
-
function userText$
|
|
1102
|
+
function userText$1(input) {
|
|
1039
1103
|
switch (input.type) {
|
|
1040
1104
|
case "text": return [input.text.replaceAll(/\[Image: source: [^\]]+\]/g, "")];
|
|
1041
1105
|
case "skill":
|
|
@@ -1133,27 +1197,31 @@ function accountSchema(provider) {
|
|
|
1133
1197
|
enabled: z.boolean().default(true)
|
|
1134
1198
|
});
|
|
1135
1199
|
}
|
|
1200
|
+
const modelSettings = z.record(z.string(), modelSetting).default({});
|
|
1136
1201
|
const configSchema = z.strictObject({
|
|
1137
|
-
|
|
1202
|
+
version: z.literal(2).default(2),
|
|
1203
|
+
claude: z.strictObject({
|
|
1204
|
+
enabled: z.boolean().default(true),
|
|
1205
|
+
baseDir: platformDirs,
|
|
1206
|
+
accounts: z.array(accountSchema("claude")).default([]),
|
|
1207
|
+
models: modelSettings
|
|
1208
|
+
}).prefault({}),
|
|
1209
|
+
codex: z.strictObject({
|
|
1210
|
+
enabled: z.boolean().default(true),
|
|
1211
|
+
accounts: z.array(accountSchema("codex")).default([]),
|
|
1212
|
+
models: modelSettings
|
|
1213
|
+
}).prefault({}),
|
|
1214
|
+
opencode: z.strictObject({
|
|
1215
|
+
enabled: z.boolean().default(true),
|
|
1216
|
+
models: modelSettings
|
|
1217
|
+
}).prefault({}),
|
|
1138
1218
|
workingDir: z.strictObject({
|
|
1139
1219
|
macos: workingDir(path.posix.isAbsolute),
|
|
1140
1220
|
linux: workingDir(path.posix.isAbsolute),
|
|
1141
1221
|
windows: workingDir(path.win32.isAbsolute)
|
|
1142
1222
|
}).prefault({}),
|
|
1143
|
-
claudeAccounts: z.array(accountSchema("claude")).default([]),
|
|
1144
|
-
codexAccounts: z.array(accountSchema("codex")).default([]),
|
|
1145
|
-
enabledProviders: z.strictObject({
|
|
1146
|
-
claude: z.boolean().default(true),
|
|
1147
|
-
codex: z.boolean().default(true),
|
|
1148
|
-
opencode: z.boolean().default(true)
|
|
1149
|
-
}).prefault({}),
|
|
1150
1223
|
commitMessage: generationModel,
|
|
1151
1224
|
titleGeneration: generationModel,
|
|
1152
|
-
models: z.strictObject({
|
|
1153
|
-
claude: z.record(z.string(), modelSetting).default({}),
|
|
1154
|
-
codex: z.record(z.string(), modelSetting).default({}),
|
|
1155
|
-
opencode: z.record(z.string(), modelSetting).default({})
|
|
1156
|
-
}).prefault({}),
|
|
1157
1225
|
delivery: z.enum(["queue", "steer"]).default("steer"),
|
|
1158
1226
|
autoSkipQuestionTimeout: z.number().positive().nullable().default(180),
|
|
1159
1227
|
autoPermissionMode: z.enum([
|
|
@@ -1187,7 +1255,7 @@ function platform() {
|
|
|
1187
1255
|
throw new Error(`unsupported platform ${process.platform}`);
|
|
1188
1256
|
}
|
|
1189
1257
|
function claudeBaseDir() {
|
|
1190
|
-
return readConfig().
|
|
1258
|
+
return readConfig().claude.baseDir[platform()] ?? path.join(os.homedir(), ".claude");
|
|
1191
1259
|
}
|
|
1192
1260
|
function workingDirectory() {
|
|
1193
1261
|
const dir = readConfig().workingDir[platform()] ?? "~";
|
|
@@ -1205,11 +1273,42 @@ function configInfo() {
|
|
|
1205
1273
|
defaultShell: defaultShell(),
|
|
1206
1274
|
defaultClaudeBaseDir: path.join(os.homedir(), ".claude"),
|
|
1207
1275
|
homeDir: os.homedir(),
|
|
1208
|
-
scratchAvailable
|
|
1276
|
+
scratchAvailable,
|
|
1277
|
+
version: "v0.0.95"
|
|
1209
1278
|
};
|
|
1210
1279
|
}
|
|
1211
1280
|
function readConfig() {
|
|
1212
|
-
return readSettings("config.json", configSchema, defaults$1);
|
|
1281
|
+
return readSettings("config.json", configSchema, defaults$1, migrate$1);
|
|
1282
|
+
}
|
|
1283
|
+
function migrate$1(value) {
|
|
1284
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
|
|
1285
|
+
if (!("version" in value)) return fromVersion1(value);
|
|
1286
|
+
if (value.version === 2) return value;
|
|
1287
|
+
throw new Error(`config.json has version ${String(value.version)}, which this version of offsite cannot read. Update offsite`);
|
|
1288
|
+
}
|
|
1289
|
+
function fromVersion1(value) {
|
|
1290
|
+
const { claudeBaseDir, claudeAccounts, codexAccounts, enabledProviders, models, ...rest } = value;
|
|
1291
|
+
const enabled = enabledProviders ?? {};
|
|
1292
|
+
const providerModels = models ?? {};
|
|
1293
|
+
return {
|
|
1294
|
+
...rest,
|
|
1295
|
+
version: 2,
|
|
1296
|
+
claude: {
|
|
1297
|
+
enabled: enabled.claude,
|
|
1298
|
+
baseDir: claudeBaseDir,
|
|
1299
|
+
accounts: claudeAccounts,
|
|
1300
|
+
models: providerModels.claude
|
|
1301
|
+
},
|
|
1302
|
+
codex: {
|
|
1303
|
+
enabled: enabled.codex,
|
|
1304
|
+
accounts: codexAccounts,
|
|
1305
|
+
models: providerModels.codex
|
|
1306
|
+
},
|
|
1307
|
+
opencode: {
|
|
1308
|
+
enabled: enabled.opencode,
|
|
1309
|
+
models: providerModels.opencode
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1213
1312
|
}
|
|
1214
1313
|
function saveConfig(value) {
|
|
1215
1314
|
const result = configSchema.safeParse(value);
|
|
@@ -1237,6 +1336,7 @@ function activity() {
|
|
|
1237
1336
|
prompted: run.prompted,
|
|
1238
1337
|
compacting: run.compacting,
|
|
1239
1338
|
backgroundAgent: run.backgroundAgents > 0,
|
|
1339
|
+
holdsCompletion: !run.failed && run.agentTasks > 0,
|
|
1240
1340
|
tasks: run.tasks
|
|
1241
1341
|
};
|
|
1242
1342
|
return result;
|
|
@@ -1285,6 +1385,24 @@ function scheduleAutoResolve(sessionId, request) {
|
|
|
1285
1385
|
}
|
|
1286
1386
|
};
|
|
1287
1387
|
}
|
|
1388
|
+
// src/lib/codex/agents.ts
|
|
1389
|
+
function trackAgents(agents, method, params) {
|
|
1390
|
+
const threadId = params.threadId;
|
|
1391
|
+
if (method === "turn/started" && agents.has(threadId)) agents.set(threadId, true);
|
|
1392
|
+
if (method === "turn/completed" && agents.has(threadId)) agents.set(threadId, false);
|
|
1393
|
+
if (method !== "item/completed") return;
|
|
1394
|
+
const item = params.item;
|
|
1395
|
+
if (item.type === "subAgentActivity") {
|
|
1396
|
+
if (item.kind === "started" && !agents.has(item.agentThreadId)) agents.set(item.agentThreadId, true);
|
|
1397
|
+
if ((item.kind === "interrupted" || item.kind === "completed") && agents.has(item.agentThreadId)) agents.set(item.agentThreadId, false);
|
|
1398
|
+
}
|
|
1399
|
+
if (item.type === "collabAgentToolCall") {
|
|
1400
|
+
if (item.tool === "spawnAgent") {
|
|
1401
|
+
for (const agent of item.receiverThreadIds) if (!agents.has(agent)) agents.set(agent, true);
|
|
1402
|
+
}
|
|
1403
|
+
for (const [agent, state] of Object.entries(item.agentsStates)) if (agents.get(agent) === true) agents.set(agent, state.status === "pendingInit" || state.status === "running");
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1288
1406
|
// src/lib/codex/presets.ts
|
|
1289
1407
|
const presets = /* @__PURE__ */ new Map([
|
|
1290
1408
|
["read-only", {
|
|
@@ -1437,11 +1555,13 @@ var CodexRun = class CodexRun {
|
|
|
1437
1555
|
compacting = false;
|
|
1438
1556
|
backgroundTasks = 0;
|
|
1439
1557
|
backgroundAgents = 0;
|
|
1558
|
+
failed = false;
|
|
1440
1559
|
tasks = {};
|
|
1441
1560
|
activeAt = Date.now();
|
|
1442
1561
|
model;
|
|
1443
1562
|
close;
|
|
1444
1563
|
turnId = null;
|
|
1564
|
+
agents = /* @__PURE__ */ new Map();
|
|
1445
1565
|
idleTimer;
|
|
1446
1566
|
static async start(cwd, settings) {
|
|
1447
1567
|
const server = codexServer(settings);
|
|
@@ -1550,6 +1670,7 @@ var CodexRun = class CodexRun {
|
|
|
1550
1670
|
}
|
|
1551
1671
|
notify(method, params) {
|
|
1552
1672
|
this.activeAt = Date.now();
|
|
1673
|
+
this.notifyAgent(method, params);
|
|
1553
1674
|
switch (method) {
|
|
1554
1675
|
case "turn/started": {
|
|
1555
1676
|
const turn = params.turn;
|
|
@@ -1563,6 +1684,7 @@ var CodexRun = class CodexRun {
|
|
|
1563
1684
|
case "turn/completed": {
|
|
1564
1685
|
const turn = params.turn;
|
|
1565
1686
|
if (turn.status === "failed" && turn.error !== null) this.put(errorItem(`${turn.id}:error`, turn.error.message));
|
|
1687
|
+
this.failed = turn.status === "failed" || turn.status === "inProgress";
|
|
1566
1688
|
this.finishTurn();
|
|
1567
1689
|
return;
|
|
1568
1690
|
}
|
|
@@ -1628,6 +1750,14 @@ var CodexRun = class CodexRun {
|
|
|
1628
1750
|
case "serverRequest/resolved": this.settle(String(params.requestId));
|
|
1629
1751
|
}
|
|
1630
1752
|
}
|
|
1753
|
+
notifyAgent(method, params) {
|
|
1754
|
+
const running = this.agentTasks;
|
|
1755
|
+
trackAgents(this.agents, method, params);
|
|
1756
|
+
if (this.agentTasks !== running) broadcastActivity();
|
|
1757
|
+
}
|
|
1758
|
+
get agentTasks() {
|
|
1759
|
+
return [...this.agents.values()].filter((running) => running).length;
|
|
1760
|
+
}
|
|
1631
1761
|
request(id, method, params) {
|
|
1632
1762
|
if (method === "mcpServer/elicitation/request") {
|
|
1633
1763
|
this.server.reply(id, {
|
|
@@ -2638,6 +2768,8 @@ var OpenCodeRun = class OpenCodeRun {
|
|
|
2638
2768
|
compacting = false;
|
|
2639
2769
|
backgroundTasks = 0;
|
|
2640
2770
|
backgroundAgents = 0;
|
|
2771
|
+
agentTasks = 0;
|
|
2772
|
+
failed = false;
|
|
2641
2773
|
tasks = {};
|
|
2642
2774
|
close;
|
|
2643
2775
|
applied;
|
|
@@ -3414,7 +3546,7 @@ const UNTRACKED_LIMIT = 100;
|
|
|
3414
3546
|
const NEW_FILE_LIMIT = 262144;
|
|
3415
3547
|
const REPO_DEPTH = 3;
|
|
3416
3548
|
const FETCH_TIMEOUT = 6e4;
|
|
3417
|
-
const run$
|
|
3549
|
+
const run$4 = promisify(execFile);
|
|
3418
3550
|
const PULL_REQUEST_INSTRUCTIONS = `You write GitHub pull requests as Markdown.
|
|
3419
3551
|
|
|
3420
3552
|
## TITLE RULES
|
|
@@ -3446,6 +3578,7 @@ Return one Markdown document. The title is the level-1 heading (\`# <title>\`),
|
|
|
3446
3578
|
async function gitRepos(cwd) {
|
|
3447
3579
|
return Promise.all((await projectRepos(cwd)).map(async (repo) => ({
|
|
3448
3580
|
...await gitStatus(path.join(cwd, repo)),
|
|
3581
|
+
...await lineChanges(path.join(cwd, repo)),
|
|
3449
3582
|
path: repo
|
|
3450
3583
|
})));
|
|
3451
3584
|
}
|
|
@@ -3506,7 +3639,7 @@ async function sync(cwd) {
|
|
|
3506
3639
|
async function fetchProject(cwd) {
|
|
3507
3640
|
await Promise.all((await projectRepos(cwd)).map(async (repo) => {
|
|
3508
3641
|
try {
|
|
3509
|
-
await run$
|
|
3642
|
+
await run$4("git", ["fetch"], {
|
|
3510
3643
|
cwd: path.join(cwd, repo),
|
|
3511
3644
|
env: {
|
|
3512
3645
|
...process.env,
|
|
@@ -3537,7 +3670,7 @@ async function switchBranch(cwd, branch, create) {
|
|
|
3537
3670
|
async function pullRequestBranches(cwd) {
|
|
3538
3671
|
const head = (await gitStatus(cwd)).branch;
|
|
3539
3672
|
if (head === null) throw new Error("Not on a branch");
|
|
3540
|
-
const base = (await run$
|
|
3673
|
+
const base = (await run$4("gh", [
|
|
3541
3674
|
"repo",
|
|
3542
3675
|
"view",
|
|
3543
3676
|
"--json",
|
|
@@ -3562,7 +3695,7 @@ async function preparePullRequest(cwd, base, commitChanges) {
|
|
|
3562
3695
|
if (commitChanges) await commit(cwd, "", true);
|
|
3563
3696
|
const status = await gitStatus(cwd);
|
|
3564
3697
|
if (status.upstream === null || status.ahead > 0) await push$1(cwd);
|
|
3565
|
-
const open = (await run$
|
|
3698
|
+
const open = (await run$4("gh", [
|
|
3566
3699
|
"pr",
|
|
3567
3700
|
"list",
|
|
3568
3701
|
"--head",
|
|
@@ -3609,7 +3742,7 @@ async function preparePullRequest(cwd, base, commitChanges) {
|
|
|
3609
3742
|
};
|
|
3610
3743
|
}
|
|
3611
3744
|
async function createPullRequest(cwd, request) {
|
|
3612
|
-
const url = (await run$
|
|
3745
|
+
const url = (await run$4("gh", [
|
|
3613
3746
|
"pr",
|
|
3614
3747
|
"create",
|
|
3615
3748
|
"--title",
|
|
@@ -3642,7 +3775,7 @@ async function projectDiff(cwd) {
|
|
|
3642
3775
|
return (await Promise.all(repos.map((repo) => repoDiff(path.join(cwd, repo), repo === "" ? "" : `${repo}/`)))).join("");
|
|
3643
3776
|
}
|
|
3644
3777
|
function git(cwd, args) {
|
|
3645
|
-
return run$
|
|
3778
|
+
return run$4("git", args, {
|
|
3646
3779
|
cwd,
|
|
3647
3780
|
maxBuffer: 67108864
|
|
3648
3781
|
});
|
|
@@ -3679,6 +3812,19 @@ async function gitStatus(cwd) {
|
|
|
3679
3812
|
}
|
|
3680
3813
|
return status;
|
|
3681
3814
|
}
|
|
3815
|
+
async function lineChanges(repo) {
|
|
3816
|
+
let added = 0;
|
|
3817
|
+
let removed = 0;
|
|
3818
|
+
let hunk = false;
|
|
3819
|
+
for (const line of (await repoDiff(repo, "")).split("\n")) if (line.startsWith("diff --git ")) hunk = false;
|
|
3820
|
+
else if (line.startsWith("@@")) hunk = true;
|
|
3821
|
+
else if (hunk && line.startsWith("+")) added++;
|
|
3822
|
+
else if (hunk && line.startsWith("-")) removed++;
|
|
3823
|
+
return {
|
|
3824
|
+
added,
|
|
3825
|
+
removed
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3682
3828
|
async function rebaseOnUpstream(cwd) {
|
|
3683
3829
|
try {
|
|
3684
3830
|
await git(cwd, ["rebase", "@{upstream}"]);
|
|
@@ -4101,8 +4247,10 @@ function rebuildThreads() {
|
|
|
4101
4247
|
const config = readConfig();
|
|
4102
4248
|
const key = JSON.stringify([
|
|
4103
4249
|
projectPaths$1(),
|
|
4104
|
-
config.
|
|
4105
|
-
config.
|
|
4250
|
+
config.claude.enabled,
|
|
4251
|
+
config.codex.enabled,
|
|
4252
|
+
config.opencode.enabled,
|
|
4253
|
+
config.claude.baseDir
|
|
4106
4254
|
]);
|
|
4107
4255
|
if (key === builtFor) return;
|
|
4108
4256
|
builtFor = key;
|
|
@@ -4116,7 +4264,7 @@ function threadFailed(provider) {
|
|
|
4116
4264
|
}
|
|
4117
4265
|
function knownThreads() {
|
|
4118
4266
|
const known = new Set(projects().map((project) => project.id));
|
|
4119
|
-
return allProviders$2.filter((provider) => readConfig()
|
|
4267
|
+
return allProviders$2.filter((provider) => readConfig()[provider].enabled).flatMap((provider) => [...threads[provider].values()]).filter((item) => known.has(item.projectId));
|
|
4120
4268
|
}
|
|
4121
4269
|
async function providerThreads(provider) {
|
|
4122
4270
|
await built;
|
|
@@ -4191,7 +4339,7 @@ async function relistAll() {
|
|
|
4191
4339
|
await Promise.all(allProviders$2.map((provider) => relist(provider)));
|
|
4192
4340
|
}
|
|
4193
4341
|
async function relist(provider) {
|
|
4194
|
-
if (!readConfig()
|
|
4342
|
+
if (!readConfig()[provider].enabled) {
|
|
4195
4343
|
threads[provider] = /* @__PURE__ */ new Map();
|
|
4196
4344
|
failures.delete(provider);
|
|
4197
4345
|
broadcast({ type: "threads" });
|
|
@@ -4291,7 +4439,7 @@ function withStatus(items) {
|
|
|
4291
4439
|
});
|
|
4292
4440
|
}
|
|
4293
4441
|
function enabledProviders() {
|
|
4294
|
-
return allProviders$1.filter((provider) => readConfig()
|
|
4442
|
+
return allProviders$1.filter((provider) => readConfig()[provider].enabled);
|
|
4295
4443
|
}
|
|
4296
4444
|
function projectPaths() {
|
|
4297
4445
|
return projects().map((project) => [project.path, project.id]);
|
|
@@ -4310,18 +4458,18 @@ function providerOf(sessionId) {
|
|
|
4310
4458
|
if (provider === void 0) throw new Error("offsite does not know this session yet, reload the page");
|
|
4311
4459
|
return provider;
|
|
4312
4460
|
}
|
|
4313
|
-
function recordInteraction(id, provider) {
|
|
4461
|
+
function recordInteraction(id, provider, status) {
|
|
4314
4462
|
db.insert(sessions$1).values({
|
|
4315
4463
|
sessionId: id,
|
|
4316
4464
|
provider,
|
|
4317
4465
|
interactedAt: Date.now(),
|
|
4318
|
-
status: "active"
|
|
4466
|
+
status: status ?? "active"
|
|
4319
4467
|
}).onConflictDoUpdate({
|
|
4320
4468
|
target: sessions$1.sessionId,
|
|
4321
4469
|
set: {
|
|
4322
4470
|
provider,
|
|
4323
4471
|
interactedAt: Date.now(),
|
|
4324
|
-
status: sql`CASE ${sessions$1.status} WHEN 'pinned' THEN 'pinned' WHEN 'snoozed' THEN 'snoozed' WHEN 'minimized' THEN 'minimized' ELSE 'active' END`
|
|
4472
|
+
status: status === null ? sql`CASE ${sessions$1.status} WHEN 'pinned' THEN 'pinned' WHEN 'snoozed' THEN 'snoozed' WHEN 'minimized' THEN 'minimized' ELSE 'active' END` : sql`CASE ${sessions$1.status} WHEN 'pinned' THEN 'pinned' ELSE ${status} END`
|
|
4325
4473
|
}
|
|
4326
4474
|
}).run();
|
|
4327
4475
|
}
|
|
@@ -4371,7 +4519,7 @@ async function list(provider) {
|
|
|
4371
4519
|
const paths = new Map(projectPaths());
|
|
4372
4520
|
const before = statuses[provider];
|
|
4373
4521
|
try {
|
|
4374
|
-
if (!readConfig()
|
|
4522
|
+
if (!readConfig()[provider].enabled) {
|
|
4375
4523
|
listed[provider] = [];
|
|
4376
4524
|
statuses[provider] = {
|
|
4377
4525
|
provider,
|
|
@@ -4426,7 +4574,7 @@ async function lookup(provider, sessionId, paths) {
|
|
|
4426
4574
|
function watchSessions() {
|
|
4427
4575
|
for (const watcher of watchers) watcher.close();
|
|
4428
4576
|
watchers = [["claude", path.join(claudeBaseDir(), "projects")], ["codex", path.join(codexBaseDir, "sessions")]].flatMap(([provider, dir]) => {
|
|
4429
|
-
if (!readConfig()
|
|
4577
|
+
if (!readConfig()[provider].enabled) return [];
|
|
4430
4578
|
try {
|
|
4431
4579
|
const watcher = watch(dir, { recursive: true }, (_, filename) => {
|
|
4432
4580
|
fileChanged(provider, filename);
|
|
@@ -4520,7 +4668,7 @@ async function fork(sessionId, projectId, messageId) {
|
|
|
4520
4668
|
const provider = providerOf(sessionId);
|
|
4521
4669
|
const result = await forkWith(provider, sessionId, projectPath(projectId), messageId);
|
|
4522
4670
|
providers.set(result.sessionId, provider);
|
|
4523
|
-
recordInteraction(result.sessionId, provider);
|
|
4671
|
+
recordInteraction(result.sessionId, provider, null);
|
|
4524
4672
|
markChanged(provider, result.sessionId);
|
|
4525
4673
|
refreshSessions(provider);
|
|
4526
4674
|
return result;
|
|
@@ -4666,16 +4814,16 @@ function providerName(provider) {
|
|
|
4666
4814
|
}
|
|
4667
4815
|
function storedAccounts() {
|
|
4668
4816
|
const config = readConfig();
|
|
4669
|
-
return [...config.
|
|
4817
|
+
return [...config.claude.accounts.map((item) => ({
|
|
4670
4818
|
...item,
|
|
4671
4819
|
configDir: item.configDir[platform()]
|
|
4672
|
-
})), ...config.
|
|
4820
|
+
})), ...config.codex.accounts.map((item) => ({
|
|
4673
4821
|
...item,
|
|
4674
4822
|
configDir: item.configDir[platform()]
|
|
4675
4823
|
}))];
|
|
4676
4824
|
}
|
|
4677
4825
|
function assertEnabled(provider, accountId) {
|
|
4678
|
-
if (!readConfig()
|
|
4826
|
+
if (!readConfig()[provider].enabled) throw new Error(`${providerName(provider)} is disabled in Settings`);
|
|
4679
4827
|
if (provider === "opencode") return;
|
|
4680
4828
|
if (!storedAccount(provider, accountId).enabled) throw new Error("That account is disabled in Settings");
|
|
4681
4829
|
}
|
|
@@ -4705,7 +4853,7 @@ function storedAccount(provider, accountId) {
|
|
|
4705
4853
|
}
|
|
4706
4854
|
async function accounts() {
|
|
4707
4855
|
const stored = await Promise.all(storedAccounts().map((account) => {
|
|
4708
|
-
if (!account.enabled || !readConfig()
|
|
4856
|
+
if (!account.enabled || !readConfig()[account.provider].enabled) return disabledAccount(account);
|
|
4709
4857
|
switch (account.provider) {
|
|
4710
4858
|
case "claude": return claudeAccount(account);
|
|
4711
4859
|
case "codex": return codexAccount(account);
|
|
@@ -4714,7 +4862,7 @@ async function accounts() {
|
|
|
4714
4862
|
return {
|
|
4715
4863
|
claudeAccounts: stored.filter((item) => item.provider === "claude"),
|
|
4716
4864
|
codexAccounts: stored.filter((item) => item.provider === "codex"),
|
|
4717
|
-
openCode: { available: openCodeInstalled() && readConfig().
|
|
4865
|
+
openCode: { available: openCodeInstalled() && readConfig().opencode.enabled }
|
|
4718
4866
|
};
|
|
4719
4867
|
}
|
|
4720
4868
|
function disabledAccount(account) {
|
|
@@ -4743,7 +4891,7 @@ async function publishAccounts() {
|
|
|
4743
4891
|
async function capabilities(provider, accountId, cwd) {
|
|
4744
4892
|
assertEnabled(provider, accountId);
|
|
4745
4893
|
const result = await providerCapabilities(provider, accountId, cwd);
|
|
4746
|
-
const settings = readConfig()
|
|
4894
|
+
const settings = readConfig()[provider].models;
|
|
4747
4895
|
return {
|
|
4748
4896
|
...result,
|
|
4749
4897
|
defaultModel: defaultModels[provider],
|
|
@@ -4776,51 +4924,80 @@ function providerCapabilities(provider, accountId, cwd) {
|
|
|
4776
4924
|
function addAccount(provider, name, configDir) {
|
|
4777
4925
|
const account = validAccount(name, configDir);
|
|
4778
4926
|
const config = readConfig();
|
|
4779
|
-
const key = `${provider}Accounts`;
|
|
4780
4927
|
saveConfig({
|
|
4781
4928
|
...config,
|
|
4782
|
-
[
|
|
4783
|
-
|
|
4784
|
-
provider,
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4929
|
+
[provider]: {
|
|
4930
|
+
...config[provider],
|
|
4931
|
+
accounts: [...config[provider].accounts, {
|
|
4932
|
+
id: randomUUID(),
|
|
4933
|
+
provider,
|
|
4934
|
+
name: account.name,
|
|
4935
|
+
color: null,
|
|
4936
|
+
configDir: { [platform()]: account.configDir },
|
|
4937
|
+
enabled: true
|
|
4938
|
+
}]
|
|
4939
|
+
}
|
|
4790
4940
|
});
|
|
4941
|
+
publishAccounts();
|
|
4942
|
+
return null;
|
|
4791
4943
|
}
|
|
4792
4944
|
async function updateAccount(update) {
|
|
4793
4945
|
const before = storedAccounts().find((item) => item.id === update.id);
|
|
4794
4946
|
if (before === void 0) throw new Error("That account was removed");
|
|
4795
4947
|
const account = validAccount(update.name, update.configDir);
|
|
4796
4948
|
const config = readConfig();
|
|
4797
|
-
const key = `${before.provider}Accounts`;
|
|
4798
4949
|
saveConfig({
|
|
4799
4950
|
...config,
|
|
4800
|
-
[
|
|
4801
|
-
...
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4951
|
+
[before.provider]: {
|
|
4952
|
+
...config[before.provider],
|
|
4953
|
+
accounts: config[before.provider].accounts.map((item) => item.id === update.id ? {
|
|
4954
|
+
...item,
|
|
4955
|
+
name: account.name,
|
|
4956
|
+
color: update.color,
|
|
4957
|
+
configDir: {
|
|
4958
|
+
...item.configDir,
|
|
4959
|
+
[platform()]: account.configDir
|
|
4960
|
+
},
|
|
4961
|
+
enabled: update.enabled
|
|
4962
|
+
} : item)
|
|
4963
|
+
}
|
|
4810
4964
|
});
|
|
4811
4965
|
forgetClaude(update.id);
|
|
4812
4966
|
forgetUsage(update.id);
|
|
4813
4967
|
if (before.enabled && !update.enabled) await stopAccount(before);
|
|
4968
|
+
publishAccounts();
|
|
4969
|
+
return null;
|
|
4970
|
+
}
|
|
4971
|
+
function accountLinkPlan(id) {
|
|
4972
|
+
return linkPlan(storedAccount("claude", id).configDir ?? claudeBaseDir());
|
|
4973
|
+
}
|
|
4974
|
+
async function linkAccount(id) {
|
|
4975
|
+
const account = storedAccount("claude", id);
|
|
4976
|
+
await stopAccount(account);
|
|
4977
|
+
try {
|
|
4978
|
+
await linkClaude(account.configDir ?? claudeBaseDir());
|
|
4979
|
+
} finally {
|
|
4980
|
+
publishAccounts();
|
|
4981
|
+
}
|
|
4982
|
+
return null;
|
|
4814
4983
|
}
|
|
4815
4984
|
function removeAccount(id) {
|
|
4816
4985
|
const config = readConfig();
|
|
4817
4986
|
saveConfig({
|
|
4818
4987
|
...config,
|
|
4819
|
-
|
|
4820
|
-
|
|
4988
|
+
claude: {
|
|
4989
|
+
...config.claude,
|
|
4990
|
+
accounts: config.claude.accounts.filter((item) => item.id !== id)
|
|
4991
|
+
},
|
|
4992
|
+
codex: {
|
|
4993
|
+
...config.codex,
|
|
4994
|
+
accounts: config.codex.accounts.filter((item) => item.id !== id)
|
|
4995
|
+
}
|
|
4821
4996
|
});
|
|
4822
4997
|
forgetClaude(id);
|
|
4823
4998
|
forgetUsage(id);
|
|
4999
|
+
publishAccounts();
|
|
5000
|
+
return null;
|
|
4824
5001
|
}
|
|
4825
5002
|
function validAccount(name, configDir) {
|
|
4826
5003
|
if (name.trim() === "") throw new Error("Give the account a name");
|
|
@@ -4955,6 +5132,213 @@ function targets() {
|
|
|
4955
5132
|
if (notifier === null || notifier.endpoint === null) return [];
|
|
4956
5133
|
return db.select().from(pushSubscriptions).where(and(eq(pushSubscriptions.server, server$1), eq(pushSubscriptions.endpoint, notifier.endpoint))).all();
|
|
4957
5134
|
}
|
|
5135
|
+
// src/lib/claude/links.ts
|
|
5136
|
+
const run$3 = promisify(execFile);
|
|
5137
|
+
const requiredFolders = [
|
|
5138
|
+
"projects",
|
|
5139
|
+
"file-history",
|
|
5140
|
+
"tasks",
|
|
5141
|
+
"todos",
|
|
5142
|
+
"plans",
|
|
5143
|
+
"paste-cache",
|
|
5144
|
+
"session-env",
|
|
5145
|
+
"jobs",
|
|
5146
|
+
"teams",
|
|
5147
|
+
"sessions"
|
|
5148
|
+
];
|
|
5149
|
+
const sharedFolders = [
|
|
5150
|
+
"skills",
|
|
5151
|
+
"agents",
|
|
5152
|
+
"commands",
|
|
5153
|
+
"output-styles",
|
|
5154
|
+
"plugins",
|
|
5155
|
+
"cache",
|
|
5156
|
+
"state",
|
|
5157
|
+
"shell-snapshots"
|
|
5158
|
+
];
|
|
5159
|
+
const sharedFiles = [
|
|
5160
|
+
"settings.json",
|
|
5161
|
+
"CLAUDE.md",
|
|
5162
|
+
"keybindings.json",
|
|
5163
|
+
"history.jsonl"
|
|
5164
|
+
];
|
|
5165
|
+
function isLinked(configDir) {
|
|
5166
|
+
const base = claudeBaseDir();
|
|
5167
|
+
return sameFile(configDir, base) || requiredFolders.every((name) => sameFile(path.join(configDir, name), path.join(base, name)));
|
|
5168
|
+
}
|
|
5169
|
+
function linkPlan(configDir) {
|
|
5170
|
+
const base = path.resolve(claudeBaseDir());
|
|
5171
|
+
const dir = statSync(configDir, { throwIfNoEntry: false });
|
|
5172
|
+
if (dir === void 0 || !dir.isDirectory()) throw new Error(`${configDir} does not exist`);
|
|
5173
|
+
if (sameFile(configDir, base)) throw new Error(`${configDir} is the main Claude folder`);
|
|
5174
|
+
return {
|
|
5175
|
+
configDir,
|
|
5176
|
+
baseDir: base,
|
|
5177
|
+
entries: [
|
|
5178
|
+
...requiredFolders.map((name) => entry(configDir, base, name, true, true)),
|
|
5179
|
+
...sharedFolders.map((name) => entry(configDir, base, name, false, true)),
|
|
5180
|
+
...sharedFiles.map((name) => entry(configDir, base, name, false, false))
|
|
5181
|
+
]
|
|
5182
|
+
};
|
|
5183
|
+
}
|
|
5184
|
+
async function linkClaude(configDir) {
|
|
5185
|
+
const plan = linkPlan(configDir);
|
|
5186
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replaceAll(":", "-");
|
|
5187
|
+
const steps = plan.entries.flatMap((item) => entrySteps(plan, item, stamp));
|
|
5188
|
+
if (steps.length === 0) return;
|
|
5189
|
+
for (const item of plan.entries) if (item.createInBase) mkdirSync(path.join(plan.baseDir, item.name), { recursive: true });
|
|
5190
|
+
if (process.platform === "win32" && !canSymlink()) await runElevated(steps);
|
|
5191
|
+
else for (const step of steps) runStep(step);
|
|
5192
|
+
const missing = plan.entries.filter((item) => item.action !== "skip" && !sameFile(path.join(plan.configDir, item.name), path.join(plan.baseDir, item.name)));
|
|
5193
|
+
if (missing.length > 0) throw new Error(`Could not link ${missing.map((item) => item.name).join(", ")}`);
|
|
5194
|
+
}
|
|
5195
|
+
function entry(configDir, base, name, required, folder) {
|
|
5196
|
+
const target = path.join(configDir, name);
|
|
5197
|
+
const source = path.join(base, name);
|
|
5198
|
+
const sourceExists = statSync(source, { throwIfNoEntry: false }) !== void 0;
|
|
5199
|
+
return {
|
|
5200
|
+
name,
|
|
5201
|
+
required,
|
|
5202
|
+
folder,
|
|
5203
|
+
action: entryAction(target, source, folder, sourceExists),
|
|
5204
|
+
createInBase: folder && !sourceExists
|
|
5205
|
+
};
|
|
5206
|
+
}
|
|
5207
|
+
function entryAction(target, source, folder, sourceExists) {
|
|
5208
|
+
if (!folder && !sourceExists) return "skip";
|
|
5209
|
+
const existing = lstatSync(target, { throwIfNoEntry: false });
|
|
5210
|
+
if (existing === void 0) return "link";
|
|
5211
|
+
if (sameFile(target, source)) return "linked";
|
|
5212
|
+
if (existing.isSymbolicLink()) return "relink";
|
|
5213
|
+
if (existing.isDirectory() && readdirSync(target).length === 0) return "replace";
|
|
5214
|
+
return "backup";
|
|
5215
|
+
}
|
|
5216
|
+
function entrySteps(plan, item, stamp) {
|
|
5217
|
+
const target = path.join(plan.configDir, item.name);
|
|
5218
|
+
const link = {
|
|
5219
|
+
type: "link",
|
|
5220
|
+
path: target,
|
|
5221
|
+
target: path.join(plan.baseDir, item.name),
|
|
5222
|
+
folder: item.folder
|
|
5223
|
+
};
|
|
5224
|
+
switch (item.action) {
|
|
5225
|
+
case "linked":
|
|
5226
|
+
case "skip": return [];
|
|
5227
|
+
case "link": return [link];
|
|
5228
|
+
case "relink": return [{
|
|
5229
|
+
type: "unlink",
|
|
5230
|
+
path: target
|
|
5231
|
+
}, link];
|
|
5232
|
+
case "replace": return [{
|
|
5233
|
+
type: "rmdir",
|
|
5234
|
+
path: target
|
|
5235
|
+
}, link];
|
|
5236
|
+
case "backup": {
|
|
5237
|
+
const backup = `${target}.backup-${stamp}`;
|
|
5238
|
+
if (lstatSync(backup, { throwIfNoEntry: false }) !== void 0) throw new Error(`${backup} already exists`);
|
|
5239
|
+
return [{
|
|
5240
|
+
type: "move",
|
|
5241
|
+
from: target,
|
|
5242
|
+
to: backup
|
|
5243
|
+
}, link];
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
function sameFile(first, second) {
|
|
5248
|
+
const firstStats = statSync(first, {
|
|
5249
|
+
bigint: true,
|
|
5250
|
+
throwIfNoEntry: false
|
|
5251
|
+
});
|
|
5252
|
+
const secondStats = statSync(second, {
|
|
5253
|
+
bigint: true,
|
|
5254
|
+
throwIfNoEntry: false
|
|
5255
|
+
});
|
|
5256
|
+
if (firstStats === void 0 || secondStats === void 0) return false;
|
|
5257
|
+
return firstStats.dev === secondStats.dev && firstStats.ino === secondStats.ino;
|
|
5258
|
+
}
|
|
5259
|
+
function canSymlink() {
|
|
5260
|
+
const dir = mkdtempSync(path.join(os.tmpdir(), "offsite-"));
|
|
5261
|
+
const link = path.join(dir, "link");
|
|
5262
|
+
try {
|
|
5263
|
+
symlinkSync(dir, link, "dir");
|
|
5264
|
+
} catch (error) {
|
|
5265
|
+
rmdirSync(dir);
|
|
5266
|
+
if (error.code === "EPERM") return false;
|
|
5267
|
+
throw error;
|
|
5268
|
+
}
|
|
5269
|
+
unlinkSync(link);
|
|
5270
|
+
rmdirSync(dir);
|
|
5271
|
+
return true;
|
|
5272
|
+
}
|
|
5273
|
+
function runStep(step) {
|
|
5274
|
+
switch (step.type) {
|
|
5275
|
+
case "unlink":
|
|
5276
|
+
unlinkSync(step.path);
|
|
5277
|
+
return;
|
|
5278
|
+
case "rmdir":
|
|
5279
|
+
rmdirSync(step.path);
|
|
5280
|
+
return;
|
|
5281
|
+
case "move":
|
|
5282
|
+
renameSync(step.from, step.to);
|
|
5283
|
+
return;
|
|
5284
|
+
case "link": symlinkSync(step.target, step.path, step.folder ? "dir" : "file");
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
async function runElevated(steps) {
|
|
5288
|
+
const user = await run$3("whoami", [
|
|
5289
|
+
"/user",
|
|
5290
|
+
"/fo",
|
|
5291
|
+
"csv",
|
|
5292
|
+
"/nh"
|
|
5293
|
+
]);
|
|
5294
|
+
const sid = user.stdout.trim().split(",").at(-1)?.replaceAll("\"", "");
|
|
5295
|
+
if (sid === void 0 || !sid.startsWith("S-1-")) throw new Error(`Could not read your Windows user: ${user.stdout}`);
|
|
5296
|
+
const owner = `*${sid}`;
|
|
5297
|
+
const dir = mkdtempSync(path.join(os.tmpdir(), "offsite-"));
|
|
5298
|
+
const errorFile = path.join(dir, "error.txt");
|
|
5299
|
+
const script = `const fs = require('node:fs')
|
|
5300
|
+
const childProcess = require('node:child_process')
|
|
5301
|
+
const data = ${JSON.stringify({
|
|
5302
|
+
steps,
|
|
5303
|
+
owner,
|
|
5304
|
+
errorFile
|
|
5305
|
+
})}
|
|
5306
|
+
try {
|
|
5307
|
+
for (const step of data.steps) {
|
|
5308
|
+
if (step.type === 'unlink') fs.unlinkSync(step.path)
|
|
5309
|
+
if (step.type === 'rmdir') fs.rmdirSync(step.path)
|
|
5310
|
+
if (step.type === 'move') fs.renameSync(step.from, step.to)
|
|
5311
|
+
if (step.type === 'link') {
|
|
5312
|
+
fs.symlinkSync(step.target, step.path, step.folder ? 'dir' : 'file')
|
|
5313
|
+
childProcess.execFileSync('icacls', [step.path, '/setowner', data.owner, '/L', '/Q'])
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
} catch (error) {
|
|
5317
|
+
fs.writeFileSync(data.errorFile, error.message)
|
|
5318
|
+
process.exit(1)
|
|
5319
|
+
}`;
|
|
5320
|
+
const code = `eval(Buffer.from('${Buffer.from(script).toString("base64")}','base64').toString())`;
|
|
5321
|
+
try {
|
|
5322
|
+
await run$3("powershell.exe", [
|
|
5323
|
+
"-NoProfile",
|
|
5324
|
+
"-NonInteractive",
|
|
5325
|
+
"-Command",
|
|
5326
|
+
`$ProgressPreference = 'SilentlyContinue'; try { $process = Start-Process -FilePath ${quote(process.execPath)} -ArgumentList '-e',${quote(code)} -Verb RunAs -WindowStyle Hidden -Wait -PassThru -ErrorAction Stop } catch { [Console]::Error.WriteLine($_.Exception.Message); exit 1 }; exit $process.ExitCode`
|
|
5327
|
+
]);
|
|
5328
|
+
} catch (error) {
|
|
5329
|
+
if (existsSync(errorFile)) throw new Error(readFileSync(errorFile, "utf8"));
|
|
5330
|
+
if (error instanceof Error && "stderr" in error && typeof error.stderr === "string" && error.stderr.trim() !== "") throw new Error(`Windows admin permission failed: ${error.stderr.trim()}`);
|
|
5331
|
+
throw error;
|
|
5332
|
+
} finally {
|
|
5333
|
+
rmSync(dir, {
|
|
5334
|
+
recursive: true,
|
|
5335
|
+
force: true
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
function quote(value) {
|
|
5340
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
5341
|
+
}
|
|
4958
5342
|
// src/lib/claude/claude.ts
|
|
4959
5343
|
const log$8 = logger.withTag("claude");
|
|
4960
5344
|
const efforts = [
|
|
@@ -5064,7 +5448,8 @@ function runOptions(options) {
|
|
|
5064
5448
|
...settings.model === null ? {} : { model: settings.model },
|
|
5065
5449
|
...settings.effort === null ? {} : { effort: claudeEffort(settings.effort) },
|
|
5066
5450
|
allowDangerouslySkipPermissions: true,
|
|
5067
|
-
includePartialMessages: true
|
|
5451
|
+
includePartialMessages: true,
|
|
5452
|
+
perTaskStopAffordance: true
|
|
5068
5453
|
};
|
|
5069
5454
|
}
|
|
5070
5455
|
async function claudeCapabilities(account, cwd) {
|
|
@@ -5213,16 +5598,6 @@ function claudeLogin(configDir) {
|
|
|
5213
5598
|
} : null
|
|
5214
5599
|
};
|
|
5215
5600
|
}
|
|
5216
|
-
function isLinked(configDir) {
|
|
5217
|
-
const base = claudeBaseDir();
|
|
5218
|
-
try {
|
|
5219
|
-
if (realpathSync(configDir) === realpathSync(base)) return true;
|
|
5220
|
-
return realpathSync(path.join(configDir, "projects")) === realpathSync(path.join(base, "projects"));
|
|
5221
|
-
} catch (error) {
|
|
5222
|
-
if (error.code === "ENOENT") return false;
|
|
5223
|
-
throw error;
|
|
5224
|
-
}
|
|
5225
|
-
}
|
|
5226
5601
|
function initialization(accountId, cwd) {
|
|
5227
5602
|
const key = `${accountId}\n${cwd}`;
|
|
5228
5603
|
const existing = probes.get(key);
|
|
@@ -5251,6 +5626,7 @@ async function* idle(until) {
|
|
|
5251
5626
|
}
|
|
5252
5627
|
// src/lib/claude/items.ts
|
|
5253
5628
|
const notifications = /<task-notification>([\s\S]*?)<\/task-notification>/g;
|
|
5629
|
+
const receivedMessage = /^(?:Another Claude session sent a message:\s*)?<(agent-message|cross-session-message) from="([^"]*)">([\s\S]*?)<\/\1>/;
|
|
5254
5630
|
const taskStatuses = /* @__PURE__ */ new Map([
|
|
5255
5631
|
["completed", "completed"],
|
|
5256
5632
|
["failed", "failed"],
|
|
@@ -5380,6 +5756,22 @@ function toItems(messages) {
|
|
|
5380
5756
|
}
|
|
5381
5757
|
continue;
|
|
5382
5758
|
}
|
|
5759
|
+
const received = receivedMessage.exec(block.text);
|
|
5760
|
+
if (received !== null) {
|
|
5761
|
+
const owner = owners.get(received[2]);
|
|
5762
|
+
const sender = owner === void 0 ? null : items[owner];
|
|
5763
|
+
items.push({
|
|
5764
|
+
id: `${message.uuid}:received`,
|
|
5765
|
+
parent: message.parent,
|
|
5766
|
+
time: message.time,
|
|
5767
|
+
kind: "received_message",
|
|
5768
|
+
source: received[1] === "agent-message" ? "agent" : "session",
|
|
5769
|
+
sender: sender?.kind === "subagent" ? sender.description : received[2],
|
|
5770
|
+
target: sender?.id ?? null,
|
|
5771
|
+
text: received[3].trim()
|
|
5772
|
+
});
|
|
5773
|
+
continue;
|
|
5774
|
+
}
|
|
5383
5775
|
if (block.text.startsWith("[Request interrupted by user")) {
|
|
5384
5776
|
items.push({
|
|
5385
5777
|
id: message.uuid,
|
|
@@ -5390,7 +5782,8 @@ function toItems(messages) {
|
|
|
5390
5782
|
});
|
|
5391
5783
|
continue;
|
|
5392
5784
|
}
|
|
5393
|
-
|
|
5785
|
+
const part = userText$2(block.text);
|
|
5786
|
+
if (part.text !== "") texts.push(part);
|
|
5394
5787
|
}
|
|
5395
5788
|
if (texts.length === 0 && images.length === 0) continue;
|
|
5396
5789
|
items.push({
|
|
@@ -5641,25 +6034,6 @@ function applyResult(item, result) {
|
|
|
5641
6034
|
default: return item;
|
|
5642
6035
|
}
|
|
5643
6036
|
}
|
|
5644
|
-
function userText$1(value) {
|
|
5645
|
-
const command = /<command-name>(.*?)<\/command-name>/s.exec(value);
|
|
5646
|
-
if (command !== null) {
|
|
5647
|
-
const args = /<command-args>(.*?)<\/command-args>/s.exec(value);
|
|
5648
|
-
return {
|
|
5649
|
-
format: "command",
|
|
5650
|
-
text: `${command[1]} ${args?.[1] ?? ""}`.trim()
|
|
5651
|
-
};
|
|
5652
|
-
}
|
|
5653
|
-
const output = /^<local-command-(?:stdout|stderr)>(.*)<\/local-command-(?:stdout|stderr)>$/s.exec(value.trim());
|
|
5654
|
-
if (output !== null) return {
|
|
5655
|
-
format: "output",
|
|
5656
|
-
text: output[1]
|
|
5657
|
-
};
|
|
5658
|
-
return {
|
|
5659
|
-
format: "text",
|
|
5660
|
-
text: value
|
|
5661
|
-
};
|
|
5662
|
-
}
|
|
5663
6037
|
function text$2(value) {
|
|
5664
6038
|
return typeof value === "string" ? value : "";
|
|
5665
6039
|
}
|
|
@@ -5784,10 +6158,18 @@ async function image(sessionId, uuid, index) {
|
|
|
5784
6158
|
data: block.data
|
|
5785
6159
|
};
|
|
5786
6160
|
}
|
|
6161
|
+
async function shownMessages(sessionId, cwd) {
|
|
6162
|
+
const [disk, session] = await Promise.all([getSessionMessages(sessionId, { dir: cwd }), loadSession(sessionId)]);
|
|
6163
|
+
const found = runs.get(sessionId);
|
|
6164
|
+
const saved = session?.entries.findLast((entry) => entry.type === "last-prompt" && entry.rewound === true);
|
|
6165
|
+
const cuts = [found?.provider === "claude" ? found.cut : null, saved?.cutUuid];
|
|
6166
|
+
const index = disk.findIndex((item) => cuts.includes(item.uuid));
|
|
6167
|
+
return index < 0 ? disk : disk.slice(0, index);
|
|
6168
|
+
}
|
|
5787
6169
|
async function history$1(sessionId, cwd) {
|
|
5788
6170
|
const found = runs.get(sessionId);
|
|
5789
6171
|
const run = found?.provider === "claude" ? found : void 0;
|
|
5790
|
-
const [disk, session] = await Promise.all([
|
|
6172
|
+
const [disk, session] = await Promise.all([shownMessages(sessionId, cwd), loadSession(sessionId)]);
|
|
5791
6173
|
const entries = session === null ? [] : session.entries;
|
|
5792
6174
|
const resumeAt = run?.resumeAt ?? null;
|
|
5793
6175
|
const kept = resumeAt === null ? disk : disk.slice(0, disk.findIndex((item) => item.uuid === resumeAt) + 1);
|
|
@@ -5830,6 +6212,28 @@ async function agentMessages(sessionId, cwd, agentId, parent) {
|
|
|
5830
6212
|
parent_tool_use_id: parent
|
|
5831
6213
|
}, null)]));
|
|
5832
6214
|
}
|
|
6215
|
+
// src/lib/claude/permission.ts
|
|
6216
|
+
function permissionResult(input, suggestions, decision, answers) {
|
|
6217
|
+
switch (decision) {
|
|
6218
|
+
case "allow": return {
|
|
6219
|
+
behavior: "allow",
|
|
6220
|
+
updatedInput: answers === null ? input : {
|
|
6221
|
+
...input,
|
|
6222
|
+
answers
|
|
6223
|
+
}
|
|
6224
|
+
};
|
|
6225
|
+
case "always": return {
|
|
6226
|
+
behavior: "allow",
|
|
6227
|
+
updatedInput: input,
|
|
6228
|
+
updatedPermissions: suggestions
|
|
6229
|
+
};
|
|
6230
|
+
case "deny": return {
|
|
6231
|
+
behavior: "deny",
|
|
6232
|
+
message: "The user rejected this action",
|
|
6233
|
+
interrupt: true
|
|
6234
|
+
};
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
5833
6237
|
// src/lib/claude/run.ts
|
|
5834
6238
|
const IDLE_LIMIT = 6e5;
|
|
5835
6239
|
var Run = class {
|
|
@@ -5842,6 +6246,7 @@ var Run = class {
|
|
|
5842
6246
|
query;
|
|
5843
6247
|
closed;
|
|
5844
6248
|
resumeAt;
|
|
6249
|
+
cut = null;
|
|
5845
6250
|
queue = [];
|
|
5846
6251
|
settings;
|
|
5847
6252
|
context = null;
|
|
@@ -5852,6 +6257,8 @@ var Run = class {
|
|
|
5852
6257
|
compacting = false;
|
|
5853
6258
|
backgroundTasks = 0;
|
|
5854
6259
|
backgroundAgents = 0;
|
|
6260
|
+
agentTasks = 0;
|
|
6261
|
+
failed = false;
|
|
5855
6262
|
tasks = {};
|
|
5856
6263
|
inbox = [];
|
|
5857
6264
|
wake = null;
|
|
@@ -5924,30 +6331,7 @@ var Run = class {
|
|
|
5924
6331
|
respond(requestId, decision, answers) {
|
|
5925
6332
|
const pending = this.pending.get(requestId);
|
|
5926
6333
|
if (pending === void 0) return;
|
|
5927
|
-
|
|
5928
|
-
switch (decision) {
|
|
5929
|
-
case "allow":
|
|
5930
|
-
this.settle(requestId, {
|
|
5931
|
-
behavior: "allow",
|
|
5932
|
-
updatedInput: answers === null ? input : {
|
|
5933
|
-
...input,
|
|
5934
|
-
answers
|
|
5935
|
-
}
|
|
5936
|
-
});
|
|
5937
|
-
return;
|
|
5938
|
-
case "always":
|
|
5939
|
-
this.settle(requestId, {
|
|
5940
|
-
behavior: "allow",
|
|
5941
|
-
updatedInput: input,
|
|
5942
|
-
updatedPermissions: pending.suggestions
|
|
5943
|
-
});
|
|
5944
|
-
return;
|
|
5945
|
-
case "deny": this.settle(requestId, {
|
|
5946
|
-
behavior: "deny",
|
|
5947
|
-
message: "The user rejected this action",
|
|
5948
|
-
interrupt: true
|
|
5949
|
-
});
|
|
5950
|
-
}
|
|
6334
|
+
this.settle(requestId, permissionResult(pending.input, pending.suggestions, decision, answers));
|
|
5951
6335
|
}
|
|
5952
6336
|
stop() {
|
|
5953
6337
|
this.stopping = true;
|
|
@@ -6041,7 +6425,9 @@ var Run = class {
|
|
|
6041
6425
|
return;
|
|
6042
6426
|
}
|
|
6043
6427
|
case "result": {
|
|
6044
|
-
|
|
6428
|
+
const failure = resultNotice(message);
|
|
6429
|
+
if (failure !== null) this.add(failure);
|
|
6430
|
+
this.failed = turnFailed(message);
|
|
6045
6431
|
this.finishTurn();
|
|
6046
6432
|
const usage = this.model === null ? void 0 : message.modelUsage[this.model];
|
|
6047
6433
|
if (this.context !== null && usage !== void 0) this.context = {
|
|
@@ -6063,6 +6449,7 @@ var Run = class {
|
|
|
6063
6449
|
});
|
|
6064
6450
|
return;
|
|
6065
6451
|
case "background_tasks_changed": {
|
|
6452
|
+
this.agentTasks = message.tasks.filter((task) => task.task_type !== "local_bash").length;
|
|
6066
6453
|
const live = message.tasks.filter((task) => task.ambient !== true);
|
|
6067
6454
|
this.backgroundTasks = live.length;
|
|
6068
6455
|
this.backgroundAgents = live.filter((task) => task.task_type === "local_agent").length;
|
|
@@ -6209,6 +6596,30 @@ var Run = class {
|
|
|
6209
6596
|
});
|
|
6210
6597
|
}
|
|
6211
6598
|
};
|
|
6599
|
+
// src/lib/claude/rewind.ts
|
|
6600
|
+
const ATTEMPTS = 10;
|
|
6601
|
+
const answerSchema = z.object({ response: z.discriminatedUnion("rewound", [z.object({ rewound: z.literal(true) }), z.object({
|
|
6602
|
+
rewound: z.literal(false),
|
|
6603
|
+
reason: z.string(),
|
|
6604
|
+
error: z.string()
|
|
6605
|
+
})]) });
|
|
6606
|
+
async function rewindLive(run, messageId, lastSeenId) {
|
|
6607
|
+
const request = Reflect.get(run.query, "request");
|
|
6608
|
+
if (typeof request !== "function") throw new Error("This Claude Code version cannot rewind a live session");
|
|
6609
|
+
for (let attempt = 1;; attempt++) {
|
|
6610
|
+
const answer = answerSchema.parse(await Reflect.apply(request, run.query, [{
|
|
6611
|
+
subtype: "rewind_conversation",
|
|
6612
|
+
target_message_uuid: messageId,
|
|
6613
|
+
last_seen_user_message_uuid: lastSeenId
|
|
6614
|
+
}])).response;
|
|
6615
|
+
if (answer.rewound) break;
|
|
6616
|
+
if (answer.reason !== "turn_running" || attempt === ATTEMPTS) throw new Error(`Claude Code could not rewind: ${answer.error}`);
|
|
6617
|
+
await setTimeout$1(500);
|
|
6618
|
+
}
|
|
6619
|
+
const index = run.messages.findIndex((item) => item.uuid === messageId);
|
|
6620
|
+
run.messages.splice(index < 0 ? 0 : index);
|
|
6621
|
+
run.cut = messageId;
|
|
6622
|
+
}
|
|
6212
6623
|
// src/lib/proxy/sites.ts
|
|
6213
6624
|
const sites = /* @__PURE__ */ new Map();
|
|
6214
6625
|
const siteNames = /* @__PURE__ */ new Map();
|
|
@@ -6360,7 +6771,6 @@ async function rewindNow(request) {
|
|
|
6360
6771
|
const sessionId = request.sessionId;
|
|
6361
6772
|
const cwd = projectPath(request.projectId);
|
|
6362
6773
|
if (runs.get(sessionId)?.running === true) throw new Error(`Stop ${providerName(providerOf(sessionId))} before rewinding`);
|
|
6363
|
-
if ((runs.get(sessionId)?.backgroundTasks ?? 0) > 0) throw new Error("Wait for background tasks to finish before rewinding");
|
|
6364
6774
|
const run = await runFor(sessionId, cwd, request.settings);
|
|
6365
6775
|
if (run.provider === "codex") {
|
|
6366
6776
|
if (request.code) throw new Error("Codex cannot restore files");
|
|
@@ -6390,9 +6800,16 @@ async function rewindNow(request) {
|
|
|
6390
6800
|
};
|
|
6391
6801
|
}
|
|
6392
6802
|
if (request.dryRun || !request.conversation) return result;
|
|
6393
|
-
const disk = await
|
|
6803
|
+
const disk = await shownMessages(sessionId, cwd);
|
|
6394
6804
|
const index = disk.findIndex((item) => item.uuid === request.messageId);
|
|
6395
6805
|
if (index < 1) throw new Error("Cannot rewind the conversation to that point");
|
|
6806
|
+
if (run.backgroundTasks > 0) {
|
|
6807
|
+
const lastSeen = disk.findLast((item) => item.type === "user" && item.parent_tool_use_id === null);
|
|
6808
|
+
if (lastSeen === void 0) throw new Error("Cannot rewind the conversation to that point");
|
|
6809
|
+
await rewindLive(run, request.messageId, lastSeen.uuid);
|
|
6810
|
+
publish$2(sessionId, await history$1(sessionId, cwd));
|
|
6811
|
+
return result;
|
|
6812
|
+
}
|
|
6396
6813
|
run.stop();
|
|
6397
6814
|
await run.closed;
|
|
6398
6815
|
new Run({
|
|
@@ -6443,7 +6860,7 @@ async function submitFirst(client, run, request, projectId) {
|
|
|
6443
6860
|
from: request.projectId
|
|
6444
6861
|
});
|
|
6445
6862
|
await run.submit(request.prompt, request.delivery);
|
|
6446
|
-
recordInteraction(sessionId, run.provider);
|
|
6863
|
+
recordInteraction(sessionId, run.provider, request.status);
|
|
6447
6864
|
}
|
|
6448
6865
|
async function sendExisting(client, sessionId, cwd, request) {
|
|
6449
6866
|
const provider = providerOf(sessionId);
|
|
@@ -6457,7 +6874,7 @@ async function sendExisting(client, sessionId, cwd, request) {
|
|
|
6457
6874
|
if (run === existing) await run.configure(request.settings);
|
|
6458
6875
|
else await open$1(client, sessionId, request.projectId);
|
|
6459
6876
|
await run.submit(request.prompt, request.delivery);
|
|
6460
|
-
recordInteraction(sessionId, provider);
|
|
6877
|
+
recordInteraction(sessionId, provider, request.status);
|
|
6461
6878
|
if (emptied.has(sessionId) && request.prompt.text.trim() !== "") {
|
|
6462
6879
|
emptied.delete(sessionId);
|
|
6463
6880
|
await updateSession(sessionId, { title: request.prompt.text.trim() });
|
|
@@ -6507,7 +6924,7 @@ async function startRun(cwd, settings) {
|
|
|
6507
6924
|
}
|
|
6508
6925
|
async function changedElsewhere(run, cwd) {
|
|
6509
6926
|
if (run.messages.length === 0) return false;
|
|
6510
|
-
const last = (await
|
|
6927
|
+
const last = (await shownMessages(run.sessionId, cwd)).at(-1)?.uuid;
|
|
6511
6928
|
return last !== void 0 && !run.messages.some((item) => item.uuid === last);
|
|
6512
6929
|
}
|
|
6513
6930
|
// src/services/export.ts
|
|
@@ -6602,6 +7019,7 @@ function itemText(item, level) {
|
|
|
6602
7019
|
}
|
|
6603
7020
|
case "subagent": return level < 1 ? null : `**Subagent (${item.agent}):** ${item.description}${statusText(item.status)}`;
|
|
6604
7021
|
case "agent_message": return level < 1 ? null : `**Message${item.target === null ? "" : ` to ${item.target}`}:** ${item.summary}${statusText(item.status)}`;
|
|
7022
|
+
case "received_message": return level < 1 ? null : `**Message from ${item.sender}:**\n\n${item.text}`;
|
|
6605
7023
|
case "notice": return level < 1 ? null : `_${item.text}_`;
|
|
6606
7024
|
case "compaction":
|
|
6607
7025
|
if (level < 1) return null;
|
|
@@ -6646,7 +7064,7 @@ async function searchMessages(query) {
|
|
|
6646
7064
|
"claude",
|
|
6647
7065
|
"codex",
|
|
6648
7066
|
"opencode"
|
|
6649
|
-
].filter((provider) => readConfig()
|
|
7067
|
+
].filter((provider) => readConfig()[provider].enabled).map(async (provider) => {
|
|
6650
7068
|
try {
|
|
6651
7069
|
return {
|
|
6652
7070
|
provider,
|
|
@@ -8174,8 +8592,8 @@ const handlers = {
|
|
|
8174
8592
|
"claude",
|
|
8175
8593
|
"codex",
|
|
8176
8594
|
"opencode"
|
|
8177
|
-
]) if (before
|
|
8178
|
-
if (JSON.stringify(saved.
|
|
8595
|
+
]) if (before[provider].enabled && !saved[provider].enabled) await stopProvider(provider);
|
|
8596
|
+
if (JSON.stringify(saved.claude.baseDir) === JSON.stringify(before.claude.baseDir) && saved.claude.enabled === before.claude.enabled && saved.codex.enabled === before.codex.enabled && saved.opencode.enabled === before.opencode.enabled) return saved;
|
|
8179
8597
|
watchSessions();
|
|
8180
8598
|
refreshAll();
|
|
8181
8599
|
publishAccounts();
|
|
@@ -8262,7 +8680,7 @@ const handlers = {
|
|
|
8262
8680
|
const run = runs.get(params.sessionId);
|
|
8263
8681
|
if (run === void 0) return null;
|
|
8264
8682
|
await run.respond(params.requestId, params.decision, params.answers);
|
|
8265
|
-
recordInteraction(params.sessionId, run.provider);
|
|
8683
|
+
recordInteraction(params.sessionId, run.provider, null);
|
|
8266
8684
|
return null;
|
|
8267
8685
|
},
|
|
8268
8686
|
"sessions.rewind": (params) => rewind(params),
|
|
@@ -8272,21 +8690,11 @@ const handlers = {
|
|
|
8272
8690
|
return result;
|
|
8273
8691
|
},
|
|
8274
8692
|
"accounts.list": () => accounts(),
|
|
8275
|
-
"accounts.add": (params) =>
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
"accounts.update": async (params) => {
|
|
8281
|
-
await updateAccount(params);
|
|
8282
|
-
publishAccounts();
|
|
8283
|
-
return null;
|
|
8284
|
-
},
|
|
8285
|
-
"accounts.remove": (params) => {
|
|
8286
|
-
removeAccount(params.id);
|
|
8287
|
-
publishAccounts();
|
|
8288
|
-
return null;
|
|
8289
|
-
},
|
|
8693
|
+
"accounts.add": (params) => addAccount(params.provider, params.name, params.configDir),
|
|
8694
|
+
"accounts.update": (params) => updateAccount(params),
|
|
8695
|
+
"accounts.remove": (params) => removeAccount(params.id),
|
|
8696
|
+
"accounts.link": (params) => linkAccount(params.id),
|
|
8697
|
+
"accounts.linkPlan": (params) => accountLinkPlan(params.id),
|
|
8290
8698
|
"accounts.usage": (params) => readUsage(params.id, params.force),
|
|
8291
8699
|
capabilities: (params) => capabilities(params.provider, params.accountId, params.projectId === null || params.projectId === "no-project" ? os.homedir() : projectPath(params.projectId)),
|
|
8292
8700
|
"files.list": (params) => params.projectId === "no-project" ? [] : listFiles(projectPath(params.projectId)),
|
|
@@ -9671,7 +10079,7 @@ async function backfillSessionProviders() {
|
|
|
9671
10079
|
const rows = db.select().from(sessions$1).where(isNull(sessions$1.provider)).all();
|
|
9672
10080
|
if (rows.length === 0) return;
|
|
9673
10081
|
const paths = new Map(projects().map((project) => [project.path, project.id]));
|
|
9674
|
-
const enabled = allProviders.filter((provider) => readConfig()
|
|
10082
|
+
const enabled = allProviders.filter((provider) => readConfig()[provider].enabled);
|
|
9675
10083
|
for (const row of rows) {
|
|
9676
10084
|
const answers = await Promise.all(enabled.map(async (provider) => {
|
|
9677
10085
|
try {
|