@linzumi/cli 0.0.85-beta → 0.0.87-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 +1190 -332
- 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
|
}
|
|
@@ -11964,14 +12058,336 @@ var init_claudeCodePlanMirror = __esm({
|
|
|
11964
12058
|
}
|
|
11965
12059
|
});
|
|
11966
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
|
+
|
|
11967
12383
|
// src/claudeCodeLiveBashOutput.ts
|
|
11968
12384
|
import {
|
|
11969
|
-
mkdirSync as
|
|
12385
|
+
mkdirSync as mkdirSync3,
|
|
11970
12386
|
rmSync,
|
|
11971
12387
|
writeFileSync,
|
|
11972
12388
|
promises as fsPromises
|
|
11973
12389
|
} from "node:fs";
|
|
11974
|
-
import { join as
|
|
12390
|
+
import { join as join7 } from "node:path";
|
|
11975
12391
|
import { StringDecoder } from "node:string_decoder";
|
|
11976
12392
|
function shellSingleQuoted(value) {
|
|
11977
12393
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
@@ -12096,12 +12512,12 @@ function createClaudeCodeLiveBashCapture(host) {
|
|
|
12096
12512
|
isClaudeLiveBashWrappedCommand(command)) {
|
|
12097
12513
|
return void 0;
|
|
12098
12514
|
}
|
|
12099
|
-
const file =
|
|
12515
|
+
const file = join7(
|
|
12100
12516
|
host.captureDir,
|
|
12101
12517
|
`${toolUseId.replaceAll(/[^\w-]/g, "_")}.out`
|
|
12102
12518
|
);
|
|
12103
12519
|
try {
|
|
12104
|
-
|
|
12520
|
+
mkdirSync3(host.captureDir, { recursive: true });
|
|
12105
12521
|
writeFileSync(file, "");
|
|
12106
12522
|
} catch (error) {
|
|
12107
12523
|
host.log?.("claude_live_bash.capture_file_failed", {
|
|
@@ -12183,8 +12599,8 @@ var init_claudeCodeLiveBashOutput = __esm({
|
|
|
12183
12599
|
|
|
12184
12600
|
// src/claudeCodeSession.ts
|
|
12185
12601
|
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
12186
|
-
import { homedir as
|
|
12187
|
-
import { join as
|
|
12602
|
+
import { homedir as homedir6 } from "node:os";
|
|
12603
|
+
import { join as join8 } from "node:path";
|
|
12188
12604
|
function claudeCodeSettingSources() {
|
|
12189
12605
|
return ["user", "project", "local"];
|
|
12190
12606
|
}
|
|
@@ -12328,7 +12744,7 @@ function claudeCodeRateLimitSummaryText(rateLimit, nowMs) {
|
|
|
12328
12744
|
async function probeClaudeCodeAvailability(args) {
|
|
12329
12745
|
if (!hasClaudeCodeAuthHint(process.env, {
|
|
12330
12746
|
cwd: args.cwd,
|
|
12331
|
-
homeDir:
|
|
12747
|
+
homeDir: homedir6(),
|
|
12332
12748
|
platform: process.platform,
|
|
12333
12749
|
fileExists: existsSync4,
|
|
12334
12750
|
readTextFile: readTextFileIfPresent
|
|
@@ -12361,7 +12777,7 @@ async function probeClaudeCodeAvailability(args) {
|
|
|
12361
12777
|
}
|
|
12362
12778
|
}
|
|
12363
12779
|
function hasClaudeCodeAuthHint(env, deps) {
|
|
12364
|
-
const configDir = env.CLAUDE_CONFIG_DIR ??
|
|
12780
|
+
const configDir = env.CLAUDE_CONFIG_DIR ?? join8(deps.homeDir, ".claude");
|
|
12365
12781
|
return hasAnthropicCredentialEnv(env) || hasClaudeCloudProviderEnv(env) || hasClaudeCodeFileCredential(configDir, deps) || hasClaudeCodeApiKeyHelper(configDir, deps) || hasMacClaudeCodeKeychainAnchor(deps);
|
|
12366
12782
|
}
|
|
12367
12783
|
function claudeCodePolicyDeferHooks() {
|
|
@@ -12396,14 +12812,14 @@ function hasClaudeCloudProviderEnv(env) {
|
|
|
12396
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);
|
|
12397
12813
|
}
|
|
12398
12814
|
function hasClaudeCodeFileCredential(configDir, deps) {
|
|
12399
|
-
return deps.fileExists(
|
|
12815
|
+
return deps.fileExists(join8(configDir, ".credentials.json")) || deps.fileExists(join8(configDir, ".claude.json")) || deps.fileExists(join8(deps.homeDir, ".claude.json"));
|
|
12400
12816
|
}
|
|
12401
12817
|
function hasClaudeCodeApiKeyHelper(configDir, deps) {
|
|
12402
12818
|
return [
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
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")
|
|
12407
12823
|
].some((path2) => settingsFileHasApiKeyHelper(path2, deps));
|
|
12408
12824
|
}
|
|
12409
12825
|
function settingsFileHasApiKeyHelper(path2, deps) {
|
|
@@ -12423,9 +12839,9 @@ function hasMacClaudeCodeKeychainAnchor(deps) {
|
|
|
12423
12839
|
return false;
|
|
12424
12840
|
}
|
|
12425
12841
|
return [
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
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")
|
|
12429
12845
|
].some((path2) => deps.fileExists(path2));
|
|
12430
12846
|
}
|
|
12431
12847
|
function readTextFileIfPresent(path2) {
|
|
@@ -12657,7 +13073,16 @@ async function* defaultClaudeCodeRunner(options) {
|
|
|
12657
13073
|
options.wrapApprovedToolInput
|
|
12658
13074
|
)
|
|
12659
13075
|
},
|
|
12660
|
-
...options.model === void 0 ? {} : { model: options.model }
|
|
13076
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
13077
|
+
...options.permissionMode === void 0 ? {} : {
|
|
13078
|
+
permissionMode: options.permissionMode,
|
|
13079
|
+
// The SDK requires this safety acknowledgement when a session is
|
|
13080
|
+
// constructed in 'bypassPermissions' (sdk.d.ts Options:
|
|
13081
|
+
// "Must be set to true when using permissionMode:
|
|
13082
|
+
// 'bypassPermissions'"). It is the intentional-bypass flag, only
|
|
13083
|
+
// meaningful for that mode; harmless for the others.
|
|
13084
|
+
...options.permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}
|
|
13085
|
+
}
|
|
12661
13086
|
// exactOptionalPropertyTypes: the conditional spreads build a union the
|
|
12662
13087
|
// SDK's Options cannot absorb verbatim; the shape is correct field-wise.
|
|
12663
13088
|
}
|
|
@@ -13076,207 +13501,6 @@ var init_claudeCodeSession = __esm({
|
|
|
13076
13501
|
}
|
|
13077
13502
|
});
|
|
13078
13503
|
|
|
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
13504
|
// src/mcpConfig.ts
|
|
13281
13505
|
function linzumiMcpServerConfig(options) {
|
|
13282
13506
|
return {
|
|
@@ -13403,6 +13627,76 @@ var init_mcpConfig = __esm({
|
|
|
13403
13627
|
}
|
|
13404
13628
|
});
|
|
13405
13629
|
|
|
13630
|
+
// src/engineParentDeathWatchdog.ts
|
|
13631
|
+
function encodeParentDeathWatchdogConfig(config) {
|
|
13632
|
+
return JSON.stringify(config);
|
|
13633
|
+
}
|
|
13634
|
+
function parentDeathWatchdogProgram() {
|
|
13635
|
+
return `
|
|
13636
|
+
const { spawn } = require('node:child_process');
|
|
13637
|
+
const config = JSON.parse(process.argv[process.argv.length - 1]);
|
|
13638
|
+
const originalParentPid = process.ppid;
|
|
13639
|
+
const child = spawn(config.command, config.args, { stdio: 'inherit' });
|
|
13640
|
+
let done = false;
|
|
13641
|
+
function shutdown(signal) {
|
|
13642
|
+
if (done) return;
|
|
13643
|
+
done = true;
|
|
13644
|
+
try { process.kill(-process.pid, signal); }
|
|
13645
|
+
catch (_e) { try { child.kill(signal); } catch (_e2) {} }
|
|
13646
|
+
}
|
|
13647
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
13648
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
13649
|
+
process.on('SIGHUP', () => shutdown('SIGTERM'));
|
|
13650
|
+
child.on('exit', (code, signal) => {
|
|
13651
|
+
clearInterval(watchdog);
|
|
13652
|
+
// The child is gone; the wrapper must follow it down rather than linger as an
|
|
13653
|
+
// orphan. On the forwarded-signal path (shutdown() above caught SIGINT/SIGTERM
|
|
13654
|
+
// /SIGHUP and propagated it to the child), the wrapper still has its own
|
|
13655
|
+
// handler installed for that signal, so a bare process.kill(self, signal)
|
|
13656
|
+
// would just re-enter shutdown() - a no-op once done is set - and the wrapper
|
|
13657
|
+
// would never exit. Remove the handlers first so the signal's DEFAULT
|
|
13658
|
+
// disposition (terminate) applies, re-raise to mirror the child's death
|
|
13659
|
+
// signal, then exit() as a belt-and-suspenders fallback in case re-raising
|
|
13660
|
+
// does not terminate (e.g. a signal with no default-terminate disposition).
|
|
13661
|
+
process.removeAllListeners('SIGINT');
|
|
13662
|
+
process.removeAllListeners('SIGTERM');
|
|
13663
|
+
process.removeAllListeners('SIGHUP');
|
|
13664
|
+
if (signal) { try { process.kill(process.pid, signal); } catch (_e) {} }
|
|
13665
|
+
process.exit(signal ? 1 : code == null ? 0 : code);
|
|
13666
|
+
});
|
|
13667
|
+
child.on('error', (err) => {
|
|
13668
|
+
process.stderr.write('engine watchdog spawn failed: ' + (err && err.message) + '\\n');
|
|
13669
|
+
process.exit(1);
|
|
13670
|
+
});
|
|
13671
|
+
const watchdog = setInterval(() => {
|
|
13672
|
+
// Inlined parentDeathWatchdogShouldReap (keep in sync): reap PURELY on a ppid
|
|
13673
|
+
// change from boot - covers reparent-to-subreaper AND reparent-to-init. NO
|
|
13674
|
+
// \`=== 1\` arm: a runner that is PID 1 captures originalParentPid === 1, so
|
|
13675
|
+
// such an arm would false-fire on the first poll and kill a healthy codex.
|
|
13676
|
+
if (process.ppid !== originalParentPid) {
|
|
13677
|
+
try { child.kill('SIGKILL'); } catch (_e) {}
|
|
13678
|
+
try { process.kill(-process.pid, 'SIGKILL'); } catch (_e) {}
|
|
13679
|
+
process.exit(0);
|
|
13680
|
+
}
|
|
13681
|
+
}, config.pollIntervalMs);
|
|
13682
|
+
`.trim();
|
|
13683
|
+
}
|
|
13684
|
+
function parentDeathWatchdogSpawn(nodeExecPath, config) {
|
|
13685
|
+
return {
|
|
13686
|
+
command: nodeExecPath,
|
|
13687
|
+
args: [
|
|
13688
|
+
"-e",
|
|
13689
|
+
parentDeathWatchdogProgram(),
|
|
13690
|
+
encodeParentDeathWatchdogConfig(config)
|
|
13691
|
+
]
|
|
13692
|
+
};
|
|
13693
|
+
}
|
|
13694
|
+
var init_engineParentDeathWatchdog = __esm({
|
|
13695
|
+
"src/engineParentDeathWatchdog.ts"() {
|
|
13696
|
+
"use strict";
|
|
13697
|
+
}
|
|
13698
|
+
});
|
|
13699
|
+
|
|
13406
13700
|
// src/codexAppServer.ts
|
|
13407
13701
|
import {
|
|
13408
13702
|
spawn as spawn2
|
|
@@ -13444,13 +13738,18 @@ async function startCodexAppServerAttempt(codexBin, cwd, options, attempt) {
|
|
|
13444
13738
|
let stderrText = "";
|
|
13445
13739
|
const configuredStdio = codexAppServerStdio(process.stdout.isTTY === true);
|
|
13446
13740
|
const stdio = [configuredStdio[0], configuredStdio[1], "pipe"];
|
|
13741
|
+
const watchdogSpawn = parentDeathWatchdogSpawn(process.execPath, {
|
|
13742
|
+
command: codexBin,
|
|
13743
|
+
args,
|
|
13744
|
+
pollIntervalMs: codexAppServerWatchdogPollMs
|
|
13745
|
+
});
|
|
13447
13746
|
writeCliAuditEvent("process.spawn", {
|
|
13448
13747
|
command: codexBin,
|
|
13449
13748
|
args,
|
|
13450
13749
|
cwd,
|
|
13451
13750
|
purpose: "codex.app_server"
|
|
13452
13751
|
});
|
|
13453
|
-
const child = spawn2(
|
|
13752
|
+
const child = spawn2(watchdogSpawn.command, [...watchdogSpawn.args], {
|
|
13454
13753
|
cwd,
|
|
13455
13754
|
env: codexAppServerEnv(options.env, options.inheritEnv ?? true),
|
|
13456
13755
|
stdio,
|
|
@@ -13467,7 +13766,17 @@ async function startCodexAppServerAttempt(codexBin, cwd, options, attempt) {
|
|
|
13467
13766
|
}
|
|
13468
13767
|
});
|
|
13469
13768
|
}
|
|
13470
|
-
const
|
|
13769
|
+
const registered = registerEngineChild({
|
|
13770
|
+
stop: () => stopCodexAppServerProcess(child),
|
|
13771
|
+
pid: child.pid,
|
|
13772
|
+
ownProcessGroup: true,
|
|
13773
|
+
kind: "codex.app_server"
|
|
13774
|
+
});
|
|
13775
|
+
child.once("exit", () => registered.unregister());
|
|
13776
|
+
const stop = () => {
|
|
13777
|
+
registered.unregister();
|
|
13778
|
+
stopCodexAppServerProcess(child);
|
|
13779
|
+
};
|
|
13471
13780
|
writeCliAuditEvent("process.spawned", {
|
|
13472
13781
|
command: codexBin,
|
|
13473
13782
|
args,
|
|
@@ -13571,7 +13880,7 @@ function stopCodexAppServerProcess(child, killProcess = process.kill) {
|
|
|
13571
13880
|
child.kill("SIGINT");
|
|
13572
13881
|
}
|
|
13573
13882
|
function logProcessGroupSignalFailure(pid, error) {
|
|
13574
|
-
const code =
|
|
13883
|
+
const code = processSignalErrorCode2(error);
|
|
13575
13884
|
const message = error instanceof Error ? error.message : String(error);
|
|
13576
13885
|
const event = code === "EPERM" ? "process.group_signal_denied" : code === "ESRCH" ? "process.group_signal_missing" : "process.group_signal_failed";
|
|
13577
13886
|
writeCliAuditEvent(event, {
|
|
@@ -13589,7 +13898,7 @@ function logProcessGroupSignalFailure(pid, error) {
|
|
|
13589
13898
|
);
|
|
13590
13899
|
}
|
|
13591
13900
|
}
|
|
13592
|
-
function
|
|
13901
|
+
function processSignalErrorCode2(error) {
|
|
13593
13902
|
if (error !== null && typeof error === "object" && "code" in error) {
|
|
13594
13903
|
const code = error.code;
|
|
13595
13904
|
return typeof code === "string" ? code : void 0;
|
|
@@ -13973,13 +14282,16 @@ function readyzUrlForWebsocket(websocketUrl) {
|
|
|
13973
14282
|
parsed.hash = "";
|
|
13974
14283
|
return parsed.toString();
|
|
13975
14284
|
}
|
|
13976
|
-
var blockedCodexAppServerEnvKeys;
|
|
14285
|
+
var codexAppServerWatchdogPollMs, blockedCodexAppServerEnvKeys;
|
|
13977
14286
|
var init_codexAppServer = __esm({
|
|
13978
14287
|
"src/codexAppServer.ts"() {
|
|
13979
14288
|
"use strict";
|
|
13980
14289
|
init_protocol();
|
|
13981
14290
|
init_runnerLogger();
|
|
13982
14291
|
init_mcpConfig();
|
|
14292
|
+
init_engineChildReaper();
|
|
14293
|
+
init_engineParentDeathWatchdog();
|
|
14294
|
+
codexAppServerWatchdogPollMs = 2e3;
|
|
13983
14295
|
blockedCodexAppServerEnvKeys = [
|
|
13984
14296
|
"LINZUMI_MCP_ACCESS_TOKEN",
|
|
13985
14297
|
"LINZUMI_MCP_OWNER_USERNAME"
|
|
@@ -14689,8 +15001,8 @@ function startCallbackServer(args) {
|
|
|
14689
15001
|
}
|
|
14690
15002
|
resolveCallback?.({ code, state });
|
|
14691
15003
|
writeOauthResult(response, {
|
|
14692
|
-
title: "
|
|
14693
|
-
body: "
|
|
15004
|
+
title: "Computer authorized",
|
|
15005
|
+
body: "This computer has been authorized for agentic use in Linzumi. Feel free to close this tab and let the terminal finish setup.",
|
|
14694
15006
|
status: 200
|
|
14695
15007
|
});
|
|
14696
15008
|
} catch (error) {
|
|
@@ -14733,29 +15045,108 @@ function writeOauthResult(response, args) {
|
|
|
14733
15045
|
response.end(oauthResultHtml(args));
|
|
14734
15046
|
}
|
|
14735
15047
|
function oauthResultHtml(args) {
|
|
15048
|
+
const success = args.status >= 200 && args.status < 300;
|
|
15049
|
+
const badgeClass = success ? "badge badge--success" : "badge badge--notice";
|
|
15050
|
+
const badgeMark = success ? '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" class="mark"><path fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" d="M5 12.5l4.2 4.2L19 7"/></svg>' : '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" class="mark"><path fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" d="M12 7v6M12 17h.01"/></svg>';
|
|
14736
15051
|
return `<!doctype html>
|
|
14737
|
-
<html>
|
|
15052
|
+
<html lang="en">
|
|
14738
15053
|
<head>
|
|
14739
15054
|
<meta charset="utf-8" />
|
|
14740
15055
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
14741
15056
|
<title>${escapeHtml(args.title)}</title>
|
|
14742
15057
|
<style>
|
|
14743
|
-
:root {
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
|
|
14747
|
-
|
|
15058
|
+
:root {
|
|
15059
|
+
color-scheme: light dark;
|
|
15060
|
+
/* Linzumi brand tokens (light) \u2014 kandan/server_v2/web/src/ui/theme/tokens.css */
|
|
15061
|
+
--page-bg: #f5f2ee;
|
|
15062
|
+
--surface: #fcfaf7;
|
|
15063
|
+
--foreground: #1e1b18;
|
|
15064
|
+
--muted: #5c5550;
|
|
15065
|
+
--divider: rgba(138, 148, 144, 0.16);
|
|
15066
|
+
--accent: #c4897a;
|
|
15067
|
+
--success: #5a9a6a;
|
|
15068
|
+
--success-soft: rgba(90, 154, 106, 0.1);
|
|
15069
|
+
--notice-soft: rgba(196, 137, 122, 0.1);
|
|
15070
|
+
--shadow: 0 12px 40px rgba(106, 138, 122, 0.12), 0 4px 12px rgba(106, 138, 122, 0.06);
|
|
15071
|
+
}
|
|
14748
15072
|
@media (prefers-color-scheme: dark) {
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
15073
|
+
:root {
|
|
15074
|
+
/* Linzumi brand tokens (dark) */
|
|
15075
|
+
--page-bg: #171b19;
|
|
15076
|
+
--surface: #202522;
|
|
15077
|
+
--foreground: rgba(245, 241, 232, 0.95);
|
|
15078
|
+
--muted: rgba(245, 241, 232, 0.68);
|
|
15079
|
+
--divider: rgba(245, 241, 232, 0.1);
|
|
15080
|
+
--accent: #c8a0b0;
|
|
15081
|
+
--success: #72c488;
|
|
15082
|
+
--success-soft: rgba(114, 196, 136, 0.14);
|
|
15083
|
+
--notice-soft: rgba(200, 160, 176, 0.14);
|
|
15084
|
+
--shadow: 0 18px 50px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.35);
|
|
15085
|
+
}
|
|
15086
|
+
}
|
|
15087
|
+
* { box-sizing: border-box; }
|
|
15088
|
+
body {
|
|
15089
|
+
margin: 0;
|
|
15090
|
+
min-height: 100vh;
|
|
15091
|
+
display: grid;
|
|
15092
|
+
place-items: center;
|
|
15093
|
+
padding: 24px;
|
|
15094
|
+
background:
|
|
15095
|
+
radial-gradient(1200px 600px at 50% -10%, var(--notice-soft), transparent 60%),
|
|
15096
|
+
var(--page-bg);
|
|
15097
|
+
color: var(--foreground);
|
|
15098
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
15099
|
+
-webkit-font-smoothing: antialiased;
|
|
15100
|
+
}
|
|
15101
|
+
main {
|
|
15102
|
+
width: min(520px, 100%);
|
|
15103
|
+
border: 1px solid var(--divider);
|
|
15104
|
+
border-radius: 16px;
|
|
15105
|
+
background: var(--surface);
|
|
15106
|
+
padding: 40px 36px;
|
|
15107
|
+
box-shadow: var(--shadow);
|
|
15108
|
+
text-align: center;
|
|
15109
|
+
}
|
|
15110
|
+
.logo {
|
|
15111
|
+
width: 44px;
|
|
15112
|
+
height: 44px;
|
|
15113
|
+
margin: 28px auto 0;
|
|
15114
|
+
color: var(--accent);
|
|
15115
|
+
}
|
|
15116
|
+
.logo svg { display: block; width: 100%; height: 100%; }
|
|
15117
|
+
.badge {
|
|
15118
|
+
width: 64px;
|
|
15119
|
+
height: 64px;
|
|
15120
|
+
margin: 0 auto 22px;
|
|
15121
|
+
display: grid;
|
|
15122
|
+
place-items: center;
|
|
15123
|
+
border-radius: 999px;
|
|
15124
|
+
}
|
|
15125
|
+
.badge--success { background: var(--success-soft); color: var(--success); }
|
|
15126
|
+
.badge--notice { background: var(--notice-soft); color: var(--accent); }
|
|
15127
|
+
.mark { width: 32px; height: 32px; }
|
|
15128
|
+
h1 {
|
|
15129
|
+
margin: 0 0 12px;
|
|
15130
|
+
font-size: 23px;
|
|
15131
|
+
font-weight: 600;
|
|
15132
|
+
line-height: 1.3;
|
|
15133
|
+
letter-spacing: -0.01em;
|
|
15134
|
+
color: var(--foreground);
|
|
15135
|
+
}
|
|
15136
|
+
p {
|
|
15137
|
+
margin: 0;
|
|
15138
|
+
color: var(--muted);
|
|
15139
|
+
font-size: 15px;
|
|
15140
|
+
line-height: 1.6;
|
|
14752
15141
|
}
|
|
14753
15142
|
</style>
|
|
14754
15143
|
</head>
|
|
14755
15144
|
<body>
|
|
14756
15145
|
<main>
|
|
15146
|
+
<div class="${badgeClass}">${badgeMark}</div>
|
|
14757
15147
|
<h1>${escapeHtml(args.title)}</h1>
|
|
14758
15148
|
<p>${escapeHtml(args.body)}</p>
|
|
15149
|
+
<div class="logo">${LINZUMI_LOGO_SVG}</div>
|
|
14759
15150
|
</main>
|
|
14760
15151
|
</body>
|
|
14761
15152
|
</html>`;
|
|
@@ -14786,10 +15177,12 @@ function openBrowser(url) {
|
|
|
14786
15177
|
});
|
|
14787
15178
|
});
|
|
14788
15179
|
}
|
|
15180
|
+
var LINZUMI_LOGO_SVG;
|
|
14789
15181
|
var init_oauth = __esm({
|
|
14790
15182
|
"src/oauth.ts"() {
|
|
14791
15183
|
"use strict";
|
|
14792
15184
|
init_runnerLogger();
|
|
15185
|
+
LINZUMI_LOGO_SVG = '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path fill="currentColor" d="M206.7,82.37l2.55-6.45,2.01-3.63c.67-1.21,1.07-1.88,2.55-3.63,1.34-1.61,3.36-4.43,5.64-6.58,2.42-2.24,5.06-4.3,7.93-6.18,3.92-2.53,8.17-4.52,12.63-5.91,1.79-.63,4.03-1.16,6.72-1.61,6.1-.88,12.29-.92,18.4-.13,2.69.45,5.06,1.03,7.12,1.75,2.15.67,3.49,1.07,5.64,2.15,4.55,2.16,8.87,4.77,12.9,7.79,1.61,1.34,2.42,2.15,4.16,4.16l5.91,7.66c1.34,2.01,1.34,2.28,2.15,4.03l2.55,6.58c.67,1.34-.13,1.34,1.07.94,1.21-.27,4.3-2.01,6.45-2.82,4.7-1.83,9.62-3,14.64-3.49,2.42-.27,3.76-.54,7.12-.27s9.27,1.21,12.76,2.15c3.63.94,6.58,2.55,8.73,3.49,2.15,1.07,2.55,1.48,4.16,2.55,3.49,2.51,6.77,5.29,9.81,8.33,1.52,1.7,3.04,3.58,4.57,5.64,2.74,4.49,4.86,9.32,6.31,14.37.67,2.28,1.07,3.49,1.34,6.72.27,3.22,0,10.48.4,12.76.27,2.28.27.67,1.61.94,1.48.27,4.3,0,6.72.4,2.42.27,4.97.85,7.66,1.75,6.76,2.32,13.01,5.92,18.4,10.61,2.28,1.75,2.96,2.69,4.3,4.3,3.51,3.93,6.32,8.43,8.33,13.3,1.07,2.15,1.21,2.28,1.88,5.1.54,2.96,1.61,8.19,1.61,12.36s-.81,9.67-1.34,12.76c-.67,3.22-1.07,4.03-2.15,6.18-2.16,4.54-4.81,8.82-7.93,12.76-1.25,1.61-2.6,3-4.03,4.16-1.48,1.34-2.69,2.42-4.7,3.76-3.79,2.34-7.74,4.4-11.82,6.18l-6.58,2.15c-2.06.45-4.12.72-6.18.81l-7.25.13c-2.28,0-2.82.27-6.58-.4-3.76-.81-11.69-2.82-15.99-4.3-4.16-1.34-6.31-2.69-9.67-4.43-3.49-1.75-10.48-6.18-12.9-7.39-8.26-4.54-16.78-8.58-25.52-12.09-4.3-1.75-9.27-3.22-11.82-4.03-2.55-.81-5.24-1.61-7.66-2.15-8.39-2.25-16.99-3.6-25.66-4.03-3.9-.27-4.84-.27-8.19-.13-3.67-.09-7.79.04-12.36.4-7.9.79-15.75,2.04-23.51,3.76-8.37,2.26-16.62,4.95-24.72,8.06-3.22,1.34-2.82.81-9.13,4.16-6.31,3.36-22.03,12.22-28.75,15.58-6.72,3.36-8.73,3.49-11.82,4.43-3.09,1.07-4.3,1.21-6.58,1.61-7.3,1.09-14.71,1.18-22.03.27-2.55-.54-5.78-1.61-7.79-2.28-2.01-.54-2.15-.67-4.03-1.75-2.02-1.07-5.64-3.22-7.79-4.7-3.24-2.15-6.14-4.77-8.6-7.79-1.34-1.52-2.69-3.36-4.03-5.51-1.52-2.51-2.96-5.24-4.3-8.19-2.03-5.46-3.03-11.24-2.96-17.06,0-2.96.13-6.72.67-9.67.54-2.87,1.34-5.6,2.42-8.19,4.24-10.52,12.04-19.22,22.03-24.58,5.19-2.98,10.88-4.98,16.79-5.91,2.55-.4,5.37-.13,6.72-.4,1.34-.27,1.34,1.34,1.61-.94.4-2.28,0-9.4.4-12.76.27-3.36.81-4.97,1.48-7.25,1.87-6.19,4.88-11.97,8.87-17.06,3.47-3.98,7.39-7.54,11.69-10.61,4.43-2.61,9.11-4.77,13.97-6.45,2.55-.67,6.99-1.34,9.13-1.75,2.15-.27,1.88-.13,3.63-.13,1.79-.09,4.03,0,6.72.27,2.69.18,5.73.72,9.13,1.61,3.49,1.07,9.27,4.03,11.28,4.7s.4.27,1.07-.94ZM512.27,255.7c0,141.39-114.62,256-256,256S.27,397.09.27,255.7,114.89-.3,256.27-.3s256,114.62,256,256ZM449.33,352.66c14.67-29.16,22.94-62.09,22.94-96.95,0-119.29-96.71-216-216-216S40.27,136.41,40.27,255.7c0,41.89,11.94,80.98,32.57,114.09,1.7,1.79,2.94.65,3.05-.57.27-1.61,2.75-20.85,4.43-27.27,1.87-7.6,4.52-14.98,7.93-22.03,1.75-3.49,3.76-7.93,6.18-11.82,8.77-13.95,19.75-26.38,32.51-36.81,7.24-5.32,14.87-10.08,22.84-14.24,9.74-4.78,20.09-8.22,30.76-10.21,4.03-.99,7.61-1.66,10.75-2.02,3.36-.54,5.78-.54,9.27-.94,3.49-.54,9.32-1.16,11.82-2.01,6.65-2.27,5.64-6.72,3.09-7.52-2.55-.81-9.67-1.88-13.84-2.96-4.16-1.21-8.73-2.82-11.28-3.76-2.55-.94-2.28-.94-3.9-2.02l-5.91-4.03c-.94-.94-1.34-.94.54-2.02l10.21-5.51c2.01-1.07,1.75-.67,3.09-.13,1.34.67,4.3,2.42,6.72,3.36,7.2,2.69,14.96,3.52,22.57,2.42,1.34-.27,1.34-.54,1.61-1.21.27-.67.81-.94-.13-2.69-.94-1.88-4.43-6.58-5.64-8.46l-1.21-2.28c-.27-.45-1.07-1.07,1.21-1.75,2.42-.54,10.48-2.58,12.9-2.82.86-.09.67,0,1.75,1.07,1.07,1.07,3.09,3.63,4.84,5.24,1.88,1.61,4.43,3.22,6.18,4.3,2.8,1.71,5.99,2.68,9.27,2.82,1.88,0,4.97-.13,7.12-.67,2.15-.4,3.63-.94,5.64-2.02,2.01-1.07,4.7-2.69,6.72-4.43,2.01-1.75,3.49-5.1,5.64-6.04,2.15-.81,4.97.54,7.12.94,2.28.4,4.97,1.21,6.18,1.61,1.21.4,2.01.4.67,2.28l-8.06,10.34c-1.34,1.88.13,5.51,1.21,5.64l5.64.27c2.28-.13,4.43.13,7.66-.67,3.36-.81,9.27-2.96,11.82-3.9,2.69-.94,3.22-1.34,4.16-2.01.81-.54,1.39-1.03,1.75-1.48.45-.27.67-1.07,2.82,0s9.4,4.97,11.28,6.18c2.01,1.21,1.07.94,0,1.75l-5.51,3.9c-1.48.94-1.07.94-3.22,1.88-2.15,1.07-6.58,2.96-9.67,4.03s-4.03,1.75-8.73,2.69c-4.7.81-14.51,1.75-19.48,2.55-4.97.81-7.93,1.34-10.34,2.01-2.28.67-2.15.94-3.49,1.88-1.48,1.07-2.69,1.61-4.97,3.9-2.28,2.28-5.91,6.85-8.73,9.81-2.82,2.96-6.45,6.04-8.33,7.79-2.02,1.61-1.61,1.34-3.36,2.42l-6.99,4.03-4.03,2.02-6.72,2.15c-2.42.67-3.9,1.21-7.66,2.01l-14.91,2.82c-3.63.81-4.3,1.07-7.12,2.01-2.96,1.07-6.22,2.46-9.81,4.16-3.22,1.43-6.81,3.45-10.75,6.04-13.6,9.14-24.61,21.63-31.97,36.27-2.01,3.76-2.96,6.58-3.9,9.27-1.07,2.69-1.61,4.03-2.28,6.58-.67,2.69-1.48,5.24-2.15,9.27-.54,4.16-1.48,11.15-1.75,15.45-.27,4.16-.27,6.04,0,10.21l1.88,14.91c.67,3.76,1.48,5.91,2.15,8.19l1.88,5.64c.99,2.6,2.42,5.78,4.3,9.54,2.15,3.76,5.37,9.13,8.06,13.03,10.08,14.51,23.81,26.36,40.17,33.05,22.55,9.22,43.32,10.46,61.89,10.46,3.25,0,6.49-.09,9.71-.23,3.07-.42,2.74-6.09,10.48-9.42,5.26-2.26,13.7-2.42,14.91-2.55,1.07-.13-5.24-4.57-7.79-6.45-2.55-1.88-4.97-4.3-6.72-5.91-1.75-1.75-2.42-2.28-3.9-4.16-1.61-1.88-3.9-5.51-5.24-7.12-1.34-1.61-1.48-2.28-2.55-2.55-.98-.27-2.37-.09-4.16.54-1.88.4-5.37,1.21-7.12,1.88-1.75.67-2.02.81-3.09,2.01-1.07,1.07-2.69,3.76-3.63,4.7-.81.94-.67.67-1.48.94-.81.27-2.42.67-3.63.54-1.34-.18-2.55-.58-3.63-1.21-.81-.81-1.48-2.42-1.88-3.22-.4-.94-.54-1.21-.54-2.15l.4-3.49c.18-1.52.72-3.27,1.61-5.24.94-1.75,1.88-3.49,4.16-5.64,2.15-2.02,7.52-5.51,9.13-6.85,1.61-1.34,1.21-2.15.54-3.63-2.69-5.16-6.33-9.77-10.75-13.57-2.42-1.88-5.51-3.22-7.79-4.3-2.15-1.21-2.01-1.21-5.78-2.42-3.76-1.07-13.16-3.63-16.79-4.84-3.63-1.07-2.82-.94-5.1-2.01l-8.19-4.3c-1.75-.94-1.34-.54-2.69-1.61l-5.51-4.57c-2.18-1.93-3.77-4.45-4.57-7.25-.45-1.52-.45-2.87,0-4.03.4-1.34,0-1.21,2.01-3.63,2.15-2.42,7.25-7.93,10.21-10.75,3.09-2.82,4.97-4.03,7.79-6.04,5.43-3.82,11.09-7.32,16.93-10.48,2.42-1.21,5.37-2.02,7.12-2.69,1.61-.81,2.64-1.43,3.09-1.88.54-.4.67.54.4-.94-.4-1.48-1.88-4.97-2.28-7.66-.4-2.82-.4-6.18,0-8.73.36-2.42,1.03-4.66,2.01-6.72,1.16-2.24,2.46-4.3,3.9-6.18,1.34-1.7,2.91-3.04,4.7-4.03,1.75-1.07,4.3-2.01,6.18-2.42,1.61-.45,3.13-.58,4.57-.4l4.57.27c1.34.18,2.55.49,3.63.94,1.07.54,2.55,1.48,3.09,2.01.4.67.67.13-.27,1.61-1.07,1.48-4.16,4.57-5.78,7.12-1.61,2.69-2.96,5.6-4.03,8.73-.94,3.22-1.88,8.73-2.01,10.75-.27,2.15,4.7,1.75,5.1.54.54-1.07.27-2.55,1.07-5.1.81-2.42,2.42-7.25,3.9-9.81,1.34-2.55,2.82-3.76,4.57-5.51,1.79-1.88,3.67-3.45,5.64-4.7,1.88-1.21,3.22-1.75,5.64-2.28,2.42-.4,6.18-.67,8.73-.67,2.42,0,3.9.13,6.18.94,2.15.81,5.24,2.69,7.12,4.03,1.79,1.52,3.22,3.09,4.3,4.7,1.21,1.61,1.88,2.96,2.55,5.1.67,2.15,1.34,5.1,1.34,7.66,0,2.69-.54,6.04-1.21,8.19-.63,2.24-1.43,4.03-2.42,5.37-.67,1.34-.94,1.61-2.15,2.82-1.21,1.34-3.22,3.22-4.97,4.57-1.75,1.21-4.43,2.28-5.51,3.22-.94.81-.4,2.51.4,2.42.94.13,1.75.4,4.57-.13,2.82-.4,8.33-2.15,12.22-2.82,11.99-2.63,24.41-2.63,36.4,0,3.22.54,4.16,1.07,6.31,1.75l6.04,2.28c2.42,1.07,5.51,2.42,8.19,4.16,8.15,5.36,15.14,12.3,20.55,20.42,1.61,2.42,2.87,4.66,3.76,6.72,1.85,4.51,3.42,9.13,4.7,13.84.67,2.82,1.21,4.43,1.61,8.19.4,3.76.4,11.82.54,14.37.27,2.55-.13,2.28,1.75,1.34,1.75-.94,20.09-7.74,30.63-31.57,9.16-20.71,1.9-47.26-2.28-55.34-1.75-3.22-4.16-6.99-6.31-9.81-4.44-5.14-9.6-9.62-15.31-13.3-2.96-1.7-5.55-3.09-7.79-4.16-4.33-1.6-8.77-2.86-13.3-3.76l-6.18-.4h-14.37c-2.69-.27-3.1-3.18-.54-4.3,3.64-1.58,9.88-2.63,18.54-2.42,8.78.21,14.91,1.88,17.33,2.55,13.98,3.94,30.19,16.65,38.55,30.76,6.61,11.16,9.81,29.96,9.81,29.96.37,1.75,2.18,1.66,3.08.35ZM232.78,345.3c-3.91,0-7.08,3.17-7.08,7.08s3.17,7.08,7.08,7.08,7.08-3.17,7.08-7.08-3.17-7.08-7.08-7.08Z"/></svg>';
|
|
14793
15186
|
}
|
|
14794
15187
|
});
|
|
14795
15188
|
|
|
@@ -15502,7 +15895,7 @@ var init_remoteCodexExecutionContext = __esm({
|
|
|
15502
15895
|
"SHELL",
|
|
15503
15896
|
"TMPDIR"
|
|
15504
15897
|
]);
|
|
15505
|
-
ignoredEnvOverrideKeys = /* @__PURE__ */ new Set(["PATH"]);
|
|
15898
|
+
ignoredEnvOverrideKeys = /* @__PURE__ */ new Set(["PATH", "GIT_PAGER"]);
|
|
15506
15899
|
deniedEnvKeys = /* @__PURE__ */ new Set([
|
|
15507
15900
|
"BASH_ENV",
|
|
15508
15901
|
"ENV",
|
|
@@ -18127,7 +18520,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
18127
18520
|
var init_version = __esm({
|
|
18128
18521
|
"src/version.ts"() {
|
|
18129
18522
|
"use strict";
|
|
18130
|
-
linzumiCliVersion = "0.0.
|
|
18523
|
+
linzumiCliVersion = "0.0.87-beta";
|
|
18131
18524
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
18132
18525
|
}
|
|
18133
18526
|
});
|
|
@@ -18406,6 +18799,9 @@ function releaseRunnerLock(path2, record) {
|
|
|
18406
18799
|
}
|
|
18407
18800
|
}
|
|
18408
18801
|
function readRunnerLockForRelease(path2) {
|
|
18802
|
+
return readRunnerLockOwner(path2);
|
|
18803
|
+
}
|
|
18804
|
+
function readRunnerLockOwner(path2) {
|
|
18409
18805
|
try {
|
|
18410
18806
|
return readRunnerLockIfPresent(path2);
|
|
18411
18807
|
} catch (_error) {
|
|
@@ -18526,8 +18922,216 @@ var init_runnerLock = __esm({
|
|
|
18526
18922
|
}
|
|
18527
18923
|
});
|
|
18528
18924
|
|
|
18529
|
-
// src/
|
|
18925
|
+
// src/runnerLockTakeover.ts
|
|
18926
|
+
function runnerLockTakeoverSleep(ms) {
|
|
18927
|
+
return new Promise((resolve12) => {
|
|
18928
|
+
setTimeout(resolve12, ms);
|
|
18929
|
+
});
|
|
18930
|
+
}
|
|
18931
|
+
function runnerLockConflictReport(baseMessage) {
|
|
18932
|
+
const bar = "=".repeat(64);
|
|
18933
|
+
return [
|
|
18934
|
+
"",
|
|
18935
|
+
bar,
|
|
18936
|
+
"ERROR: linzumi connect could not start - workspace already in use",
|
|
18937
|
+
bar,
|
|
18938
|
+
baseMessage,
|
|
18939
|
+
bar,
|
|
18940
|
+
""
|
|
18941
|
+
].join("\n");
|
|
18942
|
+
}
|
|
18943
|
+
function isRunnerLockConflictReportedError(error) {
|
|
18944
|
+
return error instanceof RunnerLockConflictReportedError || error instanceof Error && error.name === runnerLockConflictReportedErrorName;
|
|
18945
|
+
}
|
|
18946
|
+
function shouldResolveRunnerLockConflict(lockTakeover) {
|
|
18947
|
+
return lockTakeover !== void 0;
|
|
18948
|
+
}
|
|
18949
|
+
async function resolveRunnerLockConflict(args) {
|
|
18950
|
+
const report = runnerLockConflictReport(args.baseMessage);
|
|
18951
|
+
if (args.takeOverWithoutPrompt) {
|
|
18952
|
+
args.prompt.writeReport(report);
|
|
18953
|
+
await stopHolderAndAwaitRelease(args.holder, args.lockPath, args.takeover);
|
|
18954
|
+
return { outcome: "take_over" };
|
|
18955
|
+
}
|
|
18956
|
+
if (!args.prompt.isInteractive()) {
|
|
18957
|
+
args.prompt.writeReport(report);
|
|
18958
|
+
return { outcome: "declined", report };
|
|
18959
|
+
}
|
|
18960
|
+
args.prompt.writeReport(report);
|
|
18961
|
+
const yes = await args.prompt.promptYesNo(
|
|
18962
|
+
`Another runner (pid ${args.holder.pid}) is already connected to this workspace. Stop it and run here instead? [y/N] `
|
|
18963
|
+
);
|
|
18964
|
+
if (!yes) {
|
|
18965
|
+
return { outcome: "declined", report };
|
|
18966
|
+
}
|
|
18967
|
+
await stopHolderAndAwaitRelease(args.holder, args.lockPath, args.takeover);
|
|
18968
|
+
return { outcome: "take_over" };
|
|
18969
|
+
}
|
|
18970
|
+
async function stopHolderAndAwaitRelease(holder, lockPath, deps) {
|
|
18971
|
+
deps.log("runner.lock_takeover_requested", {
|
|
18972
|
+
holderPid: holder.pid,
|
|
18973
|
+
holderRunnerId: holder.runnerId,
|
|
18974
|
+
lockPath
|
|
18975
|
+
});
|
|
18976
|
+
if (!deps.isPidAlive(holder.pid)) {
|
|
18977
|
+
deps.log("runner.lock_takeover_holder_already_gone", {
|
|
18978
|
+
holderPid: holder.pid,
|
|
18979
|
+
lockPath
|
|
18980
|
+
});
|
|
18981
|
+
return;
|
|
18982
|
+
}
|
|
18983
|
+
if (!stillHeldBy(deps, holder)) {
|
|
18984
|
+
deps.log("runner.lock_takeover_owner_changed", {
|
|
18985
|
+
holderPid: holder.pid,
|
|
18986
|
+
lockPath
|
|
18987
|
+
});
|
|
18988
|
+
return;
|
|
18989
|
+
}
|
|
18990
|
+
sendSignalTolerant(deps, holder.pid, "SIGTERM");
|
|
18991
|
+
if (await waitForHolderRelease(holder, deps, runnerLockTakeoverGracefulStopMs)) {
|
|
18992
|
+
deps.log("runner.lock_takeover_graceful", {
|
|
18993
|
+
holderPid: holder.pid,
|
|
18994
|
+
lockPath
|
|
18995
|
+
});
|
|
18996
|
+
return;
|
|
18997
|
+
}
|
|
18998
|
+
if (!stillHeldBy(deps, holder)) {
|
|
18999
|
+
deps.log("runner.lock_takeover_owner_changed", {
|
|
19000
|
+
holderPid: holder.pid,
|
|
19001
|
+
lockPath
|
|
19002
|
+
});
|
|
19003
|
+
return;
|
|
19004
|
+
}
|
|
19005
|
+
deps.log("runner.lock_takeover_escalating_sigkill", {
|
|
19006
|
+
holderPid: holder.pid,
|
|
19007
|
+
lockPath
|
|
19008
|
+
});
|
|
19009
|
+
sendSignalTolerant(deps, holder.pid, "SIGKILL");
|
|
19010
|
+
if (await waitForHolderRelease(holder, deps, runnerLockTakeoverForcefulStopMs)) {
|
|
19011
|
+
deps.log("runner.lock_takeover_forceful", {
|
|
19012
|
+
holderPid: holder.pid,
|
|
19013
|
+
lockPath
|
|
19014
|
+
});
|
|
19015
|
+
return;
|
|
19016
|
+
}
|
|
19017
|
+
throw new Error(
|
|
19018
|
+
`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.`
|
|
19019
|
+
);
|
|
19020
|
+
}
|
|
19021
|
+
async function waitForHolderRelease(holder, deps, timeoutMs) {
|
|
19022
|
+
const deadline = deps.now() + timeoutMs;
|
|
19023
|
+
while (deps.now() < deadline) {
|
|
19024
|
+
if (!deps.isPidAlive(holder.pid) || deps.lockReleased()) {
|
|
19025
|
+
return true;
|
|
19026
|
+
}
|
|
19027
|
+
await deps.sleep(runnerLockTakeoverPollMs);
|
|
19028
|
+
}
|
|
19029
|
+
return !deps.isPidAlive(holder.pid) || deps.lockReleased();
|
|
19030
|
+
}
|
|
19031
|
+
function stillHeldBy(deps, holder) {
|
|
19032
|
+
const current = deps.currentLockOwner();
|
|
19033
|
+
return current !== void 0 && current.machineId === holder.machineId && current.runnerId === holder.runnerId && current.pid === holder.pid && current.startedAt === holder.startedAt;
|
|
19034
|
+
}
|
|
19035
|
+
function sendSignalTolerant(deps, pid, signal) {
|
|
19036
|
+
try {
|
|
19037
|
+
deps.signalPid(pid, signal);
|
|
19038
|
+
} catch (error) {
|
|
19039
|
+
const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
19040
|
+
if (code !== "ESRCH") {
|
|
19041
|
+
deps.log("runner.lock_takeover_signal_failed", {
|
|
19042
|
+
holderPid: pid,
|
|
19043
|
+
signal,
|
|
19044
|
+
code: typeof code === "string" ? code : null,
|
|
19045
|
+
message: error instanceof Error ? error.message : String(error)
|
|
19046
|
+
});
|
|
19047
|
+
}
|
|
19048
|
+
}
|
|
19049
|
+
}
|
|
19050
|
+
var runnerLockTakeoverGracefulStopMs, runnerLockTakeoverForcefulStopMs, runnerLockTakeoverPollMs, runnerLockConflictReportedErrorName, RunnerLockConflictReportedError;
|
|
19051
|
+
var init_runnerLockTakeover = __esm({
|
|
19052
|
+
"src/runnerLockTakeover.ts"() {
|
|
19053
|
+
"use strict";
|
|
19054
|
+
runnerLockTakeoverGracefulStopMs = 1e4;
|
|
19055
|
+
runnerLockTakeoverForcefulStopMs = 5e3;
|
|
19056
|
+
runnerLockTakeoverPollMs = 200;
|
|
19057
|
+
runnerLockConflictReportedErrorName = "RunnerLockConflictReportedError";
|
|
19058
|
+
RunnerLockConflictReportedError = class extends Error {
|
|
19059
|
+
constructor() {
|
|
19060
|
+
super("");
|
|
19061
|
+
this.name = runnerLockConflictReportedErrorName;
|
|
19062
|
+
}
|
|
19063
|
+
};
|
|
19064
|
+
}
|
|
19065
|
+
});
|
|
19066
|
+
|
|
19067
|
+
// src/blessedTputSetulcShim.ts
|
|
18530
19068
|
import blessed from "blessed";
|
|
19069
|
+
function balanceTerminfoConditionals(cap) {
|
|
19070
|
+
if (cap.indexOf("%;") === -1) {
|
|
19071
|
+
return cap;
|
|
19072
|
+
}
|
|
19073
|
+
let depth = 0;
|
|
19074
|
+
let repaired = "";
|
|
19075
|
+
let index = 0;
|
|
19076
|
+
while (index < cap.length) {
|
|
19077
|
+
const here = cap[index];
|
|
19078
|
+
const next = cap[index + 1];
|
|
19079
|
+
if (here === "%" && next === "%") {
|
|
19080
|
+
repaired += "%%";
|
|
19081
|
+
index += 2;
|
|
19082
|
+
continue;
|
|
19083
|
+
}
|
|
19084
|
+
if (here === "%" && next === "?") {
|
|
19085
|
+
depth += 1;
|
|
19086
|
+
repaired += "%?";
|
|
19087
|
+
index += 2;
|
|
19088
|
+
continue;
|
|
19089
|
+
}
|
|
19090
|
+
if (here === "%" && next === ";") {
|
|
19091
|
+
if (depth > 0) {
|
|
19092
|
+
depth -= 1;
|
|
19093
|
+
repaired += "%;";
|
|
19094
|
+
}
|
|
19095
|
+
index += 2;
|
|
19096
|
+
continue;
|
|
19097
|
+
}
|
|
19098
|
+
repaired += here;
|
|
19099
|
+
index += 1;
|
|
19100
|
+
}
|
|
19101
|
+
return repaired;
|
|
19102
|
+
}
|
|
19103
|
+
function installBlessedSetulcShim(blessedModule = blessed) {
|
|
19104
|
+
const tput = blessedModule.Tput;
|
|
19105
|
+
const proto = tput?.prototype;
|
|
19106
|
+
const original = proto?._compile;
|
|
19107
|
+
if (proto === void 0 || typeof original !== "function") {
|
|
19108
|
+
return;
|
|
19109
|
+
}
|
|
19110
|
+
if (proto[SHIM_FLAG] === true) {
|
|
19111
|
+
return;
|
|
19112
|
+
}
|
|
19113
|
+
const patched = function patchedCompile(info, key, str) {
|
|
19114
|
+
const safeStr = typeof str === "string" ? balanceTerminfoConditionals(str) : str;
|
|
19115
|
+
return original.call(this, info, key, safeStr);
|
|
19116
|
+
};
|
|
19117
|
+
proto._compile = patched;
|
|
19118
|
+
Object.defineProperty(proto, SHIM_FLAG, {
|
|
19119
|
+
value: true,
|
|
19120
|
+
enumerable: false,
|
|
19121
|
+
configurable: true,
|
|
19122
|
+
writable: true
|
|
19123
|
+
});
|
|
19124
|
+
}
|
|
19125
|
+
var SHIM_FLAG;
|
|
19126
|
+
var init_blessedTputSetulcShim = __esm({
|
|
19127
|
+
"src/blessedTputSetulcShim.ts"() {
|
|
19128
|
+
"use strict";
|
|
19129
|
+
SHIM_FLAG = "__linzumiSetulcShimInstalled";
|
|
19130
|
+
}
|
|
19131
|
+
});
|
|
19132
|
+
|
|
19133
|
+
// src/runnerConsoleReporter.ts
|
|
19134
|
+
import blessed2 from "blessed";
|
|
18531
19135
|
function reportRunnerConsoleEvent(event, payload) {
|
|
18532
19136
|
if (shouldRenderDashboard()) {
|
|
18533
19137
|
const tui = initializeDashboardTui(dashboardState);
|
|
@@ -19512,20 +20116,21 @@ function handleDashboardKey(state, key, exitProcess = () => process.kill(process
|
|
|
19512
20116
|
function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill(process.pid, "SIGINT")) {
|
|
19513
20117
|
let rendering = false;
|
|
19514
20118
|
let rawScrollAnchor;
|
|
19515
|
-
|
|
20119
|
+
installBlessedSetulcShim();
|
|
20120
|
+
const screen = blessed2.screen({
|
|
19516
20121
|
title: "Linzumi Commander",
|
|
19517
20122
|
smartCSR: true,
|
|
19518
20123
|
fullUnicode: true,
|
|
19519
20124
|
mouse: true
|
|
19520
20125
|
});
|
|
19521
|
-
const header =
|
|
20126
|
+
const header = blessed2.box({
|
|
19522
20127
|
top: 0,
|
|
19523
20128
|
left: 0,
|
|
19524
20129
|
width: "100%",
|
|
19525
20130
|
height: 1,
|
|
19526
20131
|
tags: false
|
|
19527
20132
|
});
|
|
19528
|
-
const tableElement =
|
|
20133
|
+
const tableElement = blessed2.listtable({
|
|
19529
20134
|
top: 1,
|
|
19530
20135
|
left: 0,
|
|
19531
20136
|
width: "100%",
|
|
@@ -19554,7 +20159,7 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
19554
20159
|
}
|
|
19555
20160
|
}
|
|
19556
20161
|
});
|
|
19557
|
-
const rawTitle =
|
|
20162
|
+
const rawTitle = blessed2.box({
|
|
19558
20163
|
top: 0,
|
|
19559
20164
|
left: 12,
|
|
19560
20165
|
right: 0,
|
|
@@ -19562,7 +20167,7 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
19562
20167
|
tags: false,
|
|
19563
20168
|
hidden: true
|
|
19564
20169
|
});
|
|
19565
|
-
const backButton =
|
|
20170
|
+
const backButton = blessed2.button({
|
|
19566
20171
|
top: 0,
|
|
19567
20172
|
left: 0,
|
|
19568
20173
|
width: 10,
|
|
@@ -19577,7 +20182,7 @@ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill
|
|
|
19577
20182
|
hover: { inverse: true }
|
|
19578
20183
|
}
|
|
19579
20184
|
});
|
|
19580
|
-
const rawBox =
|
|
20185
|
+
const rawBox = blessed2.box({
|
|
19581
20186
|
top: 3,
|
|
19582
20187
|
left: 0,
|
|
19583
20188
|
width: "100%",
|
|
@@ -20056,6 +20661,7 @@ var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey,
|
|
|
20056
20661
|
var init_runnerConsoleReporter = __esm({
|
|
20057
20662
|
"src/runnerConsoleReporter.ts"() {
|
|
20058
20663
|
"use strict";
|
|
20664
|
+
init_blessedTputSetulcShim();
|
|
20059
20665
|
dashboardState = {
|
|
20060
20666
|
jobs: /* @__PURE__ */ new Map(),
|
|
20061
20667
|
discovery: /* @__PURE__ */ new Map(),
|
|
@@ -21381,6 +21987,7 @@ import { spawn as spawn9, spawnSync as spawnSync5 } from "node:child_process";
|
|
|
21381
21987
|
import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
|
|
21382
21988
|
import {
|
|
21383
21989
|
chmodSync as chmodSync2,
|
|
21990
|
+
existsSync as existsSync13,
|
|
21384
21991
|
lstatSync,
|
|
21385
21992
|
mkdirSync as mkdirSync12,
|
|
21386
21993
|
mkdtempSync as mkdtempSync4,
|
|
@@ -21395,6 +22002,7 @@ import {
|
|
|
21395
22002
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
21396
22003
|
import { createServer as createServer3 } from "node:http";
|
|
21397
22004
|
import { homedir as homedir13, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
|
|
22005
|
+
import { createInterface } from "node:readline";
|
|
21398
22006
|
import {
|
|
21399
22007
|
basename as basename8,
|
|
21400
22008
|
dirname as dirname13,
|
|
@@ -21419,42 +22027,35 @@ async function runLocalCodexRunner(options) {
|
|
|
21419
22027
|
});
|
|
21420
22028
|
try {
|
|
21421
22029
|
if (options.machineId !== void 0) {
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
log2("runner.lock_takeover", {
|
|
21439
|
-
runnerId: options.runnerId,
|
|
21440
|
-
holderRunnerId: takeover.holderRunnerId,
|
|
21441
|
-
holderPid: takeover.holderPid,
|
|
21442
|
-
reason: takeover.reason,
|
|
21443
|
-
lockPath: takeover.lockPath
|
|
21444
|
-
});
|
|
21445
|
-
}
|
|
21446
|
-
});
|
|
21447
|
-
} catch (error) {
|
|
21448
|
-
if (isRunnerLockHeldError(error)) {
|
|
21449
|
-
log2("runner.lock_held_by_live_process", {
|
|
22030
|
+
const machineId = options.machineId;
|
|
22031
|
+
const acquire = () => acquireRunnerLock({
|
|
22032
|
+
machineId,
|
|
22033
|
+
runnerId: options.runnerId,
|
|
22034
|
+
cwd: options.cwd,
|
|
22035
|
+
workspace: runnerWorkspaceSlug(options) ?? null,
|
|
22036
|
+
linzumiUrl: options.kandanUrl,
|
|
22037
|
+
launchSource: options.launchSource,
|
|
22038
|
+
configPath: options.runnerLockConfigPath,
|
|
22039
|
+
// Wedged-runner takeover: a lock holder whose pid is alive but
|
|
22040
|
+
// whose lock heartbeat went stale (>3 min) gets SIGKILLed and its
|
|
22041
|
+
// lock replaced, instead of blocking recovery until someone runs
|
|
22042
|
+
// kill -9 by hand. Log it so the runner log explains where the old
|
|
22043
|
+
// pid went.
|
|
22044
|
+
onTakeover: (takeover) => {
|
|
22045
|
+
log2("runner.lock_takeover", {
|
|
21450
22046
|
runnerId: options.runnerId,
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
22047
|
+
holderRunnerId: takeover.holderRunnerId,
|
|
22048
|
+
holderPid: takeover.holderPid,
|
|
22049
|
+
reason: takeover.reason,
|
|
22050
|
+
lockPath: takeover.lockPath
|
|
21454
22051
|
});
|
|
21455
22052
|
}
|
|
21456
|
-
|
|
21457
|
-
|
|
22053
|
+
});
|
|
22054
|
+
const runnerLock = await acquireRunnerLockWithConflictResolution(
|
|
22055
|
+
acquire,
|
|
22056
|
+
options,
|
|
22057
|
+
log2
|
|
22058
|
+
);
|
|
21458
22059
|
cleanup.actions.push(() => runnerLock.release());
|
|
21459
22060
|
log2("runner.lock_acquired", {
|
|
21460
22061
|
path: runnerLock.path,
|
|
@@ -22474,6 +23075,11 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22474
23075
|
if (options.channelSession !== void 0 && codex === void 0) {
|
|
22475
23076
|
throw new Error("channel session requires a Codex app-server connection");
|
|
22476
23077
|
}
|
|
23078
|
+
const ownsRespawnableAppServer = runnerOwnsRespawnableAppServer({
|
|
23079
|
+
codexUrl: options.codexUrl,
|
|
23080
|
+
threadProcessRole: options.threadProcess?.role,
|
|
23081
|
+
ownsStartedAppServer: started !== void 0
|
|
23082
|
+
});
|
|
22477
23083
|
const seq = { value: 0 };
|
|
22478
23084
|
const discoveredCodexThreads = { value: [] };
|
|
22479
23085
|
const runtimeDefaults = runnerRuntimeDefaults(options);
|
|
@@ -22565,6 +23171,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22565
23171
|
const dynamicChannelSessions = /* @__PURE__ */ new Map();
|
|
22566
23172
|
const dynamicChannelSessionCodexClients = /* @__PURE__ */ new Map();
|
|
22567
23173
|
const codexTurnFailureGuards = /* @__PURE__ */ new Map();
|
|
23174
|
+
const lossReportPushes = [];
|
|
22568
23175
|
const codexTurnFailureGuardFor = (client) => {
|
|
22569
23176
|
const existing = codexTurnFailureGuards.get(client);
|
|
22570
23177
|
if (existing !== void 0) {
|
|
@@ -22577,7 +23184,36 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22577
23184
|
([codexThreadId]) => dynamicChannelSessionCodexClients.get(codexThreadId) === client
|
|
22578
23185
|
).map(([, session]) => session)
|
|
22579
23186
|
],
|
|
22580
|
-
log: log2
|
|
23187
|
+
log: log2,
|
|
23188
|
+
// Spec: plans/2026-06-13-orphaned-mid-turn-reconnect-recovery-spec.md
|
|
23189
|
+
// The runner process and its channel survive an app-server death, so the
|
|
23190
|
+
// server otherwise keeps these thread bindings "connected" and never
|
|
23191
|
+
// offers reconnect. Report the loss per affected thread (best-effort)
|
|
23192
|
+
// so the bindings flip to disconnected/reconnectable upstream.
|
|
23193
|
+
//
|
|
23194
|
+
// Only do this for a runner that owns a respawnable app-server. For an
|
|
23195
|
+
// external/remote `codexUrl` backend (or a thread-process worker) this
|
|
23196
|
+
// runner cannot relaunch the dead app-server, so a reconnect could never
|
|
23197
|
+
// recover the thread - marking it reconnectable would be misleading. In
|
|
23198
|
+
// that case we still fail the in-flight turns terminally, but leave the
|
|
23199
|
+
// binding as-is rather than offering a reconnect that cannot succeed.
|
|
23200
|
+
onActiveThreadsFailed: ownsRespawnableAppServer ? (threadIds) => {
|
|
23201
|
+
for (const codexThreadId of threadIds) {
|
|
23202
|
+
lossReportPushes.push(
|
|
23203
|
+
kandan.push(topic, "session:app_server_lost", {
|
|
23204
|
+
codex_thread_id: codexThreadId
|
|
23205
|
+
}).then(
|
|
23206
|
+
() => void 0,
|
|
23207
|
+
(error) => {
|
|
23208
|
+
log2("kandan.session_app_server_lost_push_failed", {
|
|
23209
|
+
codex_thread_id: codexThreadId,
|
|
23210
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23211
|
+
});
|
|
23212
|
+
}
|
|
23213
|
+
)
|
|
23214
|
+
);
|
|
23215
|
+
}
|
|
23216
|
+
} : void 0
|
|
22581
23217
|
});
|
|
22582
23218
|
codexTurnFailureGuards.set(client, guard);
|
|
22583
23219
|
return guard;
|
|
@@ -22681,7 +23317,8 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22681
23317
|
portForwardWatcher: channelSessionPortForwardWatcherOptions({
|
|
22682
23318
|
rootPid: portForwardWatcherRootPid,
|
|
22683
23319
|
start: options.portForwardWatcher,
|
|
22684
|
-
commanderBoundPids: args.commanderBoundPids
|
|
23320
|
+
commanderBoundPids: args.commanderBoundPids,
|
|
23321
|
+
commanderBoundPorts: args.commanderBoundPorts
|
|
22685
23322
|
}),
|
|
22686
23323
|
suppressedForwardPorts,
|
|
22687
23324
|
onForwardPortApproved: (port, attribution) => {
|
|
@@ -22876,6 +23513,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
22876
23513
|
portForwardWatcherRootPid: handle.processPid,
|
|
22877
23514
|
commanderBoundPids: (handle.commanderManagedPorts ?? []).flatMap(
|
|
22878
23515
|
(port) => port.pid === void 0 ? [] : [port.pid]
|
|
23516
|
+
),
|
|
23517
|
+
// The exact codex app-server ports are commander-managed even though
|
|
23518
|
+
// their listening pid is the wrapped grandchild, not the recorded
|
|
23519
|
+
// wrapper pid (Codex P1).
|
|
23520
|
+
commanderBoundPorts: (handle.commanderManagedPorts ?? []).map(
|
|
23521
|
+
(port) => port.port
|
|
22879
23522
|
)
|
|
22880
23523
|
});
|
|
22881
23524
|
switch (control.type) {
|
|
@@ -23025,6 +23668,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23025
23668
|
}
|
|
23026
23669
|
});
|
|
23027
23670
|
if (sharedCodexTurnFailureGuard !== void 0) {
|
|
23671
|
+
const appServerLost = { handled: false };
|
|
23028
23672
|
const failCodexSessionTurns = (cause) => {
|
|
23029
23673
|
if (cleanup.closePromise !== void 0) {
|
|
23030
23674
|
return;
|
|
@@ -23032,6 +23676,27 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
23032
23676
|
log2("codex.app_server_connection_lost", { cause });
|
|
23033
23677
|
sharedCodexTurnFailureGuard.failActiveTurns(cause);
|
|
23034
23678
|
started?.stop();
|
|
23679
|
+
if (ownsRespawnableAppServer && !appServerLost.handled) {
|
|
23680
|
+
appServerLost.handled = true;
|
|
23681
|
+
console.error(
|
|
23682
|
+
`Codex app-server connection was lost (${cause}); exiting so a fresh runner can relaunch it. Run \`linzumi connect\` to reconnect.`
|
|
23683
|
+
);
|
|
23684
|
+
const sessionDrains = [
|
|
23685
|
+
...channelSession !== void 0 ? [channelSession] : [],
|
|
23686
|
+
...dynamicChannelSessions.values()
|
|
23687
|
+
].map(
|
|
23688
|
+
(session) => session.close().catch((error) => {
|
|
23689
|
+
log2("codex.app_server_lost_session_drain_failed", {
|
|
23690
|
+
message: error instanceof Error ? error.message : String(error)
|
|
23691
|
+
});
|
|
23692
|
+
})
|
|
23693
|
+
);
|
|
23694
|
+
void awaitLossReportsThenExit({
|
|
23695
|
+
pushes: [...lossReportPushes, ...sessionDrains],
|
|
23696
|
+
exit: () => process.exit(75),
|
|
23697
|
+
safetyCapMs: 5e3
|
|
23698
|
+
});
|
|
23699
|
+
}
|
|
23035
23700
|
};
|
|
23036
23701
|
codex?.onClose?.((error) => failCodexSessionTurns(error.message));
|
|
23037
23702
|
started?.process.once(
|
|
@@ -23787,6 +24452,19 @@ async function resolveSessionControl(channelSession, dynamicChannelSessions, con
|
|
|
23787
24452
|
}
|
|
23788
24453
|
return void 0;
|
|
23789
24454
|
}
|
|
24455
|
+
function runnerOwnsRespawnableAppServer(args) {
|
|
24456
|
+
return args.codexUrl === void 0 && args.threadProcessRole !== "thread" && args.ownsStartedAppServer;
|
|
24457
|
+
}
|
|
24458
|
+
async function awaitLossReportsThenExit(args) {
|
|
24459
|
+
const setTimeoutImpl = args.setTimeoutFn ?? setTimeout;
|
|
24460
|
+
const drained = Promise.allSettled(args.pushes).then(() => void 0);
|
|
24461
|
+
const safetyCap = new Promise((resolve12) => {
|
|
24462
|
+
const handle = setTimeoutImpl(() => resolve12(), args.safetyCapMs);
|
|
24463
|
+
handle?.unref?.();
|
|
24464
|
+
});
|
|
24465
|
+
await Promise.race([drained, safetyCap]);
|
|
24466
|
+
args.exit();
|
|
24467
|
+
}
|
|
23790
24468
|
function createCodexTurnFailureGuard(args) {
|
|
23791
24469
|
const activeTurnsByThread = /* @__PURE__ */ new Map();
|
|
23792
24470
|
const state = { failed: false };
|
|
@@ -23822,6 +24500,7 @@ function createCodexTurnFailureGuard(args) {
|
|
|
23822
24500
|
if (activeTurns.length === 0) {
|
|
23823
24501
|
return;
|
|
23824
24502
|
}
|
|
24503
|
+
args.onActiveThreadsFailed?.(activeTurns.map(([threadId]) => threadId));
|
|
23825
24504
|
const sessions = Array.from(args.sessions());
|
|
23826
24505
|
for (const [threadId, turnId] of activeTurns) {
|
|
23827
24506
|
args.log("codex.active_turn_failed_on_connection_loss", {
|
|
@@ -23979,6 +24658,101 @@ function installCleanupHandlers(close) {
|
|
|
23979
24658
|
process.off("exit", closeOnExit);
|
|
23980
24659
|
};
|
|
23981
24660
|
}
|
|
24661
|
+
async function acquireRunnerLockWithConflictResolution(acquire, options, log2) {
|
|
24662
|
+
try {
|
|
24663
|
+
return acquire();
|
|
24664
|
+
} catch (error) {
|
|
24665
|
+
if (!isRunnerLockHeldError(error)) {
|
|
24666
|
+
throw error;
|
|
24667
|
+
}
|
|
24668
|
+
log2("runner.lock_held_by_live_process", {
|
|
24669
|
+
runnerId: options.runnerId,
|
|
24670
|
+
heldByRunnerId: error.heldBy.runnerId,
|
|
24671
|
+
heldByPid: error.heldBy.pid,
|
|
24672
|
+
lockPath: error.lockPath
|
|
24673
|
+
});
|
|
24674
|
+
const lockTakeover = options.lockTakeover;
|
|
24675
|
+
if (!shouldResolveRunnerLockConflict(lockTakeover)) {
|
|
24676
|
+
throw error;
|
|
24677
|
+
}
|
|
24678
|
+
const promptDeps = lockTakeover.prompt ?? defaultRunnerLockTakeoverPromptDeps(lockTakeover);
|
|
24679
|
+
const takeoverDeps = resolveRunnerLockTakeoverDeps(
|
|
24680
|
+
error.lockPath,
|
|
24681
|
+
lockTakeover.takeover,
|
|
24682
|
+
log2
|
|
24683
|
+
);
|
|
24684
|
+
const resolution = await resolveRunnerLockConflict({
|
|
24685
|
+
holder: error.heldBy,
|
|
24686
|
+
lockPath: error.lockPath,
|
|
24687
|
+
baseMessage: error.message,
|
|
24688
|
+
takeOverWithoutPrompt: lockTakeover.takeOverWithoutPrompt === true,
|
|
24689
|
+
prompt: promptDeps,
|
|
24690
|
+
takeover: takeoverDeps
|
|
24691
|
+
});
|
|
24692
|
+
if (resolution.outcome === "declined") {
|
|
24693
|
+
throw new RunnerLockConflictReportedError();
|
|
24694
|
+
}
|
|
24695
|
+
log2("runner.lock_takeover_interactive", {
|
|
24696
|
+
runnerId: options.runnerId,
|
|
24697
|
+
heldByRunnerId: error.heldBy.runnerId,
|
|
24698
|
+
heldByPid: error.heldBy.pid,
|
|
24699
|
+
lockPath: error.lockPath
|
|
24700
|
+
});
|
|
24701
|
+
return acquire();
|
|
24702
|
+
}
|
|
24703
|
+
}
|
|
24704
|
+
function defaultRunnerLockTakeoverPromptDeps(takeover) {
|
|
24705
|
+
return {
|
|
24706
|
+
isInteractive: () => takeover?.forceNonInteractive !== true && process.stdin.isTTY === true && process.stderr.isTTY === true,
|
|
24707
|
+
writeReport: (text2) => {
|
|
24708
|
+
process.stderr.write(`${text2}
|
|
24709
|
+
`);
|
|
24710
|
+
},
|
|
24711
|
+
promptYesNo: (question) => promptYesNoOnStdin(question)
|
|
24712
|
+
};
|
|
24713
|
+
}
|
|
24714
|
+
function promptYesNoOnStdin(question) {
|
|
24715
|
+
return new Promise((resolve12) => {
|
|
24716
|
+
const rl = createInterface({
|
|
24717
|
+
input: process.stdin,
|
|
24718
|
+
output: process.stderr
|
|
24719
|
+
});
|
|
24720
|
+
rl.question(question, (answer) => {
|
|
24721
|
+
rl.close();
|
|
24722
|
+
resolve12(/^y(es)?$/i.test(answer.trim()));
|
|
24723
|
+
});
|
|
24724
|
+
});
|
|
24725
|
+
}
|
|
24726
|
+
function resolveRunnerLockTakeoverDeps(lockPath, overrides, log2) {
|
|
24727
|
+
return {
|
|
24728
|
+
isPidAlive: overrides?.isPidAlive ?? runnerLockTakeoverPidIsAlive,
|
|
24729
|
+
signalPid: overrides?.signalPid ?? ((pid, signal) => {
|
|
24730
|
+
process.kill(pid, signal);
|
|
24731
|
+
}),
|
|
24732
|
+
lockReleased: overrides?.lockReleased ?? (() => !existsSync13(lockPath)),
|
|
24733
|
+
// Re-reads the on-disk owner so the takeover flow can revalidate identity
|
|
24734
|
+
// before signaling (never throws on a torn/partial lock file).
|
|
24735
|
+
currentLockOwner: overrides?.currentLockOwner ?? (() => readRunnerLockOwner(lockPath)),
|
|
24736
|
+
// runnerLockTakeoverSleep is deliberately NOT unref'ed - see its definition;
|
|
24737
|
+
// an unref'ed timer here would let `linzumi connect` exit before retrying
|
|
24738
|
+
// the lock acquisition during a takeover.
|
|
24739
|
+
sleep: overrides?.sleep ?? runnerLockTakeoverSleep,
|
|
24740
|
+
now: overrides?.now ?? (() => Date.now()),
|
|
24741
|
+
log: overrides?.log ?? ((event, payload) => {
|
|
24742
|
+
log2(event, payload);
|
|
24743
|
+
writeCliAuditEvent(event, payload);
|
|
24744
|
+
})
|
|
24745
|
+
};
|
|
24746
|
+
}
|
|
24747
|
+
function runnerLockTakeoverPidIsAlive(pid) {
|
|
24748
|
+
try {
|
|
24749
|
+
process.kill(pid, 0);
|
|
24750
|
+
return true;
|
|
24751
|
+
} catch (error) {
|
|
24752
|
+
const code = error !== null && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
24753
|
+
return code !== "ESRCH";
|
|
24754
|
+
}
|
|
24755
|
+
}
|
|
23982
24756
|
function launchCodexTui(codexBin, codexUrl, cwd, codexThreadId, session, fast) {
|
|
23983
24757
|
const args = codexTuiArgs(codexUrl, codexThreadId, session, fast);
|
|
23984
24758
|
writeCliAuditEvent("process.spawn", {
|
|
@@ -24965,29 +25739,18 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
24965
25739
|
approvalPolicy: stringValue(control.approvalPolicy) ?? void 0
|
|
24966
25740
|
});
|
|
24967
25741
|
if (applied.approval_policy === "never") {
|
|
24968
|
-
|
|
24969
|
-
|
|
24970
|
-
|
|
24971
|
-
|
|
24972
|
-
|
|
24973
|
-
|
|
24974
|
-
|
|
24975
|
-
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
status: "processing",
|
|
24981
|
-
reason: "Claude Code tool approved",
|
|
24982
|
-
sessionId: pending.sessionId ?? control.threadId
|
|
24983
|
-
});
|
|
24984
|
-
log2("claude_code.approval_request_auto_approved", {
|
|
24985
|
-
request_id: requestId,
|
|
24986
|
-
source_seq: pending.sourceSeq,
|
|
24987
|
-
session_id: pending.sessionId ?? control.threadId,
|
|
24988
|
-
reason: "full_access_enabled"
|
|
24989
|
-
});
|
|
24990
|
-
}
|
|
25742
|
+
await settleClaudeCodeApprovalsForSession({
|
|
25743
|
+
kandan,
|
|
25744
|
+
topic,
|
|
25745
|
+
pendingClaudeCodeApprovals,
|
|
25746
|
+
// activeClaudeCodeSessions is keyed by the SDK session id, so
|
|
25747
|
+
// control.threadId here is the SDK session id, NOT the linzumi thread
|
|
25748
|
+
// id. The drain matches pending approvals by their linzumi threadId,
|
|
25749
|
+
// so pass the session's stored linzumi threadId (Codex PR #1846 P1).
|
|
25750
|
+
sessionThreadId: claudeSession.threadId,
|
|
25751
|
+
log: log2,
|
|
25752
|
+
reason: "full_access_enabled"
|
|
25753
|
+
});
|
|
24991
25754
|
}
|
|
24992
25755
|
return {
|
|
24993
25756
|
instanceId,
|
|
@@ -25837,6 +26600,7 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25837
26600
|
args.options,
|
|
25838
26601
|
args.control
|
|
25839
26602
|
);
|
|
26603
|
+
const initialPermissionMode = runtimeSettings.approvalPolicy === void 0 ? void 0 : claudePermissionModeForApprovalPolicy(runtimeSettings.approvalPolicy);
|
|
25840
26604
|
if (args.resumeSessionId !== void 0) {
|
|
25841
26605
|
try {
|
|
25842
26606
|
await validateClaudeCodeResumeSession({
|
|
@@ -25856,6 +26620,16 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25856
26620
|
codexVersion: void 0
|
|
25857
26621
|
};
|
|
25858
26622
|
const abortController = new AbortController();
|
|
26623
|
+
const reaperRegistration = registerEngineChild({
|
|
26624
|
+
stop: () => {
|
|
26625
|
+
if (!abortController.signal.aborted) {
|
|
26626
|
+
abortController.abort(new Error("engine child reaper teardown"));
|
|
26627
|
+
}
|
|
26628
|
+
},
|
|
26629
|
+
pid: void 0,
|
|
26630
|
+
ownProcessGroup: false,
|
|
26631
|
+
kind: "claude_code"
|
|
26632
|
+
});
|
|
25859
26633
|
const acceptedSourceSeqs = new Set(
|
|
25860
26634
|
sourceSeq === void 0 ? [] : [sourceSeq]
|
|
25861
26635
|
);
|
|
@@ -25863,6 +26637,7 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
25863
26637
|
let startControlResponded = false;
|
|
25864
26638
|
let activeSessionId;
|
|
25865
26639
|
let sessionControls;
|
|
26640
|
+
let startupApprovalDrain;
|
|
25866
26641
|
const planMirrorClient = createLinzumiMcpApiClient({
|
|
25867
26642
|
kandanUrl: args.options.kandanUrl,
|
|
25868
26643
|
accessToken: args.options.token,
|
|
@@ -26193,6 +26968,18 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26193
26968
|
linzumiContext
|
|
26194
26969
|
}),
|
|
26195
26970
|
model: claudeCodeModelForRuntimeModel(runtimeSettings.model),
|
|
26971
|
+
// Apply the user's initial access level at session START, mirroring
|
|
26972
|
+
// codex (which seeds approvalPolicy / sandbox via
|
|
26973
|
+
// codexThreadRuntimeOverrides at start). Without this every Claude
|
|
26974
|
+
// Code session started in the SDK's 'default' mode and the no-prompt
|
|
26975
|
+
// ("never" -> bypassPermissions) level only took effect once an
|
|
26976
|
+
// update_session_settings control arrived post-start - so WebFetch
|
|
26977
|
+
// (no built-in allowlist) still raised an approval card on turn 1.
|
|
26978
|
+
// The mode is derived from runtimeSettings.approvalPolicy, which the
|
|
26979
|
+
// server already gates so only "never" (paired with a full-access
|
|
26980
|
+
// sandbox) survives to map to bypassPermissions; lesser levels keep
|
|
26981
|
+
// their 'default' / 'acceptEdits' modes.
|
|
26982
|
+
...initialPermissionMode === void 0 ? {} : { permissionMode: initialPermissionMode },
|
|
26196
26983
|
resumeSessionId: args.resumeSessionId,
|
|
26197
26984
|
abortController,
|
|
26198
26985
|
streamingInput: inputQueue,
|
|
@@ -26210,6 +26997,22 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26210
26997
|
allowedTools: ["mcp__linzumi__*"],
|
|
26211
26998
|
onSessionControls: (controls) => {
|
|
26212
26999
|
sessionControls = controls;
|
|
27000
|
+
if (initialPermissionMode === "bypassPermissions") {
|
|
27001
|
+
startupApprovalDrain = settleClaudeCodeApprovalsForSession({
|
|
27002
|
+
kandan: args.kandan,
|
|
27003
|
+
topic: args.topic,
|
|
27004
|
+
pendingClaudeCodeApprovals: args.pendingClaudeCodeApprovals,
|
|
27005
|
+
sessionThreadId: threadId,
|
|
27006
|
+
log: args.log,
|
|
27007
|
+
reason: "full_access_started"
|
|
27008
|
+
}).catch((error) => {
|
|
27009
|
+
args.log("claude_code.startup_approval_drain_failed", {
|
|
27010
|
+
linzumi_thread_id: threadId,
|
|
27011
|
+
claude_session_id: activeSessionId ?? null,
|
|
27012
|
+
message: error instanceof Error ? error.message : String(error)
|
|
27013
|
+
});
|
|
27014
|
+
});
|
|
27015
|
+
}
|
|
26213
27016
|
},
|
|
26214
27017
|
onTurnCompleted: () => {
|
|
26215
27018
|
inputQueue.completeTurn();
|
|
@@ -26260,6 +27063,7 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26260
27063
|
return result2;
|
|
26261
27064
|
};
|
|
26262
27065
|
const sessionWork = runSession();
|
|
27066
|
+
void sessionWork.finally(() => reaperRegistration.unregister()).catch(() => void 0);
|
|
26263
27067
|
sessionWorkHandle.value = sessionWork;
|
|
26264
27068
|
const settled = await Promise.race([
|
|
26265
27069
|
sessionWork.then((result2) => ({
|
|
@@ -26301,6 +27105,9 @@ async function startClaudeCodeProviderInstance(args) {
|
|
|
26301
27105
|
message: error instanceof Error ? error.message : String(error)
|
|
26302
27106
|
});
|
|
26303
27107
|
}
|
|
27108
|
+
if (startupApprovalDrain !== void 0) {
|
|
27109
|
+
await startupApprovalDrain;
|
|
27110
|
+
}
|
|
26304
27111
|
void sessionWork.then(
|
|
26305
27112
|
() => {
|
|
26306
27113
|
args.log("claude_code.session_loop_ended", {
|
|
@@ -26336,6 +27143,35 @@ function claudePermissionModeForApprovalPolicy(approvalPolicy) {
|
|
|
26336
27143
|
return void 0;
|
|
26337
27144
|
}
|
|
26338
27145
|
}
|
|
27146
|
+
function claudeCodeApprovalsForSessionThread(pendingClaudeCodeApprovals, sessionThreadId) {
|
|
27147
|
+
return [...pendingClaudeCodeApprovals.entries()].filter(
|
|
27148
|
+
([, pending]) => pending.threadId === sessionThreadId
|
|
27149
|
+
);
|
|
27150
|
+
}
|
|
27151
|
+
async function settleClaudeCodeApprovalsForSession(args) {
|
|
27152
|
+
const pendingForSession = claudeCodeApprovalsForSessionThread(
|
|
27153
|
+
args.pendingClaudeCodeApprovals,
|
|
27154
|
+
args.sessionThreadId
|
|
27155
|
+
);
|
|
27156
|
+
for (const [requestId, pending] of pendingForSession) {
|
|
27157
|
+
pending.resolve({ type: "allow" });
|
|
27158
|
+
await publishClaudeCodeMessageState(args.kandan, args.topic, {
|
|
27159
|
+
workspace: pending.workspace,
|
|
27160
|
+
channel: pending.channel,
|
|
27161
|
+
threadId: pending.threadId,
|
|
27162
|
+
sourceSeq: pending.sourceSeq,
|
|
27163
|
+
status: "processing",
|
|
27164
|
+
reason: "Claude Code tool approved",
|
|
27165
|
+
sessionId: pending.sessionId ?? args.sessionThreadId
|
|
27166
|
+
});
|
|
27167
|
+
args.log("claude_code.approval_request_auto_approved", {
|
|
27168
|
+
request_id: requestId,
|
|
27169
|
+
source_seq: pending.sourceSeq,
|
|
27170
|
+
session_id: pending.sessionId ?? args.sessionThreadId,
|
|
27171
|
+
reason: args.reason
|
|
27172
|
+
});
|
|
27173
|
+
}
|
|
27174
|
+
}
|
|
26339
27175
|
function claudeSteerInputText(input) {
|
|
26340
27176
|
const items = arrayValue(input) ?? [];
|
|
26341
27177
|
const text2 = items.flatMap((item) => {
|
|
@@ -26698,9 +27534,11 @@ function channelSessionPortForwardWatcherOptions(args) {
|
|
|
26698
27534
|
...args.rootPid === void 0 ? [] : [args.rootPid],
|
|
26699
27535
|
...args.commanderBoundPids ?? []
|
|
26700
27536
|
];
|
|
27537
|
+
const commanderBoundPorts = args.commanderBoundPorts ?? [];
|
|
26701
27538
|
return {
|
|
26702
27539
|
...args.rootPid === void 0 ? {} : { rootPid: args.rootPid },
|
|
26703
27540
|
...commanderBoundPids.length === 0 ? {} : { commanderBoundPids },
|
|
27541
|
+
...commanderBoundPorts.length === 0 ? {} : { commanderBoundPorts },
|
|
26704
27542
|
...args.start === void 0 ? {} : { start: args.start }
|
|
26705
27543
|
};
|
|
26706
27544
|
}
|
|
@@ -28502,6 +29340,7 @@ var init_runner = __esm({
|
|
|
28502
29340
|
init_commanderAttachments();
|
|
28503
29341
|
init_claudeCodePipeline();
|
|
28504
29342
|
init_claudeCodePlanMirror();
|
|
29343
|
+
init_engineChildReaper();
|
|
28505
29344
|
init_claudeCodeLiveBashOutput();
|
|
28506
29345
|
init_claudeCodeSession();
|
|
28507
29346
|
init_codexAppServer();
|
|
@@ -28524,6 +29363,7 @@ var init_runner = __esm({
|
|
|
28524
29363
|
init_protocol();
|
|
28525
29364
|
init_runnerLogger();
|
|
28526
29365
|
init_runnerLock();
|
|
29366
|
+
init_runnerLockTakeover();
|
|
28527
29367
|
init_runnerConsoleReporter();
|
|
28528
29368
|
init_version();
|
|
28529
29369
|
init_telemetry();
|
|
@@ -28564,7 +29404,7 @@ var init_runner = __esm({
|
|
|
28564
29404
|
});
|
|
28565
29405
|
|
|
28566
29406
|
// src/kandanTls.ts
|
|
28567
|
-
import { existsSync as
|
|
29407
|
+
import { existsSync as existsSync14, readFileSync as readFileSync17 } from "node:fs";
|
|
28568
29408
|
import { Agent } from "undici";
|
|
28569
29409
|
import WsWebSocket from "ws";
|
|
28570
29410
|
function kandanTlsTrustFromEnv() {
|
|
@@ -28575,7 +29415,7 @@ function kandanTlsTrustFromCaFile(caFile) {
|
|
|
28575
29415
|
return void 0;
|
|
28576
29416
|
}
|
|
28577
29417
|
const trimmed = caFile.trim();
|
|
28578
|
-
if (!
|
|
29418
|
+
if (!existsSync14(trimmed)) {
|
|
28579
29419
|
throw new Error(`KANDAN_TLS_CA_FILE does not exist: ${trimmed}`);
|
|
28580
29420
|
}
|
|
28581
29421
|
const ca = readFileSync17(trimmed, "utf8");
|
|
@@ -48853,7 +49693,7 @@ __export(signupFlow_exports, {
|
|
|
48853
49693
|
});
|
|
48854
49694
|
import { spawn as spawn12, spawnSync as spawnSync7 } from "node:child_process";
|
|
48855
49695
|
import {
|
|
48856
|
-
existsSync as
|
|
49696
|
+
existsSync as existsSync17,
|
|
48857
49697
|
constants as fsConstants,
|
|
48858
49698
|
mkdirSync as mkdirSync15,
|
|
48859
49699
|
mkdtempSync as mkdtempSync5,
|
|
@@ -48981,7 +49821,7 @@ function defaultSignupDraftStore(serviceUrl) {
|
|
|
48981
49821
|
const path2 = defaultSignupDraftPath(serviceUrl);
|
|
48982
49822
|
return {
|
|
48983
49823
|
read: () => {
|
|
48984
|
-
if (!
|
|
49824
|
+
if (!existsSync17(path2)) {
|
|
48985
49825
|
return void 0;
|
|
48986
49826
|
}
|
|
48987
49827
|
let parsed;
|
|
@@ -49060,7 +49900,7 @@ function defaultSignupTaskCachePath(serviceUrl) {
|
|
|
49060
49900
|
);
|
|
49061
49901
|
}
|
|
49062
49902
|
function readSignupTaskCache(path2) {
|
|
49063
|
-
if (!
|
|
49903
|
+
if (!existsSync17(path2)) {
|
|
49064
49904
|
return { version: 1, entries: {} };
|
|
49065
49905
|
}
|
|
49066
49906
|
let parsed;
|
|
@@ -51888,7 +52728,7 @@ async function discoverCodeRoots(homeDir) {
|
|
|
51888
52728
|
const candidates = ["src", "code", "projects"].map(
|
|
51889
52729
|
(name) => join23(homeDir, name)
|
|
51890
52730
|
);
|
|
51891
|
-
return candidates.filter((path2) =>
|
|
52731
|
+
return candidates.filter((path2) => existsSync17(path2)).flatMap((path2) => discoveredProjectNames(path2));
|
|
51892
52732
|
}
|
|
51893
52733
|
function discoveredProjectNames(root) {
|
|
51894
52734
|
try {
|
|
@@ -51992,25 +52832,25 @@ function looksLikeProject(path2) {
|
|
|
51992
52832
|
"pnpm-lock.yaml",
|
|
51993
52833
|
"yarn.lock",
|
|
51994
52834
|
"package-lock.json"
|
|
51995
|
-
].some((name) =>
|
|
52835
|
+
].some((name) => existsSync17(join23(path2, name)));
|
|
51996
52836
|
}
|
|
51997
52837
|
function detectProjectLanguage(path2) {
|
|
51998
|
-
if (
|
|
52838
|
+
if (existsSync17(join23(path2, "pyproject.toml")) || existsSync17(join23(path2, "requirements.txt"))) {
|
|
51999
52839
|
return "Python";
|
|
52000
52840
|
}
|
|
52001
|
-
if (
|
|
52841
|
+
if (existsSync17(join23(path2, "Cargo.toml"))) {
|
|
52002
52842
|
return "Rust";
|
|
52003
52843
|
}
|
|
52004
|
-
if (
|
|
52844
|
+
if (existsSync17(join23(path2, "go.mod"))) {
|
|
52005
52845
|
return "Go";
|
|
52006
52846
|
}
|
|
52007
|
-
if (
|
|
52847
|
+
if (existsSync17(join23(path2, "mix.exs"))) {
|
|
52008
52848
|
return "Elixir";
|
|
52009
52849
|
}
|
|
52010
|
-
if (
|
|
52850
|
+
if (existsSync17(join23(path2, "tsconfig.json")) || packageJsonMentionsTypeScript(path2)) {
|
|
52011
52851
|
return "TypeScript";
|
|
52012
52852
|
}
|
|
52013
|
-
if (
|
|
52853
|
+
if (existsSync17(join23(path2, "package.json"))) {
|
|
52014
52854
|
return "JavaScript";
|
|
52015
52855
|
}
|
|
52016
52856
|
return "Project";
|
|
@@ -52026,7 +52866,7 @@ function packageJsonMentionsTypeScript(path2) {
|
|
|
52026
52866
|
}
|
|
52027
52867
|
}
|
|
52028
52868
|
function hasGitMetadata(path2) {
|
|
52029
|
-
return
|
|
52869
|
+
return existsSync17(join23(path2, ".git"));
|
|
52030
52870
|
}
|
|
52031
52871
|
function childDirectories(root) {
|
|
52032
52872
|
try {
|
|
@@ -52147,9 +52987,10 @@ secure mission control for all your agents on your computers
|
|
|
52147
52987
|
// src/index.ts
|
|
52148
52988
|
init_onboardingDiscoveryChildProcess();
|
|
52149
52989
|
init_runner();
|
|
52990
|
+
init_runnerLockTakeover();
|
|
52150
52991
|
init_claudeCodeSession();
|
|
52151
52992
|
init_authCache();
|
|
52152
|
-
import { existsSync as
|
|
52993
|
+
import { existsSync as existsSync18, readFileSync as readFileSync22, realpathSync as realpathSync7 } from "node:fs";
|
|
52153
52994
|
import { homedir as homedir17 } from "node:os";
|
|
52154
52995
|
import { resolve as resolve11 } from "node:path";
|
|
52155
52996
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
@@ -52245,7 +53086,7 @@ init_kandanTls();
|
|
|
52245
53086
|
init_protocol();
|
|
52246
53087
|
init_json();
|
|
52247
53088
|
init_defaultUrls();
|
|
52248
|
-
import { existsSync as
|
|
53089
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "node:fs";
|
|
52249
53090
|
import { dirname as dirname14, join as join21 } from "node:path";
|
|
52250
53091
|
import { homedir as homedir14 } from "node:os";
|
|
52251
53092
|
async function runAgentCliCommand(args, deps = {
|
|
@@ -52964,7 +53805,7 @@ function authorizationHeaders(token) {
|
|
|
52964
53805
|
return { authorization: `Bearer ${token}` };
|
|
52965
53806
|
}
|
|
52966
53807
|
function readOptionalTextFile(path2) {
|
|
52967
|
-
return
|
|
53808
|
+
return existsSync15(path2) ? readFileSync18(path2, "utf8") : void 0;
|
|
52968
53809
|
}
|
|
52969
53810
|
function writeTextFile(path2, content) {
|
|
52970
53811
|
mkdirSync13(dirname14(path2), { recursive: true });
|
|
@@ -53055,7 +53896,7 @@ init_helloLinzumiProject();
|
|
|
53055
53896
|
// src/commanderDaemon.ts
|
|
53056
53897
|
init_runnerLogger();
|
|
53057
53898
|
import {
|
|
53058
|
-
existsSync as
|
|
53899
|
+
existsSync as existsSync16,
|
|
53059
53900
|
closeSync as closeSync3,
|
|
53060
53901
|
mkdirSync as mkdirSync14,
|
|
53061
53902
|
openSync as openSync4,
|
|
@@ -53149,7 +53990,7 @@ function startCommanderDaemon(options) {
|
|
|
53149
53990
|
}
|
|
53150
53991
|
function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), processIdentityReader = readProcessIdentity) {
|
|
53151
53992
|
const statusFile = commanderStatusFile(runnerId, statusDir);
|
|
53152
|
-
if (!
|
|
53993
|
+
if (!existsSync16(statusFile)) {
|
|
53153
53994
|
return { status: "missing", runnerId, statusFile };
|
|
53154
53995
|
}
|
|
53155
53996
|
const record = parseRecord(readFileSync19(statusFile, "utf8"));
|
|
@@ -53157,7 +53998,7 @@ function commanderDaemonStatus(runnerId, statusDir = commanderStatusDir(), proce
|
|
|
53157
53998
|
}
|
|
53158
53999
|
async function waitForCommanderDaemon(options) {
|
|
53159
54000
|
const now = options.now ?? (() => Date.now());
|
|
53160
|
-
const readTextFile = options.readTextFile ?? ((path2) =>
|
|
54001
|
+
const readTextFile = options.readTextFile ?? ((path2) => existsSync16(path2) ? readFileSync19(path2, "utf8") : void 0);
|
|
53161
54002
|
const statusImpl = options.statusImpl ?? commanderDaemonStatus;
|
|
53162
54003
|
const deadline = now() + options.timeoutMs;
|
|
53163
54004
|
while (now() <= deadline) {
|
|
@@ -66372,6 +67213,11 @@ var flagDefinitions = /* @__PURE__ */ new Map([
|
|
|
66372
67213
|
["command", { kind: "value" }],
|
|
66373
67214
|
["owner-username", { kind: "value" }],
|
|
66374
67215
|
["include-token", { kind: "boolean" }],
|
|
67216
|
+
// Interactive lock-conflict resolution on connect: --take-over stops a live
|
|
67217
|
+
// holder without a prompt (automation); --no-input forces non-interactive so
|
|
67218
|
+
// a TTY never blocks on the takeover prompt (falls back to report + exit 1).
|
|
67219
|
+
["take-over", { kind: "boolean" }],
|
|
67220
|
+
["no-input", { kind: "boolean" }],
|
|
66375
67221
|
["help", { kind: "boolean" }]
|
|
66376
67222
|
]);
|
|
66377
67223
|
var helloFlagDefinitions = /* @__PURE__ */ new Map([
|
|
@@ -66388,8 +67234,11 @@ if (isMainModule()) {
|
|
|
66388
67234
|
try {
|
|
66389
67235
|
await main(process.argv.slice(2));
|
|
66390
67236
|
} catch (error) {
|
|
66391
|
-
|
|
67237
|
+
const message = cliErrorMessage(error);
|
|
67238
|
+
if (message !== "") {
|
|
67239
|
+
process.stderr.write(`${message}
|
|
66392
67240
|
`);
|
|
67241
|
+
}
|
|
66393
67242
|
process.exit(1);
|
|
66394
67243
|
}
|
|
66395
67244
|
}
|
|
@@ -66812,6 +67661,9 @@ async function runCommanderDaemonCommand(args) {
|
|
|
66812
67661
|
}
|
|
66813
67662
|
}
|
|
66814
67663
|
function cliErrorMessage(error) {
|
|
67664
|
+
if (isRunnerLockConflictReportedError(error)) {
|
|
67665
|
+
return "";
|
|
67666
|
+
}
|
|
66815
67667
|
return signupPromptCancelMessage2(error) ?? userFacingErrorMessage(error, { includeSupportLine: true });
|
|
66816
67668
|
}
|
|
66817
67669
|
function signupPromptCancelMessage2(error) {
|
|
@@ -67099,7 +67951,7 @@ async function parseAgentRunnerArgs(args, deps = {
|
|
|
67099
67951
|
};
|
|
67100
67952
|
}
|
|
67101
67953
|
function readAgentTokenTextFile(path2) {
|
|
67102
|
-
return
|
|
67954
|
+
return existsSync18(path2) ? readFileSync22(path2, "utf8") : void 0;
|
|
67103
67955
|
}
|
|
67104
67956
|
function rejectAgentRunnerTargetingFlags(values) {
|
|
67105
67957
|
const unsupportedFlags = [
|
|
@@ -67274,7 +68126,11 @@ async function parseRunnerArgs(args, deps = {
|
|
|
67274
68126
|
workspaceSlug: workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
|
|
67275
68127
|
runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
|
|
67276
68128
|
onboardingDiscovery,
|
|
67277
|
-
channelSession: void 0
|
|
68129
|
+
channelSession: void 0,
|
|
68130
|
+
lockTakeover: {
|
|
68131
|
+
takeOverWithoutPrompt: values.get("take-over") === true,
|
|
68132
|
+
forceNonInteractive: values.get("no-input") === true
|
|
68133
|
+
}
|
|
67278
68134
|
};
|
|
67279
68135
|
}
|
|
67280
68136
|
function runnerRuntimeDefaultsFromValues(values) {
|
|
@@ -67624,6 +68480,8 @@ Connection:
|
|
|
67624
68480
|
|
|
67625
68481
|
Workspace:
|
|
67626
68482
|
--workspace <slug> Workspace slug
|
|
68483
|
+
--take-over If another runner already holds this workspace, stop it and run here (no prompt)
|
|
68484
|
+
--no-input Never prompt; if the workspace is held, print the conflict and exit
|
|
67627
68485
|
|
|
67628
68486
|
Codex:
|
|
67629
68487
|
--cwd <path> Working directory for Codex, default current directory
|