@workerdeck/server 2.7.2 → 2.9.0
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 +28 -28
- package/build/index.d.mts +21 -6
- package/build/index.mjs +138 -33
- package/build/index.mjs.map +1 -1
- package/package.json +9 -9
package/build/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { createServer } from "node:http";
|
|
|
2
2
|
import { WebSocketServer } from "ws";
|
|
3
3
|
import { BrowserBridgeExecutor, SessionRunner, attachmentKind, checkClaudeAuth, createEngineSession, getEngineAdapter, normalizeMediaType } from "@workerdeck/core";
|
|
4
4
|
import { JobQueue } from "@workerdeck/queue";
|
|
5
|
-
import { ENGINE_CAPABILITIES, PROTOCOL_VERSION, SHELL_COMMAND_MAX, imagePartRef, sessionState, supportsPermissionMode } from "@workerdeck/protocol";
|
|
5
|
+
import { ENGINE_CAPABILITIES, PROTOCOL_VERSION, SHELL_COMMAND_MAX, imagePartRef, isJobRun, sessionState, supportsPermissionMode } from "@workerdeck/protocol";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
7
|
import { closeSync, constants, createReadStream, existsSync, fstatSync, ftruncateSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { constants as constants$1, homedir } from "node:os";
|
|
@@ -211,6 +211,15 @@ function parseSessionRoute(basePath, url) {
|
|
|
211
211
|
return null;
|
|
212
212
|
}
|
|
213
213
|
//#endregion
|
|
214
|
+
//#region src/lib/reload-plan.ts
|
|
215
|
+
function reloadPlan(runner) {
|
|
216
|
+
const info = runner.info();
|
|
217
|
+
if (info.status === "closed" || info.status === "failed") return "drop";
|
|
218
|
+
if (isJobRun(info)) return "drop";
|
|
219
|
+
if (runner.snapshot !== void 0) return "persist";
|
|
220
|
+
return "carry";
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
214
223
|
//#region src/routes/executions.ts
|
|
215
224
|
async function handleExecutionResult(ctx, req, res, pathname, auth) {
|
|
216
225
|
const { auth: authSvc, basePath, parking, registry } = ctx;
|
|
@@ -712,7 +721,7 @@ async function handleHostFiles(ctx, req, res, pathname) {
|
|
|
712
721
|
}
|
|
713
722
|
const existing = current.ok ? current.data : null;
|
|
714
723
|
if (existing && !body.expectedHash) {
|
|
715
|
-
json(res, 409, { error: "file exists
|
|
724
|
+
json(res, 409, { error: "file exists - pass expectedHash to overwrite it" });
|
|
716
725
|
return;
|
|
717
726
|
}
|
|
718
727
|
if (existing && hashBytes(existing) !== body.expectedHash) {
|
|
@@ -1068,7 +1077,7 @@ function createFileSessionStore(options = {}) {
|
|
|
1068
1077
|
path,
|
|
1069
1078
|
op: "save"
|
|
1070
1079
|
});
|
|
1071
|
-
throw new Error(`parked session '${record.id}' is not JSON-serializable
|
|
1080
|
+
throw new Error(`parked session '${record.id}' is not JSON-serializable - a host-injected value reached its config or snapshot: ${String(error)}`, { cause: error });
|
|
1072
1081
|
}
|
|
1073
1082
|
try {
|
|
1074
1083
|
await mkdir(dir, {
|
|
@@ -1808,6 +1817,7 @@ function attachClient(ctx, ws, runner, req, access) {
|
|
|
1808
1817
|
imageRefs
|
|
1809
1818
|
});
|
|
1810
1819
|
const detachBridge = bridge.attach(runner.id, send);
|
|
1820
|
+
runner.refreshUsage?.().catch(() => {});
|
|
1811
1821
|
ws.on("message", (data) => {
|
|
1812
1822
|
let frame;
|
|
1813
1823
|
try {
|
|
@@ -2209,8 +2219,8 @@ var SessionNotifier = class {
|
|
|
2209
2219
|
return !this.#options.webhook && !this.#options.onNotification;
|
|
2210
2220
|
}
|
|
2211
2221
|
watch(runner, afterSeq = runner.info().lastSeq) {
|
|
2212
|
-
if (this.idle) return;
|
|
2213
|
-
runner.subscribe((event) => {
|
|
2222
|
+
if (this.idle) return () => {};
|
|
2223
|
+
return runner.subscribe((event) => {
|
|
2214
2224
|
switch (event.type) {
|
|
2215
2225
|
case "permission_requested":
|
|
2216
2226
|
this.#emit(runner, event.seq, event.ts, {
|
|
@@ -2301,6 +2311,7 @@ var SessionParkManager = class {
|
|
|
2301
2311
|
#configs = /* @__PURE__ */ new Map();
|
|
2302
2312
|
#remembered = /* @__PURE__ */ new Set();
|
|
2303
2313
|
#storeOps = /* @__PURE__ */ new Map();
|
|
2314
|
+
#watches = /* @__PURE__ */ new Map();
|
|
2304
2315
|
#closed = false;
|
|
2305
2316
|
constructor(options) {
|
|
2306
2317
|
this.#options = options;
|
|
@@ -2320,7 +2331,8 @@ var SessionParkManager = class {
|
|
|
2320
2331
|
}
|
|
2321
2332
|
}
|
|
2322
2333
|
watch(runner, afterSeq = 0) {
|
|
2323
|
-
|
|
2334
|
+
this.#watches.get(runner.id)?.();
|
|
2335
|
+
const unsubscribe = runner.subscribe((event) => {
|
|
2324
2336
|
switch (event.type) {
|
|
2325
2337
|
case "execution_dispatched":
|
|
2326
2338
|
if (!event.deferred) return;
|
|
@@ -2360,6 +2372,35 @@ var SessionParkManager = class {
|
|
|
2360
2372
|
default: return;
|
|
2361
2373
|
}
|
|
2362
2374
|
}, afterSeq);
|
|
2375
|
+
const detach = () => {
|
|
2376
|
+
if (this.#watches.get(runner.id) === detach) this.#watches.delete(runner.id);
|
|
2377
|
+
unsubscribe();
|
|
2378
|
+
};
|
|
2379
|
+
this.#watches.set(runner.id, detach);
|
|
2380
|
+
return detach;
|
|
2381
|
+
}
|
|
2382
|
+
release(sessionId) {
|
|
2383
|
+
this.#watches.get(sessionId)?.();
|
|
2384
|
+
this.#watches.delete(sessionId);
|
|
2385
|
+
clearTimeout(this.#detachTimers.get(sessionId));
|
|
2386
|
+
this.#detachTimers.delete(sessionId);
|
|
2387
|
+
const config = this.#configs.get(sessionId);
|
|
2388
|
+
this.#configs.delete(sessionId);
|
|
2389
|
+
this.#remembered.delete(sessionId);
|
|
2390
|
+
return config;
|
|
2391
|
+
}
|
|
2392
|
+
adopt(runner, config) {
|
|
2393
|
+
this.#options.registry.register(runner);
|
|
2394
|
+
if (config) this.remember(runner.id, config);
|
|
2395
|
+
this.watch(runner, runner.info().lastSeq);
|
|
2396
|
+
this.touch(runner);
|
|
2397
|
+
}
|
|
2398
|
+
async flush(sessionId) {
|
|
2399
|
+
if (sessionId === void 0) {
|
|
2400
|
+
await Promise.all(this.#storeOps.values());
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
await this.#storeOps.get(sessionId);
|
|
2363
2404
|
}
|
|
2364
2405
|
onDetach(sessionId) {
|
|
2365
2406
|
if (this.#closed) return;
|
|
@@ -2411,6 +2452,7 @@ var SessionParkManager = class {
|
|
|
2411
2452
|
};
|
|
2412
2453
|
}
|
|
2413
2454
|
async discard(sessionId) {
|
|
2455
|
+
this.#watches.delete(sessionId);
|
|
2414
2456
|
clearTimeout(this.#detachTimers.get(sessionId));
|
|
2415
2457
|
this.#detachTimers.delete(sessionId);
|
|
2416
2458
|
this.#configs.delete(sessionId);
|
|
@@ -2574,7 +2616,7 @@ var SessionParkManager = class {
|
|
|
2574
2616
|
}
|
|
2575
2617
|
if (runner.id !== id) {
|
|
2576
2618
|
runner.close("error");
|
|
2577
|
-
const error = /* @__PURE__ */ new Error(`rebuilt session has id '${runner.id}', expected '${id}'
|
|
2619
|
+
const error = /* @__PURE__ */ new Error(`rebuilt session has id '${runner.id}', expected '${id}' - the engine factory must forward EngineRunnerContext.restore (or, without a snapshot, the session id) to the runner config`);
|
|
2578
2620
|
this.#options.onError?.(error, {
|
|
2579
2621
|
sessionId: id,
|
|
2580
2622
|
phase: "resume"
|
|
@@ -2639,7 +2681,7 @@ var SessionParkManager = class {
|
|
|
2639
2681
|
var ProducedFileStore = class {
|
|
2640
2682
|
#bySession = /* @__PURE__ */ new Map();
|
|
2641
2683
|
watch(runner) {
|
|
2642
|
-
runner.subscribe((event) => {
|
|
2684
|
+
return runner.subscribe((event) => {
|
|
2643
2685
|
if (event.type !== "file_produced") return;
|
|
2644
2686
|
const held = this.#bySession.get(runner.id) ?? /* @__PURE__ */ new Map();
|
|
2645
2687
|
held.set(event.fileId, {
|
|
@@ -2682,7 +2724,7 @@ var ProfileService = class {
|
|
|
2682
2724
|
if (!hasEngineRunnerFactory) return `profile '${p.name}' uses engine 'provider' but no \`createEngineRunner\` was provided to build one`;
|
|
2683
2725
|
} else if (p.engine === "codex") {
|
|
2684
2726
|
if (p.codexHome && !existsSync(p.codexHome)) return `profile '${p.name}' codexHome does not exist: ${p.codexHome}`;
|
|
2685
|
-
if (p.session?.instructions) return `profile '${p.name}' declares session.instructions, which the codex engine cannot deliver
|
|
2727
|
+
if (p.session?.instructions) return `profile '${p.name}' declares session.instructions, which the codex engine cannot deliver - put instructions in the target repo’s AGENTS.md instead`;
|
|
2686
2728
|
} else if (!p.configDir || !existsSync(p.configDir)) return `profile '${p.name}' configDir does not exist: ${p.configDir}`;
|
|
2687
2729
|
if (disableBypassPermissions && p.defaults?.permissionMode === "bypassPermissions") return `profile '${p.name}' defaults to bypassPermissions but disableBypassPermissions is set`;
|
|
2688
2730
|
const fallbackMode = p.defaults?.permissionMode;
|
|
@@ -2745,7 +2787,7 @@ var ProfileService = class {
|
|
|
2745
2787
|
declaredGuard(profile) {
|
|
2746
2788
|
return this.#declaredByName.has(profile.name) ? {
|
|
2747
2789
|
status: 403,
|
|
2748
|
-
error: `profile '${profile.name}' is declared in server options and cannot be changed over the API
|
|
2790
|
+
error: `profile '${profile.name}' is declared in server options and cannot be changed over the API - edit the \`profiles\` option instead`
|
|
2749
2791
|
} : null;
|
|
2750
2792
|
}
|
|
2751
2793
|
configDirGuard(profile) {
|
|
@@ -2790,8 +2832,8 @@ var ProfileUsageTracker = class {
|
|
|
2790
2832
|
#profiles = /* @__PURE__ */ new Map();
|
|
2791
2833
|
watch(runner) {
|
|
2792
2834
|
const profile = runner.info().profile;
|
|
2793
|
-
if (!profile) return;
|
|
2794
|
-
runner.subscribe((event) => {
|
|
2835
|
+
if (!profile) return () => {};
|
|
2836
|
+
return runner.subscribe((event) => {
|
|
2795
2837
|
if (event.type !== "rate_limit") return;
|
|
2796
2838
|
const type = event.info.rateLimitType;
|
|
2797
2839
|
if (!type) return;
|
|
@@ -2835,12 +2877,13 @@ var SessionRegistry = class {
|
|
|
2835
2877
|
#sessions = /* @__PURE__ */ new Map();
|
|
2836
2878
|
#options;
|
|
2837
2879
|
#observers = /* @__PURE__ */ new Set();
|
|
2880
|
+
#detachers = /* @__PURE__ */ new Map();
|
|
2838
2881
|
constructor(options = {}) {
|
|
2839
2882
|
this.#options = options;
|
|
2840
2883
|
}
|
|
2841
2884
|
observe(listener) {
|
|
2842
2885
|
this.#observers.add(listener);
|
|
2843
|
-
for (const runner of this.#sessions.values()) listener(runner);
|
|
2886
|
+
for (const runner of this.#sessions.values()) this.#retain(runner.id, listener(runner));
|
|
2844
2887
|
return () => void this.#observers.delete(listener);
|
|
2845
2888
|
}
|
|
2846
2889
|
create(config) {
|
|
@@ -2858,11 +2901,14 @@ var SessionRegistry = class {
|
|
|
2858
2901
|
const existing = this.#sessions.get(runner.id);
|
|
2859
2902
|
this.#sessions.set(runner.id, runner);
|
|
2860
2903
|
if (existing !== runner) {
|
|
2861
|
-
this.#options.onRegister?.(runner);
|
|
2862
|
-
for (const listener of this.#observers) listener(runner);
|
|
2904
|
+
this.#retain(runner.id, this.#options.onRegister?.(runner));
|
|
2905
|
+
for (const listener of this.#observers) this.#retain(runner.id, listener(runner));
|
|
2863
2906
|
}
|
|
2864
2907
|
return runner;
|
|
2865
2908
|
}
|
|
2909
|
+
retain(id, detach) {
|
|
2910
|
+
this.#retain(id, detach);
|
|
2911
|
+
}
|
|
2866
2912
|
get(id) {
|
|
2867
2913
|
return this.#sessions.get(id);
|
|
2868
2914
|
}
|
|
@@ -2873,14 +2919,30 @@ var SessionRegistry = class {
|
|
|
2873
2919
|
const runner = this.#sessions.get(id);
|
|
2874
2920
|
if (!runner) return false;
|
|
2875
2921
|
runner.close("server");
|
|
2922
|
+
this.#detach(id);
|
|
2876
2923
|
return this.#sessions.delete(id);
|
|
2877
2924
|
}
|
|
2878
2925
|
evict(id) {
|
|
2926
|
+
this.#detach(id);
|
|
2879
2927
|
return this.#sessions.delete(id);
|
|
2880
2928
|
}
|
|
2881
2929
|
closeAll() {
|
|
2882
2930
|
for (const runner of this.#sessions.values()) runner.close("server");
|
|
2883
2931
|
}
|
|
2932
|
+
#retain(id, detach) {
|
|
2933
|
+
if (typeof detach !== "function") return;
|
|
2934
|
+
const run = detach;
|
|
2935
|
+
const existing = this.#detachers.get(id);
|
|
2936
|
+
if (existing) {
|
|
2937
|
+
existing.push(run);
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
this.#detachers.set(id, [run]);
|
|
2941
|
+
}
|
|
2942
|
+
#detach(id) {
|
|
2943
|
+
for (const detach of this.#detachers.get(id) ?? []) detach();
|
|
2944
|
+
this.#detachers.delete(id);
|
|
2945
|
+
}
|
|
2884
2946
|
};
|
|
2885
2947
|
//#endregion
|
|
2886
2948
|
//#region src/services/session-factory.ts
|
|
@@ -2895,13 +2957,13 @@ function createSessionFactory(deps) {
|
|
|
2895
2957
|
};
|
|
2896
2958
|
const checkPermissionMode = (mode, profile) => {
|
|
2897
2959
|
if (mode === void 0 || supportsPermissionMode(profile?.engine, mode)) return null;
|
|
2898
|
-
return `permission mode '${mode}' is not supported by profile '${profile.name}' (engine '${engineOf(profile)}')
|
|
2960
|
+
return `permission mode '${mode}' is not supported by profile '${profile.name}' (engine '${engineOf(profile)}') - supported: ` + adapterFor(profile?.engine).capabilities.permissionModes.join(", ");
|
|
2899
2961
|
};
|
|
2900
2962
|
const checkEngineGrants = (req, profile) => {
|
|
2901
2963
|
const engine = engineOf(profile);
|
|
2902
2964
|
const caps = adapterFor(profile?.engine).capabilities;
|
|
2903
2965
|
const name = profile?.name ?? "default";
|
|
2904
|
-
if (!caps.sessionMcpServers && req.mcpServers && Object.keys(req.mcpServers).length > 0) return `profile '${name}' runs the ${engine} engine, whose MCP servers are declared outside the session request
|
|
2966
|
+
if (!caps.sessionMcpServers && req.mcpServers && Object.keys(req.mcpServers).length > 0) return `profile '${name}' runs the ${engine} engine, whose MCP servers are declared outside the session request - a request cannot add its own`;
|
|
2905
2967
|
if (!caps.budgets && (req.maxTurns !== void 0 || req.maxBudgetUsd !== void 0)) return `the ${engine} engine does not honor maxTurns/maxBudgetUsd`;
|
|
2906
2968
|
if (!caps.settingSources && req.settingSources !== void 0) return `the ${engine} engine does not load settingSources`;
|
|
2907
2969
|
if (!caps.resume && req.resume !== void 0) return `the ${engine} engine cannot resume a session`;
|
|
@@ -2912,7 +2974,7 @@ function createSessionFactory(deps) {
|
|
|
2912
2974
|
if (!req.capabilities || !granted) return null;
|
|
2913
2975
|
const ungranted = req.capabilities.filter((c) => !granted.includes(c));
|
|
2914
2976
|
if (ungranted.length === 0) return null;
|
|
2915
|
-
return `profile '${profile.name}' does not grant: ${ungranted.join(", ")} (granted: ${granted.join(", ") || "none"})
|
|
2977
|
+
return `profile '${profile.name}' does not grant: ${ungranted.join(", ")} (granted: ${granted.join(", ") || "none"}) - a request may narrow capabilities, not widen them`;
|
|
2916
2978
|
};
|
|
2917
2979
|
const stripInertFields = (req, profile) => {
|
|
2918
2980
|
if (!adapterFor(profile?.engine).capabilities.interactiveApprovals) delete req.questionBehavior;
|
|
@@ -3006,7 +3068,7 @@ function createSessionFactory(deps) {
|
|
|
3006
3068
|
id
|
|
3007
3069
|
});
|
|
3008
3070
|
const reported = runner.info().scope;
|
|
3009
|
-
if (!sameScope(reported, config.scope)) throw new Error(`runner for session ${runner.id} reports scope ${JSON.stringify(reported)}, expected ${JSON.stringify(config.scope)}
|
|
3071
|
+
if (!sameScope(reported, config.scope)) throw new Error(`runner for session ${runner.id} reports scope ${JSON.stringify(reported)}, expected ${JSON.stringify(config.scope)} - echo config.scope from info()`);
|
|
3010
3072
|
return runner;
|
|
3011
3073
|
};
|
|
3012
3074
|
const createRunner = async (config) => {
|
|
@@ -3052,7 +3114,7 @@ function createSessionFactory(deps) {
|
|
|
3052
3114
|
};
|
|
3053
3115
|
const watchAuthSource = (runner) => {
|
|
3054
3116
|
let seen = false;
|
|
3055
|
-
runner.subscribe((event) => {
|
|
3117
|
+
return runner.subscribe((event) => {
|
|
3056
3118
|
if (seen || event.type !== "system_init") return;
|
|
3057
3119
|
seen = true;
|
|
3058
3120
|
if (event.apiKeySource !== "oauth") return;
|
|
@@ -3062,7 +3124,7 @@ function createSessionFactory(deps) {
|
|
|
3062
3124
|
if (subscriptionNoticeShown.has(profileName)) return;
|
|
3063
3125
|
subscriptionNoticeShown.add(profileName);
|
|
3064
3126
|
const scope = profileName ? `Sessions under profile '${profileName}'` : "Sessions";
|
|
3065
|
-
console.warn(`[workerdeck] ${scope} are using claude.ai subscription credentials (apiKeySource 'oauth'), not an API key. That is only appropriate for personal, single-user use of your own account. Unattended/scheduled or multi-user use requires an API key under Anthropic's terms
|
|
3127
|
+
console.warn(`[workerdeck] ${scope} are using claude.ai subscription credentials (apiKeySource 'oauth'), not an API key. That is only appropriate for personal, single-user use of your own account. Unattended/scheduled or multi-user use requires an API key under Anthropic's terms - set ANTHROPIC_API_KEY in the server environment, or set requireApiKey: true to fail closed.`);
|
|
3066
3128
|
}
|
|
3067
3129
|
});
|
|
3068
3130
|
};
|
|
@@ -3084,6 +3146,8 @@ function createSessionFactory(deps) {
|
|
|
3084
3146
|
//#endregion
|
|
3085
3147
|
//#region src/server.ts
|
|
3086
3148
|
const SOCKET_CLOSE_GRACE_MS = 250;
|
|
3149
|
+
const CLOSE_DEADLINE_MS = 1e3;
|
|
3150
|
+
const USAGE_STALE_MS = 3e5;
|
|
3087
3151
|
function surveyDrain(registry) {
|
|
3088
3152
|
const working = [];
|
|
3089
3153
|
const awaitingHuman = [];
|
|
@@ -3110,6 +3174,19 @@ function createWorkerServer(options = {}) {
|
|
|
3110
3174
|
const adapterFor = (engine) => options.engines?.[engine ?? "claude"] ?? getEngineAdapter(engine);
|
|
3111
3175
|
const profileDefaultModels = /* @__PURE__ */ new Map();
|
|
3112
3176
|
const profileUsage = new ProfileUsageTracker();
|
|
3177
|
+
const refreshStaleUsage = (name) => {
|
|
3178
|
+
const held = profileUsage.usage(name);
|
|
3179
|
+
const newest = Math.max(0, ...Object.values(held ?? {}).map((window) => window.updatedAt ?? 0));
|
|
3180
|
+
if (Date.now() - newest < USAGE_STALE_MS) return;
|
|
3181
|
+
for (const info of refs.registry?.list() ?? []) {
|
|
3182
|
+
if (info.profile !== name) continue;
|
|
3183
|
+
const runner = refs.registry?.get(info.id);
|
|
3184
|
+
if (runner?.refreshUsage) {
|
|
3185
|
+
runner.refreshUsage().catch(() => {});
|
|
3186
|
+
return;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
};
|
|
3113
3190
|
const detected = options.profiles ? [] : detectDefaultProfiles();
|
|
3114
3191
|
const profiles = new ProfileService({
|
|
3115
3192
|
declared: options.profiles ?? (options.profileStore ? [] : detected),
|
|
@@ -3122,7 +3199,10 @@ function createWorkerServer(options = {}) {
|
|
|
3122
3199
|
decorate: {
|
|
3123
3200
|
defaultModel: (name) => profileDefaultModels.get(name),
|
|
3124
3201
|
availability: (name) => availability.get(name),
|
|
3125
|
-
usage: (name) =>
|
|
3202
|
+
usage: (name) => {
|
|
3203
|
+
refreshStaleUsage(name);
|
|
3204
|
+
return profileUsage.usage(name);
|
|
3205
|
+
}
|
|
3126
3206
|
}
|
|
3127
3207
|
});
|
|
3128
3208
|
for (const p of options.profiles ?? []) {
|
|
@@ -3155,16 +3235,20 @@ function createWorkerServer(options = {}) {
|
|
|
3155
3235
|
const producedFiles = new ProducedFileStore();
|
|
3156
3236
|
const shell = options.shell?.enabled === true ? createShellService(options.shell) : null;
|
|
3157
3237
|
const registry = new SessionRegistry({ onRegister: (runner) => {
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3238
|
+
const detachers = [
|
|
3239
|
+
notifier.watch(runner),
|
|
3240
|
+
producedFiles.watch(runner),
|
|
3241
|
+
profileUsage.watch(runner),
|
|
3242
|
+
shell?.watch(runner)
|
|
3243
|
+
];
|
|
3162
3244
|
const profile = runner.info().profile;
|
|
3163
|
-
if (
|
|
3164
|
-
runner.subscribe((event) => {
|
|
3245
|
+
if (profile) detachers.push(runner.subscribe((event) => {
|
|
3165
3246
|
if (event.type !== "capabilities" || !event.defaultModel) return;
|
|
3166
3247
|
profileDefaultModels.set(profile, event.defaultModel);
|
|
3167
|
-
});
|
|
3248
|
+
}));
|
|
3249
|
+
return () => {
|
|
3250
|
+
for (const detach of detachers) detach?.();
|
|
3251
|
+
};
|
|
3168
3252
|
} });
|
|
3169
3253
|
const attachmentStore = new AttachmentStore(options.attachments);
|
|
3170
3254
|
const bridge = new BridgeHub({
|
|
@@ -3445,6 +3529,24 @@ function createWorkerServer(options = {}) {
|
|
|
3445
3529
|
});
|
|
3446
3530
|
});
|
|
3447
3531
|
},
|
|
3532
|
+
releaseSession: (id) => {
|
|
3533
|
+
const runner = registry.get(id);
|
|
3534
|
+
if (!runner) return;
|
|
3535
|
+
const plan = reloadPlan(runner);
|
|
3536
|
+
if (plan !== "carry") throw new Error(`session ${id} cannot be carried by identity (${plan})`);
|
|
3537
|
+
const config = parking.release(id);
|
|
3538
|
+
registry.evict(id);
|
|
3539
|
+
return {
|
|
3540
|
+
runner,
|
|
3541
|
+
config
|
|
3542
|
+
};
|
|
3543
|
+
},
|
|
3544
|
+
adoptSession: ({ runner, config }) => {
|
|
3545
|
+
if (reloadPlan(runner) !== "carry") return false;
|
|
3546
|
+
parking.adopt(runner, config);
|
|
3547
|
+
registry.retain(runner.id, factory.watchAuthSource(runner));
|
|
3548
|
+
return true;
|
|
3549
|
+
},
|
|
3448
3550
|
drain: async (drainOptions = {}) => {
|
|
3449
3551
|
const { timeoutMs = 3e4, pollMs = 250, onProgress } = drainOptions;
|
|
3450
3552
|
draining = true;
|
|
@@ -3477,12 +3579,15 @@ function createWorkerServer(options = {}) {
|
|
|
3477
3579
|
for (const ws of wss.clients) ws.terminate();
|
|
3478
3580
|
}, SOCKET_CLOSE_GRACE_MS);
|
|
3479
3581
|
force.unref();
|
|
3480
|
-
|
|
3481
|
-
|
|
3582
|
+
const deadline = setTimeout(() => resolve(), CLOSE_DEADLINE_MS);
|
|
3583
|
+
deadline.unref();
|
|
3584
|
+
const drained = Promise.all([new Promise((done) => wss.close(() => done())), new Promise((done) => server.close(() => done()))]);
|
|
3585
|
+
server.closeAllConnections();
|
|
3586
|
+
drained.then(() => {
|
|
3482
3587
|
clearTimeout(force);
|
|
3588
|
+
clearTimeout(deadline);
|
|
3483
3589
|
resolve();
|
|
3484
3590
|
});
|
|
3485
|
-
server.closeAllConnections();
|
|
3486
3591
|
});
|
|
3487
3592
|
return closing;
|
|
3488
3593
|
}
|
|
@@ -3579,6 +3684,6 @@ function createFileProfileStore(path = join(process.cwd(), ".workerdeck", "profi
|
|
|
3579
3684
|
};
|
|
3580
3685
|
}
|
|
3581
3686
|
//#endregion
|
|
3582
|
-
export { AttachmentStore, BridgeHub, MemorySessionStore, ProducedFileStore, ProfileUsageTracker, SessionNotifier, SessionParkManager, SessionRegistry, createFileProfileStore, createFileSessionStore, createMemoryProfileStore, createProviderRunner, createWorkerServer, sandboxedProviderProfile, toDurableRecord };
|
|
3687
|
+
export { AttachmentStore, BridgeHub, MemorySessionStore, ProducedFileStore, ProfileUsageTracker, SessionNotifier, SessionParkManager, SessionRegistry, createFileProfileStore, createFileSessionStore, createMemoryProfileStore, createProviderRunner, createWorkerServer, isDormant, reloadPlan, sandboxedProviderProfile, toDurableRecord };
|
|
3583
3688
|
|
|
3584
3689
|
//# sourceMappingURL=index.mjs.map
|