@linzumi/cli 0.0.84-beta → 0.0.86-beta
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 +1 -1
- package/dist/index.js +1344 -491
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,70 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
mod
|
|
40
40
|
));
|
|
41
41
|
|
|
42
|
+
// src/onboardingPlaceResponsiveness.ts
|
|
43
|
+
import { Worker } from "node:worker_threads";
|
|
44
|
+
function isPlaceResponsive(path2, deps = {}) {
|
|
45
|
+
const timeoutMs = deps.timeoutMs ?? defaultPlaceProbeTimeoutMs;
|
|
46
|
+
if (deps.probe !== void 0) {
|
|
47
|
+
return deps.probe(path2, timeoutMs);
|
|
48
|
+
}
|
|
49
|
+
return statProbe(path2, timeoutMs);
|
|
50
|
+
}
|
|
51
|
+
function statProbe(path2, timeoutMs) {
|
|
52
|
+
const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
|
|
53
|
+
const status = new Int32Array(sharedBuffer);
|
|
54
|
+
Atomics.store(status, 0, probeStatusPending);
|
|
55
|
+
let worker;
|
|
56
|
+
try {
|
|
57
|
+
worker = new Worker(probeWorkerSource, {
|
|
58
|
+
eval: true,
|
|
59
|
+
workerData: { sharedBuffer, path: path2 }
|
|
60
|
+
});
|
|
61
|
+
} catch {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
worker.unref();
|
|
65
|
+
worker.on("error", () => {
|
|
66
|
+
if (Atomics.load(status, 0) === probeStatusPending) {
|
|
67
|
+
Atomics.store(status, 0, probeStatusUnresponsive);
|
|
68
|
+
Atomics.notify(status, 0);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
Atomics.wait(status, 0, probeStatusPending, timeoutMs);
|
|
72
|
+
const result = Atomics.load(status, 0);
|
|
73
|
+
void worker.terminate().catch(() => {
|
|
74
|
+
});
|
|
75
|
+
return result === probeStatusResponsive;
|
|
76
|
+
}
|
|
77
|
+
var defaultPlaceProbeTimeoutMs, probeStatusPending, probeStatusResponsive, probeStatusUnresponsive, probeWorkerSource;
|
|
78
|
+
var init_onboardingPlaceResponsiveness = __esm({
|
|
79
|
+
"src/onboardingPlaceResponsiveness.ts"() {
|
|
80
|
+
"use strict";
|
|
81
|
+
defaultPlaceProbeTimeoutMs = 1e3;
|
|
82
|
+
probeStatusPending = 0;
|
|
83
|
+
probeStatusResponsive = 1;
|
|
84
|
+
probeStatusUnresponsive = 2;
|
|
85
|
+
probeWorkerSource = `
|
|
86
|
+
const { workerData, parentPort } = require('node:worker_threads');
|
|
87
|
+
const { statSync } = require('node:fs');
|
|
88
|
+
const status = new Int32Array(workerData.sharedBuffer);
|
|
89
|
+
let result = ${probeStatusUnresponsive};
|
|
90
|
+
try {
|
|
91
|
+
statSync(workerData.path);
|
|
92
|
+
result = ${probeStatusResponsive};
|
|
93
|
+
} catch {
|
|
94
|
+
// A path that throws quickly (ENOENT/EACCES/ENOTDIR) is still a *responsive*
|
|
95
|
+
// volume - discovery's own existence checks will handle it. Only a wedged
|
|
96
|
+
// syscall (which never returns here) counts as unresponsive.
|
|
97
|
+
result = ${probeStatusResponsive};
|
|
98
|
+
}
|
|
99
|
+
Atomics.store(status, 0, result);
|
|
100
|
+
Atomics.notify(status, 0);
|
|
101
|
+
if (parentPort) parentPort.postMessage(result);
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
42
106
|
// src/onboardingProjectDiscovery.ts
|
|
43
107
|
var onboardingProjectDiscovery_exports = {};
|
|
44
108
|
__export(onboardingProjectDiscovery_exports, {
|
|
@@ -50,10 +114,12 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
50
114
|
import { basename, dirname, join } from "node:path";
|
|
51
115
|
function discoverCurrentGitProject(args) {
|
|
52
116
|
const startedAtMs = args.nowMs ?? Date.now();
|
|
117
|
+
const responsiveness = args.placeResponsiveness;
|
|
53
118
|
const candidates = /* @__PURE__ */ new Map();
|
|
54
119
|
const searchStats = [];
|
|
120
|
+
const cwdResponsive = isPlaceResponsive(args.cwd, responsiveness);
|
|
55
121
|
const gitStartedAtMs = Date.now();
|
|
56
|
-
const topLevel = gitOutput(args.cwd, ["rev-parse", "--show-toplevel"]);
|
|
122
|
+
const topLevel = cwdResponsive ? gitOutput(args.cwd, ["rev-parse", "--show-toplevel"]) : void 0;
|
|
57
123
|
const gitDurationMs = Date.now() - gitStartedAtMs;
|
|
58
124
|
if (topLevel !== void 0) {
|
|
59
125
|
mergeCandidate(
|
|
@@ -65,12 +131,28 @@ function discoverCurrentGitProject(args) {
|
|
|
65
131
|
place: args.cwd,
|
|
66
132
|
source: "git",
|
|
67
133
|
duration_ms: gitDurationMs,
|
|
68
|
-
result_count: topLevel === void 0 ? 0 : 1
|
|
134
|
+
result_count: topLevel === void 0 ? 0 : 1,
|
|
135
|
+
skipped_unresponsive: cwdResponsive ? void 0 : true
|
|
69
136
|
});
|
|
70
137
|
const sourceRoots = args.sourceRoots ?? defaultLocalProjectSourceRoots(args.cwd);
|
|
71
138
|
let placesSearched = 1;
|
|
72
139
|
for (const sourceRoot of sourceRoots) {
|
|
73
140
|
const sourceRootStartedAtMs = Date.now();
|
|
141
|
+
const rootResponsive = isPlaceResponsive(sourceRoot, responsiveness);
|
|
142
|
+
if (!rootResponsive) {
|
|
143
|
+
searchStats.push({
|
|
144
|
+
place: sourceRoot,
|
|
145
|
+
source: "git",
|
|
146
|
+
duration_ms: Date.now() - sourceRootStartedAtMs,
|
|
147
|
+
result_count: 0,
|
|
148
|
+
skipped_unresponsive: true
|
|
149
|
+
});
|
|
150
|
+
placesSearched += 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!isDirectory(sourceRoot)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
74
156
|
const foundInRoot = discoverGitWorktreesInSourceRoot(sourceRoot);
|
|
75
157
|
for (const worktreePath of foundInRoot) {
|
|
76
158
|
mergeCandidate(
|
|
@@ -218,7 +300,7 @@ function defaultLocalProjectSourceRoots(cwd) {
|
|
|
218
300
|
...ancestorSourceRoots(cwd),
|
|
219
301
|
...homeSourceRoots(),
|
|
220
302
|
...externalVolumeSourceRoots()
|
|
221
|
-
])
|
|
303
|
+
]);
|
|
222
304
|
}
|
|
223
305
|
function ancestorSourceRoots(cwd) {
|
|
224
306
|
const sourceRootNames = /* @__PURE__ */ new Set([
|
|
@@ -331,6 +413,7 @@ var worktreePathSampleLimit, gitSpawnTimeoutMs;
|
|
|
331
413
|
var init_onboardingProjectDiscovery = __esm({
|
|
332
414
|
"src/onboardingProjectDiscovery.ts"() {
|
|
333
415
|
"use strict";
|
|
416
|
+
init_onboardingPlaceResponsiveness();
|
|
334
417
|
worktreePathSampleLimit = 25;
|
|
335
418
|
gitSpawnTimeoutMs = 4e3;
|
|
336
419
|
}
|
|
@@ -6977,6 +7060,7 @@ function startPortForwardWatcher(options) {
|
|
|
6977
7060
|
const scanProcessCwds = options.scanProcessCwds ?? readProcessCwdRows;
|
|
6978
7061
|
const nowMs = options.nowMs ?? Date.now;
|
|
6979
7062
|
const commanderBoundPids = validPidSet(options.commanderBoundPids ?? []);
|
|
7063
|
+
const commanderBoundPorts = validPortSet(options.commanderBoundPorts ?? []);
|
|
6980
7064
|
const candidateStabilityByPort = /* @__PURE__ */ new Map();
|
|
6981
7065
|
const emittedByPort = /* @__PURE__ */ new Map();
|
|
6982
7066
|
const missingByPort = /* @__PURE__ */ new Map();
|
|
@@ -6990,12 +7074,15 @@ function startPortForwardWatcher(options) {
|
|
|
6990
7074
|
const descendants = descendantPidSet(scanProcesses(), rootPid);
|
|
6991
7075
|
const sockets = scanListenSockets();
|
|
6992
7076
|
const observedPids = /* @__PURE__ */ new Set([...descendants, ...commanderBoundPids]);
|
|
6993
|
-
const candidatePids = sockets.filter(
|
|
7077
|
+
const candidatePids = sockets.filter(
|
|
7078
|
+
(socket) => observedPids.has(socket.pid) || commanderBoundPorts.has(socket.port)
|
|
7079
|
+
).map((socket) => socket.pid);
|
|
6994
7080
|
const candidates = detectedForwardCandidates(
|
|
6995
7081
|
sockets,
|
|
6996
7082
|
observedPids,
|
|
6997
7083
|
scanProcessCwds(candidatePids),
|
|
6998
|
-
commanderBoundPids
|
|
7084
|
+
commanderBoundPids,
|
|
7085
|
+
commanderBoundPorts
|
|
6999
7086
|
);
|
|
7000
7087
|
const scanTimeMs = nowMs();
|
|
7001
7088
|
const stable = stableForwardCandidates(
|
|
@@ -7113,10 +7200,12 @@ function descendantPidSet(rows, rootPid) {
|
|
|
7113
7200
|
}
|
|
7114
7201
|
return descendants;
|
|
7115
7202
|
}
|
|
7116
|
-
function detectedForwardCandidates(sockets, descendantPids, processCwds = /* @__PURE__ */ new Map(), commanderBoundPids = /* @__PURE__ */ new Set()) {
|
|
7117
|
-
return sockets.filter(
|
|
7203
|
+
function detectedForwardCandidates(sockets, descendantPids, processCwds = /* @__PURE__ */ new Map(), commanderBoundPids = /* @__PURE__ */ new Set(), commanderBoundPorts = /* @__PURE__ */ new Set()) {
|
|
7204
|
+
return sockets.filter(
|
|
7205
|
+
(socket) => descendantPids.has(socket.pid) || commanderBoundPorts.has(socket.port)
|
|
7206
|
+
).filter((socket) => socket.port > 0 && socket.port < 65536).sort((left, right) => left.port - right.port).map((socket) => {
|
|
7118
7207
|
const cwd = processCwds.get(socket.pid);
|
|
7119
|
-
const portKind = commanderBoundPids.size === 0 ? void 0 : commanderBoundPids.has(socket.pid) ? "commander_bound" : "descendant";
|
|
7208
|
+
const portKind = commanderBoundPids.size === 0 && commanderBoundPorts.size === 0 ? void 0 : commanderBoundPids.has(socket.pid) || commanderBoundPorts.has(socket.port) ? "commander_bound" : "descendant";
|
|
7120
7209
|
return {
|
|
7121
7210
|
port: socket.port,
|
|
7122
7211
|
pid: socket.pid,
|
|
@@ -7129,6 +7218,11 @@ function detectedForwardCandidates(sockets, descendantPids, processCwds = /* @__
|
|
|
7129
7218
|
function validPidSet(pids) {
|
|
7130
7219
|
return new Set(pids.filter((pid) => Number.isInteger(pid) && pid > 0));
|
|
7131
7220
|
}
|
|
7221
|
+
function validPortSet(ports) {
|
|
7222
|
+
return new Set(
|
|
7223
|
+
ports.filter((port) => Number.isInteger(port) && port > 0 && port < 65536)
|
|
7224
|
+
);
|
|
7225
|
+
}
|
|
7132
7226
|
function normalizedPortKind(portKind) {
|
|
7133
7227
|
return portKind ?? "descendant";
|
|
7134
7228
|
}
|
|
@@ -9822,7 +9916,11 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
|
|
|
9822
9916
|
seq: event.seq,
|
|
9823
9917
|
actor_slug: event.actorSlug ?? null,
|
|
9824
9918
|
actor_user_id: event.actorUserId ?? null,
|
|
9825
|
-
reason: "different_thread"
|
|
9919
|
+
reason: "different_thread",
|
|
9920
|
+
thread_id: event.threadId ?? null,
|
|
9921
|
+
bound_thread_id: state.kandanThreadId ?? null,
|
|
9922
|
+
codex_thread_id: state.codexThreadId ?? null,
|
|
9923
|
+
body_preview: runnerConsoleBodyPreview(event.body)
|
|
9826
9924
|
});
|
|
9827
9925
|
return;
|
|
9828
9926
|
}
|
|
@@ -11960,14 +12058,336 @@ var init_claudeCodePlanMirror = __esm({
|
|
|
11960
12058
|
}
|
|
11961
12059
|
});
|
|
11962
12060
|
|
|
12061
|
+
// src/runnerLogger.ts
|
|
12062
|
+
import { appendFileSync, openSync as openSync2 } from "node:fs";
|
|
12063
|
+
import { createWriteStream } from "node:fs";
|
|
12064
|
+
import { homedir as homedir5 } from "node:os";
|
|
12065
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
12066
|
+
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
12067
|
+
function createRunnerLogger(logFile, consoleReporter) {
|
|
12068
|
+
mkdirSync2(dirname3(logFile), { recursive: true });
|
|
12069
|
+
const fd = openSync2(logFile, "a");
|
|
12070
|
+
const stream = createWriteStream("", { fd, flags: "a", autoClose: true });
|
|
12071
|
+
const logger = ((event, payload) => {
|
|
12072
|
+
const redacted = redactForCliLog(payload);
|
|
12073
|
+
stream.write(
|
|
12074
|
+
`${JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event, ...redacted })}
|
|
12075
|
+
`,
|
|
12076
|
+
"utf8"
|
|
12077
|
+
);
|
|
12078
|
+
consoleReporter?.(event, runnerConsolePayload(redacted, payload));
|
|
12079
|
+
});
|
|
12080
|
+
Object.defineProperty(logger, "close", {
|
|
12081
|
+
value: () => closeStream(stream)
|
|
12082
|
+
});
|
|
12083
|
+
return logger;
|
|
12084
|
+
}
|
|
12085
|
+
function writeCliAuditEvent(event, payload, options = {}) {
|
|
12086
|
+
const logFile = options.logFile ?? defaultCliAuditLogFile();
|
|
12087
|
+
try {
|
|
12088
|
+
mkdirSync2(dirname3(logFile), { recursive: true });
|
|
12089
|
+
appendFileSync(
|
|
12090
|
+
logFile,
|
|
12091
|
+
`${JSON.stringify({
|
|
12092
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12093
|
+
event,
|
|
12094
|
+
...options.sessionId === void 0 ? {} : { sessionId: options.sessionId },
|
|
12095
|
+
...redactForCliLog(payload)
|
|
12096
|
+
})}
|
|
12097
|
+
`,
|
|
12098
|
+
"utf8"
|
|
12099
|
+
);
|
|
12100
|
+
} catch (_error) {
|
|
12101
|
+
return;
|
|
12102
|
+
}
|
|
12103
|
+
}
|
|
12104
|
+
function defaultCliAuditLogFile() {
|
|
12105
|
+
const override = process.env.LINZUMI_CLI_AUDIT_LOG?.trim();
|
|
12106
|
+
return override === void 0 || override === "" ? join6(homedir5(), ".linzumi", "logs", "command-events.jsonl") : override;
|
|
12107
|
+
}
|
|
12108
|
+
function defaultRunnerLogFile() {
|
|
12109
|
+
return join6(homedir5(), ".linzumi", "logs", "linzumi-runner.log");
|
|
12110
|
+
}
|
|
12111
|
+
function redactForCliLog(value) {
|
|
12112
|
+
return redactObject(value);
|
|
12113
|
+
}
|
|
12114
|
+
function redactValue(value, key) {
|
|
12115
|
+
if (sensitiveKey(key)) {
|
|
12116
|
+
return sensitiveMarker;
|
|
12117
|
+
}
|
|
12118
|
+
if (typeof value === "string") {
|
|
12119
|
+
return redactString(value);
|
|
12120
|
+
}
|
|
12121
|
+
if (Array.isArray(value)) {
|
|
12122
|
+
return key === "args" ? redactArgs(value) : value.map((item) => redactValue(item, void 0));
|
|
12123
|
+
}
|
|
12124
|
+
if (value !== null && typeof value === "object") {
|
|
12125
|
+
return redactObject(value);
|
|
12126
|
+
}
|
|
12127
|
+
return value;
|
|
12128
|
+
}
|
|
12129
|
+
function redactObject(value) {
|
|
12130
|
+
const headerName = typeof value.name === "string" ? value.name.toLowerCase() : void 0;
|
|
12131
|
+
const shouldRedactHeaderValue = headerName === "authorization" || headerName === "cookie" || headerName === "set-cookie";
|
|
12132
|
+
return Object.fromEntries(
|
|
12133
|
+
Object.entries(value).filter(([key]) => key !== "runner_console").map(([key, entry]) => [
|
|
12134
|
+
key,
|
|
12135
|
+
shouldRedactHeaderValue && key === "value" ? sensitiveMarker : redactValue(entry, key)
|
|
12136
|
+
])
|
|
12137
|
+
);
|
|
12138
|
+
}
|
|
12139
|
+
function runnerConsolePayload(redacted, original) {
|
|
12140
|
+
const consoleFields = objectValue3(original.runner_console);
|
|
12141
|
+
return consoleFields === void 0 ? redacted : { ...redacted, ...redactForCliLog(consoleFields) };
|
|
12142
|
+
}
|
|
12143
|
+
function objectValue3(value) {
|
|
12144
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
12145
|
+
}
|
|
12146
|
+
function redactArgs(args) {
|
|
12147
|
+
let redactNext = false;
|
|
12148
|
+
return args.map((arg) => {
|
|
12149
|
+
if (typeof arg !== "string") {
|
|
12150
|
+
redactNext = false;
|
|
12151
|
+
return redactValue(arg, void 0);
|
|
12152
|
+
}
|
|
12153
|
+
if (redactNext) {
|
|
12154
|
+
redactNext = false;
|
|
12155
|
+
return sensitiveMarker;
|
|
12156
|
+
}
|
|
12157
|
+
const [flag, value] = splitArgAssignment(arg);
|
|
12158
|
+
if (sensitiveArgFlags.has(flag)) {
|
|
12159
|
+
if (value === void 0) {
|
|
12160
|
+
redactNext = true;
|
|
12161
|
+
return arg;
|
|
12162
|
+
}
|
|
12163
|
+
return `${flag}=${sensitiveMarker}`;
|
|
12164
|
+
}
|
|
12165
|
+
return redactString(arg);
|
|
12166
|
+
});
|
|
12167
|
+
}
|
|
12168
|
+
function splitArgAssignment(value) {
|
|
12169
|
+
const index = value.indexOf("=");
|
|
12170
|
+
return index === -1 ? [value, void 0] : [value.slice(0, index), value.slice(index + 1)];
|
|
12171
|
+
}
|
|
12172
|
+
function sensitiveKey(key) {
|
|
12173
|
+
if (key === void 0) {
|
|
12174
|
+
return false;
|
|
12175
|
+
}
|
|
12176
|
+
return /^(authorization|cookie|set-cookie|password)$/i.test(key) || /(^|[_-])(access[_-]?token|api[_-]?key|auth[_-]?token|oauth[_-]?code|refresh[_-]?token|secret|token)$/i.test(
|
|
12177
|
+
key
|
|
12178
|
+
);
|
|
12179
|
+
}
|
|
12180
|
+
function redactString(value) {
|
|
12181
|
+
const withRedactedQuery = redactUrlQuery(value);
|
|
12182
|
+
return withRedactedQuery.replace(
|
|
12183
|
+
/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
12184
|
+
sensitiveMarker
|
|
12185
|
+
);
|
|
12186
|
+
}
|
|
12187
|
+
function redactUrlQuery(value) {
|
|
12188
|
+
let parsed;
|
|
12189
|
+
try {
|
|
12190
|
+
parsed = new URL(value);
|
|
12191
|
+
} catch (_error) {
|
|
12192
|
+
return redactRelativeUrlQuery(value);
|
|
12193
|
+
}
|
|
12194
|
+
for (const name of [...parsed.searchParams.keys()]) {
|
|
12195
|
+
if (sensitiveQueryParams.has(name.toLowerCase())) {
|
|
12196
|
+
parsed.searchParams.set(name, sensitiveMarker);
|
|
12197
|
+
}
|
|
12198
|
+
}
|
|
12199
|
+
return parsed.toString();
|
|
12200
|
+
}
|
|
12201
|
+
function redactRelativeUrlQuery(value) {
|
|
12202
|
+
const queryStart = value.indexOf("?");
|
|
12203
|
+
const hashStart = value.indexOf("#");
|
|
12204
|
+
if (queryStart === -1 || hashStart !== -1 && hashStart < queryStart) {
|
|
12205
|
+
return value;
|
|
12206
|
+
}
|
|
12207
|
+
const path2 = value.slice(0, queryStart);
|
|
12208
|
+
const query = hashStart === -1 ? value.slice(queryStart + 1) : value.slice(queryStart + 1, hashStart);
|
|
12209
|
+
const hash = hashStart === -1 ? "" : value.slice(hashStart);
|
|
12210
|
+
const searchParams = new URLSearchParams(query);
|
|
12211
|
+
let redacted = false;
|
|
12212
|
+
for (const name of [...searchParams.keys()]) {
|
|
12213
|
+
if (sensitiveQueryParams.has(name.toLowerCase())) {
|
|
12214
|
+
searchParams.set(name, sensitiveMarker);
|
|
12215
|
+
redacted = true;
|
|
12216
|
+
}
|
|
12217
|
+
}
|
|
12218
|
+
switch (redacted) {
|
|
12219
|
+
case true:
|
|
12220
|
+
return `${path2}?${searchParams.toString()}${hash}`;
|
|
12221
|
+
case false:
|
|
12222
|
+
return value;
|
|
12223
|
+
}
|
|
12224
|
+
}
|
|
12225
|
+
function closeStream(stream) {
|
|
12226
|
+
if (stream.closed || stream.destroyed) {
|
|
12227
|
+
return Promise.resolve();
|
|
12228
|
+
}
|
|
12229
|
+
return new Promise((resolve12, reject) => {
|
|
12230
|
+
stream.once("error", reject);
|
|
12231
|
+
stream.end(resolve12);
|
|
12232
|
+
});
|
|
12233
|
+
}
|
|
12234
|
+
var sensitiveMarker, sensitiveQueryParams, sensitiveArgFlags;
|
|
12235
|
+
var init_runnerLogger = __esm({
|
|
12236
|
+
"src/runnerLogger.ts"() {
|
|
12237
|
+
"use strict";
|
|
12238
|
+
sensitiveMarker = "<SENSITIVE_DATA>";
|
|
12239
|
+
sensitiveQueryParams = /* @__PURE__ */ new Set([
|
|
12240
|
+
"access_token",
|
|
12241
|
+
"authorization",
|
|
12242
|
+
"code",
|
|
12243
|
+
"cookie",
|
|
12244
|
+
"kandan_preview_ticket",
|
|
12245
|
+
"refresh_token",
|
|
12246
|
+
"token"
|
|
12247
|
+
]);
|
|
12248
|
+
sensitiveArgFlags = /* @__PURE__ */ new Set([
|
|
12249
|
+
"--access-token",
|
|
12250
|
+
"--api-key",
|
|
12251
|
+
"--authorization",
|
|
12252
|
+
"--cookie",
|
|
12253
|
+
"--oauth-code",
|
|
12254
|
+
"--password",
|
|
12255
|
+
"--refresh-token",
|
|
12256
|
+
"--secret",
|
|
12257
|
+
"--token"
|
|
12258
|
+
]);
|
|
12259
|
+
}
|
|
12260
|
+
});
|
|
12261
|
+
|
|
12262
|
+
// src/engineChildReaper.ts
|
|
12263
|
+
function registerEngineChild(registration, _killProcess = process.kill) {
|
|
12264
|
+
reaperState.children.add(registration);
|
|
12265
|
+
ensureExitHandlersInstalled();
|
|
12266
|
+
return {
|
|
12267
|
+
unregister: () => {
|
|
12268
|
+
reaperState.children.delete(registration);
|
|
12269
|
+
}
|
|
12270
|
+
};
|
|
12271
|
+
}
|
|
12272
|
+
function reapAllEngineChildren(killProcess = process.kill, reason = "exit") {
|
|
12273
|
+
const children = [...reaperState.children];
|
|
12274
|
+
reaperState.children.clear();
|
|
12275
|
+
for (const child of children) {
|
|
12276
|
+
reapEngineChild(child, killProcess, reason);
|
|
12277
|
+
}
|
|
12278
|
+
}
|
|
12279
|
+
function reapEngineChild(child, killProcess, reason) {
|
|
12280
|
+
try {
|
|
12281
|
+
child.stop();
|
|
12282
|
+
} catch (error) {
|
|
12283
|
+
auditReapFailure(child, reason, "stop", error);
|
|
12284
|
+
}
|
|
12285
|
+
if (child.pid === void 0) {
|
|
12286
|
+
return;
|
|
12287
|
+
}
|
|
12288
|
+
if (child.ownProcessGroup) {
|
|
12289
|
+
try {
|
|
12290
|
+
killProcess(-child.pid, reapSignal);
|
|
12291
|
+
return;
|
|
12292
|
+
} catch (error) {
|
|
12293
|
+
if (processSignalErrorCode(error) === "ESRCH") {
|
|
12294
|
+
return;
|
|
12295
|
+
}
|
|
12296
|
+
auditReapFailure(child, reason, "group_kill", error);
|
|
12297
|
+
}
|
|
12298
|
+
}
|
|
12299
|
+
try {
|
|
12300
|
+
killProcess(child.pid, reapSignal);
|
|
12301
|
+
} catch (error) {
|
|
12302
|
+
if (processSignalErrorCode(error) === "ESRCH") {
|
|
12303
|
+
return;
|
|
12304
|
+
}
|
|
12305
|
+
auditReapFailure(child, reason, "pid_kill", error);
|
|
12306
|
+
}
|
|
12307
|
+
}
|
|
12308
|
+
function ensureExitHandlersInstalled() {
|
|
12309
|
+
if (reaperState.handlersInstalled) {
|
|
12310
|
+
return;
|
|
12311
|
+
}
|
|
12312
|
+
reaperState.handlersInstalled = true;
|
|
12313
|
+
const onExit2 = () => {
|
|
12314
|
+
reapAllEngineChildren(process.kill, "process_exit");
|
|
12315
|
+
};
|
|
12316
|
+
process.on("exit", onExit2);
|
|
12317
|
+
const ownsUncaught = process.listenerCount("uncaughtException") === 0;
|
|
12318
|
+
const ownsRejection = process.listenerCount("unhandledRejection") === 0;
|
|
12319
|
+
const onUncaughtException = (error) => {
|
|
12320
|
+
reapAllEngineChildren(process.kill, "uncaught_exception");
|
|
12321
|
+
process.stderr.write(
|
|
12322
|
+
`linzumi runner uncaughtException: ${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
12323
|
+
`
|
|
12324
|
+
);
|
|
12325
|
+
process.exit(1);
|
|
12326
|
+
};
|
|
12327
|
+
const onUnhandledRejection = (reason) => {
|
|
12328
|
+
reapAllEngineChildren(process.kill, "unhandled_rejection");
|
|
12329
|
+
process.stderr.write(
|
|
12330
|
+
`linzumi runner unhandledRejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
|
|
12331
|
+
`
|
|
12332
|
+
);
|
|
12333
|
+
process.exit(1);
|
|
12334
|
+
};
|
|
12335
|
+
if (ownsUncaught) {
|
|
12336
|
+
process.on("uncaughtException", onUncaughtException);
|
|
12337
|
+
}
|
|
12338
|
+
if (ownsRejection) {
|
|
12339
|
+
process.on("unhandledRejection", onUnhandledRejection);
|
|
12340
|
+
}
|
|
12341
|
+
reaperState.removeHandlers = () => {
|
|
12342
|
+
process.off("exit", onExit2);
|
|
12343
|
+
if (ownsUncaught) {
|
|
12344
|
+
process.off("uncaughtException", onUncaughtException);
|
|
12345
|
+
}
|
|
12346
|
+
if (ownsRejection) {
|
|
12347
|
+
process.off("unhandledRejection", onUnhandledRejection);
|
|
12348
|
+
}
|
|
12349
|
+
};
|
|
12350
|
+
}
|
|
12351
|
+
function auditReapFailure(child, reason, stage, error) {
|
|
12352
|
+
writeCliAuditEvent("process.reap_failed", {
|
|
12353
|
+
purpose: child.kind,
|
|
12354
|
+
pid: child.pid ?? null,
|
|
12355
|
+
ownProcessGroup: child.ownProcessGroup,
|
|
12356
|
+
reason,
|
|
12357
|
+
stage,
|
|
12358
|
+
code: processSignalErrorCode(error) ?? null,
|
|
12359
|
+
message: error instanceof Error ? error.message : String(error)
|
|
12360
|
+
});
|
|
12361
|
+
}
|
|
12362
|
+
function processSignalErrorCode(error) {
|
|
12363
|
+
if (error !== null && typeof error === "object" && "code" in error) {
|
|
12364
|
+
const code = error.code;
|
|
12365
|
+
return typeof code === "string" ? code : void 0;
|
|
12366
|
+
}
|
|
12367
|
+
return void 0;
|
|
12368
|
+
}
|
|
12369
|
+
var reaperState, reapSignal;
|
|
12370
|
+
var init_engineChildReaper = __esm({
|
|
12371
|
+
"src/engineChildReaper.ts"() {
|
|
12372
|
+
"use strict";
|
|
12373
|
+
init_runnerLogger();
|
|
12374
|
+
reaperState = {
|
|
12375
|
+
children: /* @__PURE__ */ new Set(),
|
|
12376
|
+
handlersInstalled: false,
|
|
12377
|
+
removeHandlers: void 0
|
|
12378
|
+
};
|
|
12379
|
+
reapSignal = "SIGKILL";
|
|
12380
|
+
}
|
|
12381
|
+
});
|
|
12382
|
+
|
|
11963
12383
|
// src/claudeCodeLiveBashOutput.ts
|
|
11964
12384
|
import {
|
|
11965
|
-
mkdirSync as
|
|
12385
|
+
mkdirSync as mkdirSync3,
|
|
11966
12386
|
rmSync,
|
|
11967
12387
|
writeFileSync,
|
|
11968
12388
|
promises as fsPromises
|
|
11969
12389
|
} from "node:fs";
|
|
11970
|
-
import { join as
|
|
12390
|
+
import { join as join7 } from "node:path";
|
|
11971
12391
|
import { StringDecoder } from "node:string_decoder";
|
|
11972
12392
|
function shellSingleQuoted(value) {
|
|
11973
12393
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
@@ -12092,12 +12512,12 @@ function createClaudeCodeLiveBashCapture(host) {
|
|
|
12092
12512
|
isClaudeLiveBashWrappedCommand(command)) {
|
|
12093
12513
|
return void 0;
|
|
12094
12514
|
}
|
|
12095
|
-
const file =
|
|
12515
|
+
const file = join7(
|
|
12096
12516
|
host.captureDir,
|
|
12097
12517
|
`${toolUseId.replaceAll(/[^\w-]/g, "_")}.out`
|
|
12098
12518
|
);
|
|
12099
12519
|
try {
|
|
12100
|
-
|
|
12520
|
+
mkdirSync3(host.captureDir, { recursive: true });
|
|
12101
12521
|
writeFileSync(file, "");
|
|
12102
12522
|
} catch (error) {
|
|
12103
12523
|
host.log?.("claude_live_bash.capture_file_failed", {
|
|
@@ -12179,8 +12599,8 @@ var init_claudeCodeLiveBashOutput = __esm({
|
|
|
12179
12599
|
|
|
12180
12600
|
// src/claudeCodeSession.ts
|
|
12181
12601
|
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
12182
|
-
import { homedir as
|
|
12183
|
-
import { join as
|
|
12602
|
+
import { homedir as homedir6 } from "node:os";
|
|
12603
|
+
import { join as join8 } from "node:path";
|
|
12184
12604
|
function claudeCodeSettingSources() {
|
|
12185
12605
|
return ["user", "project", "local"];
|
|
12186
12606
|
}
|
|
@@ -12324,7 +12744,7 @@ function claudeCodeRateLimitSummaryText(rateLimit, nowMs) {
|
|
|
12324
12744
|
async function probeClaudeCodeAvailability(args) {
|
|
12325
12745
|
if (!hasClaudeCodeAuthHint(process.env, {
|
|
12326
12746
|
cwd: args.cwd,
|
|
12327
|
-
homeDir:
|
|
12747
|
+
homeDir: homedir6(),
|
|
12328
12748
|
platform: process.platform,
|
|
12329
12749
|
fileExists: existsSync4,
|
|
12330
12750
|
readTextFile: readTextFileIfPresent
|
|
@@ -12357,7 +12777,7 @@ async function probeClaudeCodeAvailability(args) {
|
|
|
12357
12777
|
}
|
|
12358
12778
|
}
|
|
12359
12779
|
function hasClaudeCodeAuthHint(env, deps) {
|
|
12360
|
-
const configDir = env.CLAUDE_CONFIG_DIR ??
|
|
12780
|
+
const configDir = env.CLAUDE_CONFIG_DIR ?? join8(deps.homeDir, ".claude");
|
|
12361
12781
|
return hasAnthropicCredentialEnv(env) || hasClaudeCloudProviderEnv(env) || hasClaudeCodeFileCredential(configDir, deps) || hasClaudeCodeApiKeyHelper(configDir, deps) || hasMacClaudeCodeKeychainAnchor(deps);
|
|
12362
12782
|
}
|
|
12363
12783
|
function claudeCodePolicyDeferHooks() {
|
|
@@ -12392,14 +12812,14 @@ function hasClaudeCloudProviderEnv(env) {
|
|
|
12392
12812
|
return trueishEnv(env.CLAUDE_CODE_USE_BEDROCK) || trueishEnv(env.CLAUDE_CODE_USE_VERTEX) || trueishEnv(env.CLAUDE_CODE_USE_FOUNDRY) || trueishEnv(env.CLAUDE_CODE_USE_ANTHROPIC_AWS) || trueishEnv(env.CLAUDE_CODE_USE_MANTLE);
|
|
12393
12813
|
}
|
|
12394
12814
|
function hasClaudeCodeFileCredential(configDir, deps) {
|
|
12395
|
-
return deps.fileExists(
|
|
12815
|
+
return deps.fileExists(join8(configDir, ".credentials.json")) || deps.fileExists(join8(configDir, ".claude.json")) || deps.fileExists(join8(deps.homeDir, ".claude.json"));
|
|
12396
12816
|
}
|
|
12397
12817
|
function hasClaudeCodeApiKeyHelper(configDir, deps) {
|
|
12398
12818
|
return [
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12819
|
+
join8(configDir, "settings.json"),
|
|
12820
|
+
join8(configDir, "settings.local.json"),
|
|
12821
|
+
join8(deps.cwd, ".claude", "settings.json"),
|
|
12822
|
+
join8(deps.cwd, ".claude", "settings.local.json")
|
|
12403
12823
|
].some((path2) => settingsFileHasApiKeyHelper(path2, deps));
|
|
12404
12824
|
}
|
|
12405
12825
|
function settingsFileHasApiKeyHelper(path2, deps) {
|
|
@@ -12419,9 +12839,9 @@ function hasMacClaudeCodeKeychainAnchor(deps) {
|
|
|
12419
12839
|
return false;
|
|
12420
12840
|
}
|
|
12421
12841
|
return [
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12842
|
+
join8(deps.homeDir, "Library", "Application Support", "claude-cli-nodejs"),
|
|
12843
|
+
join8(deps.homeDir, "Library", "Application Support", "Claude"),
|
|
12844
|
+
join8(deps.homeDir, "Library", "Preferences", "claude-cli-nodejs")
|
|
12425
12845
|
].some((path2) => deps.fileExists(path2));
|
|
12426
12846
|
}
|
|
12427
12847
|
function readTextFileIfPresent(path2) {
|
|
@@ -12440,87 +12860,83 @@ async function startClaudeCodeSession(options) {
|
|
|
12440
12860
|
assistantTextByKey: /* @__PURE__ */ new Map(),
|
|
12441
12861
|
usage: void 0,
|
|
12442
12862
|
startedSessionIds: /* @__PURE__ */ new Set(),
|
|
12443
|
-
completedTurnCount: 0
|
|
12863
|
+
completedTurnCount: 0,
|
|
12864
|
+
lastCompletedTurnBody: void 0
|
|
12444
12865
|
};
|
|
12445
12866
|
const streamUsageTracker = createClaudeCodeStreamUsageTracker();
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
state.startedSessionIds.
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
await options.onTranscriptEvent?.(event);
|
|
12465
|
-
}
|
|
12466
|
-
const resultOutcome = extractClaudeResultOutcome(message);
|
|
12467
|
-
switch (resultOutcome.type) {
|
|
12468
|
-
case "success": {
|
|
12469
|
-
if (resultOutcome.text !== void 0) {
|
|
12470
|
-
state.resultText = resultOutcome.text;
|
|
12471
|
-
}
|
|
12472
|
-
state.usage = extractClaudeUsage(message) ?? state.usage;
|
|
12473
|
-
state.completedTurnCount += 1;
|
|
12474
|
-
const queuedTurnCountBeforeTranscript = options.streamingInput === void 0 ? void 0 : options.streamingInput.queuedTurnCount();
|
|
12475
|
-
if (options.streamingInput !== void 0) {
|
|
12476
|
-
await emitClaudeCodeTurnCompleted(options, state);
|
|
12477
|
-
}
|
|
12478
|
-
if (options.streamingInput !== void 0 && queuedTurnCountBeforeTranscript !== void 0 && state.completedTurnCount >= Math.max(
|
|
12479
|
-
queuedTurnCountBeforeTranscript,
|
|
12480
|
-
options.streamingInput.queuedTurnCount()
|
|
12481
|
-
)) {
|
|
12482
|
-
options.streamingInput.close();
|
|
12483
|
-
return completeClaudeCodeSession(options, state);
|
|
12484
|
-
}
|
|
12485
|
-
if (options.streamingInput !== void 0) {
|
|
12486
|
-
await options.onTurnCompleted?.();
|
|
12487
|
-
resetClaudeAssistantAggregate(state);
|
|
12488
|
-
state.resultText = void 0;
|
|
12867
|
+
try {
|
|
12868
|
+
for await (const message of runner(options)) {
|
|
12869
|
+
state.sessionId = state.sessionId ?? extractClaudeSessionId(message);
|
|
12870
|
+
const sessionId = extractClaudeSessionId(message) ?? state.sessionId;
|
|
12871
|
+
if (sessionId !== void 0 && !state.startedSessionIds.has(sessionId)) {
|
|
12872
|
+
state.startedSessionIds.add(sessionId);
|
|
12873
|
+
await options.onTranscriptEvent?.({
|
|
12874
|
+
type: "session_started",
|
|
12875
|
+
sessionId
|
|
12876
|
+
});
|
|
12877
|
+
}
|
|
12878
|
+
for (const event of transcriptEventsForClaudeMessage(
|
|
12879
|
+
message,
|
|
12880
|
+
sessionId,
|
|
12881
|
+
streamUsageTracker
|
|
12882
|
+
)) {
|
|
12883
|
+
if (event.type === "assistant_message") {
|
|
12884
|
+
recordClaudeAssistantAggregate(state, event);
|
|
12489
12885
|
}
|
|
12490
|
-
|
|
12886
|
+
if (event.type === "usage") {
|
|
12887
|
+
state.usage = event.usage;
|
|
12888
|
+
}
|
|
12889
|
+
await options.onTranscriptEvent?.(event);
|
|
12491
12890
|
}
|
|
12492
|
-
|
|
12493
|
-
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12891
|
+
const resultOutcome = extractClaudeResultOutcome(message);
|
|
12892
|
+
switch (resultOutcome.type) {
|
|
12893
|
+
case "success": {
|
|
12894
|
+
if (resultOutcome.text !== void 0) {
|
|
12895
|
+
state.resultText = resultOutcome.text;
|
|
12896
|
+
}
|
|
12897
|
+
state.usage = extractClaudeUsage(message) ?? state.usage;
|
|
12898
|
+
state.completedTurnCount += 1;
|
|
12899
|
+
if (options.streamingInput !== void 0) {
|
|
12900
|
+
await emitClaudeCodeTurnCompleted(options, state);
|
|
12901
|
+
state.lastCompletedTurnBody = state.resultText ?? nonEmptyText(claudeAssistantAggregateText(state)) ?? "";
|
|
12902
|
+
await options.onTurnCompleted?.();
|
|
12903
|
+
resetClaudeAssistantAggregate(state);
|
|
12904
|
+
state.resultText = void 0;
|
|
12905
|
+
}
|
|
12906
|
+
break;
|
|
12499
12907
|
}
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12908
|
+
case "interrupted": {
|
|
12909
|
+
state.completedTurnCount += 1;
|
|
12910
|
+
if (state.sessionId !== void 0) {
|
|
12911
|
+
await options.onTranscriptEvent?.({
|
|
12912
|
+
type: "turn_interrupted",
|
|
12913
|
+
sessionId: state.sessionId
|
|
12914
|
+
});
|
|
12915
|
+
}
|
|
12916
|
+
if (options.streamingInput !== void 0) {
|
|
12917
|
+
await options.onTurnCompleted?.();
|
|
12918
|
+
resetClaudeAssistantAggregate(state);
|
|
12919
|
+
state.resultText = void 0;
|
|
12920
|
+
}
|
|
12921
|
+
break;
|
|
12504
12922
|
}
|
|
12505
|
-
|
|
12923
|
+
case "error":
|
|
12924
|
+
return await failClaudeCodeSession(
|
|
12925
|
+
options,
|
|
12926
|
+
state.sessionId,
|
|
12927
|
+
`Claude Code failed: ${resultOutcome.reason}`
|
|
12928
|
+
);
|
|
12929
|
+
case "none":
|
|
12930
|
+
break;
|
|
12506
12931
|
}
|
|
12507
|
-
case "error":
|
|
12508
|
-
return await failClaudeCodeSession(
|
|
12509
|
-
options,
|
|
12510
|
-
state.sessionId,
|
|
12511
|
-
`Claude Code failed: ${resultOutcome.reason}`
|
|
12512
|
-
);
|
|
12513
|
-
case "none":
|
|
12514
|
-
break;
|
|
12515
12932
|
}
|
|
12933
|
+
} finally {
|
|
12934
|
+
options.streamingInput?.close();
|
|
12516
12935
|
}
|
|
12517
12936
|
return completeClaudeCodeSession(options, state);
|
|
12518
12937
|
}
|
|
12519
12938
|
async function emitClaudeCodeTurnCompleted(options, state) {
|
|
12520
|
-
const body = state.resultText ?? nonEmptyText(claudeAssistantAggregateText(state));
|
|
12521
|
-
if (body === void 0) {
|
|
12522
|
-
return;
|
|
12523
|
-
}
|
|
12939
|
+
const body = state.resultText ?? nonEmptyText(claudeAssistantAggregateText(state)) ?? "";
|
|
12524
12940
|
if (state.sessionId === void 0) {
|
|
12525
12941
|
return;
|
|
12526
12942
|
}
|
|
@@ -12532,7 +12948,7 @@ async function emitClaudeCodeTurnCompleted(options, state) {
|
|
|
12532
12948
|
});
|
|
12533
12949
|
}
|
|
12534
12950
|
async function completeClaudeCodeSession(options, state) {
|
|
12535
|
-
const body = state.resultText ?? nonEmptyText(claudeAssistantAggregateText(state));
|
|
12951
|
+
const body = state.resultText ?? nonEmptyText(claudeAssistantAggregateText(state)) ?? state.lastCompletedTurnBody;
|
|
12536
12952
|
if (body === void 0) {
|
|
12537
12953
|
return await failClaudeCodeSession(
|
|
12538
12954
|
options,
|
|
@@ -13076,207 +13492,6 @@ var init_claudeCodeSession = __esm({
|
|
|
13076
13492
|
}
|
|
13077
13493
|
});
|
|
13078
13494
|
|
|
13079
|
-
// src/runnerLogger.ts
|
|
13080
|
-
import { appendFileSync, openSync as openSync2 } from "node:fs";
|
|
13081
|
-
import { createWriteStream } from "node:fs";
|
|
13082
|
-
import { homedir as homedir6 } from "node:os";
|
|
13083
|
-
import { dirname as dirname3, join as join8 } from "node:path";
|
|
13084
|
-
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
13085
|
-
function createRunnerLogger(logFile, consoleReporter) {
|
|
13086
|
-
mkdirSync3(dirname3(logFile), { recursive: true });
|
|
13087
|
-
const fd = openSync2(logFile, "a");
|
|
13088
|
-
const stream = createWriteStream("", { fd, flags: "a", autoClose: true });
|
|
13089
|
-
const logger = ((event, payload) => {
|
|
13090
|
-
const redacted = redactForCliLog(payload);
|
|
13091
|
-
stream.write(
|
|
13092
|
-
`${JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event, ...redacted })}
|
|
13093
|
-
`,
|
|
13094
|
-
"utf8"
|
|
13095
|
-
);
|
|
13096
|
-
consoleReporter?.(event, runnerConsolePayload(redacted, payload));
|
|
13097
|
-
});
|
|
13098
|
-
Object.defineProperty(logger, "close", {
|
|
13099
|
-
value: () => closeStream(stream)
|
|
13100
|
-
});
|
|
13101
|
-
return logger;
|
|
13102
|
-
}
|
|
13103
|
-
function writeCliAuditEvent(event, payload, options = {}) {
|
|
13104
|
-
const logFile = options.logFile ?? defaultCliAuditLogFile();
|
|
13105
|
-
try {
|
|
13106
|
-
mkdirSync3(dirname3(logFile), { recursive: true });
|
|
13107
|
-
appendFileSync(
|
|
13108
|
-
logFile,
|
|
13109
|
-
`${JSON.stringify({
|
|
13110
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13111
|
-
event,
|
|
13112
|
-
...options.sessionId === void 0 ? {} : { sessionId: options.sessionId },
|
|
13113
|
-
...redactForCliLog(payload)
|
|
13114
|
-
})}
|
|
13115
|
-
`,
|
|
13116
|
-
"utf8"
|
|
13117
|
-
);
|
|
13118
|
-
} catch (_error) {
|
|
13119
|
-
return;
|
|
13120
|
-
}
|
|
13121
|
-
}
|
|
13122
|
-
function defaultCliAuditLogFile() {
|
|
13123
|
-
const override = process.env.LINZUMI_CLI_AUDIT_LOG?.trim();
|
|
13124
|
-
return override === void 0 || override === "" ? join8(homedir6(), ".linzumi", "logs", "command-events.jsonl") : override;
|
|
13125
|
-
}
|
|
13126
|
-
function defaultRunnerLogFile() {
|
|
13127
|
-
return join8(homedir6(), ".linzumi", "logs", "linzumi-runner.log");
|
|
13128
|
-
}
|
|
13129
|
-
function redactForCliLog(value) {
|
|
13130
|
-
return redactObject(value);
|
|
13131
|
-
}
|
|
13132
|
-
function redactValue(value, key) {
|
|
13133
|
-
if (sensitiveKey(key)) {
|
|
13134
|
-
return sensitiveMarker;
|
|
13135
|
-
}
|
|
13136
|
-
if (typeof value === "string") {
|
|
13137
|
-
return redactString(value);
|
|
13138
|
-
}
|
|
13139
|
-
if (Array.isArray(value)) {
|
|
13140
|
-
return key === "args" ? redactArgs(value) : value.map((item) => redactValue(item, void 0));
|
|
13141
|
-
}
|
|
13142
|
-
if (value !== null && typeof value === "object") {
|
|
13143
|
-
return redactObject(value);
|
|
13144
|
-
}
|
|
13145
|
-
return value;
|
|
13146
|
-
}
|
|
13147
|
-
function redactObject(value) {
|
|
13148
|
-
const headerName = typeof value.name === "string" ? value.name.toLowerCase() : void 0;
|
|
13149
|
-
const shouldRedactHeaderValue = headerName === "authorization" || headerName === "cookie" || headerName === "set-cookie";
|
|
13150
|
-
return Object.fromEntries(
|
|
13151
|
-
Object.entries(value).filter(([key]) => key !== "runner_console").map(([key, entry]) => [
|
|
13152
|
-
key,
|
|
13153
|
-
shouldRedactHeaderValue && key === "value" ? sensitiveMarker : redactValue(entry, key)
|
|
13154
|
-
])
|
|
13155
|
-
);
|
|
13156
|
-
}
|
|
13157
|
-
function runnerConsolePayload(redacted, original) {
|
|
13158
|
-
const consoleFields = objectValue3(original.runner_console);
|
|
13159
|
-
return consoleFields === void 0 ? redacted : { ...redacted, ...redactForCliLog(consoleFields) };
|
|
13160
|
-
}
|
|
13161
|
-
function objectValue3(value) {
|
|
13162
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
13163
|
-
}
|
|
13164
|
-
function redactArgs(args) {
|
|
13165
|
-
let redactNext = false;
|
|
13166
|
-
return args.map((arg) => {
|
|
13167
|
-
if (typeof arg !== "string") {
|
|
13168
|
-
redactNext = false;
|
|
13169
|
-
return redactValue(arg, void 0);
|
|
13170
|
-
}
|
|
13171
|
-
if (redactNext) {
|
|
13172
|
-
redactNext = false;
|
|
13173
|
-
return sensitiveMarker;
|
|
13174
|
-
}
|
|
13175
|
-
const [flag, value] = splitArgAssignment(arg);
|
|
13176
|
-
if (sensitiveArgFlags.has(flag)) {
|
|
13177
|
-
if (value === void 0) {
|
|
13178
|
-
redactNext = true;
|
|
13179
|
-
return arg;
|
|
13180
|
-
}
|
|
13181
|
-
return `${flag}=${sensitiveMarker}`;
|
|
13182
|
-
}
|
|
13183
|
-
return redactString(arg);
|
|
13184
|
-
});
|
|
13185
|
-
}
|
|
13186
|
-
function splitArgAssignment(value) {
|
|
13187
|
-
const index = value.indexOf("=");
|
|
13188
|
-
return index === -1 ? [value, void 0] : [value.slice(0, index), value.slice(index + 1)];
|
|
13189
|
-
}
|
|
13190
|
-
function sensitiveKey(key) {
|
|
13191
|
-
if (key === void 0) {
|
|
13192
|
-
return false;
|
|
13193
|
-
}
|
|
13194
|
-
return /^(authorization|cookie|set-cookie|password)$/i.test(key) || /(^|[_-])(access[_-]?token|api[_-]?key|auth[_-]?token|oauth[_-]?code|refresh[_-]?token|secret|token)$/i.test(
|
|
13195
|
-
key
|
|
13196
|
-
);
|
|
13197
|
-
}
|
|
13198
|
-
function redactString(value) {
|
|
13199
|
-
const withRedactedQuery = redactUrlQuery(value);
|
|
13200
|
-
return withRedactedQuery.replace(
|
|
13201
|
-
/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
13202
|
-
sensitiveMarker
|
|
13203
|
-
);
|
|
13204
|
-
}
|
|
13205
|
-
function redactUrlQuery(value) {
|
|
13206
|
-
let parsed;
|
|
13207
|
-
try {
|
|
13208
|
-
parsed = new URL(value);
|
|
13209
|
-
} catch (_error) {
|
|
13210
|
-
return redactRelativeUrlQuery(value);
|
|
13211
|
-
}
|
|
13212
|
-
for (const name of [...parsed.searchParams.keys()]) {
|
|
13213
|
-
if (sensitiveQueryParams.has(name.toLowerCase())) {
|
|
13214
|
-
parsed.searchParams.set(name, sensitiveMarker);
|
|
13215
|
-
}
|
|
13216
|
-
}
|
|
13217
|
-
return parsed.toString();
|
|
13218
|
-
}
|
|
13219
|
-
function redactRelativeUrlQuery(value) {
|
|
13220
|
-
const queryStart = value.indexOf("?");
|
|
13221
|
-
const hashStart = value.indexOf("#");
|
|
13222
|
-
if (queryStart === -1 || hashStart !== -1 && hashStart < queryStart) {
|
|
13223
|
-
return value;
|
|
13224
|
-
}
|
|
13225
|
-
const path2 = value.slice(0, queryStart);
|
|
13226
|
-
const query = hashStart === -1 ? value.slice(queryStart + 1) : value.slice(queryStart + 1, hashStart);
|
|
13227
|
-
const hash = hashStart === -1 ? "" : value.slice(hashStart);
|
|
13228
|
-
const searchParams = new URLSearchParams(query);
|
|
13229
|
-
let redacted = false;
|
|
13230
|
-
for (const name of [...searchParams.keys()]) {
|
|
13231
|
-
if (sensitiveQueryParams.has(name.toLowerCase())) {
|
|
13232
|
-
searchParams.set(name, sensitiveMarker);
|
|
13233
|
-
redacted = true;
|
|
13234
|
-
}
|
|
13235
|
-
}
|
|
13236
|
-
switch (redacted) {
|
|
13237
|
-
case true:
|
|
13238
|
-
return `${path2}?${searchParams.toString()}${hash}`;
|
|
13239
|
-
case false:
|
|
13240
|
-
return value;
|
|
13241
|
-
}
|
|
13242
|
-
}
|
|
13243
|
-
function closeStream(stream) {
|
|
13244
|
-
if (stream.closed || stream.destroyed) {
|
|
13245
|
-
return Promise.resolve();
|
|
13246
|
-
}
|
|
13247
|
-
return new Promise((resolve12, reject) => {
|
|
13248
|
-
stream.once("error", reject);
|
|
13249
|
-
stream.end(resolve12);
|
|
13250
|
-
});
|
|
13251
|
-
}
|
|
13252
|
-
var sensitiveMarker, sensitiveQueryParams, sensitiveArgFlags;
|
|
13253
|
-
var init_runnerLogger = __esm({
|
|
13254
|
-
"src/runnerLogger.ts"() {
|
|
13255
|
-
"use strict";
|
|
13256
|
-
sensitiveMarker = "<SENSITIVE_DATA>";
|
|
13257
|
-
sensitiveQueryParams = /* @__PURE__ */ new Set([
|
|
13258
|
-
"access_token",
|
|
13259
|
-
"authorization",
|
|
13260
|
-
"code",
|
|
13261
|
-
"cookie",
|
|
13262
|
-
"kandan_preview_ticket",
|
|
13263
|
-
"refresh_token",
|
|
13264
|
-
"token"
|
|
13265
|
-
]);
|
|
13266
|
-
sensitiveArgFlags = /* @__PURE__ */ new Set([
|
|
13267
|
-
"--access-token",
|
|
13268
|
-
"--api-key",
|
|
13269
|
-
"--authorization",
|
|
13270
|
-
"--cookie",
|
|
13271
|
-
"--oauth-code",
|
|
13272
|
-
"--password",
|
|
13273
|
-
"--refresh-token",
|
|
13274
|
-
"--secret",
|
|
13275
|
-
"--token"
|
|
13276
|
-
]);
|
|
13277
|
-
}
|
|
13278
|
-
});
|
|
13279
|
-
|
|
13280
13495
|
// src/mcpConfig.ts
|
|
13281
13496
|
function linzumiMcpServerConfig(options) {
|
|
13282
13497
|
return {
|
|
@@ -13403,6 +13618,76 @@ var init_mcpConfig = __esm({
|
|
|
13403
13618
|
}
|
|
13404
13619
|
});
|
|
13405
13620
|
|
|
13621
|
+
// src/engineParentDeathWatchdog.ts
|
|
13622
|
+
function encodeParentDeathWatchdogConfig(config) {
|
|
13623
|
+
return JSON.stringify(config);
|
|
13624
|
+
}
|
|
13625
|
+
function parentDeathWatchdogProgram() {
|
|
13626
|
+
return `
|
|
13627
|
+
const { spawn } = require('node:child_process');
|
|
13628
|
+
const config = JSON.parse(process.argv[process.argv.length - 1]);
|
|
13629
|
+
const originalParentPid = process.ppid;
|
|
13630
|
+
const child = spawn(config.command, config.args, { stdio: 'inherit' });
|
|
13631
|
+
let done = false;
|
|
13632
|
+
function shutdown(signal) {
|
|
13633
|
+
if (done) return;
|
|
13634
|
+
done = true;
|
|
13635
|
+
try { process.kill(-process.pid, signal); }
|
|
13636
|
+
catch (_e) { try { child.kill(signal); } catch (_e2) {} }
|
|
13637
|
+
}
|
|
13638
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
13639
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
13640
|
+
process.on('SIGHUP', () => shutdown('SIGTERM'));
|
|
13641
|
+
child.on('exit', (code, signal) => {
|
|
13642
|
+
clearInterval(watchdog);
|
|
13643
|
+
// The child is gone; the wrapper must follow it down rather than linger as an
|
|
13644
|
+
// orphan. On the forwarded-signal path (shutdown() above caught SIGINT/SIGTERM
|
|
13645
|
+
// /SIGHUP and propagated it to the child), the wrapper still has its own
|
|
13646
|
+
// handler installed for that signal, so a bare process.kill(self, signal)
|
|
13647
|
+
// would just re-enter shutdown() - a no-op once done is set - and the wrapper
|
|
13648
|
+
// would never exit. Remove the handlers first so the signal's DEFAULT
|
|
13649
|
+
// disposition (terminate) applies, re-raise to mirror the child's death
|
|
13650
|
+
// signal, then exit() as a belt-and-suspenders fallback in case re-raising
|
|
13651
|
+
// does not terminate (e.g. a signal with no default-terminate disposition).
|
|
13652
|
+
process.removeAllListeners('SIGINT');
|
|
13653
|
+
process.removeAllListeners('SIGTERM');
|
|
13654
|
+
process.removeAllListeners('SIGHUP');
|
|
13655
|
+
if (signal) { try { process.kill(process.pid, signal); } catch (_e) {} }
|
|
13656
|
+
process.exit(signal ? 1 : code == null ? 0 : code);
|
|
13657
|
+
});
|
|
13658
|
+
child.on('error', (err) => {
|
|
13659
|
+
process.stderr.write('engine watchdog spawn failed: ' + (err && err.message) + '\\n');
|
|
13660
|
+
process.exit(1);
|
|
13661
|
+
});
|
|
13662
|
+
const watchdog = setInterval(() => {
|
|
13663
|
+
// Inlined parentDeathWatchdogShouldReap (keep in sync): reap PURELY on a ppid
|
|
13664
|
+
// change from boot - covers reparent-to-subreaper AND reparent-to-init. NO
|
|
13665
|
+
// \`=== 1\` arm: a runner that is PID 1 captures originalParentPid === 1, so
|
|
13666
|
+
// such an arm would false-fire on the first poll and kill a healthy codex.
|
|
13667
|
+
if (process.ppid !== originalParentPid) {
|
|
13668
|
+
try { child.kill('SIGKILL'); } catch (_e) {}
|
|
13669
|
+
try { process.kill(-process.pid, 'SIGKILL'); } catch (_e) {}
|
|
13670
|
+
process.exit(0);
|
|
13671
|
+
}
|
|
13672
|
+
}, config.pollIntervalMs);
|
|
13673
|
+
`.trim();
|
|
13674
|
+
}
|
|
13675
|
+
function parentDeathWatchdogSpawn(nodeExecPath, config) {
|
|
13676
|
+
return {
|
|
13677
|
+
command: nodeExecPath,
|
|
13678
|
+
args: [
|
|
13679
|
+
"-e",
|
|
13680
|
+
parentDeathWatchdogProgram(),
|
|
13681
|
+
encodeParentDeathWatchdogConfig(config)
|
|
13682
|
+
]
|
|
13683
|
+
};
|
|
13684
|
+
}
|
|
13685
|
+
var init_engineParentDeathWatchdog = __esm({
|
|
13686
|
+
"src/engineParentDeathWatchdog.ts"() {
|
|
13687
|
+
"use strict";
|
|
13688
|
+
}
|
|
13689
|
+
});
|
|
13690
|
+
|
|
13406
13691
|
// src/codexAppServer.ts
|
|
13407
13692
|
import {
|
|
13408
13693
|
spawn as spawn2
|
|
@@ -13444,13 +13729,18 @@ async function startCodexAppServerAttempt(codexBin, cwd, options, attempt) {
|
|
|
13444
13729
|
let stderrText = "";
|
|
13445
13730
|
const configuredStdio = codexAppServerStdio(process.stdout.isTTY === true);
|
|
13446
13731
|
const stdio = [configuredStdio[0], configuredStdio[1], "pipe"];
|
|
13732
|
+
const watchdogSpawn = parentDeathWatchdogSpawn(process.execPath, {
|
|
13733
|
+
command: codexBin,
|
|
13734
|
+
args,
|
|
13735
|
+
pollIntervalMs: codexAppServerWatchdogPollMs
|
|
13736
|
+
});
|
|
13447
13737
|
writeCliAuditEvent("process.spawn", {
|
|
13448
13738
|
command: codexBin,
|
|
13449
13739
|
args,
|
|
13450
13740
|
cwd,
|
|
13451
13741
|
purpose: "codex.app_server"
|
|
13452
13742
|
});
|
|
13453
|
-
const child = spawn2(
|
|
13743
|
+
const child = spawn2(watchdogSpawn.command, [...watchdogSpawn.args], {
|
|
13454
13744
|
cwd,
|
|
13455
13745
|
env: codexAppServerEnv(options.env, options.inheritEnv ?? true),
|
|
13456
13746
|
stdio,
|
|
@@ -13467,7 +13757,17 @@ async function startCodexAppServerAttempt(codexBin, cwd, options, attempt) {
|
|
|
13467
13757
|
}
|
|
13468
13758
|
});
|
|
13469
13759
|
}
|
|
13470
|
-
const
|
|
13760
|
+
const registered = registerEngineChild({
|
|
13761
|
+
stop: () => stopCodexAppServerProcess(child),
|
|
13762
|
+
pid: child.pid,
|
|
13763
|
+
ownProcessGroup: true,
|
|
13764
|
+
kind: "codex.app_server"
|
|
13765
|
+
});
|
|
13766
|
+
child.once("exit", () => registered.unregister());
|
|
13767
|
+
const stop = () => {
|
|
13768
|
+
registered.unregister();
|
|
13769
|
+
stopCodexAppServerProcess(child);
|
|
13770
|
+
};
|
|
13471
13771
|
writeCliAuditEvent("process.spawned", {
|
|
13472
13772
|
command: codexBin,
|
|
13473
13773
|
args,
|
|
@@ -13571,7 +13871,7 @@ function stopCodexAppServerProcess(child, killProcess = process.kill) {
|
|
|
13571
13871
|
child.kill("SIGINT");
|
|
13572
13872
|
}
|
|
13573
13873
|
function logProcessGroupSignalFailure(pid, error) {
|
|
13574
|
-
const code =
|
|
13874
|
+
const code = processSignalErrorCode2(error);
|
|
13575
13875
|
const message = error instanceof Error ? error.message : String(error);
|
|
13576
13876
|
const event = code === "EPERM" ? "process.group_signal_denied" : code === "ESRCH" ? "process.group_signal_missing" : "process.group_signal_failed";
|
|
13577
13877
|
writeCliAuditEvent(event, {
|
|
@@ -13589,7 +13889,7 @@ function logProcessGroupSignalFailure(pid, error) {
|
|
|
13589
13889
|
);
|
|
13590
13890
|
}
|
|
13591
13891
|
}
|
|
13592
|
-
function
|
|
13892
|
+
function processSignalErrorCode2(error) {
|
|
13593
13893
|
if (error !== null && typeof error === "object" && "code" in error) {
|
|
13594
13894
|
const code = error.code;
|
|
13595
13895
|
return typeof code === "string" ? code : void 0;
|
|
@@ -13973,13 +14273,16 @@ function readyzUrlForWebsocket(websocketUrl) {
|
|
|
13973
14273
|
parsed.hash = "";
|
|
13974
14274
|
return parsed.toString();
|
|
13975
14275
|
}
|
|
13976
|
-
var blockedCodexAppServerEnvKeys;
|
|
14276
|
+
var codexAppServerWatchdogPollMs, blockedCodexAppServerEnvKeys;
|
|
13977
14277
|
var init_codexAppServer = __esm({
|
|
13978
14278
|
"src/codexAppServer.ts"() {
|
|
13979
14279
|
"use strict";
|
|
13980
14280
|
init_protocol();
|
|
13981
14281
|
init_runnerLogger();
|
|
13982
14282
|
init_mcpConfig();
|
|
14283
|
+
init_engineChildReaper();
|
|
14284
|
+
init_engineParentDeathWatchdog();
|
|
14285
|
+
codexAppServerWatchdogPollMs = 2e3;
|
|
13983
14286
|
blockedCodexAppServerEnvKeys = [
|
|
13984
14287
|
"LINZUMI_MCP_ACCESS_TOKEN",
|
|
13985
14288
|
"LINZUMI_MCP_OWNER_USERNAME"
|
|
@@ -18127,7 +18430,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
18127
18430
|
var init_version = __esm({
|
|
18128
18431
|
"src/version.ts"() {
|
|
18129
18432
|
"use strict";
|
|
18130
|
-
linzumiCliVersion = "0.0.
|
|
18433
|
+
linzumiCliVersion = "0.0.86-beta";
|
|
18131
18434
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
18132
18435
|
}
|
|
18133
18436
|
});
|
|
@@ -18406,6 +18709,9 @@ function releaseRunnerLock(path2, record) {
|
|
|
18406
18709
|
}
|
|
18407
18710
|
}
|
|
18408
18711
|
function readRunnerLockForRelease(path2) {
|
|
18712
|
+
return readRunnerLockOwner(path2);
|
|
18713
|
+
}
|
|
18714
|
+
function readRunnerLockOwner(path2) {
|
|
18409
18715
|
try {
|
|
18410
18716
|
return readRunnerLockIfPresent(path2);
|
|
18411
18717
|
} catch (_error) {
|
|
@@ -18526,8 +18832,216 @@ var init_runnerLock = __esm({
|
|
|
18526
18832
|
}
|
|
18527
18833
|
});
|
|
18528
18834
|
|
|
18529
|
-
// src/
|
|
18835
|
+
// src/runnerLockTakeover.ts
|
|
18836
|
+
function runnerLockTakeoverSleep(ms) {
|
|
18837
|
+
return new Promise((resolve12) => {
|
|
18838
|
+
setTimeout(resolve12, ms);
|
|
18839
|
+
});
|
|
18840
|
+
}
|
|
18841
|
+
function runnerLockConflictReport(baseMessage) {
|
|
18842
|
+
const bar = "=".repeat(64);
|
|
18843
|
+
return [
|
|
18844
|
+
"",
|
|
18845
|
+
bar,
|
|
18846
|
+
"ERROR: linzumi connect could not start - workspace already in use",
|
|
18847
|
+
bar,
|
|
18848
|
+
baseMessage,
|
|
18849
|
+
bar,
|
|
18850
|
+
""
|
|
18851
|
+
].join("\n");
|
|
18852
|
+
}
|
|
18853
|
+
function isRunnerLockConflictReportedError(error) {
|
|
18854
|
+
return error instanceof RunnerLockConflictReportedError || error instanceof Error && error.name === runnerLockConflictReportedErrorName;
|
|
18855
|
+
}
|
|
18856
|
+
function shouldResolveRunnerLockConflict(lockTakeover) {
|
|
18857
|
+
return lockTakeover !== void 0;
|
|
18858
|
+
}
|
|
18859
|
+
async function resolveRunnerLockConflict(args) {
|
|
18860
|
+
const report = runnerLockConflictReport(args.baseMessage);
|
|
18861
|
+
if (args.takeOverWithoutPrompt) {
|
|
18862
|
+
args.prompt.writeReport(report);
|
|
18863
|
+
await stopHolderAndAwaitRelease(args.holder, args.lockPath, args.takeover);
|
|
18864
|
+
return { outcome: "take_over" };
|
|
18865
|
+
}
|
|
18866
|
+
if (!args.prompt.isInteractive()) {
|
|
18867
|
+
args.prompt.writeReport(report);
|
|
18868
|
+
return { outcome: "declined", report };
|
|
18869
|
+
}
|
|
18870
|
+
args.prompt.writeReport(report);
|
|
18871
|
+
const yes = await args.prompt.promptYesNo(
|
|
18872
|
+
`Another runner (pid ${args.holder.pid}) is already connected to this workspace. Stop it and run here instead? [y/N] `
|
|
18873
|
+
);
|
|
18874
|
+
if (!yes) {
|
|
18875
|
+
return { outcome: "declined", report };
|
|
18876
|
+
}
|
|
18877
|
+
await stopHolderAndAwaitRelease(args.holder, args.lockPath, args.takeover);
|
|
18878
|
+
return { outcome: "take_over" };
|
|
18879
|
+
}
|
|
18880
|
+
async function stopHolderAndAwaitRelease(holder, lockPath, deps) {
|
|
18881
|
+
deps.log("runner.lock_takeover_requested", {
|
|
18882
|
+
holderPid: holder.pid,
|
|
18883
|
+
holderRunnerId: holder.runnerId,
|
|
18884
|
+
lockPath
|
|
18885
|
+
});
|
|
18886
|
+
if (!deps.isPidAlive(holder.pid)) {
|
|
18887
|
+
deps.log("runner.lock_takeover_holder_already_gone", {
|
|
18888
|
+
holderPid: holder.pid,
|
|
18889
|
+
lockPath
|
|
18890
|
+
});
|
|
18891
|
+
return;
|
|
18892
|
+
}
|
|
18893
|
+
if (!stillHeldBy(deps, holder)) {
|
|
18894
|
+
deps.log("runner.lock_takeover_owner_changed", {
|
|
18895
|
+
holderPid: holder.pid,
|
|
18896
|
+
lockPath
|
|
18897
|
+
});
|
|
18898
|
+
return;
|
|
18899
|
+
}
|
|
18900
|
+
sendSignalTolerant(deps, holder.pid, "SIGTERM");
|
|
18901
|
+
if (await waitForHolderRelease(holder, deps, runnerLockTakeoverGracefulStopMs)) {
|
|
18902
|
+
deps.log("runner.lock_takeover_graceful", {
|
|
18903
|
+
holderPid: holder.pid,
|
|
18904
|
+
lockPath
|
|
18905
|
+
});
|
|
18906
|
+
return;
|
|
18907
|
+
}
|
|
18908
|
+
if (!stillHeldBy(deps, holder)) {
|
|
18909
|
+
deps.log("runner.lock_takeover_owner_changed", {
|
|
18910
|
+
holderPid: holder.pid,
|
|
18911
|
+
lockPath
|
|
18912
|
+
});
|
|
18913
|
+
return;
|
|
18914
|
+
}
|
|
18915
|
+
deps.log("runner.lock_takeover_escalating_sigkill", {
|
|
18916
|
+
holderPid: holder.pid,
|
|
18917
|
+
lockPath
|
|
18918
|
+
});
|
|
18919
|
+
sendSignalTolerant(deps, holder.pid, "SIGKILL");
|
|
18920
|
+
if (await waitForHolderRelease(holder, deps, runnerLockTakeoverForcefulStopMs)) {
|
|
18921
|
+
deps.log("runner.lock_takeover_forceful", {
|
|
18922
|
+
holderPid: holder.pid,
|
|
18923
|
+
lockPath
|
|
18924
|
+
});
|
|
18925
|
+
return;
|
|
18926
|
+
}
|
|
18927
|
+
throw new Error(
|
|
18928
|
+
`Could not stop the runner holding this workspace (pid ${holder.pid}). It did not exit after SIGTERM and SIGKILL within the timeout. Stop it manually or remove the lock file (${lockPath}) and retry.`
|
|
18929
|
+
);
|
|
18930
|
+
}
|
|
18931
|
+
async function waitForHolderRelease(holder, deps, timeoutMs) {
|
|
18932
|
+
const deadline = deps.now() + timeoutMs;
|
|
18933
|
+
while (deps.now() < deadline) {
|
|
18934
|
+
if (!deps.isPidAlive(holder.pid) || deps.lockReleased()) {
|
|
18935
|
+
return true;
|
|
18936
|
+
}
|
|
18937
|
+
await deps.sleep(runnerLockTakeoverPollMs);
|
|
18938
|
+
}
|
|
18939
|
+
return !deps.isPidAlive(holder.pid) || deps.lockReleased();
|
|
18940
|
+
}
|
|
18941
|
+
function stillHeldBy(deps, holder) {
|
|
18942
|
+
const current = deps.currentLockOwner();
|
|
18943
|
+
return current !== void 0 && current.machineId === holder.machineId && current.runnerId === holder.runnerId && current.pid === holder.pid && current.startedAt === holder.startedAt;
|
|
18944
|
+
}
|
|
18945
|
+
function sendSignalTolerant(deps, pid, signal) {
|
|
18946
|
+
try {
|
|
18947
|
+
deps.signalPid(pid, signal);
|
|
18948
|
+
} catch (error) {
|
|
18949
|
+
const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
18950
|
+
if (code !== "ESRCH") {
|
|
18951
|
+
deps.log("runner.lock_takeover_signal_failed", {
|
|
18952
|
+
holderPid: pid,
|
|
18953
|
+
signal,
|
|
18954
|
+
code: typeof code === "string" ? code : null,
|
|
18955
|
+
message: error instanceof Error ? error.message : String(error)
|
|
18956
|
+
});
|
|
18957
|
+
}
|
|
18958
|
+
}
|
|
18959
|
+
}
|
|
18960
|
+
var runnerLockTakeoverGracefulStopMs, runnerLockTakeoverForcefulStopMs, runnerLockTakeoverPollMs, runnerLockConflictReportedErrorName, RunnerLockConflictReportedError;
|
|
18961
|
+
var init_runnerLockTakeover = __esm({
|
|
18962
|
+
"src/runnerLockTakeover.ts"() {
|
|
18963
|
+
"use strict";
|
|
18964
|
+
runnerLockTakeoverGracefulStopMs = 1e4;
|
|
18965
|
+
runnerLockTakeoverForcefulStopMs = 5e3;
|
|
18966
|
+
runnerLockTakeoverPollMs = 200;
|
|
18967
|
+
runnerLockConflictReportedErrorName = "RunnerLockConflictReportedError";
|
|
18968
|
+
RunnerLockConflictReportedError = class extends Error {
|
|
18969
|
+
constructor() {
|
|
18970
|
+
super("");
|
|
18971
|
+
this.name = runnerLockConflictReportedErrorName;
|
|
18972
|
+
}
|
|
18973
|
+
};
|
|
18974
|
+
}
|
|
18975
|
+
});
|
|
18976
|
+
|
|
18977
|
+
// src/blessedTputSetulcShim.ts
|
|
18530
18978
|
import blessed from "blessed";
|
|
18979
|
+
function balanceTerminfoConditionals(cap) {
|
|
18980
|
+
if (cap.indexOf("%;") === -1) {
|
|
18981
|
+
return cap;
|
|
18982
|
+
}
|
|
18983
|
+
let depth = 0;
|
|
18984
|
+
let repaired = "";
|
|
18985
|
+
let index = 0;
|
|
18986
|
+
while (index < cap.length) {
|
|
18987
|
+
const here = cap[index];
|
|
18988
|
+
const next = cap[index + 1];
|
|
18989
|
+
if (here === "%" && next === "%") {
|
|
18990
|
+
repaired += "%%";
|
|
18991
|
+
index += 2;
|
|
18992
|
+
continue;
|
|
18993
|
+
}
|
|
18994
|
+
if (here === "%" && next === "?") {
|
|
18995
|
+
depth += 1;
|
|
18996
|
+
repaired += "%?";
|
|
18997
|
+
index += 2;
|
|
18998
|
+
continue;
|
|
18999
|
+
}
|
|
19000
|
+
if (here === "%" && next === ";") {
|
|
19001
|
+
if (depth > 0) {
|
|
19002
|
+
depth -= 1;
|
|
19003
|
+
repaired += "%;";
|
|
19004
|
+
}
|
|
19005
|
+
index += 2;
|
|
19006
|
+
continue;
|
|
19007
|
+
}
|
|
19008
|
+
repaired += here;
|
|
19009
|
+
index += 1;
|
|
19010
|
+
}
|
|
19011
|
+
return repaired;
|
|
19012
|
+
}
|
|
19013
|
+
function installBlessedSetulcShim(blessedModule = blessed) {
|
|
19014
|
+
const tput = blessedModule.Tput;
|
|
19015
|
+
const proto = tput?.prototype;
|
|
19016
|
+
const original = proto?._compile;
|
|
19017
|
+
if (proto === void 0 || typeof original !== "function") {
|
|
19018
|
+
return;
|
|
19019
|
+
}
|
|
19020
|
+
if (proto[SHIM_FLAG] === true) {
|
|
19021
|
+
return;
|
|
19022
|
+
}
|
|
19023
|
+
const patched = function patchedCompile(info, key, str) {
|
|
19024
|
+
const safeStr = typeof str === "string" ? balanceTerminfoConditionals(str) : str;
|
|
19025
|
+
return original.call(this, info, key, safeStr);
|
|
19026
|
+
};
|
|
19027
|
+
proto._compile = patched;
|
|
19028
|
+
Object.defineProperty(proto, SHIM_FLAG, {
|
|
19029
|
+
value: true,
|
|
19030
|
+
enumerable: false,
|
|
19031
|
+
configurable: true,
|
|
19032
|
+
writable: true
|
|
19033
|
+
});
|
|
19034
|
+
}
|
|
19035
|
+
var SHIM_FLAG;
|
|
19036
|
+
var init_blessedTputSetulcShim = __esm({
|
|
19037
|
+
"src/blessedTputSetulcShim.ts"() {
|
|
19038
|
+
"use strict";
|
|
19039
|
+
SHIM_FLAG = "__linzumiSetulcShimInstalled";
|
|
19040
|
+
}
|
|
19041
|
+
});
|
|
19042
|
+
|
|
19043
|
+
// src/runnerConsoleReporter.ts
|
|
19044
|
+
import blessed2 from "blessed";
|
|
18531
19045
|
function reportRunnerConsoleEvent(event, payload) {
|
|
18532
19046
|
if (shouldRenderDashboard()) {
|
|
18533
19047
|
const tui = initializeDashboardTui(dashboardState);
|
|
@@ -19250,7 +19764,9 @@ function ignoredMessage(payload) {
|
|
|
19250
19764
|
`reason=${text(payload.reason)}`,
|
|
19251
19765
|
optionalField("type", payload.type),
|
|
19252
19766
|
optionalField("thread", payload.thread_id),
|
|
19767
|
+
optionalField("bound_thread", payload.bound_thread_id),
|
|
19253
19768
|
optionalField("body_chars", payload.body_length),
|
|
19769
|
+
optionalField("body", payload.body_preview),
|
|
19254
19770
|
optionalField("attachments", payload.attachment_count),
|
|
19255
19771
|
optionalField("local_event", payload.local_runner_event_type)
|
|
19256
19772
|
].filter((part) => part !== void 0).join(" ");
|
|
@@ -19510,20 +20026,21 @@ function handleDashboardKey(state, key, exitProcess = () => process.kill(process
|
|
|
19510
20026
|
function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill(process.pid, "SIGINT")) {
|
|
19511
20027
|
let rendering = false;
|
|
19512
20028
|
let rawScrollAnchor;
|
|
19513
|
-
|
|
20029
|
+
installBlessedSetulcShim();
|
|
20030
|
+
const screen = blessed2.screen({
|
|
19514
20031
|
title: "Linzumi Commander",
|
|
19515
20032
|
smartCSR: true,
|
|
19516
20033
|
fullUnicode: true,
|
|
19517
20034
|
mouse: true
|
|
19518
20035
|
});
|
|
19519
|
-
const header =
|
|
20036
|
+
const header = blessed2.box({
|
|
19520
20037
|
top: 0,
|
|
19521
20038
|
left: 0,
|
|
19522
20039
|
width: "100%",
|
|
19523
20040
|
height: 1,
|
|
19524
20041
|
tags: false
|
|
19525
20042
|
});
|
|
19526
|
-
const tableElement =
|
|
20043
|
+
const tableElement = blessed2.listtable({
|
|
19527
20044
|
top: 1,
|
|
19528
20045
|
left: 0,
|
|
19529
20046
|
width: "100%",
|
|
@@ -19552,7 +20069,7 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
19552
20069
|
}
|
|
19553
20070
|
}
|
|
19554
20071
|
});
|
|
19555
|
-
const rawTitle =
|
|
20072
|
+
const rawTitle = blessed2.box({
|
|
19556
20073
|
top: 0,
|
|
19557
20074
|
left: 12,
|
|
19558
20075
|
right: 0,
|
|
@@ -19560,7 +20077,7 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
19560
20077
|
tags: false,
|
|
19561
20078
|
hidden: true
|
|
19562
20079
|
});
|
|
19563
|
-
const backButton =
|
|
20080
|
+
const backButton = blessed2.button({
|
|
19564
20081
|
top: 0,
|
|
19565
20082
|
left: 0,
|
|
19566
20083
|
width: 10,
|
|
@@ -19575,7 +20092,7 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
19575
20092
|
hover: { inverse: true }
|
|
19576
20093
|
}
|
|
19577
20094
|
});
|
|
19578
|
-
const rawBox =
|
|
20095
|
+
const rawBox = blessed2.box({
|
|
19579
20096
|
top: 3,
|
|
19580
20097
|
left: 0,
|
|
19581
20098
|
width: "100%",
|
|
@@ -20054,6 +20571,7 @@ var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey,
|
|
|
20054
20571
|
var init_runnerConsoleReporter = __esm({
|
|
20055
20572
|
"src/runnerConsoleReporter.ts"() {
|
|
20056
20573
|
"use strict";
|
|
20574
|
+
init_blessedTputSetulcShim();
|
|
20057
20575
|
dashboardState = {
|
|
20058
20576
|
jobs: /* @__PURE__ */ new Map(),
|
|
20059
20577
|
discovery: /* @__PURE__ */ new Map(),
|
|
@@ -21379,6 +21897,7 @@ import { spawn as spawn9, spawnSync as spawnSync5 } from "node:child_process";
|
|
|
21379
21897
|
import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
|
|
21380
21898
|
import {
|
|
21381
21899
|
chmodSync as chmodSync2,
|
|
21900
|
+
existsSync as existsSync13,
|
|
21382
21901
|
lstatSync,
|
|
21383
21902
|
mkdirSync as mkdirSync12,
|
|
21384
21903
|
mkdtempSync as mkdtempSync4,
|
|
@@ -21393,6 +21912,7 @@ import {
|
|
|
21393
21912
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
21394
21913
|
import { createServer as createServer3 } from "node:http";
|
|
21395
21914
|
import { homedir as homedir13, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
|
|
21915
|
+
import { createInterface } from "node:readline";
|
|
21396
21916
|
import {
|
|
21397
21917
|
basename as basename8,
|
|
21398
21918
|
dirname as dirname13,
|
|
@@ -21417,42 +21937,35 @@ async function runLocalCodexRunner(options) {
|
|
|
21417
21937
|
});
|
|
21418
21938
|
try {
|
|
21419
21939
|
if (options.machineId !== void 0) {
|
|
21420
|
-
|
|
21421
|
-
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
log2("runner.lock_takeover", {
|
|
21437
|
-
runnerId: options.runnerId,
|
|
21438
|
-
holderRunnerId: takeover.holderRunnerId,
|
|
21439
|
-
holderPid: takeover.holderPid,
|
|
21440
|
-
reason: takeover.reason,
|
|
21441
|
-
lockPath: takeover.lockPath
|
|
21442
|
-
});
|
|
21443
|
-
}
|
|
21444
|
-
});
|
|
21445
|
-
} catch (error) {
|
|
21446
|
-
if (isRunnerLockHeldError(error)) {
|
|
21447
|
-
log2("runner.lock_held_by_live_process", {
|
|
21940
|
+
const machineId = options.machineId;
|
|
21941
|
+
const acquire = () => acquireRunnerLock({
|
|
21942
|
+
machineId,
|
|
21943
|
+
runnerId: options.runnerId,
|
|
21944
|
+
cwd: options.cwd,
|
|
21945
|
+
workspace: runnerWorkspaceSlug(options) ?? null,
|
|
21946
|
+
linzumiUrl: options.kandanUrl,
|
|
21947
|
+
launchSource: options.launchSource,
|
|
21948
|
+
configPath: options.runnerLockConfigPath,
|
|
21949
|
+
// Wedged-runner takeover: a lock holder whose pid is alive but
|
|
21950
|
+
// whose lock heartbeat went stale (>3 min) gets SIGKILLed and its
|
|
21951
|
+
// lock replaced, instead of blocking recovery until someone runs
|
|
21952
|
+
// kill -9 by hand. Log it so the runner log explains where the old
|
|
21953
|
+
// pid went.
|
|
21954
|
+
onTakeover: (takeover) => {
|
|
21955
|
+
log2("runner.lock_takeover", {
|
|
21448
21956
|
runnerId: options.runnerId,
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21957
|
+
holderRunnerId: takeover.holderRunnerId,
|
|
21958
|
+
holderPid: takeover.holderPid,
|
|
21959
|
+
reason: takeover.reason,
|
|
21960
|
+
lockPath: takeover.lockPath
|
|
21452
21961
|
});
|
|
21453
21962
|
}
|
|
21454
|
-
|
|
21455
|
-
|
|
21963
|
+
});
|
|
21964
|
+
const runnerLock = await acquireRunnerLockWithConflictResolution(
|
|
21965
|
+
acquire,
|
|
21966
|
+
options,
|
|
21967
|
+
log2
|
|
21968
|
+
);
|
|
21456
21969
|
cleanup.actions.push(() => runnerLock.release());
|
|
21457
21970
|
log2("runner.lock_acquired", {
|
|
21458
21971
|
path: runnerLock.path,
|
|
@@ -22472,6 +22985,11 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22472
22985
|
if (options.channelSession !== void 0 && codex === void 0) {
|
|
22473
22986
|
throw new Error("channel session requires a Codex app-server connection");
|
|
22474
22987
|
}
|
|
22988
|
+
const ownsRespawnableAppServer = runnerOwnsRespawnableAppServer({
|
|
22989
|
+
codexUrl: options.codexUrl,
|
|
22990
|
+
threadProcessRole: options.threadProcess?.role,
|
|
22991
|
+
ownsStartedAppServer: started !== void 0
|
|
22992
|
+
});
|
|
22475
22993
|
const seq = { value: 0 };
|
|
22476
22994
|
const discoveredCodexThreads = { value: [] };
|
|
22477
22995
|
const runtimeDefaults = runnerRuntimeDefaults(options);
|
|
@@ -22563,6 +23081,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22563
23081
|
const dynamicChannelSessions = /* @__PURE__ */ new Map();
|
|
22564
23082
|
const dynamicChannelSessionCodexClients = /* @__PURE__ */ new Map();
|
|
22565
23083
|
const codexTurnFailureGuards = /* @__PURE__ */ new Map();
|
|
23084
|
+
const lossReportPushes = [];
|
|
22566
23085
|
const codexTurnFailureGuardFor = (client) => {
|
|
22567
23086
|
const existing = codexTurnFailureGuards.get(client);
|
|
22568
23087
|
if (existing !== void 0) {
|
|
@@ -22575,7 +23094,36 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22575
23094
|
([codexThreadId]) => dynamicChannelSessionCodexClients.get(codexThreadId) === client
|
|
22576
23095
|
).map(([, session]) => session)
|
|
22577
23096
|
],
|
|
22578
|
-
log: log2
|
|
23097
|
+
log: log2,
|
|
23098
|
+
// Spec: plans/2026-06-13-orphaned-mid-turn-reconnect-recovery-spec.md
|
|
23099
|
+
// The runner process and its channel survive an app-server death, so the
|
|
23100
|
+
// server otherwise keeps these thread bindings "connected" and never
|
|
23101
|
+
// offers reconnect. Report the loss per affected thread (best-effort)
|
|
23102
|
+
// so the bindings flip to disconnected/reconnectable upstream.
|
|
23103
|
+
//
|
|
23104
|
+
// Only do this for a runner that owns a respawnable app-server. For an
|
|
23105
|
+
// external/remote `codexUrl` backend (or a thread-process worker) this
|
|
23106
|
+
// runner cannot relaunch the dead app-server, so a reconnect could never
|
|
23107
|
+
// recover the thread - marking it reconnectable would be misleading. In
|
|
23108
|
+
// that case we still fail the in-flight turns terminally, but leave the
|
|
23109
|
+
// binding as-is rather than offering a reconnect that cannot succeed.
|
|
23110
|
+
onActiveThreadsFailed: ownsRespawnableAppServer ? (threadIds) => {
|
|
23111
|
+
for (const codexThreadId of threadIds) {
|
|
23112
|
+
lossReportPushes.push(
|
|
23113
|
+
kandan.push(topic, "session:app_server_lost", {
|
|
23114
|
+
codex_thread_id: codexThreadId
|
|
23115
|
+
}).then(
|
|
23116
|
+
() => void 0,
|
|
23117
|
+
(error) => {
|
|
23118
|
+
log2("kandan.session_app_server_lost_push_failed", {
|
|
23119
|
+
codex_thread_id: codexThreadId,
|
|
23120
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23121
|
+
});
|
|
23122
|
+
}
|
|
23123
|
+
)
|
|
23124
|
+
);
|
|
23125
|
+
}
|
|
23126
|
+
} : void 0
|
|
22579
23127
|
});
|
|
22580
23128
|
codexTurnFailureGuards.set(client, guard);
|
|
22581
23129
|
return guard;
|
|
@@ -22592,7 +23140,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22592
23140
|
);
|
|
22593
23141
|
dynamicChannelSessions.clear();
|
|
22594
23142
|
dynamicChannelSessionCodexClients.clear();
|
|
22595
|
-
|
|
23143
|
+
const claudeSessions = [...activeClaudeCodeSessions.values()];
|
|
23144
|
+
for (const session of claudeSessions) {
|
|
23145
|
+
try {
|
|
23146
|
+
session.closeInput();
|
|
23147
|
+
} catch (error) {
|
|
23148
|
+
log2("claude_code.session_input_close_failed", {
|
|
23149
|
+
linzumi_thread_id: session.threadId,
|
|
23150
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23151
|
+
});
|
|
23152
|
+
}
|
|
23153
|
+
}
|
|
23154
|
+
await Promise.race([
|
|
23155
|
+
Promise.allSettled(
|
|
23156
|
+
claudeSessions.flatMap((session) => {
|
|
23157
|
+
const work = session.sessionWork();
|
|
23158
|
+
return work === void 0 ? [] : [work];
|
|
23159
|
+
})
|
|
23160
|
+
),
|
|
23161
|
+
new Promise((resolve12) => {
|
|
23162
|
+
setTimeout(resolve12, 1e4).unref?.();
|
|
23163
|
+
})
|
|
23164
|
+
]);
|
|
23165
|
+
for (const session of claudeSessions) {
|
|
22596
23166
|
session.abortController.abort(new Error("local runner stopped"));
|
|
22597
23167
|
}
|
|
22598
23168
|
activeClaudeCodeSessions.clear();
|
|
@@ -22657,7 +23227,8 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22657
23227
|
portForwardWatcher: channelSessionPortForwardWatcherOptions({
|
|
22658
23228
|
rootPid: portForwardWatcherRootPid,
|
|
22659
23229
|
start: options.portForwardWatcher,
|
|
22660
|
-
commanderBoundPids: args.commanderBoundPids
|
|
23230
|
+
commanderBoundPids: args.commanderBoundPids,
|
|
23231
|
+
commanderBoundPorts: args.commanderBoundPorts
|
|
22661
23232
|
}),
|
|
22662
23233
|
suppressedForwardPorts,
|
|
22663
23234
|
onForwardPortApproved: (port, attribution) => {
|
|
@@ -22852,6 +23423,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22852
23423
|
portForwardWatcherRootPid: handle.processPid,
|
|
22853
23424
|
commanderBoundPids: (handle.commanderManagedPorts ?? []).flatMap(
|
|
22854
23425
|
(port) => port.pid === void 0 ? [] : [port.pid]
|
|
23426
|
+
),
|
|
23427
|
+
// The exact codex app-server ports are commander-managed even though
|
|
23428
|
+
// their listening pid is the wrapped grandchild, not the recorded
|
|
23429
|
+
// wrapper pid (Codex P1).
|
|
23430
|
+
commanderBoundPorts: (handle.commanderManagedPorts ?? []).map(
|
|
23431
|
+
(port) => port.port
|
|
22855
23432
|
)
|
|
22856
23433
|
});
|
|
22857
23434
|
switch (control.type) {
|
|
@@ -23001,6 +23578,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23001
23578
|
}
|
|
23002
23579
|
});
|
|
23003
23580
|
if (sharedCodexTurnFailureGuard !== void 0) {
|
|
23581
|
+
const appServerLost = { handled: false };
|
|
23004
23582
|
const failCodexSessionTurns = (cause) => {
|
|
23005
23583
|
if (cleanup.closePromise !== void 0) {
|
|
23006
23584
|
return;
|
|
@@ -23008,6 +23586,27 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23008
23586
|
log2("codex.app_server_connection_lost", { cause });
|
|
23009
23587
|
sharedCodexTurnFailureGuard.failActiveTurns(cause);
|
|
23010
23588
|
started?.stop();
|
|
23589
|
+
if (ownsRespawnableAppServer && !appServerLost.handled) {
|
|
23590
|
+
appServerLost.handled = true;
|
|
23591
|
+
console.error(
|
|
23592
|
+
`Codex app-server connection was lost (${cause}); exiting so a fresh runner can relaunch it. Run \`linzumi connect\` to reconnect.`
|
|
23593
|
+
);
|
|
23594
|
+
const sessionDrains = [
|
|
23595
|
+
...channelSession !== void 0 ? [channelSession] : [],
|
|
23596
|
+
...dynamicChannelSessions.values()
|
|
23597
|
+
].map(
|
|
23598
|
+
(session) => session.close().catch((error) => {
|
|
23599
|
+
log2("codex.app_server_lost_session_drain_failed", {
|
|
23600
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23601
|
+
});
|
|
23602
|
+
})
|
|
23603
|
+
);
|
|
23604
|
+
void awaitLossReportsThenExit({
|
|
23605
|
+
pushes: [...lossReportPushes, ...sessionDrains],
|
|
23606
|
+
exit: () => process.exit(75),
|
|
23607
|
+
safetyCapMs: 5e3
|
|
23608
|
+
});
|
|
23609
|
+
}
|
|
23011
23610
|
};
|
|
23012
23611
|
codex?.onClose?.((error) => failCodexSessionTurns(error.message));
|
|
23013
23612
|
started?.process.once(
|
|
@@ -23763,6 +24362,19 @@ async function resolveSessionControl(channelSession, dynamicChannelSessions, con
|
|
|
23763
24362
|
}
|
|
23764
24363
|
return void 0;
|
|
23765
24364
|
}
|
|
24365
|
+
function runnerOwnsRespawnableAppServer(args) {
|
|
24366
|
+
return args.codexUrl === void 0 && args.threadProcessRole !== "thread" && args.ownsStartedAppServer;
|
|
24367
|
+
}
|
|
24368
|
+
async function awaitLossReportsThenExit(args) {
|
|
24369
|
+
const setTimeoutImpl = args.setTimeoutFn ?? setTimeout;
|
|
24370
|
+
const drained = Promise.allSettled(args.pushes).then(() => void 0);
|
|
24371
|
+
const safetyCap = new Promise((resolve12) => {
|
|
24372
|
+
const handle = setTimeoutImpl(() => resolve12(), args.safetyCapMs);
|
|
24373
|
+
handle?.unref?.();
|
|
24374
|
+
});
|
|
24375
|
+
await Promise.race([drained, safetyCap]);
|
|
24376
|
+
args.exit();
|
|
24377
|
+
}
|
|
23766
24378
|
function createCodexTurnFailureGuard(args) {
|
|
23767
24379
|
const activeTurnsByThread = /* @__PURE__ */ new Map();
|
|
23768
24380
|
const state = { failed: false };
|
|
@@ -23798,6 +24410,7 @@ function createCodexTurnFailureGuard(args) {
|
|
|
23798
24410
|
if (activeTurns.length === 0) {
|
|
23799
24411
|
return;
|
|
23800
24412
|
}
|
|
24413
|
+
args.onActiveThreadsFailed?.(activeTurns.map(([threadId]) => threadId));
|
|
23801
24414
|
const sessions = Array.from(args.sessions());
|
|
23802
24415
|
for (const [threadId, turnId] of activeTurns) {
|
|
23803
24416
|
args.log("codex.active_turn_failed_on_connection_loss", {
|
|
@@ -23955,6 +24568,101 @@ function installCleanupHandlers(close) {
|
|
|
23955
24568
|
process.off("exit", closeOnExit);
|
|
23956
24569
|
};
|
|
23957
24570
|
}
|
|
24571
|
+
async function acquireRunnerLockWithConflictResolution(acquire, options, log2) {
|
|
24572
|
+
try {
|
|
24573
|
+
return acquire();
|
|
24574
|
+
} catch (error) {
|
|
24575
|
+
if (!isRunnerLockHeldError(error)) {
|
|
24576
|
+
throw error;
|
|
24577
|
+
}
|
|
24578
|
+
log2("runner.lock_held_by_live_process", {
|
|
24579
|
+
runnerId: options.runnerId,
|
|
24580
|
+
heldByRunnerId: error.heldBy.runnerId,
|
|
24581
|
+
heldByPid: error.heldBy.pid,
|
|
24582
|
+
lockPath: error.lockPath
|
|
24583
|
+
});
|
|
24584
|
+
const lockTakeover = options.lockTakeover;
|
|
24585
|
+
if (!shouldResolveRunnerLockConflict(lockTakeover)) {
|
|
24586
|
+
throw error;
|
|
24587
|
+
}
|
|
24588
|
+
const promptDeps = lockTakeover.prompt ?? defaultRunnerLockTakeoverPromptDeps(lockTakeover);
|
|
24589
|
+
const takeoverDeps = resolveRunnerLockTakeoverDeps(
|
|
24590
|
+
error.lockPath,
|
|
24591
|
+
lockTakeover.takeover,
|
|
24592
|
+
log2
|
|
24593
|
+
);
|
|
24594
|
+
const resolution = await resolveRunnerLockConflict({
|
|
24595
|
+
holder: error.heldBy,
|
|
24596
|
+
lockPath: error.lockPath,
|
|
24597
|
+
baseMessage: error.message,
|
|
24598
|
+
takeOverWithoutPrompt: lockTakeover.takeOverWithoutPrompt === true,
|
|
24599
|
+
prompt: promptDeps,
|
|
24600
|
+
takeover: takeoverDeps
|
|
24601
|
+
});
|
|
24602
|
+
if (resolution.outcome === "declined") {
|
|
24603
|
+
throw new RunnerLockConflictReportedError();
|
|
24604
|
+
}
|
|
24605
|
+
log2("runner.lock_takeover_interactive", {
|
|
24606
|
+
runnerId: options.runnerId,
|
|
24607
|
+
heldByRunnerId: error.heldBy.runnerId,
|
|
24608
|
+
heldByPid: error.heldBy.pid,
|
|
24609
|
+
lockPath: error.lockPath
|
|
24610
|
+
});
|
|
24611
|
+
return acquire();
|
|
24612
|
+
}
|
|
24613
|
+
}
|
|
24614
|
+
function defaultRunnerLockTakeoverPromptDeps(takeover) {
|
|
24615
|
+
return {
|
|
24616
|
+
isInteractive: () => takeover?.forceNonInteractive !== true && process.stdin.isTTY === true && process.stderr.isTTY === true,
|
|
24617
|
+
writeReport: (text2) => {
|
|
24618
|
+
process.stderr.write(`${text2}
|
|
24619
|
+
`);
|
|
24620
|
+
},
|
|
24621
|
+
promptYesNo: (question) => promptYesNoOnStdin(question)
|
|
24622
|
+
};
|
|
24623
|
+
}
|
|
24624
|
+
function promptYesNoOnStdin(question) {
|
|
24625
|
+
return new Promise((resolve12) => {
|
|
24626
|
+
const rl = createInterface({
|
|
24627
|
+
input: process.stdin,
|
|
24628
|
+
output: process.stderr
|
|
24629
|
+
});
|
|
24630
|
+
rl.question(question, (answer) => {
|
|
24631
|
+
rl.close();
|
|
24632
|
+
resolve12(/^y(es)?$/i.test(answer.trim()));
|
|
24633
|
+
});
|
|
24634
|
+
});
|
|
24635
|
+
}
|
|
24636
|
+
function resolveRunnerLockTakeoverDeps(lockPath, overrides, log2) {
|
|
24637
|
+
return {
|
|
24638
|
+
isPidAlive: overrides?.isPidAlive ?? runnerLockTakeoverPidIsAlive,
|
|
24639
|
+
signalPid: overrides?.signalPid ?? ((pid, signal) => {
|
|
24640
|
+
process.kill(pid, signal);
|
|
24641
|
+
}),
|
|
24642
|
+
lockReleased: overrides?.lockReleased ?? (() => !existsSync13(lockPath)),
|
|
24643
|
+
// Re-reads the on-disk owner so the takeover flow can revalidate identity
|
|
24644
|
+
// before signaling (never throws on a torn/partial lock file).
|
|
24645
|
+
currentLockOwner: overrides?.currentLockOwner ?? (() => readRunnerLockOwner(lockPath)),
|
|
24646
|
+
// runnerLockTakeoverSleep is deliberately NOT unref'ed - see its definition;
|
|
24647
|
+
// an unref'ed timer here would let `linzumi connect` exit before retrying
|
|
24648
|
+
// the lock acquisition during a takeover.
|
|
24649
|
+
sleep: overrides?.sleep ?? runnerLockTakeoverSleep,
|
|
24650
|
+
now: overrides?.now ?? (() => Date.now()),
|
|
24651
|
+
log: overrides?.log ?? ((event, payload) => {
|
|
24652
|
+
log2(event, payload);
|
|
24653
|
+
writeCliAuditEvent(event, payload);
|
|
24654
|
+
})
|
|
24655
|
+
};
|
|
24656
|
+
}
|
|
24657
|
+
function runnerLockTakeoverPidIsAlive(pid) {
|
|
24658
|
+
try {
|
|
24659
|
+
process.kill(pid, 0);
|
|
24660
|
+
return true;
|
|
24661
|
+
} catch (error) {
|
|
24662
|
+
const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
24663
|
+
return code !== "ESRCH";
|
|
24664
|
+
}
|
|
24665
|
+
}
|
|
23958
24666
|
function launchCodexTui(codexBin, codexUrl, cwd, codexThreadId, session, fast) {
|
|
23959
24667
|
const args = codexTuiArgs(codexUrl, codexThreadId, session, fast);
|
|
23960
24668
|
writeCliAuditEvent("process.spawn", {
|
|
@@ -24449,10 +25157,28 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
24449
25157
|
};
|
|
24450
25158
|
}
|
|
24451
25159
|
try {
|
|
24452
|
-
activeSession.enqueueInput({
|
|
25160
|
+
const enqueueResult = activeSession.enqueueInput({
|
|
24453
25161
|
content: contentResult.content,
|
|
24454
25162
|
sourceSeq
|
|
24455
25163
|
});
|
|
25164
|
+
if (enqueueResult === "duplicate") {
|
|
25165
|
+
log2("claude_code.message_duplicate_ignored", {
|
|
25166
|
+
linzumi_thread_id: optionalThreadControlField(control, "threadId") ?? null,
|
|
25167
|
+
claude_session_id: codexThreadId,
|
|
25168
|
+
body_preview: claudeConsoleBodyPreview(workDescription),
|
|
25169
|
+
source_seq: sourceSeq ?? null
|
|
25170
|
+
});
|
|
25171
|
+
return {
|
|
25172
|
+
instanceId,
|
|
25173
|
+
controlType: control.type,
|
|
25174
|
+
agentProvider: "claude-code",
|
|
25175
|
+
cwd: cwd.cwd,
|
|
25176
|
+
matchedRoot: cwd.matchedRoot,
|
|
25177
|
+
codexThreadId,
|
|
25178
|
+
queuedInput: true,
|
|
25179
|
+
duplicate: true
|
|
25180
|
+
};
|
|
25181
|
+
}
|
|
24456
25182
|
log2("claude_code.message_queued", {
|
|
24457
25183
|
linzumi_thread_id: optionalThreadControlField(control, "threadId") ?? null,
|
|
24458
25184
|
claude_session_id: codexThreadId,
|
|
@@ -24956,7 +25682,24 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
24956
25682
|
};
|
|
24957
25683
|
}
|
|
24958
25684
|
case "stop_instance":
|
|
24959
|
-
case "kill_instance":
|
|
25685
|
+
case "kill_instance": {
|
|
25686
|
+
const controlThreadId2 = "threadId" in control && typeof control.threadId === "string" ? control.threadId : void 0;
|
|
25687
|
+
const activeSession = controlThreadId2 === void 0 ? void 0 : activeClaudeCodeSessions.get(controlThreadId2);
|
|
25688
|
+
if (activeSession !== void 0) {
|
|
25689
|
+
await activeSession.interruptTurn("Claude Code stopped by Linzumi");
|
|
25690
|
+
activeSession.abortController.abort(
|
|
25691
|
+
new Error("Claude Code stopped by Linzumi")
|
|
25692
|
+
);
|
|
25693
|
+
return {
|
|
25694
|
+
instanceId,
|
|
25695
|
+
controlType: control.type,
|
|
25696
|
+
agentProvider: "claude-code",
|
|
25697
|
+
threadId: controlThreadId2,
|
|
25698
|
+
stopped: true
|
|
25699
|
+
};
|
|
25700
|
+
}
|
|
25701
|
+
return { instanceId, controlType: control.type, skipped: true };
|
|
25702
|
+
}
|
|
24960
25703
|
case "resolve_port_forward_request":
|
|
24961
25704
|
case "update_thread_interaction_access":
|
|
24962
25705
|
case "set_port_forward_enabled":
|
|
@@ -25456,10 +26199,10 @@ function createClaudeCodeInputQueue(input) {
|
|
|
25456
26199
|
claudeCodeUserInputMessage(input.content)
|
|
25457
26200
|
];
|
|
25458
26201
|
const pendingSourceSeqs = [input.sourceSeq];
|
|
26202
|
+
const deferredFollowUps = [];
|
|
25459
26203
|
const waiters = [];
|
|
25460
26204
|
const state = {
|
|
25461
|
-
closed: false
|
|
25462
|
-
queuedTurnCount: 1
|
|
26205
|
+
closed: false
|
|
25463
26206
|
};
|
|
25464
26207
|
const drainClosedWaiters = () => {
|
|
25465
26208
|
while (state.closed) {
|
|
@@ -25470,19 +26213,25 @@ function createClaudeCodeInputQueue(input) {
|
|
|
25470
26213
|
waiter({ done: true, value: void 0 });
|
|
25471
26214
|
}
|
|
25472
26215
|
};
|
|
26216
|
+
const deliverNow = (message, sourceSeq) => {
|
|
26217
|
+
pendingSourceSeqs.push(sourceSeq);
|
|
26218
|
+
const waiter = waiters.shift();
|
|
26219
|
+
if (waiter === void 0) {
|
|
26220
|
+
pendingMessages.push(message);
|
|
26221
|
+
return;
|
|
26222
|
+
}
|
|
26223
|
+
waiter({ done: false, value: message });
|
|
26224
|
+
};
|
|
25473
26225
|
const enqueue = (next) => {
|
|
25474
26226
|
if (state.closed) {
|
|
25475
26227
|
throw new Error("Claude Code streaming input is closed");
|
|
25476
26228
|
}
|
|
25477
26229
|
const message = claudeCodeUserInputMessage(next.content);
|
|
25478
|
-
pendingSourceSeqs.
|
|
25479
|
-
|
|
25480
|
-
const waiter = waiters.shift();
|
|
25481
|
-
if (waiter === void 0) {
|
|
25482
|
-
pendingMessages.push(message);
|
|
26230
|
+
if (pendingSourceSeqs.length > 0) {
|
|
26231
|
+
deferredFollowUps.push({ message, sourceSeq: next.sourceSeq });
|
|
25483
26232
|
return;
|
|
25484
26233
|
}
|
|
25485
|
-
|
|
26234
|
+
deliverNow(message, next.sourceSeq);
|
|
25486
26235
|
};
|
|
25487
26236
|
const enqueueSteer = (content) => {
|
|
25488
26237
|
if (state.closed) {
|
|
@@ -25520,14 +26269,30 @@ function createClaudeCodeInputQueue(input) {
|
|
|
25520
26269
|
};
|
|
25521
26270
|
}
|
|
25522
26271
|
},
|
|
25523
|
-
queuedTurnCount: () => state.queuedTurnCount,
|
|
25524
26272
|
close: () => {
|
|
26273
|
+
if (pendingSourceSeqs.length === 0) {
|
|
26274
|
+
while (deferredFollowUps.length > 0) {
|
|
26275
|
+
const followUp = deferredFollowUps.shift();
|
|
26276
|
+
if (followUp !== void 0) {
|
|
26277
|
+
deliverNow(followUp.message, followUp.sourceSeq);
|
|
26278
|
+
}
|
|
26279
|
+
}
|
|
26280
|
+
}
|
|
25525
26281
|
state.closed = true;
|
|
25526
26282
|
drainClosedWaiters();
|
|
25527
26283
|
},
|
|
25528
26284
|
enqueue,
|
|
25529
26285
|
enqueueSteer,
|
|
25530
|
-
completeTurn: () =>
|
|
26286
|
+
completeTurn: () => {
|
|
26287
|
+
const completed = pendingSourceSeqs.shift();
|
|
26288
|
+
if (!state.closed && pendingSourceSeqs.length === 0) {
|
|
26289
|
+
const nextFollowUp = deferredFollowUps.shift();
|
|
26290
|
+
if (nextFollowUp !== void 0) {
|
|
26291
|
+
deliverNow(nextFollowUp.message, nextFollowUp.sourceSeq);
|
|
26292
|
+
}
|
|
26293
|
+
}
|
|
26294
|
+
return completed;
|
|
26295
|
+
},
|
|
25531
26296
|
currentSourceSeq: () => pendingSourceSeqs[0]
|
|
25532
26297
|
};
|
|
25533
26298
|
}
|
|
@@ -25775,6 +26540,21 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25775
26540
|
codexVersion: void 0
|
|
25776
26541
|
};
|
|
25777
26542
|
const abortController = new AbortController();
|
|
26543
|
+
const reaperRegistration = registerEngineChild({
|
|
26544
|
+
stop: () => {
|
|
26545
|
+
if (!abortController.signal.aborted) {
|
|
26546
|
+
abortController.abort(new Error("engine child reaper teardown"));
|
|
26547
|
+
}
|
|
26548
|
+
},
|
|
26549
|
+
pid: void 0,
|
|
26550
|
+
ownProcessGroup: false,
|
|
26551
|
+
kind: "claude_code"
|
|
26552
|
+
});
|
|
26553
|
+
const acceptedSourceSeqs = new Set(
|
|
26554
|
+
sourceSeq === void 0 ? [] : [sourceSeq]
|
|
26555
|
+
);
|
|
26556
|
+
const sessionWorkHandle = { value: void 0 };
|
|
26557
|
+
let startControlResponded = false;
|
|
25778
26558
|
let activeSessionId;
|
|
25779
26559
|
let sessionControls;
|
|
25780
26560
|
const planMirrorClient = createLinzumiMcpApiClient({
|
|
@@ -25909,7 +26689,7 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25909
26689
|
rootSeq,
|
|
25910
26690
|
log: args.log
|
|
25911
26691
|
};
|
|
25912
|
-
|
|
26692
|
+
const lastRateLimitSignatureByWindow = /* @__PURE__ */ new Map();
|
|
25913
26693
|
const reportClaudeCodeRateLimit = async (event) => {
|
|
25914
26694
|
const nowMs = Date.now();
|
|
25915
26695
|
const summary = claudeCodeRateLimitSummaryText(event.rateLimit, nowMs);
|
|
@@ -25925,13 +26705,13 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25925
26705
|
utilization_percent: event.rateLimit.utilizationPercent ?? null,
|
|
25926
26706
|
resets_at_ms: event.rateLimit.resetsAtMs ?? null
|
|
25927
26707
|
});
|
|
26708
|
+
const windowKey = event.rateLimit.rateLimitType ?? "account";
|
|
25928
26709
|
const signature = claudeRateLimitSignature(event.rateLimit);
|
|
25929
|
-
if (signature ===
|
|
26710
|
+
if (signature === lastRateLimitSignatureByWindow.get(windowKey)) {
|
|
25930
26711
|
return;
|
|
25931
26712
|
}
|
|
25932
|
-
lastRateLimitSignature = signature;
|
|
25933
26713
|
try {
|
|
25934
|
-
await
|
|
26714
|
+
await publishClaudeCodeRateLimitState({
|
|
25935
26715
|
kandan: args.kandan,
|
|
25936
26716
|
topic: args.topic,
|
|
25937
26717
|
workspace,
|
|
@@ -25940,6 +26720,7 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25940
26720
|
claudeSessionId: event.sessionId ?? activeSessionId,
|
|
25941
26721
|
rateLimit: event.rateLimit
|
|
25942
26722
|
});
|
|
26723
|
+
lastRateLimitSignatureByWindow.set(windowKey, signature);
|
|
25943
26724
|
} catch (error) {
|
|
25944
26725
|
args.log("claude_code.rate_limit_post_failed", {
|
|
25945
26726
|
thread_id: threadId,
|
|
@@ -25947,6 +26728,10 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25947
26728
|
});
|
|
25948
26729
|
}
|
|
25949
26730
|
};
|
|
26731
|
+
let settleFirstTurn = () => void 0;
|
|
26732
|
+
const firstTurnSettled = new Promise((resolve12) => {
|
|
26733
|
+
settleFirstTurn = resolve12;
|
|
26734
|
+
});
|
|
25950
26735
|
const onTranscriptEvent = async (event) => {
|
|
25951
26736
|
try {
|
|
25952
26737
|
await mirrorClaudeSessionStoreAppend(mirrorArgs, event);
|
|
@@ -25969,7 +26754,19 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25969
26754
|
channel,
|
|
25970
26755
|
threadId,
|
|
25971
26756
|
currentSourceSeq: inputQueue.currentSourceSeq,
|
|
25972
|
-
enqueueInput:
|
|
26757
|
+
enqueueInput: (input) => {
|
|
26758
|
+
if (input.sourceSeq !== void 0 && acceptedSourceSeqs.has(input.sourceSeq)) {
|
|
26759
|
+
return "duplicate";
|
|
26760
|
+
}
|
|
26761
|
+
inputQueue.enqueue(input);
|
|
26762
|
+
if (input.sourceSeq !== void 0) {
|
|
26763
|
+
acceptedSourceSeqs.add(input.sourceSeq);
|
|
26764
|
+
}
|
|
26765
|
+
return "queued";
|
|
26766
|
+
},
|
|
26767
|
+
closeInput: inputQueue.close,
|
|
26768
|
+
hasActiveTurn: () => adapter.activeTurnId() !== void 0,
|
|
26769
|
+
sessionWork: () => sessionWorkHandle.value,
|
|
25973
26770
|
steerTurn: inputQueue.enqueueSteer,
|
|
25974
26771
|
interruptTurn: async (reason) => {
|
|
25975
26772
|
const aborted = adapter.interruptActiveTurn(reason);
|
|
@@ -26025,6 +26822,10 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26025
26822
|
claude_session_id: event.sessionId,
|
|
26026
26823
|
body_preview: claudeConsoleBodyPreview(event.body)
|
|
26027
26824
|
});
|
|
26825
|
+
settleFirstTurn();
|
|
26826
|
+
}
|
|
26827
|
+
if (event.type === "turn_interrupted") {
|
|
26828
|
+
settleFirstTurn();
|
|
26028
26829
|
}
|
|
26029
26830
|
if (event.type === "rate_limit") {
|
|
26030
26831
|
await reportClaudeCodeRateLimit(event);
|
|
@@ -26071,80 +26872,98 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26071
26872
|
request,
|
|
26072
26873
|
signal
|
|
26073
26874
|
});
|
|
26074
|
-
|
|
26075
|
-
|
|
26875
|
+
const runSession = async () => {
|
|
26876
|
+
let result2;
|
|
26076
26877
|
try {
|
|
26077
|
-
|
|
26078
|
-
|
|
26079
|
-
prompt: workDescription,
|
|
26080
|
-
developerInstructions: commanderDeveloperInstructions({
|
|
26878
|
+
try {
|
|
26879
|
+
result2 = await startClaudeCodeSession({
|
|
26081
26880
|
cwd: args.cwd,
|
|
26082
|
-
|
|
26083
|
-
|
|
26084
|
-
|
|
26085
|
-
|
|
26086
|
-
|
|
26087
|
-
|
|
26088
|
-
|
|
26089
|
-
|
|
26090
|
-
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
|
|
26095
|
-
|
|
26096
|
-
|
|
26097
|
-
|
|
26098
|
-
|
|
26099
|
-
|
|
26100
|
-
|
|
26101
|
-
|
|
26102
|
-
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
|
|
26112
|
-
|
|
26113
|
-
|
|
26114
|
-
|
|
26115
|
-
|
|
26116
|
-
|
|
26117
|
-
|
|
26881
|
+
prompt: workDescription,
|
|
26882
|
+
developerInstructions: commanderDeveloperInstructions({
|
|
26883
|
+
cwd: args.cwd,
|
|
26884
|
+
agentLabel: "Claude Code",
|
|
26885
|
+
planTool: "todo-write",
|
|
26886
|
+
developerPrompt,
|
|
26887
|
+
linzumiContext
|
|
26888
|
+
}),
|
|
26889
|
+
model: claudeCodeModelForRuntimeModel(runtimeSettings.model),
|
|
26890
|
+
resumeSessionId: args.resumeSessionId,
|
|
26891
|
+
abortController,
|
|
26892
|
+
streamingInput: inputQueue,
|
|
26893
|
+
runner: args.options.claudeCodeRunner,
|
|
26894
|
+
canUseTool,
|
|
26895
|
+
...mcpServers === void 0 ? {} : { mcpServers },
|
|
26896
|
+
...liveBashCapture === void 0 ? {} : {
|
|
26897
|
+
preToolUseHookMatchers: liveBashCapture.hookMatchers,
|
|
26898
|
+
wrapApprovedToolInput: (toolUseId, toolName2, input) => toolName2 === "Bash" ? liveBashCapture.wrapApprovedTool(toolUseId, input) : void 0
|
|
26899
|
+
},
|
|
26900
|
+
env: sessionEnv,
|
|
26901
|
+
// The built-in Linzumi MCP is trusted like the codex side: its tools
|
|
26902
|
+
// run without an approval round-trip. SDK allowedTools matches MCP
|
|
26903
|
+
// tools as mcp__<server>__<tool>; the __* suffix covers the server.
|
|
26904
|
+
allowedTools: ["mcp__linzumi__*"],
|
|
26905
|
+
onSessionControls: (controls) => {
|
|
26906
|
+
sessionControls = controls;
|
|
26907
|
+
},
|
|
26908
|
+
onTurnCompleted: () => {
|
|
26909
|
+
inputQueue.completeTurn();
|
|
26910
|
+
settleFirstTurn();
|
|
26911
|
+
},
|
|
26912
|
+
onTranscriptEvent
|
|
26913
|
+
});
|
|
26914
|
+
} finally {
|
|
26915
|
+
inputQueue.close();
|
|
26916
|
+
mcpAuthCleanup?.();
|
|
26917
|
+
liveBashCapture?.close();
|
|
26918
|
+
if (activeSessionId !== void 0) {
|
|
26919
|
+
args.activeClaudeCodeSessions.delete(activeSessionId);
|
|
26920
|
+
await args.disposeClaudeCodeForwardPortSession?.(activeSessionId);
|
|
26921
|
+
}
|
|
26922
|
+
}
|
|
26923
|
+
} catch (error) {
|
|
26924
|
+
adapter.submitLifecycle(
|
|
26925
|
+
"codexDied",
|
|
26926
|
+
error instanceof Error ? error.message : String(error)
|
|
26927
|
+
);
|
|
26928
|
+
await adapter.close(5e3).catch(() => void 0);
|
|
26929
|
+
await Promise.race([
|
|
26930
|
+
planMirror.settle().catch(() => void 0),
|
|
26931
|
+
new Promise((resolve12) => {
|
|
26932
|
+
setTimeout(resolve12, 5e3).unref?.();
|
|
26933
|
+
})
|
|
26934
|
+
]);
|
|
26935
|
+
logStartFailureTerminal(
|
|
26936
|
+
error instanceof Error ? error.message : String(error)
|
|
26937
|
+
);
|
|
26938
|
+
if (!startControlResponded) {
|
|
26939
|
+
args.setStartupStage("claude_code_session_failed");
|
|
26118
26940
|
}
|
|
26941
|
+
throw error;
|
|
26119
26942
|
}
|
|
26120
|
-
|
|
26121
|
-
|
|
26122
|
-
|
|
26123
|
-
|
|
26124
|
-
);
|
|
26125
|
-
await adapter.close(5e3).catch(() => void 0);
|
|
26943
|
+
if (!startControlResponded) {
|
|
26944
|
+
args.setStartupStage("posting_claude_code_output");
|
|
26945
|
+
}
|
|
26946
|
+
await adapter.awaitTurnsSettled(3e4);
|
|
26947
|
+
await adapter.close(3e4);
|
|
26126
26948
|
await Promise.race([
|
|
26127
|
-
planMirror.settle()
|
|
26949
|
+
planMirror.settle(),
|
|
26128
26950
|
new Promise((resolve12) => {
|
|
26129
|
-
setTimeout(resolve12,
|
|
26951
|
+
setTimeout(resolve12, 1e4).unref?.();
|
|
26130
26952
|
})
|
|
26131
26953
|
]);
|
|
26132
|
-
|
|
26133
|
-
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
|
|
26138
|
-
|
|
26139
|
-
|
|
26140
|
-
|
|
26141
|
-
|
|
26142
|
-
|
|
26143
|
-
new Promise((resolve12) => {
|
|
26144
|
-
setTimeout(resolve12, 1e4).unref?.();
|
|
26145
|
-
})
|
|
26954
|
+
return result2;
|
|
26955
|
+
};
|
|
26956
|
+
const sessionWork = runSession();
|
|
26957
|
+
void sessionWork.finally(() => reaperRegistration.unregister()).catch(() => void 0);
|
|
26958
|
+
sessionWorkHandle.value = sessionWork;
|
|
26959
|
+
const settled = await Promise.race([
|
|
26960
|
+
sessionWork.then((result2) => ({
|
|
26961
|
+
type: "session_ended",
|
|
26962
|
+
result: result2
|
|
26963
|
+
})),
|
|
26964
|
+
firstTurnSettled.then(() => ({ type: "first_turn_settled" }))
|
|
26146
26965
|
]);
|
|
26147
|
-
|
|
26966
|
+
const respondWithSessionId = (sessionId) => ({
|
|
26148
26967
|
controlResponse: {
|
|
26149
26968
|
instanceId: args.instanceId,
|
|
26150
26969
|
controlType: args.control.type,
|
|
@@ -26152,16 +26971,52 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26152
26971
|
cwd: args.cwd,
|
|
26153
26972
|
matchedRoot: args.matchedRoot,
|
|
26154
26973
|
response: {
|
|
26155
|
-
session: { id:
|
|
26974
|
+
session: { id: sessionId }
|
|
26156
26975
|
}
|
|
26157
26976
|
},
|
|
26158
26977
|
providerSession: {
|
|
26159
26978
|
provider: "claude-code",
|
|
26160
26979
|
cwd: args.cwd,
|
|
26161
26980
|
matchedRoot: args.matchedRoot,
|
|
26162
|
-
sessionId
|
|
26981
|
+
sessionId
|
|
26163
26982
|
}
|
|
26164
|
-
};
|
|
26983
|
+
});
|
|
26984
|
+
if (settled.type === "session_ended") {
|
|
26985
|
+
return respondWithSessionId(settled.result.sessionId);
|
|
26986
|
+
}
|
|
26987
|
+
args.setStartupStage("posting_claude_code_output");
|
|
26988
|
+
startControlResponded = true;
|
|
26989
|
+
await adapter.awaitTurnsSettled(3e4);
|
|
26990
|
+
try {
|
|
26991
|
+
await adapter.flush(3e4);
|
|
26992
|
+
} catch (error) {
|
|
26993
|
+
args.log("claude_code.first_turn_flush_timeout", {
|
|
26994
|
+
linzumi_thread_id: threadId,
|
|
26995
|
+
claude_session_id: activeSessionId ?? null,
|
|
26996
|
+
message: error instanceof Error ? error.message : String(error)
|
|
26997
|
+
});
|
|
26998
|
+
}
|
|
26999
|
+
void sessionWork.then(
|
|
27000
|
+
() => {
|
|
27001
|
+
args.log("claude_code.session_loop_ended", {
|
|
27002
|
+
linzumi_thread_id: threadId,
|
|
27003
|
+
claude_session_id: activeSessionId ?? null
|
|
27004
|
+
});
|
|
27005
|
+
},
|
|
27006
|
+
(error) => {
|
|
27007
|
+
args.log("claude_code.session_loop_failed", {
|
|
27008
|
+
linzumi_thread_id: threadId,
|
|
27009
|
+
claude_session_id: activeSessionId ?? null,
|
|
27010
|
+
message: error instanceof Error ? error.message : String(error)
|
|
27011
|
+
});
|
|
27012
|
+
}
|
|
27013
|
+
);
|
|
27014
|
+
const liveSessionId = activeSessionId ?? args.resumeSessionId;
|
|
27015
|
+
if (liveSessionId !== void 0) {
|
|
27016
|
+
return respondWithSessionId(liveSessionId);
|
|
27017
|
+
}
|
|
27018
|
+
const result = await sessionWork;
|
|
27019
|
+
return respondWithSessionId(result.sessionId);
|
|
26165
27020
|
}
|
|
26166
27021
|
function claudePermissionModeForApprovalPolicy(approvalPolicy) {
|
|
26167
27022
|
switch (approvalPolicy) {
|
|
@@ -26403,50 +27258,26 @@ function claudeRateLimitSignature(rateLimit) {
|
|
|
26403
27258
|
rateLimit.resetsAtMs ?? "na"
|
|
26404
27259
|
].join(":");
|
|
26405
27260
|
}
|
|
26406
|
-
async function
|
|
26407
|
-
|
|
26408
|
-
const summary = claudeCodeRateLimitSummaryText(args.rateLimit, nowMs);
|
|
26409
|
-
const headline = `Claude Code rate limit: ${summary}`;
|
|
26410
|
-
const resetLabel = claudeCodeRateLimitResetLabel(
|
|
26411
|
-
args.rateLimit.resetsAtMs,
|
|
26412
|
-
nowMs
|
|
26413
|
-
);
|
|
26414
|
-
const richUi = args.rateLimit.utilizationPercent === void 0 ? [{ primitive: "label", label: headline }] : [
|
|
26415
|
-
{
|
|
26416
|
-
primitive: "progress",
|
|
26417
|
-
value: Math.min(
|
|
26418
|
-
100,
|
|
26419
|
-
Math.max(0, Math.round(args.rateLimit.utilizationPercent))
|
|
26420
|
-
),
|
|
26421
|
-
max: 100,
|
|
26422
|
-
label: `Claude Code rate limit${resetLabel === void 0 ? "" : ` - ${resetLabel}`}`
|
|
26423
|
-
}
|
|
26424
|
-
];
|
|
26425
|
-
await args.kandan.push(args.topic, "session:post_thread_message", {
|
|
27261
|
+
async function publishClaudeCodeRateLimitState(args) {
|
|
27262
|
+
await args.kandan.push(args.topic, "claude_rate_limit_state", {
|
|
26426
27263
|
workspace: args.workspace,
|
|
26427
27264
|
channel: args.channel,
|
|
26428
27265
|
thread_id: args.threadId,
|
|
26429
|
-
|
|
26430
|
-
|
|
26431
|
-
|
|
26432
|
-
|
|
26433
|
-
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
27266
|
+
agent_provider: "claude-code",
|
|
27267
|
+
...args.claudeSessionId === void 0 ? {} : { claude_session_id: args.claudeSessionId },
|
|
27268
|
+
// "account" mirrors the server-side default for SDK frames that omit a
|
|
27269
|
+
// window type; sending it explicitly keeps the wire contract obvious.
|
|
27270
|
+
rate_limit_type: args.rateLimit.rateLimitType ?? "account",
|
|
27271
|
+
status: args.rateLimit.status ?? "allowed",
|
|
27272
|
+
...args.rateLimit.utilizationPercent === void 0 ? {} : {
|
|
27273
|
+
utilization_percent: Math.min(
|
|
27274
|
+
100,
|
|
27275
|
+
Math.max(0, Math.round(args.rateLimit.utilizationPercent))
|
|
27276
|
+
)
|
|
26439
27277
|
},
|
|
26440
|
-
|
|
26441
|
-
args.threadId,
|
|
26442
|
-
args.rateLimit
|
|
26443
|
-
)
|
|
27278
|
+
...args.rateLimit.resetsAtMs === void 0 ? {} : { resets_at_ms: args.rateLimit.resetsAtMs }
|
|
26444
27279
|
});
|
|
26445
27280
|
}
|
|
26446
|
-
function claudeRateLimitClientMessageId(threadId, rateLimit) {
|
|
26447
|
-
const digest = createHash5("sha256").update(`${threadId}:${claudeRateLimitSignature(rateLimit)}`).digest("hex").slice(0, 32);
|
|
26448
|
-
return `claude-rate-limit-${digest}`;
|
|
26449
|
-
}
|
|
26450
27281
|
async function publishStartInstanceMessageState(kandan, topic, control, status, reason, diagnostics = {}) {
|
|
26451
27282
|
const payload = startInstanceMessageStatePayload(
|
|
26452
27283
|
control,
|
|
@@ -26562,9 +27393,11 @@ function channelSessionPortForwardWatcherOptions(args) {
|
|
|
26562
27393
|
...args.rootPid === void 0 ? [] : [args.rootPid],
|
|
26563
27394
|
...args.commanderBoundPids ?? []
|
|
26564
27395
|
];
|
|
27396
|
+
const commanderBoundPorts = args.commanderBoundPorts ?? [];
|
|
26565
27397
|
return {
|
|
26566
27398
|
...args.rootPid === void 0 ? {} : { rootPid: args.rootPid },
|
|
26567
27399
|
...commanderBoundPids.length === 0 ? {} : { commanderBoundPids },
|
|
27400
|
+
...commanderBoundPorts.length === 0 ? {} : { commanderBoundPorts },
|
|
26568
27401
|
...args.start === void 0 ? {} : { start: args.start }
|
|
26569
27402
|
};
|
|
26570
27403
|
}
|
|
@@ -28366,6 +29199,7 @@ var init_runner = __esm({
|
|
|
28366
29199
|
init_commanderAttachments();
|
|
28367
29200
|
init_claudeCodePipeline();
|
|
28368
29201
|
init_claudeCodePlanMirror();
|
|
29202
|
+
init_engineChildReaper();
|
|
28369
29203
|
init_claudeCodeLiveBashOutput();
|
|
28370
29204
|
init_claudeCodeSession();
|
|
28371
29205
|
init_codexAppServer();
|
|
@@ -28388,6 +29222,7 @@ var init_runner = __esm({
|
|
|
28388
29222
|
init_protocol();
|
|
28389
29223
|
init_runnerLogger();
|
|
28390
29224
|
init_runnerLock();
|
|
29225
|
+
init_runnerLockTakeover();
|
|
28391
29226
|
init_runnerConsoleReporter();
|
|
28392
29227
|
init_version();
|
|
28393
29228
|
init_telemetry();
|
|
@@ -28428,7 +29263,7 @@ var init_runner = __esm({
|
|
|
28428
29263
|
});
|
|
28429
29264
|
|
|
28430
29265
|
// src/kandanTls.ts
|
|
28431
|
-
import { existsSync as
|
|
29266
|
+
import { existsSync as existsSync14, readFileSync as readFileSync17 } from "node:fs";
|
|
28432
29267
|
import { Agent } from "undici";
|
|
28433
29268
|
import WsWebSocket from "ws";
|
|
28434
29269
|
function kandanTlsTrustFromEnv() {
|
|
@@ -28439,7 +29274,7 @@ function kandanTlsTrustFromCaFile(caFile) {
|
|
|
28439
29274
|
return void 0;
|
|
28440
29275
|
}
|
|
28441
29276
|
const trimmed = caFile.trim();
|
|
28442
|
-
if (!
|
|
29277
|
+
if (!existsSync14(trimmed)) {
|
|
28443
29278
|
throw new Error(`KANDAN_TLS_CA_FILE does not exist: ${trimmed}`);
|
|
28444
29279
|
}
|
|
28445
29280
|
const ca = readFileSync17(trimmed, "utf8");
|
|
@@ -48717,7 +49552,7 @@ __export(signupFlow_exports, {
|
|
|
48717
49552
|
});
|
|
48718
49553
|
import { spawn as spawn12, spawnSync as spawnSync7 } from "node:child_process";
|
|
48719
49554
|
import {
|
|
48720
|
-
existsSync as
|
|
49555
|
+
existsSync as existsSync17,
|
|
48721
49556
|
constants as fsConstants,
|
|
48722
49557
|
mkdirSync as mkdirSync15,
|
|
48723
49558
|
mkdtempSync as mkdtempSync5,
|
|
@@ -48845,7 +49680,7 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
48845
49680
|
const path2 = defaultSignupDraftPath(serviceUrl);
|
|
48846
49681
|
return {
|
|
48847
49682
|
read: () => {
|
|
48848
|
-
if (!
|
|
49683
|
+
if (!existsSync17(path2)) {
|
|
48849
49684
|
return void 0;
|
|
48850
49685
|
}
|
|
48851
49686
|
let parsed;
|
|
@@ -48924,7 +49759,7 @@ function defaultSignupTaskCachePath(serviceUrl) {
|
|
|
48924
49759
|
);
|
|
48925
49760
|
}
|
|
48926
49761
|
function readSignupTaskCache(path2) {
|
|
48927
|
-
if (!
|
|
49762
|
+
if (!existsSync17(path2)) {
|
|
48928
49763
|
return { version: 1, entries: {} };
|
|
48929
49764
|
}
|
|
48930
49765
|
let parsed;
|
|
@@ -51752,7 +52587,7 @@ async function discoverCodeRoots(homeDir) {
|
|
|
51752
52587
|
const candidates = ["src", "code", "projects"].map(
|
|
51753
52588
|
(name) => join23(homeDir, name)
|
|
51754
52589
|
);
|
|
51755
|
-
return candidates.filter((path2) =>
|
|
52590
|
+
return candidates.filter((path2) => existsSync17(path2)).flatMap((path2) => discoveredProjectNames(path2));
|
|
51756
52591
|
}
|
|
51757
52592
|
function discoveredProjectNames(root) {
|
|
51758
52593
|
try {
|
|
@@ -51856,25 +52691,25 @@ function looksLikeProject(path2) {
|
|
|
51856
52691
|
"pnpm-lock.yaml",
|
|
51857
52692
|
"yarn.lock",
|
|
51858
52693
|
"package-lock.json"
|
|
51859
|
-
].some((name) =>
|
|
52694
|
+
].some((name) => existsSync17(join23(path2, name)));
|
|
51860
52695
|
}
|
|
51861
52696
|
function detectProjectLanguage(path2) {
|
|
51862
|
-
if (
|
|
52697
|
+
if (existsSync17(join23(path2, "pyproject.toml")) || existsSync17(join23(path2, "requirements.txt"))) {
|
|
51863
52698
|
return "Python";
|
|
51864
52699
|
}
|
|
51865
|
-
if (
|
|
52700
|
+
if (existsSync17(join23(path2, "Cargo.toml"))) {
|
|
51866
52701
|
return "Rust";
|
|
51867
52702
|
}
|
|
51868
|
-
if (
|
|
52703
|
+
if (existsSync17(join23(path2, "go.mod"))) {
|
|
51869
52704
|
return "Go";
|
|
51870
52705
|
}
|
|
51871
|
-
if (
|
|
52706
|
+
if (existsSync17(join23(path2, "mix.exs"))) {
|
|
51872
52707
|
return "Elixir";
|
|
51873
52708
|
}
|
|
51874
|
-
if (
|
|
52709
|
+
if (existsSync17(join23(path2, "tsconfig.json")) || packageJsonMentionsTypeScript(path2)) {
|
|
51875
52710
|
return "TypeScript";
|
|
51876
52711
|
}
|
|
51877
|
-
if (
|
|
52712
|
+
if (existsSync17(join23(path2, "package.json"))) {
|
|
51878
52713
|
return "JavaScript";
|
|
51879
52714
|
}
|
|
51880
52715
|
return "Project";
|
|
@@ -51890,7 +52725,7 @@ function packageJsonMentionsTypeScript(path2) {
|
|
|
51890
52725
|
}
|
|
51891
52726
|
}
|
|
51892
52727
|
function hasGitMetadata(path2) {
|
|
51893
|
-
return
|
|
52728
|
+
return existsSync17(join23(path2, ".git"));
|
|
51894
52729
|
}
|
|
51895
52730
|
function childDirectories(root) {
|
|
51896
52731
|
try {
|
|
@@ -52011,9 +52846,10 @@ secure mission control for all your agents on your computers
|
|
|
52011
52846
|
// src/index.ts
|
|
52012
52847
|
init_onboardingDiscoveryChildProcess();
|
|
52013
52848
|
init_runner();
|
|
52849
|
+
init_runnerLockTakeover();
|
|
52014
52850
|
init_claudeCodeSession();
|
|
52015
52851
|
init_authCache();
|
|
52016
|
-
import { existsSync as
|
|
52852
|
+
import { existsSync as existsSync18, readFileSync as readFileSync22, realpathSync as realpathSync7 } from "node:fs";
|
|
52017
52853
|
import { homedir as homedir17 } from "node:os";
|
|
52018
52854
|
import { resolve as resolve11 } from "node:path";
|
|
52019
52855
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
@@ -52109,7 +52945,7 @@ init_kandanTls();
|
|
|
52109
52945
|
init_protocol();
|
|
52110
52946
|
init_json();
|
|
52111
52947
|
init_defaultUrls();
|
|
52112
|
-
import { existsSync as
|
|
52948
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "node:fs";
|
|
52113
52949
|
import { dirname as dirname14, join as join21 } from "node:path";
|
|
52114
52950
|
import { homedir as homedir14 } from "node:os";
|
|
52115
52951
|
async function runAgentCliCommand(args, deps = {
|
|
@@ -52828,7 +53664,7 @@ function authorizationHeaders(token) {
|
|
|
52828
53664
|
return { authorization: `Bearer ${token}` };
|
|
52829
53665
|
}
|
|
52830
53666
|
function readOptionalTextFile(path2) {
|
|
52831
|
-
return
|
|
53667
|
+
return existsSync15(path2) ? readFileSync18(path2, "utf8") : void 0;
|
|
52832
53668
|
}
|
|
52833
53669
|
function writeTextFile(path2, content) {
|
|
52834
53670
|
mkdirSync13(dirname14(path2), { recursive: true });
|
|
@@ -52919,7 +53755,7 @@ init_helloLinzumiProject();
|
|
|
52919
53755
|
// src/commanderDaemon.ts
|
|
52920
53756
|
init_runnerLogger();
|
|
52921
53757
|
import {
|
|
52922
|
-
existsSync as
|
|
53758
|
+
existsSync as existsSync16,
|
|
52923
53759
|
closeSync as closeSync3,
|
|
52924
53760
|
mkdirSync as mkdirSync14,
|
|
52925
53761
|
openSync as openSync4,
|
|
@@ -53013,7 +53849,7 @@ function startCommanderDaemon(options) {
|
|
|
53013
53849
|
}
|
|
53014
53850
|
function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), processIdentityReader = readProcessIdentity) {
|
|
53015
53851
|
const statusFile = commanderStatusFile(runnerId, statusDir);
|
|
53016
|
-
if (!
|
|
53852
|
+
if (!existsSync16(statusFile)) {
|
|
53017
53853
|
return { status: "missing", runnerId, statusFile };
|
|
53018
53854
|
}
|
|
53019
53855
|
const record = parseRecord(readFileSync19(statusFile, "utf8"));
|
|
@@ -53021,7 +53857,7 @@ function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), proce
|
|
|
53021
53857
|
}
|
|
53022
53858
|
async function waitForCommanderDaemon(options) {
|
|
53023
53859
|
const now = options.now ?? (() => Date.now());
|
|
53024
|
-
const readTextFile = options.readTextFile ?? ((path2) =>
|
|
53860
|
+
const readTextFile = options.readTextFile ?? ((path2) => existsSync16(path2) ? readFileSync19(path2, "utf8") : void 0);
|
|
53025
53861
|
const statusImpl = options.statusImpl ?? commanderDaemonStatus;
|
|
53026
53862
|
const deadline = now() + options.timeoutMs;
|
|
53027
53863
|
while (now() <= deadline) {
|
|
@@ -66236,6 +67072,11 @@ var flagDefinitions = /* @__PURE__ */ new Map([
|
|
|
66236
67072
|
["command", { kind: "value" }],
|
|
66237
67073
|
["owner-username", { kind: "value" }],
|
|
66238
67074
|
["include-token", { kind: "boolean" }],
|
|
67075
|
+
// Interactive lock-conflict resolution on connect: --take-over stops a live
|
|
67076
|
+
// holder without a prompt (automation); --no-input forces non-interactive so
|
|
67077
|
+
// a TTY never blocks on the takeover prompt (falls back to report + exit 1).
|
|
67078
|
+
["take-over", { kind: "boolean" }],
|
|
67079
|
+
["no-input", { kind: "boolean" }],
|
|
66239
67080
|
["help", { kind: "boolean" }]
|
|
66240
67081
|
]);
|
|
66241
67082
|
var helloFlagDefinitions = /* @__PURE__ */ new Map([
|
|
@@ -66252,8 +67093,11 @@ if (isMainModule()) {
|
|
|
66252
67093
|
try {
|
|
66253
67094
|
await main(process.argv.slice(2));
|
|
66254
67095
|
} catch (error) {
|
|
66255
|
-
|
|
67096
|
+
const message = cliErrorMessage(error);
|
|
67097
|
+
if (message !== "") {
|
|
67098
|
+
process.stderr.write(`${message}
|
|
66256
67099
|
`);
|
|
67100
|
+
}
|
|
66257
67101
|
process.exit(1);
|
|
66258
67102
|
}
|
|
66259
67103
|
}
|
|
@@ -66676,6 +67520,9 @@ async function runCommanderDaemonCommand(args) {
|
|
|
66676
67520
|
}
|
|
66677
67521
|
}
|
|
66678
67522
|
function cliErrorMessage(error) {
|
|
67523
|
+
if (isRunnerLockConflictReportedError(error)) {
|
|
67524
|
+
return "";
|
|
67525
|
+
}
|
|
66679
67526
|
return signupPromptCancelMessage2(error) ?? userFacingErrorMessage(error, { includeSupportLine: true });
|
|
66680
67527
|
}
|
|
66681
67528
|
function signupPromptCancelMessage2(error) {
|
|
@@ -66963,7 +67810,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
66963
67810
|
};
|
|
66964
67811
|
}
|
|
66965
67812
|
function readAgentTokenTextFile(path2) {
|
|
66966
|
-
return
|
|
67813
|
+
return existsSync18(path2) ? readFileSync22(path2, "utf8") : void 0;
|
|
66967
67814
|
}
|
|
66968
67815
|
function rejectAgentRunnerTargetingFlags(values) {
|
|
66969
67816
|
const unsupportedFlags = [
|
|
@@ -67138,7 +67985,11 @@ async function parseRunnerArgs(args, deps = {
|
|
|
67138
67985
|
workspaceSlug: workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
|
|
67139
67986
|
runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
|
|
67140
67987
|
onboardingDiscovery,
|
|
67141
|
-
channelSession: void 0
|
|
67988
|
+
channelSession: void 0,
|
|
67989
|
+
lockTakeover: {
|
|
67990
|
+
takeOverWithoutPrompt: values.get("take-over") === true,
|
|
67991
|
+
forceNonInteractive: values.get("no-input") === true
|
|
67992
|
+
}
|
|
67142
67993
|
};
|
|
67143
67994
|
}
|
|
67144
67995
|
function runnerRuntimeDefaultsFromValues(values) {
|
|
@@ -67488,6 +68339,8 @@ Connection:
|
|
|
67488
68339
|
|
|
67489
68340
|
Workspace:
|
|
67490
68341
|
--workspace <slug> Workspace slug
|
|
68342
|
+
--take-over If another runner already holds this workspace, stop it and run here (no prompt)
|
|
68343
|
+
--no-input Never prompt; if the workspace is held, print the conflict and exit
|
|
67491
68344
|
|
|
67492
68345
|
Codex:
|
|
67493
68346
|
--cwd <path> Working directory for Codex, default current directory
|