@codexhost/cli-darwin-arm64 0.18.0-beta.3 → 0.18.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/app/codexhost-distribution.json +1 -1
- package/app/console-server.mjs +9 -4
- package/app/console-web.js +9 -4
- package/app/desktop-controller.mjs +9 -4
- package/app/host-runtime.mjs +4751 -2746
- package/app/plugins/antigravity/plugin.mjs +9 -4
- package/app/plugins/claude-code/plugin.mjs +129 -58
- package/app/plugins/codebuddy/plugin.mjs +9 -4
- package/app/plugins/codex-usage/plugin.mjs +9 -4
- package/app/plugins/copilot/plugin.mjs +9 -4
- package/app/plugins/cursor-cli/plugin.mjs +9 -4
- package/app/plugins/deepseek-harness/plugin.mjs +9 -4
- package/app/plugins/devin/plugin.mjs +9 -4
- package/app/plugins/grok/plugin.mjs +9 -4
- package/app/plugins/hermes/plugin.mjs +9 -4
- package/app/plugins/kimi-code/plugin.mjs +9 -4
- package/app/plugins/kiro-cli/plugin.mjs +9 -4
- package/app/plugins/omp/plugin.mjs +9 -4
- package/app/plugins/opencode/plugin.mjs +9 -4
- package/app/plugins/pi/plugin.mjs +9 -4
- package/app/plugins/qoder/plugin.mjs +9 -4
- package/app/plugins/qoder-cn/plugin.mjs +9 -4
- package/app/plugins/qwen-code/plugin.mjs +9 -4
- package/app/plugins/workbuddy/plugin.mjs +9 -4
- package/app/plugins/zcode/plugin.mjs +9 -4
- package/app/renderer-extension.js +9 -4
- package/app/web/assets/{index-aKvkfNJN.js → index-D1dOzBMI.js} +2 -2
- package/app/web/codexhost-settings.js +9 -4
- package/app/web/index.html +1 -1
- package/app/web/plugins/deepseek-ai__dsh-api-gateway/client.js +1 -1
- package/app/web/plugins/deepseek-ai__dsh-api-remotes/client.js +1 -1
- package/app/web/plugins/deepseek-ai__dsh-api-session-controller/client.js +3 -3
- package/app/web/plugins/deepseek-ai__dsh-client-connection/client.js +1 -1
- package/app/web/plugins/deepseek-ai__dsh-client-ui-codexhost/client.js +63 -63
- package/app/web/plugins/deepseek-ai__dsh-client-ui-conversation/client.js +8 -8
- package/app/web/plugins/deepseek-ai__dsh-client-ui-layout/client.js +2 -2
- package/app/web/plugins/deepseek-ai__dsh-client-ui-settings/client.js +1 -1
- package/app/web/plugins/deepseek-ai__dsh-client-ui-settings-general/client.js +2 -2
- package/app/web/plugins/deepseek-ai__dsh-client-ui-sidebar/client.js +2 -2
- package/app/web/plugins/deepseek-ai__dsh-client-ui-tool/client.js +1 -1
- package/app/web/plugins/deepseek-ai__dsh-client-ui-workspace/client.js +8 -12
- package/app/web/plugins.json +4 -0
- package/app/web-modules/server.mjs +374 -154
- package/bin/codexhost +0 -0
- package/libexec/codexhost-shim +0 -0
- package/libexec/codexhost-updater +0 -0
- package/package.json +1 -1
|
@@ -66895,7 +66895,7 @@ function hasOnlyKeys(value, required2, optional2) {
|
|
|
66895
66895
|
}
|
|
66896
66896
|
|
|
66897
66897
|
// apps/web-ui/packages/api/gateway/src/stream-server.ts
|
|
66898
|
-
var
|
|
66898
|
+
var HEARTBEAT_TIMEOUT_MS = 10 * 6e4, RemoteStreamMuxServer = class {
|
|
66899
66899
|
/**
|
|
66900
66900
|
* @param open - Gateway stream dispatcher.
|
|
66901
66901
|
* @param failure - Gateway error-to-wire mapper.
|
|
@@ -66914,7 +66914,7 @@ var MAX_MISSED_HEARTBEATS = 2, RemoteStreamMuxServer = class {
|
|
|
66914
66914
|
streamInboxBytes;
|
|
66915
66915
|
server = new import_websocket_server2.default({ noServer: !0 });
|
|
66916
66916
|
connections = /* @__PURE__ */ new Set();
|
|
66917
|
-
|
|
66917
|
+
lastPongAt = /* @__PURE__ */ new WeakMap();
|
|
66918
66918
|
heartbeatTimer;
|
|
66919
66919
|
/**
|
|
66920
66920
|
* Upgrade one admitted request and begin serving its logical streams. Every
|
|
@@ -66929,8 +66929,8 @@ var MAX_MISSED_HEARTBEATS = 2, RemoteStreamMuxServer = class {
|
|
|
66929
66929
|
this.server.handleUpgrade(req, socket, head, (websocket) => {
|
|
66930
66930
|
let release = bindPeer(websocket, peer);
|
|
66931
66931
|
if (release === void 0) return;
|
|
66932
|
-
this.
|
|
66933
|
-
this.
|
|
66932
|
+
this.lastPongAt.set(websocket, performance.now()), websocket.on("pong", () => {
|
|
66933
|
+
this.lastPongAt.set(websocket, performance.now());
|
|
66934
66934
|
}), this.startHeartbeat();
|
|
66935
66935
|
let bound = (endpoint, payload, uplink, control, window2) => this.open(endpoint, payload, uplink, peer, control, window2), done = new RemoteStreamMuxConnection(
|
|
66936
66936
|
websocket,
|
|
@@ -66955,17 +66955,16 @@ var MAX_MISSED_HEARTBEATS = 2, RemoteStreamMuxServer = class {
|
|
|
66955
66955
|
/** Start one `unref()` timer after the first upgrade; it spans empty-client periods until close(). */
|
|
66956
66956
|
startHeartbeat() {
|
|
66957
66957
|
this.heartbeatTimer === void 0 && (this.heartbeatTimer = setInterval(() => {
|
|
66958
|
-
for (let socket of this.server.clients)
|
|
66959
|
-
if (socket.readyState
|
|
66960
|
-
|
|
66961
|
-
|
|
66962
|
-
|
|
66963
|
-
|
|
66964
|
-
|
|
66965
|
-
|
|
66958
|
+
for (let socket of this.server.clients)
|
|
66959
|
+
if (socket.readyState === wrapper_default.OPEN) {
|
|
66960
|
+
if (performance.now() - this.lastPongAt.get(socket) >= HEARTBEAT_TIMEOUT_MS) {
|
|
66961
|
+
setImmediate(() => {
|
|
66962
|
+
socket.readyState === wrapper_default.OPEN && performance.now() - this.lastPongAt.get(socket) >= HEARTBEAT_TIMEOUT_MS && socket.terminate();
|
|
66963
|
+
});
|
|
66964
|
+
continue;
|
|
66965
|
+
}
|
|
66966
|
+
socket.ping();
|
|
66966
66967
|
}
|
|
66967
|
-
this.missedHeartbeats.set(socket, missed + 1), socket.ping();
|
|
66968
|
-
}
|
|
66969
66968
|
}, this.heartbeatIntervalMs), this.heartbeatTimer.unref());
|
|
66970
66969
|
}
|
|
66971
66970
|
}, RemoteStreamMuxConnection = class {
|
|
@@ -68385,12 +68384,17 @@ var clientThreadPinsSchema = external_exports.object({ threadIds: external_expor
|
|
|
68385
68384
|
});
|
|
68386
68385
|
|
|
68387
68386
|
// packages/shared-contracts/dist/thread-catalog.js
|
|
68388
|
-
var THREAD_CATALOG_READ_METHOD = "codexhost/thread/catalog/read", threadCatalogReadParamsSchema = external_exports.object({
|
|
68387
|
+
var THREAD_CATALOG_READ_METHOD = "codexhost/thread/catalog/read", threadCatalogReadParamsSchema = external_exports.object({
|
|
68388
|
+
includeArchived: external_exports.boolean().optional(),
|
|
68389
|
+
/** Absent means first page; null means this branch is exhausted. Native cursors only. */
|
|
68390
|
+
officialCursor: external_exports.object({ live: external_exports.string().nullable(), archived: external_exports.string().nullable().optional() }).strict().optional()
|
|
68391
|
+
}).strict(), threadCatalogReadResultSchema = external_exports.object({
|
|
68389
68392
|
threads: external_exports.array(jsonObjectSchema),
|
|
68390
68393
|
ownership: external_exports.array(threadOwnershipSchema),
|
|
68391
68394
|
/** Known archived IDs, including visible local external records even when their rows are
|
|
68392
|
-
* excluded. Native archived IDs
|
|
68393
|
-
archivedThreadIds: external_exports.array(hostThreadIdSchema)
|
|
68395
|
+
* excluded. Native archived IDs describe only the returned page, never a complete set. */
|
|
68396
|
+
archivedThreadIds: external_exports.array(hostThreadIdSchema),
|
|
68397
|
+
officialCursor: external_exports.object({ live: external_exports.string().nullable(), archived: external_exports.string().nullable().optional() }).strict().optional()
|
|
68394
68398
|
}).strict();
|
|
68395
68399
|
|
|
68396
68400
|
// packages/shared-contracts/dist/voice.js
|
|
@@ -68611,7 +68615,7 @@ var IDLE_RELEASE_TIMEOUT_MINUTES_MIN = 5, IDLE_RELEASE_TIMEOUT_MINUTES_MAX = 144
|
|
|
68611
68615
|
timeoutMinutes: external_exports.number().int().min(IDLE_RELEASE_TIMEOUT_MINUTES_MIN).max(IDLE_RELEASE_TIMEOUT_MINUTES_MAX)
|
|
68612
68616
|
}), DEFAULT_IDLE_RELEASE_SETTINGS = Object.freeze({
|
|
68613
68617
|
enabled: !1,
|
|
68614
|
-
timeoutMinutes:
|
|
68618
|
+
timeoutMinutes: 10
|
|
68615
68619
|
});
|
|
68616
68620
|
|
|
68617
68621
|
// packages/shared-contracts/dist/harness-preferences.js
|
|
@@ -74354,6 +74358,111 @@ var KEYS_FILE = "push-keys.json", SUBSCRIPTIONS_FILE = "push-subscriptions.json"
|
|
|
74354
74358
|
}
|
|
74355
74359
|
};
|
|
74356
74360
|
|
|
74361
|
+
// packages/web-server/src/ch-thread-title.ts
|
|
74362
|
+
import { basename as basename2, win32 as win322 } from "node:path";
|
|
74363
|
+
function threadListTitle(row) {
|
|
74364
|
+
let name2 = row.name?.trim();
|
|
74365
|
+
if (name2) return name2;
|
|
74366
|
+
let preview = row.preview?.replace(/\s+/gu, " ").trim();
|
|
74367
|
+
if (preview) {
|
|
74368
|
+
let characters = Array.from(preview);
|
|
74369
|
+
return characters.length > 80 ? `${characters.slice(0, 80).join("")}\u2026` : preview;
|
|
74370
|
+
}
|
|
74371
|
+
let cwd = row.cwd ?? "", project2 = /^[a-z]:[\\/]|^\\\\/iu.test(cwd) ? win322.basename(cwd) : basename2(cwd);
|
|
74372
|
+
return project2.trim() ? project2 : row.id;
|
|
74373
|
+
}
|
|
74374
|
+
|
|
74375
|
+
// packages/web-server/src/ch-push-notifications.ts
|
|
74376
|
+
var INTERACTION_METHODS = /* @__PURE__ */ new Set([
|
|
74377
|
+
"item/commandExecution/requestApproval",
|
|
74378
|
+
"item/fileChange/requestApproval",
|
|
74379
|
+
"item/tool/requestUserInput",
|
|
74380
|
+
"mcpServer/elicitation/request",
|
|
74381
|
+
"codexhost/thread/questions/updated"
|
|
74382
|
+
]), ChPushNotifications = class {
|
|
74383
|
+
constructor(host, push2) {
|
|
74384
|
+
this.host = host;
|
|
74385
|
+
this.push = push2;
|
|
74386
|
+
this.unsubscribe = host.subscribe((event) => this.receive(event));
|
|
74387
|
+
}
|
|
74388
|
+
host;
|
|
74389
|
+
push;
|
|
74390
|
+
unsubscribe;
|
|
74391
|
+
reads = /* @__PURE__ */ new Map();
|
|
74392
|
+
delivered = /* @__PURE__ */ new Set();
|
|
74393
|
+
epoch;
|
|
74394
|
+
sequence = 0;
|
|
74395
|
+
generation = 0;
|
|
74396
|
+
online = !1;
|
|
74397
|
+
closed = !1;
|
|
74398
|
+
receive(event) {
|
|
74399
|
+
if (this.closed) return;
|
|
74400
|
+
if (event.type === "connection") {
|
|
74401
|
+
this.online = event.online, event.online || this.generation++;
|
|
74402
|
+
return;
|
|
74403
|
+
}
|
|
74404
|
+
if (event.type === "hello") {
|
|
74405
|
+
this.epoch !== event.cursor.epoch && (this.epoch = event.cursor.epoch, this.delivered.clear(), this.sequence = 0, this.generation++), event.reset && (this.sequence = event.cursor.sequence);
|
|
74406
|
+
return;
|
|
74407
|
+
}
|
|
74408
|
+
if (event.type !== "changed" || event.cursor.epoch !== this.epoch || event.cursor.sequence <= this.sequence || (this.sequence = event.cursor.sequence, this.push.subscriptionCount === 0 || event.method !== "turn/completed" && !INTERACTION_METHODS.has(event.method)))
|
|
74409
|
+
return;
|
|
74410
|
+
let generation = this.generation, reading = (this.reads.get(event.threadId) ?? Promise.resolve()).then(() => this.reconcile(event, generation)).catch((error51) => {
|
|
74411
|
+
console.error("[push] Host notification failed", event.threadId, error51);
|
|
74412
|
+
}).finally(() => {
|
|
74413
|
+
this.reads.get(event.threadId) === reading && this.reads.delete(event.threadId);
|
|
74414
|
+
});
|
|
74415
|
+
this.reads.set(event.threadId, reading);
|
|
74416
|
+
}
|
|
74417
|
+
current(generation) {
|
|
74418
|
+
return !this.closed && this.online && this.generation === generation;
|
|
74419
|
+
}
|
|
74420
|
+
async reconcile(event, generation) {
|
|
74421
|
+
if (!this.current(generation) || this.push.subscriptionCount === 0) return;
|
|
74422
|
+
let snapshot = await this.host.snapshot(event.threadId, { truncateToolOutput: !0 });
|
|
74423
|
+
if (!this.current(generation) || snapshot.cursor.epoch !== event.cursor.epoch) return;
|
|
74424
|
+
let title = threadListTitle({ ...snapshot.thread, id: event.threadId }), messages2 = [];
|
|
74425
|
+
if (event.method === "turn/completed") {
|
|
74426
|
+
let turn = snapshot.turnsPage.data.find((value) => value.status !== "inProgress");
|
|
74427
|
+
turn && (turn.status === "completed" || turn.status === "failed") && messages2.push({
|
|
74428
|
+
key: JSON.stringify([event.cursor.epoch, event.threadId, "turn", turn.id]),
|
|
74429
|
+
message: {
|
|
74430
|
+
kind: turn.status === "failed" ? "error" : "turn",
|
|
74431
|
+
title,
|
|
74432
|
+
body: turn.status === "failed" ? "\u8FD0\u884C\u5931\u8D25" : "\u5DF2\u56DE\u590D"
|
|
74433
|
+
}
|
|
74434
|
+
});
|
|
74435
|
+
}
|
|
74436
|
+
for (let pending of snapshot.interactions)
|
|
74437
|
+
messages2.push({
|
|
74438
|
+
key: JSON.stringify([event.cursor.epoch, event.threadId, "interaction", pending.requestId]),
|
|
74439
|
+
message: {
|
|
74440
|
+
kind: pending.kind,
|
|
74441
|
+
title,
|
|
74442
|
+
body: pending.kind === "approval" ? "\u5F85\u6388\u6743" : "\u5F85\u56DE\u7B54"
|
|
74443
|
+
}
|
|
74444
|
+
});
|
|
74445
|
+
for (let { key, message: message2 } of messages2) {
|
|
74446
|
+
if (!this.current(generation)) return;
|
|
74447
|
+
if (!this.delivered.has(key)) {
|
|
74448
|
+
if (this.delivered.add(key), this.delivered.size > 512) {
|
|
74449
|
+
let oldest = this.delivered.values().next().value;
|
|
74450
|
+
oldest !== void 0 && this.delivered.delete(oldest);
|
|
74451
|
+
}
|
|
74452
|
+
await this.push.notify({
|
|
74453
|
+
...message2,
|
|
74454
|
+
tag: event.threadId,
|
|
74455
|
+
sessionId: event.threadId,
|
|
74456
|
+
url: `./?session=${encodeURIComponent(event.threadId)}`
|
|
74457
|
+
});
|
|
74458
|
+
}
|
|
74459
|
+
}
|
|
74460
|
+
}
|
|
74461
|
+
close() {
|
|
74462
|
+
this.closed = !0, this.unsubscribe(), this.reads.clear(), this.delivered.clear();
|
|
74463
|
+
}
|
|
74464
|
+
};
|
|
74465
|
+
|
|
74357
74466
|
// packages/web-server/src/plugins.ts
|
|
74358
74467
|
var CLIENT_PLUGINS = [
|
|
74359
74468
|
"@deepseek-ai/dsh-api-gateway",
|
|
@@ -74589,20 +74698,6 @@ function projectGroupRoot(snapshot, threadId, cwd) {
|
|
|
74589
74698
|
return matches2.length ? matches2.sort((a, b) => b.length - a.length)[0] : project2?.rootPaths[0];
|
|
74590
74699
|
}
|
|
74591
74700
|
|
|
74592
|
-
// packages/web-server/src/ch-thread-title.ts
|
|
74593
|
-
import { basename as basename2, win32 as win322 } from "node:path";
|
|
74594
|
-
function threadListTitle(row) {
|
|
74595
|
-
let name2 = row.name?.trim();
|
|
74596
|
-
if (name2) return name2;
|
|
74597
|
-
let preview = row.preview?.replace(/\s+/gu, " ").trim();
|
|
74598
|
-
if (preview) {
|
|
74599
|
-
let characters = Array.from(preview);
|
|
74600
|
-
return characters.length > 80 ? `${characters.slice(0, 80).join("")}\u2026` : preview;
|
|
74601
|
-
}
|
|
74602
|
-
let cwd = row.cwd ?? "", project2 = /^[a-z]:[\\/]|^\\\\/iu.test(cwd) ? win322.basename(cwd) : basename2(cwd);
|
|
74603
|
-
return project2.trim() ? project2 : row.id;
|
|
74604
|
-
}
|
|
74605
|
-
|
|
74606
74701
|
// packages/web-server/src/ch-file-operations.ts
|
|
74607
74702
|
function conversationItems(turn) {
|
|
74608
74703
|
if (turn.codexhostFileOperations === void 0) return turn.items;
|
|
@@ -75390,7 +75485,7 @@ var TurnProjector = class {
|
|
|
75390
75485
|
this.step += 1, this.stepOpen = !0, this.stepHasTools = !1, this.messageCommitted = !1, this.blocks = [], this.blockByItem.clear(), this.log.append("step/start", { turn: this.turn, step: this.step });
|
|
75391
75486
|
}
|
|
75392
75487
|
closeStep() {
|
|
75393
|
-
this.stepOpen && (!this.messageCommitted && this.blocks.some((block) => block.text !== "") ? this.commitMessage([]) : this.log.endAttempt(void 0), this.log.append("step/end", { turn: this.turn, step: this.step }), this.stepOpen = !1);
|
|
75488
|
+
this.stepOpen && (!this.messageCommitted && this.blocks.some((block) => block.text !== "") ? this.commitMessage([]) : this.log.endAttempt(void 0), [...this.tools.values()].some((tool) => tool.step === this.step) || this.log.append("step/end", { turn: this.turn, step: this.step }), this.stepOpen = !1);
|
|
75394
75489
|
}
|
|
75395
75490
|
ensureAttempt() {
|
|
75396
75491
|
this.options.live && this.log.activeAttempt === void 0 && this.log.startAttempt(this.turn, this.step);
|
|
@@ -75482,6 +75577,7 @@ var TurnProjector = class {
|
|
|
75482
75577
|
arguments: argumentsJson
|
|
75483
75578
|
}), early = this.earlyOutput.get(item.itemId) ?? "";
|
|
75484
75579
|
this.earlyOutput.delete(item.itemId), this.tools.set(item.itemId, {
|
|
75580
|
+
step: this.step,
|
|
75485
75581
|
callId,
|
|
75486
75582
|
name: call.name,
|
|
75487
75583
|
args: call.args,
|
|
@@ -75519,7 +75615,7 @@ var TurnProjector = class {
|
|
|
75519
75615
|
let call = toolCallOf(tool.item, this.options.cwd);
|
|
75520
75616
|
tool.name = call.name, tool.args = call.args, this.log.append("tool/call", {
|
|
75521
75617
|
turn: this.turn,
|
|
75522
|
-
step:
|
|
75618
|
+
step: tool.step,
|
|
75523
75619
|
callId: tool.callId,
|
|
75524
75620
|
name: call.name,
|
|
75525
75621
|
arguments: JSON.stringify(call.args)
|
|
@@ -75553,7 +75649,7 @@ var TurnProjector = class {
|
|
|
75553
75649
|
let meta3 = item.type === "fileChange" ? { diffs: item.changes.map(diffTexts) } : isError ? void 0 : editMeta(tool.name, tool.args);
|
|
75554
75650
|
this.log.append("tool/result", {
|
|
75555
75651
|
turn: this.turn,
|
|
75556
|
-
step:
|
|
75652
|
+
step: tool.step,
|
|
75557
75653
|
message: {
|
|
75558
75654
|
role: "tool",
|
|
75559
75655
|
source: { kind: "tool", callId: tool.callId },
|
|
@@ -75570,7 +75666,7 @@ var TurnProjector = class {
|
|
|
75570
75666
|
}
|
|
75571
75667
|
} : {},
|
|
75572
75668
|
...meta3 === void 0 ? {} : { meta: meta3 }
|
|
75573
|
-
}, { surfaceOp: "append", sourceEventSeqs: [tool.callSeq] });
|
|
75669
|
+
}, { surfaceOp: "append", sourceEventSeqs: [tool.callSeq] }), (tool.step !== this.step || !this.stepOpen) && ![...this.tools.values()].some((pending) => pending.step === tool.step) && this.log.append("step/end", { turn: this.turn, step: tool.step });
|
|
75574
75670
|
}
|
|
75575
75671
|
/** Close the Turn, optionally carrying the source's final reply identity. */
|
|
75576
75672
|
finish(outcome2, finalAnswerItemId) {
|
|
@@ -75676,6 +75772,9 @@ function itemOf(item, reasoningPreviewIds) {
|
|
|
75676
75772
|
return;
|
|
75677
75773
|
}
|
|
75678
75774
|
}
|
|
75775
|
+
function userMessageIdentity(item) {
|
|
75776
|
+
return JSON.stringify([item.content, item.clientId ?? null]);
|
|
75777
|
+
}
|
|
75679
75778
|
function nativeTime(seconds) {
|
|
75680
75779
|
return typeof seconds == "number" && Number.isFinite(seconds) && seconds >= 0 ? seconds * 1e3 : void 0;
|
|
75681
75780
|
}
|
|
@@ -75786,8 +75885,23 @@ var ChThreadView = class _ChThreadView {
|
|
|
75786
75885
|
for (let turn of thread.turns) {
|
|
75787
75886
|
let view = this.turns.get(turn.id);
|
|
75788
75887
|
if (!view) continue;
|
|
75789
|
-
let
|
|
75790
|
-
|
|
75888
|
+
let items = conversationItems(turn), ids = new Set(items.map((item) => item.id)), users = items.filter((item) => item.type === "userMessage"), published = [...view.started], lastUser = published.findLastIndex((id) => view.userMessages.has(id)), reasoningPreviewIds = new Set(
|
|
75889
|
+
items.filter((item) => item.type === "reasoning").map((item) => item.id)
|
|
75890
|
+
), projections = new Map(
|
|
75891
|
+
items.map((item) => [item.id, itemOf(item, reasoningPreviewIds)])
|
|
75892
|
+
), retained = items.filter((item) => {
|
|
75893
|
+
let projected = projections.get(item.id);
|
|
75894
|
+
return view.started.has(item.id) || projected && (projected.type !== "fileChange" || hasFileChangePath(projected));
|
|
75895
|
+
}), firstNewUser = items.findIndex(
|
|
75896
|
+
(item) => item.type === "userMessage" && !view.userMessages.has(item.id)
|
|
75897
|
+
);
|
|
75898
|
+
if (!view.done && [...view.items.keys()].some((id) => !ids.has(id)) || published.slice(0, lastUser + 1).some((id, index) => retained[index]?.id !== id) || [...view.userMessages].some(([id, identity], index) => {
|
|
75899
|
+
let user = users[index];
|
|
75900
|
+
return !user || user.id !== id || userMessageIdentity(user) !== identity;
|
|
75901
|
+
}) || view.done && (users.length !== view.userMessages.size || items.some((item) => {
|
|
75902
|
+
let projected = projections.get(item.id);
|
|
75903
|
+
return projected && (JSON.stringify(projected) !== JSON.stringify(view.items.get(item.id)) || typeof item.status == "string" && ["completed", "failed", "declined", "interrupted"].includes(item.status) && !view.completed.has(item.id));
|
|
75904
|
+
})) || firstNewUser !== -1 && items.slice(firstNewUser + 1).some((item) => view.started.has(item.id)))
|
|
75791
75905
|
throw new SessionRuntimeError("host/items-replaced", "CH replaced already projected Items.");
|
|
75792
75906
|
}
|
|
75793
75907
|
Object.assign(this.thread, thread), this.log.setProjection("title", threadListTitle(thread)), this.log.setProjection("codexhostImportSource", thread.codexhostImportSource ?? null), this.log.setProjection("sessionListMetadata", {
|
|
@@ -75857,7 +75971,7 @@ var ChThreadView = class _ChThreadView {
|
|
|
75857
75971
|
projectTurns(thread) {
|
|
75858
75972
|
let previousEnd = thread.createdAt * 1e3;
|
|
75859
75973
|
for (let turn of thread.turns) {
|
|
75860
|
-
let view = this.turns.get(turn.id), complete = finished(turn.status), start = nativeTime(turn.startedAt) ?? (complete ? previousEnd : Date.now()), end = Math.max(
|
|
75974
|
+
let items = conversationItems(turn), view = this.turns.get(turn.id), complete = finished(turn.status), start = nativeTime(turn.startedAt) ?? (complete ? previousEnd : Date.now()), end = Math.max(
|
|
75861
75975
|
start,
|
|
75862
75976
|
nativeTime(turn.completedAt) ?? (view && !view.done ? Date.now() : start)
|
|
75863
75977
|
);
|
|
@@ -75870,25 +75984,31 @@ var ChThreadView = class _ChThreadView {
|
|
|
75870
75984
|
onToolOutput: (id, output) => {
|
|
75871
75985
|
output === void 0 ? this.liveOutput.delete(id) : this.liveOutput.set(id, output.slice(-8e3));
|
|
75872
75986
|
}
|
|
75873
|
-
}),
|
|
75874
|
-
|
|
75875
|
-
|
|
75876
|
-
|
|
75877
|
-
|
|
75878
|
-
typeof clientId == "string" ? clientId : void 0
|
|
75879
|
-
), view = {
|
|
75987
|
+
}), opening = items[0]?.type === "userMessage" ? items[0] : void 0;
|
|
75988
|
+
opening ? projector.begin(
|
|
75989
|
+
this.projectInput(Array.isArray(opening.content) ? opening.content : []),
|
|
75990
|
+
typeof opening.clientId == "string" ? opening.clientId : void 0
|
|
75991
|
+
) : projector.beginAutonomous([]), view = {
|
|
75880
75992
|
projector,
|
|
75881
75993
|
items: /* @__PURE__ */ new Map(),
|
|
75882
|
-
|
|
75994
|
+
userMessages: new Map(opening ? [[opening.id, userMessageIdentity(opening)]] : []),
|
|
75995
|
+
started: new Set(opening ? [opening.id] : []),
|
|
75883
75996
|
completed: /* @__PURE__ */ new Set(),
|
|
75884
75997
|
done: !1
|
|
75885
75998
|
}, this.turns.set(turn.id, view);
|
|
75886
75999
|
}
|
|
75887
76000
|
if (complete || (this.log.clock = void 0), view.done) continue;
|
|
75888
|
-
let
|
|
76001
|
+
let reasoningPreviewIds = new Set(
|
|
75889
76002
|
items.filter((item) => item.type === "reasoning").map((item) => item.id)
|
|
75890
76003
|
);
|
|
75891
76004
|
for (let item of items) {
|
|
76005
|
+
if (item.type === "userMessage") {
|
|
76006
|
+
view.userMessages.has(item.id) || (view.projector.steer(
|
|
76007
|
+
this.projectInput(Array.isArray(item.content) ? item.content : []),
|
|
76008
|
+
typeof item.clientId == "string" ? item.clientId : void 0
|
|
76009
|
+
), view.userMessages.set(item.id, userMessageIdentity(item)), view.started.add(item.id));
|
|
76010
|
+
continue;
|
|
76011
|
+
}
|
|
75892
76012
|
let projected = itemOf(item, reasoningPreviewIds);
|
|
75893
76013
|
if (!projected || view.completed.has(item.id)) continue;
|
|
75894
76014
|
let old = view.items.get(item.id);
|
|
@@ -76421,35 +76541,49 @@ async function forkChThread(host, view, atSeq) {
|
|
|
76421
76541
|
let threadId = view.thread.id, owner = threadInspectionSchema.parse(
|
|
76422
76542
|
await host.request("codexhost/thread/inspect", { threadId })
|
|
76423
76543
|
);
|
|
76424
|
-
if (owner.
|
|
76544
|
+
if (owner.history?.fork === !1)
|
|
76425
76545
|
return forkUnavailable(threadId, "This Thread does not support native forking.");
|
|
76426
76546
|
let lastTurnId = view.forkTurnId(atSeq);
|
|
76427
|
-
|
|
76547
|
+
if (!lastTurnId)
|
|
76548
|
+
return forkUnavailable(
|
|
76549
|
+
threadId,
|
|
76550
|
+
"No completed Turn exists at this fork point. Reopen the conversation."
|
|
76551
|
+
);
|
|
76552
|
+
if (owner.owner === "codex") {
|
|
76553
|
+
let id = (await host.request("thread/fork", {
|
|
76554
|
+
threadId,
|
|
76555
|
+
lastTurnId,
|
|
76556
|
+
excludeTurns: !0
|
|
76557
|
+
}))?.thread?.id;
|
|
76558
|
+
if (typeof id != "string" || !id.trim() || id === threadId)
|
|
76559
|
+
throw new SessionRuntimeError(
|
|
76560
|
+
"session/fork-unconfirmed",
|
|
76561
|
+
"Codex did not confirm a distinct forked Thread. Do not retry automatically.",
|
|
76562
|
+
{ sessionId: threadId }
|
|
76563
|
+
);
|
|
76564
|
+
return { sessionId: id };
|
|
76565
|
+
}
|
|
76566
|
+
return { sessionId: externalThreadForkResultSchema.parse(
|
|
76428
76567
|
await host.request("codexhost/thread/fork", { threadId, lastTurnId })
|
|
76429
|
-
).threadId }
|
|
76430
|
-
threadId,
|
|
76431
|
-
"No completed Turn exists at this fork point. Reopen the conversation."
|
|
76432
|
-
);
|
|
76568
|
+
).threadId };
|
|
76433
76569
|
}
|
|
76434
76570
|
|
|
76435
76571
|
// packages/web-server/src/ch-pinned-threads.ts
|
|
76436
76572
|
var ChPinnedThreads = class {
|
|
76437
|
-
constructor(host, workspaces2
|
|
76573
|
+
constructor(host, workspaces2) {
|
|
76438
76574
|
this.host = host;
|
|
76439
76575
|
this.workspaces = workspaces2;
|
|
76440
|
-
this.visible = visible;
|
|
76441
76576
|
workspaces2.setReferencePins([]);
|
|
76442
76577
|
}
|
|
76443
76578
|
host;
|
|
76444
76579
|
workspaces;
|
|
76445
|
-
visible;
|
|
76446
76580
|
pending = Promise.resolve();
|
|
76447
76581
|
refreshing;
|
|
76448
76582
|
closed = !1;
|
|
76449
76583
|
sync(change) {
|
|
76450
76584
|
let next = this.pending.then(async () => {
|
|
76451
76585
|
if (this.closed) return [];
|
|
76452
|
-
let ids =
|
|
76586
|
+
let ids = await this.host.pins(change);
|
|
76453
76587
|
return this.closed || this.workspaces.setReferencePins(ids), ids;
|
|
76454
76588
|
});
|
|
76455
76589
|
return this.pending = next.catch(() => {
|
|
@@ -77159,28 +77293,22 @@ function record3(value) {
|
|
|
77159
77293
|
function presentation(pending) {
|
|
77160
77294
|
let interaction = pending.interaction;
|
|
77161
77295
|
if (interaction.type === "approval") {
|
|
77162
|
-
let labels = interaction.actions.map(
|
|
77296
|
+
let nativeApproval = pending.request.method === "item/commandExecution/requestApproval" || pending.request.method === "item/fileChange/requestApproval", labels = interaction.actions.map(
|
|
77163
77297
|
(action, index) => interaction.actions.filter((other) => other.label === action.label).length > 1 ? `${action.label} (${index + 1})` : action.label
|
|
77164
77298
|
);
|
|
77165
77299
|
return {
|
|
77166
77300
|
questions: [
|
|
77167
77301
|
{
|
|
77168
77302
|
id: "approval",
|
|
77169
|
-
|
|
77170
|
-
|
|
77171
|
-
interaction.title,
|
|
77172
|
-
interaction.description,
|
|
77173
|
-
interaction.subject ? JSON.stringify(interaction.subject, null, 2) : void 0
|
|
77174
|
-
].filter(Boolean).join(`
|
|
77175
|
-
|
|
77176
|
-
`),
|
|
77303
|
+
question: interaction.title,
|
|
77304
|
+
...!nativeApproval && interaction.description ? { detail: interaction.description } : {},
|
|
77177
77305
|
options: labels.map((label) => ({ label })),
|
|
77178
77306
|
multiSelect: !1,
|
|
77179
77307
|
allowOther: !1
|
|
77180
77308
|
}
|
|
77181
77309
|
],
|
|
77182
77310
|
result(value) {
|
|
77183
|
-
let
|
|
77311
|
+
let answer = value?.answers?.find(
|
|
77184
77312
|
(entry) => entry.id === "approval"
|
|
77185
77313
|
);
|
|
77186
77314
|
if (value == null || answer?.selected?.length === 0 && !answer.custom) {
|
|
@@ -77395,6 +77523,49 @@ function readToolOutput(log, ref) {
|
|
|
77395
77523
|
return { text: text2 };
|
|
77396
77524
|
}
|
|
77397
77525
|
|
|
77526
|
+
// packages/web-server/src/ch-session-page.ts
|
|
77527
|
+
function decodeCatalogCursor(cursor, includeArchived) {
|
|
77528
|
+
try {
|
|
77529
|
+
let value = threadCatalogReadParamsSchema.parse(JSON.parse(cursor));
|
|
77530
|
+
if (value.includeArchived !== includeArchived || !value.officialCursor)
|
|
77531
|
+
throw new Error("scope mismatch");
|
|
77532
|
+
return value.officialCursor;
|
|
77533
|
+
} catch {
|
|
77534
|
+
throw new SessionRuntimeError("session/list-invalid", "Invalid session list cursor or archive scope.");
|
|
77535
|
+
}
|
|
77536
|
+
}
|
|
77537
|
+
function nextCatalogCursor(page, includeArchived) {
|
|
77538
|
+
let cursor = page.officialCursor;
|
|
77539
|
+
return cursor && (cursor.live !== null || includeArchived && cursor.archived != null) ? JSON.stringify({ includeArchived, officialCursor: cursor }) : null;
|
|
77540
|
+
}
|
|
77541
|
+
async function readSessionPage(host, timing, includeArchived, cursor, readPins = () => host.pins()) {
|
|
77542
|
+
let officialCursor = cursor === void 0 ? void 0 : decodeCatalogCursor(cursor, includeArchived), [page, pinned] = await Promise.all([
|
|
77543
|
+
timing.measure(
|
|
77544
|
+
"host_catalog",
|
|
77545
|
+
() => host.request(THREAD_CATALOG_READ_METHOD, {
|
|
77546
|
+
includeArchived,
|
|
77547
|
+
...officialCursor ? { officialCursor } : {}
|
|
77548
|
+
})
|
|
77549
|
+
),
|
|
77550
|
+
timing.measure("pins", readPins)
|
|
77551
|
+
]), present = new Set(page.threads.map((row) => row.id)), missing = [...new Set(pinned)].filter((id) => !present.has(id)), offset = 0;
|
|
77552
|
+
return await Promise.all(
|
|
77553
|
+
Array.from({ length: Math.min(4, missing.length) }, async () => {
|
|
77554
|
+
for (; offset < missing.length; ) {
|
|
77555
|
+
let id = missing[offset++];
|
|
77556
|
+
if (id === void 0) break;
|
|
77557
|
+
let [result, owner] = await Promise.all([
|
|
77558
|
+
host.request("thread/read", { threadId: id, includeTurns: !1 }),
|
|
77559
|
+
host.request("codexhost/thread/ownership/list", { threadIds: [id] }).then((value) => threadOwnershipSchema.parse(value.threads[0]))
|
|
77560
|
+
]);
|
|
77561
|
+
if (result.thread.id !== id || owner.threadId !== id)
|
|
77562
|
+
throw new Error("Pinned Thread identity mismatch");
|
|
77563
|
+
page.threads.push(result.thread), page.ownership.push(owner);
|
|
77564
|
+
}
|
|
77565
|
+
})
|
|
77566
|
+
), { page, pinned };
|
|
77567
|
+
}
|
|
77568
|
+
|
|
77398
77569
|
// packages/web-server/src/ch-sessions.ts
|
|
77399
77570
|
function globalProjectionValues(values2) {
|
|
77400
77571
|
return Object.fromEntries(
|
|
@@ -77420,11 +77591,13 @@ var ChSessions = class {
|
|
|
77420
77591
|
"Subagent file uploads are unavailable."
|
|
77421
77592
|
);
|
|
77422
77593
|
return id;
|
|
77423
|
-
}) : void 0, this.pins = new ChPinnedThreads(host, workspaces2
|
|
77594
|
+
}) : void 0, this.pins = new ChPinnedThreads(host, workspaces2), workspaces2.setReferenceArchives([]), this.interactions = host.realtime ? new ChInteractions(host.realtime, events2, (id, message2) => {
|
|
77424
77595
|
this.threads.view(id)?.log.setProjection("nativeInteractionError", message2);
|
|
77425
77596
|
}) : void 0, this.realtime = host.realtime ? new ChRealtime(host.realtime, {
|
|
77426
77597
|
thread: (id) => this.view(id),
|
|
77427
|
-
catalog: () =>
|
|
77598
|
+
catalog: async () => {
|
|
77599
|
+
await this.refresh();
|
|
77600
|
+
},
|
|
77428
77601
|
archived: (id, archived) => this.updateArchive(id, archived),
|
|
77429
77602
|
connection: (online) => {
|
|
77430
77603
|
this.hostOnline = online, online || this.interactions?.clear();
|
|
@@ -77462,8 +77635,7 @@ var ChSessions = class {
|
|
|
77462
77635
|
fileInputs;
|
|
77463
77636
|
rows = /* @__PURE__ */ new Map();
|
|
77464
77637
|
publishedCatalog;
|
|
77465
|
-
/**
|
|
77466
|
-
* and drafts (not yet Host Threads) keep their own lifecycles. */
|
|
77638
|
+
/** Loaded rows accumulate across partial official pages; drafts are not Host Threads. */
|
|
77467
77639
|
threads = new WebThreadStates();
|
|
77468
77640
|
drafts = /* @__PURE__ */ new Map();
|
|
77469
77641
|
controls = /* @__PURE__ */ new Set();
|
|
@@ -77479,51 +77651,54 @@ var ChSessions = class {
|
|
|
77479
77651
|
interactions;
|
|
77480
77652
|
hostOnline = !0;
|
|
77481
77653
|
hostEpoch;
|
|
77482
|
-
notifier;
|
|
77483
77654
|
id(id) {
|
|
77484
77655
|
return this.drafts.get(id)?.canonicalId ?? id;
|
|
77485
77656
|
}
|
|
77486
|
-
async refresh(timing, includeArchived = !1) {
|
|
77657
|
+
async refresh(timing, includeArchived = !1, cursor) {
|
|
77487
77658
|
if (this.closed) return;
|
|
77488
|
-
if (this.refreshing && (!this.refreshingCatalog || includeArchived
|
|
77659
|
+
if (this.refreshing && (cursor !== void 0 || !this.refreshingCatalog || includeArchived !== this.refreshingArchives)) {
|
|
77489
77660
|
let stage = this.refreshingCatalog ? "catalog_wait" : "workspace_wait", pending2 = this.refreshing.catch(() => {
|
|
77490
77661
|
});
|
|
77491
|
-
return await (timing ? timing.measure(stage, () => pending2) : pending2), this.refresh(timing, includeArchived);
|
|
77662
|
+
return await (timing ? timing.measure(stage, () => pending2) : pending2), this.refresh(timing, includeArchived, cursor);
|
|
77492
77663
|
}
|
|
77493
77664
|
let mode = this.refreshing ? "joined" : "started";
|
|
77494
77665
|
if (!this.refreshing) {
|
|
77495
|
-
this.refreshingCatalog =
|
|
77666
|
+
this.refreshingCatalog = cursor === void 0, this.refreshingArchives = includeArchived, this.refreshingTiming = new SessionListTiming();
|
|
77496
77667
|
let archiveChanges = /* @__PURE__ */ new Map();
|
|
77497
77668
|
this.refreshingArchiveChanges = archiveChanges, this.refreshing = this.listAll(
|
|
77498
77669
|
this.refreshingTiming,
|
|
77499
77670
|
includeArchived,
|
|
77500
|
-
archiveChanges
|
|
77671
|
+
archiveChanges,
|
|
77672
|
+
cursor
|
|
77501
77673
|
).finally(() => {
|
|
77502
77674
|
this.refreshing = void 0, this.refreshingCatalog = !1, this.refreshingArchives = !1, this.refreshingArchiveChanges = void 0, this.refreshingTiming = void 0;
|
|
77503
77675
|
});
|
|
77504
77676
|
}
|
|
77505
77677
|
let pending = this.refreshing, catalogTiming = this.refreshingTiming;
|
|
77506
77678
|
try {
|
|
77507
|
-
await (timing ? timing.measure("catalog_wait", () => pending) : pending);
|
|
77679
|
+
return await (timing ? timing.measure("catalog_wait", () => pending) : pending);
|
|
77508
77680
|
} finally {
|
|
77509
77681
|
timing && catalogTiming && (timing.catalog = { mode, ...catalogTiming.snapshot() });
|
|
77510
77682
|
}
|
|
77511
77683
|
}
|
|
77512
|
-
async listAll(timing, includeArchived, archiveChanges) {
|
|
77513
|
-
let known = new Set(this.rows.keys()), snapshot = await
|
|
77514
|
-
|
|
77515
|
-
|
|
77516
|
-
|
|
77517
|
-
|
|
77518
|
-
|
|
77519
|
-
|
|
77684
|
+
async listAll(timing, includeArchived, archiveChanges, cursor) {
|
|
77685
|
+
let known = new Set(this.rows.keys()), [{ page: snapshot }, definitions] = await Promise.all([
|
|
77686
|
+
readSessionPage(
|
|
77687
|
+
this.host,
|
|
77688
|
+
timing,
|
|
77689
|
+
includeArchived,
|
|
77690
|
+
cursor,
|
|
77691
|
+
() => this.pins.refresh().catch((error51) => (console.error("[session/list] native pins unavailable", error51), [...this.workspaces.pinnedSessionIds()]))
|
|
77692
|
+
),
|
|
77693
|
+
timing.measure("project_definitions", () => this.host.projects([]))
|
|
77694
|
+
]);
|
|
77520
77695
|
if (this.closed) return;
|
|
77521
|
-
let found = new Map(snapshot.threads.map((row) => [row.id, row])), projects = await timing.measure(
|
|
77696
|
+
let found = new Map(snapshot.threads.map((row) => [row.id, row])), projects = { ...await timing.measure(
|
|
77522
77697
|
"projects",
|
|
77523
77698
|
() => this.host.projects([.../* @__PURE__ */ new Set([...found.keys(), ...this.rows.keys()])])
|
|
77524
|
-
);
|
|
77699
|
+
), projects: definitions.projects };
|
|
77525
77700
|
if (this.closed) return;
|
|
77526
|
-
let snapshotArchives = new Set(snapshot.archivedThreadIds), archivedIds = new Set(
|
|
77701
|
+
let snapshotArchives = new Set(snapshot.archivedThreadIds), archivedIds = new Set(this.workspaces.archivedSessionIds());
|
|
77527
77702
|
for (let id of snapshotArchives) archivedIds.add(id);
|
|
77528
77703
|
for (let id of found.keys()) snapshotArchives.has(id) || archivedIds.delete(id);
|
|
77529
77704
|
for (let [id, archived] of archiveChanges)
|
|
@@ -77541,27 +77716,13 @@ var ChSessions = class {
|
|
|
77541
77716
|
let rows = new Map([...found].filter(([id]) => ownership.has(id)));
|
|
77542
77717
|
for (let [id, row] of this.rows) {
|
|
77543
77718
|
let harnessId = this.threads.harnessId(id);
|
|
77544
|
-
|
|
77545
|
-
if (known.has(id) && !archiveChanges.has(id) && (includeArchived || !archivedIds.has(id))) {
|
|
77546
|
-
if (harnessId === "codex")
|
|
77547
|
-
try {
|
|
77548
|
-
let refreshed = await this.host.request("thread/read", {
|
|
77549
|
-
threadId: id,
|
|
77550
|
-
includeTurns: !1
|
|
77551
|
-
});
|
|
77552
|
-
refreshed.thread.id === id && (rows.set(id, refreshed.thread), ownership.set(id, harnessId));
|
|
77553
|
-
} catch {
|
|
77554
|
-
}
|
|
77555
|
-
continue;
|
|
77556
|
-
}
|
|
77557
|
-
rows.set(id, row), ownership.set(id, harnessId);
|
|
77558
|
-
}
|
|
77719
|
+
found.has(id) || !harnessId || harnessId !== "codex" && known.has(id) && !archiveChanges.has(id) && (includeArchived || !archivedIds.has(id)) || (rows.set(id, row), ownership.set(id, harnessId));
|
|
77559
77720
|
}
|
|
77560
|
-
this.threads.replaceHarnesses(ownership), this.rows = rows, timing.count("visibleRows", rows.size), await this.syncWorkspace(timing, projects), !this.closed
|
|
77561
|
-
|
|
77562
|
-
|
|
77563
|
-
|
|
77564
|
-
|
|
77721
|
+
if (this.threads.replaceHarnesses(ownership), this.rows = rows, timing.count("visibleRows", rows.size), await this.syncWorkspace(timing, projects), !this.closed)
|
|
77722
|
+
return timing.measureSync(
|
|
77723
|
+
"archive_sync",
|
|
77724
|
+
() => this.workspaces.setReferenceArchives([...archivedIds].sort())
|
|
77725
|
+
), await timing.measure("catalog_publish", () => this.publishCatalogChanges(cursor !== void 0)), this.catalogReadAt = Date.now(), snapshot;
|
|
77565
77726
|
}
|
|
77566
77727
|
async syncWorkspace(timing = new SessionListTiming(), snapshot) {
|
|
77567
77728
|
let projects = snapshot ?? await timing.measure("projects", () => this.host.projects([...this.rows.keys()]));
|
|
@@ -77581,7 +77742,7 @@ var ChSessions = class {
|
|
|
77581
77742
|
}
|
|
77582
77743
|
for (let draft of this.drafts.values())
|
|
77583
77744
|
draft.canonicalId || (groups.set(draft.id, draft.cwd), draft.cwd || projectless.add(draft.id));
|
|
77584
|
-
timing.count("workspaceReferences", references.length), this.workspaces.syncReferencedSessions(references, groups, projectless);
|
|
77745
|
+
timing.count("workspaceReferences", references.length), this.workspaces.syncReferencedSessions(references, groups, projectless, projects.projects);
|
|
77585
77746
|
});
|
|
77586
77747
|
}
|
|
77587
77748
|
/** Only fields used by the list summary; projections have their own
|
|
@@ -77600,9 +77761,11 @@ var ChSessions = class {
|
|
|
77600
77761
|
this.threads.harnessId(row.id)
|
|
77601
77762
|
]);
|
|
77602
77763
|
}
|
|
77603
|
-
async publishCatalogChanges() {
|
|
77764
|
+
async publishCatalogChanges(explicitPage = !1) {
|
|
77604
77765
|
let before = this.publishedCatalog, next = new Map([...this.rows].map(([id, row]) => [id, this.catalogKey(row)])), changed = before === void 0 ? [] : await Promise.all(
|
|
77605
|
-
[...this.rows.values()].filter(
|
|
77766
|
+
[...this.rows.values()].filter(
|
|
77767
|
+
(row) => before.get(row.id) !== next.get(row.id) && (!explicitPage || before.has(row.id))
|
|
77768
|
+
).map((row) => this.summary(row))
|
|
77606
77769
|
);
|
|
77607
77770
|
this.publishedCatalog = next;
|
|
77608
77771
|
for (let [id, row] of this.rows) {
|
|
@@ -78004,20 +78167,49 @@ var ChSessions = class {
|
|
|
78004
78167
|
return { pinnedSessionIds: await this.pins.sync({ threadId: id, pinned }) };
|
|
78005
78168
|
});
|
|
78006
78169
|
rpc2.register("session/list", async (args, context) => {
|
|
78007
|
-
let {
|
|
78170
|
+
let {
|
|
78171
|
+
includeArchived = !1,
|
|
78172
|
+
cursor,
|
|
78173
|
+
sessionId
|
|
78174
|
+
} = requestOf(args);
|
|
78175
|
+
if (sessionId !== void 0) {
|
|
78176
|
+
if (typeof sessionId != "string" || !sessionId || cursor !== void 0)
|
|
78177
|
+
throw new SessionRuntimeError("session/list-invalid", "Invalid direct-link identity.");
|
|
78178
|
+
let draft = this.drafts.get(sessionId);
|
|
78179
|
+
if (draft && !draft.canonicalId)
|
|
78180
|
+
return {
|
|
78181
|
+
items: [await this.draftSummary(draft, !1)],
|
|
78182
|
+
archivedSessionIds: this.workspaces.archivedSessionIds()
|
|
78183
|
+
};
|
|
78184
|
+
let id = this.id(sessionId), owner = await this.owner(id), { thread } = await this.host.request("thread/read", {
|
|
78185
|
+
threadId: id,
|
|
78186
|
+
includeTurns: !1
|
|
78187
|
+
});
|
|
78188
|
+
if (thread.id !== id || owner.owner === "external" && thread.modelProvider !== "codexhost")
|
|
78189
|
+
throw new SessionRuntimeError("session/ownership-unconfirmed", "Thread identity was not confirmed.");
|
|
78190
|
+
return this.rows.set(thread.id, thread), this.threads.setHarness(thread.id, owner.harnessId), {
|
|
78191
|
+
items: [await this.summary(thread)],
|
|
78192
|
+
archivedSessionIds: this.workspaces.archivedSessionIds()
|
|
78193
|
+
};
|
|
78194
|
+
}
|
|
78008
78195
|
if (typeof includeArchived != "boolean")
|
|
78009
78196
|
throw new SessionRuntimeError("session/list-invalid", "includeArchived must be a boolean.");
|
|
78010
78197
|
let timing = context.sessionListTiming ?? new SessionListTiming();
|
|
78011
|
-
|
|
78012
|
-
|
|
78198
|
+
if (cursor !== void 0 && typeof cursor != "string")
|
|
78199
|
+
throw new SessionRuntimeError("session/list-invalid", "cursor must be a string.");
|
|
78200
|
+
let page = await this.refresh(timing, includeArchived, cursor), pageIds = new Set(page?.threads.map((row) => row.id)), archivedSessionIds = this.workspaces.archivedSessionIds(), archived = new Set(archivedSessionIds), items = await timing.measure("summaries", async () => [
|
|
78013
78201
|
...await Promise.all(
|
|
78014
|
-
[...this.rows.values()].filter((row) => includeArchived || !archived.has(row.id)).map((row) => this.summary(row))
|
|
78202
|
+
[...this.rows.values()].filter((row) => pageIds.has(row.id) && (includeArchived || !archived.has(row.id))).map((row) => this.summary(row))
|
|
78015
78203
|
),
|
|
78016
78204
|
...await Promise.all(
|
|
78017
78205
|
[...this.drafts.values()].filter((d) => !d.canonicalId).map((d) => this.draftSummary(d, !1))
|
|
78018
78206
|
)
|
|
78019
78207
|
]);
|
|
78020
|
-
return timing.count("resultRows", items.length), {
|
|
78208
|
+
return timing.count("resultRows", items.length), {
|
|
78209
|
+
items,
|
|
78210
|
+
archivedSessionIds,
|
|
78211
|
+
nextCursor: page ? nextCatalogCursor(page, includeArchived) : null
|
|
78212
|
+
};
|
|
78021
78213
|
}), rpc2.register("session/create", (args) => this.create(args)), rpc2.register("session/fork", async (args) => {
|
|
78022
78214
|
let request2 = requestOf(args);
|
|
78023
78215
|
if (typeof request2.sessionId != "string" || !request2.sessionId || request2.atSeq !== void 0 && (!Number.isSafeInteger(request2.atSeq) || request2.atSeq < 0))
|
|
@@ -78084,7 +78276,10 @@ var ChSessions = class {
|
|
|
78084
78276
|
};
|
|
78085
78277
|
return state.view?.log.setProjection("modelSelection", { lastUsed: selection, next: selection }), configuration2;
|
|
78086
78278
|
}, configuration = publish(
|
|
78087
|
-
|
|
78279
|
+
owner.owner === "codex" && owner.effectiveModel?.id === request2.model ? {
|
|
78280
|
+
effectiveModel: owner.effectiveModel,
|
|
78281
|
+
effectiveThinkingOptionId: owner.effectiveThinkingOptionId
|
|
78282
|
+
} : await this.host.request("codexhost/thread/model/select", {
|
|
78088
78283
|
threadId: id,
|
|
78089
78284
|
model: { id: request2.model }
|
|
78090
78285
|
})
|
|
@@ -78124,8 +78319,6 @@ var ChSessions = class {
|
|
|
78124
78319
|
}) : await this.host.request("turn/start", {
|
|
78125
78320
|
threadId,
|
|
78126
78321
|
input,
|
|
78127
|
-
delivery: "queue",
|
|
78128
|
-
presentationInput: presentation2,
|
|
78129
78322
|
...request2.requestId ? { clientUserMessageId: request2.requestId } : {}
|
|
78130
78323
|
});
|
|
78131
78324
|
} else
|
|
@@ -78213,7 +78406,6 @@ var ChSessions = class {
|
|
|
78213
78406
|
let view = await this.view(request2.sessionId);
|
|
78214
78407
|
return { title: request2.title, seq: view.log.lastSeq };
|
|
78215
78408
|
}), rpc2.register("session/search", async (args) => {
|
|
78216
|
-
await this.refresh();
|
|
78217
78409
|
let needle = requestOf(args).query.toLowerCase();
|
|
78218
78410
|
return {
|
|
78219
78411
|
items: [...this.rows.values()].map((row) => ({ sessionId: row.id, snippet: threadListTitle(row) })).filter((item) => item.snippet.toLowerCase().includes(needle)).slice(0, 20),
|
|
@@ -81467,6 +81659,7 @@ var exec3 = promisify5(execFile5), METHODS = /* @__PURE__ */ new Set([
|
|
|
81467
81659
|
"thread/revert",
|
|
81468
81660
|
"thread/start",
|
|
81469
81661
|
"thread/resume",
|
|
81662
|
+
"thread/fork",
|
|
81470
81663
|
"thread/name/set",
|
|
81471
81664
|
"thread/archive",
|
|
81472
81665
|
"thread/unarchive",
|
|
@@ -83858,7 +84051,7 @@ var MAX_TIMER_DELAY_MS = 2147483647;
|
|
|
83858
84051
|
|
|
83859
84052
|
// apps/web-ui/packages/api/gateway/src/index.ts
|
|
83860
84053
|
import z3 from "@deepseek-ai/schemastery";
|
|
83861
|
-
var NEVER_ABORTED_SIGNAL = new AbortController().signal, DEFAULT_WEBSOCKET_HEARTBEAT_INTERVAL_MS =
|
|
84054
|
+
var NEVER_ABORTED_SIGNAL = new AbortController().signal, DEFAULT_WEBSOCKET_HEARTBEAT_INTERVAL_MS = 3e4, DEFAULT_STREAM_INBOX_BYTES = 262144, EMPTY_ASYNC_ITERABLE = {
|
|
83862
84055
|
[Symbol.asyncIterator]: () => ({ next: () => Promise.resolve({ value: void 0, done: !0 }) })
|
|
83863
84056
|
}, UPLINK_DONE2 = { value: void 0, done: !0 }, SRC_JSON_CODEC = { mode: "src-json" }, TypertGatewayError = class extends RemoteError {
|
|
83864
84057
|
/** Canonical `<namespace>/<method>` endpoint. */
|
|
@@ -93459,6 +93652,7 @@ import { execFileSync } from "node:child_process";
|
|
|
93459
93652
|
|
|
93460
93653
|
// apps/web-ui/packages/subprocess/subprocess-local/src/windows-inspector.ts
|
|
93461
93654
|
import { spawnSync } from "node:child_process";
|
|
93655
|
+
import { win32 as win326 } from "node:path";
|
|
93462
93656
|
var requireKoffi = createLazyRequire("koffi", import.meta.url);
|
|
93463
93657
|
function windowsProcessTree(entries, rootPid, started) {
|
|
93464
93658
|
let root = new Map(entries.map((entry) => [entry.pid, entry])).get(rootPid);
|
|
@@ -93516,10 +93710,16 @@ function createWindowsProcessInspector(internals = defaultWindowsProcessInternal
|
|
|
93516
93710
|
return new WindowsProcessInspector(internals);
|
|
93517
93711
|
}
|
|
93518
93712
|
function taskkillTree(pid, force) {
|
|
93519
|
-
pid <= 0
|
|
93520
|
-
|
|
93521
|
-
|
|
93522
|
-
|
|
93713
|
+
if (pid <= 0) return;
|
|
93714
|
+
let systemRoot = process.env.SystemRoot ?? process.env.windir;
|
|
93715
|
+
if (!systemRoot || !/^[A-Za-z]:[\\/]/u.test(systemRoot))
|
|
93716
|
+
throw new Error("Windows SystemRoot is unavailable or invalid");
|
|
93717
|
+
let result = spawnSync(
|
|
93718
|
+
win326.join(systemRoot, "System32", "taskkill.exe"),
|
|
93719
|
+
["/PID", String(pid), "/T", ...force ? ["/F"] : []],
|
|
93720
|
+
{ stdio: "ignore", windowsHide: !0 }
|
|
93721
|
+
);
|
|
93722
|
+
if (result.error) throw result.error;
|
|
93523
93723
|
}
|
|
93524
93724
|
function isInvalidHandle(value) {
|
|
93525
93725
|
if (value == null) return !0;
|
|
@@ -95337,20 +95537,14 @@ var LocalTerminalHandle = class {
|
|
|
95337
95537
|
this.shellActivity = shellActivity;
|
|
95338
95538
|
this.onQuiescence = onQuiescence;
|
|
95339
95539
|
this.observeShellExit = observeShellExit;
|
|
95340
|
-
this.
|
|
95341
|
-
try {
|
|
95342
|
-
this.rootIdentity = inspector.snapshot().tree(this.pid).find((member) => member.pid === this.pid);
|
|
95343
|
-
} catch {
|
|
95344
|
-
this.rootIdentity = void 0;
|
|
95345
|
-
}
|
|
95346
|
-
this.done = this.outcome.promise;
|
|
95540
|
+
this.captureProcessIdentity(), this.done = this.outcome.promise;
|
|
95347
95541
|
let resume = () => {
|
|
95348
95542
|
this.outputPaused && (this.outputPaused = !1, this.exited || terminal.resume());
|
|
95349
95543
|
};
|
|
95350
95544
|
this.output.on("drain", resume), this.output.once("close", () => {
|
|
95351
95545
|
this.output.off("drain", resume);
|
|
95352
95546
|
}), this.dataDisposable = terminal.onData((data2) => {
|
|
95353
|
-
!this.output.write(Buffer2.from(data2, "utf8")) && this.cleanup === void 0 && !this.outputPaused && (this.outputPaused = !0, terminal.pause());
|
|
95547
|
+
this.captureProcessIdentity(), !this.output.write(Buffer2.from(data2, "utf8")) && this.cleanup === void 0 && !this.outputPaused && (this.outputPaused = !0, terminal.pause());
|
|
95354
95548
|
}), this.exitDisposable = terminal.onExit(({ exitCode, signal: exitSignal }) => {
|
|
95355
95549
|
if (this.exited) return;
|
|
95356
95550
|
this.exited = !0, this.managedOwner !== void 0 && this.observeShellExit && this.managedOwner.waitForExit().then(() => {
|
|
@@ -95377,7 +95571,10 @@ var LocalTerminalHandle = class {
|
|
|
95377
95571
|
shellActivity;
|
|
95378
95572
|
onQuiescence;
|
|
95379
95573
|
observeShellExit;
|
|
95380
|
-
|
|
95574
|
+
processId = 0;
|
|
95575
|
+
get pid() {
|
|
95576
|
+
return this.processId;
|
|
95577
|
+
}
|
|
95381
95578
|
output = new PassThrough();
|
|
95382
95579
|
done;
|
|
95383
95580
|
outcome = Promise.withResolvers();
|
|
@@ -95394,6 +95591,16 @@ var LocalTerminalHandle = class {
|
|
|
95394
95591
|
managedRangeEmpty = !1;
|
|
95395
95592
|
/** The spawned shell's start identity; scans stop adopting members once the root pid no longer carries it. */
|
|
95396
95593
|
rootIdentity;
|
|
95594
|
+
captureProcessIdentity() {
|
|
95595
|
+
if (!(this.processId > 0 || this.exited || this.terminal.pid <= 0)) {
|
|
95596
|
+
this.processId = this.terminal.pid;
|
|
95597
|
+
try {
|
|
95598
|
+
this.rootIdentity = this.inspector.snapshot().tree(this.processId).find((member) => member.pid === this.processId);
|
|
95599
|
+
} catch {
|
|
95600
|
+
this.rootIdentity = void 0;
|
|
95601
|
+
}
|
|
95602
|
+
}
|
|
95603
|
+
}
|
|
95397
95604
|
/** Whether node-pty has not yet published the top-level exit event. */
|
|
95398
95605
|
get running() {
|
|
95399
95606
|
return !this.exited;
|
|
@@ -95412,7 +95619,7 @@ var LocalTerminalHandle = class {
|
|
|
95412
95619
|
// Local inspection is synchronous; the seam returns a promise for remote transports.
|
|
95413
95620
|
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider contract.
|
|
95414
95621
|
async inspectForeground() {
|
|
95415
|
-
this.descendants(this.inspector.snapshot());
|
|
95622
|
+
this.captureProcessIdentity(), this.descendants(this.inspector.snapshot());
|
|
95416
95623
|
let processGroupId = this.inspector.foregroundPgid(this.pid);
|
|
95417
95624
|
if (processGroupId !== void 0)
|
|
95418
95625
|
return {
|
|
@@ -95422,6 +95629,7 @@ var LocalTerminalHandle = class {
|
|
|
95422
95629
|
}
|
|
95423
95630
|
// oxlint-disable-next-line typescript/require-await -- Local inspection is synchronous; SSH shares this promise interface.
|
|
95424
95631
|
async inspectActivity() {
|
|
95632
|
+
this.captureProcessIdentity();
|
|
95425
95633
|
let state = this.quiescent ? "idle" : "unknown", revision = 0;
|
|
95426
95634
|
if (!this.quiescent)
|
|
95427
95635
|
try {
|
|
@@ -95482,7 +95690,7 @@ var LocalTerminalHandle = class {
|
|
|
95482
95690
|
* event. This does not claim quiescence and does not replace terminate().
|
|
95483
95691
|
*/
|
|
95484
95692
|
terminateForHostExit() {
|
|
95485
|
-
this.forceStopDescendants(), this.forceStopShell(), this.forceStopDescendants(), this.managedOwner?.terminateForHostExit();
|
|
95693
|
+
this.captureProcessIdentity(), this.forceStopDescendants(), this.forceStopShell(), this.forceStopDescendants(), this.managedOwner?.terminateForHostExit();
|
|
95486
95694
|
}
|
|
95487
95695
|
forceStopShell() {
|
|
95488
95696
|
if (!this.exited) {
|
|
@@ -95597,6 +95805,12 @@ var LocalTerminalHandle = class {
|
|
|
95597
95805
|
}
|
|
95598
95806
|
}
|
|
95599
95807
|
async closeOnce() {
|
|
95808
|
+
if (this.platform === "win32" && this.processId === 0 && !this.exited) {
|
|
95809
|
+
let deadline = Date.now() + 5e3;
|
|
95810
|
+
for (; this.terminal.pid <= 0 && !this.exited && Date.now() < deadline; ) await delay(10);
|
|
95811
|
+
if (this.captureProcessIdentity(), this.processId === 0 && !this.exited)
|
|
95812
|
+
throw new Error("terminal cleanup failed; ConPTY process is not ready");
|
|
95813
|
+
}
|
|
95600
95814
|
if (this.managedOwner !== void 0) {
|
|
95601
95815
|
try {
|
|
95602
95816
|
await this.closeManagedRange(this.managedOwner), this.dataDisposable.dispose(), this.exitDisposable.dispose();
|
|
@@ -97756,6 +97970,7 @@ var FILE3 = "workspaces.json", Workspaces = class {
|
|
|
97756
97970
|
referenceGroups;
|
|
97757
97971
|
referenceProjectless = /* @__PURE__ */ new Set();
|
|
97758
97972
|
referencePins;
|
|
97973
|
+
referenceProjectRoots = /* @__PURE__ */ new Set();
|
|
97759
97974
|
referenceArchives;
|
|
97760
97975
|
save() {
|
|
97761
97976
|
this.data.writeJson(FILE3, this.state);
|
|
@@ -97776,7 +97991,7 @@ var FILE3 = "workspaces.json", Workspaces = class {
|
|
|
97776
97991
|
);
|
|
97777
97992
|
return items.filter((workspace) => {
|
|
97778
97993
|
let legacyReference = workspace.origin === void 0 && workspace.sessionIds.length > 0 && workspace.sessionIds.some((id) => references.has(id)) && workspace.sessionIds.every((id) => references.has(id) || id.startsWith("draft-"));
|
|
97779
|
-
return workspace.origin === "web" || !(workspace.origin === "codexhost" || legacyReference) || allowedRoots.has(workspace.path);
|
|
97994
|
+
return workspace.origin === "web" || !(workspace.origin === "codexhost" || legacyReference) || allowedRoots.has(workspace.path) || this.referenceProjectRoots.has(workspace.path);
|
|
97780
97995
|
}).map((workspace) => ({
|
|
97781
97996
|
...workspace,
|
|
97782
97997
|
sessionIds: workspace.sessionIds.filter(
|
|
@@ -97873,13 +98088,28 @@ var FILE3 = "workspaces.json", Workspaces = class {
|
|
|
97873
98088
|
return title && workspace.origin !== "web" && workspace.title !== title && (workspace.title = title, this.touch(workspace)), workspace.sessionIds.includes(sessionId) || (workspace.sessionIds.push(sessionId), this.touch(workspace)), workspace;
|
|
97874
98089
|
}
|
|
97875
98090
|
/** Mirror one complete CH workspace snapshot, saving and publishing only final changes. */
|
|
97876
|
-
syncReferencedSessions(sessions2, groups, projectless = /* @__PURE__ */ new Set()) {
|
|
98091
|
+
syncReferencedSessions(sessions2, groups, projectless = /* @__PURE__ */ new Set(), projects = []) {
|
|
97877
98092
|
let before = new Map(
|
|
97878
98093
|
this.list().map((workspace) => [workspace.workspaceId, JSON.stringify(workspace)])
|
|
97879
98094
|
), items = this.state.items.map((workspace) => ({
|
|
97880
98095
|
...workspace,
|
|
97881
98096
|
sessionIds: [...workspace.sessionIds]
|
|
97882
|
-
})), byPath = new Map(items.map((workspace) => [workspace.path, workspace])), members = /* @__PURE__ */ new Map(), now = (/* @__PURE__ */ new Date()).toISOString();
|
|
98097
|
+
})), byPath = new Map(items.map((workspace) => [workspace.path, workspace])), members = /* @__PURE__ */ new Map(), now = (/* @__PURE__ */ new Date()).toISOString(), projectRoots = /* @__PURE__ */ new Set();
|
|
98098
|
+
for (let project2 of projects)
|
|
98099
|
+
for (let root of project2.rootPaths) {
|
|
98100
|
+
let path8 = resolve17(root);
|
|
98101
|
+
projectRoots.add(path8);
|
|
98102
|
+
let workspace = byPath.get(path8);
|
|
98103
|
+
workspace ? workspace.origin !== "web" && (workspace.title = project2.name) : (workspace = {
|
|
98104
|
+
workspaceId: randomUUID14(),
|
|
98105
|
+
path: path8,
|
|
98106
|
+
title: project2.name,
|
|
98107
|
+
sessionIds: [],
|
|
98108
|
+
createdAt: now,
|
|
98109
|
+
updatedAt: now,
|
|
98110
|
+
origin: "codexhost"
|
|
98111
|
+
}, items.push(workspace), byPath.set(path8, workspace));
|
|
98112
|
+
}
|
|
97883
98113
|
for (let { sessionId, cwd, title } of sessions2) {
|
|
97884
98114
|
let workspace = byPath.get(resolve17(cwd));
|
|
97885
98115
|
if (!workspace) {
|
|
@@ -97914,7 +98144,7 @@ var FILE3 = "workspaces.json", Workspaces = class {
|
|
|
97914
98144
|
let state = { ...this.state, items };
|
|
97915
98145
|
this.data.writeJson(FILE3, state), this.state = state;
|
|
97916
98146
|
}
|
|
97917
|
-
this.referenceGroups = groups, this.referenceProjectless = projectless;
|
|
98147
|
+
this.referenceProjectRoots = projectRoots, this.referenceGroups = groups, this.referenceProjectless = projectless;
|
|
97918
98148
|
let after = this.list(), ids = new Set(after.map((workspace) => workspace.workspaceId));
|
|
97919
98149
|
for (let workspaceId of before.keys())
|
|
97920
98150
|
ids.has(workspaceId) || this.broadcast({ type: "remove", workspaceId });
|
|
@@ -98264,17 +98494,7 @@ var settings = new Settings(data, events), push = new PushNotifier(data);
|
|
|
98264
98494
|
accessTokens !== void 0 && (push.retainOwners((owner) => owner === LOCAL_CREDENTIAL || accessTokens.has(owner)), accessTokens.onRevoked((id) => {
|
|
98265
98495
|
connections.revoke(id), push.dropOwner(id);
|
|
98266
98496
|
}));
|
|
98267
|
-
|
|
98268
|
-
push.notify({
|
|
98269
|
-
kind: notification.kind,
|
|
98270
|
-
title: notification.title,
|
|
98271
|
-
body: notification.body,
|
|
98272
|
-
tag: notification.sessionId,
|
|
98273
|
-
sessionId: notification.sessionId,
|
|
98274
|
-
url: `./?session=${encodeURIComponent(notification.sessionId)}`
|
|
98275
|
-
});
|
|
98276
|
-
};
|
|
98277
|
-
var devWebUi = values["dev-web-ui"] === void 0 ? void 0 : resolve19(values["dev-web-ui"]), assetOptions = PACKED && devWebUi === void 0 ? {
|
|
98497
|
+
var pushNotifications = chHost.realtime ? new ChPushNotifications(chHost.realtime, push) : void 0, devWebUi = values["dev-web-ui"] === void 0 ? void 0 : resolve19(values["dev-web-ui"]), assetOptions = PACKED && devWebUi === void 0 ? {
|
|
98278
98498
|
packedRoot: PACKED_WEB,
|
|
98279
98499
|
plugins: [],
|
|
98280
98500
|
globals: CLIENT_GLOBALS,
|
|
@@ -98649,7 +98869,7 @@ server.listen(Number(values.port), values.host, async () => {
|
|
|
98649
98869
|
});
|
|
98650
98870
|
});
|
|
98651
98871
|
async function shutdown(exitCode = 0) {
|
|
98652
|
-
relay.close(), shellReload?.close(), accessTokens?.close(), server.close(), desktopScreenshots.close(), await sidebarBrowser.close(), await dsh?.close(), await extensions2.cleanup(!0), await sessions.close(), await preferences?.close(), displaySettings.close(), process.exit(exitCode);
|
|
98872
|
+
relay.close(), shellReload?.close(), accessTokens?.close(), server.close(), desktopScreenshots.close(), await sidebarBrowser.close(), await dsh?.close(), await extensions2.cleanup(!0), pushNotifications?.close(), await sessions.close(), await preferences?.close(), displaySettings.close(), process.exit(exitCode);
|
|
98653
98873
|
}
|
|
98654
98874
|
process.on("SIGINT", () => {
|
|
98655
98875
|
shutdown();
|