@supercorks/krnl 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/kernel-companion.mjs +911 -400
- package/package.json +1 -1
|
@@ -15612,7 +15612,7 @@ function date4(params) {
|
|
|
15612
15612
|
config(en_default());
|
|
15613
15613
|
|
|
15614
15614
|
// src/hooks.ts
|
|
15615
|
-
import { readdir
|
|
15615
|
+
import { readdir } from "node:fs/promises";
|
|
15616
15616
|
import { join as join2 } from "node:path";
|
|
15617
15617
|
import { homedir as homedir2 } from "node:os";
|
|
15618
15618
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -15626,7 +15626,8 @@ import { dirname, join } from "node:path";
|
|
|
15626
15626
|
import { homedir } from "node:os";
|
|
15627
15627
|
import { createHash, randomUUID } from "node:crypto";
|
|
15628
15628
|
var dataRoot = join(homedir(), "Library/Application Support/Kernel/Codex");
|
|
15629
|
-
var
|
|
15629
|
+
var executeFile = promisify(execFile);
|
|
15630
|
+
var execute = (file2, args, options = {}) => executeFile(file2, args, { timeout: 1e4, maxBuffer: 4e6, ...options, encoding: "utf8" });
|
|
15630
15631
|
function pairingDirectory(id) {
|
|
15631
15632
|
return join(dataRoot, "machines", external_exports.uuid().parse(id));
|
|
15632
15633
|
}
|
|
@@ -15793,6 +15794,16 @@ var hookEvents = [
|
|
|
15793
15794
|
"PostToolUse",
|
|
15794
15795
|
"SessionEnd"
|
|
15795
15796
|
];
|
|
15797
|
+
var observationSchema = external_exports.object({
|
|
15798
|
+
threadId: external_exports.uuid(),
|
|
15799
|
+
turnId: external_exports.string().max(200).nullable(),
|
|
15800
|
+
event: external_exports.enum(hookEvents),
|
|
15801
|
+
tool: external_exports.string().max(200).nullable(),
|
|
15802
|
+
at: external_exports.number().finite().nonnegative(),
|
|
15803
|
+
pid: external_exports.number().int().positive(),
|
|
15804
|
+
identity: external_exports.string().max(500).optional(),
|
|
15805
|
+
draft: external_exports.object({ launchId: external_exports.uuid(), cwd: external_exports.string().min(1).max(4096) }).optional()
|
|
15806
|
+
});
|
|
15796
15807
|
function observation(raw, pid, at2 = performance.timeOrigin + performance.now()) {
|
|
15797
15808
|
const input = external_exports.object({
|
|
15798
15809
|
session_id: external_exports.uuid(),
|
|
@@ -15916,23 +15927,19 @@ async function captureHook(raw) {
|
|
|
15916
15927
|
const machines = await readdir(join2(dataRoot, "machines")).catch(() => []);
|
|
15917
15928
|
for (const machineId of machines) {
|
|
15918
15929
|
if (!external_exports.uuid().safeParse(machineId).success) continue;
|
|
15919
|
-
const directory = join2(dataRoot, "machines", machineId);
|
|
15920
|
-
const index = await readJson(join2(directory, "threads.json"));
|
|
15921
|
-
let captured = event;
|
|
15922
|
-
if (!index?.[event.threadId]) {
|
|
15923
|
-
const drafts = await readJson(join2(directory, "drafts.json"));
|
|
15924
|
-
const match = drafts && await matchDraftSubmission(raw, drafts);
|
|
15925
|
-
if (!match) continue;
|
|
15926
|
-
captured = { ...event, draft: { launchId: match.launchId, cwd: match.cwd } };
|
|
15927
|
-
}
|
|
15928
|
-
const events = join2(directory, "events");
|
|
15929
|
-
await mkdir2(events, { recursive: true, mode: 448 });
|
|
15930
|
-
const file2 = await open2(join2(events, `${event.at}-${randomUUID2()}.json`), "wx", 384);
|
|
15931
15930
|
try {
|
|
15932
|
-
|
|
15933
|
-
await
|
|
15934
|
-
|
|
15935
|
-
|
|
15931
|
+
const directory = join2(dataRoot, "machines", machineId);
|
|
15932
|
+
const index = await readJson(join2(directory, "threads.json"));
|
|
15933
|
+
let captured = event;
|
|
15934
|
+
if (!index?.[event.threadId]) {
|
|
15935
|
+
const drafts = await readJson(join2(directory, "drafts.json"));
|
|
15936
|
+
const match = drafts && await matchDraftSubmission(raw, drafts);
|
|
15937
|
+
if (!match) continue;
|
|
15938
|
+
captured = { ...event, draft: { launchId: match.launchId, cwd: match.cwd } };
|
|
15939
|
+
}
|
|
15940
|
+
const events = join2(directory, "events");
|
|
15941
|
+
await writeJson(join2(events, `${event.at}-${randomUUID2()}.json`), captured);
|
|
15942
|
+
} catch {
|
|
15936
15943
|
}
|
|
15937
15944
|
}
|
|
15938
15945
|
}
|
|
@@ -15947,21 +15954,48 @@ function kernelOrigin(raw) {
|
|
|
15947
15954
|
return url2.origin;
|
|
15948
15955
|
}
|
|
15949
15956
|
var KernelConnectionError = class extends Error {
|
|
15950
|
-
constructor(status2) {
|
|
15957
|
+
constructor(status2, code, retryAfterMs) {
|
|
15951
15958
|
super(
|
|
15952
|
-
status2 === 401
|
|
15959
|
+
status2 === 401 || status2 === 403 ? "The connection is no longer authorized. Run krnl login, then krnl connect codex." : code === "codex_stale_connection" ? "This connection was replaced. Run krnl connect codex to repair setup." : `Kernel rejected a companion delivery (${code ?? status2}). Run krnl status for recovery details.`
|
|
15953
15960
|
);
|
|
15954
15961
|
this.status = status2;
|
|
15962
|
+
this.code = code;
|
|
15963
|
+
this.retryAfterMs = retryAfterMs;
|
|
15955
15964
|
}
|
|
15956
15965
|
status;
|
|
15966
|
+
code;
|
|
15967
|
+
retryAfterMs;
|
|
15968
|
+
get fatal() {
|
|
15969
|
+
return [401, 403].includes(this.status) || this.code === "codex_stale_connection";
|
|
15970
|
+
}
|
|
15971
|
+
get retryable() {
|
|
15972
|
+
return this.code === "codex_disabled" || this.status === 408 || this.status === 429 || this.status >= 500;
|
|
15973
|
+
}
|
|
15957
15974
|
};
|
|
15958
15975
|
async function postKernel(origin, operation, payload, credential) {
|
|
15976
|
+
const controller = new AbortController();
|
|
15977
|
+
let timer;
|
|
15978
|
+
try {
|
|
15979
|
+
return await Promise.race([
|
|
15980
|
+
requestKernel(origin, operation, payload, credential, controller.signal),
|
|
15981
|
+
new Promise((_2, reject) => {
|
|
15982
|
+
timer = setTimeout(() => {
|
|
15983
|
+
controller.abort();
|
|
15984
|
+
reject(new Error("Kernel did not acknowledge the request within ten seconds"));
|
|
15985
|
+
}, 1e4);
|
|
15986
|
+
})
|
|
15987
|
+
]);
|
|
15988
|
+
} finally {
|
|
15989
|
+
clearTimeout(timer);
|
|
15990
|
+
}
|
|
15991
|
+
}
|
|
15992
|
+
async function requestKernel(origin, operation, payload, credential, signal) {
|
|
15959
15993
|
const response = await fetch(
|
|
15960
15994
|
`${kernelOrigin(origin)}/api/integrations/codex/companion/${operation}`,
|
|
15961
15995
|
{
|
|
15962
15996
|
method: "POST",
|
|
15963
15997
|
redirect: "error",
|
|
15964
|
-
signal
|
|
15998
|
+
signal,
|
|
15965
15999
|
headers: {
|
|
15966
16000
|
"content-type": "application/json",
|
|
15967
16001
|
...credential ? {
|
|
@@ -15973,10 +16007,20 @@ async function postKernel(origin, operation, payload, credential) {
|
|
|
15973
16007
|
}
|
|
15974
16008
|
);
|
|
15975
16009
|
if (!response.ok) {
|
|
15976
|
-
|
|
15977
|
-
|
|
16010
|
+
const retry = response.headers.get("retry-after");
|
|
16011
|
+
const retryAfterMs = retry ? /^\d+$/.test(retry) ? Number(retry) * 1e3 : Math.max(0, Date.parse(retry) - Date.now()) : void 0;
|
|
16012
|
+
const body = await response.json().catch(() => void 0);
|
|
16013
|
+
const rawCode = body?.error?.code;
|
|
16014
|
+
const code = typeof rawCode === "string" && /^[a-zA-Z0-9_:-]{1,100}$/.test(rawCode) ? rawCode : void 0;
|
|
16015
|
+
throw new KernelConnectionError(
|
|
16016
|
+
response.status,
|
|
16017
|
+
code,
|
|
16018
|
+
Number.isFinite(retryAfterMs) ? retryAfterMs : void 0
|
|
16019
|
+
);
|
|
15978
16020
|
}
|
|
15979
16021
|
const result = await response.json();
|
|
16022
|
+
if (!result || result.data === void 0)
|
|
16023
|
+
throw new Error("Kernel returned an invalid acknowledgment");
|
|
15980
16024
|
return result.data;
|
|
15981
16025
|
}
|
|
15982
16026
|
function connection(pairing, token, connectionId) {
|
|
@@ -16088,11 +16132,11 @@ var AppServer = class extends EventEmitter {
|
|
|
16088
16132
|
await new Promise((resolve3, reject) => {
|
|
16089
16133
|
const finish = () => {
|
|
16090
16134
|
clearTimeout(escalate);
|
|
16091
|
-
clearTimeout(
|
|
16135
|
+
clearTimeout(deadline2);
|
|
16092
16136
|
resolve3();
|
|
16093
16137
|
};
|
|
16094
16138
|
const escalate = setTimeout(() => this.child.kill("SIGKILL"), 3e3);
|
|
16095
|
-
const
|
|
16139
|
+
const deadline2 = setTimeout(() => {
|
|
16096
16140
|
clearTimeout(escalate);
|
|
16097
16141
|
this.child.off("exit", finish);
|
|
16098
16142
|
reject(new Error("The owned Codex process did not release its task"));
|
|
@@ -16104,75 +16148,184 @@ var AppServer = class extends EventEmitter {
|
|
|
16104
16148
|
};
|
|
16105
16149
|
|
|
16106
16150
|
// src/projects.ts
|
|
16107
|
-
import { mkdir as
|
|
16151
|
+
import { mkdir as mkdir2, realpath as realpath2, stat } from "node:fs/promises";
|
|
16108
16152
|
import { join as join3, isAbsolute } from "node:path";
|
|
16153
|
+
|
|
16154
|
+
// src/recovery.ts
|
|
16155
|
+
var ReceiptQueue = class {
|
|
16156
|
+
tails = /* @__PURE__ */ new Map();
|
|
16157
|
+
run(id, action) {
|
|
16158
|
+
const previous = this.tails.get(id) ?? Promise.resolve();
|
|
16159
|
+
const next = previous.catch(() => void 0).then(action);
|
|
16160
|
+
this.tails.set(id, next);
|
|
16161
|
+
void next.finally(() => {
|
|
16162
|
+
if (this.tails.get(id) === next) this.tails.delete(id);
|
|
16163
|
+
}).catch(() => void 0);
|
|
16164
|
+
return next;
|
|
16165
|
+
}
|
|
16166
|
+
};
|
|
16167
|
+
function retryFailure(error51, prior, now = Date.now(), random = Math.random) {
|
|
16168
|
+
const attempts = (prior?.attempts ?? 0) + 1;
|
|
16169
|
+
const state = {
|
|
16170
|
+
attempts,
|
|
16171
|
+
pendingSince: Math.min(prior?.pendingSince ?? now, now),
|
|
16172
|
+
nextAttemptAt: now
|
|
16173
|
+
};
|
|
16174
|
+
if (error51 instanceof KernelConnectionError && !error51.retryable) {
|
|
16175
|
+
state.blocked = { code: error51.code ?? `http_${error51.status}`, message: error51.message };
|
|
16176
|
+
} else {
|
|
16177
|
+
const backoff = Math.min(3e4, 1e3 * 2 ** Math.min(attempts, 5));
|
|
16178
|
+
state.nextAttemptAt = now + Math.max(
|
|
16179
|
+
1e3 + random() * (backoff - 1e3),
|
|
16180
|
+
error51 instanceof KernelConnectionError ? error51.retryAfterMs ?? 0 : 0
|
|
16181
|
+
);
|
|
16182
|
+
}
|
|
16183
|
+
return state;
|
|
16184
|
+
}
|
|
16185
|
+
function retryReady(state, now = Date.now()) {
|
|
16186
|
+
return !state?.blocked && (!state || state.nextAttemptAt <= now || now < state.pendingSince);
|
|
16187
|
+
}
|
|
16188
|
+
async function mapConcurrent(items, count, action) {
|
|
16189
|
+
let cursor = 0;
|
|
16190
|
+
const results = new Array(items.length);
|
|
16191
|
+
const settled = await Promise.allSettled(
|
|
16192
|
+
Array.from({ length: Math.min(count, items.length) }, async () => {
|
|
16193
|
+
for (; ; ) {
|
|
16194
|
+
const index = cursor++;
|
|
16195
|
+
if (index >= items.length) return;
|
|
16196
|
+
results[index] = await action(items[index]);
|
|
16197
|
+
}
|
|
16198
|
+
})
|
|
16199
|
+
);
|
|
16200
|
+
const failure = settled.find((result) => result.status === "rejected");
|
|
16201
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
16202
|
+
return results;
|
|
16203
|
+
}
|
|
16204
|
+
async function deadline(work, milliseconds) {
|
|
16205
|
+
let timer;
|
|
16206
|
+
try {
|
|
16207
|
+
return await Promise.race([
|
|
16208
|
+
work,
|
|
16209
|
+
new Promise((_2, reject) => {
|
|
16210
|
+
timer = setTimeout(() => reject(new Error("Local operation timed out")), milliseconds);
|
|
16211
|
+
})
|
|
16212
|
+
]);
|
|
16213
|
+
} finally {
|
|
16214
|
+
clearTimeout(timer);
|
|
16215
|
+
}
|
|
16216
|
+
}
|
|
16217
|
+
var RuntimeLanes = class {
|
|
16218
|
+
constructor(onError) {
|
|
16219
|
+
this.onError = onError;
|
|
16220
|
+
}
|
|
16221
|
+
onError;
|
|
16222
|
+
lanes = /* @__PURE__ */ new Map();
|
|
16223
|
+
start(name, interval, action) {
|
|
16224
|
+
const lane = this.lanes.get(name) ?? { at: -Infinity };
|
|
16225
|
+
if (lane.work || performance.now() - lane.at < interval) return;
|
|
16226
|
+
lane.at = performance.now();
|
|
16227
|
+
lane.work = action().catch((error51) => this.onError(name, error51)).finally(() => {
|
|
16228
|
+
delete lane.work;
|
|
16229
|
+
});
|
|
16230
|
+
this.lanes.set(name, lane);
|
|
16231
|
+
}
|
|
16232
|
+
async settle(milliseconds = 250) {
|
|
16233
|
+
await deadline(this.drain(), milliseconds).catch(() => void 0);
|
|
16234
|
+
}
|
|
16235
|
+
async drain() {
|
|
16236
|
+
for (; ; ) {
|
|
16237
|
+
const work = [...this.lanes.values()].flatMap((lane) => lane.work ? [lane.work] : []);
|
|
16238
|
+
if (!work.length) return;
|
|
16239
|
+
await Promise.all(work);
|
|
16240
|
+
}
|
|
16241
|
+
}
|
|
16242
|
+
};
|
|
16243
|
+
|
|
16244
|
+
// src/projects.ts
|
|
16109
16245
|
async function discoverProjects(server) {
|
|
16246
|
+
const signal = AbortSignal.timeout(25e3);
|
|
16110
16247
|
const projects = [];
|
|
16248
|
+
const visited = /* @__PURE__ */ new Set();
|
|
16111
16249
|
let cursor;
|
|
16112
16250
|
do {
|
|
16251
|
+
signal.throwIfAborted();
|
|
16113
16252
|
const page = await server.call(
|
|
16114
16253
|
"project/list",
|
|
16115
|
-
{ limit: 100, ...cursor ? { cursor } : {} }
|
|
16254
|
+
{ limit: 100, ...cursor ? { cursor } : {} },
|
|
16255
|
+
5e3
|
|
16116
16256
|
);
|
|
16117
16257
|
projects.push(...page.data);
|
|
16118
16258
|
cursor = page.nextCursor ?? void 0;
|
|
16259
|
+
if (cursor && visited.has(cursor)) throw new Error("Codex returned a repeated project page");
|
|
16260
|
+
if (cursor) visited.add(cursor);
|
|
16119
16261
|
if (projects.length > 500) throw new Error("The saved project catalog exceeds 500 projects");
|
|
16120
16262
|
} while (cursor);
|
|
16121
|
-
const discovered = await
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
16129
|
-
|
|
16130
|
-
|
|
16131
|
-
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
return unavailable("The saved project folder is unavailable.");
|
|
16138
|
-
} catch {
|
|
16263
|
+
const discovered = await mapConcurrent(projects, 8, async (project) => {
|
|
16264
|
+
signal.throwIfAborted();
|
|
16265
|
+
const unavailable = (issue2) => ({
|
|
16266
|
+
id: project.id,
|
|
16267
|
+
name: project.name,
|
|
16268
|
+
root: project.roots[0]?.path || "/",
|
|
16269
|
+
isGit: false,
|
|
16270
|
+
defaultBranch: null,
|
|
16271
|
+
issue: issue2
|
|
16272
|
+
});
|
|
16273
|
+
if (project.roots.length !== 1)
|
|
16274
|
+
return unavailable("Only saved projects with one local folder are supported.");
|
|
16275
|
+
let root;
|
|
16276
|
+
try {
|
|
16277
|
+
root = await deadline(realpath2(project.roots[0].path), 2e3);
|
|
16278
|
+
if (!(await deadline(stat(root), 2e3)).isDirectory())
|
|
16139
16279
|
return unavailable("The saved project folder is unavailable.");
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
})
|
|
16158
|
-
|
|
16280
|
+
} catch {
|
|
16281
|
+
return unavailable("The saved project folder is unavailable.");
|
|
16282
|
+
}
|
|
16283
|
+
let isGit = false;
|
|
16284
|
+
let defaultBranch = null;
|
|
16285
|
+
try {
|
|
16286
|
+
const top = (await execute("git", ["-C", root, "rev-parse", "--show-toplevel"], {
|
|
16287
|
+
signal,
|
|
16288
|
+
timeout: 5e3
|
|
16289
|
+
})).stdout.trim();
|
|
16290
|
+
isGit = await deadline(realpath2(top), 2e3) === root;
|
|
16291
|
+
if (isGit)
|
|
16292
|
+
defaultBranch = (await execute(
|
|
16293
|
+
"git",
|
|
16294
|
+
["-C", root, "symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
|
|
16295
|
+
{ signal, timeout: 5e3 }
|
|
16296
|
+
)).stdout.trim() || null;
|
|
16297
|
+
} catch (error51) {
|
|
16298
|
+
signal.throwIfAborted();
|
|
16299
|
+
if (error51.killed)
|
|
16300
|
+
return unavailable(
|
|
16301
|
+
"The saved project\u2019s Git metadata could not be verified. Discovery will retry."
|
|
16302
|
+
);
|
|
16303
|
+
}
|
|
16304
|
+
return { id: project.id, name: project.name, root, isGit, defaultBranch };
|
|
16305
|
+
});
|
|
16306
|
+
signal.throwIfAborted();
|
|
16159
16307
|
return discovered;
|
|
16160
16308
|
}
|
|
16309
|
+
var DestinationAuthorizationError = class extends Error {
|
|
16310
|
+
};
|
|
16161
16311
|
function authorizedDestination(pairing, launch, catalog2) {
|
|
16162
16312
|
const authorized = pairing.projects.find((project) => project.id === launch.destination.id);
|
|
16163
16313
|
const current = catalog2.find((project) => project.id === launch.destination.id);
|
|
16164
16314
|
if (!current || Boolean(current.issue) || pairing.projectAccess !== "all-saved" && (!authorized || authorized.root !== current.root) || launch.destination.root !== current.root)
|
|
16165
|
-
throw new
|
|
16315
|
+
throw new DestinationAuthorizationError(
|
|
16316
|
+
"This saved project is not locally authorized at the submitted folder"
|
|
16317
|
+
);
|
|
16166
16318
|
if (launch.checkout === "worktree" && (!current.isGit || !current.defaultBranch || current.defaultBranch !== launch.destination.defaultBranch))
|
|
16167
|
-
throw new
|
|
16168
|
-
if (!isAbsolute(current.root))
|
|
16319
|
+
throw new DestinationAuthorizationError("The submitted default branch is no longer available");
|
|
16320
|
+
if (!isAbsolute(current.root))
|
|
16321
|
+
throw new DestinationAuthorizationError("The saved project folder must be absolute");
|
|
16169
16322
|
return current;
|
|
16170
16323
|
}
|
|
16171
16324
|
async function prepareCheckout(project, launch, directory) {
|
|
16172
16325
|
if (launch.checkout === "folder") return project.root;
|
|
16173
16326
|
if (!project.isGit || !project.defaultBranch) throw new Error("A Git default branch is required");
|
|
16174
16327
|
const cwd = join3(directory, "worktrees", launch.id);
|
|
16175
|
-
await
|
|
16328
|
+
await mkdir2(join3(directory, "worktrees"), { recursive: true, mode: 448 });
|
|
16176
16329
|
await execute("git", [
|
|
16177
16330
|
"-C",
|
|
16178
16331
|
project.root,
|
|
@@ -16194,7 +16347,7 @@ async function prepareCheckout(project, launch, directory) {
|
|
|
16194
16347
|
|
|
16195
16348
|
// src/runtime.ts
|
|
16196
16349
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16197
|
-
import { readdir as readdir2, unlink } from "node:fs/promises";
|
|
16350
|
+
import { readdir as readdir2, unlink, rename as rename2, mkdir as mkdir3 } from "node:fs/promises";
|
|
16198
16351
|
import { join as join4 } from "node:path";
|
|
16199
16352
|
import { homedir as homedir3 } from "node:os";
|
|
16200
16353
|
import { setTimeout as delay } from "node:timers/promises";
|
|
@@ -22201,6 +22354,7 @@ var codexHealthSchema = external_exports.strictObject({
|
|
|
22201
22354
|
supportsThreadLinking: external_exports.boolean().optional(),
|
|
22202
22355
|
supportsTaskNamePrefix: external_exports.boolean().optional(),
|
|
22203
22356
|
supportsDrafts: external_exports.boolean().optional(),
|
|
22357
|
+
supportsStatusFreshness: external_exports.boolean().optional(),
|
|
22204
22358
|
models: external_exports.array(codexModelSchema).max(100).optional(),
|
|
22205
22359
|
appServerVersion: external_exports.string().max(100),
|
|
22206
22360
|
desktopVersion: external_exports.string().max(100),
|
|
@@ -22240,6 +22394,7 @@ var deleteCodexMappingSchema = external_exports.strictObject({
|
|
|
22240
22394
|
var launchCodexTaskSchema = external_exports.strictObject({
|
|
22241
22395
|
protocolVersion,
|
|
22242
22396
|
requestId: external_exports.uuid(),
|
|
22397
|
+
submissionDeadline: external_exports.iso.datetime().optional(),
|
|
22243
22398
|
taskId: external_exports.uuid(),
|
|
22244
22399
|
taskVersion: external_exports.number().int().positive(),
|
|
22245
22400
|
mode: codexModeSchema,
|
|
@@ -22300,7 +22455,13 @@ var codexDraftOpenedSchema = external_exports.strictObject({
|
|
|
22300
22455
|
generation: sequence,
|
|
22301
22456
|
launchId: external_exports.uuid()
|
|
22302
22457
|
});
|
|
22458
|
+
var cancelCodexOperationSchema = external_exports.strictObject({
|
|
22459
|
+
requestId: external_exports.uuid(),
|
|
22460
|
+
taskId: external_exports.uuid(),
|
|
22461
|
+
kind: external_exports.enum(["launch", "lookup"])
|
|
22462
|
+
});
|
|
22303
22463
|
var codexStatusReportSchema = external_exports.strictObject({
|
|
22464
|
+
observationAgeMs: external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
22304
22465
|
launchId: external_exports.uuid(),
|
|
22305
22466
|
sequence,
|
|
22306
22467
|
state: external_exports.enum(["running", "done", "unreachable"]),
|
|
@@ -22615,12 +22776,13 @@ async function commonGitDirectory(folder) {
|
|
|
22615
22776
|
timeout: 1500,
|
|
22616
22777
|
maxBuffer: 8192
|
|
22617
22778
|
});
|
|
22618
|
-
return await realpath3(resolve(folder, stdout.trim()));
|
|
22779
|
+
return await deadline(realpath3(resolve(folder, stdout.trim())), 2e3);
|
|
22619
22780
|
} catch {
|
|
22620
22781
|
return null;
|
|
22621
22782
|
}
|
|
22622
22783
|
}
|
|
22623
|
-
async function resolveThreadProject(thread, pairing, catalog2) {
|
|
22784
|
+
async function resolveThreadProject(thread, pairing, catalog2, signal = AbortSignal.timeout(2e4)) {
|
|
22785
|
+
signal.throwIfAborted();
|
|
22624
22786
|
const authorized = catalog2.filter(
|
|
22625
22787
|
(project) => !project.issue && (pairing.projectAccess === "all-saved" || pairing.projects.some(
|
|
22626
22788
|
(allowed) => allowed.id === project.id && allowed.root === project.root
|
|
@@ -22628,19 +22790,21 @@ async function resolveThreadProject(thread, pairing, catalog2) {
|
|
|
22628
22790
|
);
|
|
22629
22791
|
let cwd;
|
|
22630
22792
|
try {
|
|
22631
|
-
cwd = await realpath3(thread.cwd);
|
|
22632
|
-
if (!(await stat2(cwd)).isDirectory()) return null;
|
|
22793
|
+
cwd = await deadline(realpath3(thread.cwd), 2e3);
|
|
22794
|
+
if (!(await deadline(stat2(cwd), 2e3)).isDirectory()) return null;
|
|
22633
22795
|
} catch {
|
|
22634
22796
|
return null;
|
|
22635
22797
|
}
|
|
22636
22798
|
const candidates = thread.projectId ? authorized.filter((project) => project.id === thread.projectId) : [...authorized].sort((a2, b2) => b2.root.length - a2.root.length);
|
|
22637
22799
|
for (const project of candidates) {
|
|
22638
|
-
|
|
22800
|
+
signal.throwIfAborted();
|
|
22801
|
+
if (await deadline(realpath3(project.root), 2e3).catch(() => null) !== project.root) continue;
|
|
22639
22802
|
if (inside(project.root, cwd)) return { project, cwd };
|
|
22640
22803
|
}
|
|
22641
22804
|
const common = candidates.some((project) => project.isGit) ? await commonGitDirectory(cwd) : null;
|
|
22642
22805
|
if (common)
|
|
22643
22806
|
for (const project of candidates) {
|
|
22807
|
+
signal.throwIfAborted();
|
|
22644
22808
|
if (project.isGit && await commonGitDirectory(project.root) === common)
|
|
22645
22809
|
return { project, cwd };
|
|
22646
22810
|
}
|
|
@@ -22701,6 +22865,7 @@ var CodexThreadLookupService = class {
|
|
|
22701
22865
|
return this.loading;
|
|
22702
22866
|
}
|
|
22703
22867
|
async lookup(server, raw, pairing, catalog2) {
|
|
22868
|
+
const signal = AbortSignal.timeout(25e3);
|
|
22704
22869
|
const work = codexThreadLookupWorkSchema.parse(raw);
|
|
22705
22870
|
let candidates;
|
|
22706
22871
|
if (work.threadId) {
|
|
@@ -22720,7 +22885,9 @@ var CodexThreadLookupService = class {
|
|
|
22720
22885
|
for (const thread of candidates) {
|
|
22721
22886
|
if (this.now() - started > 2e4)
|
|
22722
22887
|
throw new Error("Refine the title or paste a Codex task link.");
|
|
22723
|
-
|
|
22888
|
+
signal.throwIfAborted();
|
|
22889
|
+
const destination = await resolveThreadProject(thread, pairing, catalog2, signal);
|
|
22890
|
+
signal.throwIfAborted();
|
|
22724
22891
|
if (!destination) continue;
|
|
22725
22892
|
if (results.length === CODEX_THREAD_RESULT_LIMIT) return { results, hasMore: true };
|
|
22726
22893
|
results.push({
|
|
@@ -22785,33 +22952,53 @@ function initialReceipt(launch) {
|
|
|
22785
22952
|
function canBegin(receipt, now) {
|
|
22786
22953
|
return receipt.phase === "prepared" && "expiresAt" in receipt.launch && now < Date.parse(receipt.launch.expiresAt);
|
|
22787
22954
|
}
|
|
22788
|
-
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22804
|
-
|
|
22805
|
-
|
|
22806
|
-
|
|
22807
|
-
|
|
22808
|
-
|
|
22955
|
+
var CreationConflictError = class extends Error {
|
|
22956
|
+
};
|
|
22957
|
+
async function findCreation(server, receipt, search = {}, budget = Infinity) {
|
|
22958
|
+
while (!search.done && budget-- > 0) {
|
|
22959
|
+
if (!search.candidates?.length) {
|
|
22960
|
+
if (search.cursor === null) {
|
|
22961
|
+
search.done = true;
|
|
22962
|
+
break;
|
|
22963
|
+
}
|
|
22964
|
+
const page = await server.call(
|
|
22965
|
+
"thread/list",
|
|
22966
|
+
{
|
|
22967
|
+
projectId: receipt.launch.destination.id,
|
|
22968
|
+
...receipt.cwd ? { cwd: receipt.cwd } : {},
|
|
22969
|
+
limit: 100,
|
|
22970
|
+
modelProviders: [],
|
|
22971
|
+
...search.cursor ? { cursor: search.cursor } : {}
|
|
22972
|
+
},
|
|
22973
|
+
2e3
|
|
22974
|
+
);
|
|
22975
|
+
search.candidates = page.data.filter(
|
|
22976
|
+
(candidate2) => candidate2.createdAt * 1e3 >= receipt.preparedAt - 5e3
|
|
22977
|
+
);
|
|
22978
|
+
search.cursor = page.nextCursor ?? null;
|
|
22979
|
+
continue;
|
|
22980
|
+
}
|
|
22981
|
+
const candidate = search.candidates[0];
|
|
22982
|
+
const { thread } = await server.call("thread/read", { threadId: candidate.id, includeTurns: false }, 2e3);
|
|
22983
|
+
if (thread.threadSource === sourceMarker(receipt.launch.id)) {
|
|
22984
|
+
if (search.found)
|
|
22985
|
+
throw new CreationConflictError("Multiple creation receipts require local reconciliation");
|
|
22986
|
+
search.found = thread;
|
|
22987
|
+
}
|
|
22988
|
+
search.candidates.shift();
|
|
22989
|
+
}
|
|
22990
|
+
if (!search.candidates?.length && search.cursor === null) search.done = true;
|
|
22991
|
+
return search.done ? search.found : void 0;
|
|
22809
22992
|
}
|
|
22810
22993
|
async function runCompanion(machineId, entrypoint2, signal) {
|
|
22811
22994
|
const directory = pairingDirectory(machineId);
|
|
22812
22995
|
const unlock = await processLock(directory);
|
|
22813
22996
|
const owners = /* @__PURE__ */ new Map();
|
|
22814
22997
|
let observer;
|
|
22998
|
+
let catalogServer;
|
|
22999
|
+
let lanes;
|
|
23000
|
+
let stopping = false;
|
|
23001
|
+
let persistDiagnostics;
|
|
22815
23002
|
let lookupServer;
|
|
22816
23003
|
let lookupWork;
|
|
22817
23004
|
try {
|
|
@@ -22824,17 +23011,95 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22824
23011
|
await writeJson(join4(directory, "generation.json"), pairing.generation);
|
|
22825
23012
|
const post = connection(pairing, await readCredential(machineId), randomUUID3());
|
|
22826
23013
|
const receipts = /* @__PURE__ */ new Map();
|
|
22827
|
-
const
|
|
22828
|
-
|
|
23014
|
+
const entries = async (path) => {
|
|
23015
|
+
try {
|
|
23016
|
+
return await readdir2(path);
|
|
23017
|
+
} catch (error51) {
|
|
23018
|
+
if (error51.code === "ENOENT") return [];
|
|
23019
|
+
throw error51;
|
|
23020
|
+
}
|
|
23021
|
+
};
|
|
23022
|
+
const writers = new ReceiptQueue();
|
|
23023
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
23024
|
+
const searches = /* @__PURE__ */ new Map();
|
|
23025
|
+
const quarantine = async (path, id) => {
|
|
23026
|
+
await mkdir3(join4(directory, "quarantine"), { recursive: true, mode: 448 });
|
|
23027
|
+
if (id) {
|
|
23028
|
+
blocked.add(id);
|
|
23029
|
+
await writeJson(join4(directory, "blocked", `${id}.json`), {
|
|
23030
|
+
code: "invalid_receipt",
|
|
23031
|
+
message: "A damaged creation receipt needs local recovery. This launch will not be repeated."
|
|
23032
|
+
});
|
|
23033
|
+
}
|
|
23034
|
+
await rename2(path, join4(directory, "quarantine", `${randomUUID3()}.json`));
|
|
23035
|
+
};
|
|
23036
|
+
for (const file2 of await entries(join4(directory, "blocked")))
|
|
23037
|
+
if (file2.endsWith(".json") && external_exports.uuid().safeParse(file2.slice(0, -5)).success)
|
|
23038
|
+
blocked.add(file2.slice(0, -5));
|
|
22829
23039
|
const inspected = /* @__PURE__ */ new Map();
|
|
23040
|
+
const observations = /* @__PURE__ */ new Map();
|
|
23041
|
+
const observe = (receipt, ageMs = 0) => {
|
|
23042
|
+
receipt.observedAt = Date.now() - ageMs;
|
|
23043
|
+
observations.set(receipt.launch.id, {
|
|
23044
|
+
wall: receipt.observedAt,
|
|
23045
|
+
monotonic: performance.now() - ageMs
|
|
23046
|
+
});
|
|
23047
|
+
};
|
|
22830
23048
|
const receiptPath = (id) => join4(directory, "receipts", `${id}.json`);
|
|
22831
23049
|
const save = (receipt) => writeJson(receiptPath(receipt.launch.id), receipt);
|
|
22832
|
-
for (const file2 of await
|
|
23050
|
+
for (const file2 of await entries(join4(directory, "receipts"))) {
|
|
22833
23051
|
if (!file2.endsWith(".json")) continue;
|
|
22834
|
-
const
|
|
22835
|
-
|
|
23052
|
+
const id = file2.slice(0, -5);
|
|
23053
|
+
let receipt;
|
|
23054
|
+
try {
|
|
23055
|
+
const stored = await readJson(join4(directory, "receipts", file2));
|
|
23056
|
+
if (!stored) throw new Error("Missing receipt");
|
|
23057
|
+
receipt = stored;
|
|
23058
|
+
if (!receipt || !external_exports.uuid().safeParse(id).success || receipt.launch?.id !== id || !Number.isFinite(receipt.preparedAt) || ![
|
|
23059
|
+
"prepared",
|
|
23060
|
+
"creating",
|
|
23061
|
+
"created",
|
|
23062
|
+
"starting",
|
|
23063
|
+
"opening-draft",
|
|
23064
|
+
"drafted",
|
|
23065
|
+
"released",
|
|
23066
|
+
"failed"
|
|
23067
|
+
].includes(receipt.phase))
|
|
23068
|
+
throw new Error("Invalid receipt");
|
|
23069
|
+
codexStatusReportSchema.parse(receipt.status);
|
|
23070
|
+
if ("task" in receipt.launch) codexLaunchSchema.parse(receipt.launch);
|
|
23071
|
+
else codexWatchSchema.parse(receipt.launch);
|
|
23072
|
+
} catch {
|
|
23073
|
+
await quarantine(
|
|
23074
|
+
join4(directory, "receipts", file2),
|
|
23075
|
+
external_exports.uuid().safeParse(id).success ? id : void 0
|
|
23076
|
+
);
|
|
23077
|
+
continue;
|
|
23078
|
+
}
|
|
23079
|
+
delete receipt.observedAt;
|
|
23080
|
+
if (receipt.phase === "opening-draft") {
|
|
23081
|
+
receipt.phase = "drafted";
|
|
23082
|
+
receipt.draftOpenUncertain = true;
|
|
23083
|
+
}
|
|
23084
|
+
if (["prepared", "created", "starting"].includes(receipt.phase)) {
|
|
23085
|
+
if (receipt.threadId) receipt.phase = "released";
|
|
23086
|
+
else {
|
|
23087
|
+
receipt.phase = "failed";
|
|
23088
|
+
receipt.failure = "The companion stopped before creating the task. Start again when connected.";
|
|
23089
|
+
}
|
|
23090
|
+
}
|
|
23091
|
+
if (receipt.phase === "released" && receipt.status.state === "running" && !receipt.status.waiting) {
|
|
23092
|
+
receipt.status = {
|
|
23093
|
+
...receipt.status,
|
|
23094
|
+
sequence: receipt.status.sequence + 1,
|
|
23095
|
+
state: "unreachable",
|
|
23096
|
+
message: "Checking the saved task after reconnection."
|
|
23097
|
+
};
|
|
23098
|
+
}
|
|
23099
|
+
receipts.set(id, receipt);
|
|
23100
|
+
await save(receipt);
|
|
22836
23101
|
}
|
|
22837
|
-
const indexThreads = async () => {
|
|
23102
|
+
const indexThreads = () => writers.run("index", async () => {
|
|
22838
23103
|
await writeJson(
|
|
22839
23104
|
join4(directory, "threads.json"),
|
|
22840
23105
|
Object.fromEntries(
|
|
@@ -22849,10 +23114,13 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22849
23114
|
)
|
|
22850
23115
|
)
|
|
22851
23116
|
);
|
|
22852
|
-
};
|
|
23117
|
+
});
|
|
23118
|
+
await indexThreads();
|
|
22853
23119
|
const queue = [];
|
|
22854
23120
|
const lookupService = new CodexThreadLookupService();
|
|
22855
23121
|
let lookupBusy = false;
|
|
23122
|
+
const lookupRetries = /* @__PURE__ */ new Map();
|
|
23123
|
+
const lookupExpiries = /* @__PURE__ */ new Map();
|
|
22856
23124
|
const lookupResults = [];
|
|
22857
23125
|
const watched = /* @__PURE__ */ new Map();
|
|
22858
23126
|
const watchAttempts = /* @__PURE__ */ new Map();
|
|
@@ -22872,10 +23140,18 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22872
23140
|
return lookupServer;
|
|
22873
23141
|
};
|
|
22874
23142
|
const connectObserver = async () => {
|
|
22875
|
-
|
|
22876
|
-
|
|
22877
|
-
|
|
22878
|
-
|
|
23143
|
+
const next = new AppServer(directory);
|
|
23144
|
+
try {
|
|
23145
|
+
await next.initialize();
|
|
23146
|
+
observer = next;
|
|
23147
|
+
next.once("closed", () => {
|
|
23148
|
+
if (observer === next) observer = void 0;
|
|
23149
|
+
});
|
|
23150
|
+
return next;
|
|
23151
|
+
} catch (error51) {
|
|
23152
|
+
next.close();
|
|
23153
|
+
throw error51;
|
|
23154
|
+
}
|
|
22879
23155
|
};
|
|
22880
23156
|
let catalog2 = [];
|
|
22881
23157
|
let health = {
|
|
@@ -22891,8 +23167,14 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22891
23167
|
issue: "Checking Codex compatibility."
|
|
22892
23168
|
};
|
|
22893
23169
|
let refreshed = 0;
|
|
22894
|
-
let
|
|
22895
|
-
let
|
|
23170
|
+
let supportsFreshness = false;
|
|
23171
|
+
let heartbeatHealth = "";
|
|
23172
|
+
const laneRetries = /* @__PURE__ */ new Map();
|
|
23173
|
+
let fatalError = null;
|
|
23174
|
+
const laneErrors = /* @__PURE__ */ new Map();
|
|
23175
|
+
let journalHealthy = true;
|
|
23176
|
+
let clockAnchor;
|
|
23177
|
+
const kernelNow = () => clockAnchor ? clockAnchor.server + Math.max(0, performance.now() - clockAnchor.monotonic) : Date.now();
|
|
22896
23178
|
const release = async (receipt, waiting, message) => {
|
|
22897
23179
|
const owner = owners.get(receipt.launch.id);
|
|
22898
23180
|
receipt.status = {
|
|
@@ -22904,6 +23186,7 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22904
23186
|
message
|
|
22905
23187
|
};
|
|
22906
23188
|
receipt.handoffWaiting = waiting;
|
|
23189
|
+
observe(receipt);
|
|
22907
23190
|
await save(receipt);
|
|
22908
23191
|
if (waiting && owner && receipt.turnId)
|
|
22909
23192
|
await owner.call("turn/interrupt", { threadId: receipt.threadId, turnId: receipt.turnId }).catch(() => void 0);
|
|
@@ -22914,12 +23197,12 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22914
23197
|
await save(receipt);
|
|
22915
23198
|
};
|
|
22916
23199
|
const begin = async (launch) => {
|
|
22917
|
-
if (receipts.has(launch.id)) return;
|
|
23200
|
+
if (receipts.has(launch.id) || blocked.has(launch.id) || stopping || signal.aborted) return;
|
|
22918
23201
|
const receipt = initialReceipt(launch);
|
|
22919
23202
|
receipts.set(launch.id, receipt);
|
|
22920
23203
|
await save(receipt);
|
|
22921
23204
|
try {
|
|
22922
|
-
if (launch.machineId !== machineId || launch.protocolVersion !== 1 || !canBegin(receipt,
|
|
23205
|
+
if (launch.machineId !== machineId || launch.protocolVersion !== 1 || !canBegin(receipt, kernelNow()))
|
|
22923
23206
|
throw new Error("The launch expired before local creation");
|
|
22924
23207
|
const authorized = await readJson(join4(directory, "pairing.json"));
|
|
22925
23208
|
if (!authorized) throw new Error("The local pairing is unavailable");
|
|
@@ -22942,6 +23225,8 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22942
23225
|
receipt.phase = "opening-draft";
|
|
22943
23226
|
await save(receipt);
|
|
22944
23227
|
await indexThreads();
|
|
23228
|
+
if (stopping || signal.aborted || kernelNow() >= Date.parse(launch.expiresAt))
|
|
23229
|
+
throw new Error("The launch expired before desktop dispatch");
|
|
22945
23230
|
await execute("/usr/bin/open", [url2]);
|
|
22946
23231
|
receipt.phase = "drafted";
|
|
22947
23232
|
await save(receipt);
|
|
@@ -22949,7 +23234,7 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22949
23234
|
}
|
|
22950
23235
|
receipt.cwd = await prepareCheckout(project, launch, directory);
|
|
22951
23236
|
await save(receipt);
|
|
22952
|
-
if (!canBegin(receipt,
|
|
23237
|
+
if (!canBegin(receipt, kernelNow()))
|
|
22953
23238
|
throw new Error("The launch expired during checkout preparation");
|
|
22954
23239
|
const latestPairing = await readJson(join4(directory, "pairing.json"));
|
|
22955
23240
|
if (!latestPairing) throw new Error("The local connection is unavailable");
|
|
@@ -22969,12 +23254,14 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22969
23254
|
);
|
|
22970
23255
|
await owner.initialize();
|
|
22971
23256
|
const selection = resolveModelSelection(launch, await discoverModels(owner));
|
|
22972
|
-
if (!canBegin(receipt,
|
|
23257
|
+
if (!canBegin(receipt, kernelNow()))
|
|
22973
23258
|
throw new Error("The launch expired while checking model availability");
|
|
22974
23259
|
receipt.model = selection.model;
|
|
22975
23260
|
receipt.effort = selection.reasoningEffort;
|
|
22976
23261
|
receipt.phase = "creating";
|
|
22977
23262
|
await save(receipt);
|
|
23263
|
+
if (stopping || signal.aborted || kernelNow() >= Date.parse(launch.expiresAt))
|
|
23264
|
+
throw new Error("The connection stopped or the launch expired before creation");
|
|
22978
23265
|
const created = await owner.call("thread/start", {
|
|
22979
23266
|
cwd: receipt.cwd,
|
|
22980
23267
|
projectId: project.id,
|
|
@@ -22993,6 +23280,8 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
22993
23280
|
});
|
|
22994
23281
|
receipt.phase = "starting";
|
|
22995
23282
|
await save(receipt);
|
|
23283
|
+
if (stopping || signal.aborted || kernelNow() >= Date.parse(launch.expiresAt))
|
|
23284
|
+
throw new Error("The connection stopped or the launch expired before dispatch");
|
|
22996
23285
|
const response = await owner.call("turn/start", {
|
|
22997
23286
|
threadId: receipt.threadId,
|
|
22998
23287
|
input: [{ type: "text", text: codexLaunchPrompt(launch) }],
|
|
@@ -23002,6 +23291,7 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
23002
23291
|
selection.reasoningEffort
|
|
23003
23292
|
)
|
|
23004
23293
|
});
|
|
23294
|
+
observe(receipt);
|
|
23005
23295
|
receipt.turnId = response.turn.id;
|
|
23006
23296
|
receipt.status.turnId = response.turn.id;
|
|
23007
23297
|
await save(receipt);
|
|
@@ -23048,153 +23338,181 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
23048
23338
|
await save(receipt);
|
|
23049
23339
|
}
|
|
23050
23340
|
};
|
|
23051
|
-
|
|
23052
|
-
|
|
23053
|
-
|
|
23341
|
+
lanes = new RuntimeLanes((name, error51) => {
|
|
23342
|
+
if (name === "events") journalHealthy = false;
|
|
23343
|
+
const message = error51 instanceof KernelConnectionError ? error51.message : `${name} is temporarily unavailable; recovery will retry.`;
|
|
23344
|
+
laneErrors.set(name, message);
|
|
23345
|
+
laneRetries.set(name, retryFailure(error51, laneRetries.get(name)));
|
|
23346
|
+
if (error51 instanceof KernelConnectionError && error51.fatal) {
|
|
23347
|
+
stopping = true;
|
|
23348
|
+
fatalError = message;
|
|
23349
|
+
process.stdout.write(`${error51.message} The companion has stopped.
|
|
23350
|
+
`);
|
|
23351
|
+
}
|
|
23352
|
+
});
|
|
23353
|
+
const schedule = (name, interval, work) => {
|
|
23354
|
+
if (!stopping && retryReady(laneRetries.get(name)))
|
|
23355
|
+
lanes.start(name, interval, async () => {
|
|
23356
|
+
await work();
|
|
23357
|
+
if (name === "events") journalHealthy = true;
|
|
23358
|
+
laneRetries.delete(name);
|
|
23359
|
+
laneErrors.delete(name);
|
|
23360
|
+
});
|
|
23361
|
+
};
|
|
23362
|
+
const refreshCatalog = async () => {
|
|
23054
23363
|
try {
|
|
23055
|
-
if (
|
|
23364
|
+
if (!catalogServer) {
|
|
23365
|
+
catalogServer = new AppServer(directory);
|
|
23366
|
+
await catalogServer.initialize();
|
|
23367
|
+
}
|
|
23368
|
+
if (!observer) await connectObserver();
|
|
23369
|
+
const local = await readJson(join4(directory, "pairing.json"));
|
|
23370
|
+
if (!local) throw new Error("The local connection is unavailable");
|
|
23371
|
+
const discovered = await discoverProjects(catalogServer);
|
|
23372
|
+
const authorized = discovered.filter(
|
|
23373
|
+
(project) => local.projectAccess === "all-saved" || local.projects.some((p2) => p2.id === project.id && p2.root === project.root)
|
|
23374
|
+
);
|
|
23375
|
+
const ready = await hooksReady(catalogServer, [homedir3()], hookCommand(entrypoint2));
|
|
23376
|
+
const projects = await mapConcurrent(authorized, 4, async (project) => {
|
|
23377
|
+
if (project.issue) return project;
|
|
23056
23378
|
try {
|
|
23057
|
-
|
|
23058
|
-
|
|
23059
|
-
|
|
23060
|
-
const discovered = await discoverProjects(observer);
|
|
23061
|
-
catalog2 = discovered.filter(
|
|
23062
|
-
(project) => local.projectAccess === "all-saved" || local.projects.some(
|
|
23063
|
-
(authorized) => authorized.id === project.id && authorized.root === project.root
|
|
23064
|
-
)
|
|
23065
|
-
);
|
|
23066
|
-
const ready = await hooksReady(observer, [homedir3()], hookCommand(entrypoint2));
|
|
23067
|
-
catalog2 = await Promise.all(
|
|
23068
|
-
catalog2.map(async (project) => {
|
|
23069
|
-
if (project.issue) return project;
|
|
23070
|
-
try {
|
|
23071
|
-
return await hooksReady(observer, [project.root], hookCommand(entrypoint2)) ? project : {
|
|
23072
|
-
...project,
|
|
23073
|
-
issue: "Kernel hooks are disabled or need review for this project. Open /hooks in Codex CLI from its folder."
|
|
23074
|
-
};
|
|
23075
|
-
} catch {
|
|
23076
|
-
return {
|
|
23077
|
-
...project,
|
|
23078
|
-
issue: "This project\u2019s Codex configuration could not be read."
|
|
23079
|
-
};
|
|
23080
|
-
}
|
|
23081
|
-
})
|
|
23082
|
-
);
|
|
23083
|
-
const { stdout } = await execute(codexExecutable(), ["--version"]);
|
|
23084
|
-
const desktop = await execute("/usr/libexec/PlistBuddy", [
|
|
23085
|
-
"-c",
|
|
23086
|
-
"Print:CFBundleShortVersionString",
|
|
23087
|
-
"/Applications/ChatGPT.app/Contents/Info.plist"
|
|
23088
|
-
]).then((result) => result.stdout.trim()).catch(() => "");
|
|
23089
|
-
const models = await discoverModels(observer);
|
|
23090
|
-
health = {
|
|
23091
|
-
reachable: true,
|
|
23092
|
-
hooksReady: ready,
|
|
23093
|
-
supportsModelSelection: true,
|
|
23094
|
-
supportsThreadLinking: true,
|
|
23095
|
-
supportsTaskNamePrefix: true,
|
|
23096
|
-
supportsDrafts: true,
|
|
23097
|
-
models,
|
|
23098
|
-
appServerVersion: stdout.trim(),
|
|
23099
|
-
desktopVersion: desktop,
|
|
23100
|
-
issue: ready ? null : "Review the eight Kernel hooks using /hooks in Codex CLI. The companion detects completed review automatically."
|
|
23101
|
-
};
|
|
23102
|
-
} catch (error51) {
|
|
23103
|
-
observer?.close();
|
|
23104
|
-
observer = void 0;
|
|
23105
|
-
health = {
|
|
23106
|
-
...health,
|
|
23107
|
-
reachable: false,
|
|
23108
|
-
hooksReady: false,
|
|
23109
|
-
models: [],
|
|
23110
|
-
issue: error51 instanceof CodexModelSelectionError ? error51.message : "Codex could not be reached through its supported app-server interface."
|
|
23379
|
+
return await hooksReady(catalogServer, [project.root], hookCommand(entrypoint2)) ? project : {
|
|
23380
|
+
...project,
|
|
23381
|
+
issue: "Kernel hooks are disabled or need review for this project. Open /hooks in Codex CLI from its folder."
|
|
23111
23382
|
};
|
|
23383
|
+
} catch {
|
|
23384
|
+
return { ...project, issue: "This project\u2019s Codex configuration could not be read." };
|
|
23112
23385
|
}
|
|
23386
|
+
});
|
|
23387
|
+
const { stdout } = await execute(codexExecutable(), ["--version"]);
|
|
23388
|
+
const desktop = await execute("/usr/libexec/PlistBuddy", [
|
|
23389
|
+
"-c",
|
|
23390
|
+
"Print:CFBundleShortVersionString",
|
|
23391
|
+
"/Applications/ChatGPT.app/Contents/Info.plist"
|
|
23392
|
+
]).then((result) => result.stdout.trim()).catch(() => "");
|
|
23393
|
+
const models = await discoverModels(catalogServer);
|
|
23394
|
+
catalog2 = projects;
|
|
23395
|
+
health = {
|
|
23396
|
+
...health,
|
|
23397
|
+
reachable: true,
|
|
23398
|
+
hooksReady: ready,
|
|
23399
|
+
models,
|
|
23400
|
+
appServerVersion: stdout.trim(),
|
|
23401
|
+
desktopVersion: desktop,
|
|
23402
|
+
issue: ready ? null : "Review the eight Kernel hooks using /hooks in Codex CLI. The companion detects completed review automatically."
|
|
23403
|
+
};
|
|
23404
|
+
} catch (error51) {
|
|
23405
|
+
catalogServer?.close();
|
|
23406
|
+
catalogServer = void 0;
|
|
23407
|
+
health = {
|
|
23408
|
+
...health,
|
|
23409
|
+
reachable: false,
|
|
23410
|
+
hooksReady: false,
|
|
23411
|
+
issue: error51 instanceof CodexModelSelectionError ? error51.message : "Codex could not be reached through its supported app-server interface."
|
|
23412
|
+
};
|
|
23413
|
+
throw error51;
|
|
23414
|
+
}
|
|
23415
|
+
};
|
|
23416
|
+
const heartbeat = async () => {
|
|
23417
|
+
const sentAt = performance.now();
|
|
23418
|
+
const advertised = {
|
|
23419
|
+
...health,
|
|
23420
|
+
...supportsFreshness ? { supportsStatusFreshness: true } : {}
|
|
23421
|
+
};
|
|
23422
|
+
const response = await post("heartbeat", { health: advertised, projects: catalog2 });
|
|
23423
|
+
const serverTime = Date.parse(response.serverTime);
|
|
23424
|
+
if (!Number.isFinite(serverTime)) throw new Error("Kernel returned an invalid clock");
|
|
23425
|
+
clockAnchor = {
|
|
23426
|
+
server: serverTime + Math.max(0, performance.now() - sentAt),
|
|
23427
|
+
monotonic: performance.now()
|
|
23428
|
+
};
|
|
23429
|
+
supportsFreshness = response.capabilities?.statusFreshness === true;
|
|
23430
|
+
heartbeatHealth = JSON.stringify(health);
|
|
23431
|
+
refreshed = performance.now();
|
|
23432
|
+
schedule("launches", 2e3, launches);
|
|
23433
|
+
schedule("lookups", 2e3, lookups);
|
|
23434
|
+
await writeJson(join4(directory, "health.json"), {
|
|
23435
|
+
at: Date.now(),
|
|
23436
|
+
health: advertised,
|
|
23437
|
+
projects: catalog2.length
|
|
23438
|
+
});
|
|
23439
|
+
};
|
|
23440
|
+
let reconciliationCursor = 0;
|
|
23441
|
+
const reconcile = async () => {
|
|
23442
|
+
if (!observer) return;
|
|
23443
|
+
const candidates = [...receipts.values()].filter(
|
|
23444
|
+
(receipt) => receipt.phase === "creating" && !receipt.threadId && !owners.has(receipt.launch.id) && retryReady(receipt.reconciliation)
|
|
23445
|
+
);
|
|
23446
|
+
const batch = Array.from(
|
|
23447
|
+
{ length: Math.min(candidates.length, 4) },
|
|
23448
|
+
() => candidates[reconciliationCursor++ % candidates.length]
|
|
23449
|
+
);
|
|
23450
|
+
await mapConcurrent(
|
|
23451
|
+
batch,
|
|
23452
|
+
2,
|
|
23453
|
+
(receipt) => writers.run(receipt.launch.id, async () => {
|
|
23113
23454
|
try {
|
|
23114
|
-
const
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
await writeJson(join4(directory, "health.json"), {
|
|
23123
|
-
at: refreshed,
|
|
23124
|
-
health,
|
|
23125
|
-
projects: catalog2.length
|
|
23126
|
-
});
|
|
23127
|
-
} catch (error51) {
|
|
23128
|
-
networkError = error51;
|
|
23129
|
-
}
|
|
23130
|
-
}
|
|
23131
|
-
if (observer) {
|
|
23132
|
-
let recovered = false;
|
|
23133
|
-
for (const receipt of receipts.values()) {
|
|
23134
|
-
if (!reconciled && receipt.phase === "opening-draft") {
|
|
23135
|
-
receipt.phase = "drafted";
|
|
23136
|
-
receipt.draftOpenUncertain = true;
|
|
23137
|
-
}
|
|
23138
|
-
if (reconciled && (receipt.phase !== "creating" || owners.has(receipt.launch.id)))
|
|
23139
|
-
continue;
|
|
23140
|
-
if (receipt.phase === "creating" && !receipt.threadId && !owners.has(receipt.launch.id)) {
|
|
23141
|
-
const found = await findCreation(observer, receipt);
|
|
23142
|
-
if (found) {
|
|
23143
|
-
receipt.threadId = found.id;
|
|
23144
|
-
receipt.cwd = found.cwd;
|
|
23145
|
-
receipt.phase = "released";
|
|
23146
|
-
receipt.status = {
|
|
23147
|
-
...receipt.status,
|
|
23148
|
-
sequence: receipt.status.sequence + 1,
|
|
23149
|
-
state: "unreachable",
|
|
23150
|
-
message: "Recovered the created task. Open it in Codex to check its first turn."
|
|
23151
|
-
};
|
|
23152
|
-
acknowledged.delete(receipt.launch.id);
|
|
23153
|
-
recovered = true;
|
|
23154
|
-
} else {
|
|
23155
|
-
receipt.failure = "Codex creation could not be confirmed. This launch will not be repeated.";
|
|
23156
|
-
receipt.ambiguous = true;
|
|
23157
|
-
}
|
|
23158
|
-
}
|
|
23159
|
-
if (!reconciled && ["prepared", "created", "starting"].includes(receipt.phase)) {
|
|
23160
|
-
if (receipt.threadId) receipt.phase = "released";
|
|
23161
|
-
else {
|
|
23162
|
-
receipt.phase = "failed";
|
|
23163
|
-
receipt.failure = "The companion stopped before creating the task. Start again when connected.";
|
|
23164
|
-
}
|
|
23165
|
-
}
|
|
23166
|
-
if (!reconciled && receipt.phase === "released" && receipt.status.state === "running" && !receipt.status.waiting)
|
|
23455
|
+
const search = searches.get(receipt.launch.id) ?? {};
|
|
23456
|
+
searches.set(receipt.launch.id, search);
|
|
23457
|
+
const found = await findCreation(observer, receipt, search, 5);
|
|
23458
|
+
if (found) {
|
|
23459
|
+
receipt.threadId = found.id;
|
|
23460
|
+
receipt.cwd = found.cwd;
|
|
23461
|
+
receipt.phase = "released";
|
|
23462
|
+
delete receipt.delivery;
|
|
23167
23463
|
receipt.status = {
|
|
23168
23464
|
...receipt.status,
|
|
23169
23465
|
sequence: receipt.status.sequence + 1,
|
|
23170
23466
|
state: "unreachable",
|
|
23171
|
-
message: "
|
|
23467
|
+
message: "Recovered the created task. Open it in Codex to check its first turn."
|
|
23468
|
+
};
|
|
23469
|
+
await indexThreads();
|
|
23470
|
+
} else if (search.done) {
|
|
23471
|
+
receipt.failure = "Codex creation could not be confirmed. This launch will not be repeated.";
|
|
23472
|
+
receipt.ambiguous = true;
|
|
23473
|
+
searches.delete(receipt.launch.id);
|
|
23474
|
+
receipt.reconciliation = retryFailure(
|
|
23475
|
+
new Error("Creation is still uncertain"),
|
|
23476
|
+
receipt.reconciliation
|
|
23477
|
+
);
|
|
23478
|
+
}
|
|
23479
|
+
if (found) delete receipt.reconciliation;
|
|
23480
|
+
} catch (error51) {
|
|
23481
|
+
receipt.reconciliation = retryFailure(error51, receipt.reconciliation);
|
|
23482
|
+
if (error51 instanceof CreationConflictError)
|
|
23483
|
+
receipt.reconciliation.blocked = {
|
|
23484
|
+
code: "ambiguous_creation",
|
|
23485
|
+
message: "Multiple creation records need local review. This launch will not be repeated."
|
|
23172
23486
|
};
|
|
23173
|
-
await save(receipt);
|
|
23174
23487
|
}
|
|
23175
|
-
|
|
23176
|
-
|
|
23177
|
-
|
|
23178
|
-
|
|
23179
|
-
|
|
23488
|
+
await save(receipt);
|
|
23489
|
+
})
|
|
23490
|
+
);
|
|
23491
|
+
};
|
|
23492
|
+
let eventCursor = 0;
|
|
23493
|
+
const invalidEvents = /* @__PURE__ */ new Map();
|
|
23494
|
+
const localEvents = async () => {
|
|
23495
|
+
for (const receipt of watchResults.splice(0)) {
|
|
23496
|
+
if (receipts.has(receipt.launch.id) || blocked.has(receipt.launch.id)) continue;
|
|
23497
|
+
await writers.run(receipt.launch.id, async () => {
|
|
23180
23498
|
receipts.set(receipt.launch.id, receipt);
|
|
23181
23499
|
await save(receipt);
|
|
23182
|
-
|
|
23183
|
-
|
|
23184
|
-
|
|
23185
|
-
|
|
23186
|
-
|
|
23187
|
-
|
|
23188
|
-
|
|
23189
|
-
}
|
|
23190
|
-
for (const { launchId, event } of queue.splice(0)) {
|
|
23500
|
+
});
|
|
23501
|
+
await indexThreads();
|
|
23502
|
+
}
|
|
23503
|
+
const events = queue.splice(0);
|
|
23504
|
+
await mapConcurrent(
|
|
23505
|
+
events,
|
|
23506
|
+
4,
|
|
23507
|
+
({ launchId, event }) => writers.run(launchId, async () => {
|
|
23191
23508
|
const receipt = receipts.get(launchId);
|
|
23192
|
-
if (!receipt || receipt.phase === "released" || receipt.phase === "failed")
|
|
23509
|
+
if (!receipt || receipt.phase === "released" || receipt.phase === "failed") return;
|
|
23193
23510
|
if (event.method === "turn/started") {
|
|
23194
23511
|
const turn = event.params?.turn;
|
|
23195
23512
|
if (turn) {
|
|
23196
23513
|
receipt.turnId = turn.id;
|
|
23197
23514
|
receipt.status.turnId = turn.id;
|
|
23515
|
+
observe(receipt);
|
|
23198
23516
|
await save(receipt);
|
|
23199
23517
|
}
|
|
23200
23518
|
} else if (event.id !== void 0) {
|
|
@@ -23210,7 +23528,7 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
23210
23528
|
false,
|
|
23211
23529
|
turn?.status === "failed" ? "Codex ended this turn with an error. Open the task for details." : null
|
|
23212
23530
|
);
|
|
23213
|
-
} else if (event.method === "kernel/closed") {
|
|
23531
|
+
} else if (event.method === "kernel/closed" && receipt.threadId) {
|
|
23214
23532
|
receipt.phase = "released";
|
|
23215
23533
|
receipt.status = {
|
|
23216
23534
|
...receipt.status,
|
|
@@ -23220,176 +23538,342 @@ async function runCompanion(machineId, entrypoint2, signal) {
|
|
|
23220
23538
|
};
|
|
23221
23539
|
await save(receipt);
|
|
23222
23540
|
}
|
|
23223
|
-
}
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
|
|
23227
|
-
|
|
23228
|
-
|
|
23229
|
-
|
|
23230
|
-
|
|
23231
|
-
|
|
23232
|
-
|
|
23233
|
-
|
|
23234
|
-
|
|
23235
|
-
|
|
23236
|
-
|
|
23237
|
-
|
|
23238
|
-
|
|
23239
|
-
|
|
23240
|
-
|
|
23241
|
-
await indexThreads();
|
|
23242
|
-
} catch {
|
|
23243
|
-
pending.phase = "failed";
|
|
23244
|
-
pending.failure = "The draft's saved project changed. Link the task from Codex using its task link.";
|
|
23245
|
-
await save(pending);
|
|
23246
|
-
await indexThreads();
|
|
23247
|
-
}
|
|
23248
|
-
}
|
|
23541
|
+
})
|
|
23542
|
+
);
|
|
23543
|
+
const files = (await entries(join4(directory, "events"))).filter((file2) => file2.endsWith(".json")).sort();
|
|
23544
|
+
const eventBatch = Array.from(
|
|
23545
|
+
{ length: Math.min(files.length, 100) },
|
|
23546
|
+
() => files[eventCursor++ % files.length]
|
|
23547
|
+
);
|
|
23548
|
+
await mapConcurrent(eventBatch, 4, async (file2) => {
|
|
23549
|
+
const path = join4(directory, "events", file2);
|
|
23550
|
+
let event;
|
|
23551
|
+
try {
|
|
23552
|
+
event = observationSchema.parse(await readJson(path));
|
|
23553
|
+
} catch {
|
|
23554
|
+
const first = invalidEvents.get(file2);
|
|
23555
|
+
if (first === void 0) invalidEvents.set(file2, performance.now());
|
|
23556
|
+
else if (performance.now() - first >= 2e3) {
|
|
23557
|
+
await quarantine(path);
|
|
23558
|
+
invalidEvents.delete(file2);
|
|
23249
23559
|
}
|
|
23250
|
-
|
|
23251
|
-
await unlink(path);
|
|
23560
|
+
return;
|
|
23252
23561
|
}
|
|
23253
|
-
|
|
23254
|
-
|
|
23255
|
-
|
|
23256
|
-
|
|
23257
|
-
|
|
23258
|
-
|
|
23562
|
+
invalidEvents.delete(file2);
|
|
23563
|
+
const target = [...receipts.values()].find((r2) => r2.threadId === event.threadId) ?? (event.draft && receipts.get(event.draft.launchId));
|
|
23564
|
+
if (!target) return;
|
|
23565
|
+
await writers.run(target.launch.id, async () => {
|
|
23566
|
+
if (!target.threadId && event.draft && "mode" in target.launch && target.launch.mode === "draft" && ["opening-draft", "drafted"].includes(target.phase) && event.draft.cwd === target.cwd) {
|
|
23567
|
+
if (!observer) return;
|
|
23259
23568
|
try {
|
|
23260
|
-
const
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
|
|
23264
|
-
|
|
23265
|
-
|
|
23266
|
-
|
|
23267
|
-
|
|
23268
|
-
);
|
|
23269
|
-
|
|
23270
|
-
|
|
23271
|
-
if (
|
|
23272
|
-
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
|
|
23276
|
-
|
|
23277
|
-
|
|
23278
|
-
message: turn.status === "failed" ? "Codex ended this turn with an error. Open the task for details." : null
|
|
23279
|
-
};
|
|
23280
|
-
await save(receipt);
|
|
23281
|
-
} else if (receipt.status.state === "running" && !receipt.handoffWaiting && !await observedProcessAlive(receipt.pid, receipt.nativeIdentity) && receipt.hookAt) {
|
|
23282
|
-
receipt.status = {
|
|
23283
|
-
...receipt.status,
|
|
23284
|
-
sequence: receipt.status.sequence + 1,
|
|
23285
|
-
state: "unreachable",
|
|
23286
|
-
message: "The desktop execution is no longer reachable."
|
|
23287
|
-
};
|
|
23288
|
-
await save(receipt);
|
|
23289
|
-
}
|
|
23290
|
-
} catch {
|
|
23569
|
+
const local = await readJson(join4(directory, "pairing.json"));
|
|
23570
|
+
if (!local) return;
|
|
23571
|
+
const discovered = await discoverProjects(observer);
|
|
23572
|
+
if (discovered.find((p2) => p2.id === target.launch.destination.id)?.issue) return;
|
|
23573
|
+
authorizedDestination(local, target.launch, discovered);
|
|
23574
|
+
target.threadId = event.threadId;
|
|
23575
|
+
target.phase = "released";
|
|
23576
|
+
if (target.delivery) delete target.delivery.acknowledged;
|
|
23577
|
+
await save(target);
|
|
23578
|
+
await indexThreads();
|
|
23579
|
+
} catch (error51) {
|
|
23580
|
+
if (!(error51 instanceof DestinationAuthorizationError)) return;
|
|
23581
|
+
target.phase = "failed";
|
|
23582
|
+
target.failure = "The draft's saved project changed. Link the task from Codex using its task link.";
|
|
23583
|
+
await save(target);
|
|
23584
|
+
await indexThreads();
|
|
23585
|
+
await unlink(path);
|
|
23586
|
+
return;
|
|
23291
23587
|
}
|
|
23292
23588
|
}
|
|
23293
|
-
if (
|
|
23589
|
+
if (target.threadId !== event.threadId) return;
|
|
23590
|
+
if (applyObservation(target, event)) {
|
|
23591
|
+
observe(target, Math.max(0, performance.timeOrigin + performance.now() - event.at));
|
|
23592
|
+
}
|
|
23593
|
+
await save(target);
|
|
23594
|
+
await unlink(path);
|
|
23595
|
+
});
|
|
23596
|
+
});
|
|
23597
|
+
};
|
|
23598
|
+
let inspectionCursor = 0;
|
|
23599
|
+
const inspect = async () => {
|
|
23600
|
+
if (!observer || !journalHealthy) return;
|
|
23601
|
+
const all = [...receipts.values()].filter(
|
|
23602
|
+
(receipt) => receipt.threadId && (receipt.phase === "released" || owners.has(receipt.launch.id))
|
|
23603
|
+
);
|
|
23604
|
+
const batch = [];
|
|
23605
|
+
for (let n2 = 0; n2 < all.length && batch.length < 4; n2++) {
|
|
23606
|
+
const receipt = all[inspectionCursor++ % all.length];
|
|
23607
|
+
if (performance.now() - (inspected.get(receipt.launch.id) ?? -Infinity) >= (receipt.status.state === "done" ? 3e4 : 5e3))
|
|
23608
|
+
batch.push(receipt);
|
|
23609
|
+
}
|
|
23610
|
+
await mapConcurrent(
|
|
23611
|
+
batch,
|
|
23612
|
+
4,
|
|
23613
|
+
(receipt) => writers.run(receipt.launch.id, async () => {
|
|
23614
|
+
inspected.set(receipt.launch.id, performance.now());
|
|
23294
23615
|
try {
|
|
23295
|
-
if (
|
|
23296
|
-
|
|
23297
|
-
|
|
23298
|
-
|
|
23299
|
-
|
|
23300
|
-
});
|
|
23301
|
-
acknowledged.add(receipt.launch.id);
|
|
23302
|
-
} else if (receipt.threadId && receipt.cwd) {
|
|
23303
|
-
if (!acknowledged.has(receipt.launch.id)) {
|
|
23304
|
-
deliveryBudget -= 1;
|
|
23305
|
-
await post("receipt", {
|
|
23306
|
-
launchId: receipt.launch.id,
|
|
23307
|
-
threadId: receipt.threadId,
|
|
23308
|
-
cwd: receipt.cwd
|
|
23309
|
-
});
|
|
23310
|
-
acknowledged.add(receipt.launch.id);
|
|
23311
|
-
}
|
|
23312
|
-
if (deliveryBudget > 0 && reported.get(receipt.launch.id) !== receipt.status.sequence) {
|
|
23313
|
-
deliveryBudget -= 1;
|
|
23314
|
-
await post("status", { reports: [receipt.status] });
|
|
23315
|
-
reported.set(receipt.launch.id, receipt.status.sequence);
|
|
23316
|
-
}
|
|
23317
|
-
} else if (receipt.failure && !acknowledged.has(receipt.launch.id)) {
|
|
23318
|
-
deliveryBudget -= 1;
|
|
23319
|
-
await post("failure", {
|
|
23320
|
-
launchId: receipt.launch.id,
|
|
23321
|
-
ambiguous: receipt.ambiguous ?? false,
|
|
23322
|
-
message: receipt.failure
|
|
23323
|
-
});
|
|
23324
|
-
acknowledged.add(receipt.launch.id);
|
|
23616
|
+
if (owners.has(receipt.launch.id) && receipt.phase !== "released") {
|
|
23617
|
+
observe(receipt);
|
|
23618
|
+
receipt.status.sequence++;
|
|
23619
|
+
await save(receipt);
|
|
23620
|
+
return;
|
|
23325
23621
|
}
|
|
23326
|
-
|
|
23327
|
-
|
|
23328
|
-
|
|
23329
|
-
|
|
23330
|
-
if (networkError) throw networkError;
|
|
23331
|
-
if (health.reachable && health.hooksReady && reconciled && Date.now() - tickStarted <= 5e3) {
|
|
23332
|
-
const lookups = await post("thread-lookups", { cursor: watchCursor });
|
|
23333
|
-
watchCursor = lookups.nextCursor;
|
|
23334
|
-
for (const raw of lookups.watches) {
|
|
23335
|
-
const watch = codexWatchSchema.parse(raw);
|
|
23336
|
-
if (watch.machineId === machineId && !receipts.has(watch.id))
|
|
23337
|
-
watched.set(watch.id, watch);
|
|
23338
|
-
}
|
|
23339
|
-
if (!lookupBusy) {
|
|
23340
|
-
const request = lookups.requests.find(
|
|
23341
|
-
(item) => Date.parse(item.expiresAt) > Date.now() + clockOffset
|
|
23342
|
-
);
|
|
23343
|
-
const watch = [...watched.values()].find(
|
|
23344
|
-
(item) => Date.now() - (watchAttempts.get(item.id) ?? 0) > 3e4
|
|
23622
|
+
const { data } = await observer.call(
|
|
23623
|
+
"thread/turns/list",
|
|
23624
|
+
{ threadId: receipt.threadId, limit: 1, itemsView: "notLoaded" },
|
|
23625
|
+
2e3
|
|
23345
23626
|
);
|
|
23346
|
-
|
|
23347
|
-
|
|
23348
|
-
|
|
23349
|
-
|
|
23350
|
-
|
|
23351
|
-
|
|
23352
|
-
|
|
23353
|
-
|
|
23354
|
-
|
|
23355
|
-
|
|
23356
|
-
|
|
23357
|
-
|
|
23358
|
-
|
|
23359
|
-
|
|
23360
|
-
|
|
23361
|
-
|
|
23362
|
-
|
|
23363
|
-
|
|
23364
|
-
|
|
23365
|
-
|
|
23366
|
-
|
|
23367
|
-
|
|
23368
|
-
|
|
23369
|
-
|
|
23370
|
-
|
|
23371
|
-
|
|
23627
|
+
const turn = data[0];
|
|
23628
|
+
const matchesActive = receipt.status.state !== "running" || turn?.id === receipt.status.turnId;
|
|
23629
|
+
const alive = await observedProcessAlive(receipt.pid, receipt.nativeIdentity);
|
|
23630
|
+
if (turn && ["completed", "failed"].includes(turn.status) && matchesActive && !receipt.status.waiting) {
|
|
23631
|
+
receipt.status = {
|
|
23632
|
+
...receipt.status,
|
|
23633
|
+
state: "done",
|
|
23634
|
+
waiting: false,
|
|
23635
|
+
turnId: turn.id,
|
|
23636
|
+
message: turn.status === "failed" ? "Codex ended this turn with an error. Open the task for details." : null
|
|
23637
|
+
};
|
|
23638
|
+
observe(receipt);
|
|
23639
|
+
} else if ((receipt.handoffWaiting || receipt.hookAt && alive) && receipt.status.state === "running") {
|
|
23640
|
+
observe(receipt);
|
|
23641
|
+
} else {
|
|
23642
|
+
receipt.status = {
|
|
23643
|
+
...receipt.status,
|
|
23644
|
+
state: "unreachable",
|
|
23645
|
+
message: "The task\u2019s current execution state could not be verified. Open it in Codex."
|
|
23646
|
+
};
|
|
23647
|
+
observe(receipt);
|
|
23648
|
+
}
|
|
23649
|
+
receipt.status.sequence++;
|
|
23650
|
+
await save(receipt);
|
|
23651
|
+
} catch {
|
|
23652
|
+
observer?.close();
|
|
23653
|
+
observer = void 0;
|
|
23654
|
+
}
|
|
23655
|
+
})
|
|
23656
|
+
);
|
|
23657
|
+
};
|
|
23658
|
+
let deliveryCursor = 0;
|
|
23659
|
+
const deliver = async () => {
|
|
23660
|
+
if (!clockAnchor) return;
|
|
23661
|
+
const all = [...receipts.values()];
|
|
23662
|
+
const batch = [];
|
|
23663
|
+
for (let n2 = 0; n2 < all.length && batch.length < 4; n2++) {
|
|
23664
|
+
const receipt = all[deliveryCursor++ % all.length];
|
|
23665
|
+
const kind = receipt.threadId && receipt.cwd ? "receipt" : receipt.phase === "drafted" ? "draft" : receipt.failure ? "failure" : void 0;
|
|
23666
|
+
if (kind && retryReady(receipt.delivery?.retry) && (receipt.delivery?.acknowledged !== kind || kind === "receipt" && receipt.delivery?.sequence !== receipt.status.sequence))
|
|
23667
|
+
batch.push(receipt);
|
|
23668
|
+
}
|
|
23669
|
+
await mapConcurrent(batch, 4, async (receipt) => {
|
|
23670
|
+
const snapshot = await writers.run(receipt.launch.id, async () => structuredClone(receipt));
|
|
23671
|
+
const snapshotAt = performance.now();
|
|
23672
|
+
const observation2 = observations.get(receipt.launch.id);
|
|
23673
|
+
const observationAge = observation2 && observation2.wall === snapshot.observedAt ? Math.max(0, snapshotAt - observation2.monotonic) : 9e4;
|
|
23674
|
+
const delivery = snapshot.delivery ?? {};
|
|
23675
|
+
try {
|
|
23676
|
+
if (snapshot.threadId && snapshot.cwd) {
|
|
23677
|
+
if (delivery.acknowledged !== "receipt") {
|
|
23678
|
+
await post("receipt", {
|
|
23679
|
+
launchId: snapshot.launch.id,
|
|
23680
|
+
threadId: snapshot.threadId,
|
|
23681
|
+
cwd: snapshot.cwd
|
|
23372
23682
|
});
|
|
23683
|
+
delivery.acknowledged = "receipt";
|
|
23373
23684
|
}
|
|
23685
|
+
if (delivery.sequence !== snapshot.status.sequence) {
|
|
23686
|
+
await post("status", {
|
|
23687
|
+
reports: [
|
|
23688
|
+
{
|
|
23689
|
+
...snapshot.status,
|
|
23690
|
+
...supportsFreshness ? {
|
|
23691
|
+
observationAgeMs: Math.ceil(
|
|
23692
|
+
observationAge + Math.max(0, performance.now() - snapshotAt)
|
|
23693
|
+
)
|
|
23694
|
+
} : {}
|
|
23695
|
+
}
|
|
23696
|
+
]
|
|
23697
|
+
});
|
|
23698
|
+
delivery.sequence = snapshot.status.sequence;
|
|
23699
|
+
}
|
|
23700
|
+
} else if (snapshot.phase === "drafted") {
|
|
23701
|
+
await post("draft-opened", {
|
|
23702
|
+
launchId: snapshot.launch.id,
|
|
23703
|
+
...snapshot.draftOpenUncertain ? { uncertain: true } : {}
|
|
23704
|
+
});
|
|
23705
|
+
delivery.acknowledged = "draft";
|
|
23706
|
+
} else if (snapshot.failure) {
|
|
23707
|
+
await post("failure", {
|
|
23708
|
+
launchId: snapshot.launch.id,
|
|
23709
|
+
ambiguous: snapshot.ambiguous ?? false,
|
|
23710
|
+
message: snapshot.failure
|
|
23711
|
+
});
|
|
23712
|
+
delivery.acknowledged = "failure";
|
|
23713
|
+
}
|
|
23714
|
+
delete delivery.retry;
|
|
23715
|
+
delivery.lastSuccessAt = Date.now();
|
|
23716
|
+
} catch (error51) {
|
|
23717
|
+
delivery.retry = retryFailure(error51, delivery.retry);
|
|
23718
|
+
if (error51 instanceof KernelConnectionError && error51.fatal) {
|
|
23719
|
+
stopping = true;
|
|
23720
|
+
fatalError = error51.message;
|
|
23374
23721
|
}
|
|
23375
|
-
const response = await post("launches", {});
|
|
23376
|
-
for (const launch of response.launches) await begin(codexLaunchSchema.parse(launch));
|
|
23377
23722
|
}
|
|
23378
|
-
|
|
23379
|
-
|
|
23380
|
-
|
|
23381
|
-
|
|
23382
|
-
|
|
23383
|
-
|
|
23723
|
+
await writers.run(receipt.launch.id, async () => {
|
|
23724
|
+
if (delivery.acknowledged === "draft" && receipt.threadId) delete delivery.acknowledged;
|
|
23725
|
+
receipt.delivery = delivery;
|
|
23726
|
+
await save(receipt);
|
|
23727
|
+
});
|
|
23728
|
+
});
|
|
23729
|
+
for (let i2 = lookupResults.length - 1; i2 >= 0; i2--) {
|
|
23730
|
+
const id = lookupResults[i2].id;
|
|
23731
|
+
if ((lookupExpiries.get(id) ?? 0) <= kernelNow()) {
|
|
23732
|
+
lookupResults.splice(i2, 1);
|
|
23733
|
+
lookupRetries.delete(id);
|
|
23734
|
+
lookupExpiries.delete(id);
|
|
23735
|
+
}
|
|
23736
|
+
}
|
|
23737
|
+
const pending = lookupResults.filter((result) => retryReady(lookupRetries.get(result.id))).slice(0, 4);
|
|
23738
|
+
for (const result of pending) lookupResults.splice(lookupResults.indexOf(result), 1);
|
|
23739
|
+
for (const result of pending) {
|
|
23740
|
+
try {
|
|
23741
|
+
await post("thread-lookup-result", result);
|
|
23742
|
+
} catch (error51) {
|
|
23743
|
+
const retry = retryFailure(error51, lookupRetries.get(result.id));
|
|
23744
|
+
lookupRetries.set(result.id, retry);
|
|
23745
|
+
if (!retry.blocked) lookupResults.push(result);
|
|
23746
|
+
else
|
|
23747
|
+
await writeJson(join4(directory, "blocked-lookups", `${result.id}.json`), {
|
|
23748
|
+
id: result.id,
|
|
23749
|
+
...retry.blocked
|
|
23750
|
+
});
|
|
23751
|
+
if (error51 instanceof KernelConnectionError && error51.fatal) throw error51;
|
|
23384
23752
|
}
|
|
23385
23753
|
}
|
|
23386
|
-
|
|
23754
|
+
};
|
|
23755
|
+
const lookups = async () => {
|
|
23756
|
+
if (!health.reachable || !health.hooksReady || !refreshed || lookupBusy) return;
|
|
23757
|
+
const response = await post("thread-lookups", { cursor: watchCursor });
|
|
23758
|
+
watchCursor = response.nextCursor;
|
|
23759
|
+
for (const request2 of response.requests)
|
|
23760
|
+
lookupExpiries.set(request2.id, Date.parse(request2.expiresAt));
|
|
23761
|
+
for (const raw of response.watches) {
|
|
23762
|
+
const watch2 = codexWatchSchema.parse(raw);
|
|
23763
|
+
if (watch2.machineId === machineId && !receipts.has(watch2.id) && !blocked.has(watch2.id))
|
|
23764
|
+
watched.set(watch2.id, watch2);
|
|
23765
|
+
}
|
|
23766
|
+
const request = response.requests.find((item) => Date.parse(item.expiresAt) > kernelNow());
|
|
23767
|
+
const watch = [...watched.values()].find(
|
|
23768
|
+
(item) => performance.now() - (watchAttempts.get(item.id) ?? -Infinity) > 3e4
|
|
23769
|
+
);
|
|
23770
|
+
if (!request && !watch) return;
|
|
23771
|
+
lookupBusy = true;
|
|
23772
|
+
if (!request && watch) watchAttempts.set(watch.id, performance.now());
|
|
23773
|
+
lookupWork = (async () => {
|
|
23774
|
+
const local = await readJson(join4(directory, "pairing.json"));
|
|
23775
|
+
if (!local) throw new Error("The local connection is unavailable");
|
|
23776
|
+
const server = await connectLookup();
|
|
23777
|
+
if (request) {
|
|
23778
|
+
const result = await deadline(
|
|
23779
|
+
lookupService.lookup(server, request, local, catalog2),
|
|
23780
|
+
25e3
|
|
23781
|
+
);
|
|
23782
|
+
lookupResults.push({ id: request.id, ...result, error: null });
|
|
23783
|
+
} else if (watch) {
|
|
23784
|
+
watchResults.push(await linkedReceipt(server, watch, local, catalog2));
|
|
23785
|
+
watched.delete(watch.id);
|
|
23786
|
+
}
|
|
23787
|
+
})().catch(() => {
|
|
23788
|
+
lookupServer?.close();
|
|
23789
|
+
lookupServer = void 0;
|
|
23790
|
+
if (request)
|
|
23791
|
+
lookupResults.push({
|
|
23792
|
+
id: request.id,
|
|
23793
|
+
results: [],
|
|
23794
|
+
hasMore: false,
|
|
23795
|
+
error: "The local task could not be read. Check Codex and the saved project, then try again."
|
|
23796
|
+
});
|
|
23797
|
+
}).finally(() => {
|
|
23798
|
+
lookupBusy = false;
|
|
23799
|
+
});
|
|
23800
|
+
};
|
|
23801
|
+
const launches = async () => {
|
|
23802
|
+
if (!journalHealthy || !health.reachable || !health.hooksReady || !refreshed || performance.now() - refreshed >= 3e4 || !observer)
|
|
23803
|
+
return;
|
|
23804
|
+
const response = await post("launches", {});
|
|
23805
|
+
await mapConcurrent(response.launches, 4, (raw) => {
|
|
23806
|
+
const launch = codexLaunchSchema.parse(raw);
|
|
23807
|
+
return writers.run(launch.id, () => begin(launch)).then(
|
|
23808
|
+
() => schedule("events", 0, async () => {
|
|
23809
|
+
await localEvents();
|
|
23810
|
+
schedule("delivery", 0, deliver);
|
|
23811
|
+
})
|
|
23812
|
+
);
|
|
23813
|
+
});
|
|
23814
|
+
};
|
|
23815
|
+
const diagnostics = async () => {
|
|
23816
|
+
const pending = [...receipts.values()].filter((r2) => {
|
|
23817
|
+
const kind = r2.threadId && r2.cwd ? "receipt" : r2.phase === "drafted" ? "draft" : r2.failure ? "failure" : void 0;
|
|
23818
|
+
return kind && (r2.delivery?.acknowledged !== kind || kind === "receipt" && r2.delivery?.sequence !== r2.status.sequence);
|
|
23819
|
+
});
|
|
23820
|
+
const successes = [...receipts.values()].flatMap(
|
|
23821
|
+
(r2) => r2.delivery?.lastSuccessAt ? [r2.delivery.lastSuccessAt] : []
|
|
23822
|
+
);
|
|
23823
|
+
await writeJson(join4(directory, "recovery.json"), {
|
|
23824
|
+
lastSuccessfulDelivery: successes.length ? Math.max(...successes) : null,
|
|
23825
|
+
pending: pending.filter((r2) => !r2.delivery?.retry?.blocked).length,
|
|
23826
|
+
blocked: blocked.size + [...receipts.values()].filter(
|
|
23827
|
+
(r2) => r2.delivery?.retry?.blocked || r2.reconciliation?.blocked
|
|
23828
|
+
).length + (await entries(join4(directory, "blocked-lookups"))).length,
|
|
23829
|
+
oldestPendingAt: pending.length ? Math.min(...pending.map((r2) => r2.delivery?.retry?.pendingSince ?? r2.preparedAt)) : null,
|
|
23830
|
+
issue: fatalError ?? [...laneErrors.values()][0] ?? null,
|
|
23831
|
+
failures: [...blocked].map((launchId) => ({
|
|
23832
|
+
launchId,
|
|
23833
|
+
code: "invalid_receipt",
|
|
23834
|
+
message: "A damaged receipt was isolated for local recovery. This launch will not be repeated."
|
|
23835
|
+
})).concat(
|
|
23836
|
+
[...receipts.values()].filter((r2) => r2.delivery?.retry?.blocked || r2.reconciliation?.blocked).map((r2) => ({
|
|
23837
|
+
launchId: r2.launch.id,
|
|
23838
|
+
...r2.delivery?.retry?.blocked ?? r2.reconciliation?.blocked
|
|
23839
|
+
}))
|
|
23840
|
+
)
|
|
23841
|
+
});
|
|
23842
|
+
};
|
|
23843
|
+
persistDiagnostics = diagnostics;
|
|
23844
|
+
while (!signal.aborted && !stopping) {
|
|
23845
|
+
schedule("catalog", 1e4, async () => {
|
|
23846
|
+
await refreshCatalog();
|
|
23847
|
+
schedule("heartbeat", 0, heartbeat);
|
|
23848
|
+
});
|
|
23849
|
+
schedule("heartbeat", heartbeatHealth === JSON.stringify(health) ? 1e4 : 0, heartbeat);
|
|
23850
|
+
schedule("events", 0, localEvents);
|
|
23851
|
+
schedule("reconciliation", 2e3, reconcile);
|
|
23852
|
+
schedule("observation", 2e3, inspect);
|
|
23853
|
+
schedule("delivery", 0, deliver);
|
|
23854
|
+
schedule("lookups", 2e3, lookups);
|
|
23855
|
+
schedule("launches", 2e3, launches);
|
|
23856
|
+
schedule("diagnostics", 2e3, diagnostics);
|
|
23857
|
+
await lanes.settle();
|
|
23858
|
+
await delay(250, void 0, { signal }).catch(() => void 0);
|
|
23387
23859
|
}
|
|
23388
23860
|
} finally {
|
|
23861
|
+
stopping = true;
|
|
23389
23862
|
await Promise.allSettled([...owners.values()].map((server) => server.stop()));
|
|
23390
23863
|
await observer?.stop().catch(() => void 0);
|
|
23864
|
+
await catalogServer?.stop().catch(() => void 0);
|
|
23391
23865
|
await lookupServer?.stop().catch(() => void 0);
|
|
23866
|
+
await lanes?.drain();
|
|
23392
23867
|
await lookupWork;
|
|
23868
|
+
await Promise.allSettled(
|
|
23869
|
+
[
|
|
23870
|
+
...owners.values(),
|
|
23871
|
+
...observer ? [observer] : [],
|
|
23872
|
+
...catalogServer ? [catalogServer] : [],
|
|
23873
|
+
...lookupServer ? [lookupServer] : []
|
|
23874
|
+
].map((server) => server.stop())
|
|
23875
|
+
);
|
|
23876
|
+
await persistDiagnostics?.().catch(() => void 0);
|
|
23393
23877
|
await unlock();
|
|
23394
23878
|
}
|
|
23395
23879
|
}
|
|
@@ -23580,7 +24064,7 @@ import { setTimeout as delay2 } from "node:timers/promises";
|
|
|
23580
24064
|
import { createInterface } from "node:readline/promises";
|
|
23581
24065
|
|
|
23582
24066
|
// src/setup.ts
|
|
23583
|
-
import { chmod as chmod2, copyFile, mkdir as mkdir4, rename as
|
|
24067
|
+
import { chmod as chmod2, copyFile, mkdir as mkdir4, rename as rename3, rm } from "node:fs/promises";
|
|
23584
24068
|
import { homedir as homedir4 } from "node:os";
|
|
23585
24069
|
import { dirname as dirname2, join as join6, resolve as resolve2 } from "node:path";
|
|
23586
24070
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
@@ -23592,7 +24076,7 @@ async function installBundle(entrypoint2) {
|
|
|
23592
24076
|
try {
|
|
23593
24077
|
await copyFile(entrypoint2, temporary);
|
|
23594
24078
|
await chmod2(temporary, 448);
|
|
23595
|
-
await
|
|
24079
|
+
await rename3(temporary, installedEntrypoint);
|
|
23596
24080
|
} finally {
|
|
23597
24081
|
await rm(temporary, { force: true });
|
|
23598
24082
|
}
|
|
@@ -23639,12 +24123,12 @@ async function installService(id) {
|
|
|
23639
24123
|
|
|
23640
24124
|
// src/onboarding.ts
|
|
23641
24125
|
async function waitForStoppedCompanion(directory) {
|
|
23642
|
-
const
|
|
24126
|
+
const deadline2 = Date.now() + 2e4;
|
|
23643
24127
|
for (; ; ) {
|
|
23644
24128
|
try {
|
|
23645
24129
|
return await processLock(directory);
|
|
23646
24130
|
} catch (error51) {
|
|
23647
|
-
if (Date.now() >=
|
|
24131
|
+
if (Date.now() >= deadline2) throw error51;
|
|
23648
24132
|
await delay2(200);
|
|
23649
24133
|
}
|
|
23650
24134
|
}
|
|
@@ -23771,10 +24255,11 @@ async function connectCodex(entrypoint2) {
|
|
|
23771
24255
|
else process.stdout.write("Choose your project mapping in Kernel \u2192 Integrations \u2192 Codex.\n");
|
|
23772
24256
|
}
|
|
23773
24257
|
async function status() {
|
|
23774
|
-
const profile = await
|
|
23775
|
-
|
|
24258
|
+
const profile = await readProfile();
|
|
24259
|
+
if (!profile?.session) throw new Error("Run krnl login first");
|
|
24260
|
+
process.stdout.write(`Account: ${profile.session.account} (saved locally)
|
|
23776
24261
|
`);
|
|
23777
|
-
const id = profile.session.machineId;
|
|
24262
|
+
const id = profile.session.machineId ?? profile.pendingMachineId;
|
|
23778
24263
|
if (!id) {
|
|
23779
24264
|
process.stdout.write("Codex is not connected. Run krnl connect codex.\n");
|
|
23780
24265
|
return;
|
|
@@ -23793,6 +24278,32 @@ Saved projects: ${health.projects}
|
|
|
23793
24278
|
if (health.health.issue) process.stdout.write(`${health.health.issue}
|
|
23794
24279
|
`);
|
|
23795
24280
|
}
|
|
24281
|
+
const recovery = await readJson(join7(pairingDirectory(id), "recovery.json"));
|
|
24282
|
+
if (recovery) {
|
|
24283
|
+
process.stdout.write(
|
|
24284
|
+
`Pending deliveries: ${recovery.pending} \xB7 Blocked: ${recovery.blocked}
|
|
24285
|
+
`
|
|
24286
|
+
);
|
|
24287
|
+
if (recovery.lastSuccessfulDelivery)
|
|
24288
|
+
process.stdout.write(
|
|
24289
|
+
`Last successful delivery: ${new Date(recovery.lastSuccessfulDelivery).toISOString()}
|
|
24290
|
+
`
|
|
24291
|
+
);
|
|
24292
|
+
if (recovery.oldestPendingAt)
|
|
24293
|
+
process.stdout.write(
|
|
24294
|
+
`Oldest pending delivery: ${Math.max(0, Math.floor((Date.now() - recovery.oldestPendingAt) / 1e3))} seconds
|
|
24295
|
+
`
|
|
24296
|
+
);
|
|
24297
|
+
if (recovery.issue) process.stdout.write(`${recovery.issue}
|
|
24298
|
+
`);
|
|
24299
|
+
for (const failure of recovery.failures)
|
|
24300
|
+
process.stdout.write(`${failure.launchId}: ${failure.message}
|
|
24301
|
+
`);
|
|
24302
|
+
if (recovery.blocked)
|
|
24303
|
+
process.stdout.write(
|
|
24304
|
+
"Blocked receipts remain on this Mac. Do not remove them or repeat their launches; use the existing Codex task while repairing the connection.\n"
|
|
24305
|
+
);
|
|
24306
|
+
}
|
|
23796
24307
|
}
|
|
23797
24308
|
async function disconnect(logout = false) {
|
|
23798
24309
|
const profile = await readProfile();
|
|
@@ -23907,7 +24418,7 @@ async function main() {
|
|
|
23907
24418
|
}
|
|
23908
24419
|
if (command === "--version") {
|
|
23909
24420
|
process.stdout.write(
|
|
23910
|
-
`${false ? "development" : "0.1.
|
|
24421
|
+
`${false ? "development" : "0.1.5"}
|
|
23911
24422
|
`
|
|
23912
24423
|
);
|
|
23913
24424
|
return;
|