@workerdeck/server 2.10.0 → 2.12.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/build/index.d.mts +13 -1
- package/build/index.mjs +213 -11
- package/build/index.mjs.map +1 -1
- package/package.json +5 -5
package/build/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IncomingMessage, Server, ServerResponse } from "node:http";
|
|
2
2
|
import { AttachmentInput, BridgeAnswer, BrowserBridgeExecutor, ClaudeAuthProbe, CostLedgerState, EngineAdapter, EngineSessionOptions, HostToolDefinition, LanguageModel, McpConnection, ParkedExecution, Runner, RunnerSnapshot, SessionRunnerConfig, ToolExecutionCall, ToolExecutionResult, ToolExecutor, ToolSet } from "@workerdeck/core";
|
|
3
3
|
import { JobQueue, QueueAdapter } from "@workerdeck/queue";
|
|
4
|
-
import { ByModel, CreateSessionRequest, JobEvent, MessageAttachment, ProfileEngine, ProfileInfo, ProfileSpend, ProfileUsage, ProviderConfig, SdkSessionSummary, ServerFrame, SessionCapability, SessionInfo, SessionNotification, SessionWebhookConfig } from "@workerdeck/protocol";
|
|
4
|
+
import { ByModel, CreateSessionRequest, JobEvent, MessageAttachment, ModelRate, ProfileEngine, ProfileInfo, ProfileSpend, ProfileUsage, ProviderConfig, SdkSessionSummary, ServerFrame, SessionCapability, SessionInfo, SessionNotification, SessionWebhookConfig } from "@workerdeck/protocol";
|
|
5
5
|
//#region src/services/bridge.d.ts
|
|
6
6
|
type BridgeHubOptions = {
|
|
7
7
|
timeoutMs?: number;
|
|
@@ -139,6 +139,14 @@ declare class SessionParkManager {
|
|
|
139
139
|
close(): void;
|
|
140
140
|
}
|
|
141
141
|
//#endregion
|
|
142
|
+
//#region src/services/peers.d.ts
|
|
143
|
+
type PeerServiceOptions = {
|
|
144
|
+
enabled?: boolean;
|
|
145
|
+
maxMessageChars?: number;
|
|
146
|
+
perMinute?: number;
|
|
147
|
+
maxHops?: number;
|
|
148
|
+
};
|
|
149
|
+
//#endregion
|
|
142
150
|
//#region src/services/profile-store.d.ts
|
|
143
151
|
type ProfileStore = {
|
|
144
152
|
list(): ProfileInfo[] | Promise<ProfileInfo[]>;
|
|
@@ -223,6 +231,7 @@ type WorkerServerOptions = {
|
|
|
223
231
|
requireAvailableProfile?: boolean;
|
|
224
232
|
listSdkSessions?: SdkSessionLister;
|
|
225
233
|
queue?: QueueServerOptions;
|
|
234
|
+
peers?: PeerServiceOptions;
|
|
226
235
|
notifications?: SessionNotificationOptions;
|
|
227
236
|
bridge?: BridgeHubOptions;
|
|
228
237
|
parking?: {
|
|
@@ -232,6 +241,9 @@ type WorkerServerOptions = {
|
|
|
232
241
|
persistLive?: boolean;
|
|
233
242
|
onError?: (error: unknown, context: ParkErrorContext) => void;
|
|
234
243
|
};
|
|
244
|
+
pricing?: {
|
|
245
|
+
overrides?: Record<string, ModelRate>;
|
|
246
|
+
};
|
|
235
247
|
spend?: {
|
|
236
248
|
store?: SpendStore;
|
|
237
249
|
monthlySubscriptionUsd?: Record<string, number>;
|
package/build/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { WebSocketServer } from "ws";
|
|
3
|
-
import { BrowserBridgeExecutor, SessionRunner, attachmentKind, checkClaudeAuth, createEngineSession, getEngineAdapter, normalizeMediaType } from "@workerdeck/core";
|
|
3
|
+
import { BrowserBridgeExecutor, PEER_MESSAGE_MAX_CHARS, PEER_RECENT_DEFAULT, PEER_RECENT_MAX, SessionRunner, attachmentKind, checkClaudeAuth, createEngineSession, getEngineAdapter, installPeerDirectory, isSlashCommand, normalizeMediaType, peerDirectoryHandle, peerSummary, recentLines } from "@workerdeck/core";
|
|
4
4
|
import { JobQueue } from "@workerdeck/queue";
|
|
5
|
-
import { ENGINE_CAPABILITIES, PROTOCOL_VERSION, SHELL_COMMAND_MAX, costOfByModel, imagePartRef, isJobRun, mergeByModel, sessionState, subscriptionComparison, supportsPermissionMode } from "@workerdeck/protocol";
|
|
5
|
+
import { ENGINE_CAPABILITIES, PEER_MENTION_MAX, PROTOCOL_VERSION, SHELL_COMMAND_MAX, costOfByModel, imagePartRef, isJobRun, mergeByModel, peerMentionKey, peerMentionSlug, scanPeerMentions, sessionState, setPricingOverrides, subscriptionComparison, 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";
|
|
@@ -1027,7 +1027,8 @@ const EPHEMERAL_CONFIG_KEYS = [
|
|
|
1027
1027
|
"queryFn",
|
|
1028
1028
|
"historyFn",
|
|
1029
1029
|
"extraOptions",
|
|
1030
|
-
"env"
|
|
1030
|
+
"env",
|
|
1031
|
+
"peers"
|
|
1031
1032
|
];
|
|
1032
1033
|
function toDurableRecord(record) {
|
|
1033
1034
|
const config = { ...record.config };
|
|
@@ -1290,6 +1291,7 @@ const PROJECT_FILE = ".workerdeck.json";
|
|
|
1290
1291
|
const MAX_PROJECT_FILE_BYTES = 65536;
|
|
1291
1292
|
const MAX_NAME_CHARS = 80;
|
|
1292
1293
|
const GLYPH_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
1294
|
+
const SHORTCODE_RE = /^[A-Z0-9]{2,5}$/;
|
|
1293
1295
|
const DEFAULT_TTL_MS = 3e4;
|
|
1294
1296
|
const SWEEP_ABOVE = 256;
|
|
1295
1297
|
var ProjectInfoService = class {
|
|
@@ -1361,15 +1363,20 @@ function tryLoad(file, root) {
|
|
|
1361
1363
|
const raw = parsed;
|
|
1362
1364
|
const name = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim().slice(0, MAX_NAME_CHARS) : basename(root);
|
|
1363
1365
|
const icon = classifyIcon(raw.icon, root);
|
|
1366
|
+
const shortcode = classifyShortcode(raw.shortcode);
|
|
1364
1367
|
return {
|
|
1365
1368
|
project: {
|
|
1366
1369
|
name,
|
|
1367
1370
|
root,
|
|
1368
|
-
...icon ? { icon: icon.wire } : {}
|
|
1371
|
+
...icon ? { icon: icon.wire } : {},
|
|
1372
|
+
...shortcode ? { shortcode } : {}
|
|
1369
1373
|
},
|
|
1370
1374
|
...icon?.resolved ? { icon: icon.resolved } : {}
|
|
1371
1375
|
};
|
|
1372
1376
|
}
|
|
1377
|
+
function classifyShortcode(value) {
|
|
1378
|
+
return typeof value === "string" && SHORTCODE_RE.test(value) ? value : void 0;
|
|
1379
|
+
}
|
|
1373
1380
|
function classifyIcon(value, root) {
|
|
1374
1381
|
if (typeof value !== "string") return;
|
|
1375
1382
|
const declared = value.trim();
|
|
@@ -1806,7 +1813,8 @@ function attachClient(ctx, ws, runner, req, access) {
|
|
|
1806
1813
|
protocolVersion: PROTOCOL_VERSION,
|
|
1807
1814
|
session: ctx.projects.withProject(runner.info()),
|
|
1808
1815
|
replayingFrom: afterSeq,
|
|
1809
|
-
...shellPermitted(ctx.shell, runner, access.operator) ? { shell: true } : {}
|
|
1816
|
+
...shellPermitted(ctx.shell, runner, access.operator) ? { shell: true } : {},
|
|
1817
|
+
...ctx.pricingOverrides ? { pricingOverrides: ctx.pricingOverrides } : {}
|
|
1810
1818
|
});
|
|
1811
1819
|
const unsubscribe = runner.subscribe((event) => send({
|
|
1812
1820
|
type: "event",
|
|
@@ -1846,13 +1854,15 @@ async function handleCommand(ctx, frame, runner, access) {
|
|
|
1846
1854
|
const { attachmentStore, bridge } = ctx;
|
|
1847
1855
|
switch (frame.type) {
|
|
1848
1856
|
case "user_message": {
|
|
1857
|
+
const mentions = ctx.peers && !isSlashCommand(frame.text) ? await ctx.peers.mentions(runner.id, frame.text).catch(() => []) : [];
|
|
1858
|
+
const options = mentions.length > 0 ? { mentions } : void 0;
|
|
1849
1859
|
if (!frame.attachmentIds?.length) {
|
|
1850
|
-
runner.sendMessage(frame.text);
|
|
1860
|
+
runner.sendMessage(frame.text, void 0, options);
|
|
1851
1861
|
return;
|
|
1852
1862
|
}
|
|
1853
1863
|
const resolved = attachmentStore.resolve(runner.id, frame.attachmentIds);
|
|
1854
1864
|
if (!resolved.ok) throw new Error(`unknown attachment(s): ${resolved.missing.join(", ")}`);
|
|
1855
|
-
runner.sendMessage(frame.text, resolved.attachments);
|
|
1865
|
+
runner.sendMessage(frame.text, resolved.attachments, options);
|
|
1856
1866
|
return;
|
|
1857
1867
|
}
|
|
1858
1868
|
case "permission_decision":
|
|
@@ -3030,6 +3040,182 @@ function createFileSpendStore(path, onError) {
|
|
|
3030
3040
|
};
|
|
3031
3041
|
}
|
|
3032
3042
|
//#endregion
|
|
3043
|
+
//#region src/services/peers.ts
|
|
3044
|
+
const DEFAULT_PER_MINUTE = 10;
|
|
3045
|
+
const DEFAULT_MAX_HOPS = 12;
|
|
3046
|
+
const WINDOW_MS = 6e4;
|
|
3047
|
+
function visible(from, to) {
|
|
3048
|
+
return to.id !== from.id && scopeMatches(from.scope, to.scope);
|
|
3049
|
+
}
|
|
3050
|
+
function createPeerService(deps) {
|
|
3051
|
+
const perMinute = deps.options?.perMinute ?? DEFAULT_PER_MINUTE;
|
|
3052
|
+
const maxChars = deps.options?.maxMessageChars ?? PEER_MESSAGE_MAX_CHARS;
|
|
3053
|
+
const maxHops = deps.options?.maxHops ?? DEFAULT_MAX_HOPS;
|
|
3054
|
+
const sent = /* @__PURE__ */ new Map();
|
|
3055
|
+
const inbound = /* @__PURE__ */ new Map();
|
|
3056
|
+
const registry = () => {
|
|
3057
|
+
const found = deps.refs.registry;
|
|
3058
|
+
if (!found) throw new Error("peer messaging is not ready");
|
|
3059
|
+
return found;
|
|
3060
|
+
};
|
|
3061
|
+
const allSessions = async () => {
|
|
3062
|
+
const live = registry().list();
|
|
3063
|
+
const dormant = await deps.refs.parking?.listInfo() ?? [];
|
|
3064
|
+
return [...live, ...dormant].map((info) => deps.projects.withProject(info));
|
|
3065
|
+
};
|
|
3066
|
+
const infoOf = async (id) => {
|
|
3067
|
+
const live = registry().get(id)?.info();
|
|
3068
|
+
if (live) return deps.projects.withProject(live);
|
|
3069
|
+
const record = await deps.refs.parking?.get(id);
|
|
3070
|
+
return record ? deps.projects.withProject(record.info) : void 0;
|
|
3071
|
+
};
|
|
3072
|
+
const sender = async (from) => {
|
|
3073
|
+
const info = await infoOf(from);
|
|
3074
|
+
if (!info) throw new Error(`unknown sender session: ${from}`);
|
|
3075
|
+
return info;
|
|
3076
|
+
};
|
|
3077
|
+
const underRateLimit = (from, to) => {
|
|
3078
|
+
const key = `${from}->${to}`;
|
|
3079
|
+
const now = Date.now();
|
|
3080
|
+
const recent = (sent.get(key) ?? []).filter((at) => now - at < WINDOW_MS);
|
|
3081
|
+
if (recent.length >= perMinute) {
|
|
3082
|
+
sent.set(key, recent);
|
|
3083
|
+
return false;
|
|
3084
|
+
}
|
|
3085
|
+
recent.push(now);
|
|
3086
|
+
sent.set(key, recent);
|
|
3087
|
+
return true;
|
|
3088
|
+
};
|
|
3089
|
+
const list = async (from) => {
|
|
3090
|
+
const me = await sender(from);
|
|
3091
|
+
return (await allSessions()).filter((info) => visible(me, info)).sort((a, b) => (b.lastActivityAt ?? b.createdAt) - (a.lastActivityAt ?? a.createdAt)).map(peerSummary);
|
|
3092
|
+
};
|
|
3093
|
+
const peek = async (from, sessionId, options) => {
|
|
3094
|
+
const me = await sender(from);
|
|
3095
|
+
const info = await infoOf(sessionId);
|
|
3096
|
+
if (!info || !visible(me, info)) return;
|
|
3097
|
+
const limit = Math.min(Math.max(options?.recent ?? PEER_RECENT_DEFAULT, 0), PEER_RECENT_MAX);
|
|
3098
|
+
const runner = registry().get(sessionId);
|
|
3099
|
+
const events = [];
|
|
3100
|
+
if (runner && limit > 0) runner.subscribe((event) => events.push(event), 0, {
|
|
3101
|
+
truncateResults: true,
|
|
3102
|
+
imageRefs: true
|
|
3103
|
+
})();
|
|
3104
|
+
return {
|
|
3105
|
+
...peerSummary(info),
|
|
3106
|
+
live: runner !== void 0,
|
|
3107
|
+
checklist: info.checklist,
|
|
3108
|
+
pendingApprovals: runner ? runner.pendingApprovals.map((request) => request.title ?? request.toolName) : [],
|
|
3109
|
+
recent: recentLines(events, limit)
|
|
3110
|
+
};
|
|
3111
|
+
};
|
|
3112
|
+
const send = async (from, sessionId, text, options) => {
|
|
3113
|
+
const me = await sender(from);
|
|
3114
|
+
if (sessionId === from) return {
|
|
3115
|
+
delivered: false,
|
|
3116
|
+
reason: "that is this session"
|
|
3117
|
+
};
|
|
3118
|
+
if (text.length > maxChars) return {
|
|
3119
|
+
delivered: false,
|
|
3120
|
+
reason: `message is ${text.length} characters; the limit is ${maxChars}. Write it to a file and send the path.`
|
|
3121
|
+
};
|
|
3122
|
+
const target = await infoOf(sessionId);
|
|
3123
|
+
if (!target || !visible(me, target)) return {
|
|
3124
|
+
delivered: false,
|
|
3125
|
+
reason: `no such session: ${sessionId}`
|
|
3126
|
+
};
|
|
3127
|
+
if (target.status === "closed" || target.status === "failed") return {
|
|
3128
|
+
delivered: false,
|
|
3129
|
+
reason: `session ${sessionId} is ${target.status}`
|
|
3130
|
+
};
|
|
3131
|
+
const hops = [...options?.hops ?? inbound.get(from) ?? [], from];
|
|
3132
|
+
if (hops.length > maxHops) return {
|
|
3133
|
+
delivered: false,
|
|
3134
|
+
reason: `${hops.length} messages have passed between sessions without a person speaking; stop and ask your user before continuing.`
|
|
3135
|
+
};
|
|
3136
|
+
if (!underRateLimit(from, sessionId)) return {
|
|
3137
|
+
delivered: false,
|
|
3138
|
+
reason: `rate limit: at most ${perMinute} messages a minute to one session. Batch what you have to say.`
|
|
3139
|
+
};
|
|
3140
|
+
const runner = await deps.refs.parking?.ensureLive(sessionId) ?? registry().get(sessionId);
|
|
3141
|
+
if (!runner) return {
|
|
3142
|
+
delivered: false,
|
|
3143
|
+
reason: `session ${sessionId} could not be woken`
|
|
3144
|
+
};
|
|
3145
|
+
const before = runner.info().status;
|
|
3146
|
+
const origin = {
|
|
3147
|
+
kind: "peer",
|
|
3148
|
+
sessionId: from,
|
|
3149
|
+
name: me.title,
|
|
3150
|
+
engine: me.engine,
|
|
3151
|
+
hops
|
|
3152
|
+
};
|
|
3153
|
+
try {
|
|
3154
|
+
runner.sendMessage(text, void 0, { origin });
|
|
3155
|
+
} catch (error) {
|
|
3156
|
+
return {
|
|
3157
|
+
delivered: false,
|
|
3158
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
inbound.set(sessionId, hops);
|
|
3162
|
+
return {
|
|
3163
|
+
delivered: true,
|
|
3164
|
+
sessionId,
|
|
3165
|
+
name: target.title,
|
|
3166
|
+
queued: before === "running" || before === "awaiting_approval"
|
|
3167
|
+
};
|
|
3168
|
+
};
|
|
3169
|
+
const mentions = async (from, text) => {
|
|
3170
|
+
const tokens = scanPeerMentions(text);
|
|
3171
|
+
if (tokens.length === 0) return [];
|
|
3172
|
+
const rows = await list(from);
|
|
3173
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
3174
|
+
const add = (key, row) => {
|
|
3175
|
+
const held = byKey.get(key);
|
|
3176
|
+
if (held) held.push(row);
|
|
3177
|
+
else byKey.set(key, [row]);
|
|
3178
|
+
};
|
|
3179
|
+
for (const row of rows) {
|
|
3180
|
+
add(peerMentionKey(peerMentionSlug(row.title, row.id)), row);
|
|
3181
|
+
add(peerMentionKey(row.id), row);
|
|
3182
|
+
}
|
|
3183
|
+
const resolved = [];
|
|
3184
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3185
|
+
for (const token of tokens) {
|
|
3186
|
+
const key = peerMentionKey(token.body);
|
|
3187
|
+
const exact = byKey.get(key);
|
|
3188
|
+
const matches = exact ?? (key.length >= 4 ? rows.filter((row) => row.id.startsWith(key)) : []);
|
|
3189
|
+
const target = matches[0];
|
|
3190
|
+
if (!target || matches.length > 1 && !exact) continue;
|
|
3191
|
+
if (seen.has(target.id)) continue;
|
|
3192
|
+
seen.add(target.id);
|
|
3193
|
+
const others = matches.slice(1, 4).map((row) => row.id);
|
|
3194
|
+
resolved.push({
|
|
3195
|
+
typed: token.body,
|
|
3196
|
+
id: target.id,
|
|
3197
|
+
name: target.title,
|
|
3198
|
+
engine: target.engine,
|
|
3199
|
+
status: target.status,
|
|
3200
|
+
cwd: target.cwd,
|
|
3201
|
+
...others.length ? { ambiguousWith: others } : {}
|
|
3202
|
+
});
|
|
3203
|
+
if (resolved.length >= PEER_MENTION_MAX) break;
|
|
3204
|
+
}
|
|
3205
|
+
return resolved;
|
|
3206
|
+
};
|
|
3207
|
+
const watch = (runner) => runner.subscribe((event) => {
|
|
3208
|
+
if (event.type === "user_message" && !event.origin && !event.synthetic && event.parentToolUseId == null && !event.replay) inbound.delete(runner.id);
|
|
3209
|
+
}, runner.info().lastSeq);
|
|
3210
|
+
return {
|
|
3211
|
+
list,
|
|
3212
|
+
peek,
|
|
3213
|
+
send,
|
|
3214
|
+
mentions,
|
|
3215
|
+
watch
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
//#endregion
|
|
3033
3219
|
//#region src/services/registry.ts
|
|
3034
3220
|
var SessionRegistry = class {
|
|
3035
3221
|
#sessions = /* @__PURE__ */ new Map();
|
|
@@ -3209,7 +3395,11 @@ function createSessionFactory(deps) {
|
|
|
3209
3395
|
return engineOf(profile) === "claude" ? claudeSessionEnv(profile, process.env) : process.env;
|
|
3210
3396
|
}
|
|
3211
3397
|
};
|
|
3212
|
-
const buildRunner = async (
|
|
3398
|
+
const buildRunner = async (built, restore, id) => {
|
|
3399
|
+
const config = deps.peers ? {
|
|
3400
|
+
...built,
|
|
3401
|
+
peers: deps.peers
|
|
3402
|
+
} : built;
|
|
3213
3403
|
const name = config.profile;
|
|
3214
3404
|
const profile = name !== void 0 ? profiles.get(name) : void 0;
|
|
3215
3405
|
if (name !== void 0 && !profile) throw new Error(`unknown profile: ${name}`);
|
|
@@ -3330,6 +3520,8 @@ function createWorkerServer(options = {}) {
|
|
|
3330
3520
|
const corsOrigins = options.cors?.origins.length ? new Set(options.cors.origins) : void 0;
|
|
3331
3521
|
const maxBodyBytes = options.maxBodyBytes ?? 1048576;
|
|
3332
3522
|
const adapterFor = (engine) => options.engines?.[engine ?? "claude"] ?? getEngineAdapter(engine);
|
|
3523
|
+
const pricing = setPricingOverrides(options.pricing?.overrides);
|
|
3524
|
+
if (pricing.dropped.length > 0) console.warn(`[workerdeck] Ignoring pricing.overrides for ${pricing.dropped.join(", ")}: each entry needs input, output, cacheWrite5m, cacheWrite1h and cacheRead in USD per million tokens`);
|
|
3333
3525
|
const profileDefaultModels = /* @__PURE__ */ new Map();
|
|
3334
3526
|
const profileUsage = new ProfileUsageTracker();
|
|
3335
3527
|
const spendLedger = new SpendLedger({
|
|
@@ -3375,6 +3567,13 @@ function createWorkerServer(options = {}) {
|
|
|
3375
3567
|
if (invalid) throw new Error(`createWorkerServer: ${invalid}`);
|
|
3376
3568
|
}
|
|
3377
3569
|
const refs = {};
|
|
3570
|
+
const projects = new ProjectInfoService();
|
|
3571
|
+
const peers = options.peers?.enabled === false ? void 0 : createPeerService({
|
|
3572
|
+
refs,
|
|
3573
|
+
projects,
|
|
3574
|
+
options: options.peers
|
|
3575
|
+
});
|
|
3576
|
+
if (peers) installPeerDirectory(peers);
|
|
3378
3577
|
const factory = createSessionFactory({
|
|
3379
3578
|
adapterFor,
|
|
3380
3579
|
profiles,
|
|
@@ -3384,6 +3583,7 @@ function createWorkerServer(options = {}) {
|
|
|
3384
3583
|
disableBypassPermissions: options.disableBypassPermissions,
|
|
3385
3584
|
approvalTimeoutMs: options.approvalTimeoutMs,
|
|
3386
3585
|
requireApiKey: options.requireApiKey,
|
|
3586
|
+
peers: peers ? peerDirectoryHandle() : void 0,
|
|
3387
3587
|
refs
|
|
3388
3588
|
});
|
|
3389
3589
|
const availability = new AvailabilityTracker({
|
|
@@ -3392,7 +3592,6 @@ function createWorkerServer(options = {}) {
|
|
|
3392
3592
|
adapterFor,
|
|
3393
3593
|
sessionEnvFor: factory.sessionEnvFor
|
|
3394
3594
|
});
|
|
3395
|
-
const projects = new ProjectInfoService();
|
|
3396
3595
|
const notifier = new SessionNotifier({
|
|
3397
3596
|
...options.notifications,
|
|
3398
3597
|
decorateInfo: (info) => projects.withProject(info)
|
|
@@ -3405,7 +3604,8 @@ function createWorkerServer(options = {}) {
|
|
|
3405
3604
|
producedFiles.watch(runner),
|
|
3406
3605
|
profileUsage.watch(runner),
|
|
3407
3606
|
spendLedger.watch(runner),
|
|
3408
|
-
shell?.watch(runner)
|
|
3607
|
+
shell?.watch(runner),
|
|
3608
|
+
peers?.watch(runner)
|
|
3409
3609
|
];
|
|
3410
3610
|
const profile = runner.info().profile;
|
|
3411
3611
|
if (profile) detachers.push(runner.subscribe((event) => {
|
|
@@ -3505,6 +3705,7 @@ function createWorkerServer(options = {}) {
|
|
|
3505
3705
|
factory,
|
|
3506
3706
|
registry,
|
|
3507
3707
|
parking,
|
|
3708
|
+
peers,
|
|
3508
3709
|
bridge,
|
|
3509
3710
|
projects,
|
|
3510
3711
|
queue,
|
|
@@ -3514,7 +3715,8 @@ function createWorkerServer(options = {}) {
|
|
|
3514
3715
|
hostFilesWritable: options.hostFiles?.write === true,
|
|
3515
3716
|
maxHostFileBytes: options.hostFiles?.maxFileBytes ?? 1048576,
|
|
3516
3717
|
maxHostDirEntries: options.hostFiles?.maxEntries ?? 5e3,
|
|
3517
|
-
shell
|
|
3718
|
+
shell,
|
|
3719
|
+
pricingOverrides: Object.keys(pricing.overrides).length > 0 ? pricing.overrides : void 0
|
|
3518
3720
|
};
|
|
3519
3721
|
const handleRequest = async (req, res) => {
|
|
3520
3722
|
const pathname = new URL(req.url ?? "/", "http://internal").pathname;
|