@cabane/companion 0.6.142 → 0.6.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +272 -160
- package/dist/runtime.js +226 -121
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -17,14 +17,14 @@ import { z as z2 } from "zod";
|
|
|
17
17
|
|
|
18
18
|
// src/errors.ts
|
|
19
19
|
var CompanionError = class extends Error {
|
|
20
|
-
constructor(
|
|
21
|
-
super(
|
|
20
|
+
constructor(message2) {
|
|
21
|
+
super(message2);
|
|
22
22
|
this.name = "CompanionError";
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
var ApiError = class extends CompanionError {
|
|
26
|
-
constructor(status,
|
|
27
|
-
super(
|
|
26
|
+
constructor(status, message2, body) {
|
|
27
|
+
super(message2);
|
|
28
28
|
this.status = status;
|
|
29
29
|
this.body = body;
|
|
30
30
|
this.name = "ApiError";
|
|
@@ -33,14 +33,14 @@ var ApiError = class extends CompanionError {
|
|
|
33
33
|
body;
|
|
34
34
|
};
|
|
35
35
|
var ConfigError = class extends CompanionError {
|
|
36
|
-
constructor(
|
|
37
|
-
super(
|
|
36
|
+
constructor(message2) {
|
|
37
|
+
super(message2);
|
|
38
38
|
this.name = "ConfigError";
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
var PrepareHookError = class extends CompanionError {
|
|
42
|
-
constructor(
|
|
43
|
-
super(
|
|
42
|
+
constructor(message2) {
|
|
43
|
+
super(message2);
|
|
44
44
|
this.name = "PrepareHookError";
|
|
45
45
|
}
|
|
46
46
|
};
|
|
@@ -1202,8 +1202,8 @@ function formatHumanLine(record) {
|
|
|
1202
1202
|
let line = `${time} ${word ? `${word} ` : ""}${context ? `${context} ` : ""}${humanText(record.msg)}`;
|
|
1203
1203
|
const err = record.err;
|
|
1204
1204
|
const errorRecord = err !== null && typeof err === "object" ? err : null;
|
|
1205
|
-
const
|
|
1206
|
-
const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) ||
|
|
1205
|
+
const message2 = humanText(errorRecord && "message" in errorRecord ? errorRecord.message : err);
|
|
1206
|
+
const detail = typeof record.status === "number" ? `HTTP ${record.status}: ${humanText(record.responseBody) || message2}` : message2;
|
|
1207
1207
|
if (detail && !line.includes(detail)) line += ` (${detail})`;
|
|
1208
1208
|
if (level < 30) {
|
|
1209
1209
|
const hidden = /* @__PURE__ */ new Set([
|
|
@@ -1337,9 +1337,9 @@ var CompanionStateHub = class {
|
|
|
1337
1337
|
if (info.deviceLabel !== void 0) this.deviceLabel = info.deviceLabel;
|
|
1338
1338
|
this.emitStatus();
|
|
1339
1339
|
}
|
|
1340
|
-
setDeviceError(
|
|
1341
|
-
if (this.deviceError ===
|
|
1342
|
-
this.deviceError =
|
|
1340
|
+
setDeviceError(message2) {
|
|
1341
|
+
if (this.deviceError === message2) return;
|
|
1342
|
+
this.deviceError = message2;
|
|
1343
1343
|
this.emitStatus();
|
|
1344
1344
|
}
|
|
1345
1345
|
clearDeviceError() {
|
|
@@ -1960,7 +1960,7 @@ async function warnAboutHarnessReadiness(cfg, deps = {}) {
|
|
|
1960
1960
|
const probeClaude = deps.probeClaude ?? claudeOnPath;
|
|
1961
1961
|
const probeCodex = deps.probeCodex ?? codexOnPath;
|
|
1962
1962
|
const resolveBundled = deps.resolveBundled ?? ((runtime) => resolveBundledBinary(runtime));
|
|
1963
|
-
const warn = deps.warn ?? ((
|
|
1963
|
+
const warn = deps.warn ?? ((message2) => process.stderr.write(`${message2}
|
|
1964
1964
|
`));
|
|
1965
1965
|
const connected = [
|
|
1966
1966
|
...isClaudeCodeConnected(cfg) ? ["Claude Code"] : [],
|
|
@@ -2305,8 +2305,8 @@ function apiErrorCode(err) {
|
|
|
2305
2305
|
function apiErrorMessage(err) {
|
|
2306
2306
|
const body = err instanceof ApiError ? err.body : void 0;
|
|
2307
2307
|
if (body !== null && typeof body === "object") {
|
|
2308
|
-
const
|
|
2309
|
-
if (typeof
|
|
2308
|
+
const message2 = body.message;
|
|
2309
|
+
if (typeof message2 === "string" && message2.length > 0) return message2;
|
|
2310
2310
|
}
|
|
2311
2311
|
return err instanceof Error ? err.message : String(err);
|
|
2312
2312
|
}
|
|
@@ -2341,9 +2341,9 @@ function describeErrorBody(body) {
|
|
|
2341
2341
|
}
|
|
2342
2342
|
var CAUSE_TEXT_CAP = 200;
|
|
2343
2343
|
function describeError(err) {
|
|
2344
|
-
const
|
|
2344
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
2345
2345
|
const cause = describeCause(err);
|
|
2346
|
-
return cause === void 0 ?
|
|
2346
|
+
return cause === void 0 ? message2 : `${message2}: ${cause}`;
|
|
2347
2347
|
}
|
|
2348
2348
|
function describeCause(err) {
|
|
2349
2349
|
if (!(err instanceof Error)) return void 0;
|
|
@@ -4378,15 +4378,15 @@ async function* decodeSdkStream(iter, ctx) {
|
|
|
4378
4378
|
}
|
|
4379
4379
|
} catch (err) {
|
|
4380
4380
|
if (ctx.signal.aborted) throw err;
|
|
4381
|
-
const
|
|
4382
|
-
const failure = mcpInventory.initReceived ? classifyErrorText(
|
|
4381
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
4382
|
+
const failure = mcpInventory.initReceived ? classifyErrorText(message2) : classifyFailure(signals, { text: message2 });
|
|
4383
4383
|
if (!failure && (mcpInventory.initReceived || ctx.resumedSessionId === null)) throw err;
|
|
4384
4384
|
ok = false;
|
|
4385
4385
|
if (failure) {
|
|
4386
4386
|
resultReason = encodeFailureReason(failure);
|
|
4387
4387
|
} else {
|
|
4388
4388
|
resultReason = "session_start_failed";
|
|
4389
|
-
if (ctx.startEvidence) ctx.startEvidence.thrown =
|
|
4389
|
+
if (ctx.startEvidence) ctx.startEvidence.thrown = message2.slice(0, 200);
|
|
4390
4390
|
}
|
|
4391
4391
|
sawResult = true;
|
|
4392
4392
|
}
|
|
@@ -5315,9 +5315,9 @@ function readSessionError(properties) {
|
|
|
5315
5315
|
if (typeof err === "string") return err;
|
|
5316
5316
|
const rec = asRecord(err);
|
|
5317
5317
|
if (!rec) return "unknown";
|
|
5318
|
-
const { name, message } = deepestError(rec);
|
|
5319
|
-
if (
|
|
5320
|
-
if (
|
|
5318
|
+
const { name, message: message2 } = deepestError(rec);
|
|
5319
|
+
if (message2 && name && !isGenericErrorName(name)) return `${name}: ${message2}`;
|
|
5320
|
+
if (message2) return message2;
|
|
5321
5321
|
if (name) return name;
|
|
5322
5322
|
if (typeof rec.name === "string") return rec.name;
|
|
5323
5323
|
return "unknown";
|
|
@@ -5330,10 +5330,10 @@ function deepestError(node) {
|
|
|
5330
5330
|
const best = {};
|
|
5331
5331
|
let current = node;
|
|
5332
5332
|
for (let depth = 0; current && depth < 8; depth++) {
|
|
5333
|
-
const
|
|
5333
|
+
const message2 = typeof current.message === "string" ? current.message : void 0;
|
|
5334
5334
|
const name = typeof current.name === "string" && !isGenericErrorName(current.name) ? current.name : void 0;
|
|
5335
|
-
if (
|
|
5336
|
-
best.message =
|
|
5335
|
+
if (message2) {
|
|
5336
|
+
best.message = message2;
|
|
5337
5337
|
if (name) best.name = name;
|
|
5338
5338
|
} else if (name && !best.name) {
|
|
5339
5339
|
best.name = name;
|
|
@@ -5924,9 +5924,9 @@ var echoedUserPart = (partId, messageID, text) => ({
|
|
|
5924
5924
|
properties: { part: { type: "text", id: partId, messageID, text } }
|
|
5925
5925
|
});
|
|
5926
5926
|
var idle = () => ({ type: "session.idle", properties: {} });
|
|
5927
|
-
var errored = (
|
|
5927
|
+
var errored = (message2) => ({
|
|
5928
5928
|
type: "session.error",
|
|
5929
|
-
properties: { error: { message } }
|
|
5929
|
+
properties: { error: { message: message2 } }
|
|
5930
5930
|
});
|
|
5931
5931
|
var sessionEvent2 = (sessionId, directory = DIR, degraded = false) => ({
|
|
5932
5932
|
type: "session",
|
|
@@ -6361,8 +6361,8 @@ function readItemText(item) {
|
|
|
6361
6361
|
function readItemMessage(item) {
|
|
6362
6362
|
return str(item.message) ?? "";
|
|
6363
6363
|
}
|
|
6364
|
-
function isModelMetadataError(
|
|
6365
|
-
return
|
|
6364
|
+
function isModelMetadataError(message2) {
|
|
6365
|
+
return message2.includes("Defaulting to fallback metadata");
|
|
6366
6366
|
}
|
|
6367
6367
|
function readToolItem(item, eventType) {
|
|
6368
6368
|
const type = str(item.type);
|
|
@@ -6597,11 +6597,11 @@ async function* decodeCodexStream(events, ctx) {
|
|
|
6597
6597
|
if (!item) continue;
|
|
6598
6598
|
const itemType = readItemType(item);
|
|
6599
6599
|
if (itemType === "error") {
|
|
6600
|
-
const
|
|
6601
|
-
if (isModelMetadataError(
|
|
6600
|
+
const message2 = readItemMessage(item);
|
|
6601
|
+
if (isModelMetadataError(message2)) {
|
|
6602
6602
|
yield* flushInterim();
|
|
6603
6603
|
ok = false;
|
|
6604
|
-
reason = `model_unavailable:${
|
|
6604
|
+
reason = `model_unavailable:${message2.slice(0, 200)}`;
|
|
6605
6605
|
settled = true;
|
|
6606
6606
|
break;
|
|
6607
6607
|
}
|
|
@@ -6758,7 +6758,7 @@ var ENV_ENVELOPE_KEYS = [
|
|
|
6758
6758
|
"CABANE_CONVERSATION_TITLE",
|
|
6759
6759
|
"CABANE_HOST_ACCESS"
|
|
6760
6760
|
];
|
|
6761
|
-
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null) {
|
|
6761
|
+
function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromptFingerprint = null, imagePaths = []) {
|
|
6762
6762
|
const { policy, config } = req;
|
|
6763
6763
|
const directory = req.local.cwd ?? "";
|
|
6764
6764
|
const dialect = readCodexDialect(config.runtimeOptions);
|
|
@@ -6775,7 +6775,7 @@ function buildRunSpec2(req, resumeThreadId, instructionsFile = null, threadPromp
|
|
|
6775
6775
|
skipGitRepoCheck: true,
|
|
6776
6776
|
...dialect.modelReasoningEffort ? { modelReasoningEffort: dialect.modelReasoningEffort } : {},
|
|
6777
6777
|
baseInstructionsFile,
|
|
6778
|
-
input: buildInput(req, promptRidesInput),
|
|
6778
|
+
input: withImages(buildInput(req, promptRidesInput), imagePaths),
|
|
6779
6779
|
promptRidesInput,
|
|
6780
6780
|
promptFingerprint,
|
|
6781
6781
|
config: buildConfig(req, baseInstructionsFile),
|
|
@@ -6791,6 +6791,13 @@ function buildInput(req, promptRidesInput) {
|
|
|
6791
6791
|
|
|
6792
6792
|
${body}` : body;
|
|
6793
6793
|
}
|
|
6794
|
+
function withImages(text, imagePaths) {
|
|
6795
|
+
if (imagePaths.length === 0) return text;
|
|
6796
|
+
return [
|
|
6797
|
+
{ type: "text", text },
|
|
6798
|
+
...imagePaths.map((path) => ({ type: "local_image", path }))
|
|
6799
|
+
];
|
|
6800
|
+
}
|
|
6794
6801
|
function buildConfig(req, baseInstructionsFile) {
|
|
6795
6802
|
const mcp_servers = {};
|
|
6796
6803
|
for (const [name, server] of Object.entries(req.local.mcpServers ?? {})) {
|
|
@@ -7032,12 +7039,35 @@ function createCodexAdapter(deps = {}) {
|
|
|
7032
7039
|
}
|
|
7033
7040
|
}
|
|
7034
7041
|
}
|
|
7042
|
+
let images = null;
|
|
7035
7043
|
try {
|
|
7044
|
+
const imageUrls = req.content.flatMap((b) => b.type === "image" ? [b.source.url] : []);
|
|
7045
|
+
if (imageUrls.length > 0) {
|
|
7046
|
+
if (!deps.prepareImages) {
|
|
7047
|
+
deps.onWarn?.(
|
|
7048
|
+
"codex adapter: no image preparer wired on this host \u2014 the turn runs without its images (CT1757)",
|
|
7049
|
+
{ images: imageUrls.length }
|
|
7050
|
+
);
|
|
7051
|
+
} else {
|
|
7052
|
+
try {
|
|
7053
|
+
images = await deps.prepareImages(imageUrls, signal);
|
|
7054
|
+
} catch (err) {
|
|
7055
|
+
if (!signal.aborted) {
|
|
7056
|
+
deps.onWarn?.(
|
|
7057
|
+
"codex adapter: preparing the images failed \u2014 the turn runs without them (CT1757)",
|
|
7058
|
+
{ err: err instanceof Error ? err.message : String(err) }
|
|
7059
|
+
);
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
if (signal.aborted) return;
|
|
7064
|
+
}
|
|
7036
7065
|
const spec = buildRunSpec2(
|
|
7037
7066
|
req,
|
|
7038
7067
|
resumeThreadId,
|
|
7039
7068
|
instructions?.path ?? null,
|
|
7040
|
-
threadPromptFingerprint
|
|
7069
|
+
threadPromptFingerprint,
|
|
7070
|
+
images?.paths ?? []
|
|
7041
7071
|
);
|
|
7042
7072
|
if (spec.model === null) {
|
|
7043
7073
|
deps.onWarn?.("Codex is using its default model because no model was resolved", {
|
|
@@ -7066,6 +7096,15 @@ function createCodexAdapter(deps = {}) {
|
|
|
7066
7096
|
resolvedReasoningEffort: spec.modelReasoningEffort ?? null
|
|
7067
7097
|
});
|
|
7068
7098
|
} finally {
|
|
7099
|
+
if (images) {
|
|
7100
|
+
try {
|
|
7101
|
+
await images.cleanup();
|
|
7102
|
+
} catch (err) {
|
|
7103
|
+
deps.onWarn?.("codex adapter: image cleanup failed (CT1757)", {
|
|
7104
|
+
err: err instanceof Error ? err.message : String(err)
|
|
7105
|
+
});
|
|
7106
|
+
}
|
|
7107
|
+
}
|
|
7069
7108
|
if (instructions) {
|
|
7070
7109
|
try {
|
|
7071
7110
|
await instructions.cleanup();
|
|
@@ -7132,14 +7171,14 @@ var turnCompleted = (usage) => ({
|
|
|
7132
7171
|
type: "turn.completed",
|
|
7133
7172
|
...usage ? { usage } : {}
|
|
7134
7173
|
});
|
|
7135
|
-
var turnFailed = (
|
|
7174
|
+
var turnFailed = (message2) => ({
|
|
7136
7175
|
type: "turn.failed",
|
|
7137
|
-
error: { message }
|
|
7176
|
+
error: { message: message2 }
|
|
7138
7177
|
});
|
|
7139
|
-
var fatalError = (
|
|
7140
|
-
var errorItem = (id,
|
|
7178
|
+
var fatalError = (message2) => ({ type: "error", message: message2 });
|
|
7179
|
+
var errorItem = (id, message2) => ({
|
|
7141
7180
|
type: "item.completed",
|
|
7142
|
-
item: { id, type: "error", message }
|
|
7181
|
+
item: { id, type: "error", message: message2 }
|
|
7143
7182
|
});
|
|
7144
7183
|
var OP_SUCCESS = { ok: true, result: { source: "native" } };
|
|
7145
7184
|
var OP_FAILURE = {
|
|
@@ -8058,7 +8097,7 @@ var ConnectorHealthStore = class {
|
|
|
8058
8097
|
// src/dispatcher.ts
|
|
8059
8098
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8060
8099
|
import { existsSync as existsSync10, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
8061
|
-
import { join as
|
|
8100
|
+
import { join as join15 } from "path";
|
|
8062
8101
|
|
|
8063
8102
|
// src/runner/launch.ts
|
|
8064
8103
|
import { createInterface } from "readline";
|
|
@@ -8124,8 +8163,8 @@ function unenforceable(policy) {
|
|
|
8124
8163
|
deniedDomains: as === "deniedDomains" ? [host] : []
|
|
8125
8164
|
});
|
|
8126
8165
|
if (!check.success) {
|
|
8127
|
-
const
|
|
8128
|
-
return `runners.policy.${field}.${i}: ${
|
|
8166
|
+
const message2 = check.error.issues[0]?.message ?? "not a host pattern the sandbox accepts";
|
|
8167
|
+
return `runners.policy.${field}.${i}: ${message2}`;
|
|
8129
8168
|
}
|
|
8130
8169
|
}
|
|
8131
8170
|
}
|
|
@@ -8139,8 +8178,8 @@ function unenforceable(policy) {
|
|
|
8139
8178
|
try {
|
|
8140
8179
|
new RegExp(globToRegex(path));
|
|
8141
8180
|
} catch (err) {
|
|
8142
|
-
const
|
|
8143
|
-
return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${
|
|
8181
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
8182
|
+
return `runners.policy.${field}.${i}: not a pattern the sandbox can match (${message2})`;
|
|
8144
8183
|
}
|
|
8145
8184
|
}
|
|
8146
8185
|
}
|
|
@@ -8259,8 +8298,8 @@ async function reapTurnScopes(turnId) {
|
|
|
8259
8298
|
});
|
|
8260
8299
|
return { failures: [] };
|
|
8261
8300
|
} catch (err) {
|
|
8262
|
-
const
|
|
8263
|
-
return classifyStopError(
|
|
8301
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
8302
|
+
return classifyStopError(message2) === "absent" ? { failures: [] } : { failures: [message2.slice(0, 200)] };
|
|
8264
8303
|
}
|
|
8265
8304
|
}
|
|
8266
8305
|
function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
|
|
@@ -8276,13 +8315,13 @@ function scopeCapabilityUsable(env, timeoutMs = CAPABILITY_PROBE_TIMEOUT_MS) {
|
|
|
8276
8315
|
return false;
|
|
8277
8316
|
}
|
|
8278
8317
|
}
|
|
8279
|
-
function classifyStopError(
|
|
8318
|
+
function classifyStopError(message2) {
|
|
8280
8319
|
if (/failed to connect to bus|failed to get d-?bus connection|connection refused|refusing to operate|spawn systemctl|systemctl.*ENOENT|ENOENT.*systemctl/i.test(
|
|
8281
|
-
|
|
8320
|
+
message2
|
|
8282
8321
|
)) {
|
|
8283
8322
|
return "failure";
|
|
8284
8323
|
}
|
|
8285
|
-
if (/not loaded|could not be found|no such unit|unit .* not found/i.test(
|
|
8324
|
+
if (/not loaded|could not be found|no such unit|unit .* not found/i.test(message2))
|
|
8286
8325
|
return "absent";
|
|
8287
8326
|
return "failure";
|
|
8288
8327
|
}
|
|
@@ -8394,9 +8433,9 @@ function createTurnContainment(turnId, deps = {}) {
|
|
|
8394
8433
|
timeout: (CONTAINMENT_STOP_GRACE_SEC + 10) * 1e3
|
|
8395
8434
|
});
|
|
8396
8435
|
} catch (err) {
|
|
8397
|
-
const
|
|
8398
|
-
if (classifyStopError(
|
|
8399
|
-
result.failures.push({ target: unit, error:
|
|
8436
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
8437
|
+
if (classifyStopError(message2) === "failure") {
|
|
8438
|
+
result.failures.push({ target: unit, error: message2.slice(0, 200) });
|
|
8400
8439
|
continue;
|
|
8401
8440
|
}
|
|
8402
8441
|
result.reaped.push(unit);
|
|
@@ -8801,8 +8840,8 @@ function runnerStdin(input) {
|
|
|
8801
8840
|
})}
|
|
8802
8841
|
`;
|
|
8803
8842
|
}
|
|
8804
|
-
function encodeLauncherMessage(
|
|
8805
|
-
return `${JSON.stringify(
|
|
8843
|
+
function encodeLauncherMessage(message2) {
|
|
8844
|
+
return `${JSON.stringify(message2)}
|
|
8806
8845
|
`;
|
|
8807
8846
|
}
|
|
8808
8847
|
function decodeRunnerEvent(line) {
|
|
@@ -9534,6 +9573,68 @@ async function writeCodexInstructionsFile(contents) {
|
|
|
9534
9573
|
};
|
|
9535
9574
|
}
|
|
9536
9575
|
|
|
9576
|
+
// src/codex-images.ts
|
|
9577
|
+
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
9578
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
9579
|
+
import { join as join12 } from "path";
|
|
9580
|
+
var FETCH_TIMEOUT_MS = 3e4;
|
|
9581
|
+
var EXTENSIONS = {
|
|
9582
|
+
"image/png": "png",
|
|
9583
|
+
"image/jpeg": "jpg",
|
|
9584
|
+
"image/gif": "gif",
|
|
9585
|
+
"image/webp": "webp"
|
|
9586
|
+
};
|
|
9587
|
+
function createCodexImagePreparer(opts) {
|
|
9588
|
+
const doFetch = opts.fetch ?? fetch;
|
|
9589
|
+
const timeoutMs = opts.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
9590
|
+
return async (urls, signal) => {
|
|
9591
|
+
signal.throwIfAborted();
|
|
9592
|
+
const dir2 = await mkdtemp2(join12(tmpdir2(), `cabane-codex-images-${opts.turnId}-`));
|
|
9593
|
+
const cleanup = async () => {
|
|
9594
|
+
try {
|
|
9595
|
+
await rm2(dir2, { recursive: true, force: true });
|
|
9596
|
+
} catch (err) {
|
|
9597
|
+
opts.warn(
|
|
9598
|
+
{ dir: dir2, err: message(err) },
|
|
9599
|
+
"codex images: could not remove the scratch directory"
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9602
|
+
};
|
|
9603
|
+
const prepareOne = async (url, index) => {
|
|
9604
|
+
try {
|
|
9605
|
+
const res = await doFetch(url, {
|
|
9606
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)])
|
|
9607
|
+
});
|
|
9608
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9609
|
+
const type = (res.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
9610
|
+
const ext = EXTENSIONS[type];
|
|
9611
|
+
if (!ext) throw new Error(`not a vision image type: ${type || "none"}`);
|
|
9612
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
9613
|
+
const path = join12(dir2, `image-${index + 1}.${ext}`);
|
|
9614
|
+
await writeFile2(path, bytes, { mode: 384 });
|
|
9615
|
+
return path;
|
|
9616
|
+
} catch (err) {
|
|
9617
|
+
if (!signal.aborted) {
|
|
9618
|
+
opts.warn(
|
|
9619
|
+
{ image: index + 1, of: urls.length, err: message(err) },
|
|
9620
|
+
"codex images: an image could not be prepared; the turn runs without it"
|
|
9621
|
+
);
|
|
9622
|
+
}
|
|
9623
|
+
return null;
|
|
9624
|
+
}
|
|
9625
|
+
};
|
|
9626
|
+
const paths = await Promise.all(urls.map(prepareOne));
|
|
9627
|
+
if (signal.aborted) {
|
|
9628
|
+
await cleanup();
|
|
9629
|
+
signal.throwIfAborted();
|
|
9630
|
+
}
|
|
9631
|
+
return { paths: paths.filter((p) => p !== null), cleanup };
|
|
9632
|
+
};
|
|
9633
|
+
}
|
|
9634
|
+
function message(err) {
|
|
9635
|
+
return err instanceof Error ? err.message : String(err);
|
|
9636
|
+
}
|
|
9637
|
+
|
|
9537
9638
|
// src/turn-seq-floor.ts
|
|
9538
9639
|
var SeqFloorUnavailable = class extends Error {
|
|
9539
9640
|
constructor(detail) {
|
|
@@ -9571,15 +9672,15 @@ function readOutboxFloor(read, turnId, log) {
|
|
|
9571
9672
|
import { spawn as spawn5 } from "child_process";
|
|
9572
9673
|
import { hostname } from "os";
|
|
9573
9674
|
function prepareFailureCause(error) {
|
|
9574
|
-
const
|
|
9575
|
-
if (/timed out/i.test(
|
|
9576
|
-
if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(
|
|
9675
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
9676
|
+
if (/timed out/i.test(message2)) return "prepare hook timed out";
|
|
9677
|
+
if (/MODULE_NOT_FOUND|Cannot find (?:module|package)/.test(message2))
|
|
9577
9678
|
return "prepare hook module missing; check the configured command and its installation for this device";
|
|
9578
|
-
if (/not valid JSON|JSON must carry|produced no output/.test(
|
|
9679
|
+
if (/not valid JSON|JSON must carry|produced no output/.test(message2))
|
|
9579
9680
|
return "prepare hook returned malformed output; expected a cwd path or JSON with a non-empty cwd";
|
|
9580
|
-
if (/ENOENT/.test(
|
|
9581
|
-
if (/EACCES/.test(
|
|
9582
|
-
const exit =
|
|
9681
|
+
if (/ENOENT/.test(message2)) return "prepare hook executable missing (ENOENT)";
|
|
9682
|
+
if (/EACCES/.test(message2)) return "prepare hook executable is not accessible (EACCES)";
|
|
9683
|
+
const exit = message2.match(/exited with code (\d+)/)?.[1];
|
|
9583
9684
|
return exit ? `prepare hook exited with code ${exit}` : "prepare hook failed; inspect the device-local hook log";
|
|
9584
9685
|
}
|
|
9585
9686
|
async function runPrepareWithReporting(runner, hook, input, context, reportError) {
|
|
@@ -9631,15 +9732,15 @@ function reportFailure(config, body) {
|
|
|
9631
9732
|
|
|
9632
9733
|
// src/prepared.ts
|
|
9633
9734
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync5, existsSync as existsSync8 } from "fs";
|
|
9634
|
-
import { join as
|
|
9735
|
+
import { join as join13 } from "path";
|
|
9635
9736
|
function dirFor(workspaceId) {
|
|
9636
|
-
return
|
|
9737
|
+
return join13(cabaneDir(), "prepared", encodeURIComponent(workspaceId));
|
|
9637
9738
|
}
|
|
9638
9739
|
function conversationDir(workspaceId, conversationId) {
|
|
9639
|
-
return
|
|
9740
|
+
return join13(dirFor(workspaceId), encodeURIComponent(conversationId));
|
|
9640
9741
|
}
|
|
9641
9742
|
function pathFor2(workspaceId, conversationId, agentId) {
|
|
9642
|
-
return
|
|
9743
|
+
return join13(conversationDir(workspaceId, conversationId), `${encodeURIComponent(agentId)}.json`);
|
|
9643
9744
|
}
|
|
9644
9745
|
function readPrepared(workspaceId, conversationId, agentId) {
|
|
9645
9746
|
const path = pathFor2(workspaceId, conversationId, agentId);
|
|
@@ -9671,9 +9772,9 @@ function clearPrepared(workspaceId, conversationId, agentId) {
|
|
|
9671
9772
|
|
|
9672
9773
|
// src/transcript-writer.ts
|
|
9673
9774
|
import { appendFileSync, chmodSync as chmodSync4, copyFileSync, mkdirSync as mkdirSync8, readdirSync, rmSync as rmSync6 } from "fs";
|
|
9674
|
-
import { basename, dirname as dirname6, join as
|
|
9775
|
+
import { basename, dirname as dirname6, join as join14 } from "path";
|
|
9675
9776
|
function transcriptsDir() {
|
|
9676
|
-
return
|
|
9777
|
+
return join14(runnerStateDir(), "transcripts");
|
|
9677
9778
|
}
|
|
9678
9779
|
var RETAIN = 200;
|
|
9679
9780
|
var ANOMALY_RETAIN = 50;
|
|
@@ -9683,7 +9784,7 @@ var TranscriptWriter = class {
|
|
|
9683
9784
|
onWarn;
|
|
9684
9785
|
constructor(dir2, meta, onWarn) {
|
|
9685
9786
|
this.onWarn = onWarn;
|
|
9686
|
-
this.path =
|
|
9787
|
+
this.path = join14(dir2, fileName(meta));
|
|
9687
9788
|
try {
|
|
9688
9789
|
mkdirSync8(dir2, { recursive: true });
|
|
9689
9790
|
try {
|
|
@@ -9712,10 +9813,10 @@ var TranscriptWriter = class {
|
|
|
9712
9813
|
preserveAnomaly() {
|
|
9713
9814
|
if (this.broken) return;
|
|
9714
9815
|
try {
|
|
9715
|
-
const dir2 =
|
|
9816
|
+
const dir2 = join14(dirname6(this.path), "anomalies");
|
|
9716
9817
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
9717
9818
|
chmodSync4(dir2, 448);
|
|
9718
|
-
const target =
|
|
9819
|
+
const target = join14(dir2, basename(this.path));
|
|
9719
9820
|
copyFileSync(this.path, target);
|
|
9720
9821
|
chmodSync4(target, 384);
|
|
9721
9822
|
pruneOld(dir2, ANOMALY_RETAIN);
|
|
@@ -9761,7 +9862,7 @@ function pruneOld(dir2, retain) {
|
|
|
9761
9862
|
const drop = files.sort().slice(0, files.length - retain);
|
|
9762
9863
|
for (const f of drop) {
|
|
9763
9864
|
try {
|
|
9764
|
-
rmSync6(
|
|
9865
|
+
rmSync6(join14(dir2, f), { force: true });
|
|
9765
9866
|
} catch {
|
|
9766
9867
|
}
|
|
9767
9868
|
}
|
|
@@ -10332,16 +10433,16 @@ var TurnExecution = class {
|
|
|
10332
10433
|
}
|
|
10333
10434
|
gateTrigger() {
|
|
10334
10435
|
const { payload, turnLog } = this;
|
|
10335
|
-
const
|
|
10336
|
-
const isDispatchableTrigger =
|
|
10436
|
+
const message2 = this.turnContext.message;
|
|
10437
|
+
const isDispatchableTrigger = message2.role === "user" || message2.role === "agent" || message2.role === "system";
|
|
10337
10438
|
if (!isDispatchableTrigger) {
|
|
10338
|
-
turnLog.debug({ role:
|
|
10439
|
+
turnLog.debug({ role: message2.role }, "dispatcher: trigger role not dispatchable \u2014 skipping");
|
|
10339
10440
|
throw this.concluded("unexpected_role", UNEXPECTED_ROLE_REASON);
|
|
10340
10441
|
}
|
|
10341
10442
|
this.supervisor.notifyStart({
|
|
10342
10443
|
id: this.dispatchId,
|
|
10343
10444
|
conversationId: payload.conversationId,
|
|
10344
|
-
message:
|
|
10445
|
+
message: message2.body
|
|
10345
10446
|
});
|
|
10346
10447
|
}
|
|
10347
10448
|
async resolveSecrets() {
|
|
@@ -10692,6 +10793,10 @@ var TurnExecution = class {
|
|
|
10692
10793
|
enabled: true,
|
|
10693
10794
|
onWarn,
|
|
10694
10795
|
writeInstructionsFile: writeCodexInstructionsFile,
|
|
10796
|
+
prepareImages: createCodexImagePreparer({
|
|
10797
|
+
turnId: this.turnId,
|
|
10798
|
+
warn: (meta, msg) => turnLog.warn(meta, msg)
|
|
10799
|
+
}),
|
|
10695
10800
|
...codexPath ? { transport: createSdkCodexTransport({ codexPathOverride: codexPath }) } : {}
|
|
10696
10801
|
})
|
|
10697
10802
|
);
|
|
@@ -11171,7 +11276,7 @@ function checkoutState(cwd) {
|
|
|
11171
11276
|
if (entries.length === 0) {
|
|
11172
11277
|
return { ok: false, reason: `${cwd} is empty \u2014 a recreated shell, not a prepared directory` };
|
|
11173
11278
|
}
|
|
11174
|
-
const gitPath =
|
|
11279
|
+
const gitPath = join15(cwd, ".git");
|
|
11175
11280
|
if (!existsSync10(gitPath)) return { ok: true, reason: "usable" };
|
|
11176
11281
|
let stat;
|
|
11177
11282
|
try {
|
|
@@ -11179,7 +11284,7 @@ function checkoutState(cwd) {
|
|
|
11179
11284
|
} catch (error) {
|
|
11180
11285
|
return { ok: false, reason: `${gitPath} is unreadable (${error.message})` };
|
|
11181
11286
|
}
|
|
11182
|
-
if (stat.isDirectory() && !existsSync10(
|
|
11287
|
+
if (stat.isDirectory() && !existsSync10(join15(gitPath, "HEAD")))
|
|
11183
11288
|
return { ok: false, reason: `${gitPath} has no HEAD \u2014 an empty shell, not a checkout` };
|
|
11184
11289
|
return { ok: true, reason: "usable" };
|
|
11185
11290
|
}
|
|
@@ -11372,7 +11477,7 @@ function createLocalProcessProvider(opts = {}) {
|
|
|
11372
11477
|
|
|
11373
11478
|
// src/providers/docker.ts
|
|
11374
11479
|
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
11375
|
-
import { constants as constants2, tmpdir as
|
|
11480
|
+
import { constants as constants2, tmpdir as tmpdir3 } from "os";
|
|
11376
11481
|
import {
|
|
11377
11482
|
createReadStream,
|
|
11378
11483
|
createWriteStream as createWriteStream2,
|
|
@@ -11385,7 +11490,7 @@ import {
|
|
|
11385
11490
|
statSync as statSync3,
|
|
11386
11491
|
writeFileSync as writeFileSync6
|
|
11387
11492
|
} from "fs";
|
|
11388
|
-
import { join as
|
|
11493
|
+
import { join as join18 } from "path";
|
|
11389
11494
|
import { Writable } from "stream";
|
|
11390
11495
|
|
|
11391
11496
|
// src/providers/docker-cli.ts
|
|
@@ -11579,7 +11684,7 @@ import {
|
|
|
11579
11684
|
renameSync as renameSync4,
|
|
11580
11685
|
rmSync as rmSync8
|
|
11581
11686
|
} from "fs";
|
|
11582
|
-
import { join as
|
|
11687
|
+
import { join as join16 } from "path";
|
|
11583
11688
|
function routes(t) {
|
|
11584
11689
|
const conv = `/api/workspaces/${t.workspaceId}/conversations/${t.conversationId}`;
|
|
11585
11690
|
const agent = `${conv}/participants/agents/${t.agentId}`;
|
|
@@ -11625,7 +11730,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11625
11730
|
const staged = readdirSync3(from).filter((n) => n.endsWith(".json")).map((name) => {
|
|
11626
11731
|
let entry = {};
|
|
11627
11732
|
try {
|
|
11628
|
-
entry = JSON.parse(readFileSync8(
|
|
11733
|
+
entry = JSON.parse(readFileSync8(join16(from, name), "utf8"));
|
|
11629
11734
|
} catch {
|
|
11630
11735
|
}
|
|
11631
11736
|
return { name, entry };
|
|
@@ -11638,7 +11743,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11638
11743
|
let published = 0;
|
|
11639
11744
|
for (const { name } of staged) {
|
|
11640
11745
|
try {
|
|
11641
|
-
move(
|
|
11746
|
+
move(join16(from, name), join16(to, name));
|
|
11642
11747
|
} catch {
|
|
11643
11748
|
return { published, complete: false };
|
|
11644
11749
|
}
|
|
@@ -11651,7 +11756,7 @@ function publishStaged(from, to, move = atomicMove) {
|
|
|
11651
11756
|
// src/providers/runner-base.ts
|
|
11652
11757
|
import { createHash as createHash3 } from "crypto";
|
|
11653
11758
|
import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
|
|
11654
|
-
import { dirname as dirname7, join as
|
|
11759
|
+
import { dirname as dirname7, join as join17, sep } from "path";
|
|
11655
11760
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11656
11761
|
var COMPANION_MOUNT = "/opt/cabane/companion";
|
|
11657
11762
|
var BASE_DOCKERFILE = `FROM node:22-slim
|
|
@@ -11699,11 +11804,11 @@ async function ensureBaseImage(docker, image, log) {
|
|
|
11699
11804
|
function ownInstall(moduleFile = fileURLToPath3(import.meta.url)) {
|
|
11700
11805
|
let dir2 = dirname7(moduleFile);
|
|
11701
11806
|
for (let i = 0; i < 4; i++) {
|
|
11702
|
-
if (existsSync12(
|
|
11807
|
+
if (existsSync12(join17(dir2, "dist", "cli.js")) && existsSync12(join17(dir2, "package.json"))) break;
|
|
11703
11808
|
dir2 = dirname7(dir2);
|
|
11704
11809
|
}
|
|
11705
|
-
if (!existsSync12(
|
|
11706
|
-
const sdk =
|
|
11810
|
+
if (!existsSync12(join17(dir2, "dist", "cli.js"))) return null;
|
|
11811
|
+
const sdk = join17(dir2, "node_modules", "@anthropic-ai", "claude-agent-sdk");
|
|
11707
11812
|
if (!existsSync12(sdk)) return null;
|
|
11708
11813
|
const root = realpathSync2(dir2);
|
|
11709
11814
|
return realpathSync2(sdk).startsWith(root + sep) ? root : null;
|
|
@@ -11842,21 +11947,21 @@ function networkCreateError(stderr) {
|
|
|
11842
11947
|
}
|
|
11843
11948
|
function sessionArchivePath(deviceDir, attemptId) {
|
|
11844
11949
|
if (!UUID_RE.test(attemptId)) throw new Error(`docker: not an attempt id: ${attemptId}`);
|
|
11845
|
-
return
|
|
11950
|
+
return join18(deviceDir, "attempts", attemptId, "session.tar");
|
|
11846
11951
|
}
|
|
11847
11952
|
function pruneOtherAttempts(deviceDir, pair, keep) {
|
|
11848
|
-
const root =
|
|
11953
|
+
const root = join18(deviceDir, "attempts");
|
|
11849
11954
|
if (!existsSync13(root)) return;
|
|
11850
11955
|
for (const id of readdirSync4(root)) {
|
|
11851
11956
|
if (id === keep || !UUID_RE.test(id)) continue;
|
|
11852
11957
|
let pairOf = "";
|
|
11853
11958
|
try {
|
|
11854
|
-
pairOf = readFileSync9(
|
|
11959
|
+
pairOf = readFileSync9(join18(root, id, "pair"), "utf8");
|
|
11855
11960
|
} catch {
|
|
11856
11961
|
continue;
|
|
11857
11962
|
}
|
|
11858
11963
|
if (pairOf === `${pair.conversationId}/${pair.agentId}`) {
|
|
11859
|
-
rmSync9(
|
|
11964
|
+
rmSync9(join18(root, id), { recursive: true, force: true });
|
|
11860
11965
|
}
|
|
11861
11966
|
}
|
|
11862
11967
|
}
|
|
@@ -12056,11 +12161,11 @@ function createDockerProvider(opts) {
|
|
|
12056
12161
|
await must(["network", "disconnect", "-f", other, name], "moving the container");
|
|
12057
12162
|
if (others.includes(sharedNetwork)) await retireSharedNetwork();
|
|
12058
12163
|
};
|
|
12059
|
-
const socketRoot = opts.socketRoot ??
|
|
12060
|
-
process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR :
|
|
12164
|
+
const socketRoot = opts.socketRoot ?? join18(
|
|
12165
|
+
process.env.XDG_RUNTIME_DIR && existsSync13(process.env.XDG_RUNTIME_DIR) ? process.env.XDG_RUNTIME_DIR : tmpdir3(),
|
|
12061
12166
|
`cabane-runners-${opts.deviceKey}`
|
|
12062
12167
|
);
|
|
12063
|
-
const socketDir = (name) =>
|
|
12168
|
+
const socketDir = (name) => join18(socketRoot, createHash4("sha256").update(name).digest("hex").slice(0, 12));
|
|
12064
12169
|
const create = async (name, attempt) => {
|
|
12065
12170
|
mkdirSync10(socketRoot, { recursive: true, mode: 448 });
|
|
12066
12171
|
mkdirSync10(socketDir(name), { recursive: true, mode: 457 });
|
|
@@ -12202,7 +12307,7 @@ function createDockerProvider(opts) {
|
|
|
12202
12307
|
}
|
|
12203
12308
|
};
|
|
12204
12309
|
const removeIn = (name, from, names) => names.length ? execIn(name, ["sh", "-c", 'cd "$1" && shift && rm -f -- "$@"', "sh", from, ...names]) : Promise.resolve(null);
|
|
12205
|
-
const pendingPath = (name) =>
|
|
12310
|
+
const pendingPath = (name) => join18(opts.deviceDir, "runners-pending", `${name}.json`);
|
|
12206
12311
|
const readPending = (name) => {
|
|
12207
12312
|
try {
|
|
12208
12313
|
const ids = JSON.parse(readFileSync9(pendingPath(name), "utf8"));
|
|
@@ -12216,12 +12321,12 @@ function createDockerProvider(opts) {
|
|
|
12216
12321
|
rmSync9(pendingPath(name), { force: true });
|
|
12217
12322
|
return;
|
|
12218
12323
|
}
|
|
12219
|
-
mkdirSync10(
|
|
12324
|
+
mkdirSync10(join18(opts.deviceDir, "runners-pending"), { recursive: true });
|
|
12220
12325
|
writeFileSync6(pendingPath(name), JSON.stringify([...new Set(ids)]));
|
|
12221
12326
|
};
|
|
12222
12327
|
const keepSession = async (name, attempt, attemptId) => {
|
|
12223
12328
|
const archive = sessionArchivePath(opts.deviceDir, attemptId);
|
|
12224
|
-
mkdirSync10(
|
|
12329
|
+
mkdirSync10(join18(archive, ".."), { recursive: true });
|
|
12225
12330
|
const partial = `${archive}.${process.pid}.partial`;
|
|
12226
12331
|
const file = new CappedFile(partial, sessionCap);
|
|
12227
12332
|
const r = await execIn(
|
|
@@ -12238,7 +12343,7 @@ function createDockerProvider(opts) {
|
|
|
12238
12343
|
);
|
|
12239
12344
|
if (r.code === 0 && !file.over) {
|
|
12240
12345
|
renameSync5(partial, archive);
|
|
12241
|
-
writeFileSync6(
|
|
12346
|
+
writeFileSync6(join18(archive, "..", "pair"), `${attempt.conversationId}/${attempt.agentId}`);
|
|
12242
12347
|
pruneOtherAttempts(opts.deviceDir, attempt, attemptId);
|
|
12243
12348
|
await writeMarker(name, attemptId);
|
|
12244
12349
|
log.debug({ container: name, attemptId, bytes: file.bytes }, "runners: session files kept");
|
|
@@ -12252,7 +12357,7 @@ function createDockerProvider(opts) {
|
|
|
12252
12357
|
file.over ? "runners: the session files are over the size cap and weren't kept; the next new container starts the session over" : "runners: couldn't keep the session files; the next new container starts the session over"
|
|
12253
12358
|
);
|
|
12254
12359
|
};
|
|
12255
|
-
const stagingDir = (name) =>
|
|
12360
|
+
const stagingDir = (name) => join18(opts.deviceDir, "runners-pending", `${name}.outbox`);
|
|
12256
12361
|
const ensureRunning = async (name) => {
|
|
12257
12362
|
const seen = await docker.run(["inspect", "-f", "{{.State.Running}}", name]);
|
|
12258
12363
|
if (seen.code !== 0) return false;
|
|
@@ -12260,7 +12365,7 @@ function createDockerProvider(opts) {
|
|
|
12260
12365
|
return (await docker.run(["start", name])).code === 0;
|
|
12261
12366
|
};
|
|
12262
12367
|
const dropArchive = (attemptId) => {
|
|
12263
|
-
rmSync9(
|
|
12368
|
+
rmSync9(join18(sessionArchivePath(opts.deviceDir, attemptId), ".."), {
|
|
12264
12369
|
recursive: true,
|
|
12265
12370
|
force: true
|
|
12266
12371
|
});
|
|
@@ -12329,7 +12434,7 @@ function createDockerProvider(opts) {
|
|
|
12329
12434
|
if (seen.stdout.trim().split(" ")[0] !== "true")
|
|
12330
12435
|
await must(["start", name], "starting the container");
|
|
12331
12436
|
const git = await serveGitCredentials({
|
|
12332
|
-
socketPath:
|
|
12437
|
+
socketPath: join18(socketDir(name), "git.sock"),
|
|
12333
12438
|
hosts: opts.gitHosts,
|
|
12334
12439
|
fill: opts.gitFill,
|
|
12335
12440
|
log
|
|
@@ -12478,15 +12583,15 @@ function createDockerProvider(opts) {
|
|
|
12478
12583
|
const transcripts = await readDir(name, `${CONTAINER_STATE}/transcripts`);
|
|
12479
12584
|
if (transcripts) {
|
|
12480
12585
|
const kept = transcripts.files.filter((f) => SAFE_NAME_RE.test(f.name));
|
|
12481
|
-
if (kept.length) mkdirSync10(
|
|
12482
|
-
for (const f of kept) writeFileSync6(
|
|
12586
|
+
if (kept.length) mkdirSync10(join18(opts.hostStateDir, "transcripts"), { recursive: true });
|
|
12587
|
+
for (const f of kept) writeFileSync6(join18(opts.hostStateDir, "transcripts", f.name), f.data);
|
|
12483
12588
|
await removeIn(
|
|
12484
12589
|
name,
|
|
12485
12590
|
`${CONTAINER_STATE}/transcripts`,
|
|
12486
12591
|
kept.map((f) => f.name)
|
|
12487
12592
|
);
|
|
12488
12593
|
}
|
|
12489
|
-
const outboxRel =
|
|
12594
|
+
const outboxRel = join18("outbox", encodeURIComponent(attempt.agentId));
|
|
12490
12595
|
const outbox = await readDir(name, `${CONTAINER_STATE}/${outboxRel}`);
|
|
12491
12596
|
let unpublished = false;
|
|
12492
12597
|
if (outbox) {
|
|
@@ -12512,7 +12617,7 @@ function createDockerProvider(opts) {
|
|
|
12512
12617
|
const staged = stagingDir(name);
|
|
12513
12618
|
mkdirSync10(staged, { recursive: true });
|
|
12514
12619
|
writeFileSync6(
|
|
12515
|
-
|
|
12620
|
+
join18(staged, `${encodeURIComponent(entry.turnId)}__${entry.seq}.json`),
|
|
12516
12621
|
`${JSON.stringify(entry)}
|
|
12517
12622
|
`
|
|
12518
12623
|
);
|
|
@@ -12525,7 +12630,7 @@ function createDockerProvider(opts) {
|
|
|
12525
12630
|
if (outbox.complete) {
|
|
12526
12631
|
const published = publishStaged(
|
|
12527
12632
|
stagingDir(name),
|
|
12528
|
-
|
|
12633
|
+
join18(opts.hostStateDir, outboxRel),
|
|
12529
12634
|
opts.moveFile
|
|
12530
12635
|
);
|
|
12531
12636
|
queued = published.published;
|
|
@@ -12740,8 +12845,8 @@ import {
|
|
|
12740
12845
|
} from "fs";
|
|
12741
12846
|
import { createRequire as createRequire2 } from "module";
|
|
12742
12847
|
import { isIP } from "net";
|
|
12743
|
-
import { homedir as homedir3, networkInterfaces, tmpdir as
|
|
12744
|
-
import { dirname as dirname8, join as
|
|
12848
|
+
import { homedir as homedir3, networkInterfaces, tmpdir as tmpdir4, userInfo as userInfo2 } from "os";
|
|
12849
|
+
import { dirname as dirname8, join as join19, sep as sep2 } from "path";
|
|
12745
12850
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12746
12851
|
import { SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
12747
12852
|
function accountHome() {
|
|
@@ -12951,7 +13056,7 @@ import {
|
|
|
12951
13056
|
rmSync as rmSync11,
|
|
12952
13057
|
writeFileSync as writeFileSync8
|
|
12953
13058
|
} from "fs";
|
|
12954
|
-
import { join as
|
|
13059
|
+
import { join as join20 } from "path";
|
|
12955
13060
|
var MAX_ENTRIES = 2e3;
|
|
12956
13061
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
12957
13062
|
var Outbox = class {
|
|
@@ -12964,10 +13069,10 @@ var Outbox = class {
|
|
|
12964
13069
|
// Resolved lazily (per call, like cursor.ts) so tests that swap HOME between
|
|
12965
13070
|
// cases route writes at the right tmpdir.
|
|
12966
13071
|
dir() {
|
|
12967
|
-
return
|
|
13072
|
+
return join20(runnerStateDir(), "outbox", encodeURIComponent(this.workspaceId));
|
|
12968
13073
|
}
|
|
12969
13074
|
fileFor(turnId, seq) {
|
|
12970
|
-
return
|
|
13075
|
+
return join20(this.dir(), `${encodeURIComponent(turnId)}__${seq}.json`);
|
|
12971
13076
|
}
|
|
12972
13077
|
// Persist a commit for later draining. Atomic (temp file + rename) so a
|
|
12973
13078
|
// concurrent `list()` never reads a half-written entry, then enforces the
|
|
@@ -13009,7 +13114,7 @@ var Outbox = class {
|
|
|
13009
13114
|
const entries = [];
|
|
13010
13115
|
for (const name of names) {
|
|
13011
13116
|
if (!name.endsWith(".json")) continue;
|
|
13012
|
-
const full =
|
|
13117
|
+
const full = join20(dir2, name);
|
|
13013
13118
|
try {
|
|
13014
13119
|
const parsed = JSON.parse(readFileSync11(full, "utf8"));
|
|
13015
13120
|
if (parsed && typeof parsed.turnId === "string" && typeof parsed.seq === "number" && typeof parsed.path === "string") {
|
|
@@ -14650,8 +14755,8 @@ function errorCode(err) {
|
|
|
14650
14755
|
function isRecoverableSocketError(err) {
|
|
14651
14756
|
const code = errorCode(err);
|
|
14652
14757
|
if (code && RECOVERABLE_CODES.has(code)) return true;
|
|
14653
|
-
const
|
|
14654
|
-
return /\bEPIPE\b|\bECONNRESET\b/.test(
|
|
14758
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
14759
|
+
return /\bEPIPE\b|\bECONNRESET\b/.test(message2);
|
|
14655
14760
|
}
|
|
14656
14761
|
function installProcessSafetyNet(log, opts = {}) {
|
|
14657
14762
|
const proc = opts.proc ?? process;
|
|
@@ -14666,26 +14771,26 @@ function installProcessSafetyNet(log, opts = {}) {
|
|
|
14666
14771
|
);
|
|
14667
14772
|
}
|
|
14668
14773
|
function handleUncaught(log, err, origin) {
|
|
14669
|
-
const
|
|
14774
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
14670
14775
|
const code = errorCode(err);
|
|
14671
14776
|
if (isRecoverableSocketError(err)) {
|
|
14672
14777
|
log.debug(
|
|
14673
|
-
{ origin, code, err:
|
|
14778
|
+
{ origin, code, err: message2 },
|
|
14674
14779
|
"companion: recovered from a socket error (kept running \u2014 a broken pipe never kills the companion)"
|
|
14675
14780
|
);
|
|
14676
14781
|
return;
|
|
14677
14782
|
}
|
|
14678
14783
|
log.error(
|
|
14679
|
-
{ origin, code, err:
|
|
14784
|
+
{ origin, code, err: message2, stack: err instanceof Error ? err.stack : void 0 },
|
|
14680
14785
|
"An unexpected error occurred; the companion is still running."
|
|
14681
14786
|
);
|
|
14682
14787
|
}
|
|
14683
14788
|
|
|
14684
14789
|
// src/crash-marker.ts
|
|
14685
14790
|
import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync12, rmSync as rmSync12, writeFileSync as writeFileSync9 } from "fs";
|
|
14686
|
-
import { join as
|
|
14791
|
+
import { join as join21 } from "path";
|
|
14687
14792
|
function crashMarkerPath() {
|
|
14688
|
-
return
|
|
14793
|
+
return join21(cabaneDir(), "last-error.json");
|
|
14689
14794
|
}
|
|
14690
14795
|
function recordCrash(rec) {
|
|
14691
14796
|
try {
|