remote-codex 0.11.12 → 0.11.13
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/apps/relay-server/dist/index.js +15 -2
- package/apps/supervisor-api/dist/{chunk-TQUNANIS.js → chunk-ZTQPMDTP.js} +632 -16
- package/apps/supervisor-api/dist/index.js +1 -1
- package/apps/supervisor-api/dist/worker-index.js +1 -1
- package/apps/supervisor-web/dist/assets/index-D3I41SIH.css +1 -0
- package/apps/supervisor-web/dist/assets/index-Z5yu-xV3.js +6 -0
- package/apps/supervisor-web/dist/assets/thread-ui-q6mjcjXn.js +3614 -0
- package/apps/supervisor-web/dist/index.html +3 -3
- package/package.json +1 -1
- package/packages/db/migrations/0027_harness_job_watches.sql +24 -0
- package/packages/db/src/repositories.ts +123 -0
- package/packages/db/src/schema.ts +29 -0
- package/packages/shared/src/index.ts +1 -0
- package/apps/supervisor-web/dist/assets/index-Cp9GkemI.css +0 -1
- package/apps/supervisor-web/dist/assets/index-Dsq8QmDr.js +0 -6
- package/apps/supervisor-web/dist/assets/thread-ui-DldLSgqC.js +0 -3604
|
@@ -98,6 +98,7 @@ var envSchema = z.object({
|
|
|
98
98
|
ELAGENTE_HARNESS_BASE_URL: z.string().url().optional(),
|
|
99
99
|
INACT_X_APP_KEY: z.string().min(1).optional(),
|
|
100
100
|
REMOTE_CODEX_CHEMISTRY_TOOLS_ENABLED: z.string().optional(),
|
|
101
|
+
REMOTE_CODEX_HARNESS_WAKEUP_CALLBACK_BASE_URL: z.string().url().optional(),
|
|
101
102
|
REMOTE_CODEX_WORKER_RUNTIME_MANIFEST: z.string().min(1).optional(),
|
|
102
103
|
APP_NAME: z.string().min(1).optional(),
|
|
103
104
|
APP_VERSION: z.string().min(1).optional(),
|
|
@@ -209,6 +210,7 @@ function loadRuntimeConfig(env = process.env) {
|
|
|
209
210
|
harnessBaseUrl: parsed.ELAGENTE_HARNESS_BASE_URL ?? null,
|
|
210
211
|
harnessEnabled: Boolean(parsed.ELAGENTE_HARNESS_BASE_URL && parsed.INACT_X_APP_KEY),
|
|
211
212
|
chemistryToolsEnabled: parseBoolean(parsed.REMOTE_CODEX_CHEMISTRY_TOOLS_ENABLED, false),
|
|
213
|
+
harnessWakeupCallbackBaseUrl: parsed.REMOTE_CODEX_HARNESS_WAKEUP_CALLBACK_BASE_URL?.replace(/\/+$/, "") ?? null,
|
|
212
214
|
workerRuntimeManifestPath: parsed.REMOTE_CODEX_WORKER_RUNTIME_MANIFEST ? path.resolve(parsed.REMOTE_CODEX_WORKER_RUNTIME_MANIFEST) : runtimeRole === "worker" ? "/opt/remote-codex/worker-runtime-manifest.json" : null,
|
|
213
215
|
appName: parsed.APP_NAME ?? (runtimeRole === "worker" ? "Remote Codex Worker" : "Remote Codex Supervisor"),
|
|
214
216
|
appVersion: parsed.APP_VERSION ?? "0.1.0",
|
|
@@ -6390,6 +6392,8 @@ __export(schema_exports, {
|
|
|
6390
6392
|
controlUsageImportState: () => controlUsageImportState,
|
|
6391
6393
|
controlUsers: () => controlUsers,
|
|
6392
6394
|
controlWorkspaces: () => controlWorkspaces,
|
|
6395
|
+
harnessJobWatches: () => harnessJobWatches,
|
|
6396
|
+
harnessNotifyRegistrations: () => harnessNotifyRegistrations,
|
|
6393
6397
|
hosts: () => hosts,
|
|
6394
6398
|
notifications: () => notifications,
|
|
6395
6399
|
policies: () => policies,
|
|
@@ -6855,6 +6859,33 @@ var controlAuditLogs = sqliteTable("control_audit_logs", {
|
|
|
6855
6859
|
metadataJson: text("metadata_json").notNull(),
|
|
6856
6860
|
createdAt: text("created_at").notNull()
|
|
6857
6861
|
});
|
|
6862
|
+
var harnessNotifyRegistrations = sqliteTable("harness_notify_registrations", {
|
|
6863
|
+
id: text("id").primaryKey(),
|
|
6864
|
+
agentId: text("agent_id").notNull(),
|
|
6865
|
+
hookToken: text("hook_token").notNull(),
|
|
6866
|
+
secret: text("secret").notNull(),
|
|
6867
|
+
callbackUrl: text("callback_url").notNull(),
|
|
6868
|
+
registeredAt: text("registered_at").notNull(),
|
|
6869
|
+
updatedAt: text("updated_at").notNull()
|
|
6870
|
+
});
|
|
6871
|
+
var harnessJobWatches = sqliteTable(
|
|
6872
|
+
"harness_job_watches",
|
|
6873
|
+
{
|
|
6874
|
+
id: text("id").primaryKey(),
|
|
6875
|
+
jobId: text("job_id").notNull(),
|
|
6876
|
+
threadId: text("thread_id").notNull(),
|
|
6877
|
+
title: text("title"),
|
|
6878
|
+
status: text("status").notNull().default("pending"),
|
|
6879
|
+
lastJobStatus: text("last_job_status"),
|
|
6880
|
+
lastError: text("last_error"),
|
|
6881
|
+
createdAt: text("created_at").notNull(),
|
|
6882
|
+
updatedAt: text("updated_at").notNull(),
|
|
6883
|
+
deliveredAt: text("delivered_at")
|
|
6884
|
+
},
|
|
6885
|
+
(table) => ({
|
|
6886
|
+
jobIdUnique: uniqueIndex("harness_job_watches_job_id_idx").on(table.jobId)
|
|
6887
|
+
})
|
|
6888
|
+
);
|
|
6858
6889
|
|
|
6859
6890
|
// ../../packages/db/src/client.ts
|
|
6860
6891
|
function resolvePlatform() {
|
|
@@ -7394,6 +7425,72 @@ function deleteNotificationsByThreadId(db, threadId) {
|
|
|
7394
7425
|
function deleteWorkspaceRecord(db, id) {
|
|
7395
7426
|
db.delete(workspaces).where(eq(workspaces.id, id)).run();
|
|
7396
7427
|
}
|
|
7428
|
+
function getHarnessNotifyRegistration(db) {
|
|
7429
|
+
return db.select().from(harnessNotifyRegistrations).where(eq(harnessNotifyRegistrations.id, "default")).get();
|
|
7430
|
+
}
|
|
7431
|
+
function upsertHarnessNotifyRegistration(db, input) {
|
|
7432
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7433
|
+
const existing = getHarnessNotifyRegistration(db);
|
|
7434
|
+
if (existing) {
|
|
7435
|
+
db.update(harnessNotifyRegistrations).set({
|
|
7436
|
+
agentId: input.agentId,
|
|
7437
|
+
hookToken: input.hookToken,
|
|
7438
|
+
secret: input.secret,
|
|
7439
|
+
callbackUrl: input.callbackUrl,
|
|
7440
|
+
updatedAt: now
|
|
7441
|
+
}).where(eq(harnessNotifyRegistrations.id, "default")).run();
|
|
7442
|
+
return getHarnessNotifyRegistration(db);
|
|
7443
|
+
}
|
|
7444
|
+
const record = {
|
|
7445
|
+
id: "default",
|
|
7446
|
+
agentId: input.agentId,
|
|
7447
|
+
hookToken: input.hookToken,
|
|
7448
|
+
secret: input.secret,
|
|
7449
|
+
callbackUrl: input.callbackUrl,
|
|
7450
|
+
registeredAt: now,
|
|
7451
|
+
updatedAt: now
|
|
7452
|
+
};
|
|
7453
|
+
db.insert(harnessNotifyRegistrations).values(record).run();
|
|
7454
|
+
return record;
|
|
7455
|
+
}
|
|
7456
|
+
function getHarnessJobWatchByJobId(db, jobId) {
|
|
7457
|
+
return db.select().from(harnessJobWatches).where(eq(harnessJobWatches.jobId, jobId)).get();
|
|
7458
|
+
}
|
|
7459
|
+
function listHarnessJobWatches(db) {
|
|
7460
|
+
return db.select().from(harnessJobWatches).orderBy(desc(harnessJobWatches.createdAt)).all();
|
|
7461
|
+
}
|
|
7462
|
+
function listPendingHarnessJobWatches(db) {
|
|
7463
|
+
return db.select().from(harnessJobWatches).where(eq(harnessJobWatches.status, "pending")).all();
|
|
7464
|
+
}
|
|
7465
|
+
function upsertHarnessJobWatch(db, input) {
|
|
7466
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7467
|
+
const existing = getHarnessJobWatchByJobId(db, input.jobId);
|
|
7468
|
+
if (existing) {
|
|
7469
|
+
db.update(harnessJobWatches).set({
|
|
7470
|
+
threadId: input.threadId,
|
|
7471
|
+
title: input.title ?? existing.title,
|
|
7472
|
+
updatedAt: now
|
|
7473
|
+
}).where(eq(harnessJobWatches.id, existing.id)).run();
|
|
7474
|
+
return getHarnessJobWatchByJobId(db, input.jobId);
|
|
7475
|
+
}
|
|
7476
|
+
const record = {
|
|
7477
|
+
id: randomUUID(),
|
|
7478
|
+
jobId: input.jobId,
|
|
7479
|
+
threadId: input.threadId,
|
|
7480
|
+
title: input.title ?? null,
|
|
7481
|
+
status: "pending",
|
|
7482
|
+
lastJobStatus: null,
|
|
7483
|
+
lastError: null,
|
|
7484
|
+
createdAt: now,
|
|
7485
|
+
updatedAt: now,
|
|
7486
|
+
deliveredAt: null
|
|
7487
|
+
};
|
|
7488
|
+
db.insert(harnessJobWatches).values(record).run();
|
|
7489
|
+
return record;
|
|
7490
|
+
}
|
|
7491
|
+
function updateHarnessJobWatch(db, id, input) {
|
|
7492
|
+
db.update(harnessJobWatches).set({ ...input, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(harnessJobWatches.id, id)).run();
|
|
7493
|
+
}
|
|
7397
7494
|
|
|
7398
7495
|
// ../../packages/db/src/seed.ts
|
|
7399
7496
|
var defaultPolicies = [
|
|
@@ -16200,6 +16297,15 @@ function parseUuidV7Timestamp2(id) {
|
|
|
16200
16297
|
}
|
|
16201
16298
|
return new Date(millis).toISOString();
|
|
16202
16299
|
}
|
|
16300
|
+
function normalizeHistoryItemCreatedAt(item, fallback) {
|
|
16301
|
+
if (item.createdAt) {
|
|
16302
|
+
return item;
|
|
16303
|
+
}
|
|
16304
|
+
return {
|
|
16305
|
+
...item,
|
|
16306
|
+
createdAt: parseUuidV7Timestamp2(item.id) ?? fallback
|
|
16307
|
+
};
|
|
16308
|
+
}
|
|
16203
16309
|
function summarizeText(text2, fallback) {
|
|
16204
16310
|
const lines = text2.replace(/\r\n/g, "\n").split("\n");
|
|
16205
16311
|
while (lines.length > 1 && lines.at(-1)?.trim() === "") {
|
|
@@ -16552,13 +16658,17 @@ function mergePersistedHistoryItemsIntoTurns(turns, persistedItemsByTurnId, defe
|
|
|
16552
16658
|
});
|
|
16553
16659
|
}
|
|
16554
16660
|
function agentTurnToThreadTurnDto(turn, deferredDetails) {
|
|
16661
|
+
const startedAt = turn.startedAt ?? parseUuidV7Timestamp2(turn.providerTurnId);
|
|
16555
16662
|
const baseTurn = {
|
|
16556
16663
|
id: turn.providerTurnId,
|
|
16557
|
-
startedAt
|
|
16664
|
+
startedAt,
|
|
16558
16665
|
status: turn.status,
|
|
16559
16666
|
error: turn.error?.message ?? null,
|
|
16560
16667
|
items: visibleRuntimeTurnItems(turn.items).map(
|
|
16561
|
-
(item, transcriptIndex) =>
|
|
16668
|
+
(item, transcriptIndex) => normalizeHistoryItemCreatedAt(
|
|
16669
|
+
item.transcriptOrder === transcriptIndex ? item : { ...item, transcriptOrder: transcriptIndex },
|
|
16670
|
+
startedAt
|
|
16671
|
+
)
|
|
16562
16672
|
)
|
|
16563
16673
|
};
|
|
16564
16674
|
return deferredDetails ? deferLargeHistoryItemDetails(baseTurn, deferredDetails) : baseTurn;
|
|
@@ -21005,11 +21115,18 @@ function harnessDeveloperInstructions(config) {
|
|
|
21005
21115
|
return null;
|
|
21006
21116
|
}
|
|
21007
21117
|
const baseUrl = config.harnessBaseUrl.replace(/\/+$/, "");
|
|
21008
|
-
|
|
21118
|
+
const lines = [
|
|
21009
21119
|
`ElAgente Harness chemistry tools are available at ${baseUrl}.`,
|
|
21010
21120
|
"For chemistry tasks, call its HTTP API directly using the sandbox env var INACT_X_APP_KEY as the x-api-key header; never print or expose that key.",
|
|
21011
21121
|
"Discover tools with GET /, GET /farmaco/tools, GET /farmaco/.help, GET /quntur/tools, or GET /estructural/tools; invoke approved tools with POST /{module}/tools/{tool} using JSON input."
|
|
21012
|
-
]
|
|
21122
|
+
];
|
|
21123
|
+
if (config.harnessWakeupCallbackBaseUrl) {
|
|
21124
|
+
const supervisorBaseUrl = `http://127.0.0.1:${config.port}`;
|
|
21125
|
+
lines.push(
|
|
21126
|
+
`For long-running compute jobs you do not need to stay running: first GET ${supervisorBaseUrl}/api/harness/wakeup and read "notifyTo"; submit the job with "notify_to" set to that value. If you submit directly to Harness rather than through the Remote Codex Harness invoke proxy, immediately register POST ${supervisorBaseUrl}/api/harness/job-watches with JSON {"jobId": "<job id>"}. After that you may end your turn; this thread is woken with a new message when the job reaches a terminal status.`
|
|
21127
|
+
);
|
|
21128
|
+
}
|
|
21129
|
+
return lines.join(" ");
|
|
21013
21130
|
}
|
|
21014
21131
|
function combineDeveloperInstructions(parts) {
|
|
21015
21132
|
const normalized = parts.map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
@@ -22690,6 +22807,14 @@ var harnessToolParamSchema = harnessModuleParamSchema.extend({
|
|
|
22690
22807
|
tool: z4.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9_-]+$/)
|
|
22691
22808
|
});
|
|
22692
22809
|
var harnessInvokeBodySchema = z4.record(z4.string(), z4.unknown());
|
|
22810
|
+
var harnessJobWatchBodySchema = z4.object({
|
|
22811
|
+
jobId: z4.string().trim().min(1).max(200).regex(/^[a-zA-Z0-9_.:-]+$/),
|
|
22812
|
+
threadId: z4.string().trim().min(1).max(200).optional(),
|
|
22813
|
+
title: z4.string().trim().min(1).max(300).optional()
|
|
22814
|
+
});
|
|
22815
|
+
var harnessHookParamSchema = z4.object({
|
|
22816
|
+
token: z4.string().trim().min(1).max(200).regex(/^[a-zA-Z0-9_-]+$/)
|
|
22817
|
+
});
|
|
22693
22818
|
var harnessInvokeContextSchema = z4.object({
|
|
22694
22819
|
workspaceId: z4.string().uuid().nullable().optional(),
|
|
22695
22820
|
sessionId: z4.string().uuid().nullable().optional(),
|
|
@@ -23033,6 +23158,19 @@ async function registerSystemRoutes(app) {
|
|
|
23033
23158
|
metadata: harnessUsageMetadata(payload, attributionSource)
|
|
23034
23159
|
}).catch(() => void 0);
|
|
23035
23160
|
}
|
|
23161
|
+
const invokeJobId = stringField2(result, ["job_id", "jobId", "compute_job_id", "computeJobId"]);
|
|
23162
|
+
if (invokeJobId && context.threadId && app.services.harnessWakeupService.enabled()) {
|
|
23163
|
+
await app.services.harnessWakeupService.watchJob({
|
|
23164
|
+
jobId: invokeJobId,
|
|
23165
|
+
threadId: context.threadId,
|
|
23166
|
+
title: `${params.module}/${params.tool}`
|
|
23167
|
+
}).catch((watchError) => {
|
|
23168
|
+
request.log.warn(
|
|
23169
|
+
{ err: watchError, jobId: invokeJobId },
|
|
23170
|
+
"Harness wakeup auto-watch failed."
|
|
23171
|
+
);
|
|
23172
|
+
});
|
|
23173
|
+
}
|
|
23036
23174
|
return payload;
|
|
23037
23175
|
} catch (error) {
|
|
23038
23176
|
if (error instanceof HttpError) {
|
|
@@ -23044,6 +23182,68 @@ async function registerSystemRoutes(app) {
|
|
|
23044
23182
|
});
|
|
23045
23183
|
}
|
|
23046
23184
|
});
|
|
23185
|
+
app.get("/api/harness/wakeup", async () => {
|
|
23186
|
+
try {
|
|
23187
|
+
return await app.services.harnessWakeupService.getWakeupInfo();
|
|
23188
|
+
} catch (error) {
|
|
23189
|
+
if (error instanceof HttpError) {
|
|
23190
|
+
throw error;
|
|
23191
|
+
}
|
|
23192
|
+
throw new HttpError(503, {
|
|
23193
|
+
code: "harness_unavailable",
|
|
23194
|
+
message: error instanceof Error ? error.message : "ElAgenteHarness is unavailable."
|
|
23195
|
+
});
|
|
23196
|
+
}
|
|
23197
|
+
});
|
|
23198
|
+
app.get("/api/harness/job-watches", async () => {
|
|
23199
|
+
return {
|
|
23200
|
+
watches: listHarnessJobWatches(app.services.database.db)
|
|
23201
|
+
};
|
|
23202
|
+
});
|
|
23203
|
+
app.post("/api/harness/job-watches", async (request, reply) => {
|
|
23204
|
+
const body = harnessJobWatchBodySchema.parse(request.body ?? {});
|
|
23205
|
+
try {
|
|
23206
|
+
const result = await app.services.harnessWakeupService.watchJob({
|
|
23207
|
+
jobId: body.jobId,
|
|
23208
|
+
threadId: body.threadId ?? null,
|
|
23209
|
+
title: body.title ?? null
|
|
23210
|
+
});
|
|
23211
|
+
reply.status(201);
|
|
23212
|
+
return result;
|
|
23213
|
+
} catch (error) {
|
|
23214
|
+
if (error instanceof HttpError) {
|
|
23215
|
+
throw error;
|
|
23216
|
+
}
|
|
23217
|
+
throw new HttpError(503, {
|
|
23218
|
+
code: "harness_unavailable",
|
|
23219
|
+
message: error instanceof Error ? error.message : "ElAgenteHarness is unavailable."
|
|
23220
|
+
});
|
|
23221
|
+
}
|
|
23222
|
+
});
|
|
23223
|
+
app.register(async (hookApp) => {
|
|
23224
|
+
hookApp.addContentTypeParser(
|
|
23225
|
+
["application/json", "text/plain"],
|
|
23226
|
+
{ parseAs: "buffer" },
|
|
23227
|
+
(_request, body, done) => done(null, body)
|
|
23228
|
+
);
|
|
23229
|
+
hookApp.addContentTypeParser(
|
|
23230
|
+
"*",
|
|
23231
|
+
{ parseAs: "buffer" },
|
|
23232
|
+
(_request, body, done) => done(null, body)
|
|
23233
|
+
);
|
|
23234
|
+
hookApp.post("/api/hooks/harness-notify/:token", async (request, reply) => {
|
|
23235
|
+
const params = harnessHookParamSchema.parse(request.params);
|
|
23236
|
+
const signatureHeader = request.headers["x-webhook-signature"];
|
|
23237
|
+
const rawBody = Buffer.isBuffer(request.body) ? request.body : Buffer.from(typeof request.body === "string" ? request.body : "");
|
|
23238
|
+
const result = app.services.harnessWakeupService.handleCallback({
|
|
23239
|
+
hookToken: params.token,
|
|
23240
|
+
rawBody,
|
|
23241
|
+
signature: typeof signatureHeader === "string" ? signatureHeader : null
|
|
23242
|
+
});
|
|
23243
|
+
reply.status(202);
|
|
23244
|
+
return result;
|
|
23245
|
+
});
|
|
23246
|
+
});
|
|
23047
23247
|
app.get("/api/config/workspace-settings", async () => {
|
|
23048
23248
|
return getWorkspaceSettings(
|
|
23049
23249
|
app.services.database.db,
|
|
@@ -25929,13 +26129,13 @@ var terminalPluginManifest = {
|
|
|
25929
26129
|
}
|
|
25930
26130
|
};
|
|
25931
26131
|
|
|
25932
|
-
//
|
|
26132
|
+
// src/plugins/xyz-viewer-plugin-manifest.ts
|
|
25933
26133
|
var XYZ_MOLECULE_ARTIFACT_TYPE = "chemistry.molecule3d";
|
|
25934
26134
|
var xyzViewerPluginManifest = {
|
|
25935
26135
|
id: "remote-codex.xyz-viewer",
|
|
25936
26136
|
name: "XYZ Molecule Viewer",
|
|
25937
26137
|
version: "0.1.0",
|
|
25938
|
-
description: "A
|
|
26138
|
+
description: "A built-in plugin for previewing xyz, extxyz, cif, and pdb molecular structures.",
|
|
25939
26139
|
remoteCodex: "^0.11.0",
|
|
25940
26140
|
capabilities: {
|
|
25941
26141
|
artifactTypes: [
|
|
@@ -25966,11 +26166,7 @@ var xyzViewerPluginManifest = {
|
|
|
25966
26166
|
command: "node",
|
|
25967
26167
|
args: ["bin/remote-codex-plugin-mcp.mjs"]
|
|
25968
26168
|
}
|
|
25969
|
-
]
|
|
25970
|
-
frontend: {
|
|
25971
|
-
entry: "./dist/index.js",
|
|
25972
|
-
style: "./src/styles.css"
|
|
25973
|
-
}
|
|
26169
|
+
]
|
|
25974
26170
|
}
|
|
25975
26171
|
};
|
|
25976
26172
|
|
|
@@ -27496,6 +27692,9 @@ function makeShellErrorEnvelope(shellId, error) {
|
|
|
27496
27692
|
var HARNESS_MODULES = ["estructural", "quntur", "farmaco"];
|
|
27497
27693
|
var HARNESS_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
27498
27694
|
var HARNESS_RUN_ID_PATTERN = /^[a-zA-Z0-9_.-]+$/;
|
|
27695
|
+
var HARNESS_JOB_ID_PATTERN = /^[a-zA-Z0-9_.:-]+$/;
|
|
27696
|
+
var HARNESS_NOTIFICATION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
27697
|
+
var HARNESS_TERMINAL_JOB_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
|
|
27499
27698
|
var MOLECULE_ARTIFACT_TYPES = /* @__PURE__ */ new Set(["xyz", "extxyz", "pdb", "cif"]);
|
|
27500
27699
|
function recordFrom(value) {
|
|
27501
27700
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -27615,6 +27814,51 @@ function normalizeArtifact(module, runId, value) {
|
|
|
27615
27814
|
previewKind: artifactPreviewKind(type, format, path27)
|
|
27616
27815
|
};
|
|
27617
27816
|
}
|
|
27817
|
+
function parseTomlScalar(raw) {
|
|
27818
|
+
const value = raw.trim();
|
|
27819
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
27820
|
+
try {
|
|
27821
|
+
return JSON.parse(value);
|
|
27822
|
+
} catch {
|
|
27823
|
+
return value.slice(1, -1);
|
|
27824
|
+
}
|
|
27825
|
+
}
|
|
27826
|
+
return value;
|
|
27827
|
+
}
|
|
27828
|
+
function parseTomlLines(text2) {
|
|
27829
|
+
const record = {};
|
|
27830
|
+
for (const line of text2.split("\n")) {
|
|
27831
|
+
const match = /^([A-Za-z0-9_]+)\s*=\s*(.+)$/.exec(line.trim());
|
|
27832
|
+
if (match && !(match[1] in record)) {
|
|
27833
|
+
record[match[1]] = parseTomlScalar(match[2]);
|
|
27834
|
+
}
|
|
27835
|
+
}
|
|
27836
|
+
return record;
|
|
27837
|
+
}
|
|
27838
|
+
function parseTomlBlocks(text2, blockName) {
|
|
27839
|
+
const marker = `[[${blockName}]]`;
|
|
27840
|
+
const blocks = [];
|
|
27841
|
+
let current = null;
|
|
27842
|
+
for (const line of text2.split("\n")) {
|
|
27843
|
+
if (line.trim() === marker) {
|
|
27844
|
+
if (current) {
|
|
27845
|
+
blocks.push(parseTomlLines(current.join("\n")));
|
|
27846
|
+
}
|
|
27847
|
+
current = [];
|
|
27848
|
+
continue;
|
|
27849
|
+
}
|
|
27850
|
+
if (line.trim().startsWith("[[") && current) {
|
|
27851
|
+
blocks.push(parseTomlLines(current.join("\n")));
|
|
27852
|
+
current = null;
|
|
27853
|
+
continue;
|
|
27854
|
+
}
|
|
27855
|
+
current?.push(line);
|
|
27856
|
+
}
|
|
27857
|
+
if (current) {
|
|
27858
|
+
blocks.push(parseTomlLines(current.join("\n")));
|
|
27859
|
+
}
|
|
27860
|
+
return blocks;
|
|
27861
|
+
}
|
|
27618
27862
|
function normalizeRuns(module, result) {
|
|
27619
27863
|
const runs = payloadItems(result.payload, ["runs", "items", "results"]).map((item) => normalizeRun(module, item)).filter(Boolean);
|
|
27620
27864
|
return { runs };
|
|
@@ -27658,6 +27902,57 @@ var WorkerHarnessClient = class {
|
|
|
27658
27902
|
async me() {
|
|
27659
27903
|
return this.fetchText("/members/.me");
|
|
27660
27904
|
}
|
|
27905
|
+
async whoami() {
|
|
27906
|
+
const { text: text2 } = await this.fetchText("/members/.me");
|
|
27907
|
+
const record = parseTomlLines(text2);
|
|
27908
|
+
const agentId = record.id?.trim();
|
|
27909
|
+
if (!agentId) {
|
|
27910
|
+
throw new Error("ElAgenteHarness /members/.me response did not include an id.");
|
|
27911
|
+
}
|
|
27912
|
+
return { agentId };
|
|
27913
|
+
}
|
|
27914
|
+
async registerNotifyCallback(input) {
|
|
27915
|
+
return this.fetchPayload("/notify/register", {
|
|
27916
|
+
method: "POST",
|
|
27917
|
+
headers: {
|
|
27918
|
+
"content-type": "application/json"
|
|
27919
|
+
},
|
|
27920
|
+
body: JSON.stringify({
|
|
27921
|
+
agent_id: input.agentId,
|
|
27922
|
+
callback: input.callback,
|
|
27923
|
+
secret: input.secret
|
|
27924
|
+
})
|
|
27925
|
+
});
|
|
27926
|
+
}
|
|
27927
|
+
async getComputeJob(jobId) {
|
|
27928
|
+
const id = this.requireJobId(jobId);
|
|
27929
|
+
const { text: text2 } = await this.fetchText(`/compute/jobs/${encodeURIComponent(id)}`);
|
|
27930
|
+
const record = parseTomlLines(text2);
|
|
27931
|
+
const status = record.status?.trim() ?? null;
|
|
27932
|
+
return {
|
|
27933
|
+
jobId: record.id?.trim() ?? id,
|
|
27934
|
+
status,
|
|
27935
|
+
terminal: status !== null && HARNESS_TERMINAL_JOB_STATUSES.has(status),
|
|
27936
|
+
title: record.title?.trim() || null,
|
|
27937
|
+
reason: record.reason?.trim() || null,
|
|
27938
|
+
raw: record
|
|
27939
|
+
};
|
|
27940
|
+
}
|
|
27941
|
+
async listUnreadNotifications() {
|
|
27942
|
+
const { text: text2 } = await this.fetchText("/notify/inbox");
|
|
27943
|
+
return parseTomlBlocks(text2, "notifications").filter((entry) => entry.id?.trim()).map((entry) => ({
|
|
27944
|
+
id: entry.id.trim(),
|
|
27945
|
+
from: entry.from?.trim() ?? "",
|
|
27946
|
+
message: entry.message ?? ""
|
|
27947
|
+
}));
|
|
27948
|
+
}
|
|
27949
|
+
async markNotificationRead(notificationId) {
|
|
27950
|
+
const id = notificationId.trim();
|
|
27951
|
+
if (!HARNESS_NOTIFICATION_ID_PATTERN.test(id)) {
|
|
27952
|
+
throw new Error(`Unsupported Harness notification id: ${notificationId}`);
|
|
27953
|
+
}
|
|
27954
|
+
return this.fetchText(`/notify/inbox/${encodeURIComponent(id)}`);
|
|
27955
|
+
}
|
|
27661
27956
|
async home() {
|
|
27662
27957
|
return this.fetchPayload("/");
|
|
27663
27958
|
}
|
|
@@ -27724,6 +28019,13 @@ var WorkerHarnessClient = class {
|
|
|
27724
28019
|
}
|
|
27725
28020
|
return normalized;
|
|
27726
28021
|
}
|
|
28022
|
+
requireJobId(jobId) {
|
|
28023
|
+
const normalized = jobId.trim();
|
|
28024
|
+
if (!HARNESS_JOB_ID_PATTERN.test(normalized)) {
|
|
28025
|
+
throw new Error(`Unsupported Harness job id: ${jobId}`);
|
|
28026
|
+
}
|
|
28027
|
+
return normalized;
|
|
28028
|
+
}
|
|
27727
28029
|
requireRunId(runId) {
|
|
27728
28030
|
const normalized = runId.trim();
|
|
27729
28031
|
if (!HARNESS_RUN_ID_PATTERN.test(normalized)) {
|
|
@@ -27797,6 +28099,297 @@ var WorkerHarnessClient = class {
|
|
|
27797
28099
|
}
|
|
27798
28100
|
};
|
|
27799
28101
|
|
|
28102
|
+
// src/harness-wakeup-service.ts
|
|
28103
|
+
import crypto3 from "crypto";
|
|
28104
|
+
var JOB_ID_FROM_MESSAGE_PATTERN = /^id:\s*(\S+)\s*$/m;
|
|
28105
|
+
function timingSafeEqualString2(left, right) {
|
|
28106
|
+
const leftBuffer = Buffer.from(left);
|
|
28107
|
+
const rightBuffer = Buffer.from(right);
|
|
28108
|
+
return leftBuffer.length === rightBuffer.length && crypto3.timingSafeEqual(leftBuffer, rightBuffer);
|
|
28109
|
+
}
|
|
28110
|
+
var HarnessWakeupService = class {
|
|
28111
|
+
constructor(config, db, harnessClient, threadService, logger) {
|
|
28112
|
+
this.config = config;
|
|
28113
|
+
this.db = db;
|
|
28114
|
+
this.harnessClient = harnessClient;
|
|
28115
|
+
this.threadService = threadService;
|
|
28116
|
+
this.logger = logger;
|
|
28117
|
+
}
|
|
28118
|
+
config;
|
|
28119
|
+
db;
|
|
28120
|
+
harnessClient;
|
|
28121
|
+
threadService;
|
|
28122
|
+
logger;
|
|
28123
|
+
reconcileInFlight = null;
|
|
28124
|
+
reconcileQueued = false;
|
|
28125
|
+
disabledReasonFor(keyPresent) {
|
|
28126
|
+
if (!this.config.harnessBaseUrl) {
|
|
28127
|
+
return "missing_harness_base_url";
|
|
28128
|
+
}
|
|
28129
|
+
if (!keyPresent) {
|
|
28130
|
+
return "missing_harness_key";
|
|
28131
|
+
}
|
|
28132
|
+
if (!this.config.harnessWakeupCallbackBaseUrl) {
|
|
28133
|
+
return "missing_callback_base_url";
|
|
28134
|
+
}
|
|
28135
|
+
return null;
|
|
28136
|
+
}
|
|
28137
|
+
disabledReason() {
|
|
28138
|
+
return this.disabledReasonFor(this.harnessClient.configured().keyPresent);
|
|
28139
|
+
}
|
|
28140
|
+
status() {
|
|
28141
|
+
const keyPresent = this.harnessClient.configured().keyPresent;
|
|
28142
|
+
const reason = this.disabledReasonFor(keyPresent);
|
|
28143
|
+
return {
|
|
28144
|
+
enabled: reason === null,
|
|
28145
|
+
reason,
|
|
28146
|
+
harnessBaseUrl: this.config.harnessBaseUrl,
|
|
28147
|
+
callbackBaseUrl: this.config.harnessWakeupCallbackBaseUrl,
|
|
28148
|
+
keyPresent
|
|
28149
|
+
};
|
|
28150
|
+
}
|
|
28151
|
+
enabled() {
|
|
28152
|
+
return this.disabledReason() === null;
|
|
28153
|
+
}
|
|
28154
|
+
requireEnabled() {
|
|
28155
|
+
if (!this.enabled()) {
|
|
28156
|
+
throw new HttpError(409, {
|
|
28157
|
+
code: "conflict",
|
|
28158
|
+
message: "Harness wakeup is not configured. REMOTE_CODEX_HARNESS_WAKEUP_CALLBACK_BASE_URL and the Harness key are required."
|
|
28159
|
+
});
|
|
28160
|
+
}
|
|
28161
|
+
}
|
|
28162
|
+
buildCallbackUrl(hookToken) {
|
|
28163
|
+
const base = this.config.harnessWakeupCallbackBaseUrl.replace(/\/+$/, "");
|
|
28164
|
+
const userSuffix = this.config.userId ? `?u=${encodeURIComponent(this.config.userId)}` : "";
|
|
28165
|
+
return `${base}/harness-notify/${hookToken}${userSuffix}`;
|
|
28166
|
+
}
|
|
28167
|
+
async ensureRegistration() {
|
|
28168
|
+
this.requireEnabled();
|
|
28169
|
+
const existing = getHarnessNotifyRegistration(this.db);
|
|
28170
|
+
if (existing) {
|
|
28171
|
+
const desiredUrl = this.buildCallbackUrl(existing.hookToken);
|
|
28172
|
+
if (existing.callbackUrl === desiredUrl) {
|
|
28173
|
+
return existing;
|
|
28174
|
+
}
|
|
28175
|
+
await this.harnessClient.registerNotifyCallback({
|
|
28176
|
+
agentId: existing.agentId,
|
|
28177
|
+
callback: desiredUrl,
|
|
28178
|
+
secret: existing.secret
|
|
28179
|
+
});
|
|
28180
|
+
return upsertHarnessNotifyRegistration(this.db, {
|
|
28181
|
+
agentId: existing.agentId,
|
|
28182
|
+
hookToken: existing.hookToken,
|
|
28183
|
+
secret: existing.secret,
|
|
28184
|
+
callbackUrl: desiredUrl
|
|
28185
|
+
});
|
|
28186
|
+
}
|
|
28187
|
+
const { agentId } = await this.harnessClient.whoami();
|
|
28188
|
+
const hookToken = crypto3.randomBytes(32).toString("hex");
|
|
28189
|
+
const secret = crypto3.randomBytes(32).toString("hex");
|
|
28190
|
+
const callbackUrl = this.buildCallbackUrl(hookToken);
|
|
28191
|
+
await this.harnessClient.registerNotifyCallback({
|
|
28192
|
+
agentId,
|
|
28193
|
+
callback: callbackUrl,
|
|
28194
|
+
secret
|
|
28195
|
+
});
|
|
28196
|
+
return upsertHarnessNotifyRegistration(this.db, {
|
|
28197
|
+
agentId,
|
|
28198
|
+
hookToken,
|
|
28199
|
+
secret,
|
|
28200
|
+
callbackUrl
|
|
28201
|
+
});
|
|
28202
|
+
}
|
|
28203
|
+
async getWakeupInfo() {
|
|
28204
|
+
const status = this.status();
|
|
28205
|
+
if (!status.enabled) {
|
|
28206
|
+
return {
|
|
28207
|
+
...status,
|
|
28208
|
+
enabled: false
|
|
28209
|
+
};
|
|
28210
|
+
}
|
|
28211
|
+
const registration = await this.ensureRegistration();
|
|
28212
|
+
return {
|
|
28213
|
+
...status,
|
|
28214
|
+
enabled: true,
|
|
28215
|
+
notifyTo: registration.agentId,
|
|
28216
|
+
registered: true
|
|
28217
|
+
};
|
|
28218
|
+
}
|
|
28219
|
+
async watchJob(input) {
|
|
28220
|
+
this.requireEnabled();
|
|
28221
|
+
const jobId = input.jobId.trim();
|
|
28222
|
+
if (!jobId) {
|
|
28223
|
+
throw new HttpError(400, {
|
|
28224
|
+
code: "bad_request",
|
|
28225
|
+
message: "jobId is required."
|
|
28226
|
+
});
|
|
28227
|
+
}
|
|
28228
|
+
let threadId = input.threadId?.trim() || null;
|
|
28229
|
+
if (!threadId) {
|
|
28230
|
+
const runningThreads = listThreadRecords(this.db).filter(
|
|
28231
|
+
(thread) => thread.status === "running"
|
|
28232
|
+
);
|
|
28233
|
+
if (runningThreads.length === 1) {
|
|
28234
|
+
threadId = runningThreads[0].id;
|
|
28235
|
+
}
|
|
28236
|
+
}
|
|
28237
|
+
if (!threadId) {
|
|
28238
|
+
throw new HttpError(400, {
|
|
28239
|
+
code: "bad_request",
|
|
28240
|
+
message: "threadId is required when it cannot be inferred from a single running thread."
|
|
28241
|
+
});
|
|
28242
|
+
}
|
|
28243
|
+
if (!getThreadRecordById(this.db, threadId)) {
|
|
28244
|
+
throw new HttpError(404, {
|
|
28245
|
+
code: "not_found",
|
|
28246
|
+
message: "Thread was not found."
|
|
28247
|
+
});
|
|
28248
|
+
}
|
|
28249
|
+
const registration = await this.ensureRegistration();
|
|
28250
|
+
const watch = upsertHarnessJobWatch(this.db, {
|
|
28251
|
+
jobId,
|
|
28252
|
+
threadId,
|
|
28253
|
+
title: input.title ?? null
|
|
28254
|
+
});
|
|
28255
|
+
return {
|
|
28256
|
+
watch,
|
|
28257
|
+
notifyTo: registration.agentId
|
|
28258
|
+
};
|
|
28259
|
+
}
|
|
28260
|
+
verifyCallback(input) {
|
|
28261
|
+
const registration = getHarnessNotifyRegistration(this.db);
|
|
28262
|
+
if (!registration || !timingSafeEqualString2(registration.hookToken, input.hookToken)) {
|
|
28263
|
+
throw new HttpError(404, {
|
|
28264
|
+
code: "not_found",
|
|
28265
|
+
message: "Unknown harness hook."
|
|
28266
|
+
});
|
|
28267
|
+
}
|
|
28268
|
+
const expected = crypto3.createHmac("sha256", registration.secret).update(input.rawBody).digest("hex");
|
|
28269
|
+
if (!input.signature || !timingSafeEqualString2(expected, input.signature.trim())) {
|
|
28270
|
+
throw new HttpError(403, {
|
|
28271
|
+
code: "forbidden",
|
|
28272
|
+
message: "Invalid harness hook signature."
|
|
28273
|
+
});
|
|
28274
|
+
}
|
|
28275
|
+
let payload = {};
|
|
28276
|
+
try {
|
|
28277
|
+
const parsed = JSON.parse(input.rawBody.toString("utf8"));
|
|
28278
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
28279
|
+
payload = parsed;
|
|
28280
|
+
}
|
|
28281
|
+
} catch {
|
|
28282
|
+
}
|
|
28283
|
+
return payload;
|
|
28284
|
+
}
|
|
28285
|
+
handleCallback(input) {
|
|
28286
|
+
const payload = this.verifyCallback(input);
|
|
28287
|
+
this.scheduleReconcile();
|
|
28288
|
+
return {
|
|
28289
|
+
accepted: true,
|
|
28290
|
+
type: typeof payload.type === "string" ? payload.type : null
|
|
28291
|
+
};
|
|
28292
|
+
}
|
|
28293
|
+
scheduleReconcile() {
|
|
28294
|
+
if (this.reconcileInFlight) {
|
|
28295
|
+
this.reconcileQueued = true;
|
|
28296
|
+
return;
|
|
28297
|
+
}
|
|
28298
|
+
this.reconcileInFlight = this.reconcile().catch((error) => {
|
|
28299
|
+
this.logger.error({ err: error }, "Harness wakeup reconcile failed.");
|
|
28300
|
+
}).finally(() => {
|
|
28301
|
+
this.reconcileInFlight = null;
|
|
28302
|
+
if (this.reconcileQueued) {
|
|
28303
|
+
this.reconcileQueued = false;
|
|
28304
|
+
this.scheduleReconcile();
|
|
28305
|
+
}
|
|
28306
|
+
});
|
|
28307
|
+
}
|
|
28308
|
+
async waitForReconcile() {
|
|
28309
|
+
while (this.reconcileInFlight) {
|
|
28310
|
+
await this.reconcileInFlight;
|
|
28311
|
+
}
|
|
28312
|
+
}
|
|
28313
|
+
async reconcile() {
|
|
28314
|
+
const watches = listPendingHarnessJobWatches(this.db);
|
|
28315
|
+
for (const watch of watches) {
|
|
28316
|
+
try {
|
|
28317
|
+
const job = await this.harnessClient.getComputeJob(watch.jobId);
|
|
28318
|
+
updateHarnessJobWatch(this.db, watch.id, {
|
|
28319
|
+
lastJobStatus: job.status
|
|
28320
|
+
});
|
|
28321
|
+
if (!job.terminal) {
|
|
28322
|
+
continue;
|
|
28323
|
+
}
|
|
28324
|
+
await this.wakeThread(watch, job);
|
|
28325
|
+
} catch (error) {
|
|
28326
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28327
|
+
this.logger.warn(
|
|
28328
|
+
{ jobId: watch.jobId, threadId: watch.threadId, err: error },
|
|
28329
|
+
"Harness wakeup delivery attempt failed; will retry on the next callback."
|
|
28330
|
+
);
|
|
28331
|
+
updateHarnessJobWatch(this.db, watch.id, { lastError: message });
|
|
28332
|
+
}
|
|
28333
|
+
}
|
|
28334
|
+
await this.acknowledgeNotifications();
|
|
28335
|
+
}
|
|
28336
|
+
async wakeThread(watch, job) {
|
|
28337
|
+
const thread = getThreadRecordById(this.db, watch.threadId);
|
|
28338
|
+
if (!thread) {
|
|
28339
|
+
updateHarnessJobWatch(this.db, watch.id, {
|
|
28340
|
+
status: "failed",
|
|
28341
|
+
lastError: "Thread was not found."
|
|
28342
|
+
});
|
|
28343
|
+
return;
|
|
28344
|
+
}
|
|
28345
|
+
if (thread.isConnected === false) {
|
|
28346
|
+
await this.threadService.resumeThread(watch.threadId);
|
|
28347
|
+
}
|
|
28348
|
+
const title = watch.title ?? job.title;
|
|
28349
|
+
const prompt = [
|
|
28350
|
+
`[Harness job wakeup] Compute job ${job.jobId}${title ? ` ("${title}")` : ""} finished with status: ${job.status}.`,
|
|
28351
|
+
job.reason ? `Reason: ${job.reason}.` : null,
|
|
28352
|
+
`Retrieve details and outputs from the ElAgente Harness API (GET /compute/jobs/${job.jobId}, output files under GET /compute/jobs/${job.jobId}/files/...) using the INACT_X_APP_KEY env var, then continue the original task.`
|
|
28353
|
+
].filter(Boolean).join(" ");
|
|
28354
|
+
await this.threadService.sendPrompt(watch.threadId, { prompt });
|
|
28355
|
+
updateHarnessJobWatch(this.db, watch.id, {
|
|
28356
|
+
status: "delivered",
|
|
28357
|
+
lastError: null,
|
|
28358
|
+
deliveredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28359
|
+
});
|
|
28360
|
+
}
|
|
28361
|
+
async acknowledgeNotifications() {
|
|
28362
|
+
let notifications2;
|
|
28363
|
+
try {
|
|
28364
|
+
notifications2 = await this.harnessClient.listUnreadNotifications();
|
|
28365
|
+
} catch (error) {
|
|
28366
|
+
this.logger.warn({ err: error }, "Harness wakeup inbox listing failed.");
|
|
28367
|
+
return;
|
|
28368
|
+
}
|
|
28369
|
+
for (const notification of notifications2) {
|
|
28370
|
+
if (!notification.from.includes("jobs")) {
|
|
28371
|
+
continue;
|
|
28372
|
+
}
|
|
28373
|
+
const jobId = JOB_ID_FROM_MESSAGE_PATTERN.exec(notification.message)?.[1] ?? null;
|
|
28374
|
+
if (!jobId) {
|
|
28375
|
+
continue;
|
|
28376
|
+
}
|
|
28377
|
+
const watch = getHarnessJobWatchByJobId(this.db, jobId);
|
|
28378
|
+
if (watch && watch.status === "pending") {
|
|
28379
|
+
continue;
|
|
28380
|
+
}
|
|
28381
|
+
try {
|
|
28382
|
+
await this.harnessClient.markNotificationRead(notification.id);
|
|
28383
|
+
} catch (error) {
|
|
28384
|
+
this.logger.warn(
|
|
28385
|
+
{ notificationId: notification.id, err: error },
|
|
28386
|
+
"Harness wakeup notification acknowledgement failed."
|
|
28387
|
+
);
|
|
28388
|
+
}
|
|
28389
|
+
}
|
|
28390
|
+
}
|
|
28391
|
+
};
|
|
28392
|
+
|
|
27800
28393
|
// src/worker-control-plane-sync.ts
|
|
27801
28394
|
import { setTimeout as delay } from "timers/promises";
|
|
27802
28395
|
var WorkerControlPlaneSyncError = class extends Error {
|
|
@@ -27932,7 +28525,7 @@ var WorkerControlPlaneSyncClient = class {
|
|
|
27932
28525
|
};
|
|
27933
28526
|
|
|
27934
28527
|
// src/auth.ts
|
|
27935
|
-
import
|
|
28528
|
+
import crypto4 from "crypto";
|
|
27936
28529
|
var AUTH_COOKIE_NAME = "remote_codex_session";
|
|
27937
28530
|
var AuthService = class {
|
|
27938
28531
|
required;
|
|
@@ -28015,7 +28608,7 @@ var AuthService = class {
|
|
|
28015
28608
|
const payload = {
|
|
28016
28609
|
username,
|
|
28017
28610
|
expiresAt: expiresAtMs,
|
|
28018
|
-
nonce:
|
|
28611
|
+
nonce: crypto4.randomBytes(16).toString("base64url")
|
|
28019
28612
|
};
|
|
28020
28613
|
const payloadText = Buffer.from(JSON.stringify(payload), "utf8").toString(
|
|
28021
28614
|
"base64url"
|
|
@@ -28063,7 +28656,7 @@ var AuthService = class {
|
|
|
28063
28656
|
};
|
|
28064
28657
|
}
|
|
28065
28658
|
sign(payloadText) {
|
|
28066
|
-
return
|
|
28659
|
+
return crypto4.createHmac("sha256", this.secret ?? "").update(payloadText).digest("base64url");
|
|
28067
28660
|
}
|
|
28068
28661
|
};
|
|
28069
28662
|
function unauthorizedPayload() {
|
|
@@ -28120,7 +28713,7 @@ function constantTimeEqual(left, right) {
|
|
|
28120
28713
|
if (leftBuffer.length !== rightBuffer.length) {
|
|
28121
28714
|
return false;
|
|
28122
28715
|
}
|
|
28123
|
-
return
|
|
28716
|
+
return crypto4.timingSafeEqual(leftBuffer, rightBuffer);
|
|
28124
28717
|
}
|
|
28125
28718
|
|
|
28126
28719
|
// src/relay-tunnel-client.ts
|
|
@@ -28287,6 +28880,14 @@ var RelayTunnelClient = class {
|
|
|
28287
28880
|
var MAX_PROMPT_ATTACHMENTS2 = 10;
|
|
28288
28881
|
var MAX_PROMPT_ATTACHMENT_BYTES2 = 25 * 1024 * 1024;
|
|
28289
28882
|
var WORKER_AUTH_EXEMPT_PATHS = /* @__PURE__ */ new Set(["/healthz", "/readyz"]);
|
|
28883
|
+
var WORKER_AUTH_HOOK_PATH_PREFIX = "/api/hooks/";
|
|
28884
|
+
var WORKER_AUTH_LOOPBACK_PATHS = /* @__PURE__ */ new Set([
|
|
28885
|
+
"/api/harness/wakeup",
|
|
28886
|
+
"/api/harness/job-watches"
|
|
28887
|
+
]);
|
|
28888
|
+
function isLoopbackAddress(ip) {
|
|
28889
|
+
return ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1";
|
|
28890
|
+
}
|
|
28290
28891
|
var RELAY_FORWARD_HEADER = "x-remote-codex-relay-forwarded";
|
|
28291
28892
|
var SUPERVISOR_LOG_REDACTION_PATHS = [
|
|
28292
28893
|
"req.headers.authorization",
|
|
@@ -28403,7 +29004,8 @@ function buildApp(options = {}) {
|
|
|
28403
29004
|
disableRequestLogging: config.disableRequestLogging
|
|
28404
29005
|
});
|
|
28405
29006
|
app.addHook("onRequest", async (request) => {
|
|
28406
|
-
|
|
29007
|
+
const requestPath = request.url.split("?")[0] ?? request.url;
|
|
29008
|
+
if (config.runtimeRole !== "worker" || !config.workerAuthToken || WORKER_AUTH_EXEMPT_PATHS.has(requestPath) || requestPath.startsWith(WORKER_AUTH_HOOK_PATH_PREFIX) || WORKER_AUTH_LOOPBACK_PATHS.has(requestPath) && isLoopbackAddress(request.ip)) {
|
|
28407
29009
|
return;
|
|
28408
29010
|
}
|
|
28409
29011
|
const headerToken = request.headers["x-remote-codex-worker-token"];
|
|
@@ -28433,6 +29035,13 @@ function buildApp(options = {}) {
|
|
|
28433
29035
|
relaySocketBridge.handleMessage
|
|
28434
29036
|
) : null;
|
|
28435
29037
|
relayTunnelClient?.validateConfig();
|
|
29038
|
+
const harnessWakeupService = new HarnessWakeupService(
|
|
29039
|
+
config,
|
|
29040
|
+
database.db,
|
|
29041
|
+
harnessClient,
|
|
29042
|
+
threadService,
|
|
29043
|
+
app.log
|
|
29044
|
+
);
|
|
28436
29045
|
app.decorate("services", {
|
|
28437
29046
|
config,
|
|
28438
29047
|
database,
|
|
@@ -28445,6 +29054,7 @@ function buildApp(options = {}) {
|
|
|
28445
29054
|
pluginRegistry,
|
|
28446
29055
|
pluginService,
|
|
28447
29056
|
harnessClient,
|
|
29057
|
+
harnessWakeupService,
|
|
28448
29058
|
controlPlaneSyncClient,
|
|
28449
29059
|
authService,
|
|
28450
29060
|
relayTunnelClient,
|
|
@@ -28461,6 +29071,12 @@ function buildApp(options = {}) {
|
|
|
28461
29071
|
if (requestPath === "/api/auth/login" || requestPath === "/api/auth/logout" || requestPath === "/api/auth/session") {
|
|
28462
29072
|
return;
|
|
28463
29073
|
}
|
|
29074
|
+
if (requestPath.startsWith(WORKER_AUTH_HOOK_PATH_PREFIX)) {
|
|
29075
|
+
return;
|
|
29076
|
+
}
|
|
29077
|
+
if (WORKER_AUTH_LOOPBACK_PATHS.has(requestPath) && isLoopbackAddress(request.ip)) {
|
|
29078
|
+
return;
|
|
29079
|
+
}
|
|
28464
29080
|
if (config.mode === "relay" && request.headers[RELAY_FORWARD_HEADER] === "1") {
|
|
28465
29081
|
return;
|
|
28466
29082
|
}
|