@adhdev/daemon-core 0.9.53 → 0.9.55
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/boot/daemon-lifecycle.d.ts +5 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-config.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/dist/commands/handler.d.ts +7 -0
- package/dist/git/git-commands.d.ts +139 -0
- package/dist/git/git-diff.d.ts +17 -0
- package/dist/git/git-executor.d.ts +34 -0
- package/dist/git/git-monitor.d.ts +57 -0
- package/dist/git/git-snapshot-store.d.ts +50 -0
- package/dist/git/git-status.d.ts +19 -0
- package/dist/git/git-summary.d.ts +10 -0
- package/dist/git/git-types.d.ts +113 -0
- package/dist/git/index.d.ts +16 -0
- package/dist/git/turn-snapshot-tracker.d.ts +16 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1772 -386
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1744 -383
- package/dist/index.mjs.map +1 -1
- package/dist/providers/contracts.d.ts +2 -0
- package/dist/shared-types.d.ts +7 -1
- package/dist/status/builders.d.ts +2 -0
- package/dist/status/snapshot.d.ts +2 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +6 -0
- package/src/cli-adapters/provider-cli-adapter.ts +19 -0
- package/src/cli-adapters/provider-cli-config.d.ts +1 -0
- package/src/cli-adapters/provider-cli-config.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +2 -0
- package/src/commands/handler.ts +25 -1
- package/src/git/git-commands.ts +582 -0
- package/src/git/git-diff.ts +303 -0
- package/src/git/git-executor.ts +268 -0
- package/src/git/git-monitor.ts +194 -0
- package/src/git/git-snapshot-store.ts +238 -0
- package/src/git/git-status.ts +193 -0
- package/src/git/git-summary.ts +43 -0
- package/src/git/git-types.ts +154 -0
- package/src/git/index.ts +75 -0
- package/src/git/turn-snapshot-tracker.ts +31 -0
- package/src/index.ts +4 -0
- package/src/providers/contracts.d.ts +8 -0
- package/src/providers/contracts.ts +2 -0
- package/src/providers/provider-schema.ts +1 -0
- package/src/shared-types.ts +33 -1
- package/src/status/builders.ts +26 -4
- package/src/status/snapshot.ts +10 -2
package/dist/index.mjs
CHANGED
|
@@ -265,7 +265,7 @@ var init_config = __esm({
|
|
|
265
265
|
|
|
266
266
|
// src/logging/logger.ts
|
|
267
267
|
import * as fs2 from "fs";
|
|
268
|
-
import * as
|
|
268
|
+
import * as path9 from "path";
|
|
269
269
|
import * as os4 from "os";
|
|
270
270
|
function setLogLevel(level) {
|
|
271
271
|
currentLevel = level;
|
|
@@ -281,13 +281,13 @@ function getDaemonLogDir() {
|
|
|
281
281
|
return LOG_DIR;
|
|
282
282
|
}
|
|
283
283
|
function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
|
|
284
|
-
return
|
|
284
|
+
return path9.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
|
|
285
285
|
}
|
|
286
286
|
function checkDateRotation() {
|
|
287
287
|
const today = getDateStr();
|
|
288
288
|
if (today !== currentDate) {
|
|
289
289
|
currentDate = today;
|
|
290
|
-
currentLogFile =
|
|
290
|
+
currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
291
291
|
cleanOldLogs();
|
|
292
292
|
}
|
|
293
293
|
}
|
|
@@ -301,7 +301,7 @@ function cleanOldLogs() {
|
|
|
301
301
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
302
302
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
303
303
|
try {
|
|
304
|
-
fs2.unlinkSync(
|
|
304
|
+
fs2.unlinkSync(path9.join(LOG_DIR, file));
|
|
305
305
|
} catch {
|
|
306
306
|
}
|
|
307
307
|
}
|
|
@@ -311,8 +311,8 @@ function cleanOldLogs() {
|
|
|
311
311
|
}
|
|
312
312
|
function rotateSizeIfNeeded() {
|
|
313
313
|
try {
|
|
314
|
-
const
|
|
315
|
-
if (
|
|
314
|
+
const stat2 = fs2.statSync(currentLogFile);
|
|
315
|
+
if (stat2.size > MAX_LOG_SIZE) {
|
|
316
316
|
const backup = currentLogFile.replace(".log", ".1.log");
|
|
317
317
|
try {
|
|
318
318
|
fs2.unlinkSync(backup);
|
|
@@ -424,7 +424,7 @@ var init_logger = __esm({
|
|
|
424
424
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
425
425
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
426
426
|
currentLevel = "info";
|
|
427
|
-
LOG_DIR = process.platform === "win32" ?
|
|
427
|
+
LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os4.homedir(), "Library", "Logs", "adhdev") : path9.join(os4.homedir(), ".local", "share", "adhdev", "logs");
|
|
428
428
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
429
429
|
MAX_LOG_DAYS = 7;
|
|
430
430
|
try {
|
|
@@ -432,16 +432,16 @@ var init_logger = __esm({
|
|
|
432
432
|
} catch {
|
|
433
433
|
}
|
|
434
434
|
currentDate = getDateStr();
|
|
435
|
-
currentLogFile =
|
|
435
|
+
currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
436
436
|
cleanOldLogs();
|
|
437
437
|
try {
|
|
438
|
-
const oldLog =
|
|
438
|
+
const oldLog = path9.join(LOG_DIR, "daemon.log");
|
|
439
439
|
if (fs2.existsSync(oldLog)) {
|
|
440
|
-
const
|
|
441
|
-
const oldDate =
|
|
442
|
-
fs2.renameSync(oldLog,
|
|
440
|
+
const stat2 = fs2.statSync(oldLog);
|
|
441
|
+
const oldDate = stat2.mtime.toISOString().slice(0, 10);
|
|
442
|
+
fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
443
443
|
}
|
|
444
|
-
const oldLogBackup =
|
|
444
|
+
const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
|
|
445
445
|
if (fs2.existsSync(oldLogBackup)) {
|
|
446
446
|
fs2.unlinkSync(oldLogBackup);
|
|
447
447
|
}
|
|
@@ -473,7 +473,7 @@ var init_logger = __esm({
|
|
|
473
473
|
}
|
|
474
474
|
};
|
|
475
475
|
interceptorInstalled = false;
|
|
476
|
-
LOG_PATH =
|
|
476
|
+
LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
477
477
|
}
|
|
478
478
|
});
|
|
479
479
|
|
|
@@ -1380,8 +1380,8 @@ var init_pty_transport = __esm({
|
|
|
1380
1380
|
if (cwd) {
|
|
1381
1381
|
try {
|
|
1382
1382
|
const fs16 = __require("fs");
|
|
1383
|
-
const
|
|
1384
|
-
if (!
|
|
1383
|
+
const stat2 = fs16.statSync(cwd);
|
|
1384
|
+
if (!stat2.isDirectory()) cwd = os8.homedir();
|
|
1385
1385
|
} catch {
|
|
1386
1386
|
cwd = os8.homedir();
|
|
1387
1387
|
}
|
|
@@ -1401,7 +1401,7 @@ var init_pty_transport = __esm({
|
|
|
1401
1401
|
|
|
1402
1402
|
// src/cli-adapters/provider-cli-shared.ts
|
|
1403
1403
|
import * as os9 from "os";
|
|
1404
|
-
import * as
|
|
1404
|
+
import * as path13 from "path";
|
|
1405
1405
|
import { execSync as execSync3 } from "child_process";
|
|
1406
1406
|
function stripAnsi(str) {
|
|
1407
1407
|
return str.replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][\s\S]*?\x1B\\/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B\[\d*[A-HJKSTfG]/g, " ").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/ +/g, " ");
|
|
@@ -1466,9 +1466,9 @@ function buildCliScreenSnapshot(text) {
|
|
|
1466
1466
|
function findBinary(name) {
|
|
1467
1467
|
const trimmed = String(name || "").trim();
|
|
1468
1468
|
if (!trimmed) return trimmed;
|
|
1469
|
-
const expanded = trimmed.startsWith("~") ?
|
|
1470
|
-
if (
|
|
1471
|
-
return
|
|
1469
|
+
const expanded = trimmed.startsWith("~") ? path13.join(os9.homedir(), trimmed.slice(1)) : trimmed;
|
|
1470
|
+
if (path13.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
|
|
1471
|
+
return path13.isAbsolute(expanded) ? expanded : path13.resolve(expanded);
|
|
1472
1472
|
}
|
|
1473
1473
|
const isWin = os9.platform() === "win32";
|
|
1474
1474
|
try {
|
|
@@ -1484,7 +1484,7 @@ function findBinary(name) {
|
|
|
1484
1484
|
}
|
|
1485
1485
|
}
|
|
1486
1486
|
function isScriptBinary(binaryPath) {
|
|
1487
|
-
if (!
|
|
1487
|
+
if (!path13.isAbsolute(binaryPath)) return false;
|
|
1488
1488
|
try {
|
|
1489
1489
|
const fs16 = __require("fs");
|
|
1490
1490
|
const resolved = fs16.realpathSync(binaryPath);
|
|
@@ -1500,7 +1500,7 @@ function isScriptBinary(binaryPath) {
|
|
|
1500
1500
|
}
|
|
1501
1501
|
}
|
|
1502
1502
|
function looksLikeMachOOrElf(filePath) {
|
|
1503
|
-
if (!
|
|
1503
|
+
if (!path13.isAbsolute(filePath)) return false;
|
|
1504
1504
|
try {
|
|
1505
1505
|
const fs16 = __require("fs");
|
|
1506
1506
|
const resolved = fs16.realpathSync(filePath);
|
|
@@ -1940,6 +1940,7 @@ function resolveCliAdapterConfig(provider) {
|
|
|
1940
1940
|
sendDelayMs: typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0,
|
|
1941
1941
|
sendKey: typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r",
|
|
1942
1942
|
submitStrategy: provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo",
|
|
1943
|
+
requirePromptEchoBeforeSubmit: provider.requirePromptEchoBeforeSubmit === true,
|
|
1943
1944
|
providerResolutionMeta: {
|
|
1944
1945
|
type: provider.type,
|
|
1945
1946
|
name: provider.name,
|
|
@@ -1961,7 +1962,7 @@ var init_provider_cli_config = __esm({
|
|
|
1961
1962
|
|
|
1962
1963
|
// src/cli-adapters/provider-cli-runtime.ts
|
|
1963
1964
|
import * as os10 from "os";
|
|
1964
|
-
import * as
|
|
1965
|
+
import * as path14 from "path";
|
|
1965
1966
|
import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from "@adhdev/session-host-core";
|
|
1966
1967
|
function resolveCliSpawnPlan(options) {
|
|
1967
1968
|
const { provider, runtimeSettings, workingDir, extraArgs } = options;
|
|
@@ -1972,9 +1973,9 @@ function resolveCliSpawnPlan(options) {
|
|
|
1972
1973
|
const allArgs = [...spawnConfig.args, ...extraArgs];
|
|
1973
1974
|
let shellCmd;
|
|
1974
1975
|
let shellArgs;
|
|
1975
|
-
const useShellUnix = !isWin && (!!spawnConfig.shell || !
|
|
1976
|
+
const useShellUnix = !isWin && (!!spawnConfig.shell || !path14.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
|
|
1976
1977
|
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
1977
|
-
const useShellWin = !!spawnConfig.shell || isCmdShim || !
|
|
1978
|
+
const useShellWin = !!spawnConfig.shell || isCmdShim || !path14.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1978
1979
|
const useShell = isWin ? useShellWin : useShellUnix;
|
|
1979
1980
|
if (useShell) {
|
|
1980
1981
|
shellCmd = isWin ? "cmd.exe" : process.env.SHELL || "/bin/zsh";
|
|
@@ -2204,6 +2205,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2204
2205
|
this.sendDelayMs = resolvedConfig.sendDelayMs;
|
|
2205
2206
|
this.sendKey = resolvedConfig.sendKey;
|
|
2206
2207
|
this.submitStrategy = resolvedConfig.submitStrategy;
|
|
2208
|
+
this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
|
|
2207
2209
|
this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
|
|
2208
2210
|
this.cliScripts = provider.scripts || {};
|
|
2209
2211
|
const scriptNames = listCliScriptNames(this.cliScripts);
|
|
@@ -2500,6 +2502,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2500
2502
|
sendDelayMs;
|
|
2501
2503
|
sendKey;
|
|
2502
2504
|
submitStrategy;
|
|
2505
|
+
requirePromptEchoBeforeSubmit;
|
|
2503
2506
|
static SCRIPT_STATUS_DEBOUNCE_MS = 3e3;
|
|
2504
2507
|
/** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
|
|
2505
2508
|
setCliScripts(scripts) {
|
|
@@ -2851,7 +2854,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
2851
2854
|
`[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
|
|
2852
2855
|
);
|
|
2853
2856
|
}
|
|
2854
|
-
await new Promise((
|
|
2857
|
+
await new Promise((resolve15) => setTimeout(resolve15, 50));
|
|
2855
2858
|
}
|
|
2856
2859
|
const finalScreenText = this.terminalScreen.getText() || "";
|
|
2857
2860
|
LOG.warn(
|
|
@@ -4020,6 +4023,22 @@ var init_provider_cli_adapter = __esm({
|
|
|
4020
4023
|
}
|
|
4021
4024
|
}
|
|
4022
4025
|
if (elapsed >= state.maxEchoWaitMs) {
|
|
4026
|
+
const diagnostic = {
|
|
4027
|
+
elapsed,
|
|
4028
|
+
maxEchoWaitMs: state.maxEchoWaitMs,
|
|
4029
|
+
submitDelayMs: state.submitDelayMs,
|
|
4030
|
+
promptSnippet: state.normalizedPromptSnippet,
|
|
4031
|
+
requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
|
|
4032
|
+
screenText: summarizeCliTraceText(screenText, 1e3)
|
|
4033
|
+
};
|
|
4034
|
+
this.recordTrace("submit_echo_missing", diagnostic);
|
|
4035
|
+
if (this.requirePromptEchoBeforeSubmit) {
|
|
4036
|
+
const message = `${this.cliName} prompt echo was not observed on the PTY screen before submit`;
|
|
4037
|
+
LOG.warn("CLI", `[${this.cliType}] ${message} elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
|
|
4038
|
+
completion.rejectOnce(new Error(message));
|
|
4039
|
+
return;
|
|
4040
|
+
}
|
|
4041
|
+
LOG.warn("CLI", `[${this.cliType}] prompt echo was not observed before submit; sending submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs}`);
|
|
4023
4042
|
this.submitSendKey(state, completion);
|
|
4024
4043
|
return;
|
|
4025
4044
|
}
|
|
@@ -4039,7 +4058,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
4039
4058
|
const deadline = Date.now() + 1e4;
|
|
4040
4059
|
while (this.startupParseGate && Date.now() < deadline) {
|
|
4041
4060
|
this.resolveStartupState("send_wait");
|
|
4042
|
-
await new Promise((
|
|
4061
|
+
await new Promise((resolve15) => setTimeout(resolve15, 50));
|
|
4043
4062
|
}
|
|
4044
4063
|
}
|
|
4045
4064
|
if (!allowInterventionPrompt) {
|
|
@@ -4120,13 +4139,13 @@ var init_provider_cli_adapter = __esm({
|
|
|
4120
4139
|
}
|
|
4121
4140
|
this.responseEpoch += 1;
|
|
4122
4141
|
this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
|
|
4123
|
-
await new Promise((
|
|
4142
|
+
await new Promise((resolve15, reject) => {
|
|
4124
4143
|
let resolved = false;
|
|
4125
4144
|
const completion = {
|
|
4126
4145
|
resolveOnce: () => {
|
|
4127
4146
|
if (resolved) return;
|
|
4128
4147
|
resolved = true;
|
|
4129
|
-
|
|
4148
|
+
resolve15();
|
|
4130
4149
|
},
|
|
4131
4150
|
rejectOnce: (error) => {
|
|
4132
4151
|
if (resolved) return;
|
|
@@ -4288,17 +4307,17 @@ var init_provider_cli_adapter = __esm({
|
|
|
4288
4307
|
}
|
|
4289
4308
|
}
|
|
4290
4309
|
waitForStopped(timeoutMs) {
|
|
4291
|
-
return new Promise((
|
|
4310
|
+
return new Promise((resolve15) => {
|
|
4292
4311
|
const startedAt = Date.now();
|
|
4293
4312
|
const timer = setInterval(() => {
|
|
4294
4313
|
if (!this.ptyProcess || this.currentStatus === "stopped") {
|
|
4295
4314
|
clearInterval(timer);
|
|
4296
|
-
|
|
4315
|
+
resolve15(true);
|
|
4297
4316
|
return;
|
|
4298
4317
|
}
|
|
4299
4318
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
4300
4319
|
clearInterval(timer);
|
|
4301
|
-
|
|
4320
|
+
resolve15(false);
|
|
4302
4321
|
}
|
|
4303
4322
|
}, 100);
|
|
4304
4323
|
});
|
|
@@ -4484,6 +4503,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
4484
4503
|
sendDelayMs: this.sendDelayMs,
|
|
4485
4504
|
sendKey: this.sendKey,
|
|
4486
4505
|
submitStrategy: this.submitStrategy,
|
|
4506
|
+
requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
|
|
4487
4507
|
submitPendingUntil: this.submitPendingUntil,
|
|
4488
4508
|
responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
|
|
4489
4509
|
resizeSuppressUntil: this.resizeSuppressUntil,
|
|
@@ -4524,20 +4544,1298 @@ var init_provider_cli_adapter = __esm({
|
|
|
4524
4544
|
}
|
|
4525
4545
|
});
|
|
4526
4546
|
|
|
4547
|
+
// src/git/git-executor.ts
|
|
4548
|
+
import { execFile } from "child_process";
|
|
4549
|
+
import { constants } from "fs";
|
|
4550
|
+
import { access, realpath, stat } from "fs/promises";
|
|
4551
|
+
import * as path from "path";
|
|
4552
|
+
import { promisify } from "util";
|
|
4553
|
+
var execFileAsync = promisify(execFile);
|
|
4554
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
4555
|
+
var DEFAULT_MAX_BUFFER = 1024 * 1024;
|
|
4556
|
+
var GitCommandError = class extends Error {
|
|
4557
|
+
reason;
|
|
4558
|
+
stdout;
|
|
4559
|
+
stderr;
|
|
4560
|
+
exitCode;
|
|
4561
|
+
signal;
|
|
4562
|
+
argv;
|
|
4563
|
+
cwd;
|
|
4564
|
+
constructor(reason, message, details = {}) {
|
|
4565
|
+
super(message);
|
|
4566
|
+
if (details.cause !== void 0) {
|
|
4567
|
+
this.cause = details.cause;
|
|
4568
|
+
}
|
|
4569
|
+
this.name = "GitCommandError";
|
|
4570
|
+
this.reason = reason;
|
|
4571
|
+
this.stdout = normalizeGitOutput(details.stdout);
|
|
4572
|
+
this.stderr = normalizeGitOutput(details.stderr);
|
|
4573
|
+
this.exitCode = details.exitCode;
|
|
4574
|
+
this.signal = details.signal;
|
|
4575
|
+
this.argv = details.argv ? [...details.argv] : void 0;
|
|
4576
|
+
this.cwd = details.cwd;
|
|
4577
|
+
}
|
|
4578
|
+
};
|
|
4579
|
+
async function resolveGitRepository(workspace, options = {}) {
|
|
4580
|
+
const normalizedWorkspace = await validateWorkspace(workspace);
|
|
4581
|
+
const result = await execGitRaw(normalizedWorkspace, ["rev-parse", "--show-toplevel"], options, {
|
|
4582
|
+
mapNotGitRepo: true
|
|
4583
|
+
});
|
|
4584
|
+
const repoRoot = path.resolve(result.stdout.trim());
|
|
4585
|
+
if (!repoRoot) {
|
|
4586
|
+
throw new GitCommandError("not_git_repo", "Git did not return a repository root", {
|
|
4587
|
+
stdout: result.stdout,
|
|
4588
|
+
stderr: result.stderr,
|
|
4589
|
+
argv: ["rev-parse", "--show-toplevel"],
|
|
4590
|
+
cwd: normalizedWorkspace
|
|
4591
|
+
});
|
|
4592
|
+
}
|
|
4593
|
+
return {
|
|
4594
|
+
workspace: normalizedWorkspace,
|
|
4595
|
+
repoRoot,
|
|
4596
|
+
isGitRepo: true
|
|
4597
|
+
};
|
|
4598
|
+
}
|
|
4599
|
+
async function runGit(repoOrWorkspace, argv, options = {}) {
|
|
4600
|
+
validateGitArgv(argv);
|
|
4601
|
+
const repo = typeof repoOrWorkspace === "string" ? await resolveGitRepository(repoOrWorkspace, options) : repoOrWorkspace;
|
|
4602
|
+
if (!repo.repoRoot || !repo.isGitRepo) {
|
|
4603
|
+
throw new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
|
|
4604
|
+
argv,
|
|
4605
|
+
cwd: repo.workspace
|
|
4606
|
+
});
|
|
4607
|
+
}
|
|
4608
|
+
const cwd = options.cwd ? await validateWorkspace(options.cwd) : await validateWorkspace(repo.workspace);
|
|
4609
|
+
const canonicalRepoRoot = await realpath(repo.repoRoot);
|
|
4610
|
+
const canonicalCwd = await realpath(cwd);
|
|
4611
|
+
if (!isPathInside(canonicalRepoRoot, canonicalCwd)) {
|
|
4612
|
+
throw new GitCommandError("path_outside_repo", "Git cwd is outside the repository root", {
|
|
4613
|
+
argv,
|
|
4614
|
+
cwd
|
|
4615
|
+
});
|
|
4616
|
+
}
|
|
4617
|
+
return execGitRaw(cwd, argv, options);
|
|
4618
|
+
}
|
|
4619
|
+
function normalizeGitOutput(value) {
|
|
4620
|
+
if (typeof value === "string") return value.replace(/\r\n/g, "\n");
|
|
4621
|
+
if (Buffer.isBuffer(value)) return value.toString("utf8").replace(/\r\n/g, "\n");
|
|
4622
|
+
if (value == null) return "";
|
|
4623
|
+
return String(value).replace(/\r\n/g, "\n");
|
|
4624
|
+
}
|
|
4625
|
+
function isPathInside(parent, child) {
|
|
4626
|
+
const relative3 = path.relative(path.resolve(parent), path.resolve(child));
|
|
4627
|
+
return relative3 === "" || !relative3.startsWith("..") && !path.isAbsolute(relative3);
|
|
4628
|
+
}
|
|
4629
|
+
async function validateWorkspace(workspace) {
|
|
4630
|
+
if (typeof workspace !== "string" || workspace.length === 0 || workspace.includes("\0")) {
|
|
4631
|
+
throw new GitCommandError("invalid_args", "Workspace must be a non-empty path");
|
|
4632
|
+
}
|
|
4633
|
+
if (!path.isAbsolute(workspace)) {
|
|
4634
|
+
throw new GitCommandError("invalid_args", "Workspace must be an absolute path", { cwd: workspace });
|
|
4635
|
+
}
|
|
4636
|
+
const normalizedWorkspace = path.resolve(workspace);
|
|
4637
|
+
try {
|
|
4638
|
+
const info = await stat(normalizedWorkspace);
|
|
4639
|
+
if (!info.isDirectory()) {
|
|
4640
|
+
throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
|
|
4641
|
+
cwd: normalizedWorkspace
|
|
4642
|
+
});
|
|
4643
|
+
}
|
|
4644
|
+
await access(normalizedWorkspace, constants.R_OK);
|
|
4645
|
+
} catch (error) {
|
|
4646
|
+
if (error instanceof GitCommandError) throw error;
|
|
4647
|
+
throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
|
|
4648
|
+
cwd: normalizedWorkspace,
|
|
4649
|
+
cause: error
|
|
4650
|
+
});
|
|
4651
|
+
}
|
|
4652
|
+
return normalizedWorkspace;
|
|
4653
|
+
}
|
|
4654
|
+
function validateGitArgv(argv) {
|
|
4655
|
+
if (!Array.isArray(argv) || argv.length === 0) {
|
|
4656
|
+
throw new GitCommandError("invalid_args", "Git argv must be a non-empty string array", { argv });
|
|
4657
|
+
}
|
|
4658
|
+
for (const arg of argv) {
|
|
4659
|
+
if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0")) {
|
|
4660
|
+
throw new GitCommandError("invalid_args", "Git argv contains an invalid argument", { argv });
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
if (argv.includes("-C") || argv.some((arg) => arg.startsWith("--git-dir") || arg.startsWith("--work-tree"))) {
|
|
4664
|
+
throw new GitCommandError("invalid_args", "Git argv contains unsafe repository override arguments", {
|
|
4665
|
+
argv
|
|
4666
|
+
});
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
async function execGitRaw(cwd, argv, options, behavior = {}) {
|
|
4670
|
+
validateGitArgv(argv);
|
|
4671
|
+
try {
|
|
4672
|
+
const result = await execFileAsync("git", [...argv], {
|
|
4673
|
+
cwd,
|
|
4674
|
+
encoding: "utf8",
|
|
4675
|
+
timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
4676
|
+
maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
4677
|
+
windowsHide: true
|
|
4678
|
+
});
|
|
4679
|
+
return {
|
|
4680
|
+
stdout: normalizeGitOutput(result.stdout),
|
|
4681
|
+
stderr: normalizeGitOutput(result.stderr)
|
|
4682
|
+
};
|
|
4683
|
+
} catch (error) {
|
|
4684
|
+
throw mapExecError(error, cwd, argv, behavior);
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
function mapExecError(error, cwd, argv, behavior) {
|
|
4688
|
+
const execError = error;
|
|
4689
|
+
const stdout = normalizeGitOutput(execError.stdout);
|
|
4690
|
+
const stderr = normalizeGitOutput(execError.stderr);
|
|
4691
|
+
const code = execError.code;
|
|
4692
|
+
const signal = execError.signal;
|
|
4693
|
+
const message = [stderr.trim(), execError.message].filter(Boolean).join("\n");
|
|
4694
|
+
if (code === "ENOENT") {
|
|
4695
|
+
return new GitCommandError("git_not_installed", "Git executable was not found", {
|
|
4696
|
+
stdout,
|
|
4697
|
+
stderr,
|
|
4698
|
+
exitCode: code,
|
|
4699
|
+
signal,
|
|
4700
|
+
argv,
|
|
4701
|
+
cwd,
|
|
4702
|
+
cause: error
|
|
4703
|
+
});
|
|
4704
|
+
}
|
|
4705
|
+
if (execError.killed || /timed out/i.test(execError.message)) {
|
|
4706
|
+
return new GitCommandError("timeout", "Git command timed out", {
|
|
4707
|
+
stdout,
|
|
4708
|
+
stderr,
|
|
4709
|
+
exitCode: code,
|
|
4710
|
+
signal,
|
|
4711
|
+
argv,
|
|
4712
|
+
cwd,
|
|
4713
|
+
cause: error
|
|
4714
|
+
});
|
|
4715
|
+
}
|
|
4716
|
+
if (behavior.mapNotGitRepo && /not a git repository/i.test(stderr + "\n" + execError.message)) {
|
|
4717
|
+
return new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
|
|
4718
|
+
stdout,
|
|
4719
|
+
stderr,
|
|
4720
|
+
exitCode: code,
|
|
4721
|
+
signal,
|
|
4722
|
+
argv,
|
|
4723
|
+
cwd,
|
|
4724
|
+
cause: error
|
|
4725
|
+
});
|
|
4726
|
+
}
|
|
4727
|
+
return new GitCommandError("git_command_failed", message || "Git command failed", {
|
|
4728
|
+
stdout,
|
|
4729
|
+
stderr,
|
|
4730
|
+
exitCode: code,
|
|
4731
|
+
signal,
|
|
4732
|
+
argv,
|
|
4733
|
+
cwd,
|
|
4734
|
+
cause: error
|
|
4735
|
+
});
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
// src/git/git-status.ts
|
|
4739
|
+
async function getGitRepoStatus(workspace, options = {}) {
|
|
4740
|
+
const lastCheckedAt = Date.now();
|
|
4741
|
+
try {
|
|
4742
|
+
const repo = await resolveGitRepository(workspace, options);
|
|
4743
|
+
const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
|
|
4744
|
+
const parsed = parsePorcelainV2Status(statusOutput.stdout);
|
|
4745
|
+
const head = await readHead(repo, options);
|
|
4746
|
+
const stashCount = await readStashCount(repo, options);
|
|
4747
|
+
return {
|
|
4748
|
+
workspace: repo.workspace,
|
|
4749
|
+
repoRoot: repo.repoRoot,
|
|
4750
|
+
isGitRepo: true,
|
|
4751
|
+
branch: parsed.branch,
|
|
4752
|
+
headCommit: head.commit,
|
|
4753
|
+
headMessage: head.message,
|
|
4754
|
+
upstream: parsed.upstream,
|
|
4755
|
+
ahead: parsed.ahead,
|
|
4756
|
+
behind: parsed.behind,
|
|
4757
|
+
staged: parsed.staged,
|
|
4758
|
+
modified: parsed.modified,
|
|
4759
|
+
untracked: parsed.untracked,
|
|
4760
|
+
deleted: parsed.deleted,
|
|
4761
|
+
renamed: parsed.renamed,
|
|
4762
|
+
hasConflicts: parsed.conflictFiles.length > 0,
|
|
4763
|
+
conflictFiles: parsed.conflictFiles,
|
|
4764
|
+
stashCount,
|
|
4765
|
+
lastCheckedAt
|
|
4766
|
+
};
|
|
4767
|
+
} catch (error) {
|
|
4768
|
+
if (error instanceof GitCommandError) {
|
|
4769
|
+
return emptyStatus(workspace, lastCheckedAt, error);
|
|
4770
|
+
}
|
|
4771
|
+
return emptyStatus(
|
|
4772
|
+
workspace,
|
|
4773
|
+
lastCheckedAt,
|
|
4774
|
+
new GitCommandError("git_command_failed", "Failed to read Git status", { cause: error })
|
|
4775
|
+
);
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
function parsePorcelainV2Status(output) {
|
|
4779
|
+
const parsed = {
|
|
4780
|
+
branch: null,
|
|
4781
|
+
upstream: null,
|
|
4782
|
+
ahead: 0,
|
|
4783
|
+
behind: 0,
|
|
4784
|
+
staged: 0,
|
|
4785
|
+
modified: 0,
|
|
4786
|
+
untracked: 0,
|
|
4787
|
+
deleted: 0,
|
|
4788
|
+
renamed: 0,
|
|
4789
|
+
conflictFiles: []
|
|
4790
|
+
};
|
|
4791
|
+
for (const line of output.split("\n")) {
|
|
4792
|
+
if (!line) continue;
|
|
4793
|
+
if (line.startsWith("# branch.head ")) {
|
|
4794
|
+
const branch = line.slice("# branch.head ".length).trim();
|
|
4795
|
+
parsed.branch = branch && branch !== "(detached)" ? branch : null;
|
|
4796
|
+
continue;
|
|
4797
|
+
}
|
|
4798
|
+
if (line.startsWith("# branch.upstream ")) {
|
|
4799
|
+
parsed.upstream = line.slice("# branch.upstream ".length).trim() || null;
|
|
4800
|
+
continue;
|
|
4801
|
+
}
|
|
4802
|
+
if (line.startsWith("# branch.ab ")) {
|
|
4803
|
+
const match = line.match(/\+(-?\d+)\s+-(-?\d+)/);
|
|
4804
|
+
if (match) {
|
|
4805
|
+
parsed.ahead = Number.parseInt(match[1] ?? "0", 10) || 0;
|
|
4806
|
+
parsed.behind = Number.parseInt(match[2] ?? "0", 10) || 0;
|
|
4807
|
+
}
|
|
4808
|
+
continue;
|
|
4809
|
+
}
|
|
4810
|
+
if (line.startsWith("? ")) {
|
|
4811
|
+
parsed.untracked += 1;
|
|
4812
|
+
continue;
|
|
4813
|
+
}
|
|
4814
|
+
if (line.startsWith("u ")) {
|
|
4815
|
+
const fields = line.split(" ");
|
|
4816
|
+
const filePath = fields.slice(10).join(" ");
|
|
4817
|
+
if (filePath) parsed.conflictFiles.push(filePath);
|
|
4818
|
+
continue;
|
|
4819
|
+
}
|
|
4820
|
+
if (line.startsWith("1 ") || line.startsWith("2 ")) {
|
|
4821
|
+
const fields = line.split(" ");
|
|
4822
|
+
const xy = fields[1] ?? "..";
|
|
4823
|
+
const indexStatus = xy[0] ?? ".";
|
|
4824
|
+
const worktreeStatus = xy[1] ?? ".";
|
|
4825
|
+
if (isStagedStatus(indexStatus)) parsed.staged += 1;
|
|
4826
|
+
if (worktreeStatus === "M" || worktreeStatus === "T") parsed.modified += 1;
|
|
4827
|
+
if (indexStatus === "D" || worktreeStatus === "D") parsed.deleted += 1;
|
|
4828
|
+
if (indexStatus === "R" || worktreeStatus === "R") parsed.renamed += 1;
|
|
4829
|
+
if (xy.includes("U")) {
|
|
4830
|
+
const filePath = fields.slice(line.startsWith("2 ") ? 9 : 8).join(" ").split(" ")[0] ?? "";
|
|
4831
|
+
if (filePath) parsed.conflictFiles.push(filePath);
|
|
4832
|
+
}
|
|
4833
|
+
}
|
|
4834
|
+
}
|
|
4835
|
+
parsed.conflictFiles = Array.from(new Set(parsed.conflictFiles));
|
|
4836
|
+
return parsed;
|
|
4837
|
+
}
|
|
4838
|
+
async function readHead(repo, options) {
|
|
4839
|
+
try {
|
|
4840
|
+
const result = await runGit(repo, ["log", "-1", "--pretty=%h%x00%s"], options);
|
|
4841
|
+
const text = result.stdout.trimEnd();
|
|
4842
|
+
if (!text) return { commit: null, message: null };
|
|
4843
|
+
const [commit, ...messageParts] = text.split("\0");
|
|
4844
|
+
return {
|
|
4845
|
+
commit: commit || null,
|
|
4846
|
+
message: messageParts.join("\0") || null
|
|
4847
|
+
};
|
|
4848
|
+
} catch {
|
|
4849
|
+
return { commit: null, message: null };
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
async function readStashCount(repo, options) {
|
|
4853
|
+
try {
|
|
4854
|
+
const result = await runGit(repo, ["stash", "list", "--format=%gd"], options);
|
|
4855
|
+
return result.stdout.split("\n").filter((line) => line.trim().length > 0).length;
|
|
4856
|
+
} catch {
|
|
4857
|
+
return 0;
|
|
4858
|
+
}
|
|
4859
|
+
}
|
|
4860
|
+
function isStagedStatus(status) {
|
|
4861
|
+
return status !== "." && status !== "?" && status !== "U";
|
|
4862
|
+
}
|
|
4863
|
+
function emptyStatus(workspace, lastCheckedAt, error) {
|
|
4864
|
+
return {
|
|
4865
|
+
workspace,
|
|
4866
|
+
repoRoot: null,
|
|
4867
|
+
isGitRepo: false,
|
|
4868
|
+
branch: null,
|
|
4869
|
+
headCommit: null,
|
|
4870
|
+
headMessage: null,
|
|
4871
|
+
upstream: null,
|
|
4872
|
+
ahead: 0,
|
|
4873
|
+
behind: 0,
|
|
4874
|
+
staged: 0,
|
|
4875
|
+
modified: 0,
|
|
4876
|
+
untracked: 0,
|
|
4877
|
+
deleted: 0,
|
|
4878
|
+
renamed: 0,
|
|
4879
|
+
hasConflicts: false,
|
|
4880
|
+
conflictFiles: [],
|
|
4881
|
+
stashCount: 0,
|
|
4882
|
+
lastCheckedAt,
|
|
4883
|
+
error: error.stderr || error.message,
|
|
4884
|
+
reason: error.reason
|
|
4885
|
+
};
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
// src/git/git-diff.ts
|
|
4889
|
+
import { readFile, realpath as realpath2 } from "fs/promises";
|
|
4890
|
+
import * as path2 from "path";
|
|
4891
|
+
var DEFAULT_MAX_FILES = 200;
|
|
4892
|
+
var DEFAULT_MAX_BYTES = 2e5;
|
|
4893
|
+
async function getGitDiffSummary(workspace, options = {}) {
|
|
4894
|
+
const lastCheckedAt = Date.now();
|
|
4895
|
+
try {
|
|
4896
|
+
const repo = await resolveGitRepository(workspace, options);
|
|
4897
|
+
const repoRoot = repo.repoRoot;
|
|
4898
|
+
const [unstagedNameStatus, unstagedNumstat, stagedNameStatus, stagedNumstat, untracked] = await Promise.all([
|
|
4899
|
+
runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
|
|
4900
|
+
runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
|
|
4901
|
+
runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
|
|
4902
|
+
runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
|
|
4903
|
+
runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...options, cwd: repoRoot })
|
|
4904
|
+
]);
|
|
4905
|
+
const outputBytes = byteLength(
|
|
4906
|
+
unstagedNameStatus.stdout + unstagedNumstat.stdout + stagedNameStatus.stdout + stagedNumstat.stdout + untracked.stdout
|
|
4907
|
+
);
|
|
4908
|
+
const changes = [
|
|
4909
|
+
...combineDiffEntries(unstagedNameStatus.stdout, unstagedNumstat.stdout, false),
|
|
4910
|
+
...combineDiffEntries(stagedNameStatus.stdout, stagedNumstat.stdout, true),
|
|
4911
|
+
...parseUntrackedFiles(untracked.stdout)
|
|
4912
|
+
];
|
|
4913
|
+
const maxFiles = normalizePositiveInteger(options.maxFiles, DEFAULT_MAX_FILES);
|
|
4914
|
+
const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
|
|
4915
|
+
const files = changes.slice(0, maxFiles);
|
|
4916
|
+
const truncated = changes.length > files.length || outputBytes > maxBytes;
|
|
4917
|
+
return {
|
|
4918
|
+
workspace: repo.workspace,
|
|
4919
|
+
repoRoot,
|
|
4920
|
+
isGitRepo: true,
|
|
4921
|
+
files,
|
|
4922
|
+
totalInsertions: files.reduce((sum, file) => sum + file.insertions, 0),
|
|
4923
|
+
totalDeletions: files.reduce((sum, file) => sum + file.deletions, 0),
|
|
4924
|
+
truncated,
|
|
4925
|
+
lastCheckedAt
|
|
4926
|
+
};
|
|
4927
|
+
} catch (error) {
|
|
4928
|
+
const gitError = error instanceof GitCommandError ? error : new GitCommandError("git_command_failed", "Failed to read Git diff summary", { cause: error });
|
|
4929
|
+
return {
|
|
4930
|
+
workspace,
|
|
4931
|
+
repoRoot: null,
|
|
4932
|
+
isGitRepo: false,
|
|
4933
|
+
files: [],
|
|
4934
|
+
totalInsertions: 0,
|
|
4935
|
+
totalDeletions: 0,
|
|
4936
|
+
truncated: false,
|
|
4937
|
+
lastCheckedAt,
|
|
4938
|
+
error: gitError.stderr || gitError.message,
|
|
4939
|
+
reason: gitError.reason
|
|
4940
|
+
};
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4943
|
+
async function getGitFileDiff(workspace, filePath, options = {}) {
|
|
4944
|
+
const lastCheckedAt = Date.now();
|
|
4945
|
+
const repo = await resolveGitRepository(workspace, options);
|
|
4946
|
+
const repoRoot = repo.repoRoot;
|
|
4947
|
+
const selected = await resolveRepoFilePath(repoRoot, filePath);
|
|
4948
|
+
const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
|
|
4949
|
+
const [unstaged, staged] = await Promise.all([
|
|
4950
|
+
runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot }),
|
|
4951
|
+
runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot })
|
|
4952
|
+
]);
|
|
4953
|
+
let diff = [unstaged.stdout, staged.stdout].filter((part) => part.length > 0).join("\n");
|
|
4954
|
+
if (!diff) {
|
|
4955
|
+
const untracked = await runGit(repo, ["ls-files", "--others", "--exclude-standard", "--", selected.relativePath], {
|
|
4956
|
+
...options,
|
|
4957
|
+
cwd: repoRoot
|
|
4958
|
+
});
|
|
4959
|
+
const untrackedFiles = untracked.stdout.split("\n").filter(Boolean);
|
|
4960
|
+
if (untrackedFiles.includes(selected.relativePath)) {
|
|
4961
|
+
diff = await buildUntrackedDiff(selected.absolutePath, selected.relativePath, maxBytes + 1);
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
const bounded = truncateText(diff, maxBytes);
|
|
4965
|
+
return {
|
|
4966
|
+
workspace: repo.workspace,
|
|
4967
|
+
repoRoot,
|
|
4968
|
+
isGitRepo: true,
|
|
4969
|
+
path: selected.relativePath,
|
|
4970
|
+
diff: bounded.text,
|
|
4971
|
+
truncated: bounded.truncated,
|
|
4972
|
+
lastCheckedAt
|
|
4973
|
+
};
|
|
4974
|
+
}
|
|
4975
|
+
function combineDiffEntries(nameStatusOutput, numstatOutput, staged) {
|
|
4976
|
+
const statusEntries = parseNameStatus(nameStatusOutput);
|
|
4977
|
+
const numstatEntries = parseNumstat(numstatOutput);
|
|
4978
|
+
return statusEntries.map((entry, index) => {
|
|
4979
|
+
const stats = numstatEntries[index];
|
|
4980
|
+
return {
|
|
4981
|
+
path: entry.path,
|
|
4982
|
+
oldPath: entry.oldPath,
|
|
4983
|
+
status: entry.status,
|
|
4984
|
+
staged,
|
|
4985
|
+
insertions: stats?.insertions ?? 0,
|
|
4986
|
+
deletions: stats?.deletions ?? 0,
|
|
4987
|
+
binary: stats?.binary || void 0
|
|
4988
|
+
};
|
|
4989
|
+
});
|
|
4990
|
+
}
|
|
4991
|
+
function parseNameStatus(output) {
|
|
4992
|
+
return output.split("\n").filter(Boolean).map((line) => {
|
|
4993
|
+
const fields = line.split(" ");
|
|
4994
|
+
const code = fields[0] ?? "";
|
|
4995
|
+
const statusLetter = code[0] ?? "M";
|
|
4996
|
+
if (statusLetter === "R") {
|
|
4997
|
+
return {
|
|
4998
|
+
oldPath: fields[1] ?? "",
|
|
4999
|
+
path: fields[2] ?? fields[1] ?? "",
|
|
5000
|
+
status: "renamed"
|
|
5001
|
+
};
|
|
5002
|
+
}
|
|
5003
|
+
if (statusLetter === "C") {
|
|
5004
|
+
return {
|
|
5005
|
+
oldPath: fields[1] ?? "",
|
|
5006
|
+
path: fields[2] ?? fields[1] ?? "",
|
|
5007
|
+
status: "copied"
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
5010
|
+
return {
|
|
5011
|
+
path: fields[1] ?? "",
|
|
5012
|
+
status: mapNameStatus(statusLetter)
|
|
5013
|
+
};
|
|
5014
|
+
}).filter((entry) => entry.path.length > 0);
|
|
5015
|
+
}
|
|
5016
|
+
function parseNumstat(output) {
|
|
5017
|
+
return output.split("\n").filter(Boolean).map((line) => {
|
|
5018
|
+
const fields = line.split(" ");
|
|
5019
|
+
const insertionsText = fields[0] ?? "0";
|
|
5020
|
+
const deletionsText = fields[1] ?? "0";
|
|
5021
|
+
const binary = insertionsText === "-" || deletionsText === "-";
|
|
5022
|
+
return {
|
|
5023
|
+
path: fields.slice(2).join(" "),
|
|
5024
|
+
insertions: binary ? 0 : Number.parseInt(insertionsText, 10) || 0,
|
|
5025
|
+
deletions: binary ? 0 : Number.parseInt(deletionsText, 10) || 0,
|
|
5026
|
+
binary
|
|
5027
|
+
};
|
|
5028
|
+
});
|
|
5029
|
+
}
|
|
5030
|
+
function parseUntrackedFiles(output) {
|
|
5031
|
+
return output.split("\n").filter(Boolean).map((filePath) => ({
|
|
5032
|
+
path: filePath,
|
|
5033
|
+
status: "untracked",
|
|
5034
|
+
staged: false,
|
|
5035
|
+
insertions: 0,
|
|
5036
|
+
deletions: 0
|
|
5037
|
+
}));
|
|
5038
|
+
}
|
|
5039
|
+
function mapNameStatus(status) {
|
|
5040
|
+
switch (status) {
|
|
5041
|
+
case "A":
|
|
5042
|
+
return "added";
|
|
5043
|
+
case "D":
|
|
5044
|
+
return "deleted";
|
|
5045
|
+
case "R":
|
|
5046
|
+
return "renamed";
|
|
5047
|
+
case "C":
|
|
5048
|
+
return "copied";
|
|
5049
|
+
case "U":
|
|
5050
|
+
return "conflict";
|
|
5051
|
+
case "M":
|
|
5052
|
+
case "T":
|
|
5053
|
+
default:
|
|
5054
|
+
return "modified";
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
async function resolveRepoFilePath(repoRoot, filePath) {
|
|
5058
|
+
if (typeof filePath !== "string" || filePath.length === 0 || filePath.includes("\0")) {
|
|
5059
|
+
throw new GitCommandError("invalid_args", "File path must be a non-empty path");
|
|
5060
|
+
}
|
|
5061
|
+
const canonicalRepoRoot = await realpath2(repoRoot).catch(() => path2.resolve(repoRoot));
|
|
5062
|
+
const absolutePath = path2.isAbsolute(filePath) ? path2.resolve(filePath) : path2.resolve(repoRoot, filePath);
|
|
5063
|
+
const checkPath = await realpath2(absolutePath).catch(() => absolutePath);
|
|
5064
|
+
const relativeBase = isPathInside(canonicalRepoRoot, checkPath) ? canonicalRepoRoot : path2.resolve(repoRoot);
|
|
5065
|
+
if (!isPathInside(canonicalRepoRoot, checkPath) && !isPathInside(repoRoot, absolutePath)) {
|
|
5066
|
+
throw new GitCommandError("path_outside_repo", "Selected file path is outside the repository root", {
|
|
5067
|
+
cwd: repoRoot
|
|
5068
|
+
});
|
|
5069
|
+
}
|
|
5070
|
+
const relativePath = path2.relative(relativeBase, checkPath).split(path2.sep).join("/");
|
|
5071
|
+
if (!relativePath || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
|
|
5072
|
+
throw new GitCommandError("path_outside_repo", "Selected file path is outside the repository root", {
|
|
5073
|
+
cwd: repoRoot
|
|
5074
|
+
});
|
|
5075
|
+
}
|
|
5076
|
+
return { absolutePath, relativePath };
|
|
5077
|
+
}
|
|
5078
|
+
async function buildUntrackedDiff(absolutePath, relativePath, readLimit) {
|
|
5079
|
+
const content = await readFile(absolutePath, "utf8");
|
|
5080
|
+
const limitedContent = content.length > readLimit ? content.slice(0, readLimit) : content;
|
|
5081
|
+
const lines = limitedContent.length > 0 ? limitedContent.split("\n") : [];
|
|
5082
|
+
const plusLines = lines.filter((line, index) => index < lines.length - 1 || line.length > 0).map((line) => `+${line}`).join("\n");
|
|
5083
|
+
const lineCount = plusLines ? plusLines.split("\n").length : 0;
|
|
5084
|
+
return [
|
|
5085
|
+
`diff --git a/${relativePath} b/${relativePath}`,
|
|
5086
|
+
"new file mode 100644",
|
|
5087
|
+
"index 0000000..0000000",
|
|
5088
|
+
"--- /dev/null",
|
|
5089
|
+
`+++ b/${relativePath}`,
|
|
5090
|
+
`@@ -0,0 +1,${lineCount} @@`,
|
|
5091
|
+
plusLines
|
|
5092
|
+
].filter((line) => line.length > 0).join("\n");
|
|
5093
|
+
}
|
|
5094
|
+
function truncateText(text, maxBytes) {
|
|
5095
|
+
if (byteLength(text) <= maxBytes) return { text, truncated: false };
|
|
5096
|
+
return { text: Buffer.from(text, "utf8").subarray(0, maxBytes).toString("utf8"), truncated: true };
|
|
5097
|
+
}
|
|
5098
|
+
function byteLength(text) {
|
|
5099
|
+
return Buffer.byteLength(text, "utf8");
|
|
5100
|
+
}
|
|
5101
|
+
function normalizePositiveInteger(value, fallback) {
|
|
5102
|
+
if (!Number.isFinite(value) || value == null || value <= 0) return fallback;
|
|
5103
|
+
return Math.floor(value);
|
|
5104
|
+
}
|
|
5105
|
+
|
|
5106
|
+
// src/git/git-summary.ts
|
|
5107
|
+
function countStatusChangedFiles(status) {
|
|
5108
|
+
const conflictCount = status.conflictFiles.length > 0 ? status.conflictFiles.length : status.hasConflicts ? 1 : 0;
|
|
5109
|
+
return status.staged + status.modified + status.untracked + status.deleted + status.renamed + conflictCount;
|
|
5110
|
+
}
|
|
5111
|
+
function createGitCompactSummary(status, diffSummary) {
|
|
5112
|
+
const statusChangedFiles = countStatusChangedFiles(status);
|
|
5113
|
+
const diffChangedFiles = diffSummary?.files.length ?? 0;
|
|
5114
|
+
const changedFiles = Math.max(statusChangedFiles, diffChangedFiles);
|
|
5115
|
+
const conflictCount = status.conflictFiles.length > 0 ? status.conflictFiles.length : status.hasConflicts ? 1 : 0;
|
|
5116
|
+
return {
|
|
5117
|
+
isGitRepo: status.isGitRepo,
|
|
5118
|
+
repoRoot: status.repoRoot,
|
|
5119
|
+
branch: status.branch,
|
|
5120
|
+
dirty: status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0 || conflictCount > 0 || changedFiles > 0,
|
|
5121
|
+
changedFiles,
|
|
5122
|
+
ahead: status.ahead,
|
|
5123
|
+
behind: status.behind,
|
|
5124
|
+
hasConflicts: status.hasConflicts || conflictCount > 0,
|
|
5125
|
+
lastCheckedAt: Math.max(status.lastCheckedAt, diffSummary?.lastCheckedAt ?? status.lastCheckedAt),
|
|
5126
|
+
error: status.error ?? diffSummary?.error,
|
|
5127
|
+
reason: status.reason ?? diffSummary?.reason
|
|
5128
|
+
};
|
|
5129
|
+
}
|
|
5130
|
+
var summarizeGitStatus = createGitCompactSummary;
|
|
5131
|
+
|
|
5132
|
+
// src/git/git-snapshot-store.ts
|
|
5133
|
+
function normalizeCapacity(capacity) {
|
|
5134
|
+
return Math.max(1, Math.floor(capacity ?? 100));
|
|
5135
|
+
}
|
|
5136
|
+
function createEmptyDiffSummary(status) {
|
|
5137
|
+
return {
|
|
5138
|
+
workspace: status.workspace,
|
|
5139
|
+
repoRoot: status.repoRoot,
|
|
5140
|
+
isGitRepo: status.isGitRepo,
|
|
5141
|
+
files: [],
|
|
5142
|
+
totalInsertions: 0,
|
|
5143
|
+
totalDeletions: 0,
|
|
5144
|
+
truncated: false,
|
|
5145
|
+
lastCheckedAt: status.lastCheckedAt,
|
|
5146
|
+
error: status.error,
|
|
5147
|
+
reason: status.reason
|
|
5148
|
+
};
|
|
5149
|
+
}
|
|
5150
|
+
function changedFileKey(file) {
|
|
5151
|
+
return `${file.oldPath ?? ""}\0${file.path}`;
|
|
5152
|
+
}
|
|
5153
|
+
function uniqueSorted(values) {
|
|
5154
|
+
return Array.from(new Set(Array.from(values).filter(Boolean))).sort((a, b) => a.localeCompare(b));
|
|
5155
|
+
}
|
|
5156
|
+
function plural(count, singular, pluralText = `${singular}s`) {
|
|
5157
|
+
return count === 1 ? singular : pluralText;
|
|
5158
|
+
}
|
|
5159
|
+
function compareGitSnapshots(before, after) {
|
|
5160
|
+
const beforeFileKeys = new Set(before.diffSummary.files.map(changedFileKey));
|
|
5161
|
+
const changedAfterFiles = after.diffSummary.files.filter((file) => !beforeFileKeys.has(changedFileKey(file)));
|
|
5162
|
+
const addedFiles = [];
|
|
5163
|
+
const modifiedFiles = [];
|
|
5164
|
+
const deletedFiles = [];
|
|
5165
|
+
const renamedFiles = [];
|
|
5166
|
+
const untrackedFiles = [];
|
|
5167
|
+
const conflictFilesFromDiff = [];
|
|
5168
|
+
let totalInsertions = 0;
|
|
5169
|
+
let totalDeletions = 0;
|
|
5170
|
+
for (const file of changedAfterFiles) {
|
|
5171
|
+
totalInsertions += file.insertions;
|
|
5172
|
+
totalDeletions += file.deletions;
|
|
5173
|
+
switch (file.status) {
|
|
5174
|
+
case "added":
|
|
5175
|
+
case "copied":
|
|
5176
|
+
addedFiles.push(file.path);
|
|
5177
|
+
break;
|
|
5178
|
+
case "modified":
|
|
5179
|
+
modifiedFiles.push(file.path);
|
|
5180
|
+
break;
|
|
5181
|
+
case "deleted":
|
|
5182
|
+
deletedFiles.push(file.path);
|
|
5183
|
+
break;
|
|
5184
|
+
case "renamed":
|
|
5185
|
+
renamedFiles.push({ oldPath: file.oldPath ?? file.path, path: file.path });
|
|
5186
|
+
break;
|
|
5187
|
+
case "untracked":
|
|
5188
|
+
untrackedFiles.push(file.path);
|
|
5189
|
+
break;
|
|
5190
|
+
case "conflict":
|
|
5191
|
+
conflictFilesFromDiff.push(file.path);
|
|
5192
|
+
break;
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
renamedFiles.sort((a, b) => `${a.oldPath}\0${a.path}`.localeCompare(`${b.oldPath}\0${b.path}`));
|
|
5196
|
+
const conflictFiles = uniqueSorted([...after.status.conflictFiles, ...conflictFilesFromDiff]);
|
|
5197
|
+
const changedFiles = changedAfterFiles.length;
|
|
5198
|
+
const hasConflicts = after.status.hasConflicts || conflictFiles.length > 0;
|
|
5199
|
+
const summaryParts = [];
|
|
5200
|
+
if (changedFiles > 0) summaryParts.push(`${changedFiles} ${plural(changedFiles, "file")} changed`);
|
|
5201
|
+
if (addedFiles.length > 0) summaryParts.push(`${addedFiles.length} added`);
|
|
5202
|
+
if (modifiedFiles.length > 0) summaryParts.push(`${modifiedFiles.length} modified`);
|
|
5203
|
+
if (deletedFiles.length > 0) summaryParts.push(`${deletedFiles.length} deleted`);
|
|
5204
|
+
if (renamedFiles.length > 0) summaryParts.push(`${renamedFiles.length} renamed`);
|
|
5205
|
+
if (untrackedFiles.length > 0) summaryParts.push(`${untrackedFiles.length} untracked`);
|
|
5206
|
+
if (hasConflicts) summaryParts.push(`${conflictFiles.length || 1} ${plural(conflictFiles.length || 1, "conflict")}`);
|
|
5207
|
+
return {
|
|
5208
|
+
beforeSnapshotId: before.id,
|
|
5209
|
+
afterSnapshotId: after.id,
|
|
5210
|
+
workspace: after.workspace,
|
|
5211
|
+
repoRoot: after.repoRoot,
|
|
5212
|
+
changedFiles,
|
|
5213
|
+
addedFiles: uniqueSorted(addedFiles),
|
|
5214
|
+
modifiedFiles: uniqueSorted(modifiedFiles),
|
|
5215
|
+
deletedFiles: uniqueSorted(deletedFiles),
|
|
5216
|
+
renamedFiles,
|
|
5217
|
+
untrackedFiles: uniqueSorted(untrackedFiles),
|
|
5218
|
+
conflictFiles,
|
|
5219
|
+
totalInsertions,
|
|
5220
|
+
totalDeletions,
|
|
5221
|
+
hasConflicts,
|
|
5222
|
+
currentStatus: after.status,
|
|
5223
|
+
summaryText: summaryParts.length > 0 ? summaryParts.join(", ") : "No file-set changes between snapshots."
|
|
5224
|
+
};
|
|
5225
|
+
}
|
|
5226
|
+
var InMemoryGitSnapshotStore = class {
|
|
5227
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
5228
|
+
order = [];
|
|
5229
|
+
capacity;
|
|
5230
|
+
now;
|
|
5231
|
+
idPrefix;
|
|
5232
|
+
getStatusProvider;
|
|
5233
|
+
getDiffSummaryProvider;
|
|
5234
|
+
counter = 0;
|
|
5235
|
+
constructor(options = {}) {
|
|
5236
|
+
this.capacity = normalizeCapacity(options.capacity);
|
|
5237
|
+
this.now = options.now ?? Date.now;
|
|
5238
|
+
this.idPrefix = options.idPrefix ?? "git-snapshot";
|
|
5239
|
+
this.getStatusProvider = options.getStatus;
|
|
5240
|
+
this.getDiffSummaryProvider = options.getDiffSummary;
|
|
5241
|
+
}
|
|
5242
|
+
async create(input) {
|
|
5243
|
+
const getStatus = input.getStatus ?? this.getStatusProvider;
|
|
5244
|
+
if (!getStatus) {
|
|
5245
|
+
throw new Error("GitSnapshotStore requires an injected getStatus provider");
|
|
5246
|
+
}
|
|
5247
|
+
const status = await getStatus(input.workspace);
|
|
5248
|
+
const getDiffSummary = input.getDiffSummary ?? this.getDiffSummaryProvider;
|
|
5249
|
+
const diffSummary = getDiffSummary ? await getDiffSummary(input.workspace, status) : createEmptyDiffSummary(status);
|
|
5250
|
+
const createdAt = this.now();
|
|
5251
|
+
const id = `${this.idPrefix}-${createdAt}-${++this.counter}`;
|
|
5252
|
+
const snapshot = {
|
|
5253
|
+
id,
|
|
5254
|
+
workspace: input.workspace,
|
|
5255
|
+
repoRoot: status.repoRoot ?? input.workspace,
|
|
5256
|
+
sessionId: input.sessionId,
|
|
5257
|
+
turnId: input.turnId,
|
|
5258
|
+
reason: input.reason,
|
|
5259
|
+
status,
|
|
5260
|
+
diffSummary,
|
|
5261
|
+
createdAt
|
|
5262
|
+
};
|
|
5263
|
+
this.snapshots.set(id, snapshot);
|
|
5264
|
+
this.order.push(id);
|
|
5265
|
+
this.enforceCapacity();
|
|
5266
|
+
return snapshot;
|
|
5267
|
+
}
|
|
5268
|
+
get(id) {
|
|
5269
|
+
return this.snapshots.get(id);
|
|
5270
|
+
}
|
|
5271
|
+
compare(beforeSnapshotId, afterSnapshotId) {
|
|
5272
|
+
const before = this.snapshots.get(beforeSnapshotId);
|
|
5273
|
+
if (!before) throw new Error(`Unknown before snapshot: ${beforeSnapshotId}`);
|
|
5274
|
+
const after = this.snapshots.get(afterSnapshotId);
|
|
5275
|
+
if (!after) throw new Error(`Unknown after snapshot: ${afterSnapshotId}`);
|
|
5276
|
+
return compareGitSnapshots(before, after);
|
|
5277
|
+
}
|
|
5278
|
+
list(query = {}) {
|
|
5279
|
+
const limit = Math.max(1, Math.floor(query.limit ?? this.capacity));
|
|
5280
|
+
return this.order.map((id) => this.snapshots.get(id)).filter((snapshot) => Boolean(snapshot)).filter((snapshot) => !query.workspace || snapshot.workspace === query.workspace).filter((snapshot) => !query.sessionId || snapshot.sessionId === query.sessionId).slice(-limit);
|
|
5281
|
+
}
|
|
5282
|
+
clear() {
|
|
5283
|
+
this.snapshots.clear();
|
|
5284
|
+
this.order.splice(0, this.order.length);
|
|
5285
|
+
}
|
|
5286
|
+
enforceCapacity() {
|
|
5287
|
+
while (this.order.length > this.capacity) {
|
|
5288
|
+
const evictedId = this.order.shift();
|
|
5289
|
+
if (evictedId) this.snapshots.delete(evictedId);
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
};
|
|
5293
|
+
function createGitSnapshotStore(options = {}) {
|
|
5294
|
+
return new InMemoryGitSnapshotStore(options);
|
|
5295
|
+
}
|
|
5296
|
+
|
|
5297
|
+
// src/git/git-monitor.ts
|
|
5298
|
+
var DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS = 5e3;
|
|
5299
|
+
var MIN_GIT_WORKSPACE_POLL_INTERVAL_MS = 1e3;
|
|
5300
|
+
function defaultStatusProvider(workspace) {
|
|
5301
|
+
return getGitRepoStatus(workspace);
|
|
5302
|
+
}
|
|
5303
|
+
function defaultDiffSummaryProvider(workspace) {
|
|
5304
|
+
return getGitDiffSummary(workspace);
|
|
5305
|
+
}
|
|
5306
|
+
function normalizeIntervalMs(value, defaultIntervalMs, minIntervalMs) {
|
|
5307
|
+
const requested = Number.isFinite(value) ? Math.floor(value) : defaultIntervalMs;
|
|
5308
|
+
return Math.max(minIntervalMs, requested > 0 ? requested : defaultIntervalMs);
|
|
5309
|
+
}
|
|
5310
|
+
function normalizeGitWorkspaceSubscriptionParams(params, options = {}) {
|
|
5311
|
+
const minIntervalMs = Math.max(1, Math.floor(options.minIntervalMs ?? MIN_GIT_WORKSPACE_POLL_INTERVAL_MS));
|
|
5312
|
+
const defaultIntervalMs = Math.max(minIntervalMs, Math.floor(options.defaultIntervalMs ?? DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS));
|
|
5313
|
+
return {
|
|
5314
|
+
workspace: params.workspace,
|
|
5315
|
+
includeDiffSummary: Boolean(params.includeDiffSummary),
|
|
5316
|
+
intervalMs: normalizeIntervalMs(params.intervalMs, defaultIntervalMs, minIntervalMs)
|
|
5317
|
+
};
|
|
5318
|
+
}
|
|
5319
|
+
var GitWorkspaceMonitor = class {
|
|
5320
|
+
getStatusProvider;
|
|
5321
|
+
getDiffSummaryProvider;
|
|
5322
|
+
now;
|
|
5323
|
+
minIntervalMs;
|
|
5324
|
+
defaultIntervalMs;
|
|
5325
|
+
keyPrefix;
|
|
5326
|
+
cache = /* @__PURE__ */ new Map();
|
|
5327
|
+
listeners = /* @__PURE__ */ new Set();
|
|
5328
|
+
seq = 0;
|
|
5329
|
+
constructor(options = {}) {
|
|
5330
|
+
this.getStatusProvider = options.getStatus ?? defaultStatusProvider;
|
|
5331
|
+
this.getDiffSummaryProvider = options.getDiffSummary ?? defaultDiffSummaryProvider;
|
|
5332
|
+
this.now = options.now ?? Date.now;
|
|
5333
|
+
this.minIntervalMs = Math.max(1, Math.floor(options.minIntervalMs ?? MIN_GIT_WORKSPACE_POLL_INTERVAL_MS));
|
|
5334
|
+
this.defaultIntervalMs = Math.max(
|
|
5335
|
+
this.minIntervalMs,
|
|
5336
|
+
Math.floor(options.defaultIntervalMs ?? DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS)
|
|
5337
|
+
);
|
|
5338
|
+
this.keyPrefix = options.keyPrefix ?? "git";
|
|
5339
|
+
}
|
|
5340
|
+
async refresh(params) {
|
|
5341
|
+
const normalized = this.normalize(typeof params === "string" ? { workspace: params } : params);
|
|
5342
|
+
const status = await this.getStatusProvider(normalized.workspace);
|
|
5343
|
+
const diffSummary = normalized.includeDiffSummary ? await this.getDiffSummaryProvider(normalized.workspace, status) : void 0;
|
|
5344
|
+
const compactSummary = createGitCompactSummary(status, diffSummary);
|
|
5345
|
+
const timestamp = this.now();
|
|
5346
|
+
const seq = ++this.seq;
|
|
5347
|
+
const key = this.keyForWorkspace(normalized.workspace);
|
|
5348
|
+
const update = {
|
|
5349
|
+
topic: "workspace.git",
|
|
5350
|
+
key,
|
|
5351
|
+
workspace: normalized.workspace,
|
|
5352
|
+
status,
|
|
5353
|
+
diffSummary,
|
|
5354
|
+
seq,
|
|
5355
|
+
timestamp
|
|
5356
|
+
};
|
|
5357
|
+
const cacheEntry = {
|
|
5358
|
+
key,
|
|
5359
|
+
workspace: normalized.workspace,
|
|
5360
|
+
status,
|
|
5361
|
+
diffSummary,
|
|
5362
|
+
compactSummary,
|
|
5363
|
+
seq,
|
|
5364
|
+
timestamp
|
|
5365
|
+
};
|
|
5366
|
+
this.cache.set(normalized.workspace, cacheEntry);
|
|
5367
|
+
this.emit(update, cacheEntry);
|
|
5368
|
+
return update;
|
|
5369
|
+
}
|
|
5370
|
+
poll(params) {
|
|
5371
|
+
return this.refresh(params);
|
|
5372
|
+
}
|
|
5373
|
+
getCached(workspace) {
|
|
5374
|
+
return this.cache.get(workspace);
|
|
5375
|
+
}
|
|
5376
|
+
getCompactSummary(workspace) {
|
|
5377
|
+
return this.cache.get(workspace)?.compactSummary;
|
|
5378
|
+
}
|
|
5379
|
+
onUpdate(listener) {
|
|
5380
|
+
this.listeners.add(listener);
|
|
5381
|
+
return () => {
|
|
5382
|
+
this.listeners.delete(listener);
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5385
|
+
createSubscription(params, listener) {
|
|
5386
|
+
const normalized = this.normalize(params);
|
|
5387
|
+
const scopedListener = listener ? (update, cacheEntry) => {
|
|
5388
|
+
if (update.workspace === normalized.workspace) listener(update, cacheEntry);
|
|
5389
|
+
} : void 0;
|
|
5390
|
+
const unsubscribe = scopedListener ? this.onUpdate(scopedListener) : () => void 0;
|
|
5391
|
+
return {
|
|
5392
|
+
params: normalized,
|
|
5393
|
+
refresh: () => this.refresh(normalized),
|
|
5394
|
+
getCached: () => this.getCached(normalized.workspace),
|
|
5395
|
+
dispose: unsubscribe
|
|
5396
|
+
};
|
|
5397
|
+
}
|
|
5398
|
+
normalize(params) {
|
|
5399
|
+
return normalizeGitWorkspaceSubscriptionParams(params, {
|
|
5400
|
+
defaultIntervalMs: this.defaultIntervalMs,
|
|
5401
|
+
minIntervalMs: this.minIntervalMs
|
|
5402
|
+
});
|
|
5403
|
+
}
|
|
5404
|
+
keyForWorkspace(workspace) {
|
|
5405
|
+
return `${this.keyPrefix}:${workspace}`;
|
|
5406
|
+
}
|
|
5407
|
+
emit(update, cacheEntry) {
|
|
5408
|
+
for (const listener of this.listeners) {
|
|
5409
|
+
listener(update, cacheEntry);
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
};
|
|
5413
|
+
function createGitWorkspaceMonitor(options = {}) {
|
|
5414
|
+
return new GitWorkspaceMonitor(options);
|
|
5415
|
+
}
|
|
5416
|
+
|
|
5417
|
+
// src/git/git-commands.ts
|
|
5418
|
+
import * as path3 from "path";
|
|
5419
|
+
var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
5420
|
+
"git_status",
|
|
5421
|
+
"git_diff_summary",
|
|
5422
|
+
"git_diff_file",
|
|
5423
|
+
"git_snapshot_create",
|
|
5424
|
+
"git_snapshot_compare",
|
|
5425
|
+
"git_log",
|
|
5426
|
+
"git_checkpoint",
|
|
5427
|
+
"git_stash_push",
|
|
5428
|
+
"git_stash_pop",
|
|
5429
|
+
"git_checkout_files",
|
|
5430
|
+
"git_remote_url"
|
|
5431
|
+
]);
|
|
5432
|
+
var SNAPSHOT_REASONS = /* @__PURE__ */ new Set([
|
|
5433
|
+
"session_baseline",
|
|
5434
|
+
"before_user_input_dispatch",
|
|
5435
|
+
"before_agent_work",
|
|
5436
|
+
"after_agent_work",
|
|
5437
|
+
"manual"
|
|
5438
|
+
]);
|
|
5439
|
+
var FAILURE_REASONS = /* @__PURE__ */ new Set([
|
|
5440
|
+
"not_git_repo",
|
|
5441
|
+
"git_not_installed",
|
|
5442
|
+
"timeout",
|
|
5443
|
+
"path_outside_repo",
|
|
5444
|
+
"dirty_index_required",
|
|
5445
|
+
"conflict",
|
|
5446
|
+
"invalid_args",
|
|
5447
|
+
"git_command_failed"
|
|
5448
|
+
]);
|
|
5449
|
+
function failure(reason, error) {
|
|
5450
|
+
return { success: false, reason, error };
|
|
5451
|
+
}
|
|
5452
|
+
function serviceNotImplemented(command) {
|
|
5453
|
+
return failure("invalid_args", `${command} is not implemented: daemon-core Git service is not configured`);
|
|
5454
|
+
}
|
|
5455
|
+
var defaultSnapshotStore = createGitSnapshotStore({
|
|
5456
|
+
getStatus: (workspace) => getGitRepoStatus(workspace),
|
|
5457
|
+
getDiffSummary: (workspace) => getGitDiffSummary(workspace)
|
|
5458
|
+
});
|
|
5459
|
+
function createDefaultGitCommandServices() {
|
|
5460
|
+
return {
|
|
5461
|
+
getStatus: ({ workspace }) => getGitRepoStatus(workspace),
|
|
5462
|
+
getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
|
|
5463
|
+
getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
|
|
5464
|
+
createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
|
|
5465
|
+
workspace,
|
|
5466
|
+
reason,
|
|
5467
|
+
sessionId,
|
|
5468
|
+
turnId
|
|
5469
|
+
}),
|
|
5470
|
+
compareSnapshots: ({ beforeSnapshotId, afterSnapshotId }) => defaultSnapshotStore.compare(beforeSnapshotId, afterSnapshotId),
|
|
5471
|
+
getLog: ({ workspace, limit, path: filePath, since, until }) => getGitLog(workspace, { limit, path: filePath, since, until }),
|
|
5472
|
+
checkpoint: async ({ workspace, message, includeUntracked = false }) => gitCheckpoint(workspace, message, includeUntracked),
|
|
5473
|
+
stashPush: async ({ workspace, message, includeUntracked = false }) => gitStashPush(workspace, message, includeUntracked),
|
|
5474
|
+
stashPop: async ({ workspace, stashRef }) => gitStashPop(workspace, stashRef),
|
|
5475
|
+
checkoutFiles: async ({ workspace, paths }) => gitCheckoutFiles(workspace, paths),
|
|
5476
|
+
getRemoteUrl: async ({ workspace, remote = "origin" }) => gitGetRemoteUrl(workspace, remote)
|
|
5477
|
+
};
|
|
5478
|
+
}
|
|
5479
|
+
var defaultGitCommandServices = createDefaultGitCommandServices();
|
|
5480
|
+
function validateWorkspace2(args) {
|
|
5481
|
+
if (typeof args?.workspace !== "string") {
|
|
5482
|
+
return failure("invalid_args", "workspace must be a non-empty absolute path");
|
|
5483
|
+
}
|
|
5484
|
+
const workspace = args.workspace.trim();
|
|
5485
|
+
if (!workspace || !path3.isAbsolute(workspace)) {
|
|
5486
|
+
return failure("invalid_args", "workspace must be a non-empty absolute path");
|
|
5487
|
+
}
|
|
5488
|
+
return { workspace };
|
|
5489
|
+
}
|
|
5490
|
+
function validateRepoPath(args) {
|
|
5491
|
+
if (typeof args?.path !== "string" || !args.path.trim()) {
|
|
5492
|
+
return failure("invalid_args", "path must be a non-empty repository-relative path");
|
|
5493
|
+
}
|
|
5494
|
+
return { path: args.path.trim() };
|
|
5495
|
+
}
|
|
5496
|
+
function validateSnapshotId(args, key) {
|
|
5497
|
+
if (typeof args?.[key] !== "string" || !args[key].trim()) {
|
|
5498
|
+
return failure("invalid_args", `${key} must be a non-empty string`);
|
|
5499
|
+
}
|
|
5500
|
+
return args[key].trim();
|
|
5501
|
+
}
|
|
5502
|
+
function parseSnapshotReason(args) {
|
|
5503
|
+
if (args?.reason === void 0 || args?.reason === null || args?.reason === "") {
|
|
5504
|
+
return "manual";
|
|
5505
|
+
}
|
|
5506
|
+
if (typeof args.reason !== "string" || !SNAPSHOT_REASONS.has(args.reason)) {
|
|
5507
|
+
return failure("invalid_args", "reason must be a valid GitSnapshotReason");
|
|
5508
|
+
}
|
|
5509
|
+
return args.reason;
|
|
5510
|
+
}
|
|
5511
|
+
function optionalString(value) {
|
|
5512
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
5513
|
+
}
|
|
5514
|
+
function optionalBoolean(value) {
|
|
5515
|
+
return typeof value === "boolean" ? value : void 0;
|
|
5516
|
+
}
|
|
5517
|
+
function boundedLogLimit(value) {
|
|
5518
|
+
if (value === void 0 || value === null || value === "") return 50;
|
|
5519
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
5520
|
+
if (!Number.isFinite(numeric)) return 50;
|
|
5521
|
+
return Math.max(1, Math.min(200, Math.floor(numeric)));
|
|
5522
|
+
}
|
|
5523
|
+
function failureReasonFromError(error) {
|
|
5524
|
+
return typeof error?.reason === "string" && FAILURE_REASONS.has(error.reason) ? error.reason : "git_command_failed";
|
|
5525
|
+
}
|
|
5526
|
+
async function runService(fn) {
|
|
5527
|
+
try {
|
|
5528
|
+
return await fn();
|
|
5529
|
+
} catch (error) {
|
|
5530
|
+
return failure(failureReasonFromError(error), error?.message || "Git command failed");
|
|
5531
|
+
}
|
|
5532
|
+
}
|
|
5533
|
+
function isGitCommandName(command) {
|
|
5534
|
+
return GIT_COMMAND_NAMES.has(command);
|
|
5535
|
+
}
|
|
5536
|
+
async function handleGitCommand(command, args, services = defaultGitCommandServices) {
|
|
5537
|
+
if (!isGitCommandName(command)) {
|
|
5538
|
+
return failure("invalid_args", `Unknown Git command: ${command}`);
|
|
5539
|
+
}
|
|
5540
|
+
const workspaceResult = validateWorkspace2(args);
|
|
5541
|
+
if ("success" in workspaceResult) return workspaceResult;
|
|
5542
|
+
const { workspace } = workspaceResult;
|
|
5543
|
+
switch (command) {
|
|
5544
|
+
case "git_status": {
|
|
5545
|
+
if (!services.getStatus) return serviceNotImplemented(command);
|
|
5546
|
+
const status = await runService(() => services.getStatus({ workspace }));
|
|
5547
|
+
return "success" in status ? status : { success: true, status };
|
|
5548
|
+
}
|
|
5549
|
+
case "git_diff_summary": {
|
|
5550
|
+
if (!services.getDiffSummary) return serviceNotImplemented(command);
|
|
5551
|
+
const diffSummary = await runService(() => services.getDiffSummary({ workspace, staged: optionalBoolean(args?.staged) }));
|
|
5552
|
+
return "success" in diffSummary ? diffSummary : { success: true, diffSummary };
|
|
5553
|
+
}
|
|
5554
|
+
case "git_diff_file": {
|
|
5555
|
+
if (!services.getDiffFile) return serviceNotImplemented(command);
|
|
5556
|
+
const pathResult = validateRepoPath(args);
|
|
5557
|
+
if (typeof pathResult !== "object" || "success" in pathResult) return pathResult;
|
|
5558
|
+
const diff = await runService(() => services.getDiffFile({
|
|
5559
|
+
workspace,
|
|
5560
|
+
path: pathResult.path,
|
|
5561
|
+
staged: optionalBoolean(args?.staged)
|
|
5562
|
+
}));
|
|
5563
|
+
return "success" in diff ? diff : { success: true, diff };
|
|
5564
|
+
}
|
|
5565
|
+
case "git_snapshot_create": {
|
|
5566
|
+
if (!services.createSnapshot) return serviceNotImplemented(command);
|
|
5567
|
+
const reason = parseSnapshotReason(args);
|
|
5568
|
+
if (typeof reason !== "string") return reason;
|
|
5569
|
+
const snapshot = await runService(() => services.createSnapshot({
|
|
5570
|
+
workspace,
|
|
5571
|
+
reason,
|
|
5572
|
+
sessionId: optionalString(args?.sessionId),
|
|
5573
|
+
turnId: optionalString(args?.turnId)
|
|
5574
|
+
}));
|
|
5575
|
+
return "success" in snapshot ? snapshot : { success: true, snapshot };
|
|
5576
|
+
}
|
|
5577
|
+
case "git_snapshot_compare": {
|
|
5578
|
+
if (!services.compareSnapshots) return serviceNotImplemented(command);
|
|
5579
|
+
const beforeSnapshotId = validateSnapshotId(args, "beforeSnapshotId");
|
|
5580
|
+
if (typeof beforeSnapshotId !== "string") return beforeSnapshotId;
|
|
5581
|
+
const afterSnapshotId = validateSnapshotId(args, "afterSnapshotId");
|
|
5582
|
+
if (typeof afterSnapshotId !== "string") return afterSnapshotId;
|
|
5583
|
+
const compare = await runService(() => services.compareSnapshots({ workspace, beforeSnapshotId, afterSnapshotId }));
|
|
5584
|
+
return "success" in compare ? compare : { success: true, compare };
|
|
5585
|
+
}
|
|
5586
|
+
case "git_log": {
|
|
5587
|
+
if (!services.getLog) {
|
|
5588
|
+
return failure("invalid_args", "git_log is not implemented: bounded daemon-core Git log service is not configured");
|
|
5589
|
+
}
|
|
5590
|
+
const log = await runService(() => services.getLog({
|
|
5591
|
+
workspace,
|
|
5592
|
+
limit: boundedLogLimit(args?.limit),
|
|
5593
|
+
path: optionalString(args?.path),
|
|
5594
|
+
since: optionalString(args?.since),
|
|
5595
|
+
until: optionalString(args?.until)
|
|
5596
|
+
}));
|
|
5597
|
+
return "success" in log ? log : { success: true, log };
|
|
5598
|
+
}
|
|
5599
|
+
case "git_checkpoint": {
|
|
5600
|
+
if (!services.checkpoint) return serviceNotImplemented(command);
|
|
5601
|
+
const msg = validateMutatingMessage(args?.message);
|
|
5602
|
+
if (typeof msg !== "string") return msg;
|
|
5603
|
+
const includeUntracked = Boolean(args?.includeUntracked);
|
|
5604
|
+
const checkpoint = await runService(() => services.checkpoint({ workspace, message: msg, includeUntracked }));
|
|
5605
|
+
return "success" in checkpoint ? checkpoint : { success: true, checkpoint };
|
|
5606
|
+
}
|
|
5607
|
+
case "git_stash_push": {
|
|
5608
|
+
if (!services.stashPush) return serviceNotImplemented(command);
|
|
5609
|
+
const msg = validateMutatingMessage(args?.message);
|
|
5610
|
+
if (typeof msg !== "string") return msg;
|
|
5611
|
+
const includeUntracked = Boolean(args?.includeUntracked);
|
|
5612
|
+
const stash = await runService(() => services.stashPush({ workspace, message: msg, includeUntracked }));
|
|
5613
|
+
return "success" in stash ? stash : { success: true, stash };
|
|
5614
|
+
}
|
|
5615
|
+
case "git_stash_pop": {
|
|
5616
|
+
if (!services.stashPop) return serviceNotImplemented(command);
|
|
5617
|
+
const stashRef = optionalString(args?.stashRef);
|
|
5618
|
+
if (stashRef !== void 0 && !/^stash@\{\d+\}$/.test(stashRef)) {
|
|
5619
|
+
return failure("invalid_args", "stashRef must match stash@{N} format");
|
|
5620
|
+
}
|
|
5621
|
+
const popResult = await runService(() => services.stashPop({ workspace, stashRef }));
|
|
5622
|
+
if (popResult !== void 0 && "success" in popResult) return popResult;
|
|
5623
|
+
return { success: true, stashPopped: true };
|
|
5624
|
+
}
|
|
5625
|
+
case "git_checkout_files": {
|
|
5626
|
+
if (!services.checkoutFiles) return serviceNotImplemented(command);
|
|
5627
|
+
const paths = args?.paths;
|
|
5628
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
5629
|
+
return failure("invalid_args", "paths must be a non-empty array");
|
|
5630
|
+
}
|
|
5631
|
+
if (paths.length > 50) {
|
|
5632
|
+
return failure("invalid_args", "paths array exceeds maximum of 50 entries");
|
|
5633
|
+
}
|
|
5634
|
+
const checkoutResult = await runService(() => services.checkoutFiles({ workspace, paths }));
|
|
5635
|
+
return "success" in checkoutResult ? checkoutResult : { success: true, checkedOut: checkoutResult.checkedOut };
|
|
5636
|
+
}
|
|
5637
|
+
case "git_remote_url": {
|
|
5638
|
+
if (!services.getRemoteUrl) return serviceNotImplemented(command);
|
|
5639
|
+
const remote = typeof args?.remote === "string" && args.remote.trim() ? args.remote.trim() : "origin";
|
|
5640
|
+
const remoteResult = await runService(() => services.getRemoteUrl({ workspace, remote }));
|
|
5641
|
+
if ("success" in remoteResult) return remoteResult;
|
|
5642
|
+
return { success: true, remoteUrl: remoteResult.remoteUrl, remote: remoteResult.remote };
|
|
5643
|
+
}
|
|
5644
|
+
default:
|
|
5645
|
+
return failure("invalid_args", `Unknown Git command: ${command}`);
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
function validateMutatingMessage(value) {
|
|
5649
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
5650
|
+
return failure("invalid_args", "message must be a non-empty string");
|
|
5651
|
+
}
|
|
5652
|
+
const msg = value.trim();
|
|
5653
|
+
if (msg.length > 200) {
|
|
5654
|
+
return failure("invalid_args", "message must be 200 characters or fewer");
|
|
5655
|
+
}
|
|
5656
|
+
return msg;
|
|
5657
|
+
}
|
|
5658
|
+
async function gitCheckpoint(workspace, message, includeUntracked) {
|
|
5659
|
+
const repo = await resolveGitRepository(workspace);
|
|
5660
|
+
const repoRoot = repo.repoRoot;
|
|
5661
|
+
const statusResult = await getGitRepoStatus(workspace);
|
|
5662
|
+
if (statusResult.hasConflicts) {
|
|
5663
|
+
throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
|
|
5664
|
+
}
|
|
5665
|
+
const addArgs = includeUntracked ? ["-A"] : ["-u"];
|
|
5666
|
+
await runGit(repo, ["add", ...addArgs], { cwd: repoRoot });
|
|
5667
|
+
const fullMsg = `adhdev: checkpoint ${message}`;
|
|
5668
|
+
let commitSha;
|
|
5669
|
+
try {
|
|
5670
|
+
await runGit(repo, ["commit", "-m", fullMsg], { cwd: repoRoot });
|
|
5671
|
+
const revResult = await runGit(repo, ["rev-parse", "HEAD"], { cwd: repoRoot });
|
|
5672
|
+
commitSha = revResult.stdout.trim();
|
|
5673
|
+
} catch (err) {
|
|
5674
|
+
const output = (err?.stdout || "") + (err?.stderr || "");
|
|
5675
|
+
if (/nothing to commit/i.test(output)) {
|
|
5676
|
+
throw new GitCommandError("git_command_failed", "Nothing to commit");
|
|
5677
|
+
}
|
|
5678
|
+
throw err;
|
|
5679
|
+
}
|
|
5680
|
+
return {
|
|
5681
|
+
workspace: repo.workspace,
|
|
5682
|
+
repoRoot,
|
|
5683
|
+
isGitRepo: true,
|
|
5684
|
+
commit: commitSha,
|
|
5685
|
+
message: fullMsg,
|
|
5686
|
+
lastCheckedAt: Date.now()
|
|
5687
|
+
};
|
|
5688
|
+
}
|
|
5689
|
+
async function gitStashPush(workspace, message, includeUntracked) {
|
|
5690
|
+
const repo = await resolveGitRepository(workspace);
|
|
5691
|
+
const repoRoot = repo.repoRoot;
|
|
5692
|
+
const stashArgs = ["stash", "push", "-m", message];
|
|
5693
|
+
if (includeUntracked) stashArgs.push("--include-untracked");
|
|
5694
|
+
const result = await runGit(repo, stashArgs, { cwd: repoRoot });
|
|
5695
|
+
if (/No local changes to save/i.test(result.stdout + result.stderr)) {
|
|
5696
|
+
throw new GitCommandError("git_command_failed", "Nothing to stash");
|
|
5697
|
+
}
|
|
5698
|
+
return {
|
|
5699
|
+
workspace: repo.workspace,
|
|
5700
|
+
repoRoot,
|
|
5701
|
+
isGitRepo: true,
|
|
5702
|
+
stashRef: "stash@{0}",
|
|
5703
|
+
message,
|
|
5704
|
+
lastCheckedAt: Date.now()
|
|
5705
|
+
};
|
|
5706
|
+
}
|
|
5707
|
+
async function gitStashPop(workspace, stashRef) {
|
|
5708
|
+
const repo = await resolveGitRepository(workspace);
|
|
5709
|
+
const repoRoot = repo.repoRoot;
|
|
5710
|
+
const popArgs = stashRef ? ["stash", "pop", stashRef] : ["stash", "pop"];
|
|
5711
|
+
await runGit(repo, popArgs, { cwd: repoRoot });
|
|
5712
|
+
}
|
|
5713
|
+
async function gitCheckoutFiles(workspace, paths) {
|
|
5714
|
+
const repo = await resolveGitRepository(workspace);
|
|
5715
|
+
const repoRoot = repo.repoRoot;
|
|
5716
|
+
const normalizedPaths = [];
|
|
5717
|
+
for (const p of paths) {
|
|
5718
|
+
if (typeof p !== "string" || !p.trim() || p.includes("\0")) {
|
|
5719
|
+
throw new GitCommandError("invalid_args", `Invalid path: ${String(p)}`);
|
|
5720
|
+
}
|
|
5721
|
+
if (path3.isAbsolute(p)) {
|
|
5722
|
+
throw new GitCommandError("invalid_args", `Path must be repository-relative, not absolute: ${p}`);
|
|
5723
|
+
}
|
|
5724
|
+
const normalized = path3.normalize(p.trim()).split(path3.sep).join("/");
|
|
5725
|
+
if (normalized.startsWith("../") || normalized === "..") {
|
|
5726
|
+
throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
|
|
5727
|
+
}
|
|
5728
|
+
const absolutePath = path3.resolve(repoRoot, normalized);
|
|
5729
|
+
if (!isPathInside(repoRoot, absolutePath)) {
|
|
5730
|
+
throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
|
|
5731
|
+
}
|
|
5732
|
+
normalizedPaths.push(normalized);
|
|
5733
|
+
}
|
|
5734
|
+
await runGit(repo, ["checkout", "--", ...normalizedPaths], { cwd: repoRoot });
|
|
5735
|
+
return { checkedOut: normalizedPaths };
|
|
5736
|
+
}
|
|
5737
|
+
async function gitGetRemoteUrl(workspace, remote) {
|
|
5738
|
+
const repo = await resolveGitRepository(workspace);
|
|
5739
|
+
const result = await runGit(repo, ["remote", "get-url", remote], { cwd: repo.repoRoot });
|
|
5740
|
+
const remoteUrl = result.stdout.trim();
|
|
5741
|
+
if (!remoteUrl) {
|
|
5742
|
+
throw new GitCommandError("git_command_failed", `Remote '${remote}' has no URL`);
|
|
5743
|
+
}
|
|
5744
|
+
return { remoteUrl, remote };
|
|
5745
|
+
}
|
|
5746
|
+
function formatOptionalGitLogRangeArg(flag, value) {
|
|
5747
|
+
return value ? [`${flag}=${value}`] : [];
|
|
5748
|
+
}
|
|
5749
|
+
async function getGitLog(workspace, options) {
|
|
5750
|
+
const lastCheckedAt = Date.now();
|
|
5751
|
+
const repo = await resolveGitRepository(workspace);
|
|
5752
|
+
const repoRoot = repo.repoRoot;
|
|
5753
|
+
const boundedLimit = Math.max(1, Math.min(200, Math.floor(options.limit || 50)));
|
|
5754
|
+
const selectedPath = options.path ? validateGitLogPath(repoRoot, options.path) : void 0;
|
|
5755
|
+
const result = await runGit(
|
|
5756
|
+
repo,
|
|
5757
|
+
[
|
|
5758
|
+
"log",
|
|
5759
|
+
`--max-count=${boundedLimit}`,
|
|
5760
|
+
"--format=%H%x00%an%x00%ae%x00%at%x00%ct%x00%s",
|
|
5761
|
+
...formatOptionalGitLogRangeArg("--since", options.since),
|
|
5762
|
+
...formatOptionalGitLogRangeArg("--until", options.until),
|
|
5763
|
+
"--",
|
|
5764
|
+
...selectedPath ? [selectedPath] : []
|
|
5765
|
+
],
|
|
5766
|
+
{ cwd: repoRoot }
|
|
5767
|
+
);
|
|
5768
|
+
const entries = result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => {
|
|
5769
|
+
const [commit = "", authorName, authorEmail, authoredAt, committedAt, ...messageParts] = line.split("\0");
|
|
5770
|
+
return {
|
|
5771
|
+
commit,
|
|
5772
|
+
message: messageParts.join("\0"),
|
|
5773
|
+
authorName: authorName || void 0,
|
|
5774
|
+
authorEmail: authorEmail || void 0,
|
|
5775
|
+
authoredAt: authoredAt ? Number.parseInt(authoredAt, 10) * 1e3 : void 0,
|
|
5776
|
+
committedAt: committedAt ? Number.parseInt(committedAt, 10) * 1e3 : void 0
|
|
5777
|
+
};
|
|
5778
|
+
}).filter((entry) => entry.commit.length > 0);
|
|
5779
|
+
return {
|
|
5780
|
+
workspace: repo.workspace,
|
|
5781
|
+
repoRoot,
|
|
5782
|
+
isGitRepo: true,
|
|
5783
|
+
entries,
|
|
5784
|
+
limit: boundedLimit,
|
|
5785
|
+
truncated: entries.length >= boundedLimit,
|
|
5786
|
+
lastCheckedAt
|
|
5787
|
+
};
|
|
5788
|
+
}
|
|
5789
|
+
function validateGitLogPath(repoRoot, filePath) {
|
|
5790
|
+
if (!filePath.trim() || filePath.includes("\0")) {
|
|
5791
|
+
throw new GitCommandError("invalid_args", "path must be a non-empty repository-relative path");
|
|
5792
|
+
}
|
|
5793
|
+
if (path3.isAbsolute(filePath)) {
|
|
5794
|
+
throw new GitCommandError("invalid_args", "path must be repository-relative");
|
|
5795
|
+
}
|
|
5796
|
+
const normalized = path3.normalize(filePath).split(path3.sep).join("/");
|
|
5797
|
+
const absolutePath = path3.resolve(repoRoot, normalized);
|
|
5798
|
+
if (!isPathInside(repoRoot, absolutePath) || normalized.startsWith("../") || normalized === "..") {
|
|
5799
|
+
throw new GitCommandError("path_outside_repo", "Git log path is outside the repository root");
|
|
5800
|
+
}
|
|
5801
|
+
return normalized;
|
|
5802
|
+
}
|
|
5803
|
+
|
|
5804
|
+
// src/git/turn-snapshot-tracker.ts
|
|
5805
|
+
var BUSY_STATUSES = /* @__PURE__ */ new Set(["streaming", "waiting_approval"]);
|
|
5806
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["idle", "error"]);
|
|
5807
|
+
var TurnSnapshotTracker = class {
|
|
5808
|
+
lastStatus = /* @__PURE__ */ new Map();
|
|
5809
|
+
onTurnCompleted;
|
|
5810
|
+
constructor(onTurnCompleted) {
|
|
5811
|
+
this.onTurnCompleted = onTurnCompleted;
|
|
5812
|
+
}
|
|
5813
|
+
record(sessionId, status, workspace) {
|
|
5814
|
+
const prev = this.lastStatus.get(sessionId);
|
|
5815
|
+
this.lastStatus.set(sessionId, status);
|
|
5816
|
+
if (workspace && prev && BUSY_STATUSES.has(prev) && TERMINAL_STATUSES.has(status)) {
|
|
5817
|
+
this.onTurnCompleted({ sessionId, workspace });
|
|
5818
|
+
}
|
|
5819
|
+
}
|
|
5820
|
+
forget(sessionId) {
|
|
5821
|
+
this.lastStatus.delete(sessionId);
|
|
5822
|
+
}
|
|
5823
|
+
};
|
|
5824
|
+
|
|
4527
5825
|
// src/index.ts
|
|
4528
5826
|
init_config();
|
|
4529
5827
|
|
|
4530
5828
|
// src/config/workspaces.ts
|
|
4531
5829
|
import * as fs from "fs";
|
|
4532
5830
|
import * as os from "os";
|
|
4533
|
-
import * as
|
|
5831
|
+
import * as path4 from "path";
|
|
4534
5832
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4535
5833
|
var MAX_WORKSPACES = 50;
|
|
4536
5834
|
function expandPath(p) {
|
|
4537
5835
|
const t = (p || "").trim();
|
|
4538
5836
|
if (!t) return "";
|
|
4539
|
-
if (t.startsWith("~")) return
|
|
4540
|
-
return
|
|
5837
|
+
if (t.startsWith("~")) return path4.join(os.homedir(), t.slice(1).replace(/^\//, ""));
|
|
5838
|
+
return path4.resolve(t);
|
|
4541
5839
|
}
|
|
4542
5840
|
function validateWorkspacePath(absPath) {
|
|
4543
5841
|
try {
|
|
@@ -4551,7 +5849,7 @@ function validateWorkspacePath(absPath) {
|
|
|
4551
5849
|
}
|
|
4552
5850
|
}
|
|
4553
5851
|
function defaultWorkspaceLabel(absPath) {
|
|
4554
|
-
const base =
|
|
5852
|
+
const base = path4.basename(absPath) || absPath;
|
|
4555
5853
|
return base;
|
|
4556
5854
|
}
|
|
4557
5855
|
function getDefaultWorkspacePath(config) {
|
|
@@ -4642,9 +5940,9 @@ function resolveIdeLaunchWorkspace(args, config) {
|
|
|
4642
5940
|
return getDefaultWorkspacePath(config) || void 0;
|
|
4643
5941
|
}
|
|
4644
5942
|
function findWorkspaceByPath(config, rawPath) {
|
|
4645
|
-
const abs =
|
|
5943
|
+
const abs = path4.resolve(expandPath(rawPath));
|
|
4646
5944
|
if (!abs) return void 0;
|
|
4647
|
-
return (config.workspaces || []).find((w) =>
|
|
5945
|
+
return (config.workspaces || []).find((w) => path4.resolve(expandPath(w.path)) === abs);
|
|
4648
5946
|
}
|
|
4649
5947
|
function addWorkspaceEntry(config, rawPath, label, options) {
|
|
4650
5948
|
const abs = expandPath(rawPath);
|
|
@@ -4660,7 +5958,7 @@ function addWorkspaceEntry(config, rawPath, label, options) {
|
|
|
4660
5958
|
const v = validateWorkspacePath(abs);
|
|
4661
5959
|
if (!v.ok) return { error: v.error };
|
|
4662
5960
|
const list = [...config.workspaces || []];
|
|
4663
|
-
if (list.some((w) =>
|
|
5961
|
+
if (list.some((w) => path4.resolve(w.path) === abs)) {
|
|
4664
5962
|
return { error: "Workspace already in list" };
|
|
4665
5963
|
}
|
|
4666
5964
|
if (list.length >= MAX_WORKSPACES) {
|
|
@@ -4694,7 +5992,7 @@ function setDefaultWorkspaceId(config, id) {
|
|
|
4694
5992
|
}
|
|
4695
5993
|
|
|
4696
5994
|
// src/config/recent-activity.ts
|
|
4697
|
-
import * as
|
|
5995
|
+
import * as path5 from "path";
|
|
4698
5996
|
|
|
4699
5997
|
// src/providers/summary-metadata.ts
|
|
4700
5998
|
function normalizeSummaryItem(item) {
|
|
@@ -4763,9 +6061,9 @@ var MAX_ACTIVITY = 30;
|
|
|
4763
6061
|
function normalizeWorkspace(workspace) {
|
|
4764
6062
|
if (!workspace) return "";
|
|
4765
6063
|
try {
|
|
4766
|
-
return
|
|
6064
|
+
return path5.resolve(expandPath(workspace));
|
|
4767
6065
|
} catch {
|
|
4768
|
-
return
|
|
6066
|
+
return path5.resolve(workspace);
|
|
4769
6067
|
}
|
|
4770
6068
|
}
|
|
4771
6069
|
function buildRecentActivityKey(entry) {
|
|
@@ -4933,14 +6231,14 @@ function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker
|
|
|
4933
6231
|
}
|
|
4934
6232
|
|
|
4935
6233
|
// src/config/saved-sessions.ts
|
|
4936
|
-
import * as
|
|
6234
|
+
import * as path6 from "path";
|
|
4937
6235
|
var MAX_SAVED_SESSIONS = 500;
|
|
4938
6236
|
function normalizeWorkspace2(workspace) {
|
|
4939
6237
|
if (!workspace) return "";
|
|
4940
6238
|
try {
|
|
4941
|
-
return
|
|
6239
|
+
return path6.resolve(expandPath(workspace));
|
|
4942
6240
|
} catch {
|
|
4943
|
-
return
|
|
6241
|
+
return path6.resolve(workspace);
|
|
4944
6242
|
}
|
|
4945
6243
|
}
|
|
4946
6244
|
function buildSavedProviderSessionKey(providerSessionId) {
|
|
@@ -5059,7 +6357,7 @@ function resetState() {
|
|
|
5059
6357
|
import { execSync } from "child_process";
|
|
5060
6358
|
import { existsSync as existsSync4 } from "fs";
|
|
5061
6359
|
import { platform, homedir as homedir3 } from "os";
|
|
5062
|
-
import * as
|
|
6360
|
+
import * as path7 from "path";
|
|
5063
6361
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
5064
6362
|
var registeredIDEs = /* @__PURE__ */ new Map();
|
|
5065
6363
|
function registerIDEDefinition(def) {
|
|
@@ -5078,9 +6376,9 @@ function getMergedDefinitions() {
|
|
|
5078
6376
|
function findCliCommand(command) {
|
|
5079
6377
|
const trimmed = String(command || "").trim();
|
|
5080
6378
|
if (!trimmed) return null;
|
|
5081
|
-
if (
|
|
5082
|
-
const candidate = trimmed.startsWith("~") ?
|
|
5083
|
-
const resolved =
|
|
6379
|
+
if (path7.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
6380
|
+
const candidate = trimmed.startsWith("~") ? path7.join(homedir3(), trimmed.slice(1)) : trimmed;
|
|
6381
|
+
const resolved = path7.isAbsolute(candidate) ? candidate : path7.resolve(candidate);
|
|
5084
6382
|
return existsSync4(resolved) ? resolved : null;
|
|
5085
6383
|
}
|
|
5086
6384
|
try {
|
|
@@ -5108,7 +6406,7 @@ function getIdeVersion(cliCommand) {
|
|
|
5108
6406
|
function checkPathExists(paths) {
|
|
5109
6407
|
const home = homedir3();
|
|
5110
6408
|
for (const p of paths) {
|
|
5111
|
-
const normalized = p.startsWith("~") ?
|
|
6409
|
+
const normalized = p.startsWith("~") ? path7.join(home, p.slice(1)) : p;
|
|
5112
6410
|
if (normalized.includes("*")) {
|
|
5113
6411
|
const username = home.split(/[\\/]/).pop() || "";
|
|
5114
6412
|
const resolved = normalized.replace("*", username);
|
|
@@ -5166,7 +6464,7 @@ async function detectIDEs(providerLoader) {
|
|
|
5166
6464
|
// src/detection/cli-detector.ts
|
|
5167
6465
|
import { exec } from "child_process";
|
|
5168
6466
|
import * as os2 from "os";
|
|
5169
|
-
import * as
|
|
6467
|
+
import * as path8 from "path";
|
|
5170
6468
|
import { existsSync as existsSync5 } from "fs";
|
|
5171
6469
|
function parseVersion(raw) {
|
|
5172
6470
|
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
@@ -5179,36 +6477,36 @@ function shellQuote(value) {
|
|
|
5179
6477
|
function expandHome(value) {
|
|
5180
6478
|
const trimmed = value.trim();
|
|
5181
6479
|
if (!trimmed.startsWith("~")) return trimmed;
|
|
5182
|
-
return
|
|
6480
|
+
return path8.join(os2.homedir(), trimmed.slice(1));
|
|
5183
6481
|
}
|
|
5184
6482
|
function isExplicitCommandPath(command) {
|
|
5185
6483
|
const trimmed = command.trim();
|
|
5186
|
-
return
|
|
6484
|
+
return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
5187
6485
|
}
|
|
5188
6486
|
function resolveCommandPath(command) {
|
|
5189
6487
|
const trimmed = command.trim();
|
|
5190
6488
|
if (!trimmed) return null;
|
|
5191
6489
|
if (isExplicitCommandPath(trimmed)) {
|
|
5192
6490
|
const expanded = expandHome(trimmed);
|
|
5193
|
-
const candidate =
|
|
6491
|
+
const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
|
|
5194
6492
|
return existsSync5(candidate) ? candidate : null;
|
|
5195
6493
|
}
|
|
5196
6494
|
return null;
|
|
5197
6495
|
}
|
|
5198
6496
|
function execAsync(cmd, timeoutMs = 5e3) {
|
|
5199
|
-
return new Promise((
|
|
6497
|
+
return new Promise((resolve15) => {
|
|
5200
6498
|
const child = exec(cmd, {
|
|
5201
6499
|
encoding: "utf-8",
|
|
5202
6500
|
timeout: timeoutMs,
|
|
5203
6501
|
...process.platform === "win32" ? { windowsHide: true } : {}
|
|
5204
6502
|
}, (err, stdout) => {
|
|
5205
6503
|
if (err || !stdout?.trim()) {
|
|
5206
|
-
|
|
6504
|
+
resolve15(null);
|
|
5207
6505
|
} else {
|
|
5208
|
-
|
|
6506
|
+
resolve15(stdout.trim());
|
|
5209
6507
|
}
|
|
5210
6508
|
});
|
|
5211
|
-
child.on("error", () =>
|
|
6509
|
+
child.on("error", () => resolve15(null));
|
|
5212
6510
|
});
|
|
5213
6511
|
}
|
|
5214
6512
|
async function detectCLIs(providerLoader, options) {
|
|
@@ -5573,7 +6871,7 @@ var DaemonCdpManager = class {
|
|
|
5573
6871
|
* Returns multiple entries if multiple IDE windows are open on same port
|
|
5574
6872
|
*/
|
|
5575
6873
|
static listAllTargets(port) {
|
|
5576
|
-
return new Promise((
|
|
6874
|
+
return new Promise((resolve15) => {
|
|
5577
6875
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
5578
6876
|
let data = "";
|
|
5579
6877
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -5589,16 +6887,16 @@ var DaemonCdpManager = class {
|
|
|
5589
6887
|
(t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
|
|
5590
6888
|
);
|
|
5591
6889
|
const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
|
|
5592
|
-
|
|
6890
|
+
resolve15(mainPages.length > 0 ? mainPages : fallbackPages);
|
|
5593
6891
|
} catch {
|
|
5594
|
-
|
|
6892
|
+
resolve15([]);
|
|
5595
6893
|
}
|
|
5596
6894
|
});
|
|
5597
6895
|
});
|
|
5598
|
-
req.on("error", () =>
|
|
6896
|
+
req.on("error", () => resolve15([]));
|
|
5599
6897
|
req.setTimeout(2e3, () => {
|
|
5600
6898
|
req.destroy();
|
|
5601
|
-
|
|
6899
|
+
resolve15([]);
|
|
5602
6900
|
});
|
|
5603
6901
|
});
|
|
5604
6902
|
}
|
|
@@ -5638,7 +6936,7 @@ var DaemonCdpManager = class {
|
|
|
5638
6936
|
}
|
|
5639
6937
|
}
|
|
5640
6938
|
findTargetOnPort(port) {
|
|
5641
|
-
return new Promise((
|
|
6939
|
+
return new Promise((resolve15) => {
|
|
5642
6940
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
5643
6941
|
let data = "";
|
|
5644
6942
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -5649,7 +6947,7 @@ var DaemonCdpManager = class {
|
|
|
5649
6947
|
(t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
|
|
5650
6948
|
);
|
|
5651
6949
|
if (pages.length === 0) {
|
|
5652
|
-
|
|
6950
|
+
resolve15(targets.find((t) => t.webSocketDebuggerUrl) || null);
|
|
5653
6951
|
return;
|
|
5654
6952
|
}
|
|
5655
6953
|
const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
|
|
@@ -5668,25 +6966,25 @@ var DaemonCdpManager = class {
|
|
|
5668
6966
|
this._targetId = selected.target.id;
|
|
5669
6967
|
}
|
|
5670
6968
|
this._pageTitle = selected.target.title || "";
|
|
5671
|
-
|
|
6969
|
+
resolve15(selected.target);
|
|
5672
6970
|
return;
|
|
5673
6971
|
}
|
|
5674
6972
|
if (previousTargetId) {
|
|
5675
6973
|
this.log(`[CDP] Target ${previousTargetId} not found in page list`);
|
|
5676
|
-
|
|
6974
|
+
resolve15(null);
|
|
5677
6975
|
return;
|
|
5678
6976
|
}
|
|
5679
6977
|
this._pageTitle = list[0]?.title || "";
|
|
5680
|
-
|
|
6978
|
+
resolve15(list[0]);
|
|
5681
6979
|
} catch {
|
|
5682
|
-
|
|
6980
|
+
resolve15(null);
|
|
5683
6981
|
}
|
|
5684
6982
|
});
|
|
5685
6983
|
});
|
|
5686
|
-
req.on("error", () =>
|
|
6984
|
+
req.on("error", () => resolve15(null));
|
|
5687
6985
|
req.setTimeout(2e3, () => {
|
|
5688
6986
|
req.destroy();
|
|
5689
|
-
|
|
6987
|
+
resolve15(null);
|
|
5690
6988
|
});
|
|
5691
6989
|
});
|
|
5692
6990
|
}
|
|
@@ -5697,7 +6995,7 @@ var DaemonCdpManager = class {
|
|
|
5697
6995
|
this.extensionProviders = providers;
|
|
5698
6996
|
}
|
|
5699
6997
|
connectToTarget(wsUrl) {
|
|
5700
|
-
return new Promise((
|
|
6998
|
+
return new Promise((resolve15) => {
|
|
5701
6999
|
this.ws = new WebSocket(wsUrl);
|
|
5702
7000
|
this.ws.on("open", async () => {
|
|
5703
7001
|
this._connected = true;
|
|
@@ -5707,17 +7005,17 @@ var DaemonCdpManager = class {
|
|
|
5707
7005
|
}
|
|
5708
7006
|
this.connectBrowserWs().catch(() => {
|
|
5709
7007
|
});
|
|
5710
|
-
|
|
7008
|
+
resolve15(true);
|
|
5711
7009
|
});
|
|
5712
7010
|
this.ws.on("message", (data) => {
|
|
5713
7011
|
try {
|
|
5714
7012
|
const msg = JSON.parse(data.toString());
|
|
5715
7013
|
if (msg.id && this.pending.has(msg.id)) {
|
|
5716
|
-
const { resolve:
|
|
7014
|
+
const { resolve: resolve16, reject } = this.pending.get(msg.id);
|
|
5717
7015
|
this.pending.delete(msg.id);
|
|
5718
7016
|
this.failureCount = 0;
|
|
5719
7017
|
if (msg.error) reject(new Error(msg.error.message));
|
|
5720
|
-
else
|
|
7018
|
+
else resolve16(msg.result);
|
|
5721
7019
|
} else if (msg.method === "Runtime.executionContextCreated") {
|
|
5722
7020
|
this.contexts.add(msg.params.context.id);
|
|
5723
7021
|
} else if (msg.method === "Runtime.executionContextDestroyed") {
|
|
@@ -5740,7 +7038,7 @@ var DaemonCdpManager = class {
|
|
|
5740
7038
|
this.ws.on("error", (err) => {
|
|
5741
7039
|
this.log(`[CDP] WebSocket error: ${err.message}`);
|
|
5742
7040
|
this._connected = false;
|
|
5743
|
-
|
|
7041
|
+
resolve15(false);
|
|
5744
7042
|
});
|
|
5745
7043
|
});
|
|
5746
7044
|
}
|
|
@@ -5754,7 +7052,7 @@ var DaemonCdpManager = class {
|
|
|
5754
7052
|
return;
|
|
5755
7053
|
}
|
|
5756
7054
|
this.log(`[CDP] Connecting browser WS for target discovery...`);
|
|
5757
|
-
await new Promise((
|
|
7055
|
+
await new Promise((resolve15, reject) => {
|
|
5758
7056
|
this.browserWs = new WebSocket(browserWsUrl);
|
|
5759
7057
|
this.browserWs.on("open", async () => {
|
|
5760
7058
|
this._browserConnected = true;
|
|
@@ -5764,16 +7062,16 @@ var DaemonCdpManager = class {
|
|
|
5764
7062
|
} catch (e) {
|
|
5765
7063
|
this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
|
|
5766
7064
|
}
|
|
5767
|
-
|
|
7065
|
+
resolve15();
|
|
5768
7066
|
});
|
|
5769
7067
|
this.browserWs.on("message", (data) => {
|
|
5770
7068
|
try {
|
|
5771
7069
|
const msg = JSON.parse(data.toString());
|
|
5772
7070
|
if (msg.id && this.browserPending.has(msg.id)) {
|
|
5773
|
-
const { resolve:
|
|
7071
|
+
const { resolve: resolve16, reject: reject2 } = this.browserPending.get(msg.id);
|
|
5774
7072
|
this.browserPending.delete(msg.id);
|
|
5775
7073
|
if (msg.error) reject2(new Error(msg.error.message));
|
|
5776
|
-
else
|
|
7074
|
+
else resolve16(msg.result);
|
|
5777
7075
|
}
|
|
5778
7076
|
} catch {
|
|
5779
7077
|
}
|
|
@@ -5793,31 +7091,31 @@ var DaemonCdpManager = class {
|
|
|
5793
7091
|
}
|
|
5794
7092
|
}
|
|
5795
7093
|
getBrowserWsUrl() {
|
|
5796
|
-
return new Promise((
|
|
7094
|
+
return new Promise((resolve15) => {
|
|
5797
7095
|
const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
|
|
5798
7096
|
let data = "";
|
|
5799
7097
|
res.on("data", (chunk) => data += chunk.toString());
|
|
5800
7098
|
res.on("end", () => {
|
|
5801
7099
|
try {
|
|
5802
7100
|
const info = JSON.parse(data);
|
|
5803
|
-
|
|
7101
|
+
resolve15(info.webSocketDebuggerUrl || null);
|
|
5804
7102
|
} catch {
|
|
5805
|
-
|
|
7103
|
+
resolve15(null);
|
|
5806
7104
|
}
|
|
5807
7105
|
});
|
|
5808
7106
|
});
|
|
5809
|
-
req.on("error", () =>
|
|
7107
|
+
req.on("error", () => resolve15(null));
|
|
5810
7108
|
req.setTimeout(3e3, () => {
|
|
5811
7109
|
req.destroy();
|
|
5812
|
-
|
|
7110
|
+
resolve15(null);
|
|
5813
7111
|
});
|
|
5814
7112
|
});
|
|
5815
7113
|
}
|
|
5816
7114
|
sendBrowser(method, params = {}, timeoutMs = 15e3) {
|
|
5817
|
-
return new Promise((
|
|
7115
|
+
return new Promise((resolve15, reject) => {
|
|
5818
7116
|
if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
|
|
5819
7117
|
const id = this.browserMsgId++;
|
|
5820
|
-
this.browserPending.set(id, { resolve:
|
|
7118
|
+
this.browserPending.set(id, { resolve: resolve15, reject });
|
|
5821
7119
|
this.browserWs.send(JSON.stringify({ id, method, params }));
|
|
5822
7120
|
setTimeout(() => {
|
|
5823
7121
|
if (this.browserPending.has(id)) {
|
|
@@ -5857,11 +7155,11 @@ var DaemonCdpManager = class {
|
|
|
5857
7155
|
}
|
|
5858
7156
|
// ─── CDP Protocol ────────────────────────────────────────
|
|
5859
7157
|
sendInternal(method, params = {}, timeoutMs = 15e3) {
|
|
5860
|
-
return new Promise((
|
|
7158
|
+
return new Promise((resolve15, reject) => {
|
|
5861
7159
|
if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
|
|
5862
7160
|
if (this.ws.readyState !== WebSocket.OPEN) return reject(new Error("WebSocket not open"));
|
|
5863
7161
|
const id = this.msgId++;
|
|
5864
|
-
this.pending.set(id, { resolve:
|
|
7162
|
+
this.pending.set(id, { resolve: resolve15, reject });
|
|
5865
7163
|
this.ws.send(JSON.stringify({ id, method, params }));
|
|
5866
7164
|
setTimeout(() => {
|
|
5867
7165
|
if (this.pending.has(id)) {
|
|
@@ -6110,7 +7408,7 @@ var DaemonCdpManager = class {
|
|
|
6110
7408
|
const browserWs = this.browserWs;
|
|
6111
7409
|
let msgId = this.browserMsgId;
|
|
6112
7410
|
const sendWs = (method, params = {}, sessionId) => {
|
|
6113
|
-
return new Promise((
|
|
7411
|
+
return new Promise((resolve15, reject) => {
|
|
6114
7412
|
const mid = msgId++;
|
|
6115
7413
|
this.browserMsgId = msgId;
|
|
6116
7414
|
const handler = (raw) => {
|
|
@@ -6119,7 +7417,7 @@ var DaemonCdpManager = class {
|
|
|
6119
7417
|
if (msg.id === mid) {
|
|
6120
7418
|
browserWs.removeListener("message", handler);
|
|
6121
7419
|
if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
6122
|
-
else
|
|
7420
|
+
else resolve15(msg.result);
|
|
6123
7421
|
}
|
|
6124
7422
|
} catch {
|
|
6125
7423
|
}
|
|
@@ -6320,14 +7618,14 @@ var DaemonCdpManager = class {
|
|
|
6320
7618
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
6321
7619
|
throw new Error("CDP not connected");
|
|
6322
7620
|
}
|
|
6323
|
-
return new Promise((
|
|
7621
|
+
return new Promise((resolve15, reject) => {
|
|
6324
7622
|
const id = getNextId();
|
|
6325
7623
|
pendingMap.set(id, {
|
|
6326
7624
|
resolve: (result) => {
|
|
6327
7625
|
if (result?.result?.subtype === "error") {
|
|
6328
7626
|
reject(new Error(result.result.description));
|
|
6329
7627
|
} else {
|
|
6330
|
-
|
|
7628
|
+
resolve15(result?.result?.value);
|
|
6331
7629
|
}
|
|
6332
7630
|
},
|
|
6333
7631
|
reject
|
|
@@ -6359,10 +7657,10 @@ var DaemonCdpManager = class {
|
|
|
6359
7657
|
throw new Error("CDP not connected");
|
|
6360
7658
|
}
|
|
6361
7659
|
const sendViaSession = (method, params = {}) => {
|
|
6362
|
-
return new Promise((
|
|
7660
|
+
return new Promise((resolve15, reject) => {
|
|
6363
7661
|
const pendingMap = this._browserConnected ? this.browserPending : this.pending;
|
|
6364
7662
|
const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
|
|
6365
|
-
pendingMap.set(id, { resolve:
|
|
7663
|
+
pendingMap.set(id, { resolve: resolve15, reject });
|
|
6366
7664
|
ws.send(JSON.stringify({ id, sessionId, method, params }));
|
|
6367
7665
|
setTimeout(() => {
|
|
6368
7666
|
if (pendingMap.has(id)) {
|
|
@@ -7109,9 +8407,9 @@ ${cleanBody}`;
|
|
|
7109
8407
|
// src/config/chat-history.ts
|
|
7110
8408
|
init_chat_message_normalization();
|
|
7111
8409
|
import * as fs3 from "fs";
|
|
7112
|
-
import * as
|
|
8410
|
+
import * as path10 from "path";
|
|
7113
8411
|
import * as os5 from "os";
|
|
7114
|
-
var HISTORY_DIR =
|
|
8412
|
+
var HISTORY_DIR = path10.join(os5.homedir(), ".adhdev", "history");
|
|
7115
8413
|
var RETAIN_DAYS = 30;
|
|
7116
8414
|
var SAVED_HISTORY_INDEX_VERSION = 1;
|
|
7117
8415
|
var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
|
|
@@ -7274,8 +8572,8 @@ function extractSavedHistorySessionIdFromFile(file) {
|
|
|
7274
8572
|
function buildSavedHistoryFileSignatureMap(dir, files) {
|
|
7275
8573
|
return new Map(files.map((file) => {
|
|
7276
8574
|
try {
|
|
7277
|
-
const
|
|
7278
|
-
return [file, `${file}:${
|
|
8575
|
+
const stat2 = fs3.statSync(path10.join(dir, file));
|
|
8576
|
+
return [file, `${file}:${stat2.size}:${Math.trunc(stat2.mtimeMs)}`];
|
|
7279
8577
|
} catch {
|
|
7280
8578
|
return [file, `${file}:missing`];
|
|
7281
8579
|
}
|
|
@@ -7285,7 +8583,7 @@ function buildSavedHistoryCacheSignature(files, fileSignatures) {
|
|
|
7285
8583
|
return files.map((file) => fileSignatures.get(file) || `${file}:missing`).join("|");
|
|
7286
8584
|
}
|
|
7287
8585
|
function getSavedHistoryIndexFilePath(dir) {
|
|
7288
|
-
return
|
|
8586
|
+
return path10.join(dir, SAVED_HISTORY_INDEX_FILE);
|
|
7289
8587
|
}
|
|
7290
8588
|
function getSavedHistoryIndexLockPath(dir) {
|
|
7291
8589
|
return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
|
|
@@ -7338,8 +8636,8 @@ function acquireSavedHistoryIndexLock(dir) {
|
|
|
7338
8636
|
} catch (error) {
|
|
7339
8637
|
if (error?.code !== "EEXIST") return null;
|
|
7340
8638
|
try {
|
|
7341
|
-
const
|
|
7342
|
-
if (Date.now() -
|
|
8639
|
+
const stat2 = fs3.statSync(lockPath);
|
|
8640
|
+
if (Date.now() - stat2.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
|
|
7343
8641
|
fs3.rmSync(lockPath, { recursive: true, force: true });
|
|
7344
8642
|
continue;
|
|
7345
8643
|
}
|
|
@@ -7387,7 +8685,7 @@ function savePersistedSavedHistoryIndex(dir, entries) {
|
|
|
7387
8685
|
}
|
|
7388
8686
|
for (const file of Array.from(currentEntries.keys())) {
|
|
7389
8687
|
if (incomingFiles.has(file)) continue;
|
|
7390
|
-
if (!fs3.existsSync(
|
|
8688
|
+
if (!fs3.existsSync(path10.join(dir, file))) {
|
|
7391
8689
|
currentEntries.delete(file);
|
|
7392
8690
|
}
|
|
7393
8691
|
}
|
|
@@ -7402,8 +8700,8 @@ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
|
|
|
7402
8700
|
}
|
|
7403
8701
|
function buildSavedHistoryIndexFileSignature(dir) {
|
|
7404
8702
|
try {
|
|
7405
|
-
const
|
|
7406
|
-
return `index:${
|
|
8703
|
+
const stat2 = fs3.statSync(getSavedHistoryIndexFilePath(dir));
|
|
8704
|
+
return `index:${stat2.size}:${Math.trunc(stat2.mtimeMs)}`;
|
|
7407
8705
|
} catch {
|
|
7408
8706
|
return "index:missing";
|
|
7409
8707
|
}
|
|
@@ -7413,8 +8711,8 @@ function historyDirectoryHasFilesNewerThanIndex(dir) {
|
|
|
7413
8711
|
const indexStat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
|
|
7414
8712
|
const files = listHistoryFiles(dir);
|
|
7415
8713
|
for (const file of files) {
|
|
7416
|
-
const
|
|
7417
|
-
if (
|
|
8714
|
+
const stat2 = fs3.statSync(path10.join(dir, file));
|
|
8715
|
+
if (stat2.mtimeMs > indexStat.mtimeMs) return true;
|
|
7418
8716
|
}
|
|
7419
8717
|
return false;
|
|
7420
8718
|
} catch {
|
|
@@ -7423,14 +8721,14 @@ function historyDirectoryHasFilesNewerThanIndex(dir) {
|
|
|
7423
8721
|
}
|
|
7424
8722
|
function buildSavedHistoryFileSignature(dir, file) {
|
|
7425
8723
|
try {
|
|
7426
|
-
const
|
|
7427
|
-
return `${file}:${
|
|
8724
|
+
const stat2 = fs3.statSync(path10.join(dir, file));
|
|
8725
|
+
return `${file}:${stat2.size}:${Math.trunc(stat2.mtimeMs)}`;
|
|
7428
8726
|
} catch {
|
|
7429
8727
|
return `${file}:missing`;
|
|
7430
8728
|
}
|
|
7431
8729
|
}
|
|
7432
8730
|
function persistSavedHistoryFileSummaryEntry(agentType, dir, file, updater) {
|
|
7433
|
-
const filePath =
|
|
8731
|
+
const filePath = path10.join(dir, file);
|
|
7434
8732
|
const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
|
|
7435
8733
|
const currentEntry = entries.get(file) || null;
|
|
7436
8734
|
const nextSummary = updater(currentEntry?.summary || null);
|
|
@@ -7503,7 +8801,7 @@ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file, histor
|
|
|
7503
8801
|
function computeSavedHistoryFileSummary(dir, file) {
|
|
7504
8802
|
const historySessionId = extractSavedHistorySessionIdFromFile(file);
|
|
7505
8803
|
if (!historySessionId) return null;
|
|
7506
|
-
const filePath =
|
|
8804
|
+
const filePath = path10.join(dir, file);
|
|
7507
8805
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
7508
8806
|
const lines = content.split("\n").filter(Boolean);
|
|
7509
8807
|
let messageCount = 0;
|
|
@@ -7590,7 +8888,7 @@ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatur
|
|
|
7590
8888
|
const summaryBySessionId = /* @__PURE__ */ new Map();
|
|
7591
8889
|
const nextPersistedEntries = /* @__PURE__ */ new Map();
|
|
7592
8890
|
for (const file of files.slice().sort()) {
|
|
7593
|
-
const filePath =
|
|
8891
|
+
const filePath = path10.join(dir, file);
|
|
7594
8892
|
const signature = fileSignatures.get(file) || `${file}:missing`;
|
|
7595
8893
|
const cached = savedHistoryFileSummaryCache.get(filePath);
|
|
7596
8894
|
const persisted = persistedEntries.get(file);
|
|
@@ -7710,12 +9008,12 @@ var ChatHistoryWriter = class {
|
|
|
7710
9008
|
});
|
|
7711
9009
|
}
|
|
7712
9010
|
if (newMessages.length === 0) return;
|
|
7713
|
-
const dir =
|
|
9011
|
+
const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
|
|
7714
9012
|
fs3.mkdirSync(dir, { recursive: true });
|
|
7715
9013
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7716
9014
|
const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
|
|
7717
9015
|
const fileName = `${filePrefix}${date}.jsonl`;
|
|
7718
|
-
const filePath =
|
|
9016
|
+
const filePath = path10.join(dir, fileName);
|
|
7719
9017
|
const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
7720
9018
|
fs3.appendFileSync(filePath, lines, "utf-8");
|
|
7721
9019
|
updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
|
|
@@ -7806,11 +9104,11 @@ var ChatHistoryWriter = class {
|
|
|
7806
9104
|
const ws = String(workspace || "").trim();
|
|
7807
9105
|
if (!id || !ws) return;
|
|
7808
9106
|
try {
|
|
7809
|
-
const dir =
|
|
9107
|
+
const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
|
|
7810
9108
|
fs3.mkdirSync(dir, { recursive: true });
|
|
7811
9109
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7812
9110
|
const fileName = `${this.sanitize(id)}_${date}.jsonl`;
|
|
7813
|
-
const filePath =
|
|
9111
|
+
const filePath = path10.join(dir, fileName);
|
|
7814
9112
|
const record = {
|
|
7815
9113
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7816
9114
|
receivedAt: Date.now(),
|
|
@@ -7856,14 +9154,14 @@ var ChatHistoryWriter = class {
|
|
|
7856
9154
|
this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
|
|
7857
9155
|
this.lastSeenCounts.delete(fromDedupKey);
|
|
7858
9156
|
}
|
|
7859
|
-
const dir =
|
|
9157
|
+
const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
|
|
7860
9158
|
if (!fs3.existsSync(dir)) return;
|
|
7861
9159
|
const fromPrefix = `${this.sanitize(fromId)}_`;
|
|
7862
9160
|
const toPrefix = `${this.sanitize(toId)}_`;
|
|
7863
9161
|
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
|
|
7864
9162
|
for (const file of files) {
|
|
7865
|
-
const sourcePath =
|
|
7866
|
-
const targetPath =
|
|
9163
|
+
const sourcePath = path10.join(dir, file);
|
|
9164
|
+
const targetPath = path10.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
|
|
7867
9165
|
const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
|
|
7868
9166
|
const rewritten = sourceLines.map((line) => {
|
|
7869
9167
|
try {
|
|
@@ -7897,13 +9195,13 @@ var ChatHistoryWriter = class {
|
|
|
7897
9195
|
const sessionId = String(historySessionId || "").trim();
|
|
7898
9196
|
if (!sessionId) return;
|
|
7899
9197
|
try {
|
|
7900
|
-
const dir =
|
|
9198
|
+
const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
|
|
7901
9199
|
if (!fs3.existsSync(dir)) return;
|
|
7902
9200
|
const prefix = `${this.sanitize(sessionId)}_`;
|
|
7903
9201
|
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
|
|
7904
9202
|
const seen = /* @__PURE__ */ new Set();
|
|
7905
9203
|
for (const file of files) {
|
|
7906
|
-
const filePath =
|
|
9204
|
+
const filePath = path10.join(dir, file);
|
|
7907
9205
|
const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
7908
9206
|
const next = [];
|
|
7909
9207
|
for (const line of lines) {
|
|
@@ -7957,13 +9255,13 @@ var ChatHistoryWriter = class {
|
|
|
7957
9255
|
const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
|
|
7958
9256
|
const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
7959
9257
|
for (const dir of agentDirs) {
|
|
7960
|
-
const dirPath =
|
|
9258
|
+
const dirPath = path10.join(HISTORY_DIR, dir.name);
|
|
7961
9259
|
const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
|
|
7962
9260
|
let removedAny = false;
|
|
7963
9261
|
for (const file of files) {
|
|
7964
|
-
const filePath =
|
|
7965
|
-
const
|
|
7966
|
-
if (
|
|
9262
|
+
const filePath = path10.join(dirPath, file);
|
|
9263
|
+
const stat2 = fs3.statSync(filePath);
|
|
9264
|
+
if (stat2.mtimeMs < cutoff) {
|
|
7967
9265
|
fs3.unlinkSync(filePath);
|
|
7968
9266
|
removedAny = true;
|
|
7969
9267
|
}
|
|
@@ -8011,13 +9309,13 @@ function pageHistoryRecords(agentType, records, offset = 0, limit = 30, excludeR
|
|
|
8011
9309
|
function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0, historyBehavior) {
|
|
8012
9310
|
try {
|
|
8013
9311
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
8014
|
-
const dir =
|
|
9312
|
+
const dir = path10.join(HISTORY_DIR, sanitized);
|
|
8015
9313
|
if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
|
|
8016
9314
|
const files = listHistoryFiles(dir, historySessionId);
|
|
8017
9315
|
const allMessages = [];
|
|
8018
9316
|
const seen = /* @__PURE__ */ new Set();
|
|
8019
9317
|
for (const file of files) {
|
|
8020
|
-
const filePath =
|
|
9318
|
+
const filePath = path10.join(dir, file);
|
|
8021
9319
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
8022
9320
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
8023
9321
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -8041,7 +9339,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, ex
|
|
|
8041
9339
|
function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
|
|
8042
9340
|
try {
|
|
8043
9341
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
8044
|
-
const dir =
|
|
9342
|
+
const dir = path10.join(HISTORY_DIR, sanitized);
|
|
8045
9343
|
if (!fs3.existsSync(dir)) {
|
|
8046
9344
|
savedHistorySessionCache.delete(sanitized);
|
|
8047
9345
|
return { sessions: [], hasMore: false };
|
|
@@ -8102,11 +9400,11 @@ function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
|
|
|
8102
9400
|
}
|
|
8103
9401
|
function readExistingSessionStartRecord(agentType, historySessionId) {
|
|
8104
9402
|
try {
|
|
8105
|
-
const dir =
|
|
9403
|
+
const dir = path10.join(HISTORY_DIR, agentType);
|
|
8106
9404
|
if (!fs3.existsSync(dir)) return null;
|
|
8107
9405
|
const files = listHistoryFiles(dir, historySessionId).sort();
|
|
8108
9406
|
for (const file of files) {
|
|
8109
|
-
const lines = fs3.readFileSync(
|
|
9407
|
+
const lines = fs3.readFileSync(path10.join(dir, file), "utf-8").split("\n").filter(Boolean);
|
|
8110
9408
|
for (const line of lines) {
|
|
8111
9409
|
try {
|
|
8112
9410
|
const parsed = JSON.parse(line);
|
|
@@ -8126,16 +9424,16 @@ function readExistingSessionStartRecord(agentType, historySessionId) {
|
|
|
8126
9424
|
function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
|
|
8127
9425
|
if (records.length === 0) return false;
|
|
8128
9426
|
try {
|
|
8129
|
-
const dir =
|
|
9427
|
+
const dir = path10.join(HISTORY_DIR, agentType);
|
|
8130
9428
|
fs3.mkdirSync(dir, { recursive: true });
|
|
8131
9429
|
const prefix = `${historySessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}_`;
|
|
8132
9430
|
for (const file of fs3.readdirSync(dir)) {
|
|
8133
9431
|
if (file.startsWith(prefix) && file.endsWith(".jsonl")) {
|
|
8134
|
-
fs3.unlinkSync(
|
|
9432
|
+
fs3.unlinkSync(path10.join(dir, file));
|
|
8135
9433
|
}
|
|
8136
9434
|
}
|
|
8137
9435
|
const targetDate = new Date(records[records.length - 1].receivedAt || Date.now()).toISOString().slice(0, 10);
|
|
8138
|
-
const filePath =
|
|
9436
|
+
const filePath = path10.join(dir, `${prefix}${targetDate}.jsonl`);
|
|
8139
9437
|
fs3.writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join("\n")}
|
|
8140
9438
|
`, "utf-8");
|
|
8141
9439
|
invalidatePersistedSavedHistoryIndex(agentType, dir);
|
|
@@ -10007,6 +11305,10 @@ function shouldIncludeSessionMetadata(profile) {
|
|
|
10007
11305
|
function shouldIncludeRuntimeMetadata(profile) {
|
|
10008
11306
|
return true;
|
|
10009
11307
|
}
|
|
11308
|
+
function getGitSummaryForWorkspace(workspace, options) {
|
|
11309
|
+
if (!workspace) return void 0;
|
|
11310
|
+
return options.getGitSummaryForWorkspace?.(workspace) || void 0;
|
|
11311
|
+
}
|
|
10010
11312
|
function findCdpManager(cdpManagers, key) {
|
|
10011
11313
|
const exact = cdpManagers.get(key);
|
|
10012
11314
|
if (exact) return exact.isConnected ? exact : null;
|
|
@@ -10062,6 +11364,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
|
|
|
10062
11364
|
const controlValues = normalizeProviderStateControlValues(state.controlValues);
|
|
10063
11365
|
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
10064
11366
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
11367
|
+
const workspace = state.workspace || null;
|
|
11368
|
+
const git = getGitSummaryForWorkspace(workspace, options);
|
|
10065
11369
|
const title = activeChat?.title || state.name;
|
|
10066
11370
|
return {
|
|
10067
11371
|
id: state.instanceId || state.type,
|
|
@@ -10074,7 +11378,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
|
|
|
10074
11378
|
activeModal: activeChat?.activeModal || null
|
|
10075
11379
|
}),
|
|
10076
11380
|
title,
|
|
10077
|
-
workspace
|
|
11381
|
+
workspace,
|
|
11382
|
+
...git && { git },
|
|
10078
11383
|
activeChat,
|
|
10079
11384
|
...summaryMetadata && { summaryMetadata },
|
|
10080
11385
|
...includeSessionMetadata && { capabilities: state.sessionCapabilities || IDE_SESSION_CAPABILITIES },
|
|
@@ -10095,6 +11400,8 @@ function buildExtensionAgentSession(parent, ext, options) {
|
|
|
10095
11400
|
const controlValues = normalizeProviderStateControlValues(ext.controlValues);
|
|
10096
11401
|
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
10097
11402
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
11403
|
+
const workspace = parent.workspace || null;
|
|
11404
|
+
const git = getGitSummaryForWorkspace(workspace, options);
|
|
10098
11405
|
return {
|
|
10099
11406
|
id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
|
|
10100
11407
|
parentId: parent.instanceId || parent.type,
|
|
@@ -10107,7 +11414,8 @@ function buildExtensionAgentSession(parent, ext, options) {
|
|
|
10107
11414
|
activeModal: activeChat?.activeModal || null
|
|
10108
11415
|
}),
|
|
10109
11416
|
title: activeChat?.title || ext.name,
|
|
10110
|
-
workspace
|
|
11417
|
+
workspace,
|
|
11418
|
+
...git && { git },
|
|
10111
11419
|
activeChat,
|
|
10112
11420
|
...summaryMetadata && { summaryMetadata },
|
|
10113
11421
|
...includeSessionMetadata && { capabilities: ext.sessionCapabilities || EXTENSION_SESSION_CAPABILITIES },
|
|
@@ -10143,6 +11451,8 @@ function buildCliSession(state, options) {
|
|
|
10143
11451
|
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
10144
11452
|
const includeRuntimeMetadata = shouldIncludeRuntimeMetadata(profile);
|
|
10145
11453
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
11454
|
+
const workspace = state.workspace || null;
|
|
11455
|
+
const git = getGitSummaryForWorkspace(workspace, options);
|
|
10146
11456
|
return {
|
|
10147
11457
|
id: state.instanceId,
|
|
10148
11458
|
parentId: null,
|
|
@@ -10155,7 +11465,8 @@ function buildCliSession(state, options) {
|
|
|
10155
11465
|
activeModal: activeChat?.activeModal || null
|
|
10156
11466
|
}),
|
|
10157
11467
|
title: activeChat?.title || state.name,
|
|
10158
|
-
workspace
|
|
11468
|
+
workspace,
|
|
11469
|
+
...git && { git },
|
|
10159
11470
|
...includeRuntimeMetadata && {
|
|
10160
11471
|
runtimeKey: state.runtime?.runtimeKey,
|
|
10161
11472
|
runtimeDisplayName: state.runtime?.displayName,
|
|
@@ -10190,6 +11501,8 @@ function buildAcpSession(state, options) {
|
|
|
10190
11501
|
const controlValues = normalizeProviderStateControlValues(state.controlValues);
|
|
10191
11502
|
const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
|
|
10192
11503
|
const includeSessionControls = shouldIncludeSessionControls(profile);
|
|
11504
|
+
const workspace = state.workspace || null;
|
|
11505
|
+
const git = getGitSummaryForWorkspace(workspace, options);
|
|
10193
11506
|
return {
|
|
10194
11507
|
id: state.instanceId,
|
|
10195
11508
|
parentId: null,
|
|
@@ -10201,7 +11514,8 @@ function buildAcpSession(state, options) {
|
|
|
10201
11514
|
activeModal: activeChat?.activeModal || null
|
|
10202
11515
|
}),
|
|
10203
11516
|
title: activeChat?.title || state.name,
|
|
10204
|
-
workspace
|
|
11517
|
+
workspace,
|
|
11518
|
+
...git && { git },
|
|
10205
11519
|
activeChat,
|
|
10206
11520
|
...summaryMetadata && { summaryMetadata },
|
|
10207
11521
|
...includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES },
|
|
@@ -10321,7 +11635,7 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
|
|
|
10321
11635
|
init_contracts();
|
|
10322
11636
|
import * as fs4 from "fs";
|
|
10323
11637
|
import * as os6 from "os";
|
|
10324
|
-
import * as
|
|
11638
|
+
import * as path11 from "path";
|
|
10325
11639
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
10326
11640
|
|
|
10327
11641
|
// src/providers/provider-input-support.ts
|
|
@@ -11013,7 +12327,7 @@ function buildDebugBundleText(bundle) {
|
|
|
11013
12327
|
}
|
|
11014
12328
|
function getChatDebugBundleDir() {
|
|
11015
12329
|
const override = typeof process.env.ADHDEV_DEBUG_BUNDLE_DIR === "string" ? process.env.ADHDEV_DEBUG_BUNDLE_DIR.trim() : "";
|
|
11016
|
-
return override ||
|
|
12330
|
+
return override || path11.join(os6.homedir(), ".adhdev", "debug-bundles", "chat");
|
|
11017
12331
|
}
|
|
11018
12332
|
function safeBundleIdSegment(value, fallback) {
|
|
11019
12333
|
const normalized = String(value || fallback).trim().replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
@@ -11046,7 +12360,7 @@ function storeChatDebugBundleOnDaemon(bundle, targetSessionId) {
|
|
|
11046
12360
|
const bundleId = createChatDebugBundleId(targetSessionId);
|
|
11047
12361
|
const dir = getChatDebugBundleDir();
|
|
11048
12362
|
fs4.mkdirSync(dir, { recursive: true });
|
|
11049
|
-
const savedPath =
|
|
12363
|
+
const savedPath = path11.join(dir, `${bundleId}.json`);
|
|
11050
12364
|
const json = `${JSON.stringify(bundle, null, 2)}
|
|
11051
12365
|
`;
|
|
11052
12366
|
fs4.writeFileSync(savedPath, json, { encoding: "utf8", mode: 384 });
|
|
@@ -11240,7 +12554,7 @@ function getCliVisibleTranscriptCount(adapter) {
|
|
|
11240
12554
|
async function getStableExtensionBaseline(h) {
|
|
11241
12555
|
const first = await readExtensionChatState(h);
|
|
11242
12556
|
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
11243
|
-
await new Promise((
|
|
12557
|
+
await new Promise((resolve15) => setTimeout(resolve15, 150));
|
|
11244
12558
|
const second = await readExtensionChatState(h);
|
|
11245
12559
|
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
11246
12560
|
}
|
|
@@ -11248,7 +12562,7 @@ async function verifyExtensionSendObserved(h, before) {
|
|
|
11248
12562
|
const beforeCount = getStateMessageCount(before);
|
|
11249
12563
|
const beforeSignature = getStateLastSignature(before);
|
|
11250
12564
|
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
11251
|
-
await new Promise((
|
|
12565
|
+
await new Promise((resolve15) => setTimeout(resolve15, 250));
|
|
11252
12566
|
const state = await readExtensionChatState(h);
|
|
11253
12567
|
if (state?.status === "waiting_approval") return true;
|
|
11254
12568
|
const afterCount = getStateMessageCount(state);
|
|
@@ -12184,7 +13498,7 @@ async function handleResolveAction(h, args) {
|
|
|
12184
13498
|
|
|
12185
13499
|
// src/commands/cdp-commands.ts
|
|
12186
13500
|
import * as fs5 from "fs";
|
|
12187
|
-
import * as
|
|
13501
|
+
import * as path12 from "path";
|
|
12188
13502
|
import * as os7 from "os";
|
|
12189
13503
|
var KEY_TO_VK = {
|
|
12190
13504
|
Backspace: 8,
|
|
@@ -12441,25 +13755,25 @@ function resolveSafePath(requestedPath) {
|
|
|
12441
13755
|
const inputPath = rawPath || ".";
|
|
12442
13756
|
const home = os7.homedir();
|
|
12443
13757
|
if (inputPath.startsWith("~")) {
|
|
12444
|
-
return
|
|
13758
|
+
return path12.resolve(path12.join(home, inputPath.slice(1)));
|
|
12445
13759
|
}
|
|
12446
13760
|
if (process.platform === "win32") {
|
|
12447
13761
|
const normalized = normalizeWindowsRequestedPath(inputPath);
|
|
12448
|
-
if (
|
|
12449
|
-
return
|
|
13762
|
+
if (path12.win32.isAbsolute(normalized)) {
|
|
13763
|
+
return path12.win32.normalize(normalized);
|
|
12450
13764
|
}
|
|
12451
|
-
return
|
|
13765
|
+
return path12.win32.resolve(normalized);
|
|
12452
13766
|
}
|
|
12453
|
-
if (
|
|
12454
|
-
return
|
|
13767
|
+
if (path12.isAbsolute(inputPath)) {
|
|
13768
|
+
return path12.normalize(inputPath);
|
|
12455
13769
|
}
|
|
12456
|
-
return
|
|
13770
|
+
return path12.resolve(inputPath);
|
|
12457
13771
|
}
|
|
12458
13772
|
function listDirectoryEntriesSafe(dirPath) {
|
|
12459
13773
|
const entries = fs5.readdirSync(dirPath, { withFileTypes: true });
|
|
12460
13774
|
const files = [];
|
|
12461
13775
|
for (const entry of entries) {
|
|
12462
|
-
const entryPath =
|
|
13776
|
+
const entryPath = path12.join(dirPath, entry.name);
|
|
12463
13777
|
try {
|
|
12464
13778
|
if (entry.isDirectory()) {
|
|
12465
13779
|
files.push({ name: entry.name, type: "directory" });
|
|
@@ -12475,11 +13789,11 @@ function listDirectoryEntriesSafe(dirPath) {
|
|
|
12475
13789
|
files.push({ name: entry.name, type: "file", size });
|
|
12476
13790
|
continue;
|
|
12477
13791
|
}
|
|
12478
|
-
const
|
|
13792
|
+
const stat2 = fs5.statSync(entryPath);
|
|
12479
13793
|
files.push({
|
|
12480
13794
|
name: entry.name,
|
|
12481
|
-
type:
|
|
12482
|
-
size:
|
|
13795
|
+
type: stat2.isDirectory() ? "directory" : "file",
|
|
13796
|
+
size: stat2.isFile() ? stat2.size : void 0
|
|
12483
13797
|
});
|
|
12484
13798
|
} catch {
|
|
12485
13799
|
}
|
|
@@ -12513,7 +13827,7 @@ async function handleFileRead(h, args) {
|
|
|
12513
13827
|
async function handleFileWrite(h, args) {
|
|
12514
13828
|
try {
|
|
12515
13829
|
const filePath = resolveSafePath(args?.path);
|
|
12516
|
-
fs5.mkdirSync(
|
|
13830
|
+
fs5.mkdirSync(path12.dirname(filePath), { recursive: true });
|
|
12517
13831
|
fs5.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
12518
13832
|
return { success: true, path: filePath };
|
|
12519
13833
|
} catch (e) {
|
|
@@ -12862,7 +14176,7 @@ async function executeProviderScript(h, args, scriptName) {
|
|
|
12862
14176
|
const enterCount = cliCommand.enterCount || 1;
|
|
12863
14177
|
await adapter.writeRaw(cliCommand.text + "\r");
|
|
12864
14178
|
for (let i = 1; i < enterCount; i += 1) {
|
|
12865
|
-
await new Promise((
|
|
14179
|
+
await new Promise((resolve15) => setTimeout(resolve15, 50));
|
|
12866
14180
|
await adapter.writeRaw("\r");
|
|
12867
14181
|
}
|
|
12868
14182
|
}
|
|
@@ -13356,6 +14670,12 @@ var DaemonCommandHandler = class {
|
|
|
13356
14670
|
this._currentRoute = this.resolveRoute(args);
|
|
13357
14671
|
const startedAt = Date.now();
|
|
13358
14672
|
this.logCommandStart(cmd, args);
|
|
14673
|
+
let result;
|
|
14674
|
+
if (isGitCommandName(cmd)) {
|
|
14675
|
+
result = await handleGitCommand(cmd, args, this._ctx.gitCommandServices);
|
|
14676
|
+
this.logCommandEnd(cmd, result, startedAt);
|
|
14677
|
+
return result;
|
|
14678
|
+
}
|
|
13359
14679
|
const sessionScopedCommands = /* @__PURE__ */ new Set([
|
|
13360
14680
|
"read_chat",
|
|
13361
14681
|
"get_chat_debug_bundle",
|
|
@@ -13381,7 +14701,6 @@ var DaemonCommandHandler = class {
|
|
|
13381
14701
|
this.logCommandEnd(cmd, result2, startedAt);
|
|
13382
14702
|
return result2;
|
|
13383
14703
|
}
|
|
13384
|
-
let result;
|
|
13385
14704
|
if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
|
|
13386
14705
|
const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
|
|
13387
14706
|
if (cdpCommands.includes(cmd)) {
|
|
@@ -13390,6 +14709,16 @@ var DaemonCommandHandler = class {
|
|
|
13390
14709
|
return result;
|
|
13391
14710
|
}
|
|
13392
14711
|
}
|
|
14712
|
+
if (cmd === "send_chat" && this._ctx.onBeforeSendChat) {
|
|
14713
|
+
const sessionId = this._currentRoute.session?.sessionId;
|
|
14714
|
+
const workspace = sessionId ? this._ctx.instanceManager?.getInstance(sessionId)?.getState?.()?.workspace : void 0;
|
|
14715
|
+
if (workspace && sessionId) {
|
|
14716
|
+
try {
|
|
14717
|
+
this._ctx.onBeforeSendChat({ workspace, sessionId });
|
|
14718
|
+
} catch {
|
|
14719
|
+
}
|
|
14720
|
+
}
|
|
14721
|
+
}
|
|
13393
14722
|
try {
|
|
13394
14723
|
result = await this.dispatch(cmd, args);
|
|
13395
14724
|
this.logCommandEnd(cmd, result, startedAt);
|
|
@@ -13532,7 +14861,7 @@ var DaemonCommandHandler = class {
|
|
|
13532
14861
|
try {
|
|
13533
14862
|
const http3 = await import("http");
|
|
13534
14863
|
const postData = JSON.stringify(body);
|
|
13535
|
-
const result = await new Promise((
|
|
14864
|
+
const result = await new Promise((resolve15, reject) => {
|
|
13536
14865
|
const req = http3.request({
|
|
13537
14866
|
hostname: "127.0.0.1",
|
|
13538
14867
|
port: 19280,
|
|
@@ -13544,9 +14873,9 @@ var DaemonCommandHandler = class {
|
|
|
13544
14873
|
res.on("data", (chunk) => data += chunk);
|
|
13545
14874
|
res.on("end", () => {
|
|
13546
14875
|
try {
|
|
13547
|
-
|
|
14876
|
+
resolve15(JSON.parse(data));
|
|
13548
14877
|
} catch {
|
|
13549
|
-
|
|
14878
|
+
resolve15({ raw: data });
|
|
13550
14879
|
}
|
|
13551
14880
|
});
|
|
13552
14881
|
});
|
|
@@ -13564,15 +14893,15 @@ var DaemonCommandHandler = class {
|
|
|
13564
14893
|
if (!providerType) return { success: false, error: "providerType required" };
|
|
13565
14894
|
try {
|
|
13566
14895
|
const http3 = await import("http");
|
|
13567
|
-
const result = await new Promise((
|
|
14896
|
+
const result = await new Promise((resolve15, reject) => {
|
|
13568
14897
|
http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
|
|
13569
14898
|
let data = "";
|
|
13570
14899
|
res.on("data", (chunk) => data += chunk);
|
|
13571
14900
|
res.on("end", () => {
|
|
13572
14901
|
try {
|
|
13573
|
-
|
|
14902
|
+
resolve15(JSON.parse(data));
|
|
13574
14903
|
} catch {
|
|
13575
|
-
|
|
14904
|
+
resolve15({ raw: data });
|
|
13576
14905
|
}
|
|
13577
14906
|
});
|
|
13578
14907
|
}).on("error", reject);
|
|
@@ -13586,7 +14915,7 @@ var DaemonCommandHandler = class {
|
|
|
13586
14915
|
try {
|
|
13587
14916
|
const http3 = await import("http");
|
|
13588
14917
|
const postData = JSON.stringify(args || {});
|
|
13589
|
-
const result = await new Promise((
|
|
14918
|
+
const result = await new Promise((resolve15, reject) => {
|
|
13590
14919
|
const req = http3.request({
|
|
13591
14920
|
hostname: "127.0.0.1",
|
|
13592
14921
|
port: 19280,
|
|
@@ -13598,9 +14927,9 @@ var DaemonCommandHandler = class {
|
|
|
13598
14927
|
res.on("data", (chunk) => data += chunk);
|
|
13599
14928
|
res.on("end", () => {
|
|
13600
14929
|
try {
|
|
13601
|
-
|
|
14930
|
+
resolve15(JSON.parse(data));
|
|
13602
14931
|
} catch {
|
|
13603
|
-
|
|
14932
|
+
resolve15({ raw: data });
|
|
13604
14933
|
}
|
|
13605
14934
|
});
|
|
13606
14935
|
});
|
|
@@ -13618,7 +14947,7 @@ var DaemonCommandHandler = class {
|
|
|
13618
14947
|
// src/commands/cli-manager.ts
|
|
13619
14948
|
init_provider_cli_adapter();
|
|
13620
14949
|
import * as os13 from "os";
|
|
13621
|
-
import * as
|
|
14950
|
+
import * as path16 from "path";
|
|
13622
14951
|
import * as crypto4 from "crypto";
|
|
13623
14952
|
import { existsSync as existsSync10 } from "fs";
|
|
13624
14953
|
import { execFileSync } from "child_process";
|
|
@@ -13628,7 +14957,7 @@ init_config();
|
|
|
13628
14957
|
// src/providers/cli-provider-instance.ts
|
|
13629
14958
|
init_contracts();
|
|
13630
14959
|
import * as os12 from "os";
|
|
13631
|
-
import * as
|
|
14960
|
+
import * as path15 from "path";
|
|
13632
14961
|
import * as crypto3 from "crypto";
|
|
13633
14962
|
import * as fs6 from "fs";
|
|
13634
14963
|
import { createRequire } from "module";
|
|
@@ -13688,7 +15017,7 @@ function buildIncrementalHistoryAppendMessages(previousMessages, currentMessages
|
|
|
13688
15017
|
var CachedDatabaseSync = null;
|
|
13689
15018
|
function getDatabaseSync() {
|
|
13690
15019
|
if (CachedDatabaseSync) return CachedDatabaseSync;
|
|
13691
|
-
const requireFn = typeof __require === "function" ? __require : createRequire(
|
|
15020
|
+
const requireFn = typeof __require === "function" ? __require : createRequire(path15.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
|
|
13692
15021
|
const sqliteModule = requireFn(`node:${"sqlite"}`);
|
|
13693
15022
|
CachedDatabaseSync = sqliteModule.DatabaseSync;
|
|
13694
15023
|
if (!CachedDatabaseSync) {
|
|
@@ -13726,7 +15055,7 @@ async function waitForCliAdapterReady(adapter, options) {
|
|
|
13726
15055
|
if (status === "stopped") {
|
|
13727
15056
|
throw new Error("CLI runtime stopped before it became ready");
|
|
13728
15057
|
}
|
|
13729
|
-
await new Promise((
|
|
15058
|
+
await new Promise((resolve15) => setTimeout(resolve15, pollMs));
|
|
13730
15059
|
}
|
|
13731
15060
|
throw new Error(`CLI runtime did not become ready within ${timeoutMs}ms`);
|
|
13732
15061
|
}
|
|
@@ -14077,7 +15406,7 @@ var CliProviderInstance = class {
|
|
|
14077
15406
|
const enterCount = cliCommand.enterCount || 1;
|
|
14078
15407
|
await this.adapter.writeRaw(cliCommand.text + "\r");
|
|
14079
15408
|
for (let i = 1; i < enterCount; i += 1) {
|
|
14080
|
-
await new Promise((
|
|
15409
|
+
await new Promise((resolve15) => setTimeout(resolve15, 50));
|
|
14081
15410
|
await this.adapter.writeRaw("\r");
|
|
14082
15411
|
}
|
|
14083
15412
|
}
|
|
@@ -15196,13 +16525,13 @@ var AcpProviderInstance = class {
|
|
|
15196
16525
|
}
|
|
15197
16526
|
this.currentStatus = "waiting_approval";
|
|
15198
16527
|
this.detectStatusTransition();
|
|
15199
|
-
const approved = await new Promise((
|
|
15200
|
-
this.permissionResolvers.push(
|
|
16528
|
+
const approved = await new Promise((resolve15) => {
|
|
16529
|
+
this.permissionResolvers.push(resolve15);
|
|
15201
16530
|
setTimeout(() => {
|
|
15202
|
-
const idx = this.permissionResolvers.indexOf(
|
|
16531
|
+
const idx = this.permissionResolvers.indexOf(resolve15);
|
|
15203
16532
|
if (idx >= 0) {
|
|
15204
16533
|
this.permissionResolvers.splice(idx, 1);
|
|
15205
|
-
|
|
16534
|
+
resolve15(false);
|
|
15206
16535
|
}
|
|
15207
16536
|
}, 3e5);
|
|
15208
16537
|
});
|
|
@@ -15777,11 +17106,11 @@ function shouldRestoreHostedRuntime(record, managerTag) {
|
|
|
15777
17106
|
// src/commands/cli-manager.ts
|
|
15778
17107
|
function isExplicitCommand(command) {
|
|
15779
17108
|
const trimmed = command.trim();
|
|
15780
|
-
return
|
|
17109
|
+
return path16.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
15781
17110
|
}
|
|
15782
17111
|
function expandExecutable(command) {
|
|
15783
17112
|
const trimmed = command.trim();
|
|
15784
|
-
return trimmed.startsWith("~") ?
|
|
17113
|
+
return trimmed.startsWith("~") ? path16.join(os13.homedir(), trimmed.slice(1)) : trimmed;
|
|
15785
17114
|
}
|
|
15786
17115
|
function commandExists(command) {
|
|
15787
17116
|
const trimmed = command.trim();
|
|
@@ -16062,7 +17391,7 @@ var DaemonCliManager = class {
|
|
|
16062
17391
|
async startSession(cliType, workingDir, cliArgs, initialModel, options) {
|
|
16063
17392
|
const trimmed = (workingDir || "").trim();
|
|
16064
17393
|
if (!trimmed) throw new Error("working directory required");
|
|
16065
|
-
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os13.homedir()) :
|
|
17394
|
+
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os13.homedir()) : path16.resolve(trimmed);
|
|
16066
17395
|
const normalizedType = this.providerLoader.resolveAlias(cliType);
|
|
16067
17396
|
const rawProvider = this.providerLoader.getByAlias(cliType);
|
|
16068
17397
|
const provider = rawProvider ? this.providerLoader.resolve(normalizedType) || rawProvider : void 0;
|
|
@@ -16563,11 +17892,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
16563
17892
|
import { execSync as execSync4, spawn as spawn2 } from "child_process";
|
|
16564
17893
|
import * as net from "net";
|
|
16565
17894
|
import * as os15 from "os";
|
|
16566
|
-
import * as
|
|
17895
|
+
import * as path18 from "path";
|
|
16567
17896
|
|
|
16568
17897
|
// src/providers/provider-loader.ts
|
|
16569
17898
|
import * as fs7 from "fs";
|
|
16570
|
-
import * as
|
|
17899
|
+
import * as path17 from "path";
|
|
16571
17900
|
import * as os14 from "os";
|
|
16572
17901
|
import * as chokidar from "chokidar";
|
|
16573
17902
|
init_logger();
|
|
@@ -16630,6 +17959,7 @@ var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
|
|
|
16630
17959
|
"sendDelayMs",
|
|
16631
17960
|
"sendKey",
|
|
16632
17961
|
"submitStrategy",
|
|
17962
|
+
"requirePromptEchoBeforeSubmit",
|
|
16633
17963
|
"timeouts",
|
|
16634
17964
|
"disableUpstream"
|
|
16635
17965
|
]);
|
|
@@ -16832,7 +18162,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16832
18162
|
try {
|
|
16833
18163
|
if (!fs7.existsSync(candidate) || !fs7.statSync(candidate).isDirectory()) return false;
|
|
16834
18164
|
return ["ide", "extension", "cli", "acp"].some(
|
|
16835
|
-
(category) => fs7.existsSync(
|
|
18165
|
+
(category) => fs7.existsSync(path17.join(candidate, category))
|
|
16836
18166
|
);
|
|
16837
18167
|
} catch {
|
|
16838
18168
|
return false;
|
|
@@ -16840,20 +18170,20 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16840
18170
|
}
|
|
16841
18171
|
static hasProviderRootMarker(candidate) {
|
|
16842
18172
|
try {
|
|
16843
|
-
return fs7.existsSync(
|
|
18173
|
+
return fs7.existsSync(path17.join(candidate, _ProviderLoader.SIBLING_MARKER_FILE));
|
|
16844
18174
|
} catch {
|
|
16845
18175
|
return false;
|
|
16846
18176
|
}
|
|
16847
18177
|
}
|
|
16848
18178
|
detectDefaultUserDir() {
|
|
16849
|
-
const fallback =
|
|
18179
|
+
const fallback = path17.join(os14.homedir(), ".adhdev", "providers");
|
|
16850
18180
|
const envOptIn = process.env[_ProviderLoader.SIBLING_ENV_VAR] === "1";
|
|
16851
18181
|
const visited = /* @__PURE__ */ new Set();
|
|
16852
18182
|
for (const start of this.probeStarts) {
|
|
16853
|
-
let current =
|
|
18183
|
+
let current = path17.resolve(start);
|
|
16854
18184
|
while (!visited.has(current)) {
|
|
16855
18185
|
visited.add(current);
|
|
16856
|
-
const siblingCandidate =
|
|
18186
|
+
const siblingCandidate = path17.join(path17.dirname(current), _ProviderLoader.REPO_PROVIDER_DIRNAME);
|
|
16857
18187
|
if (_ProviderLoader.looksLikeProviderRoot(siblingCandidate)) {
|
|
16858
18188
|
const hasMarker = _ProviderLoader.hasProviderRootMarker(siblingCandidate);
|
|
16859
18189
|
if (envOptIn || hasMarker) {
|
|
@@ -16875,7 +18205,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16875
18205
|
return { path: siblingCandidate, source };
|
|
16876
18206
|
}
|
|
16877
18207
|
}
|
|
16878
|
-
const parent =
|
|
18208
|
+
const parent = path17.dirname(current);
|
|
16879
18209
|
if (parent === current) break;
|
|
16880
18210
|
current = parent;
|
|
16881
18211
|
}
|
|
@@ -16885,11 +18215,11 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16885
18215
|
constructor(options) {
|
|
16886
18216
|
this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
|
|
16887
18217
|
this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
|
|
16888
|
-
this.defaultProvidersDir =
|
|
18218
|
+
this.defaultProvidersDir = path17.join(os14.homedir(), ".adhdev", "providers");
|
|
16889
18219
|
const detected = this.detectDefaultUserDir();
|
|
16890
18220
|
this.userDir = detected.path;
|
|
16891
18221
|
this.userDirSource = detected.source;
|
|
16892
|
-
this.upstreamDir =
|
|
18222
|
+
this.upstreamDir = path17.join(this.defaultProvidersDir, ".upstream");
|
|
16893
18223
|
this.disableUpstream = false;
|
|
16894
18224
|
this.applySourceConfig({
|
|
16895
18225
|
userDir: options?.userDir,
|
|
@@ -16948,7 +18278,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16948
18278
|
this.userDir = detected.path;
|
|
16949
18279
|
this.userDirSource = detected.source;
|
|
16950
18280
|
}
|
|
16951
|
-
this.upstreamDir =
|
|
18281
|
+
this.upstreamDir = path17.join(this.defaultProvidersDir, ".upstream");
|
|
16952
18282
|
this.disableUpstream = this.sourceMode === "no-upstream";
|
|
16953
18283
|
if (this.explicitProviderDir) {
|
|
16954
18284
|
this.log(`Config 'providerDir' applied: ${this.userDir}`);
|
|
@@ -16962,7 +18292,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16962
18292
|
* Canonical provider directory shape for a given root.
|
|
16963
18293
|
*/
|
|
16964
18294
|
getProviderDir(root, category, type) {
|
|
16965
|
-
return
|
|
18295
|
+
return path17.join(root, category, type);
|
|
16966
18296
|
}
|
|
16967
18297
|
/**
|
|
16968
18298
|
* Canonical user override directory for a provider.
|
|
@@ -16989,7 +18319,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
16989
18319
|
resolveProviderFile(type, ...segments) {
|
|
16990
18320
|
const dir = this.findProviderDirInternal(type);
|
|
16991
18321
|
if (!dir) return null;
|
|
16992
|
-
return
|
|
18322
|
+
return path17.join(dir, ...segments);
|
|
16993
18323
|
}
|
|
16994
18324
|
/**
|
|
16995
18325
|
* Load all providers (3-tier priority)
|
|
@@ -17028,7 +18358,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17028
18358
|
if (!fs7.existsSync(this.upstreamDir)) return false;
|
|
17029
18359
|
try {
|
|
17030
18360
|
return fs7.readdirSync(this.upstreamDir).some(
|
|
17031
|
-
(d) => fs7.statSync(
|
|
18361
|
+
(d) => fs7.statSync(path17.join(this.upstreamDir, d)).isDirectory()
|
|
17032
18362
|
);
|
|
17033
18363
|
} catch {
|
|
17034
18364
|
return false;
|
|
@@ -17525,8 +18855,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17525
18855
|
resolved._resolvedScriptDir = entry.scriptDir;
|
|
17526
18856
|
resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
|
|
17527
18857
|
if (providerDir) {
|
|
17528
|
-
const fullDir =
|
|
17529
|
-
resolved._resolvedScriptsPath = fs7.existsSync(
|
|
18858
|
+
const fullDir = path17.join(providerDir, entry.scriptDir);
|
|
18859
|
+
resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
|
|
17530
18860
|
}
|
|
17531
18861
|
matched = true;
|
|
17532
18862
|
}
|
|
@@ -17541,8 +18871,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17541
18871
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
17542
18872
|
resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
|
|
17543
18873
|
if (providerDir) {
|
|
17544
|
-
const fullDir =
|
|
17545
|
-
resolved._resolvedScriptsPath = fs7.existsSync(
|
|
18874
|
+
const fullDir = path17.join(providerDir, base.defaultScriptDir);
|
|
18875
|
+
resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
|
|
17546
18876
|
}
|
|
17547
18877
|
}
|
|
17548
18878
|
resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
|
|
@@ -17559,8 +18889,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17559
18889
|
resolved._resolvedScriptDir = dirOverride;
|
|
17560
18890
|
resolved._resolvedScriptsSource = `versions:${range}`;
|
|
17561
18891
|
if (providerDir) {
|
|
17562
|
-
const fullDir =
|
|
17563
|
-
resolved._resolvedScriptsPath = fs7.existsSync(
|
|
18892
|
+
const fullDir = path17.join(providerDir, dirOverride);
|
|
18893
|
+
resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
|
|
17564
18894
|
}
|
|
17565
18895
|
}
|
|
17566
18896
|
} else if (override.scripts) {
|
|
@@ -17576,8 +18906,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17576
18906
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
17577
18907
|
resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
|
|
17578
18908
|
if (providerDir) {
|
|
17579
|
-
const fullDir =
|
|
17580
|
-
resolved._resolvedScriptsPath = fs7.existsSync(
|
|
18909
|
+
const fullDir = path17.join(providerDir, base.defaultScriptDir);
|
|
18910
|
+
resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
|
|
17581
18911
|
}
|
|
17582
18912
|
}
|
|
17583
18913
|
}
|
|
@@ -17609,14 +18939,14 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17609
18939
|
this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
|
|
17610
18940
|
return null;
|
|
17611
18941
|
}
|
|
17612
|
-
const dir =
|
|
18942
|
+
const dir = path17.join(providerDir, scriptDir);
|
|
17613
18943
|
if (!fs7.existsSync(dir)) {
|
|
17614
18944
|
this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
|
|
17615
18945
|
return null;
|
|
17616
18946
|
}
|
|
17617
18947
|
const cached = this.scriptsCache.get(dir);
|
|
17618
18948
|
if (cached) return cached;
|
|
17619
|
-
const scriptsJs =
|
|
18949
|
+
const scriptsJs = path17.join(dir, "scripts.js");
|
|
17620
18950
|
if (fs7.existsSync(scriptsJs)) {
|
|
17621
18951
|
try {
|
|
17622
18952
|
delete __require.cache[__require.resolve(scriptsJs)];
|
|
@@ -17658,7 +18988,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17658
18988
|
return;
|
|
17659
18989
|
}
|
|
17660
18990
|
if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
|
|
17661
|
-
this.log(`File changed: ${
|
|
18991
|
+
this.log(`File changed: ${path17.basename(filePath)}, reloading...`);
|
|
17662
18992
|
this.reload();
|
|
17663
18993
|
}
|
|
17664
18994
|
};
|
|
@@ -17713,7 +19043,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17713
19043
|
}
|
|
17714
19044
|
const https = __require("https");
|
|
17715
19045
|
const { execSync: execSync7 } = __require("child_process");
|
|
17716
|
-
const metaPath =
|
|
19046
|
+
const metaPath = path17.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
17717
19047
|
let prevEtag = "";
|
|
17718
19048
|
let prevTimestamp = 0;
|
|
17719
19049
|
try {
|
|
@@ -17730,7 +19060,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17730
19060
|
return { updated: false };
|
|
17731
19061
|
}
|
|
17732
19062
|
try {
|
|
17733
|
-
const etag = await new Promise((
|
|
19063
|
+
const etag = await new Promise((resolve15, reject) => {
|
|
17734
19064
|
const options = {
|
|
17735
19065
|
method: "HEAD",
|
|
17736
19066
|
hostname: "github.com",
|
|
@@ -17748,7 +19078,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17748
19078
|
headers: { "User-Agent": "adhdev-launcher" },
|
|
17749
19079
|
timeout: 1e4
|
|
17750
19080
|
}, (res2) => {
|
|
17751
|
-
|
|
19081
|
+
resolve15(res2.headers.etag || res2.headers["last-modified"] || "");
|
|
17752
19082
|
});
|
|
17753
19083
|
req2.on("error", reject);
|
|
17754
19084
|
req2.on("timeout", () => {
|
|
@@ -17757,7 +19087,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17757
19087
|
});
|
|
17758
19088
|
req2.end();
|
|
17759
19089
|
} else {
|
|
17760
|
-
|
|
19090
|
+
resolve15(res.headers.etag || res.headers["last-modified"] || "");
|
|
17761
19091
|
}
|
|
17762
19092
|
});
|
|
17763
19093
|
req.on("error", reject);
|
|
@@ -17773,17 +19103,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17773
19103
|
return { updated: false };
|
|
17774
19104
|
}
|
|
17775
19105
|
this.log("Downloading latest providers from GitHub...");
|
|
17776
|
-
const tmpTar =
|
|
17777
|
-
const tmpExtract =
|
|
19106
|
+
const tmpTar = path17.join(os14.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
|
|
19107
|
+
const tmpExtract = path17.join(os14.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
|
|
17778
19108
|
await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
|
|
17779
19109
|
fs7.mkdirSync(tmpExtract, { recursive: true });
|
|
17780
19110
|
execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
|
|
17781
19111
|
const extracted = fs7.readdirSync(tmpExtract);
|
|
17782
19112
|
const rootDir = extracted.find(
|
|
17783
|
-
(d) => fs7.statSync(
|
|
19113
|
+
(d) => fs7.statSync(path17.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
|
|
17784
19114
|
);
|
|
17785
19115
|
if (!rootDir) throw new Error("Unexpected tarball structure");
|
|
17786
|
-
const sourceDir =
|
|
19116
|
+
const sourceDir = path17.join(tmpExtract, rootDir);
|
|
17787
19117
|
const backupDir = this.upstreamDir + ".bak";
|
|
17788
19118
|
if (fs7.existsSync(this.upstreamDir)) {
|
|
17789
19119
|
if (fs7.existsSync(backupDir)) fs7.rmSync(backupDir, { recursive: true, force: true });
|
|
@@ -17821,7 +19151,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17821
19151
|
downloadFile(url, destPath) {
|
|
17822
19152
|
const https = __require("https");
|
|
17823
19153
|
const http3 = __require("http");
|
|
17824
|
-
return new Promise((
|
|
19154
|
+
return new Promise((resolve15, reject) => {
|
|
17825
19155
|
const doRequest = (reqUrl, redirectCount = 0) => {
|
|
17826
19156
|
if (redirectCount > 5) {
|
|
17827
19157
|
reject(new Error("Too many redirects"));
|
|
@@ -17841,7 +19171,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17841
19171
|
res.pipe(ws);
|
|
17842
19172
|
ws.on("finish", () => {
|
|
17843
19173
|
ws.close();
|
|
17844
|
-
|
|
19174
|
+
resolve15();
|
|
17845
19175
|
});
|
|
17846
19176
|
ws.on("error", reject);
|
|
17847
19177
|
});
|
|
@@ -17858,8 +19188,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17858
19188
|
copyDirRecursive(src, dest) {
|
|
17859
19189
|
fs7.mkdirSync(dest, { recursive: true });
|
|
17860
19190
|
for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
|
|
17861
|
-
const srcPath =
|
|
17862
|
-
const destPath =
|
|
19191
|
+
const srcPath = path17.join(src, entry.name);
|
|
19192
|
+
const destPath = path17.join(dest, entry.name);
|
|
17863
19193
|
if (entry.isDirectory()) {
|
|
17864
19194
|
this.copyDirRecursive(srcPath, destPath);
|
|
17865
19195
|
} else {
|
|
@@ -17870,7 +19200,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17870
19200
|
/** .meta.json save */
|
|
17871
19201
|
writeMeta(metaPath, etag, timestamp) {
|
|
17872
19202
|
try {
|
|
17873
|
-
fs7.mkdirSync(
|
|
19203
|
+
fs7.mkdirSync(path17.dirname(metaPath), { recursive: true });
|
|
17874
19204
|
fs7.writeFileSync(metaPath, JSON.stringify({
|
|
17875
19205
|
etag,
|
|
17876
19206
|
timestamp,
|
|
@@ -17887,7 +19217,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
17887
19217
|
const scan = (d) => {
|
|
17888
19218
|
try {
|
|
17889
19219
|
for (const entry of fs7.readdirSync(d, { withFileTypes: true })) {
|
|
17890
|
-
if (entry.isDirectory()) scan(
|
|
19220
|
+
if (entry.isDirectory()) scan(path17.join(d, entry.name));
|
|
17891
19221
|
else if (entry.name === "provider.json") count++;
|
|
17892
19222
|
}
|
|
17893
19223
|
} catch {
|
|
@@ -18115,17 +19445,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18115
19445
|
for (const root of searchRoots) {
|
|
18116
19446
|
if (!fs7.existsSync(root)) continue;
|
|
18117
19447
|
const candidate = this.getProviderDir(root, cat, type);
|
|
18118
|
-
if (fs7.existsSync(
|
|
18119
|
-
const catDir =
|
|
19448
|
+
if (fs7.existsSync(path17.join(candidate, "provider.json"))) return candidate;
|
|
19449
|
+
const catDir = path17.join(root, cat);
|
|
18120
19450
|
if (fs7.existsSync(catDir)) {
|
|
18121
19451
|
try {
|
|
18122
19452
|
for (const entry of fs7.readdirSync(catDir, { withFileTypes: true })) {
|
|
18123
19453
|
if (!entry.isDirectory()) continue;
|
|
18124
|
-
const jsonPath =
|
|
19454
|
+
const jsonPath = path17.join(catDir, entry.name, "provider.json");
|
|
18125
19455
|
if (fs7.existsSync(jsonPath)) {
|
|
18126
19456
|
try {
|
|
18127
19457
|
const data = JSON.parse(fs7.readFileSync(jsonPath, "utf-8"));
|
|
18128
|
-
if (data.type === type) return
|
|
19458
|
+
if (data.type === type) return path17.join(catDir, entry.name);
|
|
18129
19459
|
} catch {
|
|
18130
19460
|
}
|
|
18131
19461
|
}
|
|
@@ -18142,7 +19472,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18142
19472
|
* (template substitution is NOT applied here — scripts.js handles that)
|
|
18143
19473
|
*/
|
|
18144
19474
|
buildScriptWrappersFromDir(dir) {
|
|
18145
|
-
const scriptsJs =
|
|
19475
|
+
const scriptsJs = path17.join(dir, "scripts.js");
|
|
18146
19476
|
if (fs7.existsSync(scriptsJs)) {
|
|
18147
19477
|
try {
|
|
18148
19478
|
delete __require.cache[__require.resolve(scriptsJs)];
|
|
@@ -18156,7 +19486,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18156
19486
|
for (const file of fs7.readdirSync(dir)) {
|
|
18157
19487
|
if (!file.endsWith(".js")) continue;
|
|
18158
19488
|
const scriptName = toCamel(file.replace(".js", ""));
|
|
18159
|
-
const filePath =
|
|
19489
|
+
const filePath = path17.join(dir, file);
|
|
18160
19490
|
result[scriptName] = (...args) => {
|
|
18161
19491
|
try {
|
|
18162
19492
|
let content = fs7.readFileSync(filePath, "utf-8");
|
|
@@ -18216,7 +19546,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18216
19546
|
}
|
|
18217
19547
|
const hasJson = entries.some((e) => e.name === "provider.json");
|
|
18218
19548
|
if (hasJson) {
|
|
18219
|
-
const jsonPath =
|
|
19549
|
+
const jsonPath = path17.join(d, "provider.json");
|
|
18220
19550
|
try {
|
|
18221
19551
|
const raw = fs7.readFileSync(jsonPath, "utf-8");
|
|
18222
19552
|
const mod = JSON.parse(raw);
|
|
@@ -18237,7 +19567,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18237
19567
|
this.log(`\u26A0 Invalid provider at ${jsonPath}: ${validation.errors.join("; ")}`);
|
|
18238
19568
|
} else {
|
|
18239
19569
|
const hasCompatibility = Array.isArray(normalizedProvider.compatibility);
|
|
18240
|
-
const scriptsPath =
|
|
19570
|
+
const scriptsPath = path17.join(d, "scripts.js");
|
|
18241
19571
|
if (!hasCompatibility && fs7.existsSync(scriptsPath)) {
|
|
18242
19572
|
try {
|
|
18243
19573
|
delete __require.cache[__require.resolve(scriptsPath)];
|
|
@@ -18263,7 +19593,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18263
19593
|
if (!entry.isDirectory()) continue;
|
|
18264
19594
|
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
18265
19595
|
if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
|
|
18266
|
-
scan(
|
|
19596
|
+
scan(path17.join(d, entry.name));
|
|
18267
19597
|
}
|
|
18268
19598
|
}
|
|
18269
19599
|
};
|
|
@@ -18298,9 +19628,9 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
18298
19628
|
}
|
|
18299
19629
|
}
|
|
18300
19630
|
compareVersions(a, b) {
|
|
18301
|
-
const
|
|
18302
|
-
const pa =
|
|
18303
|
-
const pb =
|
|
19631
|
+
const normalize4 = (v) => v.split(/[-_+]/)[0].split(".").map((x) => parseInt(x, 10) || 0);
|
|
19632
|
+
const pa = normalize4(a);
|
|
19633
|
+
const pb = normalize4(b);
|
|
18304
19634
|
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
18305
19635
|
const va = pa[i] || 0;
|
|
18306
19636
|
const vb = pb[i] || 0;
|
|
@@ -18420,17 +19750,17 @@ async function findFreePort(ports) {
|
|
|
18420
19750
|
throw new Error("No free port found");
|
|
18421
19751
|
}
|
|
18422
19752
|
function checkPortFree(port) {
|
|
18423
|
-
return new Promise((
|
|
19753
|
+
return new Promise((resolve15) => {
|
|
18424
19754
|
const server = net.createServer();
|
|
18425
19755
|
server.unref();
|
|
18426
|
-
server.on("error", () =>
|
|
19756
|
+
server.on("error", () => resolve15(false));
|
|
18427
19757
|
server.listen(port, "127.0.0.1", () => {
|
|
18428
|
-
server.close(() =>
|
|
19758
|
+
server.close(() => resolve15(true));
|
|
18429
19759
|
});
|
|
18430
19760
|
});
|
|
18431
19761
|
}
|
|
18432
19762
|
async function isCdpActive(port) {
|
|
18433
|
-
return new Promise((
|
|
19763
|
+
return new Promise((resolve15) => {
|
|
18434
19764
|
const req = __require("http").get(`http://127.0.0.1:${port}/json/version`, {
|
|
18435
19765
|
timeout: 2e3
|
|
18436
19766
|
}, (res) => {
|
|
@@ -18439,16 +19769,16 @@ async function isCdpActive(port) {
|
|
|
18439
19769
|
res.on("end", () => {
|
|
18440
19770
|
try {
|
|
18441
19771
|
const info = JSON.parse(data);
|
|
18442
|
-
|
|
19772
|
+
resolve15(!!info["WebKit-Version"] || !!info["Browser"]);
|
|
18443
19773
|
} catch {
|
|
18444
|
-
|
|
19774
|
+
resolve15(false);
|
|
18445
19775
|
}
|
|
18446
19776
|
});
|
|
18447
19777
|
});
|
|
18448
|
-
req.on("error", () =>
|
|
19778
|
+
req.on("error", () => resolve15(false));
|
|
18449
19779
|
req.on("timeout", () => {
|
|
18450
19780
|
req.destroy();
|
|
18451
|
-
|
|
19781
|
+
resolve15(false);
|
|
18452
19782
|
});
|
|
18453
19783
|
});
|
|
18454
19784
|
}
|
|
@@ -18588,8 +19918,8 @@ function detectCurrentWorkspace(ideId) {
|
|
|
18588
19918
|
const appNameMap = getMacAppIdentifiers();
|
|
18589
19919
|
const appName = appNameMap[ideId];
|
|
18590
19920
|
if (appName) {
|
|
18591
|
-
const storagePath =
|
|
18592
|
-
process.env.APPDATA ||
|
|
19921
|
+
const storagePath = path18.join(
|
|
19922
|
+
process.env.APPDATA || path18.join(os15.homedir(), "AppData", "Roaming"),
|
|
18593
19923
|
appName,
|
|
18594
19924
|
"storage.json"
|
|
18595
19925
|
);
|
|
@@ -18767,9 +20097,9 @@ init_logger();
|
|
|
18767
20097
|
|
|
18768
20098
|
// src/logging/command-log.ts
|
|
18769
20099
|
import * as fs8 from "fs";
|
|
18770
|
-
import * as
|
|
20100
|
+
import * as path19 from "path";
|
|
18771
20101
|
import * as os16 from "os";
|
|
18772
|
-
var LOG_DIR2 = process.platform === "win32" ?
|
|
20102
|
+
var LOG_DIR2 = process.platform === "win32" ? path19.join(process.env.LOCALAPPDATA || process.env.APPDATA || path19.join(os16.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path19.join(os16.homedir(), "Library", "Logs", "adhdev") : path19.join(os16.homedir(), ".local", "share", "adhdev", "logs");
|
|
18773
20103
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
18774
20104
|
var MAX_DAYS = 7;
|
|
18775
20105
|
try {
|
|
@@ -18807,13 +20137,13 @@ function getDateStr2() {
|
|
|
18807
20137
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
18808
20138
|
}
|
|
18809
20139
|
var currentDate2 = getDateStr2();
|
|
18810
|
-
var currentFile =
|
|
20140
|
+
var currentFile = path19.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
|
|
18811
20141
|
var writeCount2 = 0;
|
|
18812
20142
|
function checkRotation() {
|
|
18813
20143
|
const today = getDateStr2();
|
|
18814
20144
|
if (today !== currentDate2) {
|
|
18815
20145
|
currentDate2 = today;
|
|
18816
|
-
currentFile =
|
|
20146
|
+
currentFile = path19.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
|
|
18817
20147
|
cleanOldFiles();
|
|
18818
20148
|
}
|
|
18819
20149
|
}
|
|
@@ -18827,7 +20157,7 @@ function cleanOldFiles() {
|
|
|
18827
20157
|
const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
|
|
18828
20158
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
18829
20159
|
try {
|
|
18830
|
-
fs8.unlinkSync(
|
|
20160
|
+
fs8.unlinkSync(path19.join(LOG_DIR2, file));
|
|
18831
20161
|
} catch {
|
|
18832
20162
|
}
|
|
18833
20163
|
}
|
|
@@ -18837,8 +20167,8 @@ function cleanOldFiles() {
|
|
|
18837
20167
|
}
|
|
18838
20168
|
function checkSize() {
|
|
18839
20169
|
try {
|
|
18840
|
-
const
|
|
18841
|
-
if (
|
|
20170
|
+
const stat2 = fs8.statSync(currentFile);
|
|
20171
|
+
if (stat2.size > MAX_FILE_SIZE) {
|
|
18842
20172
|
const backup = currentFile.replace(".jsonl", ".1.jsonl");
|
|
18843
20173
|
try {
|
|
18844
20174
|
fs8.unlinkSync(backup);
|
|
@@ -19124,12 +20454,18 @@ function buildStatusSnapshot(options) {
|
|
|
19124
20454
|
const unreadSourceSessions = buildSessionEntries(
|
|
19125
20455
|
options.allStates,
|
|
19126
20456
|
options.cdpManagers,
|
|
19127
|
-
{
|
|
20457
|
+
{
|
|
20458
|
+
profile: "full",
|
|
20459
|
+
getGitSummaryForWorkspace: options.getGitSummaryForWorkspace
|
|
20460
|
+
}
|
|
19128
20461
|
);
|
|
19129
20462
|
const sessions = profile === "full" ? unreadSourceSessions : profile === "live" ? unreadSourceSessions.map(projectLiveSessionFromFull) : buildSessionEntries(
|
|
19130
20463
|
options.allStates,
|
|
19131
20464
|
options.cdpManagers,
|
|
19132
|
-
{
|
|
20465
|
+
{
|
|
20466
|
+
profile,
|
|
20467
|
+
getGitSummaryForWorkspace: options.getGitSummaryForWorkspace
|
|
20468
|
+
}
|
|
19133
20469
|
);
|
|
19134
20470
|
const sessionsById = new Map(sessions.map((session) => [session.id, session]));
|
|
19135
20471
|
for (const sourceSession of unreadSourceSessions) {
|
|
@@ -19223,13 +20559,13 @@ import { execFileSync as execFileSync2 } from "child_process";
|
|
|
19223
20559
|
import { spawn as spawn3 } from "child_process";
|
|
19224
20560
|
import * as fs9 from "fs";
|
|
19225
20561
|
import * as os18 from "os";
|
|
19226
|
-
import * as
|
|
20562
|
+
import * as path20 from "path";
|
|
19227
20563
|
var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
|
|
19228
20564
|
function getUpgradeLogPath() {
|
|
19229
20565
|
const home = os18.homedir();
|
|
19230
|
-
const dir =
|
|
20566
|
+
const dir = path20.join(home, ".adhdev");
|
|
19231
20567
|
fs9.mkdirSync(dir, { recursive: true });
|
|
19232
|
-
return
|
|
20568
|
+
return path20.join(dir, "daemon-upgrade.log");
|
|
19233
20569
|
}
|
|
19234
20570
|
function appendUpgradeLog(message) {
|
|
19235
20571
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
@@ -19240,14 +20576,14 @@ function appendUpgradeLog(message) {
|
|
|
19240
20576
|
}
|
|
19241
20577
|
}
|
|
19242
20578
|
function resolveSiblingNpmInvocation(nodeExecutable, platform10 = process.platform) {
|
|
19243
|
-
const binDir =
|
|
20579
|
+
const binDir = path20.dirname(nodeExecutable);
|
|
19244
20580
|
if (platform10 === "win32") {
|
|
19245
|
-
const npmCliPath =
|
|
20581
|
+
const npmCliPath = path20.join(binDir, "node_modules", "npm", "bin", "npm-cli.js");
|
|
19246
20582
|
if (fs9.existsSync(npmCliPath)) {
|
|
19247
20583
|
return { executable: nodeExecutable, argsPrefix: [npmCliPath], execOptions: getNpmExecOptions(platform10) };
|
|
19248
20584
|
}
|
|
19249
20585
|
for (const candidate of ["npm.exe", "npm"]) {
|
|
19250
|
-
const candidatePath =
|
|
20586
|
+
const candidatePath = path20.join(binDir, candidate);
|
|
19251
20587
|
if (fs9.existsSync(candidatePath)) {
|
|
19252
20588
|
return { executable: candidatePath, argsPrefix: [], execOptions: getNpmExecOptions(platform10) };
|
|
19253
20589
|
}
|
|
@@ -19255,7 +20591,7 @@ function resolveSiblingNpmInvocation(nodeExecutable, platform10 = process.platfo
|
|
|
19255
20591
|
return { executable: nodeExecutable, argsPrefix: [npmCliPath], execOptions: getNpmExecOptions(platform10) };
|
|
19256
20592
|
}
|
|
19257
20593
|
for (const candidate of ["npm"]) {
|
|
19258
|
-
const candidatePath =
|
|
20594
|
+
const candidatePath = path20.join(binDir, candidate);
|
|
19259
20595
|
if (fs9.existsSync(candidatePath)) {
|
|
19260
20596
|
return { executable: candidatePath, argsPrefix: [], execOptions: getNpmExecOptions(platform10) };
|
|
19261
20597
|
}
|
|
@@ -19272,13 +20608,13 @@ function findCurrentPackageRoot(currentCliPath, packageName) {
|
|
|
19272
20608
|
let currentDir = resolvedPath;
|
|
19273
20609
|
try {
|
|
19274
20610
|
if (fs9.statSync(resolvedPath).isFile()) {
|
|
19275
|
-
currentDir =
|
|
20611
|
+
currentDir = path20.dirname(resolvedPath);
|
|
19276
20612
|
}
|
|
19277
20613
|
} catch {
|
|
19278
|
-
currentDir =
|
|
20614
|
+
currentDir = path20.dirname(resolvedPath);
|
|
19279
20615
|
}
|
|
19280
20616
|
while (true) {
|
|
19281
|
-
const packageJsonPath =
|
|
20617
|
+
const packageJsonPath = path20.join(currentDir, "package.json");
|
|
19282
20618
|
try {
|
|
19283
20619
|
if (fs9.existsSync(packageJsonPath)) {
|
|
19284
20620
|
const parsed = JSON.parse(fs9.readFileSync(packageJsonPath, "utf8"));
|
|
@@ -19289,7 +20625,7 @@ function findCurrentPackageRoot(currentCliPath, packageName) {
|
|
|
19289
20625
|
}
|
|
19290
20626
|
} catch {
|
|
19291
20627
|
}
|
|
19292
|
-
const parentDir =
|
|
20628
|
+
const parentDir = path20.dirname(currentDir);
|
|
19293
20629
|
if (parentDir === currentDir) {
|
|
19294
20630
|
return null;
|
|
19295
20631
|
}
|
|
@@ -19297,13 +20633,13 @@ function findCurrentPackageRoot(currentCliPath, packageName) {
|
|
|
19297
20633
|
}
|
|
19298
20634
|
}
|
|
19299
20635
|
function resolveInstallPrefixFromPackageRoot(packageRoot, packageName) {
|
|
19300
|
-
const nodeModulesDir = packageName.startsWith("@") ?
|
|
19301
|
-
if (
|
|
20636
|
+
const nodeModulesDir = packageName.startsWith("@") ? path20.dirname(path20.dirname(packageRoot)) : path20.dirname(packageRoot);
|
|
20637
|
+
if (path20.basename(nodeModulesDir) !== "node_modules") {
|
|
19302
20638
|
return null;
|
|
19303
20639
|
}
|
|
19304
|
-
const maybeLibDir =
|
|
19305
|
-
if (
|
|
19306
|
-
return
|
|
20640
|
+
const maybeLibDir = path20.dirname(nodeModulesDir);
|
|
20641
|
+
if (path20.basename(maybeLibDir) === "lib") {
|
|
20642
|
+
return path20.dirname(maybeLibDir);
|
|
19307
20643
|
}
|
|
19308
20644
|
return maybeLibDir;
|
|
19309
20645
|
}
|
|
@@ -19411,14 +20747,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
19411
20747
|
while (Date.now() - start < timeoutMs) {
|
|
19412
20748
|
try {
|
|
19413
20749
|
process.kill(pid, 0);
|
|
19414
|
-
await new Promise((
|
|
20750
|
+
await new Promise((resolve15) => setTimeout(resolve15, 250));
|
|
19415
20751
|
} catch {
|
|
19416
20752
|
return;
|
|
19417
20753
|
}
|
|
19418
20754
|
}
|
|
19419
20755
|
}
|
|
19420
20756
|
function stopSessionHostProcesses(appName) {
|
|
19421
|
-
const pidFile =
|
|
20757
|
+
const pidFile = path20.join(os18.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
19422
20758
|
try {
|
|
19423
20759
|
if (fs9.existsSync(pidFile)) {
|
|
19424
20760
|
const pid = Number.parseInt(fs9.readFileSync(pidFile, "utf8").trim(), 10);
|
|
@@ -19435,7 +20771,7 @@ function stopSessionHostProcesses(appName) {
|
|
|
19435
20771
|
}
|
|
19436
20772
|
}
|
|
19437
20773
|
function removeDaemonPidFile() {
|
|
19438
|
-
const pidFile =
|
|
20774
|
+
const pidFile = path20.join(os18.homedir(), ".adhdev", "daemon.pid");
|
|
19439
20775
|
try {
|
|
19440
20776
|
fs9.unlinkSync(pidFile);
|
|
19441
20777
|
} catch {
|
|
@@ -19446,7 +20782,7 @@ function cleanupStaleGlobalInstallDirs(pkgName, surface) {
|
|
|
19446
20782
|
const npmRoot = String(execNpmCommandSync(["root", "-g", ...prefixArgs], { encoding: "utf8" }, surface)).trim();
|
|
19447
20783
|
if (!npmRoot) return;
|
|
19448
20784
|
const npmPrefix = surface.installPrefix || String(execNpmCommandSync(["prefix", "-g", ...prefixArgs], { encoding: "utf8" }, surface)).trim();
|
|
19449
|
-
const binDir = process.platform === "win32" ? npmPrefix :
|
|
20785
|
+
const binDir = process.platform === "win32" ? npmPrefix : path20.join(npmPrefix, "bin");
|
|
19450
20786
|
const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
|
|
19451
20787
|
const binNames = /* @__PURE__ */ new Set([packageBaseName]);
|
|
19452
20788
|
if (pkgName === "@adhdev/daemon-standalone") {
|
|
@@ -19454,25 +20790,25 @@ function cleanupStaleGlobalInstallDirs(pkgName, surface) {
|
|
|
19454
20790
|
}
|
|
19455
20791
|
if (pkgName.startsWith("@")) {
|
|
19456
20792
|
const [scope, name] = pkgName.split("/");
|
|
19457
|
-
const scopeDir =
|
|
20793
|
+
const scopeDir = path20.join(npmRoot, scope);
|
|
19458
20794
|
if (!fs9.existsSync(scopeDir)) return;
|
|
19459
20795
|
for (const entry of fs9.readdirSync(scopeDir)) {
|
|
19460
20796
|
if (!entry.startsWith(`.${name}-`)) continue;
|
|
19461
|
-
fs9.rmSync(
|
|
19462
|
-
appendUpgradeLog(`Removed stale scoped staging dir: ${
|
|
20797
|
+
fs9.rmSync(path20.join(scopeDir, entry), { recursive: true, force: true });
|
|
20798
|
+
appendUpgradeLog(`Removed stale scoped staging dir: ${path20.join(scopeDir, entry)}`);
|
|
19463
20799
|
}
|
|
19464
20800
|
} else {
|
|
19465
20801
|
for (const entry of fs9.readdirSync(npmRoot)) {
|
|
19466
20802
|
if (!entry.startsWith(`.${pkgName}-`)) continue;
|
|
19467
|
-
fs9.rmSync(
|
|
19468
|
-
appendUpgradeLog(`Removed stale staging dir: ${
|
|
20803
|
+
fs9.rmSync(path20.join(npmRoot, entry), { recursive: true, force: true });
|
|
20804
|
+
appendUpgradeLog(`Removed stale staging dir: ${path20.join(npmRoot, entry)}`);
|
|
19469
20805
|
}
|
|
19470
20806
|
}
|
|
19471
20807
|
if (fs9.existsSync(binDir)) {
|
|
19472
20808
|
for (const entry of fs9.readdirSync(binDir)) {
|
|
19473
20809
|
if (!Array.from(binNames).some((name) => entry.startsWith(`.${name}-`))) continue;
|
|
19474
|
-
fs9.rmSync(
|
|
19475
|
-
appendUpgradeLog(`Removed stale bin staging entry: ${
|
|
20810
|
+
fs9.rmSync(path20.join(binDir, entry), { recursive: true, force: true });
|
|
20811
|
+
appendUpgradeLog(`Removed stale bin staging entry: ${path20.join(binDir, entry)}`);
|
|
19476
20812
|
}
|
|
19477
20813
|
}
|
|
19478
20814
|
}
|
|
@@ -19522,7 +20858,7 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
19522
20858
|
appendUpgradeLog(installOutput.trim());
|
|
19523
20859
|
}
|
|
19524
20860
|
if (process.platform === "win32") {
|
|
19525
|
-
await new Promise((
|
|
20861
|
+
await new Promise((resolve15) => setTimeout(resolve15, 500));
|
|
19526
20862
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
19527
20863
|
appendUpgradeLog("Post-install staging cleanup complete");
|
|
19528
20864
|
}
|
|
@@ -20916,7 +22252,7 @@ var ProviderStreamAdapter = class {
|
|
|
20916
22252
|
const beforeCount = this.messageCount(before);
|
|
20917
22253
|
const beforeSignature = this.lastMessageSignature(before);
|
|
20918
22254
|
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
20919
|
-
await new Promise((
|
|
22255
|
+
await new Promise((resolve15) => setTimeout(resolve15, 250));
|
|
20920
22256
|
let state;
|
|
20921
22257
|
try {
|
|
20922
22258
|
state = await this.readChat(evaluate);
|
|
@@ -20938,7 +22274,7 @@ var ProviderStreamAdapter = class {
|
|
|
20938
22274
|
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
20939
22275
|
return first;
|
|
20940
22276
|
}
|
|
20941
|
-
await new Promise((
|
|
22277
|
+
await new Promise((resolve15) => setTimeout(resolve15, 150));
|
|
20942
22278
|
const second = await this.readChat(evaluate);
|
|
20943
22279
|
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
20944
22280
|
}
|
|
@@ -21089,7 +22425,7 @@ var ProviderStreamAdapter = class {
|
|
|
21089
22425
|
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
21090
22426
|
}
|
|
21091
22427
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
21092
|
-
await new Promise((
|
|
22428
|
+
await new Promise((resolve15) => setTimeout(resolve15, 250));
|
|
21093
22429
|
const state = await this.readChat(evaluate);
|
|
21094
22430
|
const title = this.getStateTitle(state);
|
|
21095
22431
|
if (this.titlesMatch(title, sessionId)) return true;
|
|
@@ -21950,11 +23286,11 @@ init_chat_message_normalization();
|
|
|
21950
23286
|
|
|
21951
23287
|
// src/providers/version-archive.ts
|
|
21952
23288
|
import * as fs11 from "fs";
|
|
21953
|
-
import * as
|
|
23289
|
+
import * as path21 from "path";
|
|
21954
23290
|
import * as os19 from "os";
|
|
21955
23291
|
import { execSync as execSync5 } from "child_process";
|
|
21956
23292
|
import { platform as platform8 } from "os";
|
|
21957
|
-
var ARCHIVE_PATH =
|
|
23293
|
+
var ARCHIVE_PATH = path21.join(os19.homedir(), ".adhdev", "version-history.json");
|
|
21958
23294
|
var MAX_ENTRIES_PER_PROVIDER = 20;
|
|
21959
23295
|
var VersionArchive = class {
|
|
21960
23296
|
history = {};
|
|
@@ -22001,7 +23337,7 @@ var VersionArchive = class {
|
|
|
22001
23337
|
}
|
|
22002
23338
|
save() {
|
|
22003
23339
|
try {
|
|
22004
|
-
fs11.mkdirSync(
|
|
23340
|
+
fs11.mkdirSync(path21.dirname(ARCHIVE_PATH), { recursive: true });
|
|
22005
23341
|
fs11.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
|
|
22006
23342
|
} catch {
|
|
22007
23343
|
}
|
|
@@ -22058,7 +23394,7 @@ function checkPathExists2(paths) {
|
|
|
22058
23394
|
for (const p of paths) {
|
|
22059
23395
|
if (p.includes("*")) {
|
|
22060
23396
|
const home = os19.homedir();
|
|
22061
|
-
const resolved = p.replace(/\*/g, home.split(
|
|
23397
|
+
const resolved = p.replace(/\*/g, home.split(path21.sep).pop() || "");
|
|
22062
23398
|
if (fs11.existsSync(resolved)) return resolved;
|
|
22063
23399
|
} else {
|
|
22064
23400
|
if (fs11.existsSync(p)) return p;
|
|
@@ -22068,7 +23404,7 @@ function checkPathExists2(paths) {
|
|
|
22068
23404
|
}
|
|
22069
23405
|
function getMacAppVersion(appPath) {
|
|
22070
23406
|
if (platform8() !== "darwin" || !appPath.endsWith(".app")) return null;
|
|
22071
|
-
const plistPath =
|
|
23407
|
+
const plistPath = path21.join(appPath, "Contents", "Info.plist");
|
|
22072
23408
|
if (!fs11.existsSync(plistPath)) return null;
|
|
22073
23409
|
const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
|
|
22074
23410
|
return raw || null;
|
|
@@ -22094,7 +23430,7 @@ async function detectAllVersions(loader, archive) {
|
|
|
22094
23430
|
const cliBin = provider.cli ? findBinary2(provider.cli) : null;
|
|
22095
23431
|
let resolvedBin = cliBin;
|
|
22096
23432
|
if (!resolvedBin && appPath && currentOs === "darwin") {
|
|
22097
|
-
const bundled =
|
|
23433
|
+
const bundled = path21.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
|
|
22098
23434
|
if (provider.cli && fs11.existsSync(bundled)) resolvedBin = bundled;
|
|
22099
23435
|
}
|
|
22100
23436
|
info.installed = !!(appPath || resolvedBin);
|
|
@@ -22135,7 +23471,7 @@ async function detectAllVersions(loader, archive) {
|
|
|
22135
23471
|
// src/daemon/dev-server.ts
|
|
22136
23472
|
import * as http2 from "http";
|
|
22137
23473
|
import * as fs15 from "fs";
|
|
22138
|
-
import * as
|
|
23474
|
+
import * as path25 from "path";
|
|
22139
23475
|
init_config();
|
|
22140
23476
|
|
|
22141
23477
|
// src/daemon/scaffold-template.ts
|
|
@@ -22487,7 +23823,7 @@ init_logger();
|
|
|
22487
23823
|
// src/daemon/dev-cdp-handlers.ts
|
|
22488
23824
|
init_logger();
|
|
22489
23825
|
import * as fs12 from "fs";
|
|
22490
|
-
import * as
|
|
23826
|
+
import * as path22 from "path";
|
|
22491
23827
|
async function handleCdpEvaluate(ctx, req, res) {
|
|
22492
23828
|
const body = await ctx.readBody(req);
|
|
22493
23829
|
const { expression, timeout, ideType } = body;
|
|
@@ -22665,17 +24001,17 @@ async function handleScriptHints(ctx, type, _req, res) {
|
|
|
22665
24001
|
return;
|
|
22666
24002
|
}
|
|
22667
24003
|
let scriptsPath = "";
|
|
22668
|
-
const directScripts =
|
|
24004
|
+
const directScripts = path22.join(dir, "scripts.js");
|
|
22669
24005
|
if (fs12.existsSync(directScripts)) {
|
|
22670
24006
|
scriptsPath = directScripts;
|
|
22671
24007
|
} else {
|
|
22672
|
-
const scriptsDir =
|
|
24008
|
+
const scriptsDir = path22.join(dir, "scripts");
|
|
22673
24009
|
if (fs12.existsSync(scriptsDir)) {
|
|
22674
24010
|
const versions = fs12.readdirSync(scriptsDir).filter((d) => {
|
|
22675
|
-
return fs12.statSync(
|
|
24011
|
+
return fs12.statSync(path22.join(scriptsDir, d)).isDirectory();
|
|
22676
24012
|
}).sort().reverse();
|
|
22677
24013
|
for (const ver of versions) {
|
|
22678
|
-
const p =
|
|
24014
|
+
const p = path22.join(scriptsDir, ver, "scripts.js");
|
|
22679
24015
|
if (fs12.existsSync(p)) {
|
|
22680
24016
|
scriptsPath = p;
|
|
22681
24017
|
break;
|
|
@@ -23504,7 +24840,7 @@ async function handleDomContext(ctx, type, req, res) {
|
|
|
23504
24840
|
|
|
23505
24841
|
// src/daemon/dev-cli-debug.ts
|
|
23506
24842
|
import * as fs13 from "fs";
|
|
23507
|
-
import * as
|
|
24843
|
+
import * as path23 from "path";
|
|
23508
24844
|
function slugifyFixtureName(value) {
|
|
23509
24845
|
const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23510
24846
|
return normalized || `fixture-${Date.now()}`;
|
|
@@ -23514,11 +24850,11 @@ function getCliFixtureDir(ctx, type) {
|
|
|
23514
24850
|
if (!providerDir) {
|
|
23515
24851
|
throw new Error(`Provider directory not found for '${type}'`);
|
|
23516
24852
|
}
|
|
23517
|
-
return
|
|
24853
|
+
return path23.join(providerDir, "fixtures");
|
|
23518
24854
|
}
|
|
23519
24855
|
function readCliFixture(ctx, type, name) {
|
|
23520
24856
|
const fixtureDir = getCliFixtureDir(ctx, type);
|
|
23521
|
-
const filePath =
|
|
24857
|
+
const filePath = path23.join(fixtureDir, `${name}.json`);
|
|
23522
24858
|
if (!fs13.existsSync(filePath)) {
|
|
23523
24859
|
throw new Error(`Fixture not found: ${filePath}`);
|
|
23524
24860
|
}
|
|
@@ -23687,7 +25023,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
|
|
|
23687
25023
|
return { target, instance, adapter };
|
|
23688
25024
|
}
|
|
23689
25025
|
function sleep(ms) {
|
|
23690
|
-
return new Promise((
|
|
25026
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
23691
25027
|
}
|
|
23692
25028
|
async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
|
|
23693
25029
|
const startedAt = Date.now();
|
|
@@ -23985,7 +25321,7 @@ async function runCliAutoImplVerification(ctx, type, verification) {
|
|
|
23985
25321
|
return {
|
|
23986
25322
|
mode: "fixture_replay_suite",
|
|
23987
25323
|
pass: results.every((item) => item.pass),
|
|
23988
|
-
failures: results.flatMap((item) => item.failures.map((
|
|
25324
|
+
failures: results.flatMap((item) => item.failures.map((failure2) => `${item.fixtureName}: ${failure2}`)),
|
|
23989
25325
|
result: firstFailure.result,
|
|
23990
25326
|
assertions: firstFailure.assertions,
|
|
23991
25327
|
fixture: firstFailure.fixture,
|
|
@@ -24285,7 +25621,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
|
|
|
24285
25621
|
},
|
|
24286
25622
|
notes: typeof body?.notes === "string" ? body.notes : void 0
|
|
24287
25623
|
};
|
|
24288
|
-
const filePath =
|
|
25624
|
+
const filePath = path23.join(fixtureDir, `${name}.json`);
|
|
24289
25625
|
fs13.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
|
|
24290
25626
|
ctx.json(res, 200, {
|
|
24291
25627
|
saved: true,
|
|
@@ -24309,7 +25645,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
|
|
|
24309
25645
|
return;
|
|
24310
25646
|
}
|
|
24311
25647
|
const fixtures = fs13.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
|
|
24312
|
-
const fullPath =
|
|
25648
|
+
const fullPath = path23.join(fixtureDir, file);
|
|
24313
25649
|
try {
|
|
24314
25650
|
const raw = JSON.parse(fs13.readFileSync(fullPath, "utf-8"));
|
|
24315
25651
|
return {
|
|
@@ -24445,7 +25781,7 @@ async function handleCliRaw(ctx, req, res) {
|
|
|
24445
25781
|
|
|
24446
25782
|
// src/daemon/dev-auto-implement.ts
|
|
24447
25783
|
import * as fs14 from "fs";
|
|
24448
|
-
import * as
|
|
25784
|
+
import * as path24 from "path";
|
|
24449
25785
|
import * as os20 from "os";
|
|
24450
25786
|
function getAutoImplPid(ctx) {
|
|
24451
25787
|
const pid = ctx.autoImplProcess?.pid;
|
|
@@ -24495,22 +25831,22 @@ function getLatestScriptVersionDir(scriptsDir) {
|
|
|
24495
25831
|
if (!fs14.existsSync(scriptsDir)) return null;
|
|
24496
25832
|
const versions = fs14.readdirSync(scriptsDir).filter((d) => {
|
|
24497
25833
|
try {
|
|
24498
|
-
return fs14.statSync(
|
|
25834
|
+
return fs14.statSync(path24.join(scriptsDir, d)).isDirectory();
|
|
24499
25835
|
} catch {
|
|
24500
25836
|
return false;
|
|
24501
25837
|
}
|
|
24502
25838
|
}).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
24503
25839
|
if (versions.length === 0) return null;
|
|
24504
|
-
return
|
|
25840
|
+
return path24.join(scriptsDir, versions[0]);
|
|
24505
25841
|
}
|
|
24506
25842
|
function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
24507
|
-
const canonicalUserDir =
|
|
24508
|
-
const desiredDir = requestedDir ?
|
|
24509
|
-
const upstreamRoot =
|
|
24510
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
25843
|
+
const canonicalUserDir = path24.resolve(ctx.providerLoader.getUserProviderDir(category, type));
|
|
25844
|
+
const desiredDir = requestedDir ? path24.resolve(requestedDir) : canonicalUserDir;
|
|
25845
|
+
const upstreamRoot = path24.resolve(ctx.providerLoader.getUpstreamDir());
|
|
25846
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path24.sep}`)) {
|
|
24511
25847
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
24512
25848
|
}
|
|
24513
|
-
if (
|
|
25849
|
+
if (path24.basename(desiredDir) !== type) {
|
|
24514
25850
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
24515
25851
|
}
|
|
24516
25852
|
const sourceDir = ctx.findProviderDir(type);
|
|
@@ -24518,11 +25854,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
|
24518
25854
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
24519
25855
|
}
|
|
24520
25856
|
if (!fs14.existsSync(desiredDir)) {
|
|
24521
|
-
fs14.mkdirSync(
|
|
25857
|
+
fs14.mkdirSync(path24.dirname(desiredDir), { recursive: true });
|
|
24522
25858
|
fs14.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
24523
25859
|
ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
24524
25860
|
}
|
|
24525
|
-
const providerJson =
|
|
25861
|
+
const providerJson = path24.join(desiredDir, "provider.json");
|
|
24526
25862
|
if (!fs14.existsSync(providerJson)) {
|
|
24527
25863
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
24528
25864
|
}
|
|
@@ -24533,13 +25869,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
|
|
|
24533
25869
|
const refDir = ctx.findProviderDir(referenceType);
|
|
24534
25870
|
if (!refDir || !fs14.existsSync(refDir)) return {};
|
|
24535
25871
|
const referenceScripts = {};
|
|
24536
|
-
const scriptsDir =
|
|
25872
|
+
const scriptsDir = path24.join(refDir, "scripts");
|
|
24537
25873
|
const latestDir = getLatestScriptVersionDir(scriptsDir);
|
|
24538
25874
|
if (!latestDir) return referenceScripts;
|
|
24539
25875
|
for (const file of fs14.readdirSync(latestDir)) {
|
|
24540
25876
|
if (!file.endsWith(".js")) continue;
|
|
24541
25877
|
try {
|
|
24542
|
-
referenceScripts[file] = fs14.readFileSync(
|
|
25878
|
+
referenceScripts[file] = fs14.readFileSync(path24.join(latestDir, file), "utf-8");
|
|
24543
25879
|
} catch {
|
|
24544
25880
|
}
|
|
24545
25881
|
}
|
|
@@ -24647,9 +25983,9 @@ async function handleAutoImplement(ctx, type, req, res) {
|
|
|
24647
25983
|
});
|
|
24648
25984
|
const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
|
|
24649
25985
|
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
|
|
24650
|
-
const tmpDir =
|
|
25986
|
+
const tmpDir = path24.join(os20.tmpdir(), "adhdev-autoimpl");
|
|
24651
25987
|
if (!fs14.existsSync(tmpDir)) fs14.mkdirSync(tmpDir, { recursive: true });
|
|
24652
|
-
const promptFile =
|
|
25988
|
+
const promptFile = path24.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
|
|
24653
25989
|
fs14.writeFileSync(promptFile, prompt, "utf-8");
|
|
24654
25990
|
ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
|
|
24655
25991
|
const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
|
|
@@ -25081,7 +26417,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
25081
26417
|
setMode: "set_mode.js"
|
|
25082
26418
|
};
|
|
25083
26419
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
25084
|
-
const scriptsDir =
|
|
26420
|
+
const scriptsDir = path24.join(providerDir, "scripts");
|
|
25085
26421
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
25086
26422
|
if (latestScriptsDir) {
|
|
25087
26423
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -25092,7 +26428,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
25092
26428
|
for (const file of fs14.readdirSync(latestScriptsDir)) {
|
|
25093
26429
|
if (file.endsWith(".js") && targetFileNames.has(file)) {
|
|
25094
26430
|
try {
|
|
25095
|
-
const content = fs14.readFileSync(
|
|
26431
|
+
const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
|
|
25096
26432
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
25097
26433
|
lines.push("```javascript");
|
|
25098
26434
|
lines.push(content);
|
|
@@ -25109,7 +26445,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
25109
26445
|
lines.push("");
|
|
25110
26446
|
for (const file of refFiles) {
|
|
25111
26447
|
try {
|
|
25112
|
-
const content = fs14.readFileSync(
|
|
26448
|
+
const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
|
|
25113
26449
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
25114
26450
|
lines.push("```javascript");
|
|
25115
26451
|
lines.push(content);
|
|
@@ -25150,10 +26486,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
25150
26486
|
lines.push("");
|
|
25151
26487
|
}
|
|
25152
26488
|
}
|
|
25153
|
-
const docsDir =
|
|
26489
|
+
const docsDir = path24.join(providerDir, "../../docs");
|
|
25154
26490
|
const loadGuide = (name) => {
|
|
25155
26491
|
try {
|
|
25156
|
-
const p =
|
|
26492
|
+
const p = path24.join(docsDir, name);
|
|
25157
26493
|
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
25158
26494
|
} catch {
|
|
25159
26495
|
}
|
|
@@ -25390,7 +26726,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
25390
26726
|
parseApproval: "parse_approval.js"
|
|
25391
26727
|
};
|
|
25392
26728
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
25393
|
-
const scriptsDir =
|
|
26729
|
+
const scriptsDir = path24.join(providerDir, "scripts");
|
|
25394
26730
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
25395
26731
|
if (latestScriptsDir) {
|
|
25396
26732
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -25402,7 +26738,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
25402
26738
|
if (!file.endsWith(".js")) continue;
|
|
25403
26739
|
if (!targetFileNames.has(file)) continue;
|
|
25404
26740
|
try {
|
|
25405
|
-
const content = fs14.readFileSync(
|
|
26741
|
+
const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
|
|
25406
26742
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
25407
26743
|
lines.push("```javascript");
|
|
25408
26744
|
lines.push(content);
|
|
@@ -25418,7 +26754,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
25418
26754
|
lines.push("");
|
|
25419
26755
|
for (const file of refFiles) {
|
|
25420
26756
|
try {
|
|
25421
|
-
const content = fs14.readFileSync(
|
|
26757
|
+
const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
|
|
25422
26758
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
25423
26759
|
lines.push("```javascript");
|
|
25424
26760
|
lines.push(content);
|
|
@@ -25451,10 +26787,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
25451
26787
|
lines.push("");
|
|
25452
26788
|
}
|
|
25453
26789
|
}
|
|
25454
|
-
const docsDir =
|
|
26790
|
+
const docsDir = path24.join(providerDir, "../../docs");
|
|
25455
26791
|
const loadGuide = (name) => {
|
|
25456
26792
|
try {
|
|
25457
|
-
const p =
|
|
26793
|
+
const p = path24.join(docsDir, name);
|
|
25458
26794
|
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
25459
26795
|
} catch {
|
|
25460
26796
|
}
|
|
@@ -25901,8 +27237,8 @@ var DevServer = class _DevServer {
|
|
|
25901
27237
|
}
|
|
25902
27238
|
getEndpointList() {
|
|
25903
27239
|
return this.routes.map((r) => {
|
|
25904
|
-
const
|
|
25905
|
-
return `${r.method.padEnd(5)} ${
|
|
27240
|
+
const path26 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
|
|
27241
|
+
return `${r.method.padEnd(5)} ${path26}`;
|
|
25906
27242
|
});
|
|
25907
27243
|
}
|
|
25908
27244
|
async start(port = DEV_SERVER_PORT) {
|
|
@@ -25933,15 +27269,15 @@ var DevServer = class _DevServer {
|
|
|
25933
27269
|
this.json(res, 500, { error: e.message });
|
|
25934
27270
|
}
|
|
25935
27271
|
});
|
|
25936
|
-
return new Promise((
|
|
27272
|
+
return new Promise((resolve15, reject) => {
|
|
25937
27273
|
this.server.listen(port, "127.0.0.1", () => {
|
|
25938
27274
|
this.log(`Dev server listening on http://127.0.0.1:${port}`);
|
|
25939
|
-
|
|
27275
|
+
resolve15();
|
|
25940
27276
|
});
|
|
25941
27277
|
this.server.on("error", (e) => {
|
|
25942
27278
|
if (e.code === "EADDRINUSE") {
|
|
25943
27279
|
this.log(`Port ${port} in use, skipping dev server`);
|
|
25944
|
-
|
|
27280
|
+
resolve15();
|
|
25945
27281
|
} else {
|
|
25946
27282
|
reject(e);
|
|
25947
27283
|
}
|
|
@@ -26023,20 +27359,20 @@ var DevServer = class _DevServer {
|
|
|
26023
27359
|
child.stderr?.on("data", (d) => {
|
|
26024
27360
|
stderr += d.toString().slice(0, 2e3);
|
|
26025
27361
|
});
|
|
26026
|
-
await new Promise((
|
|
27362
|
+
await new Promise((resolve15) => {
|
|
26027
27363
|
const timer = setTimeout(() => {
|
|
26028
27364
|
child.kill();
|
|
26029
|
-
|
|
27365
|
+
resolve15();
|
|
26030
27366
|
}, 3e3);
|
|
26031
27367
|
child.on("exit", () => {
|
|
26032
27368
|
clearTimeout(timer);
|
|
26033
|
-
|
|
27369
|
+
resolve15();
|
|
26034
27370
|
});
|
|
26035
27371
|
child.stdout?.once("data", () => {
|
|
26036
27372
|
setTimeout(() => {
|
|
26037
27373
|
child.kill();
|
|
26038
27374
|
clearTimeout(timer);
|
|
26039
|
-
|
|
27375
|
+
resolve15();
|
|
26040
27376
|
}, 500);
|
|
26041
27377
|
});
|
|
26042
27378
|
});
|
|
@@ -26190,12 +27526,12 @@ var DevServer = class _DevServer {
|
|
|
26190
27526
|
// ─── DevConsole SPA ───
|
|
26191
27527
|
getConsoleDistDir() {
|
|
26192
27528
|
const candidates = [
|
|
26193
|
-
|
|
26194
|
-
|
|
26195
|
-
|
|
27529
|
+
path25.resolve(__dirname, "../../web-devconsole/dist"),
|
|
27530
|
+
path25.resolve(__dirname, "../../../web-devconsole/dist"),
|
|
27531
|
+
path25.join(process.cwd(), "packages/web-devconsole/dist")
|
|
26196
27532
|
];
|
|
26197
27533
|
for (const dir of candidates) {
|
|
26198
|
-
if (fs15.existsSync(
|
|
27534
|
+
if (fs15.existsSync(path25.join(dir, "index.html"))) return dir;
|
|
26199
27535
|
}
|
|
26200
27536
|
return null;
|
|
26201
27537
|
}
|
|
@@ -26205,7 +27541,7 @@ var DevServer = class _DevServer {
|
|
|
26205
27541
|
this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
|
|
26206
27542
|
return;
|
|
26207
27543
|
}
|
|
26208
|
-
const htmlPath =
|
|
27544
|
+
const htmlPath = path25.join(distDir, "index.html");
|
|
26209
27545
|
try {
|
|
26210
27546
|
const html = fs15.readFileSync(htmlPath, "utf-8");
|
|
26211
27547
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
@@ -26230,15 +27566,15 @@ var DevServer = class _DevServer {
|
|
|
26230
27566
|
this.json(res, 404, { error: "Not found" });
|
|
26231
27567
|
return;
|
|
26232
27568
|
}
|
|
26233
|
-
const safePath =
|
|
26234
|
-
const filePath =
|
|
27569
|
+
const safePath = path25.normalize(pathname).replace(/^\.\.\//, "");
|
|
27570
|
+
const filePath = path25.join(distDir, safePath);
|
|
26235
27571
|
if (!filePath.startsWith(distDir)) {
|
|
26236
27572
|
this.json(res, 403, { error: "Forbidden" });
|
|
26237
27573
|
return;
|
|
26238
27574
|
}
|
|
26239
27575
|
try {
|
|
26240
27576
|
const content = fs15.readFileSync(filePath);
|
|
26241
|
-
const ext =
|
|
27577
|
+
const ext = path25.extname(filePath);
|
|
26242
27578
|
const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
|
|
26243
27579
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
|
|
26244
27580
|
res.end(content);
|
|
@@ -26351,10 +27687,10 @@ var DevServer = class _DevServer {
|
|
|
26351
27687
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
26352
27688
|
if (entry.isDirectory()) {
|
|
26353
27689
|
files.push({ path: rel, size: 0, type: "dir" });
|
|
26354
|
-
scan(
|
|
27690
|
+
scan(path25.join(d, entry.name), rel);
|
|
26355
27691
|
} else {
|
|
26356
|
-
const
|
|
26357
|
-
files.push({ path: rel, size:
|
|
27692
|
+
const stat2 = fs15.statSync(path25.join(d, entry.name));
|
|
27693
|
+
files.push({ path: rel, size: stat2.size, type: "file" });
|
|
26358
27694
|
}
|
|
26359
27695
|
}
|
|
26360
27696
|
} catch {
|
|
@@ -26376,7 +27712,7 @@ var DevServer = class _DevServer {
|
|
|
26376
27712
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
26377
27713
|
return;
|
|
26378
27714
|
}
|
|
26379
|
-
const fullPath =
|
|
27715
|
+
const fullPath = path25.resolve(dir, path25.normalize(filePath));
|
|
26380
27716
|
if (!fullPath.startsWith(dir)) {
|
|
26381
27717
|
this.json(res, 403, { error: "Forbidden" });
|
|
26382
27718
|
return;
|
|
@@ -26401,14 +27737,14 @@ var DevServer = class _DevServer {
|
|
|
26401
27737
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
26402
27738
|
return;
|
|
26403
27739
|
}
|
|
26404
|
-
const fullPath =
|
|
27740
|
+
const fullPath = path25.resolve(dir, path25.normalize(filePath));
|
|
26405
27741
|
if (!fullPath.startsWith(dir)) {
|
|
26406
27742
|
this.json(res, 403, { error: "Forbidden" });
|
|
26407
27743
|
return;
|
|
26408
27744
|
}
|
|
26409
27745
|
try {
|
|
26410
27746
|
if (fs15.existsSync(fullPath)) fs15.copyFileSync(fullPath, fullPath + ".bak");
|
|
26411
|
-
fs15.mkdirSync(
|
|
27747
|
+
fs15.mkdirSync(path25.dirname(fullPath), { recursive: true });
|
|
26412
27748
|
fs15.writeFileSync(fullPath, content, "utf-8");
|
|
26413
27749
|
this.log(`File saved: ${fullPath} (${content.length} chars)`);
|
|
26414
27750
|
this.providerLoader.reload();
|
|
@@ -26425,7 +27761,7 @@ var DevServer = class _DevServer {
|
|
|
26425
27761
|
return;
|
|
26426
27762
|
}
|
|
26427
27763
|
for (const name of ["scripts.js", "provider.json"]) {
|
|
26428
|
-
const p =
|
|
27764
|
+
const p = path25.join(dir, name);
|
|
26429
27765
|
if (fs15.existsSync(p)) {
|
|
26430
27766
|
const source = fs15.readFileSync(p, "utf-8");
|
|
26431
27767
|
this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
|
|
@@ -26446,8 +27782,8 @@ var DevServer = class _DevServer {
|
|
|
26446
27782
|
this.json(res, 404, { error: `Provider not found: ${type}` });
|
|
26447
27783
|
return;
|
|
26448
27784
|
}
|
|
26449
|
-
const target = fs15.existsSync(
|
|
26450
|
-
const targetPath =
|
|
27785
|
+
const target = fs15.existsSync(path25.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
|
|
27786
|
+
const targetPath = path25.join(dir, target);
|
|
26451
27787
|
try {
|
|
26452
27788
|
if (fs15.existsSync(targetPath)) fs15.copyFileSync(targetPath, targetPath + ".bak");
|
|
26453
27789
|
fs15.writeFileSync(targetPath, source, "utf-8");
|
|
@@ -26539,14 +27875,14 @@ var DevServer = class _DevServer {
|
|
|
26539
27875
|
child.stderr?.on("data", (d) => {
|
|
26540
27876
|
stderr += d.toString();
|
|
26541
27877
|
});
|
|
26542
|
-
await new Promise((
|
|
27878
|
+
await new Promise((resolve15) => {
|
|
26543
27879
|
const timer = setTimeout(() => {
|
|
26544
27880
|
child.kill();
|
|
26545
|
-
|
|
27881
|
+
resolve15();
|
|
26546
27882
|
}, timeout);
|
|
26547
27883
|
child.on("exit", () => {
|
|
26548
27884
|
clearTimeout(timer);
|
|
26549
|
-
|
|
27885
|
+
resolve15();
|
|
26550
27886
|
});
|
|
26551
27887
|
});
|
|
26552
27888
|
const elapsed = Date.now() - start;
|
|
@@ -26594,7 +27930,7 @@ var DevServer = class _DevServer {
|
|
|
26594
27930
|
}
|
|
26595
27931
|
let targetDir;
|
|
26596
27932
|
targetDir = this.providerLoader.getUserProviderDir(category, type);
|
|
26597
|
-
const jsonPath =
|
|
27933
|
+
const jsonPath = path25.join(targetDir, "provider.json");
|
|
26598
27934
|
if (fs15.existsSync(jsonPath)) {
|
|
26599
27935
|
this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
|
|
26600
27936
|
return;
|
|
@@ -26606,8 +27942,8 @@ var DevServer = class _DevServer {
|
|
|
26606
27942
|
const createdFiles = ["provider.json"];
|
|
26607
27943
|
if (result.files) {
|
|
26608
27944
|
for (const [relPath, content] of Object.entries(result.files)) {
|
|
26609
|
-
const fullPath =
|
|
26610
|
-
fs15.mkdirSync(
|
|
27945
|
+
const fullPath = path25.join(targetDir, relPath);
|
|
27946
|
+
fs15.mkdirSync(path25.dirname(fullPath), { recursive: true });
|
|
26611
27947
|
fs15.writeFileSync(fullPath, content, "utf-8");
|
|
26612
27948
|
createdFiles.push(relPath);
|
|
26613
27949
|
}
|
|
@@ -26660,22 +27996,22 @@ var DevServer = class _DevServer {
|
|
|
26660
27996
|
if (!fs15.existsSync(scriptsDir)) return null;
|
|
26661
27997
|
const versions = fs15.readdirSync(scriptsDir).filter((d) => {
|
|
26662
27998
|
try {
|
|
26663
|
-
return fs15.statSync(
|
|
27999
|
+
return fs15.statSync(path25.join(scriptsDir, d)).isDirectory();
|
|
26664
28000
|
} catch {
|
|
26665
28001
|
return false;
|
|
26666
28002
|
}
|
|
26667
28003
|
}).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
26668
28004
|
if (versions.length === 0) return null;
|
|
26669
|
-
return
|
|
28005
|
+
return path25.join(scriptsDir, versions[0]);
|
|
26670
28006
|
}
|
|
26671
28007
|
resolveAutoImplWritableProviderDir(category, type, requestedDir) {
|
|
26672
|
-
const canonicalUserDir =
|
|
26673
|
-
const desiredDir = requestedDir ?
|
|
26674
|
-
const upstreamRoot =
|
|
26675
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
28008
|
+
const canonicalUserDir = path25.resolve(this.providerLoader.getUserProviderDir(category, type));
|
|
28009
|
+
const desiredDir = requestedDir ? path25.resolve(requestedDir) : canonicalUserDir;
|
|
28010
|
+
const upstreamRoot = path25.resolve(this.providerLoader.getUpstreamDir());
|
|
28011
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path25.sep}`)) {
|
|
26676
28012
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
26677
28013
|
}
|
|
26678
|
-
if (
|
|
28014
|
+
if (path25.basename(desiredDir) !== type) {
|
|
26679
28015
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
26680
28016
|
}
|
|
26681
28017
|
const sourceDir = this.findProviderDir(type);
|
|
@@ -26683,11 +28019,11 @@ var DevServer = class _DevServer {
|
|
|
26683
28019
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
26684
28020
|
}
|
|
26685
28021
|
if (!fs15.existsSync(desiredDir)) {
|
|
26686
|
-
fs15.mkdirSync(
|
|
28022
|
+
fs15.mkdirSync(path25.dirname(desiredDir), { recursive: true });
|
|
26687
28023
|
fs15.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
26688
28024
|
this.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
26689
28025
|
}
|
|
26690
|
-
const providerJson =
|
|
28026
|
+
const providerJson = path25.join(desiredDir, "provider.json");
|
|
26691
28027
|
if (!fs15.existsSync(providerJson)) {
|
|
26692
28028
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
26693
28029
|
}
|
|
@@ -26723,7 +28059,7 @@ var DevServer = class _DevServer {
|
|
|
26723
28059
|
setMode: "set_mode.js"
|
|
26724
28060
|
};
|
|
26725
28061
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
26726
|
-
const scriptsDir =
|
|
28062
|
+
const scriptsDir = path25.join(providerDir, "scripts");
|
|
26727
28063
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
26728
28064
|
if (latestScriptsDir) {
|
|
26729
28065
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -26734,7 +28070,7 @@ var DevServer = class _DevServer {
|
|
|
26734
28070
|
for (const file of fs15.readdirSync(latestScriptsDir)) {
|
|
26735
28071
|
if (file.endsWith(".js") && targetFileNames.has(file)) {
|
|
26736
28072
|
try {
|
|
26737
|
-
const content = fs15.readFileSync(
|
|
28073
|
+
const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
|
|
26738
28074
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
26739
28075
|
lines.push("```javascript");
|
|
26740
28076
|
lines.push(content);
|
|
@@ -26751,7 +28087,7 @@ var DevServer = class _DevServer {
|
|
|
26751
28087
|
lines.push("");
|
|
26752
28088
|
for (const file of refFiles) {
|
|
26753
28089
|
try {
|
|
26754
|
-
const content = fs15.readFileSync(
|
|
28090
|
+
const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
|
|
26755
28091
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
26756
28092
|
lines.push("```javascript");
|
|
26757
28093
|
lines.push(content);
|
|
@@ -26792,10 +28128,10 @@ var DevServer = class _DevServer {
|
|
|
26792
28128
|
lines.push("");
|
|
26793
28129
|
}
|
|
26794
28130
|
}
|
|
26795
|
-
const docsDir =
|
|
28131
|
+
const docsDir = path25.join(providerDir, "../../docs");
|
|
26796
28132
|
const loadGuide = (name) => {
|
|
26797
28133
|
try {
|
|
26798
|
-
const p =
|
|
28134
|
+
const p = path25.join(docsDir, name);
|
|
26799
28135
|
if (fs15.existsSync(p)) return fs15.readFileSync(p, "utf-8");
|
|
26800
28136
|
} catch {
|
|
26801
28137
|
}
|
|
@@ -26969,7 +28305,7 @@ var DevServer = class _DevServer {
|
|
|
26969
28305
|
parseApproval: "parse_approval.js"
|
|
26970
28306
|
};
|
|
26971
28307
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
26972
|
-
const scriptsDir =
|
|
28308
|
+
const scriptsDir = path25.join(providerDir, "scripts");
|
|
26973
28309
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
26974
28310
|
if (latestScriptsDir) {
|
|
26975
28311
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -26981,7 +28317,7 @@ var DevServer = class _DevServer {
|
|
|
26981
28317
|
if (!file.endsWith(".js")) continue;
|
|
26982
28318
|
if (!targetFileNames.has(file)) continue;
|
|
26983
28319
|
try {
|
|
26984
|
-
const content = fs15.readFileSync(
|
|
28320
|
+
const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
|
|
26985
28321
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
26986
28322
|
lines.push("```javascript");
|
|
26987
28323
|
lines.push(content);
|
|
@@ -26997,7 +28333,7 @@ var DevServer = class _DevServer {
|
|
|
26997
28333
|
lines.push("");
|
|
26998
28334
|
for (const file of refFiles) {
|
|
26999
28335
|
try {
|
|
27000
|
-
const content = fs15.readFileSync(
|
|
28336
|
+
const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
|
|
27001
28337
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
27002
28338
|
lines.push("```javascript");
|
|
27003
28339
|
lines.push(content);
|
|
@@ -27030,10 +28366,10 @@ var DevServer = class _DevServer {
|
|
|
27030
28366
|
lines.push("");
|
|
27031
28367
|
}
|
|
27032
28368
|
}
|
|
27033
|
-
const docsDir =
|
|
28369
|
+
const docsDir = path25.join(providerDir, "../../docs");
|
|
27034
28370
|
const loadGuide = (name) => {
|
|
27035
28371
|
try {
|
|
27036
|
-
const p =
|
|
28372
|
+
const p = path25.join(docsDir, name);
|
|
27037
28373
|
if (fs15.existsSync(p)) return fs15.readFileSync(p, "utf-8");
|
|
27038
28374
|
} catch {
|
|
27039
28375
|
}
|
|
@@ -27216,14 +28552,14 @@ data: ${JSON.stringify(msg.data)}
|
|
|
27216
28552
|
res.end(JSON.stringify(data, null, 2));
|
|
27217
28553
|
}
|
|
27218
28554
|
async readBody(req) {
|
|
27219
|
-
return new Promise((
|
|
28555
|
+
return new Promise((resolve15) => {
|
|
27220
28556
|
let body = "";
|
|
27221
28557
|
req.on("data", (chunk) => body += chunk);
|
|
27222
28558
|
req.on("end", () => {
|
|
27223
28559
|
try {
|
|
27224
|
-
|
|
28560
|
+
resolve15(JSON.parse(body));
|
|
27225
28561
|
} catch {
|
|
27226
|
-
|
|
28562
|
+
resolve15({});
|
|
27227
28563
|
}
|
|
27228
28564
|
});
|
|
27229
28565
|
});
|
|
@@ -27738,7 +29074,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
|
|
|
27738
29074
|
const deadline = Date.now() + timeoutMs;
|
|
27739
29075
|
while (Date.now() < deadline) {
|
|
27740
29076
|
if (await canConnect(endpoint)) return;
|
|
27741
|
-
await new Promise((
|
|
29077
|
+
await new Promise((resolve15) => setTimeout(resolve15, STARTUP_POLL_MS));
|
|
27742
29078
|
}
|
|
27743
29079
|
throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
|
|
27744
29080
|
}
|
|
@@ -27916,10 +29252,10 @@ async function installExtension(ide, extension) {
|
|
|
27916
29252
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
27917
29253
|
const fs16 = await import("fs");
|
|
27918
29254
|
fs16.writeFileSync(vsixPath, buffer);
|
|
27919
|
-
return new Promise((
|
|
29255
|
+
return new Promise((resolve15) => {
|
|
27920
29256
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
27921
29257
|
exec2(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
|
|
27922
|
-
|
|
29258
|
+
resolve15({
|
|
27923
29259
|
extensionId: extension.id,
|
|
27924
29260
|
marketplaceId: extension.marketplaceId,
|
|
27925
29261
|
success: !error,
|
|
@@ -27932,11 +29268,11 @@ async function installExtension(ide, extension) {
|
|
|
27932
29268
|
} catch (e) {
|
|
27933
29269
|
}
|
|
27934
29270
|
}
|
|
27935
|
-
return new Promise((
|
|
29271
|
+
return new Promise((resolve15) => {
|
|
27936
29272
|
const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
|
|
27937
29273
|
exec2(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
|
|
27938
29274
|
if (error) {
|
|
27939
|
-
|
|
29275
|
+
resolve15({
|
|
27940
29276
|
extensionId: extension.id,
|
|
27941
29277
|
marketplaceId: extension.marketplaceId,
|
|
27942
29278
|
success: false,
|
|
@@ -27944,7 +29280,7 @@ async function installExtension(ide, extension) {
|
|
|
27944
29280
|
error: stderr || error.message
|
|
27945
29281
|
});
|
|
27946
29282
|
} else {
|
|
27947
|
-
|
|
29283
|
+
resolve15({
|
|
27948
29284
|
extensionId: extension.id,
|
|
27949
29285
|
marketplaceId: extension.marketplaceId,
|
|
27950
29286
|
success: true,
|
|
@@ -28148,6 +29484,7 @@ async function initDaemonComponents(config) {
|
|
|
28148
29484
|
providerLoader,
|
|
28149
29485
|
instanceManager,
|
|
28150
29486
|
sessionRegistry,
|
|
29487
|
+
gitCommandServices: createDefaultGitCommandServices(),
|
|
28151
29488
|
onProviderSettingChanged: async (providerType) => {
|
|
28152
29489
|
await refreshProviderAvailability(providerType);
|
|
28153
29490
|
config.onStatusChange?.();
|
|
@@ -28155,7 +29492,8 @@ async function initDaemonComponents(config) {
|
|
|
28155
29492
|
onProviderSourceConfigChanged: async () => {
|
|
28156
29493
|
await refreshProviderAvailability();
|
|
28157
29494
|
config.onStatusChange?.();
|
|
28158
|
-
}
|
|
29495
|
+
},
|
|
29496
|
+
onBeforeSendChat: config.onBeforeSendChat
|
|
28159
29497
|
});
|
|
28160
29498
|
agentStreamManager = new DaemonAgentStreamManager(
|
|
28161
29499
|
LOG.forComponent("AgentStream").asLogFn(),
|
|
@@ -28273,6 +29611,7 @@ export {
|
|
|
28273
29611
|
DEFAULT_CDP_SCAN_INTERVAL_MS,
|
|
28274
29612
|
DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS,
|
|
28275
29613
|
DEFAULT_DAEMON_PORT,
|
|
29614
|
+
DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
28276
29615
|
DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
28277
29616
|
DEFAULT_SESSION_HOST_APP_NAME,
|
|
28278
29617
|
DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
@@ -28291,8 +29630,12 @@ export {
|
|
|
28291
29630
|
DaemonCommandRouter,
|
|
28292
29631
|
DaemonStatusReporter,
|
|
28293
29632
|
DevServer,
|
|
29633
|
+
GitCommandError,
|
|
29634
|
+
GitWorkspaceMonitor,
|
|
28294
29635
|
IdeProviderInstance,
|
|
29636
|
+
InMemoryGitSnapshotStore,
|
|
28295
29637
|
LOG,
|
|
29638
|
+
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
28296
29639
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
28297
29640
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
28298
29641
|
NodePtyTransportFactory,
|
|
@@ -28301,6 +29644,7 @@ export {
|
|
|
28301
29644
|
ProviderLoader,
|
|
28302
29645
|
STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
28303
29646
|
SessionHostPtyTransportFactory,
|
|
29647
|
+
TurnSnapshotTracker,
|
|
28304
29648
|
VersionArchive,
|
|
28305
29649
|
appendRecentActivity,
|
|
28306
29650
|
buildAssistantChatMessage,
|
|
@@ -28320,9 +29664,14 @@ export {
|
|
|
28320
29664
|
buildUserChatMessage,
|
|
28321
29665
|
classifyHotChatSessionsForSubscriptionFlush,
|
|
28322
29666
|
clearDebugTrace,
|
|
29667
|
+
compareGitSnapshots,
|
|
28323
29668
|
configureDebugTraceStore,
|
|
28324
29669
|
connectCdpManager,
|
|
28325
29670
|
createDebugTraceStore,
|
|
29671
|
+
createDefaultGitCommandServices,
|
|
29672
|
+
createGitCompactSummary,
|
|
29673
|
+
createGitSnapshotStore,
|
|
29674
|
+
createGitWorkspaceMonitor,
|
|
28326
29675
|
createInteractionId,
|
|
28327
29676
|
detectAllVersions,
|
|
28328
29677
|
detectCLIs,
|
|
@@ -28337,6 +29686,9 @@ export {
|
|
|
28337
29686
|
getCurrentDaemonLogPath,
|
|
28338
29687
|
getDaemonLogDir,
|
|
28339
29688
|
getDebugRuntimeConfig,
|
|
29689
|
+
getGitDiffSummary,
|
|
29690
|
+
getGitFileDiff,
|
|
29691
|
+
getGitRepoStatus,
|
|
28340
29692
|
getHostMemorySnapshot,
|
|
28341
29693
|
getLogLevel,
|
|
28342
29694
|
getNpmExecOptions,
|
|
@@ -28348,6 +29700,7 @@ export {
|
|
|
28348
29700
|
getSessionHostRecoveryLabel,
|
|
28349
29701
|
getSessionHostSurfaceKind,
|
|
28350
29702
|
getWorkspaceState,
|
|
29703
|
+
handleGitCommand,
|
|
28351
29704
|
hasCdpManager,
|
|
28352
29705
|
hashSignatureParts,
|
|
28353
29706
|
initDaemonComponents,
|
|
@@ -28356,9 +29709,11 @@ export {
|
|
|
28356
29709
|
isBuiltinChatMessageKind,
|
|
28357
29710
|
isCdpConnected,
|
|
28358
29711
|
isExtensionInstalled,
|
|
29712
|
+
isGitCommandName,
|
|
28359
29713
|
isIdeRunning,
|
|
28360
29714
|
isManagedStatusWaiting,
|
|
28361
29715
|
isManagedStatusWorking,
|
|
29716
|
+
isPathInside,
|
|
28362
29717
|
isSessionHostLiveRuntime,
|
|
28363
29718
|
isSessionHostRecoverySnapshot,
|
|
28364
29719
|
isSetupComplete,
|
|
@@ -28376,10 +29731,13 @@ export {
|
|
|
28376
29731
|
normalizeChatMessageKind,
|
|
28377
29732
|
normalizeChatMessages,
|
|
28378
29733
|
normalizeChatTailActiveModal,
|
|
29734
|
+
normalizeGitOutput,
|
|
29735
|
+
normalizeGitWorkspaceSubscriptionParams,
|
|
28379
29736
|
normalizeInputEnvelope,
|
|
28380
29737
|
normalizeManagedStatus,
|
|
28381
29738
|
normalizeMessageParts,
|
|
28382
29739
|
normalizeSessionModalFields,
|
|
29740
|
+
parsePorcelainV2Status,
|
|
28383
29741
|
parseProviderSourceConfigUpdate,
|
|
28384
29742
|
partitionSessionHostDiagnosticsSessions,
|
|
28385
29743
|
partitionSessionHostRecords,
|
|
@@ -28395,9 +29753,11 @@ export {
|
|
|
28395
29753
|
resolveChatMessageKind,
|
|
28396
29754
|
resolveCurrentGlobalInstallSurface,
|
|
28397
29755
|
resolveDebugRuntimeConfig,
|
|
29756
|
+
resolveGitRepository,
|
|
28398
29757
|
resolveSessionHostAppName,
|
|
28399
29758
|
resolveSessionHostAppNameResolution,
|
|
28400
29759
|
runAsyncBatch,
|
|
29760
|
+
runGit,
|
|
28401
29761
|
saveConfig,
|
|
28402
29762
|
saveState,
|
|
28403
29763
|
setDebugRuntimeConfig,
|
|
@@ -28408,6 +29768,7 @@ export {
|
|
|
28408
29768
|
shutdownDaemonComponents,
|
|
28409
29769
|
spawnDetachedDaemonUpgradeHelper,
|
|
28410
29770
|
startDaemonDevSupport,
|
|
29771
|
+
summarizeGitStatus,
|
|
28411
29772
|
updateConfig,
|
|
28412
29773
|
upsertSavedProviderSession
|
|
28413
29774
|
};
|