autopilot-ai 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -3
- package/dist/index.js +1498 -434
- package/dist/index.js.map +1 -1
- package/dist/sdk/index.d.ts +16 -1
- package/dist/sdk/index.js +323 -70
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,8 +75,8 @@ async function flushAndCloseForTesting() {
|
|
|
75
75
|
const s = currentStream;
|
|
76
76
|
currentStream = null;
|
|
77
77
|
currentDate = null;
|
|
78
|
-
await new Promise((
|
|
79
|
-
s.end(() =>
|
|
78
|
+
await new Promise((resolve22) => {
|
|
79
|
+
s.end(() => resolve22());
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
function isLogSinkEnabled() {
|
|
@@ -1067,10 +1067,10 @@ async function tokenFromFile(file, fetchImpl2, now2) {
|
|
|
1067
1067
|
return { token: t.access_token, expiresAt: now2 + (t.expires_in ?? 3600) * 1e3 };
|
|
1068
1068
|
}
|
|
1069
1069
|
function gcloudToken() {
|
|
1070
|
-
return new Promise((
|
|
1070
|
+
return new Promise((resolve22, reject) => {
|
|
1071
1071
|
execFile("gcloud", ["auth", "print-access-token"], { timeout: 15e3 }, (err, stdout) => {
|
|
1072
1072
|
if (err) reject(new Error("No Google credentials found. Run `gcloud auth application-default login`, or set GOOGLE_APPLICATION_CREDENTIALS."));
|
|
1073
|
-
else
|
|
1073
|
+
else resolve22(stdout.trim());
|
|
1074
1074
|
});
|
|
1075
1075
|
});
|
|
1076
1076
|
}
|
|
@@ -2322,8 +2322,8 @@ function onShutdown(name, task, phase = SHUTDOWN_PHASE.sessions) {
|
|
|
2322
2322
|
}
|
|
2323
2323
|
function withDeadline(work, ms) {
|
|
2324
2324
|
let timer2;
|
|
2325
|
-
const deadline = new Promise((
|
|
2326
|
-
timer2 = setTimeout(() =>
|
|
2325
|
+
const deadline = new Promise((resolve22) => {
|
|
2326
|
+
timer2 = setTimeout(() => resolve22("timeout"), Math.max(0, ms));
|
|
2327
2327
|
});
|
|
2328
2328
|
return Promise.race([work, deadline]).finally(() => clearTimeout(timer2));
|
|
2329
2329
|
}
|
|
@@ -3782,8 +3782,8 @@ var init_copilot = __esm({
|
|
|
3782
3782
|
COPILOT_API_VERSION = "2026-08-01";
|
|
3783
3783
|
USER_API_VERSION = "2025-04-01";
|
|
3784
3784
|
COPILOT_DEFAULT_BASE = "https://api.githubcopilot.com";
|
|
3785
|
-
sleep = (ms, signal) => new Promise((
|
|
3786
|
-
const t = setTimeout(
|
|
3785
|
+
sleep = (ms, signal) => new Promise((resolve22, reject) => {
|
|
3786
|
+
const t = setTimeout(resolve22, ms);
|
|
3787
3787
|
signal?.addEventListener("abort", () => {
|
|
3788
3788
|
clearTimeout(t);
|
|
3789
3789
|
reject(new DOMException("aborted", "AbortError"));
|
|
@@ -3874,10 +3874,10 @@ async function startChatGPTSignIn(opts2) {
|
|
|
3874
3874
|
const { verifier, challenge } = pkce();
|
|
3875
3875
|
const state = randomBytes(32).toString("base64url");
|
|
3876
3876
|
const nonce = randomBytes(32).toString("base64url");
|
|
3877
|
-
let
|
|
3877
|
+
let resolve22;
|
|
3878
3878
|
let reject;
|
|
3879
3879
|
const done = new Promise((res, rej) => {
|
|
3880
|
-
|
|
3880
|
+
resolve22 = res;
|
|
3881
3881
|
reject = rej;
|
|
3882
3882
|
});
|
|
3883
3883
|
done.catch(() => void 0);
|
|
@@ -3900,7 +3900,7 @@ async function startChatGPTSignIn(opts2) {
|
|
|
3900
3900
|
}
|
|
3901
3901
|
if (!clientId) return finish("No client id returned"), reject(new Error("ChatGPT sign-in did not return a client id. Try again."));
|
|
3902
3902
|
completeSignIn(code, clientId, redirect, verifier, nonce, opts2.fetchImpl).then(
|
|
3903
|
-
(s) => (finish(),
|
|
3903
|
+
(s) => (finish(), resolve22(s)),
|
|
3904
3904
|
(e) => (finish(e.message), reject(e))
|
|
3905
3905
|
);
|
|
3906
3906
|
});
|
|
@@ -4608,8 +4608,8 @@ async function* runKimi(opts2) {
|
|
|
4608
4608
|
if (isAbortError(fetchErr)) throw fetchErr;
|
|
4609
4609
|
logger.warn("runKimi:fetch_error", { requestId, attempt, error: msg });
|
|
4610
4610
|
if (attempt < MAX_ATTEMPTS - 1) {
|
|
4611
|
-
const
|
|
4612
|
-
await sleep2(
|
|
4611
|
+
const delay2 = Math.random() * (500 * 2 ** attempt);
|
|
4612
|
+
await sleep2(delay2, opts2.signal);
|
|
4613
4613
|
continue;
|
|
4614
4614
|
}
|
|
4615
4615
|
const local = customEndpoint?.providerId && !preset.keyRequired;
|
|
@@ -4652,9 +4652,9 @@ Is ${preset.name} running at ${customEndpoint.baseUrl}? Start it, or set provide
|
|
|
4652
4652
|
const isRateLimit = apiErr.httpStatus === 429;
|
|
4653
4653
|
const baseDelay = isRateLimit ? 2e3 : 500;
|
|
4654
4654
|
const retryAfterSec = Number(res.headers.get("retry-after"));
|
|
4655
|
-
const
|
|
4656
|
-
logger.warn("runKimi:retrying", { requestId, attempt, code: apiErr.code, httpStatus: apiErr.httpStatus, delay });
|
|
4657
|
-
await sleep2(
|
|
4655
|
+
const delay2 = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? Math.min(retryAfterSec * 1e3, 3e4) : Math.random() * (baseDelay * 2 ** attempt);
|
|
4656
|
+
logger.warn("runKimi:retrying", { requestId, attempt, code: apiErr.code, httpStatus: apiErr.httpStatus, delay: delay2 });
|
|
4657
|
+
await sleep2(delay2, opts2.signal);
|
|
4658
4658
|
continue;
|
|
4659
4659
|
}
|
|
4660
4660
|
throw apiErr;
|
|
@@ -5106,11 +5106,11 @@ function extractApiError(parsed, rawText) {
|
|
|
5106
5106
|
return null;
|
|
5107
5107
|
}
|
|
5108
5108
|
function sleep2(ms, signal) {
|
|
5109
|
-
return new Promise((
|
|
5109
|
+
return new Promise((resolve22, reject) => {
|
|
5110
5110
|
if (signal?.aborted) return reject(new DOMException("aborted", "AbortError"));
|
|
5111
5111
|
const t = setTimeout(() => {
|
|
5112
5112
|
signal?.removeEventListener("abort", onAbort);
|
|
5113
|
-
|
|
5113
|
+
resolve22();
|
|
5114
5114
|
}, ms);
|
|
5115
5115
|
const onAbort = () => {
|
|
5116
5116
|
clearTimeout(t);
|
|
@@ -5260,7 +5260,7 @@ var init_worker_registry = __esm({
|
|
|
5260
5260
|
delivered: false
|
|
5261
5261
|
};
|
|
5262
5262
|
if (done.background) this.finished.set(info.id, done);
|
|
5263
|
-
for (const
|
|
5263
|
+
for (const resolve22 of this.waiters.get(info.id) ?? []) resolve22(done);
|
|
5264
5264
|
this.waiters.delete(info.id);
|
|
5265
5265
|
for (const l of this.finishListeners) {
|
|
5266
5266
|
try {
|
|
@@ -5292,14 +5292,14 @@ var init_worker_registry = __esm({
|
|
|
5292
5292
|
if (!found) return Promise.reject(new Error(`No subagent ${ref}`));
|
|
5293
5293
|
if (found.finished) return Promise.resolve(found.finished);
|
|
5294
5294
|
const id = found.running.id;
|
|
5295
|
-
return new Promise((
|
|
5295
|
+
return new Promise((resolve22, reject) => {
|
|
5296
5296
|
const onAbort = () => reject(new DOMException("aborted", "AbortError"));
|
|
5297
5297
|
if (signal?.aborted) return onAbort();
|
|
5298
5298
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5299
5299
|
const list = this.waiters.get(id) ?? [];
|
|
5300
5300
|
list.push((w) => {
|
|
5301
5301
|
signal?.removeEventListener("abort", onAbort);
|
|
5302
|
-
|
|
5302
|
+
resolve22(w);
|
|
5303
5303
|
});
|
|
5304
5304
|
this.waiters.set(id, list);
|
|
5305
5305
|
});
|
|
@@ -5547,7 +5547,7 @@ async function pruneFiles(files, maxAgeDays, maxCount) {
|
|
|
5547
5547
|
return removed;
|
|
5548
5548
|
}
|
|
5549
5549
|
async function rotateJsonl(path, maxBytes, rotations) {
|
|
5550
|
-
const { rename:
|
|
5550
|
+
const { rename: rename5 } = await import("fs/promises");
|
|
5551
5551
|
let s;
|
|
5552
5552
|
try {
|
|
5553
5553
|
s = await stat(path);
|
|
@@ -5559,7 +5559,7 @@ async function rotateJsonl(path, maxBytes, rotations) {
|
|
|
5559
5559
|
const src = i === 1 ? path : `${path}.${i - 1}`;
|
|
5560
5560
|
const dst = `${path}.${i}`;
|
|
5561
5561
|
try {
|
|
5562
|
-
await
|
|
5562
|
+
await rename5(src, dst);
|
|
5563
5563
|
} catch {
|
|
5564
5564
|
}
|
|
5565
5565
|
}
|
|
@@ -6610,7 +6610,7 @@ function truncateForEmbedding(text3) {
|
|
|
6610
6610
|
return text3.slice(0, MAX_EMBED_CHARS);
|
|
6611
6611
|
}
|
|
6612
6612
|
async function sleep3(ms) {
|
|
6613
|
-
return new Promise((
|
|
6613
|
+
return new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
6614
6614
|
}
|
|
6615
6615
|
async function fetchWithRetry(url, init, retries = 3) {
|
|
6616
6616
|
let lastError;
|
|
@@ -6619,8 +6619,8 @@ async function fetchWithRetry(url, init, retries = 3) {
|
|
|
6619
6619
|
const res = await fetch(url, init);
|
|
6620
6620
|
if (res.ok) return res;
|
|
6621
6621
|
if (res.status === 429 || res.status >= 500) {
|
|
6622
|
-
const
|
|
6623
|
-
await sleep3(
|
|
6622
|
+
const delay2 = 1e3 * 2 ** i;
|
|
6623
|
+
await sleep3(delay2);
|
|
6624
6624
|
continue;
|
|
6625
6625
|
}
|
|
6626
6626
|
const errText = await res.text().catch(() => "unknown error");
|
|
@@ -8050,15 +8050,15 @@ async function gitNumstat(cwd, opts2 = {}) {
|
|
|
8050
8050
|
if (opts2.includeUntracked) {
|
|
8051
8051
|
try {
|
|
8052
8052
|
const { stdout } = await execFileAsync("git", ["ls-files", "--others", "--exclude-standard"], { cwd, timeout: 5e3 });
|
|
8053
|
-
const { readFile:
|
|
8054
|
-
const { join:
|
|
8053
|
+
const { readFile: readFile42, stat: stat15 } = await import("fs/promises");
|
|
8054
|
+
const { join: join64 } = await import("path");
|
|
8055
8055
|
for (const file of stdout.split("\n").filter(Boolean)) {
|
|
8056
8056
|
let lines = 0;
|
|
8057
8057
|
try {
|
|
8058
|
-
const path =
|
|
8059
|
-
const size = (await
|
|
8058
|
+
const path = join64(cwd, file);
|
|
8059
|
+
const size = (await stat15(path)).size;
|
|
8060
8060
|
if (size > 0 && size <= 1024 * 1024) {
|
|
8061
|
-
const text3 = await
|
|
8061
|
+
const text3 = await readFile42(path, "utf8");
|
|
8062
8062
|
lines = text3.split("\n").length - (text3.endsWith("\n") ? 1 : 0);
|
|
8063
8063
|
}
|
|
8064
8064
|
} catch {
|
|
@@ -8177,6 +8177,146 @@ var init_git_delta = __esm({
|
|
|
8177
8177
|
}
|
|
8178
8178
|
});
|
|
8179
8179
|
|
|
8180
|
+
// src/util/line-diff.ts
|
|
8181
|
+
function splitLines(text3) {
|
|
8182
|
+
if (text3 === "") return [];
|
|
8183
|
+
const lines = text3.split("\n");
|
|
8184
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
8185
|
+
return lines;
|
|
8186
|
+
}
|
|
8187
|
+
function myers(a, b) {
|
|
8188
|
+
const n = a.length;
|
|
8189
|
+
const m = b.length;
|
|
8190
|
+
const max = n + m;
|
|
8191
|
+
if (max === 0) return [];
|
|
8192
|
+
const offset = max;
|
|
8193
|
+
let v = new Int32Array(2 * max + 2);
|
|
8194
|
+
const trace = [];
|
|
8195
|
+
for (let d = 0; d <= max; d++) {
|
|
8196
|
+
if ((n + m) * d > MAX_MYERS_COST) return null;
|
|
8197
|
+
trace.push(v.slice());
|
|
8198
|
+
const next = v;
|
|
8199
|
+
for (let k = -d; k <= d; k += 2) {
|
|
8200
|
+
let x;
|
|
8201
|
+
if (k === -d || k !== d && next[offset + k - 1] < next[offset + k + 1]) x = next[offset + k + 1];
|
|
8202
|
+
else x = next[offset + k - 1] + 1;
|
|
8203
|
+
let y = x - k;
|
|
8204
|
+
while (x < n && y < m && a[x] === b[y]) {
|
|
8205
|
+
x++;
|
|
8206
|
+
y++;
|
|
8207
|
+
}
|
|
8208
|
+
next[offset + k] = x;
|
|
8209
|
+
if (x >= n && y >= m) {
|
|
8210
|
+
trace.push(next.slice());
|
|
8211
|
+
return backtrack(trace, a, b, offset);
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
v = next;
|
|
8215
|
+
}
|
|
8216
|
+
return null;
|
|
8217
|
+
}
|
|
8218
|
+
function backtrack(trace, a, b, offset) {
|
|
8219
|
+
const ops = [];
|
|
8220
|
+
let x = a.length;
|
|
8221
|
+
let y = b.length;
|
|
8222
|
+
for (let d = trace.length - 2; d >= 0; d--) {
|
|
8223
|
+
const v = trace[d];
|
|
8224
|
+
const k = x - y;
|
|
8225
|
+
let prevK;
|
|
8226
|
+
if (k === -d || k !== d && v[offset + k - 1] < v[offset + k + 1]) prevK = k + 1;
|
|
8227
|
+
else prevK = k - 1;
|
|
8228
|
+
const prevX = d === 0 ? 0 : v[offset + prevK];
|
|
8229
|
+
const prevY = prevX - prevK;
|
|
8230
|
+
while (x > prevX && y > prevY) {
|
|
8231
|
+
ops.push({ kind: " ", text: a[x - 1] });
|
|
8232
|
+
x--;
|
|
8233
|
+
y--;
|
|
8234
|
+
}
|
|
8235
|
+
if (d === 0) break;
|
|
8236
|
+
if (x === prevX) ops.push({ kind: "+", text: b[y - 1] });
|
|
8237
|
+
else ops.push({ kind: "-", text: a[x - 1] });
|
|
8238
|
+
x = prevX;
|
|
8239
|
+
y = prevY;
|
|
8240
|
+
}
|
|
8241
|
+
while (x > 0 && y > 0) {
|
|
8242
|
+
ops.push({ kind: " ", text: a[x - 1] });
|
|
8243
|
+
x--;
|
|
8244
|
+
y--;
|
|
8245
|
+
}
|
|
8246
|
+
return ops.reverse();
|
|
8247
|
+
}
|
|
8248
|
+
function editScript(a, b) {
|
|
8249
|
+
let pre = 0;
|
|
8250
|
+
while (pre < a.length && pre < b.length && a[pre] === b[pre]) pre++;
|
|
8251
|
+
let suf = 0;
|
|
8252
|
+
while (suf < a.length - pre && suf < b.length - pre && a[a.length - 1 - suf] === b[b.length - 1 - suf]) suf++;
|
|
8253
|
+
const midA = a.slice(pre, a.length - suf);
|
|
8254
|
+
const midB = b.slice(pre, b.length - suf);
|
|
8255
|
+
const mid = myers(midA, midB) ?? [
|
|
8256
|
+
...midA.map((text3) => ({ kind: "-", text: text3 })),
|
|
8257
|
+
...midB.map((text3) => ({ kind: "+", text: text3 }))
|
|
8258
|
+
];
|
|
8259
|
+
return [
|
|
8260
|
+
...a.slice(0, pre).map((text3) => ({ kind: " ", text: text3 })),
|
|
8261
|
+
...mid,
|
|
8262
|
+
...a.slice(a.length - suf).map((text3) => ({ kind: " ", text: text3 }))
|
|
8263
|
+
];
|
|
8264
|
+
}
|
|
8265
|
+
function structuredPatch(oldText, newText, context = DEFAULT_CONTEXT) {
|
|
8266
|
+
const ops = editScript(splitLines(oldText), splitLines(newText));
|
|
8267
|
+
const hunks = [];
|
|
8268
|
+
let oldNo = 1;
|
|
8269
|
+
let newNo = 1;
|
|
8270
|
+
const pos = ops.map((op) => {
|
|
8271
|
+
const at = { old: oldNo, new: newNo };
|
|
8272
|
+
if (op.kind !== "+") oldNo++;
|
|
8273
|
+
if (op.kind !== "-") newNo++;
|
|
8274
|
+
return at;
|
|
8275
|
+
});
|
|
8276
|
+
let i = 0;
|
|
8277
|
+
while (i < ops.length) {
|
|
8278
|
+
if (ops[i].kind === " ") {
|
|
8279
|
+
i++;
|
|
8280
|
+
continue;
|
|
8281
|
+
}
|
|
8282
|
+
const start = Math.max(0, i - context);
|
|
8283
|
+
let end = i;
|
|
8284
|
+
for (; ; ) {
|
|
8285
|
+
while (end < ops.length && ops[end].kind !== " ") end++;
|
|
8286
|
+
let gap = end;
|
|
8287
|
+
while (gap < ops.length && ops[gap].kind === " ") gap++;
|
|
8288
|
+
if (gap < ops.length && gap - end <= 2 * context) {
|
|
8289
|
+
end = gap;
|
|
8290
|
+
continue;
|
|
8291
|
+
}
|
|
8292
|
+
end = Math.min(ops.length, end + context);
|
|
8293
|
+
break;
|
|
8294
|
+
}
|
|
8295
|
+
const slice = ops.slice(start, end);
|
|
8296
|
+
const oldLines = slice.filter((o) => o.kind !== "+").length;
|
|
8297
|
+
const newLines = slice.filter((o) => o.kind !== "-").length;
|
|
8298
|
+
const first = pos[start];
|
|
8299
|
+
hunks.push({
|
|
8300
|
+
// jsdiff's convention: a hunk with no old (or new) lines starts one line earlier.
|
|
8301
|
+
oldStart: oldLines === 0 ? first.old - 1 : first.old,
|
|
8302
|
+
oldLines,
|
|
8303
|
+
newStart: newLines === 0 ? first.new - 1 : first.new,
|
|
8304
|
+
newLines,
|
|
8305
|
+
lines: slice.map((o) => o.kind + o.text)
|
|
8306
|
+
});
|
|
8307
|
+
i = end;
|
|
8308
|
+
}
|
|
8309
|
+
return hunks;
|
|
8310
|
+
}
|
|
8311
|
+
var DEFAULT_CONTEXT, MAX_MYERS_COST;
|
|
8312
|
+
var init_line_diff = __esm({
|
|
8313
|
+
"src/util/line-diff.ts"() {
|
|
8314
|
+
"use strict";
|
|
8315
|
+
DEFAULT_CONTEXT = 3;
|
|
8316
|
+
MAX_MYERS_COST = 4e6;
|
|
8317
|
+
}
|
|
8318
|
+
});
|
|
8319
|
+
|
|
8180
8320
|
// src/hooks/claude-compat.ts
|
|
8181
8321
|
function claudeToolName(tool, args) {
|
|
8182
8322
|
if (tool === "edit" && Array.isArray(args.edits)) return "MultiEdit";
|
|
@@ -8261,9 +8401,35 @@ function claudeToolResponse(tool, args, cwd, result2) {
|
|
|
8261
8401
|
}
|
|
8262
8402
|
if ((tool === "write" || tool === "edit") && isStr(args.path) && !failed) {
|
|
8263
8403
|
response.filePath = resolvePath(cwd, args.path);
|
|
8404
|
+
if (result2.fileChange) Object.assign(response, claudeFileChangeFields(tool, args, result2.fileChange));
|
|
8264
8405
|
}
|
|
8265
8406
|
return response;
|
|
8266
8407
|
}
|
|
8408
|
+
function claudeFileChangeFields(tool, args, change) {
|
|
8409
|
+
const fields = {};
|
|
8410
|
+
if (tool === "write") {
|
|
8411
|
+
fields.type = change.existed ? "update" : "create";
|
|
8412
|
+
if (isStr(args.content)) fields.content = args.content;
|
|
8413
|
+
} else if (Array.isArray(args.edits)) {
|
|
8414
|
+
fields.edits = editPairs(args);
|
|
8415
|
+
} else {
|
|
8416
|
+
fields.oldString = args.old_string;
|
|
8417
|
+
fields.newString = args.new_string;
|
|
8418
|
+
fields.replaceAll = args.replace_all === true;
|
|
8419
|
+
}
|
|
8420
|
+
if (!change.existed) {
|
|
8421
|
+
fields.originalFile = null;
|
|
8422
|
+
fields.structuredPatch = [];
|
|
8423
|
+
} else {
|
|
8424
|
+
if (change.original !== null && Buffer.byteLength(change.original, "utf8") <= ORIGINAL_FILE_CAP_BYTES) {
|
|
8425
|
+
fields.originalFile = change.original;
|
|
8426
|
+
}
|
|
8427
|
+
if (change.original !== null && change.updated !== null) {
|
|
8428
|
+
fields.structuredPatch = structuredPatch(change.original, change.updated);
|
|
8429
|
+
}
|
|
8430
|
+
}
|
|
8431
|
+
return fields;
|
|
8432
|
+
}
|
|
8267
8433
|
function claudeError(result2) {
|
|
8268
8434
|
if (isNum(result2.exitCode) && result2.exitCode !== 0) {
|
|
8269
8435
|
const output = [result2.stdout, result2.stderr].filter((s) => s && s.trim()).map((s) => s.trimEnd()).join("\n");
|
|
@@ -8296,12 +8462,13 @@ function claudeToolFields(payload) {
|
|
|
8296
8462
|
}
|
|
8297
8463
|
return { tool_name: toolName, ...fields };
|
|
8298
8464
|
}
|
|
8299
|
-
var CLAUDE_TOOL_NAMES, FILE_EDIT_NAMES, isStr, isNum;
|
|
8465
|
+
var CLAUDE_TOOL_NAMES, FILE_EDIT_NAMES, isStr, isNum, ORIGINAL_FILE_CAP_BYTES;
|
|
8300
8466
|
var init_claude_compat = __esm({
|
|
8301
8467
|
"src/hooks/claude-compat.ts"() {
|
|
8302
8468
|
"use strict";
|
|
8303
8469
|
init_edit();
|
|
8304
8470
|
init_paths();
|
|
8471
|
+
init_line_diff();
|
|
8305
8472
|
CLAUDE_TOOL_NAMES = {
|
|
8306
8473
|
bash: "Bash",
|
|
8307
8474
|
edit: "Edit",
|
|
@@ -8316,6 +8483,7 @@ var init_claude_compat = __esm({
|
|
|
8316
8483
|
FILE_EDIT_NAMES = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
|
|
8317
8484
|
isStr = (v) => typeof v === "string";
|
|
8318
8485
|
isNum = (v) => typeof v === "number";
|
|
8486
|
+
ORIGINAL_FILE_CAP_BYTES = 256 * 1024;
|
|
8319
8487
|
}
|
|
8320
8488
|
});
|
|
8321
8489
|
|
|
@@ -10125,8 +10293,8 @@ function applyConfirmedCost(log2, session, turn, snapshot) {
|
|
|
10125
10293
|
session.gatewayLogs = logs.slice(-100);
|
|
10126
10294
|
}
|
|
10127
10295
|
async function reconcileTurnCost(sessionId, turnId, lookup, fetchImpl2 = fetch, delaysMs = RECONCILE_DELAYS_MS) {
|
|
10128
|
-
for (const
|
|
10129
|
-
await new Promise((r) => setTimeout(r,
|
|
10296
|
+
for (const delay2 of delaysMs) {
|
|
10297
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
10130
10298
|
let snapshot;
|
|
10131
10299
|
try {
|
|
10132
10300
|
snapshot = await fetchGenerationSnapshot(lookup, fetchImpl2);
|
|
@@ -10606,15 +10774,15 @@ function makeErrorOutput(message2) {
|
|
|
10606
10774
|
return { content: message2, rawBytes: bytes, reducedBytes: bytes };
|
|
10607
10775
|
}
|
|
10608
10776
|
async function execOnce(command, cwd) {
|
|
10609
|
-
return new Promise((
|
|
10777
|
+
return new Promise((resolve22) => {
|
|
10610
10778
|
const child = spawn("bash", ["-lc", command], { cwd });
|
|
10611
10779
|
let out = "";
|
|
10612
10780
|
child.stdout.on("data", (d) => {
|
|
10613
10781
|
out += d.toString("utf8");
|
|
10614
10782
|
});
|
|
10615
10783
|
child.on("close", (code) => {
|
|
10616
|
-
if (code !== 0) return
|
|
10617
|
-
|
|
10784
|
+
if (code !== 0) return resolve22(void 0);
|
|
10785
|
+
resolve22(out.trim() || void 0);
|
|
10618
10786
|
});
|
|
10619
10787
|
});
|
|
10620
10788
|
}
|
|
@@ -10768,7 +10936,7 @@ async function runBash(args, ctx) {
|
|
|
10768
10936
|
if (pushGuard) return pushGuard;
|
|
10769
10937
|
const { shell, args: shellArgs, isPosix } = getShellCommand(ctx.shell);
|
|
10770
10938
|
const { command, env: coauthorEnv } = isPosix ? injectCoauthor(args.command, ctx.coauthor) : { command: args.command, env: {} };
|
|
10771
|
-
return new Promise((
|
|
10939
|
+
return new Promise((resolve22, reject) => {
|
|
10772
10940
|
logger.debug("bash:spawn", { command: args.command.slice(0, 200), cwd: ctx.cwd, shell });
|
|
10773
10941
|
const child = spawn(shell, [...shellArgs, command], {
|
|
10774
10942
|
cwd: ctx.cwd,
|
|
@@ -10833,7 +11001,7 @@ ${stderr}`
|
|
|
10833
11001
|
if (hint) parts.push(hint);
|
|
10834
11002
|
}
|
|
10835
11003
|
const raw = parts.join("\n");
|
|
10836
|
-
|
|
11004
|
+
resolve22({
|
|
10837
11005
|
content: raw,
|
|
10838
11006
|
rawBytes: Buffer.byteLength(raw, "utf8"),
|
|
10839
11007
|
reducedBytes: Buffer.byteLength(raw, "utf8"),
|
|
@@ -12231,7 +12399,7 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
12231
12399
|
import { join as join22, dirname as dirname9 } from "path";
|
|
12232
12400
|
async function autoScroll(page) {
|
|
12233
12401
|
await page.evaluate(async () => {
|
|
12234
|
-
await new Promise((
|
|
12402
|
+
await new Promise((resolve22) => {
|
|
12235
12403
|
let totalHeight = 0;
|
|
12236
12404
|
const distance = 300;
|
|
12237
12405
|
const timer2 = setInterval(() => {
|
|
@@ -12240,12 +12408,12 @@ async function autoScroll(page) {
|
|
|
12240
12408
|
totalHeight += distance;
|
|
12241
12409
|
if (totalHeight >= scrollHeight) {
|
|
12242
12410
|
clearInterval(timer2);
|
|
12243
|
-
|
|
12411
|
+
resolve22();
|
|
12244
12412
|
}
|
|
12245
12413
|
}, 100);
|
|
12246
12414
|
setTimeout(() => {
|
|
12247
12415
|
clearInterval(timer2);
|
|
12248
|
-
|
|
12416
|
+
resolve22();
|
|
12249
12417
|
}, 1e4);
|
|
12250
12418
|
});
|
|
12251
12419
|
});
|
|
@@ -14494,8 +14662,8 @@ function waitForProcessStartTime(pid, jobId, exitPath) {
|
|
|
14494
14662
|
function readProcessStartTime(pid) {
|
|
14495
14663
|
if (process.platform === "linux") {
|
|
14496
14664
|
try {
|
|
14497
|
-
const
|
|
14498
|
-
const fieldsAfterCommand =
|
|
14665
|
+
const stat15 = readFileSync6(`/proc/${pid}/stat`, "utf8");
|
|
14666
|
+
const fieldsAfterCommand = stat15.slice(stat15.lastIndexOf(")") + 1).trim().split(/\s+/);
|
|
14499
14667
|
return fieldsAfterCommand[19] ? `linux:${fieldsAfterCommand[19]}` : null;
|
|
14500
14668
|
} catch {
|
|
14501
14669
|
return null;
|
|
@@ -15501,8 +15669,8 @@ var init_store = __esm({
|
|
|
15501
15669
|
if (timer2.attempts >= timer2.maxAttempts) {
|
|
15502
15670
|
this.db.prepare("UPDATE run_timers SET status = 'failed', lease_until = NULL, last_error = ? WHERE id = ?").run(error.slice(0, 1e3), id);
|
|
15503
15671
|
} else {
|
|
15504
|
-
const
|
|
15505
|
-
this.db.prepare("UPDATE run_timers SET status = 'scheduled', wake_at = ?, lease_until = NULL, last_error = ? WHERE id = ?").run(now2 +
|
|
15672
|
+
const delay2 = Math.min(MAX_RETRY_MS, DEFAULT_RETRY_MS * 2 ** Math.max(0, timer2.attempts - 1));
|
|
15673
|
+
this.db.prepare("UPDATE run_timers SET status = 'scheduled', wake_at = ?, lease_until = NULL, last_error = ? WHERE id = ?").run(now2 + delay2, error.slice(0, 1e3), id);
|
|
15506
15674
|
}
|
|
15507
15675
|
});
|
|
15508
15676
|
}
|
|
@@ -15675,7 +15843,7 @@ var init_wait_for = __esm({
|
|
|
15675
15843
|
});
|
|
15676
15844
|
|
|
15677
15845
|
// src/tools/executor.ts
|
|
15678
|
-
import { realpath as realpath4 } from "fs/promises";
|
|
15846
|
+
import { readFile as readFile17, realpath as realpath4, stat as stat7 } from "fs/promises";
|
|
15679
15847
|
import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5, sep as sep3 } from "path";
|
|
15680
15848
|
function getWorkerTools(profile) {
|
|
15681
15849
|
return profile === "research" ? ALL_TOOLS.filter((tool) => RESEARCH_WORKER_TOOL_NAMES.has(tool.name) && tool.isReadOnly === true).map(restrictResearchTool) : ALL_TOOLS;
|
|
@@ -15744,6 +15912,16 @@ function toPermissionResult(d) {
|
|
|
15744
15912
|
}
|
|
15745
15913
|
return d;
|
|
15746
15914
|
}
|
|
15915
|
+
async function snapshotForHook(abs) {
|
|
15916
|
+
try {
|
|
15917
|
+
const st = await stat7(abs);
|
|
15918
|
+
if (!st.isFile()) return { existed: true, content: null };
|
|
15919
|
+
if (st.size > HOOK_FILE_SNAPSHOT_MAX_BYTES) return { existed: true, content: null };
|
|
15920
|
+
return { existed: true, content: await readFile17(abs, "utf8") };
|
|
15921
|
+
} catch {
|
|
15922
|
+
return { existed: false, content: null };
|
|
15923
|
+
}
|
|
15924
|
+
}
|
|
15747
15925
|
function shellResultForHook(meta) {
|
|
15748
15926
|
if (!meta || meta.exitCode === void 0) return {};
|
|
15749
15927
|
return {
|
|
@@ -15762,6 +15940,15 @@ function capContentForHook(s) {
|
|
|
15762
15940
|
return `${cut}
|
|
15763
15941
|
[\u2026truncated for hook payload]`;
|
|
15764
15942
|
}
|
|
15943
|
+
async function fileChangeAfter(tool, args, cwd, before) {
|
|
15944
|
+
let updated = null;
|
|
15945
|
+
if (tool === "write" && typeof args.content === "string") {
|
|
15946
|
+
updated = Buffer.byteLength(args.content, "utf8") <= HOOK_FILE_SNAPSHOT_MAX_BYTES ? args.content : null;
|
|
15947
|
+
} else {
|
|
15948
|
+
updated = (await snapshotForHook(resolvePath(cwd, args.path))).content;
|
|
15949
|
+
}
|
|
15950
|
+
return { existed: before.existed, original: before.content, updated };
|
|
15951
|
+
}
|
|
15765
15952
|
function tracePermissionDecision(result2) {
|
|
15766
15953
|
if (result2.decision === "deny") return "deny";
|
|
15767
15954
|
if (result2.scope === "session") return "allow_session";
|
|
@@ -15785,7 +15972,7 @@ function normalizeToolOutput(result2) {
|
|
|
15785
15972
|
}
|
|
15786
15973
|
return result2;
|
|
15787
15974
|
}
|
|
15788
|
-
var ALL_TOOLS, RESEARCH_WORKER_TOOL_NAMES, SENSITIVE_RESEARCH_PATHS, HOOK_RESULT_CONTENT_CAP_BYTES, ToolExecutor, TRACE_ARGS_INLINE_MAX, ToolTrace;
|
|
15975
|
+
var ALL_TOOLS, RESEARCH_WORKER_TOOL_NAMES, SENSITIVE_RESEARCH_PATHS, HOOK_RESULT_CONTENT_CAP_BYTES, HOOK_FILE_SNAPSHOT_MAX_BYTES, ToolExecutor, TRACE_ARGS_INLINE_MAX, ToolTrace;
|
|
15789
15976
|
var init_executor = __esm({
|
|
15790
15977
|
"src/tools/executor.ts"() {
|
|
15791
15978
|
"use strict";
|
|
@@ -15815,6 +16002,7 @@ var init_executor = __esm({
|
|
|
15815
16002
|
init_jobs();
|
|
15816
16003
|
init_wait_for();
|
|
15817
16004
|
init_workers();
|
|
16005
|
+
init_paths();
|
|
15818
16006
|
init_store();
|
|
15819
16007
|
ALL_TOOLS = [
|
|
15820
16008
|
{ ...readTool, isReadOnly: true },
|
|
@@ -15869,6 +16057,7 @@ var init_executor = __esm({
|
|
|
15869
16057
|
".git-credentials"
|
|
15870
16058
|
]);
|
|
15871
16059
|
HOOK_RESULT_CONTENT_CAP_BYTES = 4 * 1024;
|
|
16060
|
+
HOOK_FILE_SNAPSHOT_MAX_BYTES = 1024 * 1024;
|
|
15872
16061
|
ToolExecutor = class {
|
|
15873
16062
|
sessionAllowed = /* @__PURE__ */ new Set();
|
|
15874
16063
|
tools;
|
|
@@ -16046,8 +16235,10 @@ var init_executor = __esm({
|
|
|
16046
16235
|
runStore.recordToolIntent(ctx.runId, call.id, call.name, args);
|
|
16047
16236
|
intentRecorded = true;
|
|
16048
16237
|
}
|
|
16238
|
+
const before = this.wantsFileChange(call.name, args) ? await snapshotForHook(resolvePath(ctx.cwd, args.path)) : void 0;
|
|
16049
16239
|
const result2 = await tool.run(args, ctx);
|
|
16050
16240
|
toolCompleted = true;
|
|
16241
|
+
const fileChange = before ? await fileChangeAfter(call.name, args, ctx.cwd, before) : void 0;
|
|
16051
16242
|
const normalized = normalizeToolOutput(result2);
|
|
16052
16243
|
tr?.raw(normalized);
|
|
16053
16244
|
if (runStore && ctx.runId) {
|
|
@@ -16099,7 +16290,7 @@ var init_executor = __esm({
|
|
|
16099
16290
|
artifactId: reduced.artifactId,
|
|
16100
16291
|
...normalized.waitRequest ? { waitRequest: normalized.waitRequest } : {}
|
|
16101
16292
|
};
|
|
16102
|
-
this.firePostToolUse(call, args, success, ctx, normalized.meta);
|
|
16293
|
+
this.firePostToolUse(call, args, success, ctx, normalized.meta, fileChange);
|
|
16103
16294
|
return success;
|
|
16104
16295
|
} catch (e) {
|
|
16105
16296
|
if (e instanceof RunToolBudgetExceededError) ctx.onRunBudgetExceeded?.(e.reason);
|
|
@@ -16170,7 +16361,7 @@ var init_executor = __esm({
|
|
|
16170
16361
|
* `content` field is capped via `capContentForHook` to keep large tool
|
|
16171
16362
|
* outputs from blowing the OS env-var limits on `KIMIFLARE_HOOK_PAYLOAD`.
|
|
16172
16363
|
*/
|
|
16173
|
-
firePostToolUse(call, args, result2, ctx, meta) {
|
|
16364
|
+
firePostToolUse(call, args, result2, ctx, meta, fileChange) {
|
|
16174
16365
|
const hooks = this.hooks;
|
|
16175
16366
|
if (!hooks) return;
|
|
16176
16367
|
const post = hooks.hasEnabledHooks("PostToolUse");
|
|
@@ -16180,7 +16371,8 @@ var init_executor = __esm({
|
|
|
16180
16371
|
ok: result2.ok,
|
|
16181
16372
|
content: capContentForHook(result2.content),
|
|
16182
16373
|
errorCode: result2.errorCode,
|
|
16183
|
-
...shellResultForHook(meta)
|
|
16374
|
+
...shellResultForHook(meta),
|
|
16375
|
+
...fileChange ? { fileChange } : {}
|
|
16184
16376
|
};
|
|
16185
16377
|
const base = {
|
|
16186
16378
|
session_id: ctx.sessionId ?? null,
|
|
@@ -16198,6 +16390,11 @@ var init_executor = __esm({
|
|
|
16198
16390
|
void hooks.fire("PostToolUseFailure", { event: "PostToolUseFailure", ...base, error: claudeError(resultFields) }, call.name, ctx.signal).catch(swallow2);
|
|
16199
16391
|
}
|
|
16200
16392
|
}
|
|
16393
|
+
/** True when a PostToolUse hook would get this write / edit call's diff. */
|
|
16394
|
+
wantsFileChange(tool, args) {
|
|
16395
|
+
if (tool !== "write" && tool !== "edit" || typeof args.path !== "string" || !args.path) return false;
|
|
16396
|
+
return !!this.hooks && (this.hooks.hasEnabledHooks("PostToolUse") || this.hooks.hasEnabledHooks("PostToolUseFailure"));
|
|
16397
|
+
}
|
|
16201
16398
|
permissionKey(tool, args) {
|
|
16202
16399
|
if (tool.name === "bash" && typeof args.command === "string") {
|
|
16203
16400
|
const firstToken = args.command.trim().split(/\s+/)[0] ?? "";
|
|
@@ -16324,8 +16521,106 @@ var init_executor = __esm({
|
|
|
16324
16521
|
}
|
|
16325
16522
|
});
|
|
16326
16523
|
|
|
16524
|
+
// src/intent/classify.ts
|
|
16525
|
+
function classifyIntent(prompt) {
|
|
16526
|
+
let intentScore = 0;
|
|
16527
|
+
let matchedIntent = "other";
|
|
16528
|
+
for (const [intent, pattern] of Object.entries(INTENT_PATTERNS)) {
|
|
16529
|
+
const matches = (prompt.match(pattern) || []).length;
|
|
16530
|
+
if (matches > intentScore) {
|
|
16531
|
+
intentScore = matches;
|
|
16532
|
+
matchedIntent = intent;
|
|
16533
|
+
}
|
|
16534
|
+
}
|
|
16535
|
+
const hasFileMentions = (prompt.match(/@\w+|\b[\w/-]+\.(ts|tsx|js|jsx|py|go|rs)\b/g) || []).length;
|
|
16536
|
+
const hasMutatingVerb = /\b(add|create|write|edit|delete|remove|rename|migrate|implement)\b/i.test(prompt);
|
|
16537
|
+
const isQuestion = prompt.trim().endsWith("?") || /\b(what|how|why|is|does|can)\b/i.test(prompt.split(" ")[0] || "");
|
|
16538
|
+
const hasHeavySignal = HEAVY_SIGNAL.test(prompt);
|
|
16539
|
+
const rawScore = Math.min(
|
|
16540
|
+
1,
|
|
16541
|
+
intentScore * 0.25 + (hasFileMentions > 2 ? 0.3 : hasFileMentions * 0.1) + (hasMutatingVerb ? 0.25 : 0) + (isQuestion ? 0 : 0.1) + (hasHeavySignal ? 0.6 : 0)
|
|
16542
|
+
);
|
|
16543
|
+
const tier = rawScore < 0.3 ? "light" : rawScore < 0.65 ? "medium" : "heavy";
|
|
16544
|
+
return {
|
|
16545
|
+
intent: hasHeavySignal && matchedIntent === "other" ? "explore" : matchedIntent,
|
|
16546
|
+
rawScore,
|
|
16547
|
+
tier,
|
|
16548
|
+
confidence: 0.5 + (intentScore > 0 || hasHeavySignal ? 0.3 : 0) + (hasFileMentions > 0 ? 0.1 : 0)
|
|
16549
|
+
};
|
|
16550
|
+
}
|
|
16551
|
+
var INTENT_PATTERNS, HEAVY_SIGNAL;
|
|
16552
|
+
var init_classify = __esm({
|
|
16553
|
+
"src/intent/classify.ts"() {
|
|
16554
|
+
"use strict";
|
|
16555
|
+
INTENT_PATTERNS = {
|
|
16556
|
+
qa: /\b(what|how|why|explain|describe|what's|what is)\b/i,
|
|
16557
|
+
diagnose: /\b(broken|failing|error|bug|crash|why.*fail|not working)\b/i,
|
|
16558
|
+
verify: /\b(correct|right|verify|review|check|is this|does this)\b/i,
|
|
16559
|
+
polish: /\b(rename|refactor|extract|move|clean|lint|format)\b/i,
|
|
16560
|
+
small_edit: /\b(add|change|update|fix|remove|delete)\b.+\b(line|here|this|variable|function)\b/i,
|
|
16561
|
+
feature_bounded: /\b(add|implement|create|support)\b.+\b(flag|option|param|arg|field)\b/i,
|
|
16562
|
+
feature_exploratory: /\b(add|implement|migrate|integrate|build)\b.+\b(module|system|auth|oauth|framework|service)\b/i,
|
|
16563
|
+
explore: /\b(how.*work|architecture|structure|where.*used|find.*all|understand)\b/i,
|
|
16564
|
+
meta: /\b(plan|design|strategy|ontology|roadmap|approach)\b/i
|
|
16565
|
+
};
|
|
16566
|
+
HEAVY_SIGNAL = /\b(research|investigat\w*|audit|deep[\s-]?(dive|thinking)|explorat\w*|comprehensive|end[\s-]?to[\s-]?end|entire\s+(code\s?base|repo\w*|project|app)|whole\s+(code\s?base|repo\w*|project|app)|across\s+the\s+(code\s?base|repo\w*|project)|security|vulnerab\w*|loophole|exploit\w*|threat\s?model|attack\s+surface|penetration)\b/i;
|
|
16567
|
+
}
|
|
16568
|
+
});
|
|
16569
|
+
|
|
16570
|
+
// src/hooks/prompt-submit.ts
|
|
16571
|
+
async function submitUserPrompt(info) {
|
|
16572
|
+
let provider;
|
|
16573
|
+
try {
|
|
16574
|
+
provider = info.auth ? providerPresetOf(info.auth).id : void 0;
|
|
16575
|
+
} catch {
|
|
16576
|
+
provider = void 0;
|
|
16577
|
+
}
|
|
16578
|
+
await noteSessionStart({
|
|
16579
|
+
...info.hooks ? { hooks: info.hooks } : {},
|
|
16580
|
+
sessionId: info.sessionId,
|
|
16581
|
+
cwd: info.cwd,
|
|
16582
|
+
resumed: info.resumed,
|
|
16583
|
+
model: info.model,
|
|
16584
|
+
...provider ? { provider } : {}
|
|
16585
|
+
});
|
|
16586
|
+
if (!info.hooks?.hasEnabledHooks("UserPromptSubmit")) return { blocked: false };
|
|
16587
|
+
const outcome = await info.hooks.fire(
|
|
16588
|
+
"UserPromptSubmit",
|
|
16589
|
+
{
|
|
16590
|
+
event: "UserPromptSubmit",
|
|
16591
|
+
session_id: info.sessionId,
|
|
16592
|
+
cwd: info.cwd,
|
|
16593
|
+
prompt: info.prompt,
|
|
16594
|
+
tier: info.tier ?? classifyIntent(info.prompt).tier
|
|
16595
|
+
},
|
|
16596
|
+
null
|
|
16597
|
+
);
|
|
16598
|
+
if (!outcome.vetoed) return { blocked: false };
|
|
16599
|
+
return { blocked: true, reason: outcome.vetoReason || PROMPT_BLOCKED_DEFAULT_REASON };
|
|
16600
|
+
}
|
|
16601
|
+
var PROMPT_BLOCKED_EXIT_CODE, PROMPT_BLOCKED_DEFAULT_REASON, PromptBlockedError;
|
|
16602
|
+
var init_prompt_submit = __esm({
|
|
16603
|
+
"src/hooks/prompt-submit.ts"() {
|
|
16604
|
+
"use strict";
|
|
16605
|
+
init_session_lifecycle();
|
|
16606
|
+
init_classify();
|
|
16607
|
+
init_llm_auth();
|
|
16608
|
+
PROMPT_BLOCKED_EXIT_CODE = 44;
|
|
16609
|
+
PROMPT_BLOCKED_DEFAULT_REASON = "UserPromptSubmit hook blocked the prompt";
|
|
16610
|
+
PromptBlockedError = class extends Error {
|
|
16611
|
+
reason;
|
|
16612
|
+
code = "prompt_blocked";
|
|
16613
|
+
constructor(reason) {
|
|
16614
|
+
super(`prompt blocked by a UserPromptSubmit hook: ${reason}`);
|
|
16615
|
+
this.name = "PromptBlockedError";
|
|
16616
|
+
this.reason = reason;
|
|
16617
|
+
}
|
|
16618
|
+
};
|
|
16619
|
+
}
|
|
16620
|
+
});
|
|
16621
|
+
|
|
16327
16622
|
// src/util/image.ts
|
|
16328
|
-
import { readFile as
|
|
16623
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
16329
16624
|
import { basename as basename3 } from "path";
|
|
16330
16625
|
import jpeg from "jpeg-js";
|
|
16331
16626
|
import { PNG } from "pngjs";
|
|
@@ -16404,7 +16699,7 @@ function imageNote(path, change) {
|
|
|
16404
16699
|
return `[Attached image ${basename3(path)} was downscaled to ${change.width}\xD7${change.height} for upload; the full-resolution original is at ${path}]`;
|
|
16405
16700
|
}
|
|
16406
16701
|
async function encodeImageFile(filePath, opts2 = {}) {
|
|
16407
|
-
const buf = await
|
|
16702
|
+
const buf = await readFile18(filePath);
|
|
16408
16703
|
if (buf.byteLength > MAX_IMAGE_BYTES) {
|
|
16409
16704
|
throw new Error(
|
|
16410
16705
|
`image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
|
|
@@ -16448,7 +16743,7 @@ var init_image = __esm({
|
|
|
16448
16743
|
});
|
|
16449
16744
|
|
|
16450
16745
|
// src/util/attachments.ts
|
|
16451
|
-
import { readFile as
|
|
16746
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
16452
16747
|
import { basename as basename4 } from "path";
|
|
16453
16748
|
async function buildAttachmentMessage(prompt, files, cwd, imageOptions2 = {}) {
|
|
16454
16749
|
let text3 = prompt;
|
|
@@ -16471,7 +16766,7 @@ async function buildAttachmentMessage(prompt, files, cwd, imageOptions2 = {}) {
|
|
|
16471
16766
|
}
|
|
16472
16767
|
} else {
|
|
16473
16768
|
try {
|
|
16474
|
-
const content = await
|
|
16769
|
+
const content = await readFile19(filePath, "utf8");
|
|
16475
16770
|
const relPath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
|
|
16476
16771
|
fileContents.push(`
|
|
16477
16772
|
--- ${relPath} ---
|
|
@@ -16571,52 +16866,6 @@ var init_permissions_evaluator = __esm({
|
|
|
16571
16866
|
}
|
|
16572
16867
|
});
|
|
16573
16868
|
|
|
16574
|
-
// src/intent/classify.ts
|
|
16575
|
-
function classifyIntent(prompt) {
|
|
16576
|
-
let intentScore = 0;
|
|
16577
|
-
let matchedIntent = "other";
|
|
16578
|
-
for (const [intent, pattern] of Object.entries(INTENT_PATTERNS)) {
|
|
16579
|
-
const matches = (prompt.match(pattern) || []).length;
|
|
16580
|
-
if (matches > intentScore) {
|
|
16581
|
-
intentScore = matches;
|
|
16582
|
-
matchedIntent = intent;
|
|
16583
|
-
}
|
|
16584
|
-
}
|
|
16585
|
-
const hasFileMentions = (prompt.match(/@\w+|\b[\w/-]+\.(ts|tsx|js|jsx|py|go|rs)\b/g) || []).length;
|
|
16586
|
-
const hasMutatingVerb = /\b(add|create|write|edit|delete|remove|rename|migrate|implement)\b/i.test(prompt);
|
|
16587
|
-
const isQuestion = prompt.trim().endsWith("?") || /\b(what|how|why|is|does|can)\b/i.test(prompt.split(" ")[0] || "");
|
|
16588
|
-
const hasHeavySignal = HEAVY_SIGNAL.test(prompt);
|
|
16589
|
-
const rawScore = Math.min(
|
|
16590
|
-
1,
|
|
16591
|
-
intentScore * 0.25 + (hasFileMentions > 2 ? 0.3 : hasFileMentions * 0.1) + (hasMutatingVerb ? 0.25 : 0) + (isQuestion ? 0 : 0.1) + (hasHeavySignal ? 0.6 : 0)
|
|
16592
|
-
);
|
|
16593
|
-
const tier = rawScore < 0.3 ? "light" : rawScore < 0.65 ? "medium" : "heavy";
|
|
16594
|
-
return {
|
|
16595
|
-
intent: hasHeavySignal && matchedIntent === "other" ? "explore" : matchedIntent,
|
|
16596
|
-
rawScore,
|
|
16597
|
-
tier,
|
|
16598
|
-
confidence: 0.5 + (intentScore > 0 || hasHeavySignal ? 0.3 : 0) + (hasFileMentions > 0 ? 0.1 : 0)
|
|
16599
|
-
};
|
|
16600
|
-
}
|
|
16601
|
-
var INTENT_PATTERNS, HEAVY_SIGNAL;
|
|
16602
|
-
var init_classify = __esm({
|
|
16603
|
-
"src/intent/classify.ts"() {
|
|
16604
|
-
"use strict";
|
|
16605
|
-
INTENT_PATTERNS = {
|
|
16606
|
-
qa: /\b(what|how|why|explain|describe|what's|what is)\b/i,
|
|
16607
|
-
diagnose: /\b(broken|failing|error|bug|crash|why.*fail|not working)\b/i,
|
|
16608
|
-
verify: /\b(correct|right|verify|review|check|is this|does this)\b/i,
|
|
16609
|
-
polish: /\b(rename|refactor|extract|move|clean|lint|format)\b/i,
|
|
16610
|
-
small_edit: /\b(add|change|update|fix|remove|delete)\b.+\b(line|here|this|variable|function)\b/i,
|
|
16611
|
-
feature_bounded: /\b(add|implement|create|support)\b.+\b(flag|option|param|arg|field)\b/i,
|
|
16612
|
-
feature_exploratory: /\b(add|implement|migrate|integrate|build)\b.+\b(module|system|auth|oauth|framework|service)\b/i,
|
|
16613
|
-
explore: /\b(how.*work|architecture|structure|where.*used|find.*all|understand)\b/i,
|
|
16614
|
-
meta: /\b(plan|design|strategy|ontology|roadmap|approach)\b/i
|
|
16615
|
-
};
|
|
16616
|
-
HEAVY_SIGNAL = /\b(research|investigat\w*|audit|deep[\s-]?(dive|thinking)|explorat\w*|comprehensive|end[\s-]?to[\s-]?end|entire\s+(code\s?base|repo\w*|project|app)|whole\s+(code\s?base|repo\w*|project|app)|across\s+the\s+(code\s?base|repo\w*|project)|security|vulnerab\w*|loophole|exploit\w*|threat\s?model|attack\s+surface|penetration)\b/i;
|
|
16617
|
-
}
|
|
16618
|
-
});
|
|
16619
|
-
|
|
16620
16869
|
// src/agent/session-core.ts
|
|
16621
16870
|
function lastUserText(messages) {
|
|
16622
16871
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -16707,7 +16956,7 @@ __export(retention_exports, {
|
|
|
16707
16956
|
listTraceSessions: () => listTraceSessions,
|
|
16708
16957
|
pruneTraces: () => pruneTraces
|
|
16709
16958
|
});
|
|
16710
|
-
import { readdir as readdir5, rm as rm4, stat as
|
|
16959
|
+
import { readdir as readdir5, rm as rm4, stat as stat8 } from "fs/promises";
|
|
16711
16960
|
import { join as join29 } from "path";
|
|
16712
16961
|
async function dirSize(path) {
|
|
16713
16962
|
let total = 0;
|
|
@@ -16717,7 +16966,7 @@ async function dirSize(path) {
|
|
|
16717
16966
|
if (entry.isDirectory()) total += await dirSize(p);
|
|
16718
16967
|
else if (entry.isFile()) {
|
|
16719
16968
|
try {
|
|
16720
|
-
total += (await
|
|
16969
|
+
total += (await stat8(p)).size;
|
|
16721
16970
|
} catch {
|
|
16722
16971
|
}
|
|
16723
16972
|
}
|
|
@@ -16736,11 +16985,11 @@ async function listTraceSessions(root) {
|
|
|
16736
16985
|
for (const name of names) {
|
|
16737
16986
|
const path = join29(base, name);
|
|
16738
16987
|
try {
|
|
16739
|
-
const s = await
|
|
16988
|
+
const s = await stat8(path);
|
|
16740
16989
|
if (!s.isDirectory()) continue;
|
|
16741
16990
|
let mtime = s.mtime;
|
|
16742
16991
|
try {
|
|
16743
|
-
mtime = (await
|
|
16992
|
+
mtime = (await stat8(join29(path, TRACE_FILE_NAME))).mtime;
|
|
16744
16993
|
} catch {
|
|
16745
16994
|
}
|
|
16746
16995
|
sessions.push({ sessionId: name, path, mtime, bytes: await dirSize(path) });
|
|
@@ -17343,6 +17592,10 @@ function hookWirePayload(payload) {
|
|
|
17343
17592
|
if (payload.session_id) Object.assign(wire, hookTranscriptFields(payload.session_id));
|
|
17344
17593
|
if (isToolEventPayload(payload)) {
|
|
17345
17594
|
Object.assign(wire, claudeToolFields(payload));
|
|
17595
|
+
if ((payload.event === "PostToolUse" || payload.event === "PostToolUseFailure") && payload.result.fileChange) {
|
|
17596
|
+
const { fileChange: _fileChange, ...result2 } = payload.result;
|
|
17597
|
+
wire.result = result2;
|
|
17598
|
+
}
|
|
17346
17599
|
if (payload.tool_call_id) wire.tool_use_id = payload.tool_call_id;
|
|
17347
17600
|
if (payload.event === "PermissionRequest") wire.permission_mode = claudePermissionMode(payload.mode);
|
|
17348
17601
|
}
|
|
@@ -17514,7 +17767,7 @@ var init_runner = __esm({
|
|
|
17514
17767
|
DECISION_STDOUT_CAP_BYTES = 64 * 1024;
|
|
17515
17768
|
HOOK_ENV_PAYLOAD_MAX_BYTES = 32 * 1024;
|
|
17516
17769
|
HOOK_BLOCK_EXIT_CODE = 2;
|
|
17517
|
-
defaultSpawn = (command, payloadJson, env, cwd, timeoutMs, signal) => new Promise((
|
|
17770
|
+
defaultSpawn = (command, payloadJson, env, cwd, timeoutMs, signal) => new Promise((resolve22) => {
|
|
17518
17771
|
const child = spawn5(command, {
|
|
17519
17772
|
shell: true,
|
|
17520
17773
|
cwd,
|
|
@@ -17529,7 +17782,7 @@ var init_runner = __esm({
|
|
|
17529
17782
|
settled = true;
|
|
17530
17783
|
clearTimeout(timer2);
|
|
17531
17784
|
signal?.removeEventListener("abort", onAbort);
|
|
17532
|
-
|
|
17785
|
+
resolve22({ exitCode, stdout, stderr, timedOut });
|
|
17533
17786
|
};
|
|
17534
17787
|
const onAbort = () => {
|
|
17535
17788
|
child.kill("SIGTERM");
|
|
@@ -17677,7 +17930,7 @@ var init_manager2 = __esm({
|
|
|
17677
17930
|
});
|
|
17678
17931
|
|
|
17679
17932
|
// src/util/state.ts
|
|
17680
|
-
import { readFile as
|
|
17933
|
+
import { readFile as readFile20, writeFile as writeFile11, mkdir as mkdir12 } from "fs/promises";
|
|
17681
17934
|
import { homedir as homedir19 } from "os";
|
|
17682
17935
|
import { join as join32 } from "path";
|
|
17683
17936
|
function statePath() {
|
|
@@ -17686,7 +17939,7 @@ function statePath() {
|
|
|
17686
17939
|
}
|
|
17687
17940
|
async function readState() {
|
|
17688
17941
|
try {
|
|
17689
|
-
const raw = await
|
|
17942
|
+
const raw = await readFile20(statePath(), "utf8");
|
|
17690
17943
|
return JSON.parse(raw);
|
|
17691
17944
|
} catch {
|
|
17692
17945
|
return {};
|
|
@@ -17766,11 +18019,11 @@ var init_notices = __esm({
|
|
|
17766
18019
|
});
|
|
17767
18020
|
|
|
17768
18021
|
// src/eval/config.ts
|
|
17769
|
-
import { readFile as
|
|
18022
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
17770
18023
|
import { basename as basename6, resolve as resolve8 } from "path";
|
|
17771
18024
|
async function loadEvalConfig(file) {
|
|
17772
18025
|
const path = resolve8(file);
|
|
17773
|
-
const raw = JSON.parse(await
|
|
18026
|
+
const raw = JSON.parse(await readFile21(path, "utf8"));
|
|
17774
18027
|
if (!raw || typeof raw !== "object") throw new Error(`${file}: config must be a JSON object`);
|
|
17775
18028
|
const config2 = raw.config ?? {};
|
|
17776
18029
|
if (typeof config2 !== "object" || Array.isArray(config2)) throw new Error(`${file}: "config" must be an object`);
|
|
@@ -17790,7 +18043,7 @@ var init_config2 = __esm({
|
|
|
17790
18043
|
});
|
|
17791
18044
|
|
|
17792
18045
|
// src/eval/tasks.ts
|
|
17793
|
-
import { readdir as readdir6, readFile as
|
|
18046
|
+
import { readdir as readdir6, readFile as readFile22, stat as stat9 } from "fs/promises";
|
|
17794
18047
|
import { basename as basename7, join as join33, resolve as resolve9 } from "path";
|
|
17795
18048
|
function isNonEmptyString(v) {
|
|
17796
18049
|
return typeof v === "string" && v.trim().length > 0;
|
|
@@ -17825,7 +18078,7 @@ async function loadTasks(dir, filter) {
|
|
|
17825
18078
|
const seen = /* @__PURE__ */ new Set();
|
|
17826
18079
|
for (const name of names) {
|
|
17827
18080
|
const file = join33(root, name);
|
|
17828
|
-
const task = validateTask(JSON.parse(await
|
|
18081
|
+
const task = validateTask(JSON.parse(await readFile22(file, "utf8")), name);
|
|
17829
18082
|
if (task.id !== basename7(name, ".json")) throw new Error(`${name}: "id" (${task.id}) must match the file name`);
|
|
17830
18083
|
if (seen.has(task.id)) throw new Error(`duplicate task id ${task.id}`);
|
|
17831
18084
|
seen.add(task.id);
|
|
@@ -17834,7 +18087,7 @@ async function loadTasks(dir, filter) {
|
|
|
17834
18087
|
for (const [dest, src] of Object.entries(task.files ?? {})) {
|
|
17835
18088
|
const abs = resolve9(root, src);
|
|
17836
18089
|
if (!abs.startsWith(root)) throw new Error(`${name}: files["${dest}"] escapes the task directory`);
|
|
17837
|
-
await
|
|
18090
|
+
await stat9(abs).catch(() => {
|
|
17838
18091
|
throw new Error(`${name}: files["${dest}"] -> ${src} does not exist`);
|
|
17839
18092
|
});
|
|
17840
18093
|
resolvedFiles[dest] = abs;
|
|
@@ -17855,7 +18108,7 @@ var init_tasks2 = __esm({
|
|
|
17855
18108
|
// src/eval/workspace.ts
|
|
17856
18109
|
import { execFile as execFile8 } from "child_process";
|
|
17857
18110
|
import { createHash as createHash6 } from "crypto";
|
|
17858
|
-
import { mkdir as mkdir13, rm as rm5, stat as
|
|
18111
|
+
import { mkdir as mkdir13, rm as rm5, stat as stat10 } from "fs/promises";
|
|
17859
18112
|
import { isAbsolute as isAbsolute4, join as join34, resolve as resolve10 } from "path";
|
|
17860
18113
|
import { promisify as promisify7 } from "util";
|
|
17861
18114
|
async function git3(args, cwd, timeoutMs = 6e5) {
|
|
@@ -17883,7 +18136,7 @@ async function prepareSourceRepoUncached(repo, cacheDir) {
|
|
|
17883
18136
|
const slug = `${repo.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(-60)}-${createHash6("sha1").update(repo).digest("hex").slice(0, 8)}`;
|
|
17884
18137
|
const dir = join34(cacheDir, slug);
|
|
17885
18138
|
await mkdir13(cacheDir, { recursive: true });
|
|
17886
|
-
const exists = await
|
|
18139
|
+
const exists = await stat10(join34(dir, ".git")).then(() => true).catch(() => false);
|
|
17887
18140
|
if (!exists) {
|
|
17888
18141
|
await git3(["clone", "--quiet", "--no-checkout", repo, dir], cacheDir);
|
|
17889
18142
|
} else {
|
|
@@ -19155,7 +19408,7 @@ var init_connection = __esm({
|
|
|
19155
19408
|
if (this.child) {
|
|
19156
19409
|
throw new Error("LSP connection already started");
|
|
19157
19410
|
}
|
|
19158
|
-
return new Promise((
|
|
19411
|
+
return new Promise((resolve22, reject) => {
|
|
19159
19412
|
const abortController = new AbortController();
|
|
19160
19413
|
const spawnTimer = setTimeout(() => {
|
|
19161
19414
|
abortController.abort();
|
|
@@ -19194,7 +19447,7 @@ var init_connection = __esm({
|
|
|
19194
19447
|
if (child.pid) {
|
|
19195
19448
|
clearTimeout(spawnTimer);
|
|
19196
19449
|
this.child = child;
|
|
19197
|
-
|
|
19450
|
+
resolve22();
|
|
19198
19451
|
}
|
|
19199
19452
|
});
|
|
19200
19453
|
} catch (err) {
|
|
@@ -19209,12 +19462,12 @@ var init_connection = __esm({
|
|
|
19209
19462
|
}
|
|
19210
19463
|
const id = this.nextId++;
|
|
19211
19464
|
const msg = { jsonrpc: "2.0", id, method, params };
|
|
19212
|
-
return new Promise((
|
|
19465
|
+
return new Promise((resolve22, reject) => {
|
|
19213
19466
|
const timer2 = setTimeout(() => {
|
|
19214
19467
|
this.pending.delete(id);
|
|
19215
19468
|
reject(toolTimeoutError(`LSP request '${method}'`, this.requestTimeoutMs));
|
|
19216
19469
|
}, this.requestTimeoutMs);
|
|
19217
|
-
const pending = { resolve:
|
|
19470
|
+
const pending = { resolve: resolve22, reject, signal, timer: timer2 };
|
|
19218
19471
|
this.pending.set(id, pending);
|
|
19219
19472
|
if (signal) {
|
|
19220
19473
|
const onAbort = () => {
|
|
@@ -20118,7 +20371,7 @@ __export(openrouter_catalog_exports, {
|
|
|
20118
20371
|
loadOpenRouterCatalog: () => loadOpenRouterCatalog,
|
|
20119
20372
|
mapOpenRouterModel: () => mapOpenRouterModel
|
|
20120
20373
|
});
|
|
20121
|
-
import { readFile as
|
|
20374
|
+
import { readFile as readFile23, writeFile as writeFile12, mkdir as mkdir14 } from "fs/promises";
|
|
20122
20375
|
import { homedir as homedir20 } from "os";
|
|
20123
20376
|
import { join as join36, dirname as dirname17 } from "path";
|
|
20124
20377
|
function catalogCachePath() {
|
|
@@ -20174,7 +20427,7 @@ async function fetchOpenRouterCatalog(fetchImpl2 = fetch) {
|
|
|
20174
20427
|
}
|
|
20175
20428
|
async function readCache3() {
|
|
20176
20429
|
try {
|
|
20177
|
-
const raw = await
|
|
20430
|
+
const raw = await readFile23(catalogCachePath(), "utf8");
|
|
20178
20431
|
const parsed = JSON.parse(raw);
|
|
20179
20432
|
return parsed.version === CACHE_VERSION ? parsed : null;
|
|
20180
20433
|
} catch {
|
|
@@ -20233,7 +20486,7 @@ __export(requesty_catalog_exports, {
|
|
|
20233
20486
|
loadRequestyCatalog: () => loadRequestyCatalog,
|
|
20234
20487
|
mapRequestyModel: () => mapRequestyModel
|
|
20235
20488
|
});
|
|
20236
|
-
import { readFile as
|
|
20489
|
+
import { readFile as readFile24, writeFile as writeFile13, mkdir as mkdir15 } from "fs/promises";
|
|
20237
20490
|
import { homedir as homedir21 } from "os";
|
|
20238
20491
|
import { join as join37, dirname as dirname18 } from "path";
|
|
20239
20492
|
function catalogCachePath2() {
|
|
@@ -20294,7 +20547,7 @@ async function fetchRequestyCatalog(fetchImpl2 = fetch) {
|
|
|
20294
20547
|
}
|
|
20295
20548
|
async function readCache4() {
|
|
20296
20549
|
try {
|
|
20297
|
-
const raw = await
|
|
20550
|
+
const raw = await readFile24(catalogCachePath2(), "utf8");
|
|
20298
20551
|
const parsed = JSON.parse(raw);
|
|
20299
20552
|
if (parsed.version !== CACHE_VERSION2) return null;
|
|
20300
20553
|
return {
|
|
@@ -20372,7 +20625,7 @@ __export(provider_catalog_exports, {
|
|
|
20372
20625
|
mapModelsDevModel: () => mapModelsDevModel,
|
|
20373
20626
|
mapModelsDevProvider: () => mapModelsDevProvider
|
|
20374
20627
|
});
|
|
20375
|
-
import { readFile as
|
|
20628
|
+
import { readFile as readFile25, writeFile as writeFile14, mkdir as mkdir16 } from "fs/promises";
|
|
20376
20629
|
import { homedir as homedir22 } from "os";
|
|
20377
20630
|
import { join as join38, dirname as dirname19 } from "path";
|
|
20378
20631
|
function mapModelsDevModel(m) {
|
|
@@ -20412,7 +20665,7 @@ function cachePath2() {
|
|
|
20412
20665
|
}
|
|
20413
20666
|
async function readCache5() {
|
|
20414
20667
|
try {
|
|
20415
|
-
const parsed = JSON.parse(await
|
|
20668
|
+
const parsed = JSON.parse(await readFile25(cachePath2(), "utf8"));
|
|
20416
20669
|
return parsed.version === CACHE_VERSION3 ? parsed : null;
|
|
20417
20670
|
} catch {
|
|
20418
20671
|
return null;
|
|
@@ -20787,6 +21040,7 @@ var init_session = __esm({
|
|
|
20787
21040
|
"use strict";
|
|
20788
21041
|
init_image();
|
|
20789
21042
|
init_session_lifecycle();
|
|
21043
|
+
init_prompt_submit();
|
|
20790
21044
|
init_process_exit();
|
|
20791
21045
|
init_loop();
|
|
20792
21046
|
init_system_prompt();
|
|
@@ -20930,6 +21184,16 @@ var init_session = __esm({
|
|
|
20930
21184
|
return;
|
|
20931
21185
|
}
|
|
20932
21186
|
const mode = options?.mode ?? this.currentMode;
|
|
21187
|
+
const gate = await submitUserPrompt({
|
|
21188
|
+
...this.hooks ? { hooks: this.hooks } : {},
|
|
21189
|
+
sessionId: this.sessionId,
|
|
21190
|
+
cwd: this.cwd,
|
|
21191
|
+
prompt: text3,
|
|
21192
|
+
resumed: this.messages.some((m) => m.role === "assistant"),
|
|
21193
|
+
model: this.model,
|
|
21194
|
+
auth: llmAuthFromConfig(this.config)
|
|
21195
|
+
});
|
|
21196
|
+
if (gate.blocked) throw new PromptBlockedError(gate.reason);
|
|
20933
21197
|
const messageId = `msg_${this.nextMessageId++}`;
|
|
20934
21198
|
let userContent2 = text3;
|
|
20935
21199
|
if (options?.images && options.images.length > 0) {
|
|
@@ -21209,12 +21473,12 @@ ${notes.join("\n")}` };
|
|
|
21209
21473
|
toolName: req.tool.name,
|
|
21210
21474
|
args: req.args
|
|
21211
21475
|
});
|
|
21212
|
-
const decision = await new Promise((
|
|
21213
|
-
this.permissionResolvers.set(requestId,
|
|
21476
|
+
const decision = await new Promise((resolve22) => {
|
|
21477
|
+
this.permissionResolvers.set(requestId, resolve22);
|
|
21214
21478
|
setTimeout(() => {
|
|
21215
21479
|
if (this.permissionResolvers.has(requestId)) {
|
|
21216
21480
|
this.permissionResolvers.delete(requestId);
|
|
21217
|
-
|
|
21481
|
+
resolve22("deny");
|
|
21218
21482
|
}
|
|
21219
21483
|
}, 3e5);
|
|
21220
21484
|
});
|
|
@@ -21275,7 +21539,7 @@ ${notes.join("\n")}` };
|
|
|
21275
21539
|
this.lspManager?.notifyChange(path, content);
|
|
21276
21540
|
} else {
|
|
21277
21541
|
void import("fs/promises").then(
|
|
21278
|
-
({ readFile:
|
|
21542
|
+
({ readFile: readFile42 }) => readFile42(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
|
|
21279
21543
|
})
|
|
21280
21544
|
).catch(() => {
|
|
21281
21545
|
});
|
|
@@ -21307,7 +21571,7 @@ __export(run_exports, {
|
|
|
21307
21571
|
summarizeTrace: () => summarizeTrace
|
|
21308
21572
|
});
|
|
21309
21573
|
import { spawn as spawn7 } from "child_process";
|
|
21310
|
-
import { appendFile as appendFile3, copyFile, mkdir as mkdir17, readFile as
|
|
21574
|
+
import { appendFile as appendFile3, copyFile, mkdir as mkdir17, readFile as readFile26, writeFile as writeFile15 } from "fs/promises";
|
|
21311
21575
|
import { dirname as dirname20, join as join40, resolve as resolve12 } from "path";
|
|
21312
21576
|
import { performance as performance2 } from "perf_hooks";
|
|
21313
21577
|
function runShell(command, cwd, timeoutMs) {
|
|
@@ -21350,7 +21614,7 @@ async function summarizeTrace(traceFile) {
|
|
|
21350
21614
|
const out = { cost_usd: 0, prompt_tokens: 0, completion_tokens: 0, turns: 0, tool_calls: 0, overflow: false };
|
|
21351
21615
|
let raw;
|
|
21352
21616
|
try {
|
|
21353
|
-
raw = await
|
|
21617
|
+
raw = await readFile26(traceFile, "utf8");
|
|
21354
21618
|
} catch {
|
|
21355
21619
|
return out;
|
|
21356
21620
|
}
|
|
@@ -21546,9 +21810,9 @@ __export(compare_exports, {
|
|
|
21546
21810
|
perTask: () => perTask,
|
|
21547
21811
|
summarizeArm: () => summarizeArm
|
|
21548
21812
|
});
|
|
21549
|
-
import { readFile as
|
|
21813
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
21550
21814
|
async function loadResults(file) {
|
|
21551
|
-
const raw = await
|
|
21815
|
+
const raw = await readFile27(file, "utf8");
|
|
21552
21816
|
return raw.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l));
|
|
21553
21817
|
}
|
|
21554
21818
|
function median(values) {
|
|
@@ -21681,7 +21945,7 @@ __export(cost_verify_exports, {
|
|
|
21681
21945
|
verifySession: () => verifySession,
|
|
21682
21946
|
verifyTurn: () => verifyTurn
|
|
21683
21947
|
});
|
|
21684
|
-
import { readFile as
|
|
21948
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
21685
21949
|
import { homedir as homedir24 } from "os";
|
|
21686
21950
|
import { join as join41 } from "path";
|
|
21687
21951
|
function usagePath2() {
|
|
@@ -21690,7 +21954,7 @@ function usagePath2() {
|
|
|
21690
21954
|
}
|
|
21691
21955
|
async function loadSessions() {
|
|
21692
21956
|
try {
|
|
21693
|
-
return JSON.parse(await
|
|
21957
|
+
return JSON.parse(await readFile28(usagePath2(), "utf8")).sessions ?? [];
|
|
21694
21958
|
} catch {
|
|
21695
21959
|
return [];
|
|
21696
21960
|
}
|
|
@@ -22221,7 +22485,7 @@ var init_heuristic = __esm({
|
|
|
22221
22485
|
});
|
|
22222
22486
|
|
|
22223
22487
|
// src/cost-attribution/classify-from-session.ts
|
|
22224
|
-
import { readFile as
|
|
22488
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
22225
22489
|
import { join as join42 } from "path";
|
|
22226
22490
|
import { homedir as homedir25 } from "os";
|
|
22227
22491
|
function sessionsDir2() {
|
|
@@ -22240,7 +22504,7 @@ function parseToolCalls(calls) {
|
|
|
22240
22504
|
}
|
|
22241
22505
|
async function classifyFromSessionFile(sessionId) {
|
|
22242
22506
|
try {
|
|
22243
|
-
const raw = await
|
|
22507
|
+
const raw = await readFile29(join42(sessionsDir2(), `${sessionId}.json`), "utf8");
|
|
22244
22508
|
const session = JSON.parse(raw);
|
|
22245
22509
|
const messages = session.messages ?? [];
|
|
22246
22510
|
const turns = [];
|
|
@@ -22273,7 +22537,7 @@ var cli_exports = {};
|
|
|
22273
22537
|
__export(cli_exports, {
|
|
22274
22538
|
runCostCommand: () => runCostCommand
|
|
22275
22539
|
});
|
|
22276
|
-
import { readFile as
|
|
22540
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
22277
22541
|
import { join as join43 } from "path";
|
|
22278
22542
|
import { homedir as homedir26 } from "os";
|
|
22279
22543
|
function usageDir2() {
|
|
@@ -22293,7 +22557,7 @@ function daysAgo(n) {
|
|
|
22293
22557
|
}
|
|
22294
22558
|
async function loadLog2() {
|
|
22295
22559
|
try {
|
|
22296
|
-
const raw = await
|
|
22560
|
+
const raw = await readFile30(usagePath3(), "utf8");
|
|
22297
22561
|
return JSON.parse(raw);
|
|
22298
22562
|
} catch {
|
|
22299
22563
|
return { version: 1, days: [], sessions: [] };
|
|
@@ -22377,6 +22641,661 @@ var init_cli = __esm({
|
|
|
22377
22641
|
}
|
|
22378
22642
|
});
|
|
22379
22643
|
|
|
22644
|
+
// src/remote/client.ts
|
|
22645
|
+
async function* parseEventStream(body) {
|
|
22646
|
+
const decoder = new TextDecoder();
|
|
22647
|
+
let buffer = "";
|
|
22648
|
+
for await (const chunk of body) {
|
|
22649
|
+
buffer += decoder.decode(chunk, { stream: true }).replace(/\r\n/g, "\n");
|
|
22650
|
+
let boundary;
|
|
22651
|
+
while ((boundary = buffer.indexOf("\n\n")) >= 0) {
|
|
22652
|
+
const frame = buffer.slice(0, boundary);
|
|
22653
|
+
buffer = buffer.slice(boundary + 2);
|
|
22654
|
+
const event = parseFrame(frame);
|
|
22655
|
+
if (event) yield event;
|
|
22656
|
+
}
|
|
22657
|
+
}
|
|
22658
|
+
}
|
|
22659
|
+
function parseFrame(frame) {
|
|
22660
|
+
let id;
|
|
22661
|
+
let type = "message";
|
|
22662
|
+
const data = [];
|
|
22663
|
+
for (const line of frame.split("\n")) {
|
|
22664
|
+
if (!line || line.startsWith(":")) continue;
|
|
22665
|
+
const colon = line.indexOf(":");
|
|
22666
|
+
const field = colon < 0 ? line : line.slice(0, colon);
|
|
22667
|
+
const value = colon < 0 ? "" : line.slice(colon + 1).replace(/^ /, "");
|
|
22668
|
+
if (field === "id" && /^\d+$/.test(value)) id = Number(value);
|
|
22669
|
+
else if (field === "event") type = value;
|
|
22670
|
+
else if (field === "data") data.push(value);
|
|
22671
|
+
}
|
|
22672
|
+
if (id === void 0) return void 0;
|
|
22673
|
+
let parsed = {};
|
|
22674
|
+
try {
|
|
22675
|
+
parsed = data.length ? JSON.parse(data.join("\n")) : {};
|
|
22676
|
+
} catch {
|
|
22677
|
+
return void 0;
|
|
22678
|
+
}
|
|
22679
|
+
return { id, type, data: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {} };
|
|
22680
|
+
}
|
|
22681
|
+
async function toApiError(response) {
|
|
22682
|
+
let code = `http_${response.status}`;
|
|
22683
|
+
let message2 = `Server returned ${response.status}`;
|
|
22684
|
+
try {
|
|
22685
|
+
const body = await response.json();
|
|
22686
|
+
if (body.error?.code) code = body.error.code;
|
|
22687
|
+
if (body.error?.message) message2 = body.error.message;
|
|
22688
|
+
} catch {
|
|
22689
|
+
}
|
|
22690
|
+
return new RemoteApiError(response.status, code, message2);
|
|
22691
|
+
}
|
|
22692
|
+
function delay(ms, signal) {
|
|
22693
|
+
return new Promise((resolve22, reject) => {
|
|
22694
|
+
if (signal?.aborted) return reject(signal.reason);
|
|
22695
|
+
const timer2 = setTimeout(() => {
|
|
22696
|
+
signal?.removeEventListener("abort", onAbort);
|
|
22697
|
+
resolve22();
|
|
22698
|
+
}, ms);
|
|
22699
|
+
const onAbort = () => {
|
|
22700
|
+
clearTimeout(timer2);
|
|
22701
|
+
reject(signal?.reason);
|
|
22702
|
+
};
|
|
22703
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
22704
|
+
});
|
|
22705
|
+
}
|
|
22706
|
+
var TERMINAL_STATUSES, RemoteApiError, RemoteClient;
|
|
22707
|
+
var init_client3 = __esm({
|
|
22708
|
+
"src/remote/client.ts"() {
|
|
22709
|
+
"use strict";
|
|
22710
|
+
TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "interrupted", "merged", "destroyed"]);
|
|
22711
|
+
RemoteApiError = class extends Error {
|
|
22712
|
+
constructor(status, code, message2) {
|
|
22713
|
+
super(message2);
|
|
22714
|
+
this.status = status;
|
|
22715
|
+
this.code = code;
|
|
22716
|
+
this.name = "RemoteApiError";
|
|
22717
|
+
}
|
|
22718
|
+
status;
|
|
22719
|
+
code;
|
|
22720
|
+
};
|
|
22721
|
+
RemoteClient = class {
|
|
22722
|
+
constructor(baseUrl, token, fetchImpl2 = fetch) {
|
|
22723
|
+
this.baseUrl = baseUrl;
|
|
22724
|
+
this.token = token;
|
|
22725
|
+
this.fetchImpl = fetchImpl2;
|
|
22726
|
+
}
|
|
22727
|
+
baseUrl;
|
|
22728
|
+
token;
|
|
22729
|
+
fetchImpl;
|
|
22730
|
+
startDeviceAuthorization(clientName2) {
|
|
22731
|
+
return this.request("POST", "/auth/device", { body: { clientName: clientName2 }, auth: false });
|
|
22732
|
+
}
|
|
22733
|
+
/** Polls until the owner decides; resolves with the credential, rejects on denial or expiry. */
|
|
22734
|
+
async waitForCredential(device, options = {}) {
|
|
22735
|
+
const sleep6 = options.sleep ?? ((ms) => delay(ms, options.signal));
|
|
22736
|
+
let intervalMs = Math.max(1, device.interval) * 1e3;
|
|
22737
|
+
const deadline = Date.now() + device.expiresIn * 1e3;
|
|
22738
|
+
while (Date.now() < deadline) {
|
|
22739
|
+
await sleep6(intervalMs);
|
|
22740
|
+
try {
|
|
22741
|
+
return await this.request("POST", "/auth/device/token", { body: { deviceCode: device.deviceCode }, auth: false, signal: options.signal });
|
|
22742
|
+
} catch (error) {
|
|
22743
|
+
if (!(error instanceof RemoteApiError)) throw error;
|
|
22744
|
+
if (error.code === "authorization_pending") continue;
|
|
22745
|
+
if (error.code === "slow_down") {
|
|
22746
|
+
intervalMs += 5e3;
|
|
22747
|
+
continue;
|
|
22748
|
+
}
|
|
22749
|
+
throw error;
|
|
22750
|
+
}
|
|
22751
|
+
}
|
|
22752
|
+
throw new RemoteApiError(400, "expired_token", "The pairing code expired before it was approved; run the command again");
|
|
22753
|
+
}
|
|
22754
|
+
health() {
|
|
22755
|
+
return this.request("GET", "/health");
|
|
22756
|
+
}
|
|
22757
|
+
async workspaces() {
|
|
22758
|
+
return (await this.request("GET", "/workspaces")).workspaces;
|
|
22759
|
+
}
|
|
22760
|
+
async models() {
|
|
22761
|
+
return (await this.request("GET", "/models")).models;
|
|
22762
|
+
}
|
|
22763
|
+
createCodeConversation(input) {
|
|
22764
|
+
const { idempotencyKey, ...body } = input;
|
|
22765
|
+
return this.withRetries(() => this.request("POST", "/conversations", {
|
|
22766
|
+
body: { ...body, kind: "code" },
|
|
22767
|
+
headers: { "Idempotency-Key": idempotencyKey }
|
|
22768
|
+
}));
|
|
22769
|
+
}
|
|
22770
|
+
submitTurn(conversationId, text3, clientTurnId) {
|
|
22771
|
+
return this.withRetries(() => this.request("POST", `/conversations/${encodeURIComponent(conversationId)}/turns`, { body: { clientTurnId, text: text3 } }));
|
|
22772
|
+
}
|
|
22773
|
+
getConversation(conversationId) {
|
|
22774
|
+
return this.request("GET", `/conversations/${encodeURIComponent(conversationId)}`);
|
|
22775
|
+
}
|
|
22776
|
+
async listConversations(limit = 50) {
|
|
22777
|
+
return (await this.request("GET", `/conversations?limit=${limit}`)).conversations;
|
|
22778
|
+
}
|
|
22779
|
+
cancel(conversationId) {
|
|
22780
|
+
return this.request("POST", `/conversations/${encodeURIComponent(conversationId)}/cancel`);
|
|
22781
|
+
}
|
|
22782
|
+
/**
|
|
22783
|
+
* Events after `cursor`, reconnecting with the last seen ID when the stream drops,
|
|
22784
|
+
* until the caller stops iterating or the signal aborts.
|
|
22785
|
+
*/
|
|
22786
|
+
async *events(conversationId, cursor, signal) {
|
|
22787
|
+
let last = cursor;
|
|
22788
|
+
let failures = 0;
|
|
22789
|
+
while (!signal?.aborted) {
|
|
22790
|
+
try {
|
|
22791
|
+
const response = await this.fetchImpl(this.url(`/conversations/${encodeURIComponent(conversationId)}/events`), {
|
|
22792
|
+
headers: { ...this.authHeaders(), Accept: "text/event-stream", "Last-Event-ID": String(last) },
|
|
22793
|
+
signal
|
|
22794
|
+
});
|
|
22795
|
+
if (!response.ok || !response.body) throw await toApiError(response);
|
|
22796
|
+
failures = 0;
|
|
22797
|
+
for await (const event of parseEventStream(response.body)) {
|
|
22798
|
+
if (event.id <= last) continue;
|
|
22799
|
+
last = event.id;
|
|
22800
|
+
yield event;
|
|
22801
|
+
}
|
|
22802
|
+
} catch (error) {
|
|
22803
|
+
if (signal?.aborted) return;
|
|
22804
|
+
if (error instanceof RemoteApiError && error.status < 500) throw error;
|
|
22805
|
+
if (++failures > 30) throw error;
|
|
22806
|
+
}
|
|
22807
|
+
await delay(Math.min(1e3 * failures, 1e4), signal).catch(() => {
|
|
22808
|
+
});
|
|
22809
|
+
}
|
|
22810
|
+
}
|
|
22811
|
+
async request(method, path, options = {}) {
|
|
22812
|
+
const response = await this.fetchImpl(this.url(path), {
|
|
22813
|
+
method,
|
|
22814
|
+
headers: {
|
|
22815
|
+
...options.auth === false ? {} : this.authHeaders(),
|
|
22816
|
+
...options.body === void 0 ? {} : { "Content-Type": "application/json" },
|
|
22817
|
+
...options.headers
|
|
22818
|
+
},
|
|
22819
|
+
...options.body === void 0 ? {} : { body: JSON.stringify(options.body) },
|
|
22820
|
+
...options.signal ? { signal: options.signal } : {}
|
|
22821
|
+
});
|
|
22822
|
+
if (!response.ok) throw await toApiError(response);
|
|
22823
|
+
return await response.json();
|
|
22824
|
+
}
|
|
22825
|
+
/** Retries network failures and 5xx; safe only for requests carrying an idempotency key. */
|
|
22826
|
+
async withRetries(attempt) {
|
|
22827
|
+
for (let tries = 1; ; tries++) {
|
|
22828
|
+
try {
|
|
22829
|
+
return await attempt();
|
|
22830
|
+
} catch (error) {
|
|
22831
|
+
const retryable = !(error instanceof RemoteApiError) || error.status >= 500;
|
|
22832
|
+
if (!retryable || tries >= 3) throw error;
|
|
22833
|
+
await delay(1e3 * tries);
|
|
22834
|
+
}
|
|
22835
|
+
}
|
|
22836
|
+
}
|
|
22837
|
+
url(path) {
|
|
22838
|
+
return `${this.baseUrl}/api/v1${path}`;
|
|
22839
|
+
}
|
|
22840
|
+
authHeaders() {
|
|
22841
|
+
return this.token ? { Authorization: `Bearer ${this.token}` } : {};
|
|
22842
|
+
}
|
|
22843
|
+
};
|
|
22844
|
+
}
|
|
22845
|
+
});
|
|
22846
|
+
|
|
22847
|
+
// src/remote/config.ts
|
|
22848
|
+
import { chmod as chmod4, mkdir as mkdir18, readFile as readFile31, rename as rename4, writeFile as writeFile16 } from "fs/promises";
|
|
22849
|
+
import { homedir as homedir27 } from "os";
|
|
22850
|
+
import { dirname as dirname21, join as join44 } from "path";
|
|
22851
|
+
function remotesPath(env = process.env) {
|
|
22852
|
+
if (env.AUTOPILOT_REMOTES_FILE) return env.AUTOPILOT_REMOTES_FILE;
|
|
22853
|
+
const configHome2 = env.XDG_CONFIG_HOME?.trim() || join44(homedir27(), ".config");
|
|
22854
|
+
return join44(configHome2, "autopilot", "remotes.json");
|
|
22855
|
+
}
|
|
22856
|
+
async function loadRemotes(path = remotesPath()) {
|
|
22857
|
+
let raw;
|
|
22858
|
+
try {
|
|
22859
|
+
raw = await readFile31(path, "utf8");
|
|
22860
|
+
} catch (error) {
|
|
22861
|
+
if (error.code === "ENOENT") return [];
|
|
22862
|
+
throw error;
|
|
22863
|
+
}
|
|
22864
|
+
const parsed = JSON.parse(raw);
|
|
22865
|
+
return Array.isArray(parsed.remotes) ? parsed.remotes.filter(isRemote) : [];
|
|
22866
|
+
}
|
|
22867
|
+
async function saveRemote(remote, path = remotesPath()) {
|
|
22868
|
+
const remotes = (await loadRemotes(path)).filter((entry) => entry.name !== remote.name);
|
|
22869
|
+
remotes.push(remote);
|
|
22870
|
+
await writeRemotes(remotes, path);
|
|
22871
|
+
}
|
|
22872
|
+
async function removeRemote(name, path = remotesPath()) {
|
|
22873
|
+
const remotes = await loadRemotes(path);
|
|
22874
|
+
const kept = remotes.filter((entry) => entry.name !== name);
|
|
22875
|
+
if (kept.length === remotes.length) return false;
|
|
22876
|
+
await writeRemotes(kept, path);
|
|
22877
|
+
return true;
|
|
22878
|
+
}
|
|
22879
|
+
async function resolveRemote(name, path = remotesPath()) {
|
|
22880
|
+
const remotes = await loadRemotes(path);
|
|
22881
|
+
if (remotes.length === 0) throw new Error("No servers paired yet. Run: autopilot remote add https://your-server");
|
|
22882
|
+
if (name === void 0) {
|
|
22883
|
+
if (remotes.length === 1) return remotes[0];
|
|
22884
|
+
throw new Error(`Several servers are paired; pick one with --on (${remotes.map((entry) => entry.name).join(", ")})`);
|
|
22885
|
+
}
|
|
22886
|
+
const remote = remotes.find((entry) => entry.name === name);
|
|
22887
|
+
if (!remote) throw new Error(`No server named "${name}". Paired: ${remotes.map((entry) => entry.name).join(", ")}`);
|
|
22888
|
+
return remote;
|
|
22889
|
+
}
|
|
22890
|
+
function defaultRemoteName(url) {
|
|
22891
|
+
const host = new URL(url).hostname.toLowerCase();
|
|
22892
|
+
const label = /^\d+(\.\d+){3}$/.test(host) ? host.replace(/\./g, "-") : host.split(".")[0] ?? "server";
|
|
22893
|
+
const name = label.replace(/[^a-z0-9-]/g, "-").replace(/^-+/, "").slice(0, 40);
|
|
22894
|
+
return REMOTE_NAME_RE.test(name) ? name : "server";
|
|
22895
|
+
}
|
|
22896
|
+
function normalizeServerUrl(input) {
|
|
22897
|
+
let url;
|
|
22898
|
+
try {
|
|
22899
|
+
url = new URL(/^[a-z]+:\/\//i.test(input) ? input : `https://${input}`);
|
|
22900
|
+
} catch {
|
|
22901
|
+
throw new Error(`Not a URL: ${input}`);
|
|
22902
|
+
}
|
|
22903
|
+
const loopback = url.hostname === "localhost" || url.hostname === "[::1]" || url.hostname.startsWith("127.");
|
|
22904
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
22905
|
+
throw new Error("Use an https:// server URL (http:// is only allowed for localhost)");
|
|
22906
|
+
}
|
|
22907
|
+
if (url.username || url.password || url.search || url.hash) throw new Error("The server URL must not include credentials, a query, or a fragment");
|
|
22908
|
+
return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
|
|
22909
|
+
}
|
|
22910
|
+
async function writeRemotes(remotes, path) {
|
|
22911
|
+
await mkdir18(dirname21(path), { recursive: true, mode: 448 });
|
|
22912
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
22913
|
+
await writeFile16(temp, `${JSON.stringify({ version: 1, remotes }, null, 2)}
|
|
22914
|
+
`, { mode: 384 });
|
|
22915
|
+
await chmod4(temp, 384);
|
|
22916
|
+
await rename4(temp, path);
|
|
22917
|
+
}
|
|
22918
|
+
function isRemote(value) {
|
|
22919
|
+
if (!value || typeof value !== "object") return false;
|
|
22920
|
+
const entry = value;
|
|
22921
|
+
return ["name", "url", "token", "credentialId", "expiresAt", "addedAt"].every((key) => typeof entry[key] === "string");
|
|
22922
|
+
}
|
|
22923
|
+
var REMOTE_NAME_RE;
|
|
22924
|
+
var init_config4 = __esm({
|
|
22925
|
+
"src/remote/config.ts"() {
|
|
22926
|
+
"use strict";
|
|
22927
|
+
REMOTE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
22928
|
+
}
|
|
22929
|
+
});
|
|
22930
|
+
|
|
22931
|
+
// src/remote/git.ts
|
|
22932
|
+
import { execFile as execFile9 } from "child_process";
|
|
22933
|
+
import { mkdtemp as mkdtemp3, rm as rm6, copyFile as copyFile2 } from "fs/promises";
|
|
22934
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
22935
|
+
import { join as join45, resolve as resolve13 } from "path";
|
|
22936
|
+
import { promisify as promisify8 } from "util";
|
|
22937
|
+
function parseGitHubRemote(url) {
|
|
22938
|
+
const match = url.trim().match(/^(?:https:\/\/(?:[^@/]+@)?github\.com\/|ssh:\/\/git@github\.com(?::\d+)?\/|git@github\.com:)([A-Za-z0-9-]{1,39})\/([A-Za-z0-9._-]{1,100}?)(?:\.git)?\/?$/);
|
|
22939
|
+
return match ? `${match[1]}/${match[2]}` : void 0;
|
|
22940
|
+
}
|
|
22941
|
+
async function detectGitHubRepository(cwd, remote = "origin") {
|
|
22942
|
+
let url;
|
|
22943
|
+
try {
|
|
22944
|
+
url = await git4(cwd, ["remote", "get-url", remote]);
|
|
22945
|
+
} catch {
|
|
22946
|
+
throw new Error(`This directory has no "${remote}" git remote; pass --repo owner/name`);
|
|
22947
|
+
}
|
|
22948
|
+
const repository = parseGitHubRemote(url);
|
|
22949
|
+
if (!repository) throw new Error(`The "${remote}" remote is not a GitHub repository (${url}); pass --repo owner/name`);
|
|
22950
|
+
return repository;
|
|
22951
|
+
}
|
|
22952
|
+
async function snapshotWorkingTree(cwd) {
|
|
22953
|
+
const root = await git4(cwd, ["rev-parse", "--show-toplevel"]).catch(() => {
|
|
22954
|
+
throw new Error("Not inside a git repository");
|
|
22955
|
+
});
|
|
22956
|
+
await git4(root, ["rev-parse", "--verify", "HEAD"]).catch(() => {
|
|
22957
|
+
throw new Error("This repository has no commits yet; push a first commit before running remotely");
|
|
22958
|
+
});
|
|
22959
|
+
const baseRef = await upstreamRef(root);
|
|
22960
|
+
const baseCommit = await git4(root, ["merge-base", "HEAD", baseRef]).catch(() => {
|
|
22961
|
+
throw new Error(`HEAD shares no history with ${baseRef}`);
|
|
22962
|
+
});
|
|
22963
|
+
const scratch = await mkdtemp3(join45(tmpdir5(), "autopilot-snapshot-"));
|
|
22964
|
+
try {
|
|
22965
|
+
const index = join45(scratch, "index");
|
|
22966
|
+
const realIndex = resolve13(root, await git4(root, ["rev-parse", "--git-path", "index"]));
|
|
22967
|
+
await copyFile2(realIndex, index).catch(() => {
|
|
22968
|
+
});
|
|
22969
|
+
const env = { ...process.env, GIT_INDEX_FILE: index };
|
|
22970
|
+
await git4(root, ["add", "--all"], env);
|
|
22971
|
+
const patch = await git4(root, ["diff", "--cached", "--binary", "--no-color", "--no-ext-diff", baseCommit], env, false);
|
|
22972
|
+
if (patch.length > MAX_PATCH_CHARS) {
|
|
22973
|
+
throw new Error(`Local changes are ${(patch.length / 1024 / 1024).toFixed(1)} MiB, over the 3 MiB limit; commit and push some of them first`);
|
|
22974
|
+
}
|
|
22975
|
+
const files = (await git4(root, ["diff", "--cached", "--name-only", "--no-renames", baseCommit], env)).split("\n").filter(Boolean);
|
|
22976
|
+
return { baseCommit, patch, baseRef, files };
|
|
22977
|
+
} finally {
|
|
22978
|
+
await rm6(scratch, { recursive: true, force: true });
|
|
22979
|
+
}
|
|
22980
|
+
}
|
|
22981
|
+
async function upstreamRef(root) {
|
|
22982
|
+
const upstream = await git4(root, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]).catch(() => "");
|
|
22983
|
+
if (upstream.startsWith("origin/")) return upstream;
|
|
22984
|
+
const originHead = await git4(root, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]).catch(() => "");
|
|
22985
|
+
if (originHead) return originHead;
|
|
22986
|
+
for (const candidate of ["origin/main", "origin/master"]) {
|
|
22987
|
+
if (await git4(root, ["rev-parse", "--verify", "--quiet", candidate]).then(() => true, () => false)) return candidate;
|
|
22988
|
+
}
|
|
22989
|
+
throw new Error("Can't find this branch on origin; run `git fetch origin` (or push the branch) first");
|
|
22990
|
+
}
|
|
22991
|
+
async function git4(cwd, args, env, trim = true) {
|
|
22992
|
+
const { stdout } = await execFileAsync6("git", ["-C", cwd, ...args], { env: env ?? process.env, maxBuffer: 64 * 1024 * 1024 });
|
|
22993
|
+
return trim ? stdout.trim() : stdout;
|
|
22994
|
+
}
|
|
22995
|
+
var execFileAsync6, MAX_PATCH_CHARS;
|
|
22996
|
+
var init_git = __esm({
|
|
22997
|
+
"src/remote/git.ts"() {
|
|
22998
|
+
"use strict";
|
|
22999
|
+
execFileAsync6 = promisify8(execFile9);
|
|
23000
|
+
MAX_PATCH_CHARS = 3 * 1024 * 1024;
|
|
23001
|
+
}
|
|
23002
|
+
});
|
|
23003
|
+
|
|
23004
|
+
// src/remote/cli.ts
|
|
23005
|
+
var cli_exports2 = {};
|
|
23006
|
+
__export(cli_exports2, {
|
|
23007
|
+
cancelRun: () => cancelRun,
|
|
23008
|
+
describeError: () => describeError,
|
|
23009
|
+
listRuns: () => listRuns,
|
|
23010
|
+
pullRun: () => pullRun,
|
|
23011
|
+
remoteAdd: () => remoteAdd,
|
|
23012
|
+
remoteList: () => remoteList,
|
|
23013
|
+
remoteRemove: () => remoteRemove,
|
|
23014
|
+
runOnRemote: () => runOnRemote,
|
|
23015
|
+
watchRun: () => watchRun
|
|
23016
|
+
});
|
|
23017
|
+
import { execFile as execFile10 } from "child_process";
|
|
23018
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
23019
|
+
import { hostname } from "os";
|
|
23020
|
+
import { promisify as promisify9 } from "util";
|
|
23021
|
+
async function remoteAdd(input, io = processIO) {
|
|
23022
|
+
const url = normalizeServerUrl(input.url);
|
|
23023
|
+
const name = input.name ?? defaultRemoteName(url);
|
|
23024
|
+
if (!REMOTE_NAME_RE.test(name)) throw new Error("Server names are 1-40 lowercase letters, digits, or dashes");
|
|
23025
|
+
const anonymous = new RemoteClient(url, void 0);
|
|
23026
|
+
const device = await anonymous.startDeviceAuthorization(clientName());
|
|
23027
|
+
io.out(`To pair this machine with ${url}, approve code ${device.userCode} at:
|
|
23028
|
+
${device.verificationUriComplete}
|
|
23029
|
+
`);
|
|
23030
|
+
if (input.open !== false && openBrowser(device.verificationUriComplete)) io.out("(opened in your browser)\n");
|
|
23031
|
+
io.out("Waiting for approval\u2026\n");
|
|
23032
|
+
const credential = await anonymous.waitForCredential(device, {
|
|
23033
|
+
...input.signal ? { signal: input.signal } : {},
|
|
23034
|
+
...input.sleep ? { sleep: input.sleep } : {}
|
|
23035
|
+
});
|
|
23036
|
+
const remote = {
|
|
23037
|
+
name,
|
|
23038
|
+
url,
|
|
23039
|
+
token: credential.token,
|
|
23040
|
+
credentialId: credential.credentialId,
|
|
23041
|
+
expiresAt: credential.expiresAt,
|
|
23042
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
23043
|
+
};
|
|
23044
|
+
await new RemoteClient(url, remote.token).health();
|
|
23045
|
+
await saveRemote(remote);
|
|
23046
|
+
io.out(`\u2713 Paired as "${name}" (credential expires ${remote.expiresAt.slice(0, 10)}).
|
|
23047
|
+
Try: autopilot --on ${name} "fix the flaky test"
|
|
23048
|
+
`);
|
|
23049
|
+
return remote;
|
|
23050
|
+
}
|
|
23051
|
+
async function remoteList(io = processIO) {
|
|
23052
|
+
const remotes = await loadRemotes();
|
|
23053
|
+
if (remotes.length === 0) {
|
|
23054
|
+
io.out("No servers paired. Run: autopilot remote add https://your-server\n");
|
|
23055
|
+
return;
|
|
23056
|
+
}
|
|
23057
|
+
for (const remote of remotes) io.out(`${remote.name} ${remote.url} expires ${remote.expiresAt.slice(0, 10)}
|
|
23058
|
+
`);
|
|
23059
|
+
}
|
|
23060
|
+
async function remoteRemove(name, io = processIO) {
|
|
23061
|
+
const removed = await removeRemote(name);
|
|
23062
|
+
if (removed) io.out(`Removed "${name}". To revoke its credential on the server: autopilot aster token revoke <id>
|
|
23063
|
+
`);
|
|
23064
|
+
else io.err(`No server named "${name}".
|
|
23065
|
+
`);
|
|
23066
|
+
return removed;
|
|
23067
|
+
}
|
|
23068
|
+
async function runOnRemote(input, io = processIO) {
|
|
23069
|
+
const remote = await resolveRemote(input.remote);
|
|
23070
|
+
const client = new RemoteClient(remote.url, remote.token);
|
|
23071
|
+
const repository = input.repository ?? await detectGitHubRepository(input.cwd);
|
|
23072
|
+
const snapshot = await snapshotWorkingTree(input.cwd);
|
|
23073
|
+
const [workspaces, models] = await Promise.all([client.workspaces(), client.models()]);
|
|
23074
|
+
const workspace = workspaces[0];
|
|
23075
|
+
if (!workspace) throw new Error(`${remote.name} has no workspace this credential can use`);
|
|
23076
|
+
const model = pickModel(models, input.model, input.preferredModel);
|
|
23077
|
+
io.err(`Starting on ${remote.name}: ${repository} from ${snapshot.baseCommit.slice(0, 7)} (${snapshot.baseRef})${snapshot.files.length ? `, with ${snapshot.files.length} changed file${snapshot.files.length === 1 ? "" : "s"}` : ""} \xB7 ${model}
|
|
23078
|
+
`);
|
|
23079
|
+
const conversation = await client.createCodeConversation({
|
|
23080
|
+
workspaceId: workspace.id,
|
|
23081
|
+
model,
|
|
23082
|
+
repository,
|
|
23083
|
+
baseCommit: snapshot.baseCommit,
|
|
23084
|
+
...snapshot.patch ? { patch: snapshot.patch } : {},
|
|
23085
|
+
idempotencyKey: `cli-${randomUUID8()}`
|
|
23086
|
+
});
|
|
23087
|
+
await client.submitTurn(conversation.conversationId, input.task, `cli-${randomUUID8()}`);
|
|
23088
|
+
const shortId = conversation.conversationId.slice(0, 8);
|
|
23089
|
+
io.err(`Run ${shortId} \xB7 branch ${conversation.branch ?? "?"}
|
|
23090
|
+
`);
|
|
23091
|
+
if (input.detach) {
|
|
23092
|
+
io.err(`Detached. Follow it with: autopilot runs watch ${shortId}
|
|
23093
|
+
`);
|
|
23094
|
+
return 0;
|
|
23095
|
+
}
|
|
23096
|
+
io.err(`Ctrl-C detaches; the run keeps going on the server (autopilot runs watch ${shortId}).
|
|
23097
|
+
|
|
23098
|
+
`);
|
|
23099
|
+
return watchConversation(client, remote, conversation.conversationId, conversation.lastEventId, input.signal, io);
|
|
23100
|
+
}
|
|
23101
|
+
async function listRuns(input, io = processIO) {
|
|
23102
|
+
const remotes = input.remote ? [await resolveRemote(input.remote)] : await loadRemotes();
|
|
23103
|
+
if (remotes.length === 0) throw new Error("No servers paired yet. Run: autopilot remote add https://your-server");
|
|
23104
|
+
const rows = [];
|
|
23105
|
+
for (const remote of remotes) {
|
|
23106
|
+
try {
|
|
23107
|
+
const runs = await new RemoteClient(remote.url, remote.token).listConversations();
|
|
23108
|
+
rows.push(...runs.filter((run) => run.kind === "code").map((run) => ({ ...run, remote: remote.name })));
|
|
23109
|
+
} catch (error) {
|
|
23110
|
+
io.err(`${remote.name}: ${describeError(error)}
|
|
23111
|
+
`);
|
|
23112
|
+
}
|
|
23113
|
+
}
|
|
23114
|
+
rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
23115
|
+
if (input.json) {
|
|
23116
|
+
io.out(`${JSON.stringify(rows, null, 2)}
|
|
23117
|
+
`);
|
|
23118
|
+
return;
|
|
23119
|
+
}
|
|
23120
|
+
if (rows.length === 0) {
|
|
23121
|
+
io.out("No runs yet.\n");
|
|
23122
|
+
return;
|
|
23123
|
+
}
|
|
23124
|
+
for (const run of rows) {
|
|
23125
|
+
io.out([
|
|
23126
|
+
run.conversationId.slice(0, 8),
|
|
23127
|
+
run.remote,
|
|
23128
|
+
run.status.padEnd(16),
|
|
23129
|
+
run.repository ?? "",
|
|
23130
|
+
run.pullRequestURL ?? run.branch ?? "",
|
|
23131
|
+
run.createdAt.slice(0, 16).replace("T", " ")
|
|
23132
|
+
].join(" ") + "\n");
|
|
23133
|
+
}
|
|
23134
|
+
}
|
|
23135
|
+
async function watchRun(input, io = processIO) {
|
|
23136
|
+
const { remote, run } = await findRun(input.id, input.remote);
|
|
23137
|
+
const client = new RemoteClient(remote.url, remote.token);
|
|
23138
|
+
const finished = TERMINAL_STATUSES.has(run.status) && !run.activeRunId;
|
|
23139
|
+
return watchConversation(client, remote, run.conversationId, 0, input.signal, io, finished ? run.lastEventId : void 0);
|
|
23140
|
+
}
|
|
23141
|
+
async function cancelRun(input, io = processIO) {
|
|
23142
|
+
const { remote, run } = await findRun(input.id, input.remote);
|
|
23143
|
+
const cancelled = await new RemoteClient(remote.url, remote.token).cancel(run.conversationId);
|
|
23144
|
+
io.out(`${run.conversationId.slice(0, 8)}: ${cancelled.status}
|
|
23145
|
+
`);
|
|
23146
|
+
}
|
|
23147
|
+
async function pullRun(input, io = processIO) {
|
|
23148
|
+
const { run } = await findRun(input.id, input.remote);
|
|
23149
|
+
const branch = run.branch;
|
|
23150
|
+
if (!branch) throw new Error("That run has no branch");
|
|
23151
|
+
if (run.activeRunId) io.err("The run is still going; you'll get what it has pushed so far.\n");
|
|
23152
|
+
const git5 = (args) => execFileAsync7("git", ["-C", input.cwd, ...args]);
|
|
23153
|
+
try {
|
|
23154
|
+
await git5(["fetch", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`]);
|
|
23155
|
+
} catch {
|
|
23156
|
+
throw new Error(`Branch ${branch} isn't on origin yet (the run pushes it when a turn finishes)`);
|
|
23157
|
+
}
|
|
23158
|
+
const exists = await git5(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]).then(() => true, () => false);
|
|
23159
|
+
if (exists) {
|
|
23160
|
+
await git5(["switch", branch]);
|
|
23161
|
+
await git5(["merge", "--ff-only", `origin/${branch}`]);
|
|
23162
|
+
} else {
|
|
23163
|
+
await git5(["switch", "-c", branch, "--track", `origin/${branch}`]);
|
|
23164
|
+
}
|
|
23165
|
+
io.out(`On ${branch}${run.pullRequestURL ? ` \xB7 ${run.pullRequestURL}` : ""}
|
|
23166
|
+
`);
|
|
23167
|
+
}
|
|
23168
|
+
async function watchConversation(client, remote, conversationId, cursor, signal, io, replayUntil) {
|
|
23169
|
+
const controller = new AbortController();
|
|
23170
|
+
const stop = () => controller.abort();
|
|
23171
|
+
signal?.addEventListener("abort", stop, { once: true });
|
|
23172
|
+
const renderer = new EventRenderer(io);
|
|
23173
|
+
let outcome;
|
|
23174
|
+
try {
|
|
23175
|
+
if (replayUntil === void 0 || replayUntil > cursor) {
|
|
23176
|
+
for await (const event of client.events(conversationId, cursor, controller.signal)) {
|
|
23177
|
+
renderer.render(event);
|
|
23178
|
+
if (RUN_EVENT_TYPES.has(event.type)) outcome = event.type;
|
|
23179
|
+
if (replayUntil === void 0 ? RUN_EVENT_TYPES.has(event.type) : event.id >= replayUntil) break;
|
|
23180
|
+
}
|
|
23181
|
+
}
|
|
23182
|
+
if (replayUntil !== void 0 && !controller.signal.aborted) outcome ??= "completed";
|
|
23183
|
+
} finally {
|
|
23184
|
+
signal?.removeEventListener("abort", stop);
|
|
23185
|
+
controller.abort();
|
|
23186
|
+
}
|
|
23187
|
+
renderer.finish();
|
|
23188
|
+
const shortId = conversationId.slice(0, 8);
|
|
23189
|
+
if (!outcome) {
|
|
23190
|
+
io.err(`
|
|
23191
|
+
Detached from ${shortId}; it keeps running on ${remote.name}. Follow it with: autopilot runs watch ${shortId}
|
|
23192
|
+
`);
|
|
23193
|
+
return 0;
|
|
23194
|
+
}
|
|
23195
|
+
const final = await client.getConversation(conversationId);
|
|
23196
|
+
if (outcome === "completed") {
|
|
23197
|
+
io.err(`
|
|
23198
|
+
\u2713 ${shortId} completed${final.pullRequestURL ? `: ${final.pullRequestURL}` : " (no changes, so no pull request)"}
|
|
23199
|
+
`);
|
|
23200
|
+
if (final.pullRequestURL) io.err(` Check it out locally: autopilot pull ${shortId}
|
|
23201
|
+
`);
|
|
23202
|
+
return 0;
|
|
23203
|
+
}
|
|
23204
|
+
io.err(`
|
|
23205
|
+
\u2717 ${shortId} ${outcome}
|
|
23206
|
+
`);
|
|
23207
|
+
return 1;
|
|
23208
|
+
}
|
|
23209
|
+
async function findRun(id, remoteName) {
|
|
23210
|
+
const needle = id.trim().toLowerCase();
|
|
23211
|
+
if (!/^[0-9a-f-]{4,36}$/.test(needle)) throw new Error("Run IDs are the hex IDs `autopilot runs` shows");
|
|
23212
|
+
const remotes = remoteName ? [await resolveRemote(remoteName)] : await loadRemotes();
|
|
23213
|
+
const matches = [];
|
|
23214
|
+
for (const remote of remotes) {
|
|
23215
|
+
const runs = await new RemoteClient(remote.url, remote.token).listConversations(200).catch(() => []);
|
|
23216
|
+
for (const run of runs) if (run.conversationId.startsWith(needle)) matches.push({ remote, run });
|
|
23217
|
+
}
|
|
23218
|
+
if (matches.length === 0) throw new Error(`No run ${id} on ${remoteName ?? "any paired server"}`);
|
|
23219
|
+
if (matches.length > 1) throw new Error(`${id} matches several runs; use more characters`);
|
|
23220
|
+
return matches[0];
|
|
23221
|
+
}
|
|
23222
|
+
function pickModel(models, requested, preferred) {
|
|
23223
|
+
if (requested) {
|
|
23224
|
+
if (!models.includes(requested)) throw new Error(`The server doesn't offer ${requested}`);
|
|
23225
|
+
return requested;
|
|
23226
|
+
}
|
|
23227
|
+
if (preferred && models.includes(preferred)) return preferred;
|
|
23228
|
+
const first = models[0];
|
|
23229
|
+
if (!first) throw new Error("The server offers no models");
|
|
23230
|
+
return first;
|
|
23231
|
+
}
|
|
23232
|
+
function clientName() {
|
|
23233
|
+
const host = hostname().replace(/\.local$/, "").replace(/[^\p{L}\p{N} ._'()@-]/gu, "-").slice(0, 40) || "laptop";
|
|
23234
|
+
return `${host} (autopilot)`;
|
|
23235
|
+
}
|
|
23236
|
+
function oneLine(text3, max) {
|
|
23237
|
+
const flat = text3.replace(/\s+/g, " ").trim();
|
|
23238
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
23239
|
+
}
|
|
23240
|
+
function describeError(error) {
|
|
23241
|
+
if (error instanceof RemoteApiError) return `${error.message} (${error.code})`;
|
|
23242
|
+
return error instanceof Error ? error.message : String(error);
|
|
23243
|
+
}
|
|
23244
|
+
var execFileAsync7, RUN_EVENT_TYPES, processIO, EventRenderer;
|
|
23245
|
+
var init_cli2 = __esm({
|
|
23246
|
+
"src/remote/cli.ts"() {
|
|
23247
|
+
"use strict";
|
|
23248
|
+
init_open_browser();
|
|
23249
|
+
init_client3();
|
|
23250
|
+
init_config4();
|
|
23251
|
+
init_git();
|
|
23252
|
+
execFileAsync7 = promisify9(execFile10);
|
|
23253
|
+
RUN_EVENT_TYPES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "interrupted"]);
|
|
23254
|
+
processIO = {
|
|
23255
|
+
out: (text3) => process.stdout.write(text3),
|
|
23256
|
+
err: (text3) => process.stderr.write(text3)
|
|
23257
|
+
};
|
|
23258
|
+
EventRenderer = class {
|
|
23259
|
+
constructor(io) {
|
|
23260
|
+
this.io = io;
|
|
23261
|
+
}
|
|
23262
|
+
io;
|
|
23263
|
+
midLine = false;
|
|
23264
|
+
render(event) {
|
|
23265
|
+
const data = event.data;
|
|
23266
|
+
if (event.type === "assistant.delta") {
|
|
23267
|
+
const text3 = typeof data.text === "string" ? data.text : typeof data.delta === "string" ? data.delta : "";
|
|
23268
|
+
if (!text3) return;
|
|
23269
|
+
this.io.out(text3);
|
|
23270
|
+
this.midLine = !text3.endsWith("\n");
|
|
23271
|
+
return;
|
|
23272
|
+
}
|
|
23273
|
+
if (event.type === "tool.activity" && data.activity === "started") {
|
|
23274
|
+
const input = typeof data.input === "string" ? ` ${oneLine(data.input, 120)}` : "";
|
|
23275
|
+
this.line(` \u203A ${String(data.tool ?? "tool")}${input}`);
|
|
23276
|
+
return;
|
|
23277
|
+
}
|
|
23278
|
+
if (event.type === "tool.activity" && data.activity === "failed") {
|
|
23279
|
+
this.line(` \u2717 ${String(data.tool ?? "tool")} failed`);
|
|
23280
|
+
return;
|
|
23281
|
+
}
|
|
23282
|
+
if (event.type === "checkpoint.created" && typeof data.message === "string") {
|
|
23283
|
+
this.line(` \u25CF ${oneLine(data.message, 100)}`);
|
|
23284
|
+
}
|
|
23285
|
+
}
|
|
23286
|
+
finish() {
|
|
23287
|
+
if (this.midLine) this.io.out("\n");
|
|
23288
|
+
this.midLine = false;
|
|
23289
|
+
}
|
|
23290
|
+
line(text3) {
|
|
23291
|
+
this.finish();
|
|
23292
|
+
this.io.out(`${text3}
|
|
23293
|
+
`);
|
|
23294
|
+
}
|
|
23295
|
+
};
|
|
23296
|
+
}
|
|
23297
|
+
});
|
|
23298
|
+
|
|
22380
23299
|
// src/server/aster-workspaces.ts
|
|
22381
23300
|
var aster_workspaces_exports = {};
|
|
22382
23301
|
__export(aster_workspaces_exports, {
|
|
@@ -22387,23 +23306,23 @@ __export(aster_workspaces_exports, {
|
|
|
22387
23306
|
resolveAsterServerConfigPath: () => resolveAsterServerConfigPath
|
|
22388
23307
|
});
|
|
22389
23308
|
import { existsSync as existsSync11 } from "fs";
|
|
22390
|
-
import { readFile as
|
|
22391
|
-
import { homedir as
|
|
22392
|
-
import { isAbsolute as isAbsolute5, join as
|
|
23309
|
+
import { readFile as readFile32, realpath as realpath5, stat as stat11 } from "fs/promises";
|
|
23310
|
+
import { homedir as homedir28 } from "os";
|
|
23311
|
+
import { isAbsolute as isAbsolute5, join as join46, relative as relative8, resolve as resolve14, sep as sep4 } from "path";
|
|
22393
23312
|
function localAsterConfigPath(env = process.env) {
|
|
22394
|
-
const configHome2 = env.XDG_CONFIG_HOME?.trim() ||
|
|
22395
|
-
return
|
|
23313
|
+
const configHome2 = env.XDG_CONFIG_HOME?.trim() || join46(homedir28(), ".config");
|
|
23314
|
+
return resolve14(configHome2, "autopilot", "aster.json");
|
|
22396
23315
|
}
|
|
22397
23316
|
function resolveAsterServerConfigPath(env = process.env) {
|
|
22398
23317
|
const configuredPath = env.AUTOPILOT_ASTER_CONFIG?.trim();
|
|
22399
|
-
if (configuredPath) return
|
|
23318
|
+
if (configuredPath) return resolve14(configuredPath);
|
|
22400
23319
|
const userConfigPath = localAsterConfigPath(env);
|
|
22401
23320
|
return existsSync11(userConfigPath) ? userConfigPath : SYSTEM_ASTER_CONFIG_PATH;
|
|
22402
23321
|
}
|
|
22403
23322
|
async function loadAsterServerConfig(configPath2 = resolveAsterServerConfigPath()) {
|
|
22404
23323
|
let raw;
|
|
22405
23324
|
try {
|
|
22406
|
-
raw = JSON.parse(await
|
|
23325
|
+
raw = JSON.parse(await readFile32(configPath2, "utf8"));
|
|
22407
23326
|
} catch {
|
|
22408
23327
|
throw new AsterConfigError();
|
|
22409
23328
|
}
|
|
@@ -22440,7 +23359,7 @@ async function loadAsterServerConfig(configPath2 = resolveAsterServerConfigPath(
|
|
|
22440
23359
|
let rootPath;
|
|
22441
23360
|
try {
|
|
22442
23361
|
rootPath = await realpath5(workspace.rootPath);
|
|
22443
|
-
if (!(await
|
|
23362
|
+
if (!(await stat11(rootPath)).isDirectory()) throw new Error("not a directory");
|
|
22444
23363
|
} catch {
|
|
22445
23364
|
throw new AsterConfigError();
|
|
22446
23365
|
}
|
|
@@ -22480,10 +23399,10 @@ __export(aster_setup_exports, {
|
|
|
22480
23399
|
setupLocalAsterConfig: () => setupLocalAsterConfig
|
|
22481
23400
|
});
|
|
22482
23401
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
22483
|
-
import { randomUUID as
|
|
23402
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
22484
23403
|
import { existsSync as existsSync12 } from "fs";
|
|
22485
|
-
import { chmod as
|
|
22486
|
-
import { basename as basename9, dirname as
|
|
23404
|
+
import { chmod as chmod5, link, mkdir as mkdir19, realpath as realpath6, rm as rm7, stat as stat12, writeFile as writeFile17 } from "fs/promises";
|
|
23405
|
+
import { basename as basename9, dirname as dirname22, resolve as resolve15 } from "path";
|
|
22487
23406
|
async function setupLocalAsterConfig(options) {
|
|
22488
23407
|
if (options.workspacePaths.length === 0 || options.workspacePaths.length > 100) {
|
|
22489
23408
|
throw new Error("provide between 1 and 100 Git workspace roots");
|
|
@@ -22495,7 +23414,7 @@ async function setupLocalAsterConfig(options) {
|
|
|
22495
23414
|
const requestedWorkspaces = await createWorkspaceEntries(options.workspacePaths);
|
|
22496
23415
|
const env = options.env ?? process.env;
|
|
22497
23416
|
const requestedPath = options.configPath?.trim() || env.AUTOPILOT_ASTER_CONFIG?.trim() || localAsterConfigPath(env);
|
|
22498
|
-
const path =
|
|
23417
|
+
const path = resolve15(requestedPath);
|
|
22499
23418
|
if (existsSync12(path)) {
|
|
22500
23419
|
const config3 = await loadAsterServerConfig(path);
|
|
22501
23420
|
const selectedWorkspaces = requestedWorkspaces.map((requested) => {
|
|
@@ -22511,21 +23430,21 @@ async function setupLocalAsterConfig(options) {
|
|
|
22511
23430
|
models: models.length > 0 ? models : "openrouter",
|
|
22512
23431
|
workspaces: requestedWorkspaces
|
|
22513
23432
|
};
|
|
22514
|
-
const directory =
|
|
22515
|
-
await
|
|
22516
|
-
await
|
|
23433
|
+
const directory = dirname22(path);
|
|
23434
|
+
await mkdir19(directory, { recursive: true, mode: 448 });
|
|
23435
|
+
await chmod5(directory, 448).catch(() => {
|
|
22517
23436
|
});
|
|
22518
|
-
const temporaryPath = `${path}.tmp-${
|
|
23437
|
+
const temporaryPath = `${path}.tmp-${randomUUID9()}`;
|
|
22519
23438
|
try {
|
|
22520
|
-
await
|
|
23439
|
+
await writeFile17(temporaryPath, `${JSON.stringify(config2, null, 2)}
|
|
22521
23440
|
`, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
22522
|
-
await
|
|
23441
|
+
await chmod5(temporaryPath, 384);
|
|
22523
23442
|
const validated = await loadAsterServerConfig(temporaryPath);
|
|
22524
23443
|
await link(temporaryPath, path);
|
|
22525
|
-
await
|
|
23444
|
+
await rm7(temporaryPath);
|
|
22526
23445
|
return { path, config: validated, selectedWorkspaces: validated.workspaces };
|
|
22527
23446
|
} catch (error) {
|
|
22528
|
-
await
|
|
23447
|
+
await rm7(temporaryPath, { force: true }).catch(() => {
|
|
22529
23448
|
});
|
|
22530
23449
|
if (isAlreadyExists(error)) {
|
|
22531
23450
|
return setupLocalAsterConfig(options);
|
|
@@ -22537,11 +23456,11 @@ async function createWorkspaceEntries(paths) {
|
|
|
22537
23456
|
const usedIds = /* @__PURE__ */ new Set();
|
|
22538
23457
|
const workspaces = [];
|
|
22539
23458
|
for (const input of paths) {
|
|
22540
|
-
const requested =
|
|
23459
|
+
const requested = resolve15(input);
|
|
22541
23460
|
let root;
|
|
22542
23461
|
try {
|
|
22543
23462
|
root = await realpath6(requested);
|
|
22544
|
-
if (!(await
|
|
23463
|
+
if (!(await stat12(root)).isDirectory()) throw new Error("not a directory");
|
|
22545
23464
|
const output = execFileSync3("git", ["-C", root, "rev-parse", "--show-toplevel"], {
|
|
22546
23465
|
encoding: "utf8",
|
|
22547
23466
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -22588,13 +23507,13 @@ __export(aster_store_exports, {
|
|
|
22588
23507
|
normalizeUserCode: () => normalizeUserCode
|
|
22589
23508
|
});
|
|
22590
23509
|
import Database4 from "better-sqlite3";
|
|
22591
|
-
import { createHash as createHash7, randomBytes as randomBytes3, randomInt, randomUUID as
|
|
23510
|
+
import { createHash as createHash7, randomBytes as randomBytes3, randomInt, randomUUID as randomUUID10 } from "crypto";
|
|
22592
23511
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync10 } from "fs";
|
|
22593
|
-
import { homedir as
|
|
22594
|
-
import { dirname as
|
|
23512
|
+
import { homedir as homedir29 } from "os";
|
|
23513
|
+
import { dirname as dirname23, join as join47 } from "path";
|
|
22595
23514
|
function defaultAsterDbPath() {
|
|
22596
|
-
const root = process.env.XDG_STATE_HOME ||
|
|
22597
|
-
return process.env.AUTOPILOT_ASTER_DB ||
|
|
23515
|
+
const root = process.env.XDG_STATE_HOME || join47(homedir29(), ".local", "state");
|
|
23516
|
+
return process.env.AUTOPILOT_ASTER_DB || join47(root, "autopilot", "aster.db");
|
|
22598
23517
|
}
|
|
22599
23518
|
function hashToken(token) {
|
|
22600
23519
|
return createHash7("sha256").update(token).digest("hex");
|
|
@@ -22681,9 +23600,9 @@ var init_aster_store = __esm({
|
|
|
22681
23600
|
AsterStore = class {
|
|
22682
23601
|
db;
|
|
22683
23602
|
constructor(dbPath = defaultAsterDbPath()) {
|
|
22684
|
-
mkdirSync10(
|
|
23603
|
+
mkdirSync10(dirname23(dbPath), { recursive: true, mode: 448 });
|
|
22685
23604
|
try {
|
|
22686
|
-
chmodSync2(
|
|
23605
|
+
chmodSync2(dirname23(dbPath), 448);
|
|
22687
23606
|
} catch {
|
|
22688
23607
|
}
|
|
22689
23608
|
this.db = new Database4(dbPath);
|
|
@@ -22898,7 +23817,7 @@ var init_aster_store = __esm({
|
|
|
22898
23817
|
const scopes = [...new Set(input.scopes)];
|
|
22899
23818
|
if (scopes.length === 0 || scopes.some((scope) => !VALID_SCOPES.has(scope))) throw new Error("credential scopes are invalid");
|
|
22900
23819
|
if (!Number.isSafeInteger(input.expiresAt) || input.expiresAt <= Date.now()) throw new Error("credential expiry must be in the future");
|
|
22901
|
-
const id =
|
|
23820
|
+
const id = randomUUID10();
|
|
22902
23821
|
const token = `aster_${randomBytes3(32).toString("base64url")}`;
|
|
22903
23822
|
const now2 = Date.now();
|
|
22904
23823
|
this.db.prepare(`INSERT INTO aster_credentials
|
|
@@ -22952,7 +23871,7 @@ var init_aster_store = __esm({
|
|
|
22952
23871
|
}
|
|
22953
23872
|
/** The owner's decision on a user code. An approval fixes the credential the client will receive. */
|
|
22954
23873
|
resolveDeviceAuthorization(userCode, decision, grant, now2 = Date.now()) {
|
|
22955
|
-
const
|
|
23874
|
+
const resolve22 = this.db.transaction(() => {
|
|
22956
23875
|
const row = this.db.prepare("SELECT * FROM aster_device_authorizations WHERE user_code = ?").get(normalizeUserCode(userCode));
|
|
22957
23876
|
if (!row) return "not_found";
|
|
22958
23877
|
if (row.status !== "pending") return "already_resolved";
|
|
@@ -22965,7 +23884,7 @@ var init_aster_store = __esm({
|
|
|
22965
23884
|
SET status = 'approved', workspace_ids_json = ?, credential_ttl_ms = ?, resolved_at = ? WHERE user_code = ?`).run(JSON.stringify(grant.workspaceIds), grant.credentialTtlMs, now2, row.user_code);
|
|
22966
23885
|
return "approved";
|
|
22967
23886
|
});
|
|
22968
|
-
return
|
|
23887
|
+
return resolve22.immediate();
|
|
22969
23888
|
}
|
|
22970
23889
|
/** The client's poll. An approved code is exchanged for its credential exactly once. */
|
|
22971
23890
|
redeemDeviceAuthorization(deviceCode, now2 = Date.now()) {
|
|
@@ -23212,7 +24131,7 @@ var init_aster_store = __esm({
|
|
|
23212
24131
|
}));
|
|
23213
24132
|
}
|
|
23214
24133
|
createApproval(input) {
|
|
23215
|
-
const id =
|
|
24134
|
+
const id = randomUUID10();
|
|
23216
24135
|
const now2 = Date.now();
|
|
23217
24136
|
this.db.prepare(`INSERT INTO aster_approvals
|
|
23218
24137
|
(id, conversation_id, run_id, tool_name, explanation, arguments_json, status, expires_at, created_at)
|
|
@@ -23315,9 +24234,9 @@ async function handleAsterPairing(req, res, url, store) {
|
|
|
23315
24234
|
const method = req.method ?? "GET";
|
|
23316
24235
|
if (path === `${AUTH_PREFIX}/device` && method === "POST") {
|
|
23317
24236
|
const body = await readJsonBody(req);
|
|
23318
|
-
const
|
|
23319
|
-
if (!CLIENT_NAME_RE.test(
|
|
23320
|
-
const started2 = store.createDeviceAuthorization({ clientName, ttlMs: DEVICE_CODE_TTL_MS, maxPending: MAX_PENDING_DEVICE_CODES });
|
|
24237
|
+
const clientName2 = typeof body.clientName === "string" ? body.clientName.trim() : "Autopilot CLI";
|
|
24238
|
+
if (!CLIENT_NAME_RE.test(clientName2)) return sendError(res, 400, "invalid_client_name", "clientName must be 1-60 letters, digits, spaces, or ._'()@-");
|
|
24239
|
+
const started2 = store.createDeviceAuthorization({ clientName: clientName2, ttlMs: DEVICE_CODE_TTL_MS, maxPending: MAX_PENDING_DEVICE_CODES });
|
|
23321
24240
|
if (!started2) return sendError(res, 429, "too_many_pending", "Too many pairings are waiting for approval; try again later");
|
|
23322
24241
|
const verificationUri = `${publicBaseUrl(req)}${AUTH_PREFIX}/approve`;
|
|
23323
24242
|
return json(res, 200, {
|
|
@@ -23464,9 +24383,9 @@ function notOwnerBody(code) {
|
|
|
23464
24383
|
<p>You can also approve from the server directly:</p>
|
|
23465
24384
|
<pre>autopilot aster pair approve ${escapeHtml2(code ?? "<code>")}</pre>`;
|
|
23466
24385
|
}
|
|
23467
|
-
function approvalForm(userCode,
|
|
24386
|
+
function approvalForm(userCode, clientName2) {
|
|
23468
24387
|
return `
|
|
23469
|
-
<p><strong>${escapeHtml2(
|
|
24388
|
+
<p><strong>${escapeHtml2(clientName2)}</strong> wants to use this server.</p>
|
|
23470
24389
|
<p class="code">${escapeHtml2(userCode)}</p>
|
|
23471
24390
|
<p class="muted">Approve only if this matches the code shown on your device.</p>
|
|
23472
24391
|
<form method="post" action="${AUTH_PREFIX}/approve">
|
|
@@ -23587,7 +24506,7 @@ __export(keys_exports, {
|
|
|
23587
24506
|
saveProviderKey: () => saveProviderKey,
|
|
23588
24507
|
switchProvider: () => switchProvider
|
|
23589
24508
|
});
|
|
23590
|
-
import { readFile as
|
|
24509
|
+
import { readFile as readFile33 } from "fs/promises";
|
|
23591
24510
|
function looksLikeProviderKey(id, key) {
|
|
23592
24511
|
if (id === "openrouter") return looksLikeOpenRouterKey(key);
|
|
23593
24512
|
return key.length >= 8 && !/\s/.test(key);
|
|
@@ -23670,7 +24589,7 @@ async function checkProviderKey(id, key, baseUrl, fetchImpl2 = fetch) {
|
|
|
23670
24589
|
}
|
|
23671
24590
|
async function persistedProviders() {
|
|
23672
24591
|
try {
|
|
23673
|
-
const raw = JSON.parse(await
|
|
24592
|
+
const raw = JSON.parse(await readFile33(configPath(), "utf8"));
|
|
23674
24593
|
return raw.providers ?? {};
|
|
23675
24594
|
} catch {
|
|
23676
24595
|
return {};
|
|
@@ -23948,7 +24867,7 @@ __export(github_auth_exports, {
|
|
|
23948
24867
|
authGitHubForTui: () => authGitHubForTui
|
|
23949
24868
|
});
|
|
23950
24869
|
function sleep4(ms) {
|
|
23951
|
-
return new Promise((
|
|
24870
|
+
return new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
23952
24871
|
}
|
|
23953
24872
|
async function* authGitHubForTui() {
|
|
23954
24873
|
yield { message: "Starting GitHub OAuth device flow..." };
|
|
@@ -24955,13 +25874,13 @@ var init_openapi = __esm({
|
|
|
24955
25874
|
});
|
|
24956
25875
|
|
|
24957
25876
|
// src/runs/worktrees.ts
|
|
24958
|
-
import { execFile as
|
|
24959
|
-
import { access as access2, chmod as
|
|
24960
|
-
import { homedir as
|
|
24961
|
-
import { isAbsolute as isAbsolute6, join as
|
|
25877
|
+
import { execFile as execFile11 } from "child_process";
|
|
25878
|
+
import { access as access2, chmod as chmod6, mkdir as mkdir20, realpath as realpath7 } from "fs/promises";
|
|
25879
|
+
import { homedir as homedir30 } from "os";
|
|
25880
|
+
import { isAbsolute as isAbsolute6, join as join48, parse, relative as relative9, resolve as resolve16, sep as sep5 } from "path";
|
|
24962
25881
|
function defaultWorktreesDir() {
|
|
24963
|
-
const dataHome = process.env.XDG_DATA_HOME ||
|
|
24964
|
-
return
|
|
25882
|
+
const dataHome = process.env.XDG_DATA_HOME || join48(homedir30(), ".local", "share");
|
|
25883
|
+
return join48(dataHome, "autopilot", "worktrees");
|
|
24965
25884
|
}
|
|
24966
25885
|
function isOutside(path) {
|
|
24967
25886
|
return path === ".." || path.startsWith(`..${sep5}`) || isAbsolute6(path);
|
|
@@ -24972,7 +25891,7 @@ function isWithin3(parent, candidate) {
|
|
|
24972
25891
|
}
|
|
24973
25892
|
function runGit(args, cwd) {
|
|
24974
25893
|
return new Promise((resolveOutput, reject) => {
|
|
24975
|
-
|
|
25894
|
+
execFile11("git", args, { cwd, encoding: "utf8", timeout: 3e4, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
|
|
24976
25895
|
if (error) {
|
|
24977
25896
|
const detail = stderr.trim();
|
|
24978
25897
|
reject(new Error(`git ${args[0]} failed${detail ? `: ${detail}` : ""}`, { cause: error }));
|
|
@@ -25002,17 +25921,17 @@ var init_worktrees = __esm({
|
|
|
25002
25921
|
if (isOutside(sourceRelativePath)) {
|
|
25003
25922
|
throw new Error("sourceCwd must be inside the Git repository");
|
|
25004
25923
|
}
|
|
25005
|
-
const configuredRoot =
|
|
25924
|
+
const configuredRoot = resolve16(this.rootDir);
|
|
25006
25925
|
if (configuredRoot === parse(configuredRoot).root || isWithin3(repository, configuredRoot)) {
|
|
25007
25926
|
throw new Error("worktree root must be outside the source repository and not the filesystem root");
|
|
25008
25927
|
}
|
|
25009
|
-
await
|
|
25928
|
+
await mkdir20(configuredRoot, { recursive: true, mode: 448 });
|
|
25010
25929
|
const worktreesRoot = await realpath7(configuredRoot);
|
|
25011
25930
|
if (isWithin3(repository, worktreesRoot)) {
|
|
25012
25931
|
throw new Error("worktree root must be outside the source repository");
|
|
25013
25932
|
}
|
|
25014
|
-
await
|
|
25015
|
-
const worktreePath =
|
|
25933
|
+
await chmod6(worktreesRoot, 448);
|
|
25934
|
+
const worktreePath = join48(worktreesRoot, runId);
|
|
25016
25935
|
try {
|
|
25017
25936
|
await access2(worktreePath);
|
|
25018
25937
|
throw new Error(`worktree path already exists for run ${runId}`);
|
|
@@ -25026,7 +25945,7 @@ var init_worktrees = __esm({
|
|
|
25026
25945
|
repositoryRoot: repository,
|
|
25027
25946
|
sourceCwd: source,
|
|
25028
25947
|
worktreePath,
|
|
25029
|
-
cwd:
|
|
25948
|
+
cwd: resolve16(worktreePath, sourceRelativePath),
|
|
25030
25949
|
branch
|
|
25031
25950
|
};
|
|
25032
25951
|
}
|
|
@@ -25108,10 +26027,10 @@ var init_wake_scheduler = __esm({
|
|
|
25108
26027
|
scheduleNextTick() {
|
|
25109
26028
|
if (!this.running || this.disposed) return;
|
|
25110
26029
|
const nextAt = this.store.nextTimerAt();
|
|
25111
|
-
const
|
|
26030
|
+
const delay2 = nextAt === null ? IDLE_RECHECK_MS : Math.max(0, Math.min(MAX_NODE_TIMEOUT_MS, nextAt - Date.now()));
|
|
25112
26031
|
this.timer = setTimeout(() => {
|
|
25113
26032
|
void this.tick();
|
|
25114
|
-
},
|
|
26033
|
+
}, delay2);
|
|
25115
26034
|
this.timer.unref();
|
|
25116
26035
|
}
|
|
25117
26036
|
async processTimer(timer2, now2) {
|
|
@@ -25473,9 +26392,9 @@ var init_aster_tools = __esm({
|
|
|
25473
26392
|
|
|
25474
26393
|
// src/server/aster-memory.ts
|
|
25475
26394
|
import Database5 from "better-sqlite3";
|
|
25476
|
-
import { randomUUID as
|
|
26395
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
25477
26396
|
import { chmodSync as chmodSync3, mkdirSync as mkdirSync11 } from "fs";
|
|
25478
|
-
import { dirname as
|
|
26397
|
+
import { dirname as dirname24 } from "path";
|
|
25479
26398
|
function rowToItem(row) {
|
|
25480
26399
|
return {
|
|
25481
26400
|
id: row.id,
|
|
@@ -25528,7 +26447,7 @@ var init_aster_memory = __esm({
|
|
|
25528
26447
|
AsterMemory = class {
|
|
25529
26448
|
constructor(dbPath = defaultAsterDbPath(), options = {}) {
|
|
25530
26449
|
this.options = options;
|
|
25531
|
-
mkdirSync11(
|
|
26450
|
+
mkdirSync11(dirname24(dbPath), { recursive: true, mode: 448 });
|
|
25532
26451
|
this.db = new Database5(dbPath);
|
|
25533
26452
|
this.db.pragma("journal_mode = WAL");
|
|
25534
26453
|
this.db.pragma("busy_timeout = 5000");
|
|
@@ -25592,7 +26511,7 @@ var init_aster_memory = __esm({
|
|
|
25592
26511
|
this.db.prepare("UPDATE aster_memories SET title = ?, content = ?, pinned = ?, updated_at = ? WHERE id = ?").run(title, content, existing.pinned || input.pinned ? 1 : 0, now2, existing.id);
|
|
25593
26512
|
return { item: this.get(existing.id), change: "updated" };
|
|
25594
26513
|
}
|
|
25595
|
-
const id =
|
|
26514
|
+
const id = randomUUID11();
|
|
25596
26515
|
this.db.prepare(`INSERT INTO aster_memories (id, kind, key, title, content, pinned, source_conversation_id, created_at, updated_at)
|
|
25597
26516
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.kind, input.key ?? null, title, content, input.pinned ? 1 : 0, input.sourceConversationId ?? null, now2, now2);
|
|
25598
26517
|
return { item: this.get(id), change: "added" };
|
|
@@ -25780,14 +26699,14 @@ function parseReceivePackCommands(data) {
|
|
|
25780
26699
|
function isSafeBranch(branch) {
|
|
25781
26700
|
return /^[A-Za-z0-9._/-]{1,200}$/.test(branch) && !branch.startsWith("-") && !branch.endsWith("/") && !branch.includes("..") && !branch.includes("//") && !branch.includes("@{") && branch.split("/").every((part) => part !== "" && part !== "." && part !== ".." && !part.endsWith(".lock"));
|
|
25782
26701
|
}
|
|
25783
|
-
function upstreamHeaders(req, token,
|
|
26702
|
+
function upstreamHeaders(req, token, git5) {
|
|
25784
26703
|
const headers = {};
|
|
25785
26704
|
for (const name of FORWARDED_REQUEST_HEADERS) {
|
|
25786
26705
|
const value = req.headers[name];
|
|
25787
26706
|
if (typeof value === "string") headers[name] = value;
|
|
25788
26707
|
}
|
|
25789
26708
|
headers["user-agent"] ??= "autopilot-aster";
|
|
25790
|
-
headers.authorization =
|
|
26709
|
+
headers.authorization = git5 ? "Basic " + Buffer.from(`x-access-token:${token}`).toString("base64") : `Bearer ${token}`;
|
|
25791
26710
|
return headers;
|
|
25792
26711
|
}
|
|
25793
26712
|
function pipeResponse(upstreamRes, res) {
|
|
@@ -25837,9 +26756,9 @@ var init_aster_github_proxy = __esm({
|
|
|
25837
26756
|
gitUpstream;
|
|
25838
26757
|
async listen(port, host) {
|
|
25839
26758
|
this.server = createServer2((req, res) => this.handle(req, res));
|
|
25840
|
-
await new Promise((
|
|
26759
|
+
await new Promise((resolve22, reject) => {
|
|
25841
26760
|
this.server.once("error", reject);
|
|
25842
|
-
this.server.listen(port, host, () =>
|
|
26761
|
+
this.server.listen(port, host, () => resolve22());
|
|
25843
26762
|
});
|
|
25844
26763
|
const address = this.server.address();
|
|
25845
26764
|
return typeof address === "object" && address ? address.port : port;
|
|
@@ -25850,8 +26769,8 @@ var init_aster_github_proxy = __esm({
|
|
|
25850
26769
|
}
|
|
25851
26770
|
handle(req, res, pathOverride) {
|
|
25852
26771
|
const url = new URL(pathOverride ?? req.url ?? "/", "http://proxy.local");
|
|
25853
|
-
const
|
|
25854
|
-
if (!
|
|
26772
|
+
const git5 = url.pathname.match(GIT_PATH_RE);
|
|
26773
|
+
if (!git5) return deny(res, 404, "Only Git Smart HTTP is available through this proxy");
|
|
25855
26774
|
const grantToken = extractToken(req);
|
|
25856
26775
|
if (!grantToken) {
|
|
25857
26776
|
res.writeHead(401, { "www-authenticate": 'Basic realm="aster-github"', "content-type": "text/plain" });
|
|
@@ -25860,7 +26779,7 @@ var init_aster_github_proxy = __esm({
|
|
|
25860
26779
|
}
|
|
25861
26780
|
const grant = this.options.resolveGrant(grantToken);
|
|
25862
26781
|
if (!grant) return deny(res, 403, "This repository grant is invalid or was revoked");
|
|
25863
|
-
const [, owner, name, service] =
|
|
26782
|
+
const [, owner, name, service] = git5;
|
|
25864
26783
|
if (`${owner}/${name}`.toLowerCase() !== grant.repository.toLowerCase()) {
|
|
25865
26784
|
return deny(res, 403, `This conversation can only access ${grant.repository}`);
|
|
25866
26785
|
}
|
|
@@ -26021,7 +26940,7 @@ var init_aster_push = __esm({
|
|
|
26021
26940
|
};
|
|
26022
26941
|
if (this.options.deliver) return this.options.deliver({ host, token, headers, payload });
|
|
26023
26942
|
const session = this.session(host);
|
|
26024
|
-
return await new Promise((
|
|
26943
|
+
return await new Promise((resolve22, reject) => {
|
|
26025
26944
|
const stream = session.request({ ":method": "POST", ":path": `/3/device/${token}`, ...headers, "content-type": "application/json" });
|
|
26026
26945
|
let status = 0;
|
|
26027
26946
|
stream.setTimeout(15e3, () => stream.close());
|
|
@@ -26029,7 +26948,7 @@ var init_aster_push = __esm({
|
|
|
26029
26948
|
status = Number(responseHeaders[":status"]) || 0;
|
|
26030
26949
|
});
|
|
26031
26950
|
stream.on("error", reject);
|
|
26032
|
-
stream.on("close", () =>
|
|
26951
|
+
stream.on("close", () => resolve22(status));
|
|
26033
26952
|
stream.resume();
|
|
26034
26953
|
stream.end(payload);
|
|
26035
26954
|
});
|
|
@@ -26060,9 +26979,9 @@ var init_aster_push = __esm({
|
|
|
26060
26979
|
});
|
|
26061
26980
|
|
|
26062
26981
|
// src/server/aster-api.ts
|
|
26063
|
-
import { randomUUID as
|
|
26982
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
26064
26983
|
import { unlink as unlink3 } from "fs/promises";
|
|
26065
|
-
import { resolve as
|
|
26984
|
+
import { resolve as resolve17 } from "path";
|
|
26066
26985
|
function parseCursor(header, query) {
|
|
26067
26986
|
const value = (Array.isArray(header) ? header[0] : header) ?? query ?? "0";
|
|
26068
26987
|
if (!/^\d{1,16}$/.test(value)) return void 0;
|
|
@@ -26164,7 +27083,7 @@ function json2(res, status, data) {
|
|
|
26164
27083
|
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store" });
|
|
26165
27084
|
res.end(JSON.stringify(data));
|
|
26166
27085
|
}
|
|
26167
|
-
var API_PREFIX, MAX_BODY_BYTES, MAX_USER_TURN_CHARS, CLIENT_TURN_ID_RE, CREATE_IDEMPOTENCY_KEY_RE, MAX_APPROVAL_ARG_BYTES, CONVERSATION_ID_RE, ALLOWED_ASTER_TOOLS, BASE_COMMIT_RE,
|
|
27086
|
+
var API_PREFIX, MAX_BODY_BYTES, MAX_USER_TURN_CHARS, CLIENT_TURN_ID_RE, CREATE_IDEMPOTENCY_KEY_RE, MAX_APPROVAL_ARG_BYTES, CONVERSATION_ID_RE, ALLOWED_ASTER_TOOLS, BASE_COMMIT_RE, MAX_PATCH_CHARS2, MAX_LISTED_CONVERSATIONS, AsterApi;
|
|
26168
27087
|
var init_aster_api = __esm({
|
|
26169
27088
|
"src/server/aster-api.ts"() {
|
|
26170
27089
|
"use strict";
|
|
@@ -26190,7 +27109,7 @@ var init_aster_api = __esm({
|
|
|
26190
27109
|
CONVERSATION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
26191
27110
|
ALLOWED_ASTER_TOOLS = ["read", "write", "edit"];
|
|
26192
27111
|
BASE_COMMIT_RE = /^[0-9a-f]{40}$/;
|
|
26193
|
-
|
|
27112
|
+
MAX_PATCH_CHARS2 = 3 * 1024 * 1024;
|
|
26194
27113
|
MAX_LISTED_CONVERSATIONS = 200;
|
|
26195
27114
|
AsterApi = class {
|
|
26196
27115
|
store;
|
|
@@ -26310,7 +27229,7 @@ var init_aster_api = __esm({
|
|
|
26310
27229
|
if (body.baseCommit !== void 0 && (typeof body.baseCommit !== "string" || !BASE_COMMIT_RE.test(body.baseCommit))) {
|
|
26311
27230
|
return sendError2(res, 400, "invalid_base_commit", "baseCommit must be a full 40-character commit SHA");
|
|
26312
27231
|
}
|
|
26313
|
-
if (body.patch !== void 0 && (typeof body.patch !== "string" || body.patch.length >
|
|
27232
|
+
if (body.patch !== void 0 && (typeof body.patch !== "string" || body.patch.length > MAX_PATCH_CHARS2)) {
|
|
26314
27233
|
return sendError2(res, 400, "invalid_patch", "patch must be a git diff of at most 3 MiB");
|
|
26315
27234
|
}
|
|
26316
27235
|
if (typeof body.patch === "string" && containsLikelyProviderSecret(body.patch)) {
|
|
@@ -26675,8 +27594,8 @@ var init_aster_api = __esm({
|
|
|
26675
27594
|
};
|
|
26676
27595
|
}
|
|
26677
27596
|
async createConversation(res, store, principal, workspace, model, idempotencyKey, kind, repository, changes) {
|
|
26678
|
-
const candidateId =
|
|
26679
|
-
const candidateSessionId =
|
|
27597
|
+
const candidateId = randomUUID12();
|
|
27598
|
+
const candidateSessionId = randomUUID12();
|
|
26680
27599
|
const reservation = store.reserveCellConversation({
|
|
26681
27600
|
conversationId: candidateId,
|
|
26682
27601
|
credentialId: principal.credentialId,
|
|
@@ -26817,12 +27736,12 @@ var init_aster_api = __esm({
|
|
|
26817
27736
|
sendError2(res, 409, "conversation_cell_unavailable", "Conversation cell is not available");
|
|
26818
27737
|
return;
|
|
26819
27738
|
}
|
|
26820
|
-
const runId =
|
|
27739
|
+
const runId = randomUUID12();
|
|
26821
27740
|
if (!idempotent && !store.beginTurn(conversation.id, runId)) {
|
|
26822
27741
|
sendError2(res, 409, "conversation_busy", "A turn is already active for this conversation");
|
|
26823
27742
|
return;
|
|
26824
27743
|
}
|
|
26825
|
-
const sessionPath =
|
|
27744
|
+
const sessionPath = resolve17(sessionsDir(), conversation.sessionId + ".json");
|
|
26826
27745
|
let sessionFile;
|
|
26827
27746
|
try {
|
|
26828
27747
|
sessionFile = await loadSession(sessionPath);
|
|
@@ -27132,7 +28051,7 @@ var init_aster_api = __esm({
|
|
|
27132
28051
|
}
|
|
27133
28052
|
try {
|
|
27134
28053
|
if (mapping.cellId) await this.runtime.destroyCell(mapping.cellId);
|
|
27135
|
-
await unlink3(
|
|
28054
|
+
await unlink3(resolve17(sessionsDir(), mapping.sessionId + ".json")).catch(() => {
|
|
27136
28055
|
});
|
|
27137
28056
|
store.setCellStatus(conversation.id, "destroyed");
|
|
27138
28057
|
store.setConversationStatus(conversation.id, "destroyed");
|
|
@@ -27179,7 +28098,7 @@ var init_aster_api = __esm({
|
|
|
27179
28098
|
return void 0;
|
|
27180
28099
|
}
|
|
27181
28100
|
try {
|
|
27182
|
-
const sessionPath =
|
|
28101
|
+
const sessionPath = resolve17(sessionsDir(), conversation.sessionId + ".json");
|
|
27183
28102
|
const sessionFile = await loadSession(sessionPath);
|
|
27184
28103
|
sessionFile.model = model;
|
|
27185
28104
|
await saveSession(sessionFile);
|
|
@@ -27318,10 +28237,10 @@ data: ${JSON.stringify(event.data)}
|
|
|
27318
28237
|
});
|
|
27319
28238
|
|
|
27320
28239
|
// src/server/aster-cell-runtime.ts
|
|
27321
|
-
import { execFile as
|
|
27322
|
-
import { readFile as
|
|
27323
|
-
import { promisify as
|
|
27324
|
-
import { randomUUID as
|
|
28240
|
+
import { execFile as execFile12 } from "child_process";
|
|
28241
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
28242
|
+
import { promisify as promisify10 } from "util";
|
|
28243
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
27325
28244
|
async function writeCellFileChunked(cell, path, content) {
|
|
27326
28245
|
if (Buffer.byteLength(content, "utf8") <= CELL_FILE_WRITE_CHUNK_CHARS) {
|
|
27327
28246
|
await cell.writeFile(path, content, { mode: "0600" });
|
|
@@ -27349,14 +28268,14 @@ async function writeCellFileChunked(cell, path, content) {
|
|
|
27349
28268
|
async function readBundledCellSdkBundle() {
|
|
27350
28269
|
for (const url of [new URL("./sdk/index.js", import.meta.url), new URL("../sdk/index.js", import.meta.url)]) {
|
|
27351
28270
|
try {
|
|
27352
|
-
return await
|
|
28271
|
+
return await readFile34(url, "utf8");
|
|
27353
28272
|
} catch {
|
|
27354
28273
|
}
|
|
27355
28274
|
}
|
|
27356
28275
|
throw new Error("cell_runtime_bundle_unavailable");
|
|
27357
28276
|
}
|
|
27358
28277
|
async function defaultArchiveWorkspace(workspaceRoot) {
|
|
27359
|
-
const { stdout } = await
|
|
28278
|
+
const { stdout } = await execFileAsync8("git", ["-C", workspaceRoot, "archive", "--format=tar", "HEAD"], {
|
|
27360
28279
|
encoding: "buffer",
|
|
27361
28280
|
maxBuffer: 512 * 1024 * 1024
|
|
27362
28281
|
});
|
|
@@ -27402,15 +28321,15 @@ function boundedToolText(text3, limit) {
|
|
|
27402
28321
|
${redacted.slice(-half)}`;
|
|
27403
28322
|
}
|
|
27404
28323
|
function sleep5(ms) {
|
|
27405
|
-
return new Promise((
|
|
28324
|
+
return new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
27406
28325
|
}
|
|
27407
|
-
var
|
|
28326
|
+
var execFileAsync8, CELL_WORKSPACE, CELL_STATE_DIR, CHECKPOINT_MESSAGE_FILE, CELL_RUNTIME_DIR, INSTALLED_RUNTIME_PACKAGE, CELL_RUNTIME_SDK_ENTRY, NOTES_FILE, MEMORY_FILE, MEMORY_EXTRACTION_TIMEOUT_MS, LIVENESS_CHECK_MS, ARTIFACTS_DIR, MAX_ARTIFACTS, MAX_ARTIFACT_BYTES, BRANCH_RE2, REPOSITORY_RE, GIT, COMMIT_SHA_RE, FULL_COMMIT_SHA_RE, LOCAL_CHANGES_PATCH, MAX_TOOL_OUTPUT_CHARS, MAX_TOOL_INPUT_CHARS, MAX_CHECKPOINTS, CELL_CONTROL_DIR, BRIDGE_MARKER, CONTROL_RUNNER, BRIDGE_RUNNER, HEALTH_TIMEOUT_MS, EVENT_POLL_MS, AsterHotcellRuntime, CELL_FILE_WRITE_CHUNK_CHARS, ARTIFACT_MIME_TYPES;
|
|
27408
28327
|
var init_aster_cell_runtime = __esm({
|
|
27409
28328
|
"src/server/aster-cell-runtime.ts"() {
|
|
27410
28329
|
"use strict";
|
|
27411
28330
|
init_version();
|
|
27412
28331
|
init_aster_tools();
|
|
27413
|
-
|
|
28332
|
+
execFileAsync8 = promisify10(execFile12);
|
|
27414
28333
|
CELL_WORKSPACE = "/workspace";
|
|
27415
28334
|
CELL_STATE_DIR = "/workspace/.aster";
|
|
27416
28335
|
CHECKPOINT_MESSAGE_FILE = `${CELL_STATE_DIR}/checkpoint-message`;
|
|
@@ -27519,7 +28438,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
27519
28438
|
await this.installRuntime(cell);
|
|
27520
28439
|
await this.ensureBridge(cell);
|
|
27521
28440
|
await this.control(cell, "POST", "/rpc", {
|
|
27522
|
-
id:
|
|
28441
|
+
id: randomUUID13(),
|
|
27523
28442
|
type: "new_session",
|
|
27524
28443
|
sessionId: input.sessionId,
|
|
27525
28444
|
config: { model: input.model, cwd: CELL_WORKSPACE }
|
|
@@ -27538,7 +28457,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
27538
28457
|
/** Switch the cell session's model between turns. The bridge persists it for session restore. */
|
|
27539
28458
|
async setModel(cellId, model) {
|
|
27540
28459
|
await this.ensureReady(cellId);
|
|
27541
|
-
await this.controlById(cellId, "POST", "/rpc", { id:
|
|
28460
|
+
await this.controlById(cellId, "POST", "/rpc", { id: randomUUID13(), type: "set_model", modelId: model });
|
|
27542
28461
|
}
|
|
27543
28462
|
/** Reinstall the runtime if the cell's image lost it and restart the bridge if it isn't running. */
|
|
27544
28463
|
async ensureReady(cellId) {
|
|
@@ -27636,7 +28555,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
27636
28555
|
const active = this.activeRuns.get(runId);
|
|
27637
28556
|
if (!active) return;
|
|
27638
28557
|
active.stop();
|
|
27639
|
-
void this.controlById(active.cellId, "POST", "/rpc", { id:
|
|
28558
|
+
void this.controlById(active.cellId, "POST", "/rpc", { id: randomUUID13(), type: "abort" }).catch(() => {
|
|
27640
28559
|
});
|
|
27641
28560
|
}
|
|
27642
28561
|
async runTurn(turn, options = {}) {
|
|
@@ -27736,7 +28655,7 @@ ${turn.userText}` : turn.userText;
|
|
|
27736
28655
|
args: event.args ?? {}
|
|
27737
28656
|
});
|
|
27738
28657
|
await this.controlById(cellId, "POST", "/rpc", {
|
|
27739
|
-
id:
|
|
28658
|
+
id: randomUUID13(),
|
|
27740
28659
|
type: "resolve_permission",
|
|
27741
28660
|
requestId: event.requestId,
|
|
27742
28661
|
decision
|
|
@@ -27917,9 +28836,9 @@ ${turn.userText}` : turn.userText;
|
|
|
27917
28836
|
if (!isSafeArtifactPath(path)) throw new Error("invalid_artifact_path");
|
|
27918
28837
|
const cell = await this.provider.getCell(cellId);
|
|
27919
28838
|
const file = shellQuote2(`${ARTIFACTS_DIR}/${path}`);
|
|
27920
|
-
const
|
|
27921
|
-
if (
|
|
27922
|
-
if (Number(
|
|
28839
|
+
const stat15 = await cell.exec(`[ -f ${file} ] && [ ! -L ${file} ] && stat -c %s -- ${file}`);
|
|
28840
|
+
if (stat15.exitCode !== 0) throw new Error("artifact_not_found");
|
|
28841
|
+
if (Number(stat15.stdout.trim()) > MAX_ARTIFACT_BYTES) throw new Error("artifact_too_large");
|
|
27923
28842
|
const encoded = await cell.exec(`base64 -w0 -- ${file}`);
|
|
27924
28843
|
if (encoded.exitCode !== 0) throw new Error("artifact_not_found");
|
|
27925
28844
|
return { data: Buffer.from(encoded.stdout.trim(), "base64"), mimeType: artifactMimeType(path), name: path.split("/").pop() ?? path };
|
|
@@ -27998,7 +28917,7 @@ ${text3}` : text3, { mode: "0600" });
|
|
|
27998
28917
|
}
|
|
27999
28918
|
}
|
|
28000
28919
|
async control(cell, method, path, body) {
|
|
28001
|
-
const requestId =
|
|
28920
|
+
const requestId = randomUUID13();
|
|
28002
28921
|
await cell.mkdir(CELL_CONTROL_DIR, { parents: true });
|
|
28003
28922
|
await writeCellFileChunked(cell, `${CELL_CONTROL_DIR}/${requestId}.json`, JSON.stringify({ method, path, body }));
|
|
28004
28923
|
const result2 = await cell.exec(`${CONTROL_RUNNER} ${requestId}`);
|
|
@@ -28214,11 +29133,11 @@ var init_hotcell_provider = __esm({
|
|
|
28214
29133
|
});
|
|
28215
29134
|
|
|
28216
29135
|
// src/server/routes.ts
|
|
28217
|
-
import { randomUUID as
|
|
28218
|
-
import { hostname } from "os";
|
|
29136
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
29137
|
+
import { hostname as hostname2 } from "os";
|
|
28219
29138
|
import { URL as URL2 } from "url";
|
|
28220
29139
|
import { unlink as unlink4 } from "fs/promises";
|
|
28221
|
-
import { resolve as
|
|
29140
|
+
import { resolve as resolve18 } from "path";
|
|
28222
29141
|
function createHotcellAsterRuntime() {
|
|
28223
29142
|
try {
|
|
28224
29143
|
const idlePauseMs = Number(process.env.AUTOPILOT_ASTER_IDLE_PAUSE_MS ?? 15 * 60 * 1e3);
|
|
@@ -28282,6 +29201,9 @@ function json3(res, status, data) {
|
|
|
28282
29201
|
function badRequest(res, message2) {
|
|
28283
29202
|
json3(res, 400, { error: message2 });
|
|
28284
29203
|
}
|
|
29204
|
+
function promptBlocked(res, reason) {
|
|
29205
|
+
json3(res, 403, { error: "prompt_blocked", reason, message: `prompt blocked by a UserPromptSubmit hook: ${reason}` });
|
|
29206
|
+
}
|
|
28285
29207
|
function notFound(res, message2) {
|
|
28286
29208
|
json3(res, 404, { error: message2 });
|
|
28287
29209
|
}
|
|
@@ -28301,9 +29223,9 @@ async function resolveFiles2(filePatterns, cwd) {
|
|
|
28301
29223
|
const resolved = /* @__PURE__ */ new Set();
|
|
28302
29224
|
for (const pattern of filePatterns) {
|
|
28303
29225
|
try {
|
|
28304
|
-
const
|
|
28305
|
-
if (
|
|
28306
|
-
resolved.add(
|
|
29226
|
+
const stat15 = await import("fs/promises").then((m) => m.stat(resolve18(cwd, pattern)));
|
|
29227
|
+
if (stat15.isFile()) {
|
|
29228
|
+
resolved.add(resolve18(cwd, pattern));
|
|
28307
29229
|
continue;
|
|
28308
29230
|
}
|
|
28309
29231
|
} catch {
|
|
@@ -28353,7 +29275,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28353
29275
|
if (!run.sessionId) throw new Error("Cannot restore run without a session id: " + run.id);
|
|
28354
29276
|
const existing = activeRuns.get(run.id) ?? activeSessions.get(run.sessionId);
|
|
28355
29277
|
if (existing) return existing;
|
|
28356
|
-
const sessionFile = await loadSession(
|
|
29278
|
+
const sessionFile = await loadSession(resolve18(sessionsDir(), run.sessionId + ".json"));
|
|
28357
29279
|
const allowedTools = new Set(run.allowedTools);
|
|
28358
29280
|
allowedTools.add("wait_for");
|
|
28359
29281
|
const tools = ALL_TOOLS.filter((tool) => allowedTools.has(tool.name));
|
|
@@ -28405,7 +29327,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28405
29327
|
}
|
|
28406
29328
|
});
|
|
28407
29329
|
const notificationSettings = notificationSettingsFromConfig(config2);
|
|
28408
|
-
const stopNotifications = notificationSettings ? startNotificationDispatcher(() => new RunStore(), notificationSettings, { host:
|
|
29330
|
+
const stopNotifications = notificationSettings ? startNotificationDispatcher(() => new RunStore(), notificationSettings, { host: hostname2() }) : () => {
|
|
28409
29331
|
};
|
|
28410
29332
|
const imapSettings = notificationSettings?.decisionsReplyTo ? imapSettingsFromEnv() : null;
|
|
28411
29333
|
const stopDecisions = imapSettings && notificationSettings ? startDecisionPoller(imapSettings, async (mail) => {
|
|
@@ -28420,7 +29342,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28420
29342
|
},
|
|
28421
29343
|
modelExists: (id) => listModels().some((m) => m.id === id),
|
|
28422
29344
|
confirm: async (to, subject, text3) => {
|
|
28423
|
-
await notificationSettings.transport.send({ to: [to], from: notificationSettings.from, subject, text: text3, idempotencyKey: `confirm_${
|
|
29345
|
+
await notificationSettings.transport.send({ to: [to], from: notificationSettings.from, subject, text: text3, idempotencyKey: `confirm_${randomUUID14()}` });
|
|
28424
29346
|
}
|
|
28425
29347
|
}, { allowedSenders: notificationSettings.to, requireAuthentication: process.env.AUTOPILOT_DECISIONS_REQUIRE_AUTH !== "0" });
|
|
28426
29348
|
} finally {
|
|
@@ -28556,7 +29478,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28556
29478
|
const task = typeof body.task === "string" ? body.task.trim() : "";
|
|
28557
29479
|
let cwd;
|
|
28558
29480
|
try {
|
|
28559
|
-
cwd = typeof body.cwd === "string" ?
|
|
29481
|
+
cwd = typeof body.cwd === "string" ? resolve18(body.cwd) : process.cwd();
|
|
28560
29482
|
} catch {
|
|
28561
29483
|
badRequest(res, "cwd must be a valid path");
|
|
28562
29484
|
return;
|
|
@@ -28620,7 +29542,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28620
29542
|
const executor = new ToolExecutor(tools);
|
|
28621
29543
|
const { makeSessionId: makeSessionId2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
28622
29544
|
const sessionId = makeSessionId2(task);
|
|
28623
|
-
const runId =
|
|
29545
|
+
const runId = randomUUID14();
|
|
28624
29546
|
const worktreeManager = new RunWorktreeManager();
|
|
28625
29547
|
let worktree;
|
|
28626
29548
|
if (useWorktree) {
|
|
@@ -28669,7 +29591,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28669
29591
|
store.close();
|
|
28670
29592
|
}
|
|
28671
29593
|
} catch (error) {
|
|
28672
|
-
await unlink4(
|
|
29594
|
+
await unlink4(resolve18(sessionsDir(), `${sessionId}.json`)).catch(() => {
|
|
28673
29595
|
});
|
|
28674
29596
|
if (worktree) {
|
|
28675
29597
|
await worktreeManager.discard(worktree).catch((cleanupError) => {
|
|
@@ -28812,7 +29734,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28812
29734
|
return;
|
|
28813
29735
|
}
|
|
28814
29736
|
try {
|
|
28815
|
-
const file = await loadSession(
|
|
29737
|
+
const file = await loadSession(resolve18(sessionsDir(), `${sessionId}.json`));
|
|
28816
29738
|
json3(res, 200, {
|
|
28817
29739
|
id: file.id,
|
|
28818
29740
|
cwd: file.cwd,
|
|
@@ -28832,7 +29754,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
28832
29754
|
activeSessions.delete(sessionId);
|
|
28833
29755
|
try {
|
|
28834
29756
|
const { unlink: unlink7 } = await import("fs/promises");
|
|
28835
|
-
await unlink7(
|
|
29757
|
+
await unlink7(resolve18(sessionsDir(), `${sessionId}.json`));
|
|
28836
29758
|
} catch {
|
|
28837
29759
|
}
|
|
28838
29760
|
json3(res, 200, { deleted: sessionId });
|
|
@@ -28862,6 +29784,12 @@ function setupRoutes(config2, options = {}) {
|
|
|
28862
29784
|
title
|
|
28863
29785
|
};
|
|
28864
29786
|
const hooks = new HooksManager(cwd);
|
|
29787
|
+
const gate = await submitUserPrompt({ hooks, sessionId, cwd, prompt, resumed: false, model, auth: llmAuthFromConfig(config2) });
|
|
29788
|
+
if (gate.blocked) {
|
|
29789
|
+
await endSession(sessionId, "exit");
|
|
29790
|
+
promptBlocked(res, gate.reason);
|
|
29791
|
+
return;
|
|
29792
|
+
}
|
|
28865
29793
|
const executor = new ToolExecutor(ALL_TOOLS, { hooks });
|
|
28866
29794
|
const messages = [
|
|
28867
29795
|
{
|
|
@@ -28914,6 +29842,19 @@ function setupRoutes(config2, options = {}) {
|
|
|
28914
29842
|
badRequest(res, "prompt is required");
|
|
28915
29843
|
return;
|
|
28916
29844
|
}
|
|
29845
|
+
const gate = await submitUserPrompt({
|
|
29846
|
+
...active.hooks ? { hooks: active.hooks } : {},
|
|
29847
|
+
sessionId,
|
|
29848
|
+
cwd: active.sessionFile.cwd,
|
|
29849
|
+
prompt,
|
|
29850
|
+
resumed: active.messages.some((m) => m.role === "assistant"),
|
|
29851
|
+
model: active.sessionFile.model,
|
|
29852
|
+
auth: llmAuthFromConfig(config2)
|
|
29853
|
+
});
|
|
29854
|
+
if (gate.blocked) {
|
|
29855
|
+
promptBlocked(res, gate.reason);
|
|
29856
|
+
return;
|
|
29857
|
+
}
|
|
28917
29858
|
const resolvedFiles = await resolveFiles2(files, active.sessionFile.cwd);
|
|
28918
29859
|
const { content: userContent2 } = await buildAttachmentMessage(prompt, resolvedFiles, active.sessionFile.cwd, imageOptions(config2));
|
|
28919
29860
|
active.messages.push({ role: "user", content: userContent2 });
|
|
@@ -29237,6 +30178,7 @@ var init_routes = __esm({
|
|
|
29237
30178
|
"use strict";
|
|
29238
30179
|
init_session_core();
|
|
29239
30180
|
init_session_lifecycle();
|
|
30181
|
+
init_prompt_submit();
|
|
29240
30182
|
init_manager2();
|
|
29241
30183
|
init_exit();
|
|
29242
30184
|
init_registry();
|
|
@@ -29328,7 +30270,7 @@ async function startServer(opts2) {
|
|
|
29328
30270
|
server.keepAliveTimeout = 5e3;
|
|
29329
30271
|
const unregisterShutdown = onShutdown("server", async () => {
|
|
29330
30272
|
const ended = cleanup();
|
|
29331
|
-
const closed = new Promise((
|
|
30273
|
+
const closed = new Promise((resolve22) => server.close(() => resolve22()));
|
|
29332
30274
|
server.closeAllConnections();
|
|
29333
30275
|
await Promise.all([ended, closed]);
|
|
29334
30276
|
});
|
|
@@ -29337,13 +30279,13 @@ async function startServer(opts2) {
|
|
|
29337
30279
|
void cleanup();
|
|
29338
30280
|
});
|
|
29339
30281
|
installExitHandlers({ signals: ["SIGINT", "SIGTERM"], exitCode: () => 0, crashes: true });
|
|
29340
|
-
return new Promise((
|
|
30282
|
+
return new Promise((resolve22, reject) => {
|
|
29341
30283
|
server.listen(opts2.port, opts2.hostname, () => {
|
|
29342
30284
|
logger.info("server: listening", { hostname: opts2.hostname, port: opts2.port });
|
|
29343
30285
|
console.log(`kimiflare serve: http://${opts2.hostname}:${opts2.port}`);
|
|
29344
30286
|
console.log(` API docs: http://${opts2.hostname}:${opts2.port}/doc`);
|
|
29345
30287
|
console.log(` SSE stream: http://${opts2.hostname}:${opts2.port}/event`);
|
|
29346
|
-
|
|
30288
|
+
resolve22(server);
|
|
29347
30289
|
});
|
|
29348
30290
|
server.on("error", reject);
|
|
29349
30291
|
});
|
|
@@ -29677,8 +30619,8 @@ async function runEmitMode(opts2) {
|
|
|
29677
30619
|
async function nextFollowUp() {
|
|
29678
30620
|
if (followUpQueue.length > 0) return followUpQueue.shift();
|
|
29679
30621
|
if (stdinClosed) return null;
|
|
29680
|
-
return new Promise((
|
|
29681
|
-
followUpResolver =
|
|
30622
|
+
return new Promise((resolve22) => {
|
|
30623
|
+
followUpResolver = resolve22;
|
|
29682
30624
|
});
|
|
29683
30625
|
}
|
|
29684
30626
|
const cwd = process.cwd();
|
|
@@ -29708,6 +30650,25 @@ async function runEmitMode(opts2) {
|
|
|
29708
30650
|
process.on("SIGINT", sigintHandler);
|
|
29709
30651
|
async function runTurn(text3) {
|
|
29710
30652
|
emit("UserMessageCreated", { text: text3 });
|
|
30653
|
+
const gate = await submitUserPrompt({
|
|
30654
|
+
hooks,
|
|
30655
|
+
sessionId,
|
|
30656
|
+
cwd,
|
|
30657
|
+
prompt: text3,
|
|
30658
|
+
resumed: messages.some((m) => m.role === "assistant"),
|
|
30659
|
+
model: opts2.model,
|
|
30660
|
+
auth: llmAuthFromConfig(opts2)
|
|
30661
|
+
});
|
|
30662
|
+
if (gate.blocked) {
|
|
30663
|
+
emit("RuntimeError", {
|
|
30664
|
+
message: `prompt blocked by a UserPromptSubmit hook: ${gate.reason}`,
|
|
30665
|
+
source: "hooks",
|
|
30666
|
+
kind: "generic",
|
|
30667
|
+
severity: "error"
|
|
30668
|
+
});
|
|
30669
|
+
if (!opts2.multiTurn) exitCode = PROMPT_BLOCKED_EXIT_CODE;
|
|
30670
|
+
return;
|
|
30671
|
+
}
|
|
29711
30672
|
messages.push({ role: "user", content: text3 });
|
|
29712
30673
|
try {
|
|
29713
30674
|
const policy = await resolveTurnPolicy({ cfg: opts2, prompt: text3, tools: ALL_TOOLS, signal: controller.signal });
|
|
@@ -29830,8 +30791,8 @@ async function runEmitMode(opts2) {
|
|
|
29830
30791
|
return fromHook;
|
|
29831
30792
|
}
|
|
29832
30793
|
if (opts2.multiTurn) {
|
|
29833
|
-
const choice = await new Promise((
|
|
29834
|
-
pendingPermissions.set(reqId,
|
|
30794
|
+
const choice = await new Promise((resolve22) => {
|
|
30795
|
+
pendingPermissions.set(reqId, resolve22);
|
|
29835
30796
|
});
|
|
29836
30797
|
if (choice === "allow" || choice === "allow_session") {
|
|
29837
30798
|
emit("PermissionGranted", { request_id: reqId });
|
|
@@ -29931,6 +30892,7 @@ var init_emit_mode = __esm({
|
|
|
29931
30892
|
init_executor();
|
|
29932
30893
|
init_errors();
|
|
29933
30894
|
init_session_lifecycle();
|
|
30895
|
+
init_prompt_submit();
|
|
29934
30896
|
init_sessions();
|
|
29935
30897
|
}
|
|
29936
30898
|
});
|
|
@@ -29958,7 +30920,7 @@ async function runAttachMode(opts2) {
|
|
|
29958
30920
|
if (!response.ok) {
|
|
29959
30921
|
const text3 = await response.text().catch(() => "unknown error");
|
|
29960
30922
|
console.error(`kimiflare attach: ${response.status} ${text3}`);
|
|
29961
|
-
process.exit(1);
|
|
30923
|
+
process.exit(response.status === 403 && text3.includes('"prompt_blocked"') ? PROMPT_BLOCKED_EXIT_CODE : 1);
|
|
29962
30924
|
}
|
|
29963
30925
|
const result2 = await response.json();
|
|
29964
30926
|
const sessionId = result2.sessionId;
|
|
@@ -29971,7 +30933,7 @@ async function runAttachMode(opts2) {
|
|
|
29971
30933
|
async function streamSse(url, sessionId, format) {
|
|
29972
30934
|
const eventSource = new EventSource(`${url}/event`);
|
|
29973
30935
|
let done = false;
|
|
29974
|
-
return new Promise((
|
|
30936
|
+
return new Promise((resolve22, reject) => {
|
|
29975
30937
|
eventSource.onmessage = (event) => {
|
|
29976
30938
|
try {
|
|
29977
30939
|
const data = JSON.parse(event.data);
|
|
@@ -30018,7 +30980,7 @@ async function streamSse(url, sessionId, format) {
|
|
|
30018
30980
|
done = true;
|
|
30019
30981
|
eventSource.close();
|
|
30020
30982
|
if (format === "text") process.stdout.write("\n");
|
|
30021
|
-
|
|
30983
|
+
resolve22(void 0);
|
|
30022
30984
|
break;
|
|
30023
30985
|
case "error":
|
|
30024
30986
|
done = true;
|
|
@@ -30031,7 +30993,7 @@ async function streamSse(url, sessionId, format) {
|
|
|
30031
30993
|
process.stdout.write(JSON.stringify({ event: "error", message: data.message }) + "\n");
|
|
30032
30994
|
}
|
|
30033
30995
|
process.exitCode = 1;
|
|
30034
|
-
|
|
30996
|
+
resolve22(void 0);
|
|
30035
30997
|
break;
|
|
30036
30998
|
}
|
|
30037
30999
|
} catch {
|
|
@@ -30057,7 +31019,7 @@ async function streamSseJson(url, sessionId) {
|
|
|
30057
31019
|
const toolResults = [];
|
|
30058
31020
|
let usage;
|
|
30059
31021
|
const eventSource = new EventSource(`${url}/event`);
|
|
30060
|
-
return new Promise((
|
|
31022
|
+
return new Promise((resolve22, reject) => {
|
|
30061
31023
|
eventSource.onmessage = (event) => {
|
|
30062
31024
|
try {
|
|
30063
31025
|
const data = JSON.parse(event.data);
|
|
@@ -30094,7 +31056,7 @@ async function streamSseJson(url, sessionId) {
|
|
|
30094
31056
|
2
|
|
30095
31057
|
) + "\n"
|
|
30096
31058
|
);
|
|
30097
|
-
|
|
31059
|
+
resolve22(void 0);
|
|
30098
31060
|
break;
|
|
30099
31061
|
case "error":
|
|
30100
31062
|
eventSource.close();
|
|
@@ -30102,7 +31064,7 @@ async function streamSseJson(url, sessionId) {
|
|
|
30102
31064
|
JSON.stringify({ error: data.message, sessionId }, null, 2) + "\n"
|
|
30103
31065
|
);
|
|
30104
31066
|
process.exitCode = 1;
|
|
30105
|
-
|
|
31067
|
+
resolve22(void 0);
|
|
30106
31068
|
break;
|
|
30107
31069
|
}
|
|
30108
31070
|
} catch {
|
|
@@ -30122,6 +31084,7 @@ var EventSource;
|
|
|
30122
31084
|
var init_attach_mode = __esm({
|
|
30123
31085
|
"src/attach-mode.ts"() {
|
|
30124
31086
|
"use strict";
|
|
31087
|
+
init_prompt_submit();
|
|
30125
31088
|
EventSource = class {
|
|
30126
31089
|
url;
|
|
30127
31090
|
controller;
|
|
@@ -30279,7 +31242,7 @@ __export(openrouter_oauth_exports, {
|
|
|
30279
31242
|
});
|
|
30280
31243
|
import { createHash as createHash8, randomBytes as randomBytes4 } from "crypto";
|
|
30281
31244
|
import { createServer as createServer4 } from "http";
|
|
30282
|
-
import { hostname as
|
|
31245
|
+
import { hostname as hostname3 } from "os";
|
|
30283
31246
|
function createPkce() {
|
|
30284
31247
|
const verifier = randomBytes4(32).toString("base64url");
|
|
30285
31248
|
return { verifier, challenge: pkceChallenge(verifier) };
|
|
@@ -30291,7 +31254,7 @@ function openRouterAuthPageUrl() {
|
|
|
30291
31254
|
return (process.env.OPENROUTER_AUTH_URL?.trim() || "https://openrouter.ai/auth").replace(/\/+$/, "");
|
|
30292
31255
|
}
|
|
30293
31256
|
function defaultKeyLabel() {
|
|
30294
|
-
return `autopilot (${
|
|
31257
|
+
return `autopilot (${hostname3()})`.slice(0, 60);
|
|
30295
31258
|
}
|
|
30296
31259
|
function buildAuthUrl(opts2) {
|
|
30297
31260
|
const params = new URLSearchParams();
|
|
@@ -30368,9 +31331,9 @@ async function startLoopbackListener(opts2 = {}) {
|
|
|
30368
31331
|
rejectCode(e);
|
|
30369
31332
|
finish();
|
|
30370
31333
|
}
|
|
30371
|
-
await new Promise((
|
|
31334
|
+
await new Promise((resolve22, reject) => {
|
|
30372
31335
|
server.once("error", reject);
|
|
30373
|
-
server.listen(0, "127.0.0.1", () =>
|
|
31336
|
+
server.listen(0, "127.0.0.1", () => resolve22());
|
|
30374
31337
|
});
|
|
30375
31338
|
const address = server.address();
|
|
30376
31339
|
const port = typeof address === "object" && address ? address.port : 0;
|
|
@@ -30517,11 +31480,11 @@ async function chatgptSignIn(onUrl, signal) {
|
|
|
30517
31480
|
}
|
|
30518
31481
|
function untilAborted(p, signal) {
|
|
30519
31482
|
if (!signal) return p;
|
|
30520
|
-
return new Promise((
|
|
31483
|
+
return new Promise((resolve22, reject) => {
|
|
30521
31484
|
const abort = () => reject(new DOMException("sign-in cancelled", "AbortError"));
|
|
30522
31485
|
if (signal.aborted) return abort();
|
|
30523
31486
|
signal.addEventListener("abort", abort, { once: true });
|
|
30524
|
-
p.then(
|
|
31487
|
+
p.then(resolve22, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
30525
31488
|
});
|
|
30526
31489
|
}
|
|
30527
31490
|
async function openRouterSignIn(io) {
|
|
@@ -30767,20 +31730,20 @@ var init_help_catalog = __esm({
|
|
|
30767
31730
|
|
|
30768
31731
|
// src/ui/bang-command.ts
|
|
30769
31732
|
import { spawn as spawn8 } from "child_process";
|
|
30770
|
-
import { mkdtemp as
|
|
30771
|
-
import { tmpdir as
|
|
30772
|
-
import { join as
|
|
31733
|
+
import { mkdtemp as mkdtemp4, readFile as readFile36, rm as rm8 } from "fs/promises";
|
|
31734
|
+
import { tmpdir as tmpdir6, platform as platform5 } from "os";
|
|
31735
|
+
import { join as join49 } from "path";
|
|
30773
31736
|
function registerTerminalHandoff(h) {
|
|
30774
31737
|
terminalHandoff = h;
|
|
30775
31738
|
}
|
|
30776
31739
|
function runCaptured(inv, cwd) {
|
|
30777
|
-
return new Promise((
|
|
31740
|
+
return new Promise((resolve22) => {
|
|
30778
31741
|
const child = spawn8(inv.file, inv.args, { cwd, stdio: ["ignore", "pipe", "pipe"], env: inv.env ?? process.env });
|
|
30779
31742
|
let output = "";
|
|
30780
31743
|
child.stdout?.on("data", (c) => output += c.toString("utf8"));
|
|
30781
31744
|
child.stderr?.on("data", (c) => output += c.toString("utf8"));
|
|
30782
|
-
child.once("error", (e) =>
|
|
30783
|
-
child.once("exit", (code, signal) =>
|
|
31745
|
+
child.once("error", (e) => resolve22({ code: 127, signal: null, output: `failed to start: ${e.message}` }));
|
|
31746
|
+
child.once("exit", (code, signal) => resolve22({ code, signal, output }));
|
|
30784
31747
|
});
|
|
30785
31748
|
}
|
|
30786
31749
|
function isTerminalHandedOff() {
|
|
@@ -30848,10 +31811,10 @@ function swallow() {
|
|
|
30848
31811
|
});
|
|
30849
31812
|
}
|
|
30850
31813
|
function spawnInherit(inv, cwd) {
|
|
30851
|
-
return new Promise((
|
|
31814
|
+
return new Promise((resolve22, reject) => {
|
|
30852
31815
|
const child = spawn8(inv.file, inv.args, { cwd, stdio: "inherit", env: inv.env ?? process.env });
|
|
30853
31816
|
child.once("error", reject);
|
|
30854
|
-
child.once("exit", (code, signal) =>
|
|
31817
|
+
child.once("exit", (code, signal) => resolve22({ code, signal }));
|
|
30855
31818
|
});
|
|
30856
31819
|
}
|
|
30857
31820
|
async function runBangCommand(command, cwd) {
|
|
@@ -30869,8 +31832,8 @@ async function runBangCommand(command, cwd) {
|
|
|
30869
31832
|
const origErr = stderr.write;
|
|
30870
31833
|
const readable = stdin.listeners("readable");
|
|
30871
31834
|
const wasRaw = stdin.isTTY ? stdin.isRaw : false;
|
|
30872
|
-
const logDir2 = isWindows ? null : await
|
|
30873
|
-
const logPath = logDir2 ?
|
|
31835
|
+
const logDir2 = isWindows ? null : await mkdtemp4(join49(tmpdir6(), "autopilot-bang-"));
|
|
31836
|
+
const logPath = logDir2 ? join49(logDir2, "out.log") : null;
|
|
30874
31837
|
const viaScript = logPath ? scriptInvocation(command, shell, logPath) : null;
|
|
30875
31838
|
origOut.call(stdout, `
|
|
30876
31839
|
$ ${command}
|
|
@@ -30911,9 +31874,9 @@ $ ${command}
|
|
|
30911
31874
|
}
|
|
30912
31875
|
let output = spawnError ? `failed to start: ${spawnError}` : null;
|
|
30913
31876
|
if (captured && logPath) {
|
|
30914
|
-
output = cleanTerminalOutput(await
|
|
31877
|
+
output = cleanTerminalOutput(await readFile36(logPath, "utf8").catch(() => ""));
|
|
30915
31878
|
}
|
|
30916
|
-
if (logDir2) await
|
|
31879
|
+
if (logDir2) await rm8(logDir2, { recursive: true, force: true }).catch(() => {
|
|
30917
31880
|
});
|
|
30918
31881
|
return { command, exitCode: result2.code, signal: result2.signal, output };
|
|
30919
31882
|
}
|
|
@@ -31150,8 +32113,8 @@ function activityFromSubagent(worker) {
|
|
|
31150
32113
|
};
|
|
31151
32114
|
}
|
|
31152
32115
|
function activityTaskPreview(task) {
|
|
31153
|
-
const
|
|
31154
|
-
return
|
|
32116
|
+
const oneLine3 = task.replace(/\s+/g, " ").trim();
|
|
32117
|
+
return oneLine3.length > 72 ? `${oneLine3.slice(0, 71)}\u2026` : oneLine3;
|
|
31155
32118
|
}
|
|
31156
32119
|
function diffActivityItems(previous, current, snapshotSent) {
|
|
31157
32120
|
if (!snapshotSent) return [{ type: "ActivitySnapshot", payload: { items: [...current] } }];
|
|
@@ -31188,14 +32151,14 @@ __export(camouflage_launch_exports, {
|
|
|
31188
32151
|
runWithCrashRestarts: () => runWithCrashRestarts
|
|
31189
32152
|
});
|
|
31190
32153
|
import { accessSync, constants, existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
|
|
31191
|
-
import { homedir as
|
|
31192
|
-
import { delimiter, dirname as
|
|
32154
|
+
import { homedir as homedir31 } from "os";
|
|
32155
|
+
import { delimiter, dirname as dirname25, isAbsolute as isAbsolute8, join as join50, resolve as resolve19, sep as sep7 } from "path";
|
|
31193
32156
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
31194
32157
|
function defaultPackageDir() {
|
|
31195
32158
|
try {
|
|
31196
|
-
let dir =
|
|
31197
|
-
while (!existsSync14(
|
|
31198
|
-
return existsSync14(
|
|
32159
|
+
let dir = dirname25(fileURLToPath5(import.meta.resolve("camouflage-tui")));
|
|
32160
|
+
while (!existsSync14(join50(dir, "package.json")) && dirname25(dir) !== dir) dir = dirname25(dir);
|
|
32161
|
+
return existsSync14(join50(dir, "package.json")) ? dir : null;
|
|
31199
32162
|
} catch {
|
|
31200
32163
|
return null;
|
|
31201
32164
|
}
|
|
@@ -31209,11 +32172,11 @@ function defaultIsExecutable(path) {
|
|
|
31209
32172
|
}
|
|
31210
32173
|
}
|
|
31211
32174
|
function findOnPath(bin, env, platform7, isExecutable) {
|
|
31212
|
-
if (isAbsolute8(bin) || bin.includes("/") || bin.includes(sep7)) return isExecutable(
|
|
32175
|
+
if (isAbsolute8(bin) || bin.includes("/") || bin.includes(sep7)) return isExecutable(resolve19(bin));
|
|
31213
32176
|
const exts = platform7 === "win32" ? ["", ...(env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
|
|
31214
32177
|
for (const dir of (env.PATH ?? "").split(delimiter)) {
|
|
31215
32178
|
if (!dir) continue;
|
|
31216
|
-
for (const ext of exts) if (isExecutable(
|
|
32179
|
+
for (const ext of exts) if (isExecutable(join50(dir, bin + ext))) return true;
|
|
31217
32180
|
}
|
|
31218
32181
|
return false;
|
|
31219
32182
|
}
|
|
@@ -31222,21 +32185,21 @@ function diagnoseRenderer(deps = {}) {
|
|
|
31222
32185
|
const platform7 = deps.platform ?? process.platform;
|
|
31223
32186
|
const arch = deps.arch ?? process.arch;
|
|
31224
32187
|
const isExecutable = deps.isExecutable ?? defaultIsExecutable;
|
|
31225
|
-
const
|
|
32188
|
+
const readFile42 = deps.readFile ?? ((p) => readFileSync9(p, "utf8"));
|
|
31226
32189
|
const packageDir = (deps.packageDir ?? defaultPackageDir)();
|
|
31227
32190
|
let packageVersion = null;
|
|
31228
32191
|
let packageBin = null;
|
|
31229
32192
|
if (packageDir) {
|
|
31230
32193
|
try {
|
|
31231
|
-
packageVersion = JSON.parse(
|
|
32194
|
+
packageVersion = JSON.parse(readFile42(join50(packageDir, "package.json"))).version ?? null;
|
|
31232
32195
|
} catch {
|
|
31233
32196
|
}
|
|
31234
|
-
packageBin =
|
|
32197
|
+
packageBin = join50(packageDir, "bin", platform7 === "win32" ? "camouflage-tui.exe" : "camouflage-tui");
|
|
31235
32198
|
}
|
|
31236
|
-
const installErrorPath =
|
|
32199
|
+
const installErrorPath = join50(deps.home ?? homedir31(), ".camouflage", "install-error.txt");
|
|
31237
32200
|
let installError = null;
|
|
31238
32201
|
try {
|
|
31239
|
-
installError =
|
|
32202
|
+
installError = readFile42(installErrorPath).trim() || null;
|
|
31240
32203
|
} catch {
|
|
31241
32204
|
}
|
|
31242
32205
|
const envBin = env.CAMOUFLAGE_BIN || null;
|
|
@@ -31283,7 +32246,7 @@ function formatUnavailable(d, reason) {
|
|
|
31283
32246
|
if (d.envBin) lines.push(` - point CAMOUFLAGE_BIN at an existing camouflage-tui binary, or unset it to use the bundled one`);
|
|
31284
32247
|
if (d.packageDir) {
|
|
31285
32248
|
lines.push(
|
|
31286
|
-
` - download the renderer again: node "${
|
|
32249
|
+
` - download the renderer again: node "${join50(d.packageDir, "scripts", "install.js")}"`,
|
|
31287
32250
|
` (or run \`npm rebuild camouflage-tui\` where autopilot-ai is installed; \`npm rebuild -g camouflage-tui\` for a global install)`
|
|
31288
32251
|
);
|
|
31289
32252
|
}
|
|
@@ -31332,7 +32295,7 @@ async function runWithCrashRestarts(run, opts2 = {}) {
|
|
|
31332
32295
|
function crashGiveUpMessage(outcome, windowMs = 6e4) {
|
|
31333
32296
|
const lines = [
|
|
31334
32297
|
`autopilot: the Camouflage renderer crashed ${outcome.crashes} times within ${Math.round(windowMs / 1e3)} seconds, so autopilot stopped restarting it.`,
|
|
31335
|
-
`Crash reports: ${
|
|
32298
|
+
`Crash reports: ${join50(homedir31(), ".camouflage")} (crash-*.txt). Please attach one to an issue: https://github.com/sinameraji/autopilot/issues`
|
|
31336
32299
|
];
|
|
31337
32300
|
if (outcome.sessionId) {
|
|
31338
32301
|
lines.push(
|
|
@@ -31414,11 +32377,11 @@ __export(interactive_resume_exports, {
|
|
|
31414
32377
|
resumeHint: () => resumeHint
|
|
31415
32378
|
});
|
|
31416
32379
|
import { existsSync as existsSync15 } from "fs";
|
|
31417
|
-
import { join as
|
|
32380
|
+
import { join as join51 } from "path";
|
|
31418
32381
|
async function resolveInteractiveResume(req, cwd = process.cwd()) {
|
|
31419
32382
|
if (req.sessionId !== void 0) {
|
|
31420
32383
|
const id = req.sessionId.trim().replace(/\.json$/, "");
|
|
31421
|
-
if (!SESSION_ID.test(id) || !existsSync15(
|
|
32384
|
+
if (!SESSION_ID.test(id) || !existsSync15(join51(sessionsDir(), `${id}.json`))) {
|
|
31422
32385
|
return { kind: "error", message: `no saved conversation with id "${req.sessionId}". Run \`autopilot resume\` to pick one.` };
|
|
31423
32386
|
}
|
|
31424
32387
|
return { kind: "session", sessionId: id };
|
|
@@ -31431,7 +32394,7 @@ async function resolveInteractiveResume(req, cwd = process.cwd()) {
|
|
|
31431
32394
|
return { kind: "picker" };
|
|
31432
32395
|
}
|
|
31433
32396
|
function resumeHint(sessionId) {
|
|
31434
|
-
if (!sessionId || !SESSION_ID.test(sessionId) || !existsSync15(
|
|
32397
|
+
if (!sessionId || !SESSION_ID.test(sessionId) || !existsSync15(join51(sessionsDir(), `${sessionId}.json`))) return null;
|
|
31435
32398
|
return `Resume this conversation with: autopilot resume ${sessionId}
|
|
31436
32399
|
`;
|
|
31437
32400
|
}
|
|
@@ -31471,8 +32434,8 @@ __export(camouflage_view_exports, {
|
|
|
31471
32434
|
import { execFileSync as execFileSync4, spawn as spawn9 } from "child_process";
|
|
31472
32435
|
import { readdir as readdir7 } from "fs/promises";
|
|
31473
32436
|
import { existsSync as existsSync16, readFileSync as readFileSync10 } from "fs";
|
|
31474
|
-
import { homedir as
|
|
31475
|
-
import { dirname as
|
|
32437
|
+
import { homedir as homedir32 } from "os";
|
|
32438
|
+
import { dirname as dirname26, join as join52, resolve as resolve20 } from "path";
|
|
31476
32439
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
31477
32440
|
function noticeSeverity(e) {
|
|
31478
32441
|
return e.kind === "info" && e.tone === "warn" ? "warn" : "info";
|
|
@@ -31493,10 +32456,10 @@ function rendererInfo(env = process.env) {
|
|
|
31493
32456
|
let packageVersion = null;
|
|
31494
32457
|
let packageBin = "camouflage-tui";
|
|
31495
32458
|
try {
|
|
31496
|
-
let dir =
|
|
31497
|
-
while (!existsSync16(
|
|
31498
|
-
packageVersion = JSON.parse(readFileSync10(
|
|
31499
|
-
const local =
|
|
32459
|
+
let dir = dirname26(fileURLToPath6(import.meta.resolve("camouflage-tui")));
|
|
32460
|
+
while (!existsSync16(join52(dir, "package.json")) && dirname26(dir) !== dir) dir = dirname26(dir);
|
|
32461
|
+
packageVersion = JSON.parse(readFileSync10(join52(dir, "package.json"), "utf8")).version ?? null;
|
|
32462
|
+
const local = join52(dir, "bin", process.platform === "win32" ? "camouflage-tui.exe" : "camouflage-tui");
|
|
31500
32463
|
packageBin = local;
|
|
31501
32464
|
} catch {
|
|
31502
32465
|
}
|
|
@@ -31624,7 +32587,7 @@ async function runCamouflageView(opts2) {
|
|
|
31624
32587
|
if (process.stdin.isTTY) process.stdin.setRawMode?.(false);
|
|
31625
32588
|
process.stderr.write(
|
|
31626
32589
|
`
|
|
31627
|
-
The Camouflage renderer stopped unexpectedly (${signal ?? `exit code ${code}`}). Its crash report is in ${
|
|
32590
|
+
The Camouflage renderer stopped unexpectedly (${signal ?? `exit code ${code}`}). Its crash report is in ${join52(homedir32(), ".camouflage")} (crash-*.txt).
|
|
31628
32591
|
` + (view.busy ? "Finishing the current turn first\u2026\n" : "")
|
|
31629
32592
|
);
|
|
31630
32593
|
await view.settle();
|
|
@@ -31635,7 +32598,7 @@ The Camouflage renderer stopped unexpectedly (${signal ?? `exit code ${code}`}).
|
|
|
31635
32598
|
let endBeforeApp = () => {
|
|
31636
32599
|
};
|
|
31637
32600
|
let quitBeforeApp = false;
|
|
31638
|
-
const beforeApp = new Promise((
|
|
32601
|
+
const beforeApp = new Promise((resolve22) => endBeforeApp = resolve22);
|
|
31639
32602
|
let setup = null;
|
|
31640
32603
|
try {
|
|
31641
32604
|
let cfg = opts2.cfg;
|
|
@@ -31680,14 +32643,14 @@ The Camouflage renderer stopped unexpectedly (${signal ?? `exit code ${code}`}).
|
|
|
31680
32643
|
return "done";
|
|
31681
32644
|
}
|
|
31682
32645
|
function runShell2(command) {
|
|
31683
|
-
return new Promise((
|
|
32646
|
+
return new Promise((resolve22) => {
|
|
31684
32647
|
const { shell, args } = getShellCommand();
|
|
31685
32648
|
const child = spawn9(shell, [...args, command], { env: process.env });
|
|
31686
32649
|
let out = "";
|
|
31687
32650
|
child.stdout?.on("data", (c) => out += c.toString("utf8"));
|
|
31688
32651
|
child.stderr?.on("data", (c) => out += c.toString("utf8"));
|
|
31689
|
-
child.on("close", (code) =>
|
|
31690
|
-
child.on("error", (err) =>
|
|
32652
|
+
child.on("close", (code) => resolve22({ ok: code === 0, output: out || (code === 0 ? "" : `Exit code: ${code}`) }));
|
|
32653
|
+
child.on("error", (err) => resolve22({ ok: false, output: err.message }));
|
|
31691
32654
|
});
|
|
31692
32655
|
}
|
|
31693
32656
|
function syncSlashCommands(custom, lastKey, send) {
|
|
@@ -31770,8 +32733,8 @@ function truncateText(s, max) {
|
|
|
31770
32733
|
async function listMentionDir(cwd, query) {
|
|
31771
32734
|
const exact = query === "~" || query === "." || query === ".." || query.endsWith("/..");
|
|
31772
32735
|
const dir = exact ? `${query}/` : query.slice(0, query.lastIndexOf("/") + 1);
|
|
31773
|
-
const expanded = dir.startsWith("~") ?
|
|
31774
|
-
const target =
|
|
32736
|
+
const expanded = dir.startsWith("~") ? join52(homedir32(), dir.slice(1)) : dir;
|
|
32737
|
+
const target = resolve20(cwd, expanded || ".");
|
|
31775
32738
|
try {
|
|
31776
32739
|
const items = await readdir7(target, { withFileTypes: true });
|
|
31777
32740
|
const entries = items.filter((d) => !d.name.startsWith(".")).map((d) => ({ name: d.name, isDir: d.isDirectory() })).sort((a, b) => a.isDir !== b.isDir ? a.isDir ? -1 : 1 : a.name.localeCompare(b.name)).slice(0, 300).map((d) => `${dir}${d.name}${d.isDir ? "/" : ""}`);
|
|
@@ -31910,7 +32873,7 @@ function shortModel(id) {
|
|
|
31910
32873
|
return id.includes("/") ? id.slice(id.indexOf("/") + 1) : id;
|
|
31911
32874
|
}
|
|
31912
32875
|
function shortPath(p) {
|
|
31913
|
-
const home =
|
|
32876
|
+
const home = homedir32();
|
|
31914
32877
|
return p === home ? "~" : p.startsWith(home + "/") ? `~${p.slice(home.length)}` : p;
|
|
31915
32878
|
}
|
|
31916
32879
|
function gitBranch(cwd) {
|
|
@@ -32032,15 +32995,15 @@ var init_camouflage_view = __esm({
|
|
|
32032
32995
|
/** Resolves once the App is idle (no turn running). */
|
|
32033
32996
|
settle(maxWaitMs = 12e4) {
|
|
32034
32997
|
if (!this.busy) return Promise.resolve();
|
|
32035
|
-
return new Promise((
|
|
32036
|
-
this.idleWaiters.push(
|
|
32037
|
-
setTimeout(
|
|
32998
|
+
return new Promise((resolve22) => {
|
|
32999
|
+
this.idleWaiters.push(resolve22);
|
|
33000
|
+
setTimeout(resolve22, maxWaitMs).unref();
|
|
32038
33001
|
});
|
|
32039
33002
|
}
|
|
32040
33003
|
sync(s) {
|
|
32041
33004
|
this.busy = s.turnRunning;
|
|
32042
33005
|
this.sessionId = s.sessionId;
|
|
32043
|
-
if (!s.turnRunning) for (const
|
|
33006
|
+
if (!s.turnRunning) for (const resolve22 of this.idleWaiters.splice(0)) resolve22();
|
|
32044
33007
|
if (this.disposed) {
|
|
32045
33008
|
if (s.permission) this.actions?.decidePermission("deny");
|
|
32046
33009
|
return;
|
|
@@ -32337,7 +33300,7 @@ var init_camouflage_view = __esm({
|
|
|
32337
33300
|
*/
|
|
32338
33301
|
async denyFeedback(given, escaped) {
|
|
32339
33302
|
if (given?.trim()) return given.trim();
|
|
32340
|
-
await new Promise((
|
|
33303
|
+
await new Promise((resolve22) => setTimeout(resolve22, 50));
|
|
32341
33304
|
if (escaped() || this.disposed) return "";
|
|
32342
33305
|
const r = await this.sdk.form(this.cam, {
|
|
32343
33306
|
id: `deny-${Date.now()}`,
|
|
@@ -33447,9 +34410,9 @@ function usePermissionController(getMode, onPlanModeBlocked) {
|
|
|
33447
34410
|
const askPermission = useCallback(
|
|
33448
34411
|
(req, askOpts) => resolvePermission(req, getModeRef.current(), askOpts, {
|
|
33449
34412
|
onPlanModeBlocked: (toolName) => onPlanModeBlockedRef.current(toolName),
|
|
33450
|
-
prompt: () => new Promise((
|
|
33451
|
-
resolveRef.current =
|
|
33452
|
-
setPending({ tool: req.tool, args: req.args, resolve:
|
|
34413
|
+
prompt: () => new Promise((resolve22) => {
|
|
34414
|
+
resolveRef.current = resolve22;
|
|
34415
|
+
setPending({ tool: req.tool, args: req.args, resolve: resolve22 });
|
|
33453
34416
|
})
|
|
33454
34417
|
}),
|
|
33455
34418
|
[]
|
|
@@ -33830,14 +34793,14 @@ var init_frontmatter = __esm({
|
|
|
33830
34793
|
|
|
33831
34794
|
// src/commands/loader.ts
|
|
33832
34795
|
import { open as open2, realpath as realpath9 } from "fs/promises";
|
|
33833
|
-
import { homedir as
|
|
33834
|
-
import { join as
|
|
34796
|
+
import { homedir as homedir33 } from "os";
|
|
34797
|
+
import { join as join53, relative as relative11, sep as sep8 } from "path";
|
|
33835
34798
|
function projectCommandsDir(cwd = process.cwd()) {
|
|
33836
|
-
return
|
|
34799
|
+
return join53(cwd, ".kimiflare", "commands");
|
|
33837
34800
|
}
|
|
33838
34801
|
function globalCommandsDir() {
|
|
33839
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
33840
|
-
return
|
|
34802
|
+
const xdg = process.env.XDG_CONFIG_HOME || join53(homedir33(), ".config");
|
|
34803
|
+
return join53(xdg, "kimiflare", "commands");
|
|
33841
34804
|
}
|
|
33842
34805
|
async function loadCustomCommands(cwd = process.cwd()) {
|
|
33843
34806
|
const warnings = [];
|
|
@@ -33991,7 +34954,7 @@ var init_loader = __esm({
|
|
|
33991
34954
|
import { exec } from "child_process";
|
|
33992
34955
|
import { open as open3, realpath as realpath10 } from "fs/promises";
|
|
33993
34956
|
import { isAbsolute as isAbsolute9, relative as relative12, resolve as resolvePathJoin, basename as pathBasename } from "path";
|
|
33994
|
-
import { promisify as
|
|
34957
|
+
import { promisify as promisify11 } from "util";
|
|
33995
34958
|
function tokenizeArgs(s) {
|
|
33996
34959
|
return [...s.matchAll(ARG_TOKEN_RE)].map((match) => {
|
|
33997
34960
|
const token = match[0];
|
|
@@ -34144,7 +35107,7 @@ var init_renderer2 = __esm({
|
|
|
34144
35107
|
"src/commands/renderer.ts"() {
|
|
34145
35108
|
"use strict";
|
|
34146
35109
|
init_paths();
|
|
34147
|
-
execAsync =
|
|
35110
|
+
execAsync = promisify11(exec);
|
|
34148
35111
|
ARG_TOKEN_RE = /(?:"[^"]*"|'[^']*'|[^\s"']+)/g;
|
|
34149
35112
|
POSITIONAL_RE = /\$(\d+)/g;
|
|
34150
35113
|
HAS_POSITIONAL = /\$\d+/;
|
|
@@ -34453,6 +35416,9 @@ Rules:
|
|
|
34453
35416
|
|
|
34454
35417
|
// src/ui/use-session-manager.ts
|
|
34455
35418
|
import React, { useCallback as useCallback2, useRef as useRef2, useState as useState4 } from "react";
|
|
35419
|
+
function sessionNameText(messages, firstPrompt) {
|
|
35420
|
+
return firstPrompt?.trim() || extractFirstUserText(messages);
|
|
35421
|
+
}
|
|
34456
35422
|
function extractFirstUserText(messages) {
|
|
34457
35423
|
const firstUser = messages.find((m) => m.role === "user");
|
|
34458
35424
|
if (!firstUser) return "session";
|
|
@@ -34485,9 +35451,9 @@ function useSessionManager(deps) {
|
|
|
34485
35451
|
const [checkpointList, setCheckpointList] = useState4([]);
|
|
34486
35452
|
const depsRef = useRef2(deps);
|
|
34487
35453
|
depsRef.current = deps;
|
|
34488
|
-
const ensureSessionId = useCallback2(() => {
|
|
35454
|
+
const ensureSessionId = useCallback2((firstPrompt) => {
|
|
34489
35455
|
if (sessionIdRef.current) return sessionIdRef.current;
|
|
34490
|
-
const text3 =
|
|
35456
|
+
const text3 = sessionNameText(depsRef.current.messagesRef.current, firstPrompt);
|
|
34491
35457
|
sessionIdRef.current = makeSessionId(text3);
|
|
34492
35458
|
setLogSessionId(sessionIdRef.current);
|
|
34493
35459
|
return sessionIdRef.current;
|
|
@@ -34986,8 +35952,8 @@ var init_terminal_qr = __esm({
|
|
|
34986
35952
|
|
|
34987
35953
|
// src/agent/jev-context.ts
|
|
34988
35954
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
34989
|
-
import { readFile as
|
|
34990
|
-
import { basename as basename11, join as
|
|
35955
|
+
import { readFile as readFile37 } from "fs/promises";
|
|
35956
|
+
import { basename as basename11, join as join54, resolve as resolve21 } from "path";
|
|
34991
35957
|
function messageText(message2) {
|
|
34992
35958
|
if (typeof message2.content === "string") return message2.content.trim();
|
|
34993
35959
|
if (!Array.isArray(message2.content)) return "";
|
|
@@ -35004,12 +35970,12 @@ function projectRoot(cwd) {
|
|
|
35004
35970
|
timeout: 1e3
|
|
35005
35971
|
}).trim();
|
|
35006
35972
|
} catch {
|
|
35007
|
-
return
|
|
35973
|
+
return resolve21(cwd);
|
|
35008
35974
|
}
|
|
35009
35975
|
}
|
|
35010
35976
|
async function readPackageMetadata(root) {
|
|
35011
35977
|
try {
|
|
35012
|
-
const parsed = JSON.parse(await
|
|
35978
|
+
const parsed = JSON.parse(await readFile37(join54(root, "package.json"), "utf8"));
|
|
35013
35979
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
35014
35980
|
const pkg = parsed;
|
|
35015
35981
|
const metadata = {};
|
|
@@ -35026,7 +35992,7 @@ async function readPackageMetadata(root) {
|
|
|
35026
35992
|
async function readLicenseEvidence(root) {
|
|
35027
35993
|
for (const name of ["LICENSE", "LICENSE.md", "LICENSE.txt", "LICENCE", "COPYING"]) {
|
|
35028
35994
|
try {
|
|
35029
|
-
const content = (await
|
|
35995
|
+
const content = (await readFile37(join54(root, name), "utf8")).trim();
|
|
35030
35996
|
if (content) return { source: `project license (${name})`, content: content.slice(0, 700) };
|
|
35031
35997
|
} catch {
|
|
35032
35998
|
}
|
|
@@ -35059,7 +36025,7 @@ async function buildJevContext(question, messages, cwd = process.cwd()) {
|
|
|
35059
36025
|
conversationRemaining -= source.length + content.length + 2;
|
|
35060
36026
|
}
|
|
35061
36027
|
}
|
|
35062
|
-
const currentDirectory = basename11(
|
|
36028
|
+
const currentDirectory = basename11(resolve21(cwd));
|
|
35063
36029
|
const escapedDirectory = currentDirectory.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35064
36030
|
const namesCurrentDirectory = currentDirectory.length >= 5 && !/^(?:agent|app|code|lib|project|repo|src|test|tests|workspace)$/i.test(currentDirectory) && new RegExp(`\\b${escapedDirectory}\\b`, "i").test(question);
|
|
35065
36031
|
if (CURRENT_PROJECT_REFERENCE.test(question) || namesCurrentDirectory) {
|
|
@@ -35282,8 +36248,8 @@ var init_frontmatter2 = __esm({
|
|
|
35282
36248
|
});
|
|
35283
36249
|
|
|
35284
36250
|
// src/skills/loader.ts
|
|
35285
|
-
import { readFile as
|
|
35286
|
-
import { join as
|
|
36251
|
+
import { readFile as readFile38, readdir as readdir8, stat as stat13 } from "fs/promises";
|
|
36252
|
+
import { join as join55, extname } from "path";
|
|
35287
36253
|
function normalizeManifest(raw, filePath) {
|
|
35288
36254
|
const name = typeof raw.name === "string" ? raw.name : "";
|
|
35289
36255
|
const description = typeof raw.description === "string" ? raw.description : "";
|
|
@@ -35297,7 +36263,7 @@ function normalizeManifest(raw, filePath) {
|
|
|
35297
36263
|
return { name, description, match, scope, priority, enabled: enabled2 };
|
|
35298
36264
|
}
|
|
35299
36265
|
async function loadSkillFile(filePath) {
|
|
35300
|
-
const raw = await
|
|
36266
|
+
const raw = await readFile38(filePath, "utf-8");
|
|
35301
36267
|
const parsed = parseFrontmatter2(raw);
|
|
35302
36268
|
const manifest = normalizeManifest(parsed.data, filePath);
|
|
35303
36269
|
const body = parsed.content.trim();
|
|
@@ -35319,8 +36285,8 @@ async function loadSkillsFromDir(dirPath) {
|
|
|
35319
36285
|
const entries = await readdir8(dirPath);
|
|
35320
36286
|
const files = [];
|
|
35321
36287
|
for (const entry of entries) {
|
|
35322
|
-
const full =
|
|
35323
|
-
const s = await
|
|
36288
|
+
const full = join55(dirPath, entry);
|
|
36289
|
+
const s = await stat13(full);
|
|
35324
36290
|
if (s.isFile() && extname(entry) === ".md") {
|
|
35325
36291
|
files.push(full);
|
|
35326
36292
|
}
|
|
@@ -35347,12 +36313,12 @@ var init_loader2 = __esm({
|
|
|
35347
36313
|
});
|
|
35348
36314
|
|
|
35349
36315
|
// src/skills/manager.ts
|
|
35350
|
-
import { mkdir as
|
|
35351
|
-
import { join as
|
|
36316
|
+
import { mkdir as mkdir21, writeFile as writeFile18, unlink as unlink5, readFile as readFile39 } from "fs/promises";
|
|
36317
|
+
import { join as join56 } from "path";
|
|
35352
36318
|
function getSkillDirs(cwd) {
|
|
35353
36319
|
return {
|
|
35354
|
-
projectDir:
|
|
35355
|
-
globalDir:
|
|
36320
|
+
projectDir: join56(cwd, ".kimiflare", "skills"),
|
|
36321
|
+
globalDir: join56(process.env.HOME ?? "", ".config", "kimiflare", "skills")
|
|
35356
36322
|
};
|
|
35357
36323
|
}
|
|
35358
36324
|
async function listAllSkills(cwd) {
|
|
@@ -35366,7 +36332,7 @@ async function listAllSkills(cwd) {
|
|
|
35366
36332
|
async function createSkill(opts2) {
|
|
35367
36333
|
const dirs = getSkillDirs(opts2.cwd);
|
|
35368
36334
|
const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
|
|
35369
|
-
const filepath =
|
|
36335
|
+
const filepath = join56(dir, `${opts2.name}.md`);
|
|
35370
36336
|
const frontmatter = {
|
|
35371
36337
|
name: opts2.name,
|
|
35372
36338
|
enabled: true,
|
|
@@ -35387,8 +36353,8 @@ ${yaml}
|
|
|
35387
36353
|
|
|
35388
36354
|
Add your instructions here.
|
|
35389
36355
|
`;
|
|
35390
|
-
await
|
|
35391
|
-
await
|
|
36356
|
+
await mkdir21(dir, { recursive: true });
|
|
36357
|
+
await writeFile18(filepath, content, "utf8");
|
|
35392
36358
|
return { filepath };
|
|
35393
36359
|
}
|
|
35394
36360
|
async function deleteSkill(name, cwd) {
|
|
@@ -35402,7 +36368,7 @@ async function setSkillEnabled(name, enabled2, cwd) {
|
|
|
35402
36368
|
const all = await listAllSkills(cwd);
|
|
35403
36369
|
const skill = all.project.find((s) => s.name === name) ?? all.global.find((s) => s.name === name);
|
|
35404
36370
|
if (!skill) throw new Error(`skill "${name}" not found`);
|
|
35405
|
-
const raw = await
|
|
36371
|
+
const raw = await readFile39(skill.filePath, "utf-8");
|
|
35406
36372
|
const parsed = parseFrontmatter2(raw);
|
|
35407
36373
|
parsed.data.enabled = enabled2;
|
|
35408
36374
|
const yaml = Object.entries(parsed.data).map(([k, v]) => {
|
|
@@ -35414,7 +36380,7 @@ ${v.map((item) => ` - ${item}`).join("\n")}`;
|
|
|
35414
36380
|
${yaml}
|
|
35415
36381
|
---
|
|
35416
36382
|
${parsed.content}`;
|
|
35417
|
-
await
|
|
36383
|
+
await writeFile18(skill.filePath, content, "utf8");
|
|
35418
36384
|
return { filepath: skill.filePath };
|
|
35419
36385
|
}
|
|
35420
36386
|
async function findSkillFile(name, cwd) {
|
|
@@ -35495,15 +36461,15 @@ var tui_report_exports = {};
|
|
|
35495
36461
|
__export(tui_report_exports, {
|
|
35496
36462
|
getCategoryReportText: () => getCategoryReportText
|
|
35497
36463
|
});
|
|
35498
|
-
import { readFile as
|
|
35499
|
-
import { join as
|
|
35500
|
-
import { homedir as
|
|
36464
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
36465
|
+
import { join as join57 } from "path";
|
|
36466
|
+
import { homedir as homedir34 } from "os";
|
|
35501
36467
|
function usageDir3() {
|
|
35502
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
35503
|
-
return
|
|
36468
|
+
const xdg = process.env.XDG_DATA_HOME || join57(homedir34(), ".local", "share");
|
|
36469
|
+
return join57(xdg, "kimiflare");
|
|
35504
36470
|
}
|
|
35505
36471
|
function usagePath4() {
|
|
35506
|
-
return
|
|
36472
|
+
return join57(usageDir3(), "usage.json");
|
|
35507
36473
|
}
|
|
35508
36474
|
function today3() {
|
|
35509
36475
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -35515,7 +36481,7 @@ function daysAgo2(n) {
|
|
|
35515
36481
|
}
|
|
35516
36482
|
async function loadLog3() {
|
|
35517
36483
|
try {
|
|
35518
|
-
const raw = await
|
|
36484
|
+
const raw = await readFile40(usagePath4(), "utf8");
|
|
35519
36485
|
return JSON.parse(raw);
|
|
35520
36486
|
} catch {
|
|
35521
36487
|
return { version: 1, days: [], sessions: [] };
|
|
@@ -35572,7 +36538,7 @@ var init_tui_report = __esm({
|
|
|
35572
36538
|
});
|
|
35573
36539
|
|
|
35574
36540
|
// src/ui/slash-commands.ts
|
|
35575
|
-
import { join as
|
|
36541
|
+
import { join as join58 } from "path";
|
|
35576
36542
|
function executeFreshStart(ctx, planText, overrideMode, opts2 = {}) {
|
|
35577
36543
|
const oldSessionId = ctx.sessionIdRef.current;
|
|
35578
36544
|
if (ctx.cacheStableRef.current && ctx.messagesRef.current.length >= 2) {
|
|
@@ -35624,7 +36590,7 @@ function formatRunningSubagents(now2 = Date.now()) {
|
|
|
35624
36590
|
if (workers.length === 0) return "no subagents running";
|
|
35625
36591
|
return [
|
|
35626
36592
|
`${workers.length} subagent${workers.length === 1 ? "" : "s"} running:`,
|
|
35627
|
-
...workers.map((w) => ` #${w.index} ${formatElapsed2(now2 - w.startedAt)} ${w.status === "cancelling" ? "(cancelling) " : ""}${
|
|
36593
|
+
...workers.map((w) => ` #${w.index} ${formatElapsed2(now2 - w.startedAt)} ${w.status === "cancelling" ? "(cancelling) " : ""}${oneLine2(w.task, 90)}`),
|
|
35628
36594
|
"cancel one with /subagents cancel <n>, or all with /subagents cancel all \u2014 the turn keeps going"
|
|
35629
36595
|
].join("\n");
|
|
35630
36596
|
}
|
|
@@ -35637,7 +36603,7 @@ function cancelSubagents(target) {
|
|
|
35637
36603
|
const ref = target.replace(/^#/, "");
|
|
35638
36604
|
const cancelled = workerRegistry.cancel(/^\d+$/.test(ref) ? Number(ref) : ref);
|
|
35639
36605
|
if (!cancelled) return `no running subagent #${ref} (see /subagents list)`;
|
|
35640
|
-
return `cancelling subagent #${cancelled.index} (${
|
|
36606
|
+
return `cancelling subagent #${cancelled.index} (${oneLine2(cancelled.task, 60)}); the turn continues`;
|
|
35641
36607
|
}
|
|
35642
36608
|
function formatElapsed2(ms) {
|
|
35643
36609
|
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
@@ -35645,7 +36611,7 @@ function formatElapsed2(ms) {
|
|
|
35645
36611
|
const sec = total % 60;
|
|
35646
36612
|
return m > 0 ? `${m}m${String(sec).padStart(2, "0")}s` : `${sec}s`;
|
|
35647
36613
|
}
|
|
35648
|
-
function
|
|
36614
|
+
function oneLine2(text3, max) {
|
|
35649
36615
|
const single = text3.replace(/\s+/g, " ").trim();
|
|
35650
36616
|
return single.length > max ? `${single.slice(0, max - 1)}\u2026` : single;
|
|
35651
36617
|
}
|
|
@@ -36044,7 +37010,7 @@ auto (default) lets the coordinator assess substantial tasks and delegate indepe
|
|
|
36044
37010
|
const context = noContext ? [] : await buildJevContext(question.prompt, ctx.messagesRef.current, process.cwd());
|
|
36045
37011
|
const receipt = formatJevContextReceipt(context, showContext);
|
|
36046
37012
|
setResult(eventKey, { kind: "info", key: eventKey, text: `Jev is evaluating \xB7 ${receipt}`, pending: true });
|
|
36047
|
-
await new Promise((
|
|
37013
|
+
await new Promise((resolve22) => setTimeout(resolve22, 0));
|
|
36048
37014
|
const answer = await askJev(apiKey, question, { context });
|
|
36049
37015
|
const presentation = presentJevAnswer(question, answer);
|
|
36050
37016
|
setResult(eventKey, {
|
|
@@ -36542,7 +37508,7 @@ ${lines.join("\n")}` }]);
|
|
|
36542
37508
|
void (async () => {
|
|
36543
37509
|
try {
|
|
36544
37510
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
36545
|
-
const file = await loadSession(
|
|
37511
|
+
const file = await loadSession(join58(sessionsDir3(), `${currentId}.json`));
|
|
36546
37512
|
const cps = file.checkpoints ?? [];
|
|
36547
37513
|
if (cps.length === 0) {
|
|
36548
37514
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
|
|
@@ -36587,7 +37553,7 @@ ${lines.join("\n")}` }]);
|
|
|
36587
37553
|
try {
|
|
36588
37554
|
ctx.ensureSessionId();
|
|
36589
37555
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
36590
|
-
const filePath =
|
|
37556
|
+
const filePath = join58(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
|
|
36591
37557
|
await addCheckpoint(filePath, cp);
|
|
36592
37558
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
|
|
36593
37559
|
} catch (e) {
|
|
@@ -37087,12 +38053,12 @@ also read (legacy): ${legacyGlobalSettingsPath()}, ${legacyProjectSettingsPath(c
|
|
|
37087
38053
|
|
|
37088
38054
|
// src/init/context-generator.ts
|
|
37089
38055
|
import { existsSync as existsSync17, statSync as statSync6 } from "fs";
|
|
37090
|
-
import { join as
|
|
38056
|
+
import { join as join59 } from "path";
|
|
37091
38057
|
function detectFlavor(cwd) {
|
|
37092
38058
|
for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
|
|
37093
38059
|
if (flavor === "generic") continue;
|
|
37094
38060
|
for (const sig of signatures) {
|
|
37095
|
-
const path =
|
|
38061
|
+
const path = join59(cwd, sig);
|
|
37096
38062
|
if (sig.includes("*")) {
|
|
37097
38063
|
try {
|
|
37098
38064
|
const parts = sig.split("*");
|
|
@@ -37113,14 +38079,14 @@ function detectFlavor(cwd) {
|
|
|
37113
38079
|
}
|
|
37114
38080
|
function findFile(cwd, candidates) {
|
|
37115
38081
|
for (const c of candidates) {
|
|
37116
|
-
if (existsSync17(
|
|
38082
|
+
if (existsSync17(join59(cwd, c))) return c;
|
|
37117
38083
|
}
|
|
37118
38084
|
return null;
|
|
37119
38085
|
}
|
|
37120
38086
|
function findSourceRoots(cwd) {
|
|
37121
38087
|
const roots = [];
|
|
37122
38088
|
for (const r of SOURCE_ROOT_CANDIDATES) {
|
|
37123
|
-
const p =
|
|
38089
|
+
const p = join59(cwd, r);
|
|
37124
38090
|
try {
|
|
37125
38091
|
const s = statSync6(p);
|
|
37126
38092
|
if (s.isDirectory()) roots.push(r);
|
|
@@ -37131,9 +38097,9 @@ function findSourceRoots(cwd) {
|
|
|
37131
38097
|
}
|
|
37132
38098
|
function findCiConfig(cwd) {
|
|
37133
38099
|
for (const c of CI_PATHS) {
|
|
37134
|
-
if (existsSync17(
|
|
38100
|
+
if (existsSync17(join59(cwd, c))) {
|
|
37135
38101
|
try {
|
|
37136
|
-
const s = statSync6(
|
|
38102
|
+
const s = statSync6(join59(cwd, c));
|
|
37137
38103
|
return s.isDirectory() ? c : c;
|
|
37138
38104
|
} catch {
|
|
37139
38105
|
}
|
|
@@ -37270,7 +38236,7 @@ function analyzeProject(cwd) {
|
|
|
37270
38236
|
ciConfig: findCiConfig(cwd),
|
|
37271
38237
|
readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
|
|
37272
38238
|
sourceRoots: findSourceRoots(cwd),
|
|
37273
|
-
hasGit: existsSync17(
|
|
38239
|
+
hasGit: existsSync17(join59(cwd, ".git"))
|
|
37274
38240
|
};
|
|
37275
38241
|
}
|
|
37276
38242
|
function bashDiscoveryCommands(profile) {
|
|
@@ -37445,7 +38411,7 @@ function buildInitTasks(targetFilename) {
|
|
|
37445
38411
|
}
|
|
37446
38412
|
function buildInitPrompt(cwd) {
|
|
37447
38413
|
const existingName = ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
|
|
37448
|
-
(n) => existsSync17(
|
|
38414
|
+
(n) => existsSync17(join59(cwd, n))
|
|
37449
38415
|
);
|
|
37450
38416
|
const isRefresh = existingName !== void 0;
|
|
37451
38417
|
const targetFilename = existingName ?? "AGENTS.md";
|
|
@@ -37533,7 +38499,7 @@ var init_context_generator = __esm({
|
|
|
37533
38499
|
|
|
37534
38500
|
// src/init/run-init.ts
|
|
37535
38501
|
import { existsSync as existsSync18 } from "fs";
|
|
37536
|
-
import { join as
|
|
38502
|
+
import { join as join60 } from "path";
|
|
37537
38503
|
async function runInit(deps) {
|
|
37538
38504
|
const {
|
|
37539
38505
|
cfg,
|
|
@@ -37629,7 +38595,7 @@ async function runInit(deps) {
|
|
|
37629
38595
|
lspManagerRef.current.notifyChange(path, content);
|
|
37630
38596
|
} else {
|
|
37631
38597
|
void import("fs/promises").then(
|
|
37632
|
-
({ readFile:
|
|
38598
|
+
({ readFile: readFile42 }) => readFile42(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
|
|
37633
38599
|
})
|
|
37634
38600
|
);
|
|
37635
38601
|
}
|
|
@@ -37730,7 +38696,7 @@ async function runInit(deps) {
|
|
|
37730
38696
|
}
|
|
37731
38697
|
}
|
|
37732
38698
|
} }));
|
|
37733
|
-
if (existsSync18(
|
|
38699
|
+
if (existsSync18(join60(cwd, targetFilename))) {
|
|
37734
38700
|
if (cacheStableRef.current) {
|
|
37735
38701
|
messagesRef.current[1] = {
|
|
37736
38702
|
role: "system",
|
|
@@ -37824,11 +38790,11 @@ var init_run_init = __esm({
|
|
|
37824
38790
|
});
|
|
37825
38791
|
|
|
37826
38792
|
// src/skills/discovery.ts
|
|
37827
|
-
import { readdir as readdir9, stat as
|
|
37828
|
-
import { join as
|
|
38793
|
+
import { readdir as readdir9, stat as stat14, readFile as readFile41 } from "fs/promises";
|
|
38794
|
+
import { join as join61, extname as extname2 } from "path";
|
|
37829
38795
|
async function dirExists(path) {
|
|
37830
38796
|
try {
|
|
37831
|
-
const s = await
|
|
38797
|
+
const s = await stat14(path);
|
|
37832
38798
|
return s.isDirectory();
|
|
37833
38799
|
} catch {
|
|
37834
38800
|
return false;
|
|
@@ -37836,7 +38802,7 @@ async function dirExists(path) {
|
|
|
37836
38802
|
}
|
|
37837
38803
|
async function fileExists(path) {
|
|
37838
38804
|
try {
|
|
37839
|
-
const s = await
|
|
38805
|
+
const s = await stat14(path);
|
|
37840
38806
|
return s.isFile();
|
|
37841
38807
|
} catch {
|
|
37842
38808
|
return false;
|
|
@@ -37849,20 +38815,20 @@ async function scanSkillDir(dirPath, source) {
|
|
|
37849
38815
|
for (const entry of entries) {
|
|
37850
38816
|
if (!entry.isFile()) continue;
|
|
37851
38817
|
if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
|
|
37852
|
-
files.push({ filePath:
|
|
38818
|
+
files.push({ filePath: join61(dirPath, entry.name), source });
|
|
37853
38819
|
}
|
|
37854
38820
|
return files;
|
|
37855
38821
|
}
|
|
37856
38822
|
async function discoverSkills(cwd) {
|
|
37857
|
-
const agentsSkills = await scanSkillDir(
|
|
37858
|
-
const agentsMd = await fileExists(
|
|
37859
|
-
const githubSkills = await scanSkillDir(
|
|
37860
|
-
const kimiflareSkills = await scanSkillDir(
|
|
38823
|
+
const agentsSkills = await scanSkillDir(join61(cwd, ".agents", "skills"), "agents");
|
|
38824
|
+
const agentsMd = await fileExists(join61(cwd, "AGENTS.md")) ? [{ filePath: join61(cwd, "AGENTS.md"), source: "agents-md" }] : [];
|
|
38825
|
+
const githubSkills = await scanSkillDir(join61(cwd, ".github", "skills"), "github");
|
|
38826
|
+
const kimiflareSkills = await scanSkillDir(join61(cwd, ".kimiflare", "skills"), "kimiflare");
|
|
37861
38827
|
const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
|
|
37862
38828
|
return ordered;
|
|
37863
38829
|
}
|
|
37864
38830
|
async function readSkillFile(filePath) {
|
|
37865
|
-
const bytes = await
|
|
38831
|
+
const bytes = await readFile41(filePath);
|
|
37866
38832
|
return { bytes, text: bytes.toString("utf-8") };
|
|
37867
38833
|
}
|
|
37868
38834
|
var SKILL_EXTENSIONS;
|
|
@@ -38069,7 +39035,7 @@ var init_skills = __esm({
|
|
|
38069
39035
|
|
|
38070
39036
|
// src/ui/run-startup-tasks.ts
|
|
38071
39037
|
import { existsSync as existsSync19 } from "fs";
|
|
38072
|
-
import { join as
|
|
39038
|
+
import { join as join62 } from "path";
|
|
38073
39039
|
function runStartupTasks(deps) {
|
|
38074
39040
|
const {
|
|
38075
39041
|
cfg,
|
|
@@ -38092,7 +39058,7 @@ function runStartupTasks(deps) {
|
|
|
38092
39058
|
}
|
|
38093
39059
|
});
|
|
38094
39060
|
if (cfg.memoryEnabled) {
|
|
38095
|
-
const dbPath = cfg.memoryDbPath ??
|
|
39061
|
+
const dbPath = cfg.memoryDbPath ?? join62(process.cwd(), ".kimiflare", "memory.db");
|
|
38096
39062
|
const manager = new MemoryManager({
|
|
38097
39063
|
dbPath,
|
|
38098
39064
|
...auth,
|
|
@@ -38125,7 +39091,7 @@ function runStartupTasks(deps) {
|
|
|
38125
39091
|
});
|
|
38126
39092
|
const cwd = process.cwd();
|
|
38127
39093
|
sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
|
|
38128
|
-
if (["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync19(
|
|
39094
|
+
if (["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync19(join62(cwd, name)))) {
|
|
38129
39095
|
const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
|
|
38130
39096
|
const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
|
|
38131
39097
|
if (driftCount >= 5) {
|
|
@@ -38136,7 +39102,7 @@ function runStartupTasks(deps) {
|
|
|
38136
39102
|
memoryManagerRef.current?.close();
|
|
38137
39103
|
memoryManagerRef.current = null;
|
|
38138
39104
|
}
|
|
38139
|
-
const skillDbPath = cfg.memoryDbPath ??
|
|
39105
|
+
const skillDbPath = cfg.memoryDbPath ?? join62(process.cwd(), ".kimiflare", "memory.db");
|
|
38140
39106
|
const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
|
|
38141
39107
|
initSkillsSchema(skillDb);
|
|
38142
39108
|
void indexSkills({
|
|
@@ -38812,8 +39778,8 @@ var init_turn_nudges = __esm({
|
|
|
38812
39778
|
});
|
|
38813
39779
|
|
|
38814
39780
|
// src/commands/save.ts
|
|
38815
|
-
import { mkdir as
|
|
38816
|
-
import { dirname as
|
|
39781
|
+
import { mkdir as mkdir22, writeFile as writeFile19, unlink as unlink6 } from "fs/promises";
|
|
39782
|
+
import { dirname as dirname28 } from "path";
|
|
38817
39783
|
async function saveCustomCommand(opts2) {
|
|
38818
39784
|
const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
|
|
38819
39785
|
const filepath = `${dir}/${opts2.name}.md`;
|
|
@@ -38824,8 +39790,8 @@ async function saveCustomCommand(opts2) {
|
|
|
38824
39790
|
if (opts2.effort) data.effort = opts2.effort;
|
|
38825
39791
|
const frontmatter = serializeFrontmatter(data);
|
|
38826
39792
|
const content = frontmatter + opts2.template;
|
|
38827
|
-
await
|
|
38828
|
-
await
|
|
39793
|
+
await mkdir22(dirname28(filepath), { recursive: true });
|
|
39794
|
+
await writeFile19(filepath, content, "utf8");
|
|
38829
39795
|
return { filepath };
|
|
38830
39796
|
}
|
|
38831
39797
|
async function deleteCustomCommand(cmd) {
|
|
@@ -38924,7 +39890,7 @@ __export(app_exports, {
|
|
|
38924
39890
|
import { useState as useState6, useRef as useRef4, useEffect as useEffect2, useCallback as useCallback4, useMemo } from "react";
|
|
38925
39891
|
import { useApp, render } from "ink";
|
|
38926
39892
|
import { existsSync as existsSync20 } from "fs";
|
|
38927
|
-
import { join as
|
|
39893
|
+
import { join as join63 } from "path";
|
|
38928
39894
|
import { jsx } from "react/jsx-runtime";
|
|
38929
39895
|
function App({
|
|
38930
39896
|
initialCfg,
|
|
@@ -39178,7 +40144,7 @@ function App({
|
|
|
39178
40144
|
void (async () => {
|
|
39179
40145
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
39180
40146
|
try {
|
|
39181
|
-
await doResumeSession(
|
|
40147
|
+
await doResumeSession(join63(sessionsDir3(), `${initialResumeSessionId}.json`));
|
|
39182
40148
|
if (initialResumeReason === "crash") {
|
|
39183
40149
|
setEvents((e) => [
|
|
39184
40150
|
...e,
|
|
@@ -39411,7 +40377,7 @@ function App({
|
|
|
39411
40377
|
saveCheckpoint: async (checkpoint) => {
|
|
39412
40378
|
ensureSessionId();
|
|
39413
40379
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
39414
|
-
await addCheckpoint(
|
|
40380
|
+
await addCheckpoint(join63(sessionsDir3(), `${sessionIdRef.current}.json`), checkpoint);
|
|
39415
40381
|
},
|
|
39416
40382
|
summarize: () => generateContinuationSummary({
|
|
39417
40383
|
messages: messagesRef.current,
|
|
@@ -39930,27 +40896,20 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39930
40896
|
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: nudge }]);
|
|
39931
40897
|
}
|
|
39932
40898
|
const classification = classifyIntent(trimmed);
|
|
39933
|
-
|
|
39934
|
-
|
|
39935
|
-
|
|
39936
|
-
|
|
39937
|
-
|
|
39938
|
-
|
|
39939
|
-
|
|
39940
|
-
|
|
39941
|
-
|
|
39942
|
-
|
|
39943
|
-
|
|
39944
|
-
);
|
|
39945
|
-
|
|
39946
|
-
|
|
39947
|
-
setEvents((e) => [
|
|
39948
|
-
...e,
|
|
39949
|
-
{ kind: "info", key: mkKey(), text: `hook blocked the prompt: ${reason}` }
|
|
39950
|
-
]);
|
|
39951
|
-
endTurn();
|
|
39952
|
-
return;
|
|
39953
|
-
}
|
|
40899
|
+
const promptGate = await submitUserPrompt({
|
|
40900
|
+
hooks: hooksManagerRef.current,
|
|
40901
|
+
sessionId: ensureSessionId(trimmed),
|
|
40902
|
+
cwd: process.cwd(),
|
|
40903
|
+
prompt: opts2?.userPromptForHooks ?? display,
|
|
40904
|
+
tier: classification.tier,
|
|
40905
|
+
resumed: messagesRef.current.some((m) => m.role === "assistant"),
|
|
40906
|
+
model: overrideModel ?? cfg.model,
|
|
40907
|
+
auth: llmAuthFromConfig(cfg)
|
|
40908
|
+
});
|
|
40909
|
+
if (promptGate.blocked) {
|
|
40910
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `hook blocked the prompt: ${promptGate.reason}` }]);
|
|
40911
|
+
endTurn();
|
|
40912
|
+
return;
|
|
39954
40913
|
}
|
|
39955
40914
|
messagesRef.current.push({ role: "user", content });
|
|
39956
40915
|
const traceUserSource = denyFeedbackRef.current ? "deny_feedback" : "typed";
|
|
@@ -39970,7 +40929,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39970
40929
|
turnCounterRef.current += 1;
|
|
39971
40930
|
const nudges = turnNudges({
|
|
39972
40931
|
turn: turnCounterRef.current,
|
|
39973
|
-
hasContextFile: ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync20(
|
|
40932
|
+
hasContextFile: ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync20(join63(process.cwd(), name))),
|
|
39974
40933
|
contextStale: kimiMdStale,
|
|
39975
40934
|
mode: modeRef.current,
|
|
39976
40935
|
freshAfterTurns: cfg?.autoFreshSuggestionTurns ?? DEFAULT_AUTO_FRESH_SUGGESTION_TURNS,
|
|
@@ -40147,7 +41106,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40147
41106
|
lspManagerRef.current.notifyChange(path, content2);
|
|
40148
41107
|
} else {
|
|
40149
41108
|
void import("fs/promises").then(
|
|
40150
|
-
({ readFile:
|
|
41109
|
+
({ readFile: readFile42 }) => readFile42(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
|
|
40151
41110
|
})
|
|
40152
41111
|
);
|
|
40153
41112
|
}
|
|
@@ -40655,7 +41614,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40655
41614
|
lsp: {
|
|
40656
41615
|
servers: cfg?.lspServers ?? {},
|
|
40657
41616
|
scope: lspScope,
|
|
40658
|
-
hasProjectDir: existsSync20(
|
|
41617
|
+
hasProjectDir: existsSync20(join63(process.cwd(), ".kimiflare"))
|
|
40659
41618
|
}
|
|
40660
41619
|
});
|
|
40661
41620
|
});
|
|
@@ -40747,6 +41706,7 @@ var init_app = __esm({
|
|
|
40747
41706
|
init_run_init();
|
|
40748
41707
|
init_connect();
|
|
40749
41708
|
init_session_lifecycle();
|
|
41709
|
+
init_prompt_submit();
|
|
40750
41710
|
init_run_startup_tasks();
|
|
40751
41711
|
init_manager_init();
|
|
40752
41712
|
init_run_compact();
|
|
@@ -40786,6 +41746,7 @@ init_sessions();
|
|
|
40786
41746
|
init_context_budget();
|
|
40787
41747
|
init_subagent_stats();
|
|
40788
41748
|
init_session_lifecycle();
|
|
41749
|
+
init_prompt_submit();
|
|
40789
41750
|
init_attachments();
|
|
40790
41751
|
init_glob();
|
|
40791
41752
|
init_permissions_evaluator();
|
|
@@ -40830,8 +41791,8 @@ async function resolveFiles(filePatterns, cwd) {
|
|
|
40830
41791
|
const resolved = /* @__PURE__ */ new Set();
|
|
40831
41792
|
for (const pattern of filePatterns) {
|
|
40832
41793
|
try {
|
|
40833
|
-
const
|
|
40834
|
-
if (
|
|
41794
|
+
const stat15 = await import("fs/promises").then((m) => m.stat(resolve7(cwd, pattern)));
|
|
41795
|
+
if (stat15.isFile()) {
|
|
40835
41796
|
resolved.add(resolve7(cwd, pattern));
|
|
40836
41797
|
continue;
|
|
40837
41798
|
}
|
|
@@ -40959,6 +41920,33 @@ async function runPrintMode(opts2) {
|
|
|
40959
41920
|
});
|
|
40960
41921
|
const usageWrites = [];
|
|
40961
41922
|
const auth = llmAuthFromConfig(opts2);
|
|
41923
|
+
const gate = await submitUserPrompt({
|
|
41924
|
+
hooks,
|
|
41925
|
+
sessionId: sessionFile.id,
|
|
41926
|
+
cwd,
|
|
41927
|
+
prompt: opts2.prompt,
|
|
41928
|
+
tier: policy.classification.tier,
|
|
41929
|
+
resumed: messages.some((m) => m.role === "assistant"),
|
|
41930
|
+
model: opts2.model,
|
|
41931
|
+
auth
|
|
41932
|
+
});
|
|
41933
|
+
if (gate.blocked) {
|
|
41934
|
+
const msg = `prompt blocked by a UserPromptSubmit hook: ${gate.reason}`;
|
|
41935
|
+
if (format === "text") {
|
|
41936
|
+
process.stderr.write(`\x1B[31mautopilot: ${msg}\x1B[0m
|
|
41937
|
+
`);
|
|
41938
|
+
} else if (format === "json") {
|
|
41939
|
+
jsonOutput.durationMs = Date.now() - startMs;
|
|
41940
|
+
process.stdout.write(JSON.stringify({ ...jsonOutput, error: msg, blocked: { reason: gate.reason } }, null, 2) + "\n");
|
|
41941
|
+
} else {
|
|
41942
|
+
emitStreamJson("error", { message: msg, code: PROMPT_BLOCKED_EXIT_CODE, reason: "prompt_blocked" });
|
|
41943
|
+
}
|
|
41944
|
+
process.exitCode = PROMPT_BLOCKED_EXIT_CODE;
|
|
41945
|
+
await endSessions("exit");
|
|
41946
|
+
trace.emit("session_end", { reason: "exit" }, null);
|
|
41947
|
+
await trace.flush();
|
|
41948
|
+
return;
|
|
41949
|
+
}
|
|
40962
41950
|
const callbacks = {
|
|
40963
41951
|
onUsageFinal: (u, meta) => {
|
|
40964
41952
|
const lookup = opts2.openrouterApiKey && !auth.customEndpoint && meta?.generationId ? { apiKey: opts2.openrouterApiKey, meta } : void 0;
|
|
@@ -41204,7 +42192,7 @@ function registerEvalCommand(program2) {
|
|
|
41204
42192
|
// src/index.tsx
|
|
41205
42193
|
var resumeCommand = null;
|
|
41206
42194
|
var program = new Command();
|
|
41207
|
-
program.name("autopilot").description("Terminal coding agent. Bring your own provider: OpenRouter, OpenAI, DeepSeek, Kimi, Z.ai, Ollama, any OpenAI-compatible endpoint.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id for the active provider (defaults to its configured model)").addOption(new Option("--cloud", "(retired) KimiFlare Cloud").hideHelp()).option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").addOption(new Option("--worker-profile <profile>", "internal worker permission profile").hideHelp()).option("--reasoning", "include reasoning in stdout (print mode only)").option("--thinking", "alias for --reasoning").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-total-tool-iterations <n>", "hard ceiling on tool iterations per turn across --continue-on-limit resets (default 1000)", (v) => parseInt(v, 10)).option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--mode <mode>", "run mode: interactive (default), print, rpc").option("-c, --continue", "continue the most recent session in the current working directory").option("-S, --session <id>", "resume a specific session by id").option("-f, --file <path>", "attach file(s) to the prompt; repeatable, supports globs (print mode only)", (v, prev) => (prev ?? []).concat(v)).option("--format <mode>", "output format for print mode: text (default), json, stream-json").option("--dir <path>", "run in the specified directory instead of the current one (print mode only)").option("--title <title>", "override the auto-generated session title (print mode only)").option("--attach <url>", "attach to a running autopilot serve instance (print mode only)");
|
|
42195
|
+
program.name("autopilot").description("Terminal coding agent. Bring your own provider: OpenRouter, OpenAI, DeepSeek, Kimi, Z.ai, Ollama, any OpenAI-compatible endpoint.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id for the active provider (defaults to its configured model)").addOption(new Option("--cloud", "(retired) KimiFlare Cloud").hideHelp()).option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").addOption(new Option("--worker-profile <profile>", "internal worker permission profile").hideHelp()).option("--reasoning", "include reasoning in stdout (print mode only)").option("--thinking", "alias for --reasoning").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-total-tool-iterations <n>", "hard ceiling on tool iterations per turn across --continue-on-limit resets (default 1000)", (v) => parseInt(v, 10)).option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--mode <mode>", "run mode: interactive (default), print, rpc").option("-c, --continue", "continue the most recent session in the current working directory").option("-S, --session <id>", "resume a specific session by id").option("-f, --file <path>", "attach file(s) to the prompt; repeatable, supports globs (print mode only)", (v, prev) => (prev ?? []).concat(v)).option("--format <mode>", "output format for print mode: text (default), json, stream-json").option("--dir <path>", "run in the specified directory instead of the current one (print mode only)").option("--title <title>", "override the auto-generated session title (print mode only)").option("--attach <url>", "attach to a running autopilot serve instance (print mode only)").option("--on <server>", "run the task on a paired server (autopilot remote add) instead of this machine").option("--local", "run on this machine (the default)").option("--repo <owner/name>", "with --on: GitHub repository (default: this checkout's origin)").option("--detach", "with --on: start the task and return without streaming its output").argument("[task...]", "with --on: the task to run on the server");
|
|
41208
42196
|
program.command("cost").description("Show cost attribution by task type (requires costAttribution enabled)").option("-w, --week", "last 7 days (default)").option("-m, --month", "last 30 days").option("-d, --day", "today only").option("-s, --session <id>", "single session detail").option("-c, --category <name>", "filter by category").option("--json", "machine-readable output").option("--reclassify", "re-run classification on all sessions").option("--local-only", "skip OpenRouter reconciliation (no network)").option("--verify", "check recorded tokens and cost against OpenRouter's generation records (latest session, or -s <id>)").action(async (cmdOpts) => {
|
|
41209
42197
|
const cfg = await loadConfig();
|
|
41210
42198
|
if (cmdOpts.verify) {
|
|
@@ -41235,10 +42223,51 @@ program.command("cost").description("Show cost attribution by task type (require
|
|
|
41235
42223
|
const { runCostCommand: runCostCommand2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
|
|
41236
42224
|
await runCostCommand2({ ...cmdOpts, config: cfg });
|
|
41237
42225
|
});
|
|
41238
|
-
|
|
41239
|
-
|
|
41240
|
-
|
|
41241
|
-
|
|
42226
|
+
async function runRemoteCommand(fn) {
|
|
42227
|
+
const controller = new AbortController();
|
|
42228
|
+
const onInterrupt = () => controller.abort();
|
|
42229
|
+
process.once("SIGINT", onInterrupt);
|
|
42230
|
+
try {
|
|
42231
|
+
const code = await fn(controller.signal);
|
|
42232
|
+
if (typeof code === "number") process.exitCode = code;
|
|
42233
|
+
} catch (error) {
|
|
42234
|
+
if (controller.signal.aborted) return;
|
|
42235
|
+
const { describeError: describeError2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42236
|
+
console.error(`autopilot: ${describeError2(error)}`);
|
|
42237
|
+
process.exitCode = 1;
|
|
42238
|
+
} finally {
|
|
42239
|
+
process.off("SIGINT", onInterrupt);
|
|
42240
|
+
}
|
|
42241
|
+
}
|
|
42242
|
+
var remoteCommand = program.command("remote").description("Pair this machine with Autopilot servers that run tasks for you");
|
|
42243
|
+
remoteCommand.command("add").description("Pair with a server: approve the code it shows in your browser (no keys copied)").argument("<url>", "server URL, e.g. https://acme.example.com").option("-n, --name <name>", "short name for --on (default: the host's first label)").option("--no-open", "print the approval link instead of opening a browser").action((url, cmdOpts) => runRemoteCommand(async (signal) => {
|
|
42244
|
+
const { remoteAdd: remoteAdd2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42245
|
+
await remoteAdd2({ url, open: cmdOpts.open, signal, ...cmdOpts.name ? { name: cmdOpts.name } : {} });
|
|
42246
|
+
}));
|
|
42247
|
+
remoteCommand.command("list").description("List paired servers").action(() => runRemoteCommand(async () => {
|
|
42248
|
+
const { remoteList: remoteList2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42249
|
+
await remoteList2();
|
|
42250
|
+
}));
|
|
42251
|
+
remoteCommand.command("remove").description("Forget a paired server").argument("<name>").action((name) => runRemoteCommand(async () => {
|
|
42252
|
+
const { remoteRemove: remoteRemove2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42253
|
+
return await remoteRemove2(name) ? 0 : 1;
|
|
42254
|
+
}));
|
|
42255
|
+
var runsCommand = program.command("runs").description("List tasks running or finished on your servers").option("--on <server>", "only this server").option("--json", "machine-readable output").action((cmdOpts) => runRemoteCommand(async () => {
|
|
42256
|
+
const { listRuns: listRuns2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42257
|
+
await listRuns2({ ...cmdOpts.on ? { remote: cmdOpts.on } : {}, json: !!cmdOpts.json });
|
|
42258
|
+
}));
|
|
42259
|
+
runsCommand.command("watch").description("Show a run's output and follow it until it ends (Ctrl-C detaches)").argument("<run>", "run ID or prefix").option("--on <server>", "server the run is on").action((id, cmdOpts) => runRemoteCommand(async (signal) => {
|
|
42260
|
+
const { watchRun: watchRun2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42261
|
+
return watchRun2({ id, signal, ...cmdOpts.on ? { remote: cmdOpts.on } : {} });
|
|
42262
|
+
}));
|
|
42263
|
+
runsCommand.command("cancel").description("Stop a running task").argument("<run>", "run ID or prefix").option("--on <server>", "server the run is on").action((id, cmdOpts) => runRemoteCommand(async () => {
|
|
42264
|
+
const { cancelRun: cancelRun2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42265
|
+
await cancelRun2({ id, ...cmdOpts.on ? { remote: cmdOpts.on } : {} });
|
|
42266
|
+
}));
|
|
42267
|
+
program.command("pull").description("Check out a run's branch in this repository").argument("<run>", "run ID or prefix").option("--on <server>", "server the run is on").action((id, cmdOpts) => runRemoteCommand(async () => {
|
|
42268
|
+
const { pullRun: pullRun2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42269
|
+
await pullRun2({ id, cwd: process.cwd(), ...cmdOpts.on ? { remote: cmdOpts.on } : {} });
|
|
42270
|
+
}));
|
|
41242
42271
|
registerEvalCommand(program);
|
|
41243
42272
|
var asterCommand = program.command("aster").description("Manage the Aster mobile API");
|
|
41244
42273
|
asterCommand.command("setup").description("Configure Aster for one or more local Git workspaces").requiredOption("-w, --workspace <path>", "Local Git repository root (repeatable)", (value, previous = []) => [...previous, value]).option("--json", "Print the selected workspace IDs as JSON").action(async (options) => {
|
|
@@ -41639,7 +42668,9 @@ program.command("serve").description("Start a headless HTTP server for API acces
|
|
|
41639
42668
|
process.exit(1);
|
|
41640
42669
|
}
|
|
41641
42670
|
});
|
|
41642
|
-
|
|
42671
|
+
var positionalTask = [];
|
|
42672
|
+
program.action(async (task = []) => {
|
|
42673
|
+
positionalTask = task;
|
|
41643
42674
|
await main();
|
|
41644
42675
|
const { runShutdownTasks: runShutdownTasks2 } = await Promise.resolve().then(() => (init_exit(), exit_exports));
|
|
41645
42676
|
await runShutdownTasks2();
|
|
@@ -41654,10 +42685,43 @@ async function initOtel() {
|
|
|
41654
42685
|
onShutdown2("otel-flush", shutdownOtelSink2, SHUTDOWN_PHASE2.flush);
|
|
41655
42686
|
}
|
|
41656
42687
|
}
|
|
42688
|
+
async function runRemoteTask() {
|
|
42689
|
+
if (opts.on === void 0) {
|
|
42690
|
+
console.error(`autopilot: unknown command "${positionalTask[0]}". To run a task here use -p "\u2026"; on a server, --on <server> "\u2026".`);
|
|
42691
|
+
process.exit(2);
|
|
42692
|
+
}
|
|
42693
|
+
if (opts.local) {
|
|
42694
|
+
console.error("autopilot: --on and --local can't be combined.");
|
|
42695
|
+
process.exit(2);
|
|
42696
|
+
}
|
|
42697
|
+
const task = (positionalTask.join(" ") || opts.print || "").trim();
|
|
42698
|
+
if (!task) {
|
|
42699
|
+
console.error(`autopilot: --on needs a task, e.g. autopilot --on ${opts.on} "fix the flaky checkout test"`);
|
|
42700
|
+
process.exit(2);
|
|
42701
|
+
}
|
|
42702
|
+
const cfg = await loadConfig().catch(() => null);
|
|
42703
|
+
await runRemoteCommand(async (signal) => {
|
|
42704
|
+
const { runOnRemote: runOnRemote2 } = await Promise.resolve().then(() => (init_cli2(), cli_exports2));
|
|
42705
|
+
return runOnRemote2({
|
|
42706
|
+
remote: opts.on,
|
|
42707
|
+
task,
|
|
42708
|
+
cwd: opts.dir ?? process.cwd(),
|
|
42709
|
+
signal,
|
|
42710
|
+
detach: !!opts.detach,
|
|
42711
|
+
...opts.model ? { model: opts.model } : {},
|
|
42712
|
+
...cfg?.model ? { preferredModel: cfg.model } : {},
|
|
42713
|
+
...opts.repo ? { repository: opts.repo } : {}
|
|
42714
|
+
});
|
|
42715
|
+
});
|
|
42716
|
+
}
|
|
41657
42717
|
async function main() {
|
|
41658
42718
|
await initOtel();
|
|
41659
42719
|
const { installExitHandlers: installExitHandlers2 } = await Promise.resolve().then(() => (init_exit(), exit_exports));
|
|
41660
42720
|
installExitHandlers2({ crashes: true });
|
|
42721
|
+
if (opts.on !== void 0 || positionalTask.length > 0) {
|
|
42722
|
+
await runRemoteTask();
|
|
42723
|
+
return;
|
|
42724
|
+
}
|
|
41661
42725
|
const globalCfg = await loadConfig();
|
|
41662
42726
|
const updateResult = await checkForUpdate();
|
|
41663
42727
|
let cfg = globalCfg;
|
|
@@ -41836,12 +42900,12 @@ function requireModel(model) {
|
|
|
41836
42900
|
process.exit(2);
|
|
41837
42901
|
}
|
|
41838
42902
|
async function noCredentialsMessage() {
|
|
41839
|
-
const { readFile:
|
|
42903
|
+
const { readFile: readFile42 } = await import("fs/promises");
|
|
41840
42904
|
const { configPath: configPath2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
41841
42905
|
const { resolveActiveProvider: resolveActiveProvider2 } = await Promise.resolve().then(() => (init_resolve(), resolve_exports));
|
|
41842
42906
|
let persisted = {};
|
|
41843
42907
|
try {
|
|
41844
|
-
persisted = JSON.parse(await
|
|
42908
|
+
persisted = JSON.parse(await readFile42(configPath2(), "utf8"));
|
|
41845
42909
|
} catch {
|
|
41846
42910
|
}
|
|
41847
42911
|
const { id, preset } = resolveActiveProvider2(persisted);
|
|
@@ -41869,11 +42933,11 @@ async function promptHidden(question) {
|
|
|
41869
42933
|
}
|
|
41870
42934
|
});
|
|
41871
42935
|
const rl = createInterface4({ input: process.stdin, output, terminal: true });
|
|
41872
|
-
return new Promise((
|
|
42936
|
+
return new Promise((resolve22) => {
|
|
41873
42937
|
rl.question(question, (answer) => {
|
|
41874
42938
|
rl.close();
|
|
41875
42939
|
process.stdout.write("\n");
|
|
41876
|
-
|
|
42940
|
+
resolve22(answer);
|
|
41877
42941
|
});
|
|
41878
42942
|
muted = true;
|
|
41879
42943
|
});
|