@ours.network/cowork 0.4.0 → 0.4.1-nightly.20260816.4aaf940

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/dist/daemon.js CHANGED
@@ -10,446 +10,6 @@ var __export = (target, all) => {
10
10
  __defProp(target, name, { get: all[name], enumerable: true });
11
11
  };
12
12
 
13
- // src/adapt.ts
14
- import * as fs from "node:fs";
15
- import { dirname, join, resolve } from "node:path";
16
- import { fileURLToPath } from "node:url";
17
- import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants } from "node:zlib";
18
- function withScope(fn) {
19
- const lifetime = new AdaptObjectLifetime();
20
- try {
21
- return fn(lifetime);
22
- } finally {
23
- lifetime.Finalize();
24
- }
25
- }
26
- async function withScopeAsync(fn) {
27
- const lifetime = new AdaptObjectLifetime();
28
- try {
29
- return await fn(lifetime);
30
- } finally {
31
- lifetime.Finalize();
32
- }
33
- }
34
- function locateUnit() {
35
- const here = dirname(fileURLToPath(import.meta.url));
36
- const override = process.env.OURS_COWORK_UNIT_DIR;
37
- const candidates = override ? [resolve(override)] : [join(here, "mufl_code"), join(here, "..", "mufl_code")];
38
- for (const dir of candidates) {
39
- if (!fs.existsSync(dir)) continue;
40
- const compiled = fs.readdirSync(dir).find((name) => name.endsWith(".muflo"));
41
- if (compiled) {
42
- return {
43
- dir,
44
- hash: compiled.slice(0, -".muflo".length),
45
- contents: new Uint8Array(fs.readFileSync(join(dir, compiled)))
46
- };
47
- }
48
- }
49
- throw new Error(`no compiled .muflo packet found (looked in: ${candidates.join(", ")})`);
50
- }
51
- function wireHandlers(packet, hooks, log) {
52
- const settleAfterActionLoop = (pending) => {
53
- queueMicrotask(() => {
54
- if (packet.pending[0] !== pending || !pending.payload || pending.callbackError) return;
55
- packet.pending.shift();
56
- clearTimeout(pending.timer);
57
- pending.settled = true;
58
- pending.resolve(pending.payload);
59
- });
60
- };
61
- packet.pw.on_return_data = (data) => {
62
- const lifetime = new AdaptObjectLifetime();
63
- data.Attach(lifetime);
64
- try {
65
- if (packet.isClosed) return;
66
- const kind = data.Reduce("kind").Visualize();
67
- if (kind === "save_state") {
68
- try {
69
- hooks.onSaveState();
70
- } catch (error) {
71
- const pending2 = packet.pending[0];
72
- if (pending2) pending2.callbackError = asError(error);
73
- throw error;
74
- }
75
- return;
76
- }
77
- if (kind === "notify_agent") {
78
- const payload = data.Reduce("payload");
79
- hooks.onNotify(payload.Reduce("event").Visualize(), payload);
80
- return;
81
- }
82
- const pending = packet.pending[0];
83
- if (!pending) return;
84
- if (pending.expired) {
85
- packet.releaseExpired(pending);
86
- return;
87
- }
88
- if (pending.payload) pending.payload.Destroy();
89
- pending.payload = data.Reduce("payload").Detach();
90
- settleAfterActionLoop(pending);
91
- } finally {
92
- lifetime.Finalize();
93
- }
94
- };
95
- packet.pw.on_transaction_failure = (message) => {
96
- log(`[${packet.name}] transaction rejected (origin uncorrelated):`, message);
97
- };
98
- }
99
- function asError(error) {
100
- return error instanceof Error ? error : new Error(String(error));
101
- }
102
- function packInvite(raw) {
103
- return brotliCompressSync(raw, {
104
- params: {
105
- [zlibConstants.BROTLI_PARAM_QUALITY]: 11,
106
- [zlibConstants.BROTLI_PARAM_SIZE_HINT]: raw.length
107
- }
108
- }).toString("base64url");
109
- }
110
- function unpackInvite(encoded, maximumBytes) {
111
- const normalized = encoded.replace(/\s+/g, "");
112
- if (maximumBytes !== void 0 && Buffer.byteLength(encoded, "utf8") > maximumBytes || normalized.length === 0 || !/^[A-Za-z0-9_-]+$/.test(normalized)) {
113
- throw new Error("the invite blob is empty, oversized, or invalid base64url");
114
- }
115
- const compressed = Buffer.from(normalized, "base64url");
116
- if (compressed.length === 0) throw new Error("the invite blob is empty or invalid base64url");
117
- return Buffer.from(maximumBytes === void 0 ? brotliDecompressSync(compressed) : brotliDecompressSync(compressed, { maxOutputLength: maximumBytes }));
118
- }
119
- var commonSdk, executableSdk, wrapperSdk, wrappersSdk, AdaptObjectLifetime, adapt_wrapper, object_to_adapt_value, PacketWrapperConfigurator, LATE_RESULT_DRAIN_MS, Packet, AdaptHostShutdownError, AdaptHost;
120
- var init_adapt = __esm({
121
- async "src/adapt.ts"() {
122
- "use strict";
123
- [commonSdk, executableSdk, wrapperSdk, wrappersSdk] = await Promise.all([
124
- import("@adapt-toolkit/sdk/common"),
125
- import("@adapt-toolkit/sdk/executables"),
126
- import("@adapt-toolkit/sdk/wrapper"),
127
- import("@adapt-toolkit/sdk/wrappers")
128
- ]);
129
- ({ AdaptObjectLifetime } = commonSdk);
130
- ({ adapt_wrapper } = executableSdk);
131
- ({ object_to_adapt_value } = wrapperSdk);
132
- ({ PacketWrapperConfigurator } = wrappersSdk);
133
- LATE_RESULT_DRAIN_MS = 50;
134
- Packet = class {
135
- pending = [];
136
- lock = Promise.resolve();
137
- expiredDrain;
138
- releaseExpiredDrain;
139
- closedError;
140
- terminalNotified = false;
141
- onTerminal;
142
- terminalListeners = /* @__PURE__ */ new Set();
143
- makeEnvelope;
144
- name;
145
- cid;
146
- pw;
147
- constructor(name, cid, pw, onTerminal = () => {
148
- }, makeEnvelope = (transactionName, targ) => object_to_adapt_value({ name: transactionName, targ })) {
149
- this.name = name;
150
- this.cid = cid;
151
- this.pw = pw;
152
- this.onTerminal = onTerminal;
153
- this.makeEnvelope = makeEnvelope;
154
- }
155
- get isClosed() {
156
- return this.closedError !== void 0;
157
- }
158
- onTerminalClose(listener) {
159
- if (this.closedError) {
160
- listener(this.closedError);
161
- return () => {
162
- };
163
- }
164
- this.terminalListeners.add(listener);
165
- return () => this.terminalListeners.delete(listener);
166
- }
167
- assertOpen() {
168
- if (this.closedError) throw this.closedError;
169
- }
170
- readonlyTx(name, lifetime) {
171
- this.assertOpen();
172
- const envelope = this.makeEnvelope(name, void 0);
173
- try {
174
- const result = this.pw.packet.ExecuteTransaction(envelope);
175
- return lifetime ? result.Attach(lifetime) : result;
176
- } finally {
177
- envelope.Destroy();
178
- }
179
- }
180
- async withLock(fn) {
181
- this.assertOpen();
182
- const previous = this.lock;
183
- let release;
184
- this.lock = new Promise((resolveLock) => {
185
- release = resolveLock;
186
- });
187
- await previous;
188
- try {
189
- this.assertOpen();
190
- if (this.expiredDrain) await this.expiredDrain;
191
- this.assertOpen();
192
- return await fn();
193
- } finally {
194
- release();
195
- }
196
- }
197
- enqueue(envelope, timeoutMs) {
198
- return new Promise((resolveResult, rejectResult) => {
199
- const timer = setTimeout(() => {
200
- const pending2 = this.pending.find((candidate) => candidate.timer === timer);
201
- if (!pending2 || pending2.settled) return;
202
- pending2.settled = true;
203
- pending2.expired = true;
204
- pending2.reject(new Error(`timed out waiting for transaction result on packet "${this.name}"`));
205
- this.expiredDrain = new Promise((resolveDrain) => {
206
- this.releaseExpiredDrain = resolveDrain;
207
- });
208
- pending2.lateTimer = setTimeout(() => {
209
- if (this.closedError || !pending2.expired || !this.pending.includes(pending2)) return;
210
- this.close(new Error(
211
- `packet "${this.name}" closed after a timed-out transaction produced no correlated callback`
212
- ));
213
- }, LATE_RESULT_DRAIN_MS);
214
- }, timeoutMs);
215
- const pending = { resolve: resolveResult, reject: rejectResult, timer };
216
- this.pending.push(pending);
217
- try {
218
- this.pw.add_client_message(envelope);
219
- } catch (error) {
220
- clearTimeout(timer);
221
- const index = this.pending.indexOf(pending);
222
- if (index >= 0) this.pending.splice(index, 1);
223
- rejectResult(asError(error));
224
- }
225
- });
226
- }
227
- mutatingTx(name, targ, lifetime, timeoutMs = 25e3) {
228
- let envelope;
229
- try {
230
- this.assertOpen();
231
- envelope = this.makeEnvelope(name, targ);
232
- } catch (error) {
233
- return Promise.reject(asError(error));
234
- }
235
- return this.withLock(() => this.enqueue(envelope, timeoutMs)).then(
236
- (payload) => {
237
- envelope.Destroy();
238
- return lifetime ? payload.Attach(lifetime) : payload;
239
- },
240
- (error) => {
241
- envelope.Destroy();
242
- throw error;
243
- }
244
- );
245
- }
246
- newBinary(bytes, lifetime) {
247
- this.assertOpen();
248
- const value = this.pw.packet.NewBinaryFromBuffer(bytes);
249
- return lifetime ? value.Attach(lifetime) : value;
250
- }
251
- close(error = new Error(`packet "${this.name}" is closed`)) {
252
- if (this.closedError) return;
253
- this.closedError = error;
254
- const pending = this.pending.splice(0);
255
- for (const call of pending) {
256
- clearTimeout(call.timer);
257
- if (call.lateTimer) clearTimeout(call.lateTimer);
258
- call.payload?.Destroy();
259
- if (!call.settled) {
260
- call.settled = true;
261
- call.reject(error);
262
- }
263
- }
264
- this.releaseExpiredDrain?.();
265
- this.releaseExpiredDrain = void 0;
266
- this.expiredDrain = void 0;
267
- if (!this.terminalNotified) {
268
- this.terminalNotified = true;
269
- for (const listener of this.terminalListeners) listener(error);
270
- this.terminalListeners.clear();
271
- this.onTerminal(error);
272
- }
273
- }
274
- releaseExpired(pending) {
275
- const index = this.pending.indexOf(pending);
276
- if (index >= 0) this.pending.splice(index, 1);
277
- if (pending.lateTimer) clearTimeout(pending.lateTimer);
278
- pending.payload?.Destroy();
279
- this.releaseExpiredDrain?.();
280
- this.releaseExpiredDrain = void 0;
281
- this.expiredDrain = void 0;
282
- }
283
- };
284
- AdaptHostShutdownError = class extends AggregateError {
285
- requiresProcessExit;
286
- constructor(errors, requiresProcessExit) {
287
- super(errors, "AdaptHost shutdown encountered errors");
288
- this.name = "AdaptHostShutdownError";
289
- this.requiresProcessExit = requiresProcessExit;
290
- }
291
- };
292
- AdaptHost = class {
293
- wrapper;
294
- packets = /* @__PURE__ */ new Map();
295
- exposedPackets = /* @__PURE__ */ new Set();
296
- brokerUrl;
297
- log;
298
- unit;
299
- shutdownWrapper;
300
- constructor(brokerUrl, log = () => {
301
- }, options = {}) {
302
- this.brokerUrl = brokerUrl;
303
- this.log = log;
304
- this.unit = options.unit ?? locateUnit();
305
- this.shutdownWrapper = options.shutdownWrapper;
306
- }
307
- get packetCount() {
308
- return this.packets.size;
309
- }
310
- async boot() {
311
- if (this.wrapper) return;
312
- this.wrapper = await adapt_wrapper.start([
313
- "--broker_address",
314
- this.brokerUrl,
315
- "--test_mode",
316
- "--logger_config",
317
- "--level",
318
- "WARNING",
319
- "--stdout",
320
- "stderr",
321
- "--logger_config_end"
322
- ]);
323
- this.wrapper.on_packet_created_cb = (cid) => {
324
- this.log(`wrapper: packet ready ${cid.slice(0, 12)}\u2026`);
325
- };
326
- this.wrapper.start();
327
- }
328
- createPacket(name, seed, signingSecret, options = {}) {
329
- const wrapper = this.wrapper;
330
- if (!wrapper) return Promise.reject(new Error("AdaptHost.boot() must complete before creating packets"));
331
- const config = new PacketWrapperConfigurator();
332
- config.deferred_exposure = options.deferredExposure ?? false;
333
- const args = [
334
- "--unit_hash",
335
- this.unit.hash,
336
- "--seed_phrase",
337
- seed,
338
- "--unit_dir_path",
339
- this.unit.dir
340
- ];
341
- if (signingSecret) args.push("--init_trn_argument", JSON.stringify(signingSecret));
342
- config.process_arguments(args);
343
- return new Promise((resolveCreate, rejectCreate) => {
344
- let settled = false;
345
- const timer = setTimeout(() => {
346
- settled = true;
347
- rejectCreate(new Error(`packet creation for "${name}" timed out`));
348
- }, 3e4);
349
- try {
350
- wrapper.packet_manager.create_packet(config, (pw) => {
351
- if (settled) {
352
- pw.dispose();
353
- return;
354
- }
355
- settled = true;
356
- clearTimeout(timer);
357
- const cid = withScope((lifetime) => pw.packet.GetContainerID().Attach(lifetime).Visualize());
358
- let packet;
359
- packet = new Packet(name, cid, pw, (error) => {
360
- queueMicrotask(() => {
361
- if (this.packets.get(cid) !== packet) return;
362
- try {
363
- this.removePacket(cid, error);
364
- } catch (removeError) {
365
- this.log(`[${name}] terminal packet removal failed:`, removeError);
366
- }
367
- });
368
- });
369
- this.packets.set(cid, packet);
370
- if (!config.deferred_exposure) this.exposedPackets.add(cid);
371
- resolveCreate(packet);
372
- }, this.unit.contents);
373
- } catch (error) {
374
- if (settled) return;
375
- settled = true;
376
- clearTimeout(timer);
377
- rejectCreate(asError(error));
378
- }
379
- });
380
- }
381
- exposePacket(cid) {
382
- const wrapper = this.wrapper;
383
- const packet = this.packets.get(cid);
384
- if (!wrapper || !packet) throw new Error(`cannot expose unknown packet ${cid}`);
385
- if (packet.isClosed) throw new Error(`cannot expose closed packet ${cid}`);
386
- wrapper.expose_packet(cid);
387
- this.exposedPackets.add(cid);
388
- }
389
- isPacketExposed(cid) {
390
- return this.exposedPackets.has(cid);
391
- }
392
- removePacket(cid, error = new Error(`packet ${cid} removed from host`)) {
393
- const wrapper = this.wrapper;
394
- if (!wrapper) throw new Error("AdaptHost is not booted");
395
- const packet = this.packets.get(cid);
396
- if (!packet) return;
397
- packet.close(error);
398
- wrapper.remove_packet(cid);
399
- this.packets.delete(cid);
400
- this.exposedPackets.delete(cid);
401
- }
402
- close() {
403
- if (!this.wrapper) return;
404
- const errors = [];
405
- for (const cid of [...this.packets.keys()]) {
406
- try {
407
- this.removePacket(cid, new Error("AdaptHost closed"));
408
- } catch (error) {
409
- errors.push(asError(error));
410
- }
411
- }
412
- this.wrapper = void 0;
413
- if (errors.length) throw new AggregateError(errors, "failed to remove all hosted packets");
414
- }
415
- /**
416
- * Release every public SDK resource. SDK 0.10.12 exposes packet disposal but
417
- * not its private broker client's stop(), so a real native wrapper reports
418
- * that the owning process must exit after graceful daemon cleanup.
419
- */
420
- async shutdown() {
421
- const wrapper = this.wrapper;
422
- if (!wrapper) return { requiresProcessExit: false };
423
- const errors = [];
424
- let requiresProcessExit = true;
425
- try {
426
- this.close();
427
- } catch (error) {
428
- errors.push(error);
429
- }
430
- try {
431
- if (this.shutdownWrapper) {
432
- await this.shutdownWrapper(wrapper);
433
- requiresProcessExit = false;
434
- } else {
435
- const futureWrapper = wrapper;
436
- const publicStop = futureWrapper.shutdown ?? futureWrapper.stop ?? futureWrapper.dispose;
437
- if (typeof publicStop === "function") {
438
- await publicStop.call(wrapper);
439
- requiresProcessExit = false;
440
- }
441
- }
442
- } catch (error) {
443
- errors.push(error);
444
- }
445
- this.wrapper = void 0;
446
- if (errors.length > 0) throw new AdaptHostShutdownError(errors, requiresProcessExit);
447
- return { requiresProcessExit };
448
- }
449
- };
450
- }
451
- });
452
-
453
13
  // node_modules/zod/v3/helpers/util.js
454
14
  var util, objectUtil, ZodParsedType, getParsedType;
455
15
  var init_util = __esm({
@@ -861,8 +421,8 @@ var init_parseUtil = __esm({
861
421
  "node_modules/zod/v3/helpers/parseUtil.js"() {
862
422
  init_errors();
863
423
  init_en();
864
- makeIssue = (params2) => {
865
- const { data, path, errorMaps, issueData } = params2;
424
+ makeIssue = (params) => {
425
+ const { data, path, errorMaps, issueData } = params;
866
426
  const fullPath = [...path, ...issueData.path || []];
867
427
  const fullIssue = {
868
428
  ...issueData,
@@ -971,17 +531,17 @@ var init_errorUtil = __esm({
971
531
  });
972
532
 
973
533
  // node_modules/zod/v3/types.js
974
- function processCreateParams(params2) {
975
- if (!params2)
534
+ function processCreateParams(params) {
535
+ if (!params)
976
536
  return {};
977
- const { errorMap: errorMap2, invalid_type_error, required_error, description } = params2;
537
+ const { errorMap: errorMap2, invalid_type_error, required_error, description } = params;
978
538
  if (errorMap2 && (invalid_type_error || required_error)) {
979
539
  throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);
980
540
  }
981
541
  if (errorMap2)
982
542
  return { errorMap: errorMap2, description };
983
543
  const customMap = (iss, ctx) => {
984
- const { message } = params2;
544
+ const { message } = params;
985
545
  if (iss.code === "invalid_enum_value") {
986
546
  return { message: message ?? ctx.defaultError };
987
547
  }
@@ -1128,15 +688,15 @@ function mergeValues(a, b) {
1128
688
  return { valid: false };
1129
689
  }
1130
690
  }
1131
- function createZodEnum(values, params2) {
691
+ function createZodEnum(values, params) {
1132
692
  return new ZodEnum({
1133
693
  values,
1134
694
  typeName: ZodFirstPartyTypeKind.ZodEnum,
1135
- ...processCreateParams(params2)
695
+ ...processCreateParams(params)
1136
696
  });
1137
697
  }
1138
- function cleanParams(params2, data) {
1139
- const p = typeof params2 === "function" ? params2(data) : typeof params2 === "string" ? { message: params2 } : params2;
698
+ function cleanParams(params, data) {
699
+ const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
1140
700
  const p2 = typeof p === "string" ? { message: p } : p;
1141
701
  return p2;
1142
702
  }
@@ -1147,16 +707,16 @@ function custom(check, _params = {}, fatal) {
1147
707
  if (r instanceof Promise) {
1148
708
  return r.then((r2) => {
1149
709
  if (!r2) {
1150
- const params2 = cleanParams(_params, data);
1151
- const _fatal = params2.fatal ?? fatal ?? true;
1152
- ctx.addIssue({ code: "custom", ...params2, fatal: _fatal });
710
+ const params = cleanParams(_params, data);
711
+ const _fatal = params.fatal ?? fatal ?? true;
712
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
1153
713
  }
1154
714
  });
1155
715
  }
1156
716
  if (!r) {
1157
- const params2 = cleanParams(_params, data);
1158
- const _fatal = params2.fatal ?? fatal ?? true;
1159
- ctx.addIssue({ code: "custom", ...params2, fatal: _fatal });
717
+ const params = cleanParams(_params, data);
718
+ const _fatal = params.fatal ?? fatal ?? true;
719
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
1160
720
  }
1161
721
  return;
1162
722
  });
@@ -1249,20 +809,20 @@ var init_types = __esm({
1249
809
  const result = this._parse(input);
1250
810
  return Promise.resolve(result);
1251
811
  }
1252
- parse(data, params2) {
1253
- const result = this.safeParse(data, params2);
812
+ parse(data, params) {
813
+ const result = this.safeParse(data, params);
1254
814
  if (result.success)
1255
815
  return result.data;
1256
816
  throw result.error;
1257
817
  }
1258
- safeParse(data, params2) {
818
+ safeParse(data, params) {
1259
819
  const ctx = {
1260
820
  common: {
1261
821
  issues: [],
1262
- async: params2?.async ?? false,
1263
- contextualErrorMap: params2?.errorMap
822
+ async: params?.async ?? false,
823
+ contextualErrorMap: params?.errorMap
1264
824
  },
1265
- path: params2?.path || [],
825
+ path: params?.path || [],
1266
826
  schemaErrorMap: this._def.errorMap,
1267
827
  parent: null,
1268
828
  data,
@@ -1307,20 +867,20 @@ var init_types = __esm({
1307
867
  issues: ctx.common.issues
1308
868
  });
1309
869
  }
1310
- async parseAsync(data, params2) {
1311
- const result = await this.safeParseAsync(data, params2);
870
+ async parseAsync(data, params) {
871
+ const result = await this.safeParseAsync(data, params);
1312
872
  if (result.success)
1313
873
  return result.data;
1314
874
  throw result.error;
1315
875
  }
1316
- async safeParseAsync(data, params2) {
876
+ async safeParseAsync(data, params) {
1317
877
  const ctx = {
1318
878
  common: {
1319
879
  issues: [],
1320
- contextualErrorMap: params2?.errorMap,
880
+ contextualErrorMap: params?.errorMap,
1321
881
  async: true
1322
882
  },
1323
- path: params2?.path || [],
883
+ path: params?.path || [],
1324
884
  schemaErrorMap: this._def.errorMap,
1325
885
  parent: null,
1326
886
  data,
@@ -2046,12 +1606,12 @@ var init_types = __esm({
2046
1606
  return max;
2047
1607
  }
2048
1608
  };
2049
- ZodString.create = (params2) => {
1609
+ ZodString.create = (params) => {
2050
1610
  return new ZodString({
2051
1611
  checks: [],
2052
1612
  typeName: ZodFirstPartyTypeKind.ZodString,
2053
- coerce: params2?.coerce ?? false,
2054
- ...processCreateParams(params2)
1613
+ coerce: params?.coerce ?? false,
1614
+ ...processCreateParams(params)
2055
1615
  });
2056
1616
  };
2057
1617
  ZodNumber = class _ZodNumber extends ZodType {
@@ -2278,12 +1838,12 @@ var init_types = __esm({
2278
1838
  return Number.isFinite(min) && Number.isFinite(max);
2279
1839
  }
2280
1840
  };
2281
- ZodNumber.create = (params2) => {
1841
+ ZodNumber.create = (params) => {
2282
1842
  return new ZodNumber({
2283
1843
  checks: [],
2284
1844
  typeName: ZodFirstPartyTypeKind.ZodNumber,
2285
- coerce: params2?.coerce || false,
2286
- ...processCreateParams(params2)
1845
+ coerce: params?.coerce || false,
1846
+ ...processCreateParams(params)
2287
1847
  });
2288
1848
  };
2289
1849
  ZodBigInt = class _ZodBigInt extends ZodType {
@@ -2450,12 +2010,12 @@ var init_types = __esm({
2450
2010
  return max;
2451
2011
  }
2452
2012
  };
2453
- ZodBigInt.create = (params2) => {
2013
+ ZodBigInt.create = (params) => {
2454
2014
  return new ZodBigInt({
2455
2015
  checks: [],
2456
2016
  typeName: ZodFirstPartyTypeKind.ZodBigInt,
2457
- coerce: params2?.coerce ?? false,
2458
- ...processCreateParams(params2)
2017
+ coerce: params?.coerce ?? false,
2018
+ ...processCreateParams(params)
2459
2019
  });
2460
2020
  };
2461
2021
  ZodBoolean = class extends ZodType {
@@ -2476,11 +2036,11 @@ var init_types = __esm({
2476
2036
  return OK(input.data);
2477
2037
  }
2478
2038
  };
2479
- ZodBoolean.create = (params2) => {
2039
+ ZodBoolean.create = (params) => {
2480
2040
  return new ZodBoolean({
2481
2041
  typeName: ZodFirstPartyTypeKind.ZodBoolean,
2482
- coerce: params2?.coerce || false,
2483
- ...processCreateParams(params2)
2042
+ coerce: params?.coerce || false,
2043
+ ...processCreateParams(params)
2484
2044
  });
2485
2045
  };
2486
2046
  ZodDate = class _ZodDate extends ZodType {
@@ -2584,12 +2144,12 @@ var init_types = __esm({
2584
2144
  return max != null ? new Date(max) : null;
2585
2145
  }
2586
2146
  };
2587
- ZodDate.create = (params2) => {
2147
+ ZodDate.create = (params) => {
2588
2148
  return new ZodDate({
2589
2149
  checks: [],
2590
- coerce: params2?.coerce || false,
2150
+ coerce: params?.coerce || false,
2591
2151
  typeName: ZodFirstPartyTypeKind.ZodDate,
2592
- ...processCreateParams(params2)
2152
+ ...processCreateParams(params)
2593
2153
  });
2594
2154
  };
2595
2155
  ZodSymbol = class extends ZodType {
@@ -2607,10 +2167,10 @@ var init_types = __esm({
2607
2167
  return OK(input.data);
2608
2168
  }
2609
2169
  };
2610
- ZodSymbol.create = (params2) => {
2170
+ ZodSymbol.create = (params) => {
2611
2171
  return new ZodSymbol({
2612
2172
  typeName: ZodFirstPartyTypeKind.ZodSymbol,
2613
- ...processCreateParams(params2)
2173
+ ...processCreateParams(params)
2614
2174
  });
2615
2175
  };
2616
2176
  ZodUndefined = class extends ZodType {
@@ -2628,10 +2188,10 @@ var init_types = __esm({
2628
2188
  return OK(input.data);
2629
2189
  }
2630
2190
  };
2631
- ZodUndefined.create = (params2) => {
2191
+ ZodUndefined.create = (params) => {
2632
2192
  return new ZodUndefined({
2633
2193
  typeName: ZodFirstPartyTypeKind.ZodUndefined,
2634
- ...processCreateParams(params2)
2194
+ ...processCreateParams(params)
2635
2195
  });
2636
2196
  };
2637
2197
  ZodNull = class extends ZodType {
@@ -2649,10 +2209,10 @@ var init_types = __esm({
2649
2209
  return OK(input.data);
2650
2210
  }
2651
2211
  };
2652
- ZodNull.create = (params2) => {
2212
+ ZodNull.create = (params) => {
2653
2213
  return new ZodNull({
2654
2214
  typeName: ZodFirstPartyTypeKind.ZodNull,
2655
- ...processCreateParams(params2)
2215
+ ...processCreateParams(params)
2656
2216
  });
2657
2217
  };
2658
2218
  ZodAny = class extends ZodType {
@@ -2664,10 +2224,10 @@ var init_types = __esm({
2664
2224
  return OK(input.data);
2665
2225
  }
2666
2226
  };
2667
- ZodAny.create = (params2) => {
2227
+ ZodAny.create = (params) => {
2668
2228
  return new ZodAny({
2669
2229
  typeName: ZodFirstPartyTypeKind.ZodAny,
2670
- ...processCreateParams(params2)
2230
+ ...processCreateParams(params)
2671
2231
  });
2672
2232
  };
2673
2233
  ZodUnknown = class extends ZodType {
@@ -2679,10 +2239,10 @@ var init_types = __esm({
2679
2239
  return OK(input.data);
2680
2240
  }
2681
2241
  };
2682
- ZodUnknown.create = (params2) => {
2242
+ ZodUnknown.create = (params) => {
2683
2243
  return new ZodUnknown({
2684
2244
  typeName: ZodFirstPartyTypeKind.ZodUnknown,
2685
- ...processCreateParams(params2)
2245
+ ...processCreateParams(params)
2686
2246
  });
2687
2247
  };
2688
2248
  ZodNever = class extends ZodType {
@@ -2696,10 +2256,10 @@ var init_types = __esm({
2696
2256
  return INVALID;
2697
2257
  }
2698
2258
  };
2699
- ZodNever.create = (params2) => {
2259
+ ZodNever.create = (params) => {
2700
2260
  return new ZodNever({
2701
2261
  typeName: ZodFirstPartyTypeKind.ZodNever,
2702
- ...processCreateParams(params2)
2262
+ ...processCreateParams(params)
2703
2263
  });
2704
2264
  };
2705
2265
  ZodVoid = class extends ZodType {
@@ -2717,10 +2277,10 @@ var init_types = __esm({
2717
2277
  return OK(input.data);
2718
2278
  }
2719
2279
  };
2720
- ZodVoid.create = (params2) => {
2280
+ ZodVoid.create = (params) => {
2721
2281
  return new ZodVoid({
2722
2282
  typeName: ZodFirstPartyTypeKind.ZodVoid,
2723
- ...processCreateParams(params2)
2283
+ ...processCreateParams(params)
2724
2284
  });
2725
2285
  };
2726
2286
  ZodArray = class _ZodArray extends ZodType {
@@ -2814,14 +2374,14 @@ var init_types = __esm({
2814
2374
  return this.min(1, message);
2815
2375
  }
2816
2376
  };
2817
- ZodArray.create = (schema, params2) => {
2377
+ ZodArray.create = (schema, params) => {
2818
2378
  return new ZodArray({
2819
2379
  type: schema,
2820
2380
  minLength: null,
2821
2381
  maxLength: null,
2822
2382
  exactLength: null,
2823
2383
  typeName: ZodFirstPartyTypeKind.ZodArray,
2824
- ...processCreateParams(params2)
2384
+ ...processCreateParams(params)
2825
2385
  });
2826
2386
  };
2827
2387
  ZodObject = class _ZodObject extends ZodType {
@@ -3135,31 +2695,31 @@ var init_types = __esm({
3135
2695
  return createZodEnum(util.objectKeys(this.shape));
3136
2696
  }
3137
2697
  };
3138
- ZodObject.create = (shape, params2) => {
2698
+ ZodObject.create = (shape, params) => {
3139
2699
  return new ZodObject({
3140
2700
  shape: () => shape,
3141
2701
  unknownKeys: "strip",
3142
2702
  catchall: ZodNever.create(),
3143
2703
  typeName: ZodFirstPartyTypeKind.ZodObject,
3144
- ...processCreateParams(params2)
2704
+ ...processCreateParams(params)
3145
2705
  });
3146
2706
  };
3147
- ZodObject.strictCreate = (shape, params2) => {
2707
+ ZodObject.strictCreate = (shape, params) => {
3148
2708
  return new ZodObject({
3149
2709
  shape: () => shape,
3150
2710
  unknownKeys: "strict",
3151
2711
  catchall: ZodNever.create(),
3152
2712
  typeName: ZodFirstPartyTypeKind.ZodObject,
3153
- ...processCreateParams(params2)
2713
+ ...processCreateParams(params)
3154
2714
  });
3155
2715
  };
3156
- ZodObject.lazycreate = (shape, params2) => {
2716
+ ZodObject.lazycreate = (shape, params) => {
3157
2717
  return new ZodObject({
3158
2718
  shape,
3159
2719
  unknownKeys: "strip",
3160
2720
  catchall: ZodNever.create(),
3161
2721
  typeName: ZodFirstPartyTypeKind.ZodObject,
3162
- ...processCreateParams(params2)
2722
+ ...processCreateParams(params)
3163
2723
  });
3164
2724
  };
3165
2725
  ZodUnion = class extends ZodType {
@@ -3246,11 +2806,11 @@ var init_types = __esm({
3246
2806
  return this._def.options;
3247
2807
  }
3248
2808
  };
3249
- ZodUnion.create = (types, params2) => {
2809
+ ZodUnion.create = (types, params) => {
3250
2810
  return new ZodUnion({
3251
2811
  options: types,
3252
2812
  typeName: ZodFirstPartyTypeKind.ZodUnion,
3253
- ...processCreateParams(params2)
2813
+ ...processCreateParams(params)
3254
2814
  });
3255
2815
  };
3256
2816
  getDiscriminator = (type) => {
@@ -3337,7 +2897,7 @@ var init_types = __esm({
3337
2897
  * @param types an array of object schemas
3338
2898
  * @param params
3339
2899
  */
3340
- static create(discriminator, options, params2) {
2900
+ static create(discriminator, options, params) {
3341
2901
  const optionsMap = /* @__PURE__ */ new Map();
3342
2902
  for (const type of options) {
3343
2903
  const discriminatorValues = getDiscriminator(type.shape[discriminator]);
@@ -3356,7 +2916,7 @@ var init_types = __esm({
3356
2916
  discriminator,
3357
2917
  options,
3358
2918
  optionsMap,
3359
- ...processCreateParams(params2)
2919
+ ...processCreateParams(params)
3360
2920
  });
3361
2921
  }
3362
2922
  };
@@ -3405,12 +2965,12 @@ var init_types = __esm({
3405
2965
  }
3406
2966
  }
3407
2967
  };
3408
- ZodIntersection.create = (left, right, params2) => {
2968
+ ZodIntersection.create = (left, right, params) => {
3409
2969
  return new ZodIntersection({
3410
2970
  left,
3411
2971
  right,
3412
2972
  typeName: ZodFirstPartyTypeKind.ZodIntersection,
3413
- ...processCreateParams(params2)
2973
+ ...processCreateParams(params)
3414
2974
  });
3415
2975
  };
3416
2976
  ZodTuple = class _ZodTuple extends ZodType {
@@ -3469,7 +3029,7 @@ var init_types = __esm({
3469
3029
  });
3470
3030
  }
3471
3031
  };
3472
- ZodTuple.create = (schemas, params2) => {
3032
+ ZodTuple.create = (schemas, params) => {
3473
3033
  if (!Array.isArray(schemas)) {
3474
3034
  throw new Error("You must pass an array of schemas to z.tuple([ ... ])");
3475
3035
  }
@@ -3477,7 +3037,7 @@ var init_types = __esm({
3477
3037
  items: schemas,
3478
3038
  typeName: ZodFirstPartyTypeKind.ZodTuple,
3479
3039
  rest: null,
3480
- ...processCreateParams(params2)
3040
+ ...processCreateParams(params)
3481
3041
  });
3482
3042
  };
3483
3043
  ZodRecord = class _ZodRecord extends ZodType {
@@ -3591,12 +3151,12 @@ var init_types = __esm({
3591
3151
  }
3592
3152
  }
3593
3153
  };
3594
- ZodMap.create = (keyType, valueType, params2) => {
3154
+ ZodMap.create = (keyType, valueType, params) => {
3595
3155
  return new ZodMap({
3596
3156
  valueType,
3597
3157
  keyType,
3598
3158
  typeName: ZodFirstPartyTypeKind.ZodMap,
3599
- ...processCreateParams(params2)
3159
+ ...processCreateParams(params)
3600
3160
  });
3601
3161
  };
3602
3162
  ZodSet = class _ZodSet extends ZodType {
@@ -3675,13 +3235,13 @@ var init_types = __esm({
3675
3235
  return this.min(1, message);
3676
3236
  }
3677
3237
  };
3678
- ZodSet.create = (valueType, params2) => {
3238
+ ZodSet.create = (valueType, params) => {
3679
3239
  return new ZodSet({
3680
3240
  valueType,
3681
3241
  minSize: null,
3682
3242
  maxSize: null,
3683
3243
  typeName: ZodFirstPartyTypeKind.ZodSet,
3684
- ...processCreateParams(params2)
3244
+ ...processCreateParams(params)
3685
3245
  });
3686
3246
  };
3687
3247
  ZodFunction = class _ZodFunction extends ZodType {
@@ -3721,18 +3281,18 @@ var init_types = __esm({
3721
3281
  }
3722
3282
  });
3723
3283
  }
3724
- const params2 = { errorMap: ctx.common.contextualErrorMap };
3284
+ const params = { errorMap: ctx.common.contextualErrorMap };
3725
3285
  const fn = ctx.data;
3726
3286
  if (this._def.returns instanceof ZodPromise) {
3727
3287
  const me = this;
3728
3288
  return OK(async function(...args) {
3729
3289
  const error = new ZodError([]);
3730
- const parsedArgs = await me._def.args.parseAsync(args, params2).catch((e) => {
3290
+ const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
3731
3291
  error.addIssue(makeArgsIssue(args, e));
3732
3292
  throw error;
3733
3293
  });
3734
3294
  const result = await Reflect.apply(fn, this, parsedArgs);
3735
- const parsedReturns = await me._def.returns._def.type.parseAsync(result, params2).catch((e) => {
3295
+ const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
3736
3296
  error.addIssue(makeReturnsIssue(result, e));
3737
3297
  throw error;
3738
3298
  });
@@ -3741,12 +3301,12 @@ var init_types = __esm({
3741
3301
  } else {
3742
3302
  const me = this;
3743
3303
  return OK(function(...args) {
3744
- const parsedArgs = me._def.args.safeParse(args, params2);
3304
+ const parsedArgs = me._def.args.safeParse(args, params);
3745
3305
  if (!parsedArgs.success) {
3746
3306
  throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
3747
3307
  }
3748
3308
  const result = Reflect.apply(fn, this, parsedArgs.data);
3749
- const parsedReturns = me._def.returns.safeParse(result, params2);
3309
+ const parsedReturns = me._def.returns.safeParse(result, params);
3750
3310
  if (!parsedReturns.success) {
3751
3311
  throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
3752
3312
  }
@@ -3780,12 +3340,12 @@ var init_types = __esm({
3780
3340
  const validatedFunc = this.parse(func);
3781
3341
  return validatedFunc;
3782
3342
  }
3783
- static create(args, returns, params2) {
3343
+ static create(args, returns, params) {
3784
3344
  return new _ZodFunction({
3785
3345
  args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
3786
3346
  returns: returns || ZodUnknown.create(),
3787
3347
  typeName: ZodFirstPartyTypeKind.ZodFunction,
3788
- ...processCreateParams(params2)
3348
+ ...processCreateParams(params)
3789
3349
  });
3790
3350
  }
3791
3351
  };
@@ -3799,11 +3359,11 @@ var init_types = __esm({
3799
3359
  return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx });
3800
3360
  }
3801
3361
  };
3802
- ZodLazy.create = (getter, params2) => {
3362
+ ZodLazy.create = (getter, params) => {
3803
3363
  return new ZodLazy({
3804
3364
  getter,
3805
3365
  typeName: ZodFirstPartyTypeKind.ZodLazy,
3806
- ...processCreateParams(params2)
3366
+ ...processCreateParams(params)
3807
3367
  });
3808
3368
  };
3809
3369
  ZodLiteral = class extends ZodType {
@@ -3823,11 +3383,11 @@ var init_types = __esm({
3823
3383
  return this._def.value;
3824
3384
  }
3825
3385
  };
3826
- ZodLiteral.create = (value, params2) => {
3386
+ ZodLiteral.create = (value, params) => {
3827
3387
  return new ZodLiteral({
3828
3388
  value,
3829
3389
  typeName: ZodFirstPartyTypeKind.ZodLiteral,
3830
- ...processCreateParams(params2)
3390
+ ...processCreateParams(params)
3831
3391
  });
3832
3392
  };
3833
3393
  ZodEnum = class _ZodEnum extends ZodType {
@@ -3926,11 +3486,11 @@ var init_types = __esm({
3926
3486
  return this._def.values;
3927
3487
  }
3928
3488
  };
3929
- ZodNativeEnum.create = (values, params2) => {
3489
+ ZodNativeEnum.create = (values, params) => {
3930
3490
  return new ZodNativeEnum({
3931
3491
  values,
3932
3492
  typeName: ZodFirstPartyTypeKind.ZodNativeEnum,
3933
- ...processCreateParams(params2)
3493
+ ...processCreateParams(params)
3934
3494
  });
3935
3495
  };
3936
3496
  ZodPromise = class extends ZodType {
@@ -3956,11 +3516,11 @@ var init_types = __esm({
3956
3516
  }));
3957
3517
  }
3958
3518
  };
3959
- ZodPromise.create = (schema, params2) => {
3519
+ ZodPromise.create = (schema, params) => {
3960
3520
  return new ZodPromise({
3961
3521
  type: schema,
3962
3522
  typeName: ZodFirstPartyTypeKind.ZodPromise,
3963
- ...processCreateParams(params2)
3523
+ ...processCreateParams(params)
3964
3524
  });
3965
3525
  };
3966
3526
  ZodEffects = class extends ZodType {
@@ -4086,20 +3646,20 @@ var init_types = __esm({
4086
3646
  util.assertNever(effect);
4087
3647
  }
4088
3648
  };
4089
- ZodEffects.create = (schema, effect, params2) => {
3649
+ ZodEffects.create = (schema, effect, params) => {
4090
3650
  return new ZodEffects({
4091
3651
  schema,
4092
3652
  typeName: ZodFirstPartyTypeKind.ZodEffects,
4093
3653
  effect,
4094
- ...processCreateParams(params2)
3654
+ ...processCreateParams(params)
4095
3655
  });
4096
3656
  };
4097
- ZodEffects.createWithPreprocess = (preprocess, schema, params2) => {
3657
+ ZodEffects.createWithPreprocess = (preprocess, schema, params) => {
4098
3658
  return new ZodEffects({
4099
3659
  schema,
4100
3660
  effect: { type: "preprocess", transform: preprocess },
4101
3661
  typeName: ZodFirstPartyTypeKind.ZodEffects,
4102
- ...processCreateParams(params2)
3662
+ ...processCreateParams(params)
4103
3663
  });
4104
3664
  };
4105
3665
  ZodOptional = class extends ZodType {
@@ -4114,11 +3674,11 @@ var init_types = __esm({
4114
3674
  return this._def.innerType;
4115
3675
  }
4116
3676
  };
4117
- ZodOptional.create = (type, params2) => {
3677
+ ZodOptional.create = (type, params) => {
4118
3678
  return new ZodOptional({
4119
3679
  innerType: type,
4120
3680
  typeName: ZodFirstPartyTypeKind.ZodOptional,
4121
- ...processCreateParams(params2)
3681
+ ...processCreateParams(params)
4122
3682
  });
4123
3683
  };
4124
3684
  ZodNullable = class extends ZodType {
@@ -4133,11 +3693,11 @@ var init_types = __esm({
4133
3693
  return this._def.innerType;
4134
3694
  }
4135
3695
  };
4136
- ZodNullable.create = (type, params2) => {
3696
+ ZodNullable.create = (type, params) => {
4137
3697
  return new ZodNullable({
4138
3698
  innerType: type,
4139
3699
  typeName: ZodFirstPartyTypeKind.ZodNullable,
4140
- ...processCreateParams(params2)
3700
+ ...processCreateParams(params)
4141
3701
  });
4142
3702
  };
4143
3703
  ZodDefault = class extends ZodType {
@@ -4157,12 +3717,12 @@ var init_types = __esm({
4157
3717
  return this._def.innerType;
4158
3718
  }
4159
3719
  };
4160
- ZodDefault.create = (type, params2) => {
3720
+ ZodDefault.create = (type, params) => {
4161
3721
  return new ZodDefault({
4162
3722
  innerType: type,
4163
3723
  typeName: ZodFirstPartyTypeKind.ZodDefault,
4164
- defaultValue: typeof params2.default === "function" ? params2.default : () => params2.default,
4165
- ...processCreateParams(params2)
3724
+ defaultValue: typeof params.default === "function" ? params.default : () => params.default,
3725
+ ...processCreateParams(params)
4166
3726
  });
4167
3727
  };
4168
3728
  ZodCatch = class extends ZodType {
@@ -4210,12 +3770,12 @@ var init_types = __esm({
4210
3770
  return this._def.innerType;
4211
3771
  }
4212
3772
  };
4213
- ZodCatch.create = (type, params2) => {
3773
+ ZodCatch.create = (type, params) => {
4214
3774
  return new ZodCatch({
4215
3775
  innerType: type,
4216
3776
  typeName: ZodFirstPartyTypeKind.ZodCatch,
4217
- catchValue: typeof params2.catch === "function" ? params2.catch : () => params2.catch,
4218
- ...processCreateParams(params2)
3777
+ catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
3778
+ ...processCreateParams(params)
4219
3779
  });
4220
3780
  };
4221
3781
  ZodNaN = class extends ZodType {
@@ -4233,10 +3793,10 @@ var init_types = __esm({
4233
3793
  return { status: "valid", value: input.data };
4234
3794
  }
4235
3795
  };
4236
- ZodNaN.create = (params2) => {
3796
+ ZodNaN.create = (params) => {
4237
3797
  return new ZodNaN({
4238
3798
  typeName: ZodFirstPartyTypeKind.ZodNaN,
4239
- ...processCreateParams(params2)
3799
+ ...processCreateParams(params)
4240
3800
  });
4241
3801
  };
4242
3802
  BRAND = Symbol("zod_brand");
@@ -4324,11 +3884,11 @@ var init_types = __esm({
4324
3884
  return this._def.innerType;
4325
3885
  }
4326
3886
  };
4327
- ZodReadonly.create = (type, params2) => {
3887
+ ZodReadonly.create = (type, params) => {
4328
3888
  return new ZodReadonly({
4329
3889
  innerType: type,
4330
3890
  typeName: ZodFirstPartyTypeKind.ZodReadonly,
4331
- ...processCreateParams(params2)
3891
+ ...processCreateParams(params)
4332
3892
  });
4333
3893
  };
4334
3894
  late = {
@@ -4372,9 +3932,9 @@ var init_types = __esm({
4372
3932
  ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
4373
3933
  ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
4374
3934
  })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
4375
- instanceOfType = (cls, params2 = {
3935
+ instanceOfType = (cls, params = {
4376
3936
  message: `Input not instance of ${cls.name}`
4377
- }) => custom((data) => data instanceof cls, params2);
3937
+ }) => custom((data) => data instanceof cls, params);
4378
3938
  stringType = ZodString.create;
4379
3939
  numberType = ZodNumber.create;
4380
3940
  nanType = ZodNaN.create;
@@ -4559,26 +4119,29 @@ var init_zod = __esm({
4559
4119
  // src/config.ts
4560
4120
  import * as nodeFs from "node:fs";
4561
4121
  import { homedir } from "node:os";
4562
- import { dirname as dirname2, isAbsolute, join as join2, parse, resolve as resolve2 } from "node:path";
4122
+ import { dirname, isAbsolute, join, parse, resolve } from "node:path";
4123
+ function daemonMode(config) {
4124
+ return config.daemon?.mode ?? "embedded";
4125
+ }
4563
4126
  function defaultConfig(home = homedir()) {
4564
4127
  return {
4565
4128
  version: 1,
4566
4129
  brokerUrl: "wss://broker1.ours.network",
4567
- stateDir: resolve2(home, ".ours-cowork"),
4130
+ stateDir: resolve(home, ".ours-cowork"),
4568
4131
  rest: { enabled: true, port: 3052 }
4569
4132
  };
4570
4133
  }
4571
4134
  function loadConfig(env = process.env, io = {}) {
4572
- const fs2 = io.fs ?? nodeFs;
4135
+ const fs3 = io.fs ?? nodeFs;
4573
4136
  const defaults = defaultConfig(io.home);
4574
- const configPath = resolve2(env.OURS_COWORK_CONFIG ?? join2(io.home ?? homedir(), ".ours-cowork", "config.json"));
4137
+ const configPath = resolve(env.OURS_COWORK_CONFIG ?? join(io.home ?? homedir(), ".ours-cowork", "config.json"));
4575
4138
  let file = defaults;
4576
- const stat = lstatIfPresent(fs2, configPath);
4139
+ const stat = lstatIfPresent(fs3, configPath);
4577
4140
  if (stat) {
4578
- assertSecureFile(fs2, configPath, "config file");
4141
+ assertSecureFile(fs3, configPath, "config file");
4579
4142
  let parsed;
4580
4143
  try {
4581
- parsed = JSON.parse(readSecureFile(fs2, configPath, "config file").toString("utf8"));
4144
+ parsed = JSON.parse(readSecureFile(fs3, configPath, "config file").toString("utf8"));
4582
4145
  } catch (error) {
4583
4146
  throw new CoworkConfigError(`malformed cowork config at ${configPath}`, { cause: error });
4584
4147
  }
@@ -4591,42 +4154,100 @@ function loadConfig(env = process.env, io = {}) {
4591
4154
  throw new CoworkConfigError(`configured cowork config does not exist: ${configPath}`);
4592
4155
  }
4593
4156
  const restPort = env.OURS_COWORK_REST_PORT === void 0 ? void 0 : parsePort(env.OURS_COWORK_REST_PORT);
4157
+ const daemon = mergeDaemonSelection(env, file.daemon);
4594
4158
  try {
4595
4159
  return CoworkConfigSchema.parse({
4596
4160
  version: 1,
4597
4161
  brokerUrl: env.OURS_COWORK_BROKER_URL ?? file.brokerUrl,
4598
- stateDir: resolve2(env.OURS_COWORK_STATE_DIR ?? file.stateDir),
4162
+ stateDir: resolve(env.OURS_COWORK_STATE_DIR ?? file.stateDir),
4599
4163
  rest: {
4600
4164
  enabled: restPort === void 0 ? file.rest.enabled : true,
4601
4165
  port: restPort ?? file.rest.port
4602
- }
4166
+ },
4167
+ ...daemon === void 0 ? {} : { daemon }
4603
4168
  });
4604
4169
  } catch (error) {
4605
4170
  throw new CoworkConfigError("invalid effective cowork config", { cause: error });
4606
4171
  }
4607
4172
  }
4173
+ function mergeDaemonSelection(env, file) {
4174
+ const mode = env.OURS_COWORK_DAEMON_MODE;
4175
+ const endpoint = env.OURS_COWORK_DAEMON_ENDPOINT;
4176
+ const stateDir = env.OURS_COWORK_DAEMON_STATE_DIR;
4177
+ if (mode === void 0 && endpoint === void 0 && stateDir === void 0) return file;
4178
+ if (mode !== void 0 && mode !== "embedded" && mode !== "external") {
4179
+ throw new CoworkConfigError('OURS_COWORK_DAEMON_MODE must be "embedded" or "external"');
4180
+ }
4181
+ const effectiveMode = mode ?? file?.mode ?? "external";
4182
+ if (effectiveMode === "embedded") {
4183
+ if (endpoint !== void 0 || stateDir !== void 0) {
4184
+ throw new CoworkConfigError(
4185
+ 'OURS_COWORK_DAEMON_ENDPOINT and OURS_COWORK_DAEMON_STATE_DIR require daemon mode "external"'
4186
+ );
4187
+ }
4188
+ return { mode: "embedded" };
4189
+ }
4190
+ const effectiveEndpoint = endpoint ?? file?.endpoint;
4191
+ const effectiveStateDir = stateDir ?? file?.stateDir;
4192
+ return {
4193
+ mode: "external",
4194
+ ...effectiveEndpoint === void 0 ? {} : { endpoint: effectiveEndpoint },
4195
+ ...effectiveStateDir === void 0 ? {} : { stateDir: effectiveStateDir }
4196
+ };
4197
+ }
4198
+ function isDaemonOrigin(value) {
4199
+ let url;
4200
+ try {
4201
+ url = new URL(value);
4202
+ } catch {
4203
+ return false;
4204
+ }
4205
+ return (url.protocol === "http:" || url.protocol === "https:") && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && (url.pathname === "" || url.pathname === "/");
4206
+ }
4207
+ function isConfidentialDaemonEndpoint(value) {
4208
+ let url;
4209
+ try {
4210
+ url = new URL(value);
4211
+ } catch {
4212
+ return false;
4213
+ }
4214
+ return url.protocol === "https:" || isLoopbackHost(url.hostname);
4215
+ }
4216
+ function isLoopbackHost(hostname) {
4217
+ const host = hostname.toLowerCase().replace(/^\[(.*)\]$/, "$1");
4218
+ if (host === "localhost") return true;
4219
+ if (host === "::1" || host === "0:0:0:0:0:0:0:1") return true;
4220
+ const octets = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
4221
+ if (!octets) return false;
4222
+ const parts = octets.slice(1).map(Number);
4223
+ return parts.every((part) => part <= 255) && parts[0] === 127;
4224
+ }
4225
+ function normalizeDaemonEndpoint(value) {
4226
+ const url = new URL(value);
4227
+ return `${url.protocol}//${url.host}`;
4228
+ }
4608
4229
  function ensureRuntimeState(config, io = {}) {
4609
4230
  const parsed = CoworkConfigSchema.parse(config);
4610
- const fs2 = io.fs ?? nodeFs;
4611
- const stateDir = resolve2(parsed.stateDir);
4612
- assertSecureAncestors(fs2, stateDir, "state directory");
4613
- const existing = lstatIfPresent(fs2, stateDir);
4231
+ const fs3 = io.fs ?? nodeFs;
4232
+ const stateDir = resolve(parsed.stateDir);
4233
+ assertSecureAncestors(fs3, stateDir, "state directory");
4234
+ const existing = lstatIfPresent(fs3, stateDir);
4614
4235
  if (!existing) {
4615
- createSecureDirectoryTree(fs2, stateDir, "state directory");
4236
+ createSecureDirectoryTree(fs3, stateDir, "state directory");
4616
4237
  }
4617
- assertSecureDirectory(fs2, stateDir, "state directory");
4618
- const roomsPath = join2(stateDir, "rooms");
4619
- const rooms = lstatIfPresent(fs2, roomsPath);
4238
+ assertSecureDirectory(fs3, stateDir, "state directory");
4239
+ const roomsPath = join(stateDir, "rooms");
4240
+ const rooms = lstatIfPresent(fs3, roomsPath);
4620
4241
  if (!rooms) {
4621
- fs2.mkdirSync(roomsPath, { mode: DIRECTORY_MODE });
4622
- secureOpenedDirectory(fs2, roomsPath, "rooms directory");
4623
- fsyncDirectory(fs2, stateDir);
4242
+ fs3.mkdirSync(roomsPath, { mode: DIRECTORY_MODE });
4243
+ secureOpenedDirectory(fs3, roomsPath, "rooms directory");
4244
+ fsyncDirectory(fs3, stateDir);
4624
4245
  }
4625
- assertSecureDirectory(fs2, roomsPath, "rooms directory");
4246
+ assertSecureDirectory(fs3, roomsPath, "rooms directory");
4626
4247
  return {
4627
- socketPath: join2(stateDir, "management.sock"),
4628
- pidPath: join2(stateDir, "daemon.pid"),
4629
- lockPath: join2(stateDir, "daemon.lock")
4248
+ socketPath: join(stateDir, "management.sock"),
4249
+ pidPath: join(stateDir, "daemon.pid"),
4250
+ lockPath: join(stateDir, "daemon.lock")
4630
4251
  };
4631
4252
  }
4632
4253
  function parsePort(value) {
@@ -4637,15 +4258,15 @@ function parsePort(value) {
4637
4258
  if (port > 65535) throw new CoworkConfigError("OURS_COWORK_REST_PORT must be from 1 to 65535");
4638
4259
  return port;
4639
4260
  }
4640
- function assertSecureAncestors(fs2, path, label) {
4641
- const absolute = isAbsolute(path) ? path : resolve2(path);
4261
+ function assertSecureAncestors(fs3, path, label) {
4262
+ const absolute = isAbsolute(path) ? path : resolve(path);
4642
4263
  const root = parse(absolute).root;
4643
- const rootOwner = fs2.lstatSync(root).uid;
4264
+ const rootOwner = fs3.lstatSync(root).uid;
4644
4265
  let cursor = root;
4645
4266
  const components = absolute.slice(root.length).split("/").filter(Boolean);
4646
4267
  for (const [index, component] of components.entries()) {
4647
- cursor = join2(cursor, component);
4648
- const stat = lstatIfPresent(fs2, cursor);
4268
+ cursor = join(cursor, component);
4269
+ const stat = lstatIfPresent(fs3, cursor);
4649
4270
  if (stat?.isSymbolicLink()) throw new CoworkConfigError(`${label} must not traverse a symbolic link (symlink): ${cursor}`);
4650
4271
  if (!stat) break;
4651
4272
  if (!stat.isDirectory()) {
@@ -4664,39 +4285,39 @@ function assertTrustedAncestor(stat, rootOwner, path, label) {
4664
4285
  throw new CoworkConfigError(`${label} has an unsafe writable ancestor: ${path}`);
4665
4286
  }
4666
4287
  }
4667
- function createSecureDirectoryTree(fs2, path, label) {
4288
+ function createSecureDirectoryTree(fs3, path, label) {
4668
4289
  const missing = [];
4669
4290
  let cursor = path;
4670
- while (!lstatIfPresent(fs2, cursor)) {
4291
+ while (!lstatIfPresent(fs3, cursor)) {
4671
4292
  missing.push(cursor);
4672
- const parent = dirname2(cursor);
4293
+ const parent = dirname(cursor);
4673
4294
  if (parent === cursor) throw new CoworkConfigError(`cannot locate an existing ancestor for ${label}`);
4674
4295
  cursor = parent;
4675
4296
  }
4676
- assertSecureAncestors(fs2, path, label);
4297
+ assertSecureAncestors(fs3, path, label);
4677
4298
  for (const directory of missing.reverse()) {
4678
- fs2.mkdirSync(directory, { mode: DIRECTORY_MODE });
4679
- secureOpenedDirectory(fs2, directory, label);
4680
- fsyncDirectory(fs2, dirname2(directory));
4299
+ fs3.mkdirSync(directory, { mode: DIRECTORY_MODE });
4300
+ secureOpenedDirectory(fs3, directory, label);
4301
+ fsyncDirectory(fs3, dirname(directory));
4681
4302
  }
4682
4303
  }
4683
- function secureOpenedDirectory(fs2, path, label) {
4304
+ function secureOpenedDirectory(fs3, path, label) {
4684
4305
  let fd;
4685
4306
  try {
4686
- fd = fs2.openSync(path, nodeFs.constants.O_RDONLY | NO_FOLLOW);
4687
- const opened = fs2.fstatSync(fd);
4688
- const current = fs2.lstatSync(path);
4307
+ fd = fs3.openSync(path, nodeFs.constants.O_RDONLY | NO_FOLLOW);
4308
+ const opened = fs3.fstatSync(fd);
4309
+ const current = fs3.lstatSync(path);
4689
4310
  if (!opened.isDirectory() || current.isSymbolicLink() || opened.dev !== current.dev || opened.ino !== current.ino) {
4690
4311
  throw new CoworkConfigError(`${label} changed while opening`);
4691
4312
  }
4692
- fs2.fchmodSync(fd, DIRECTORY_MODE);
4693
- fs2.fsyncSync(fd);
4313
+ fs3.fchmodSync(fd, DIRECTORY_MODE);
4314
+ fs3.fsyncSync(fd);
4694
4315
  } finally {
4695
- if (fd !== void 0) fs2.closeSync(fd);
4316
+ if (fd !== void 0) fs3.closeSync(fd);
4696
4317
  }
4697
4318
  }
4698
- function assertSecureDirectory(fs2, path, label) {
4699
- const stat = fs2.lstatSync(path);
4319
+ function assertSecureDirectory(fs3, path, label) {
4320
+ const stat = fs3.lstatSync(path);
4700
4321
  if (stat.isSymbolicLink()) throw new CoworkConfigError(`${label} must not be a symbolic link (symlink)`);
4701
4322
  if (!stat.isDirectory()) throw new CoworkConfigError(`${label} must be a directory`);
4702
4323
  if ((stat.mode & 511) !== DIRECTORY_MODE) {
@@ -4704,8 +4325,8 @@ function assertSecureDirectory(fs2, path, label) {
4704
4325
  }
4705
4326
  assertOwner(stat, label);
4706
4327
  }
4707
- function assertSecureFile(fs2, path, label) {
4708
- const stat = fs2.lstatSync(path);
4328
+ function assertSecureFile(fs3, path, label) {
4329
+ const stat = fs3.lstatSync(path);
4709
4330
  if (stat.isSymbolicLink()) throw new CoworkConfigError(`${label} must not be a symbolic link (symlink)`);
4710
4331
  if (!stat.isFile() || stat.nlink !== 1) throw new CoworkConfigError(`${label} must be a single-link regular file`);
4711
4332
  if ((stat.mode & 511) !== FILE_MODE) throw new CoworkConfigError(`${label} mode must be 0600`);
@@ -4716,38 +4337,38 @@ function assertOwner(stat, label) {
4716
4337
  throw new CoworkConfigError(`${label} must be owned by the current user`);
4717
4338
  }
4718
4339
  }
4719
- function readSecureFile(fs2, path, label) {
4340
+ function readSecureFile(fs3, path, label) {
4720
4341
  let fd;
4721
4342
  try {
4722
- fd = fs2.openSync(path, nodeFs.constants.O_RDONLY | NO_FOLLOW);
4723
- const opened = fs2.fstatSync(fd);
4724
- const current = fs2.lstatSync(path);
4343
+ fd = fs3.openSync(path, nodeFs.constants.O_RDONLY | NO_FOLLOW);
4344
+ const opened = fs3.fstatSync(fd);
4345
+ const current = fs3.lstatSync(path);
4725
4346
  if (!opened.isFile() || opened.nlink !== 1 || (opened.mode & 511) !== FILE_MODE || typeof process.getuid === "function" && opened.uid !== process.getuid() || current.isSymbolicLink() || current.dev !== opened.dev || current.ino !== opened.ino) {
4726
4347
  throw new CoworkConfigError(`${label} changed while opening`);
4727
4348
  }
4728
- return fs2.readFileSync(fd);
4349
+ return fs3.readFileSync(fd);
4729
4350
  } finally {
4730
- if (fd !== void 0) fs2.closeSync(fd);
4351
+ if (fd !== void 0) fs3.closeSync(fd);
4731
4352
  }
4732
4353
  }
4733
- function fsyncDirectory(fs2, path) {
4354
+ function fsyncDirectory(fs3, path) {
4734
4355
  let fd;
4735
4356
  try {
4736
- fd = fs2.openSync(path, nodeFs.constants.O_RDONLY | NO_FOLLOW);
4737
- fs2.fsyncSync(fd);
4357
+ fd = fs3.openSync(path, nodeFs.constants.O_RDONLY | NO_FOLLOW);
4358
+ fs3.fsyncSync(fd);
4738
4359
  } finally {
4739
- if (fd !== void 0) fs2.closeSync(fd);
4360
+ if (fd !== void 0) fs3.closeSync(fd);
4740
4361
  }
4741
4362
  }
4742
- function lstatIfPresent(fs2, path) {
4363
+ function lstatIfPresent(fs3, path) {
4743
4364
  try {
4744
- return fs2.lstatSync(path);
4365
+ return fs3.lstatSync(path);
4745
4366
  } catch (error) {
4746
4367
  if (error.code === "ENOENT") return void 0;
4747
4368
  throw error;
4748
4369
  }
4749
4370
  }
4750
- var DIRECTORY_MODE, FILE_MODE, NO_FOLLOW, CoworkConfigSchema, CoworkConfigError;
4371
+ var DIRECTORY_MODE, FILE_MODE, NO_FOLLOW, CoworkDaemonSchema, CoworkConfigSchema, CoworkConfigError;
4751
4372
  var init_config = __esm({
4752
4373
  "src/config.ts"() {
4753
4374
  "use strict";
@@ -4755,6 +4376,33 @@ var init_config = __esm({
4755
4376
  DIRECTORY_MODE = 448;
4756
4377
  FILE_MODE = 384;
4757
4378
  NO_FOLLOW = nodeFs.constants.O_NOFOLLOW ?? 0;
4379
+ CoworkDaemonSchema = external_exports.object({
4380
+ mode: external_exports.enum(["embedded", "external"]),
4381
+ endpoint: external_exports.string().url().refine(isDaemonOrigin, "daemon.endpoint must be an http(s) origin with no credentials, path, query, or fragment").refine(isConfidentialDaemonEndpoint, "daemon.endpoint must use https:// unless the daemon is on this host (http:// is allowed only for localhost, 127.0.0.0/8, or [::1])").transform(normalizeDaemonEndpoint).optional(),
4382
+ stateDir: external_exports.string().min(1).transform((value) => resolve(value)).optional()
4383
+ }).strict().superRefine((value, ctx) => {
4384
+ if (value.mode === "external") {
4385
+ for (const field of ["endpoint", "stateDir"]) {
4386
+ if (value[field] === void 0) {
4387
+ ctx.addIssue({
4388
+ code: external_exports.ZodIssueCode.custom,
4389
+ path: [field],
4390
+ message: `daemon.${field} is required when daemon.mode is "external"`
4391
+ });
4392
+ }
4393
+ }
4394
+ return;
4395
+ }
4396
+ for (const field of ["endpoint", "stateDir"]) {
4397
+ if (value[field] !== void 0) {
4398
+ ctx.addIssue({
4399
+ code: external_exports.ZodIssueCode.custom,
4400
+ path: [field],
4401
+ message: `daemon.${field} is only valid when daemon.mode is "external"`
4402
+ });
4403
+ }
4404
+ }
4405
+ });
4758
4406
  CoworkConfigSchema = external_exports.object({
4759
4407
  version: external_exports.literal(1),
4760
4408
  brokerUrl: external_exports.string().url().refine((value) => {
@@ -4765,7 +4413,10 @@ var init_config = __esm({
4765
4413
  rest: external_exports.object({
4766
4414
  enabled: external_exports.boolean(),
4767
4415
  port: external_exports.number().int().min(1).max(65535)
4768
- }).strict()
4416
+ }).strict(),
4417
+ // Absent unless an operator selected a daemon explicitly, so an untouched
4418
+ // deployment keeps producing byte-identical effective configuration.
4419
+ daemon: CoworkDaemonSchema.optional()
4769
4420
  }).strict();
4770
4421
  CoworkConfigError = class extends Error {
4771
4422
  constructor(message, options) {
@@ -4776,6 +4427,271 @@ var init_config = __esm({
4776
4427
  }
4777
4428
  });
4778
4429
 
4430
+ // src/ours-runtime.ts
4431
+ import { randomBytes } from "node:crypto";
4432
+ import * as fs from "node:fs";
4433
+ import { join as join2, resolve as resolve2 } from "node:path";
4434
+ function createOursHost(config, log = () => {
4435
+ }) {
4436
+ return daemonMode(config) === "external" ? new ExternalOursHost(config, log) : new EmbeddedOursHost(config, log);
4437
+ }
4438
+ function sleep(ms, signal) {
4439
+ return new Promise((resolveSleep) => {
4440
+ const timer = setTimeout(finish, ms);
4441
+ signal.addEventListener("abort", finish, { once: true });
4442
+ function finish() {
4443
+ clearTimeout(timer);
4444
+ signal.removeEventListener("abort", finish);
4445
+ resolveSleep();
4446
+ }
4447
+ });
4448
+ }
4449
+ function sdkRuntimeStateDir(config) {
4450
+ return resolve2(config.stateDir, "ours-sdk");
4451
+ }
4452
+ function configureOwnedSdkEnvironment(config) {
4453
+ const stateDir = sdkRuntimeStateDir(config);
4454
+ fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
4455
+ fs.chmodSync(stateDir, 448);
4456
+ process.env.OURS_CONFIG = join2(stateDir, "config.json");
4457
+ process.env.OURS_STATE_DIR = stateDir;
4458
+ process.env.OURS_BROKER_URL = config.brokerUrl;
4459
+ process.env.OURS_PORT = "0";
4460
+ process.env.OURS_API_VISIBILITY = "owner";
4461
+ process.env.OURS_TRANSPORT = "http";
4462
+ process.env.OURS_AUTOSTART = "false";
4463
+ process.env.OURS_GC_INTERVAL_MS = "3600000";
4464
+ delete process.env.OURS_API_TOKEN;
4465
+ return stateDir;
4466
+ }
4467
+ var WATCH_RETRY_MIN_MS, WATCH_RETRY_MAX_MS, EmbeddedOursHost, ExternalOursHost;
4468
+ var init_ours_runtime = __esm({
4469
+ "src/ours-runtime.ts"() {
4470
+ "use strict";
4471
+ init_config();
4472
+ WATCH_RETRY_MIN_MS = 500;
4473
+ WATCH_RETRY_MAX_MS = 3e4;
4474
+ EmbeddedOursHost = class {
4475
+ config;
4476
+ log;
4477
+ listeners = /* @__PURE__ */ new Set();
4478
+ handle;
4479
+ Client;
4480
+ apiToken;
4481
+ closed = false;
4482
+ constructor(config, log = () => {
4483
+ }) {
4484
+ this.config = config;
4485
+ this.log = log;
4486
+ }
4487
+ async boot() {
4488
+ if (this.handle) return;
4489
+ if (this.closed) throw new Error("embedded ours SDK runtime cannot restart in the same process");
4490
+ const runtimeDir = configureOwnedSdkEnvironment(this.config);
4491
+ let handle;
4492
+ try {
4493
+ const [{ OursClient }, { startDaemon }] = await Promise.all([
4494
+ import("@ours.network/sdk"),
4495
+ import("@ours.network/sdk/daemon")
4496
+ ]);
4497
+ handle = await startDaemon({
4498
+ version: "@ours.network/cowork@vNext",
4499
+ handleSignals: false,
4500
+ onIdentityNotify: (identityName) => {
4501
+ for (const listener of this.listeners) {
4502
+ try {
4503
+ listener(identityName);
4504
+ } catch (error) {
4505
+ this.log(`cowork SDK notification listener failed for ${identityName}:`, error);
4506
+ }
4507
+ }
4508
+ }
4509
+ });
4510
+ const tokenPath = join2(runtimeDir, "daemon-token");
4511
+ const apiToken = fs.readFileSync(tokenPath, "utf8").trim();
4512
+ if (!apiToken) throw new Error("embedded ours SDK runtime created an empty owner token");
4513
+ this.Client = OursClient;
4514
+ this.apiToken = apiToken;
4515
+ this.handle = handle;
4516
+ } catch (error) {
4517
+ await handle?.close();
4518
+ throw new Error("failed to start the embedded ours SDK runtime", { cause: error });
4519
+ }
4520
+ }
4521
+ createClient(leaseToken = `cowork-${randomBytes(16).toString("hex")}`) {
4522
+ if (!this.handle || !this.Client || !this.apiToken) {
4523
+ throw new Error("embedded ours SDK runtime is not booted");
4524
+ }
4525
+ return new this.Client({
4526
+ url: `http://127.0.0.1:${this.handle.port}`,
4527
+ leaseToken,
4528
+ apiToken: this.apiToken
4529
+ });
4530
+ }
4531
+ onIdentityNotify(listener) {
4532
+ this.listeners.add(listener);
4533
+ return () => this.listeners.delete(listener);
4534
+ }
4535
+ close() {
4536
+ }
4537
+ async shutdown() {
4538
+ if (this.closed) return { requiresProcessExit: true };
4539
+ this.closed = true;
4540
+ const handle = this.handle;
4541
+ this.handle = void 0;
4542
+ this.Client = void 0;
4543
+ this.apiToken = void 0;
4544
+ this.listeners.clear();
4545
+ await handle?.close();
4546
+ return { requiresProcessExit: true };
4547
+ }
4548
+ };
4549
+ ExternalOursHost = class {
4550
+ endpoint;
4551
+ daemonStateDir;
4552
+ log;
4553
+ listeners = /* @__PURE__ */ new Set();
4554
+ watchers = /* @__PURE__ */ new Map();
4555
+ watchLeaseToken = `cowork-watch-${randomBytes(16).toString("hex")}`;
4556
+ Client;
4557
+ resolved;
4558
+ watchClient;
4559
+ closed = false;
4560
+ constructor(config, log = () => {
4561
+ }) {
4562
+ const daemon = config.daemon;
4563
+ if (daemon?.mode !== "external" || daemon.endpoint === void 0 || daemon.stateDir === void 0) {
4564
+ throw new Error("external ours daemon mode requires both daemon.endpoint and daemon.stateDir");
4565
+ }
4566
+ this.endpoint = daemon.endpoint;
4567
+ this.daemonStateDir = daemon.stateDir;
4568
+ this.log = log;
4569
+ }
4570
+ async boot() {
4571
+ if (this.resolved) return;
4572
+ if (this.closed) throw new Error("external ours daemon host cannot restart in the same process");
4573
+ const { OursClient, resolveDaemonConfig, assertDaemonStateDir } = await import("@ours.network/sdk");
4574
+ let resolved;
4575
+ try {
4576
+ resolved = resolveDaemonConfig({ endpoint: this.endpoint, expectStateDir: this.daemonStateDir });
4577
+ } catch (error) {
4578
+ throw new Error(`cowork cannot select the external ours daemon at ${this.endpoint}`, { cause: error });
4579
+ }
4580
+ try {
4581
+ await assertDaemonStateDir(resolved);
4582
+ } catch (error) {
4583
+ throw new Error(
4584
+ `the external ours daemon at ${this.endpoint} is unavailable or does not own ${this.daemonStateDir}`,
4585
+ { cause: error }
4586
+ );
4587
+ }
4588
+ this.Client = OursClient;
4589
+ this.resolved = resolved;
4590
+ this.watchClient = this.createClient(this.watchLeaseToken);
4591
+ }
4592
+ createClient(leaseToken = `cowork-${randomBytes(16).toString("hex")}`) {
4593
+ if (!this.Client || !this.resolved) throw new Error("external ours daemon host is not booted");
4594
+ return new this.Client({
4595
+ url: this.resolved.baseUrl.value,
4596
+ leaseToken,
4597
+ ...this.resolved.token === void 0 ? {} : { apiToken: this.resolved.token.value }
4598
+ });
4599
+ }
4600
+ onIdentityNotify(listener) {
4601
+ this.listeners.add(listener);
4602
+ return () => this.listeners.delete(listener);
4603
+ }
4604
+ trackIdentity(identityName) {
4605
+ if (!this.watchClient) throw new Error("external ours daemon host is not booted");
4606
+ const existing = this.watchers.get(identityName);
4607
+ if (existing) return () => this.untrack(identityName, existing);
4608
+ const controller = new AbortController();
4609
+ const watcher = { controller, work: Promise.resolve() };
4610
+ watcher.work = this.follow(identityName, controller.signal);
4611
+ this.watchers.set(identityName, watcher);
4612
+ return () => this.untrack(identityName, watcher);
4613
+ }
4614
+ close() {
4615
+ }
4616
+ async shutdown() {
4617
+ if (this.closed) return { requiresProcessExit: false };
4618
+ this.closed = true;
4619
+ this.listeners.clear();
4620
+ const watchers = [...this.watchers.values()];
4621
+ this.watchers.clear();
4622
+ for (const watcher of watchers) watcher.controller.abort();
4623
+ await Promise.allSettled(watchers.map((watcher) => watcher.work));
4624
+ const client = this.watchClient;
4625
+ this.watchClient = void 0;
4626
+ this.Client = void 0;
4627
+ this.resolved = void 0;
4628
+ if (client) await client.releaseLease().catch(() => {
4629
+ });
4630
+ return { requiresProcessExit: false };
4631
+ }
4632
+ untrack(identityName, watcher) {
4633
+ if (this.watchers.get(identityName) !== watcher) return;
4634
+ this.watchers.delete(identityName);
4635
+ watcher.controller.abort();
4636
+ }
4637
+ /**
4638
+ * Long-poll one identity forever, reconnecting with bounded backoff.
4639
+ *
4640
+ * A watch primes at the daemon's tip, so a reconnected watch never replays
4641
+ * what arrived while it was down. Losing those arrivals would be silent, so
4642
+ * every reconnect announces the identity once. The consumer's reaction to an
4643
+ * announcement is a full state refresh, which is exactly the recovery this
4644
+ * needs; it is idempotent and self-coalescing, so the extra announcement can
4645
+ * only cost one refresh.
4646
+ *
4647
+ * ORDER IS LOAD-BEARING. The resync is issued only AFTER the replacement
4648
+ * watch's request is already in flight, because that request is what fixes
4649
+ * the new tip. Announcing first would read the inbox at T0 and fix the tip at
4650
+ * T1 > T0, and anything arriving in between would be in neither — the very
4651
+ * gap this exists to close. This way every arrival lands in the refresh, on
4652
+ * the stream, or (harmlessly) in both.
4653
+ */
4654
+ async follow(identityName, signal) {
4655
+ let backoffMs = WATCH_RETRY_MIN_MS;
4656
+ let resyncPending = false;
4657
+ while (!signal.aborted) {
4658
+ const client = this.watchClient;
4659
+ if (!client) return;
4660
+ try {
4661
+ const stream = client.watchNotifications(identityName, { signal });
4662
+ let step = stream.next();
4663
+ if (resyncPending) {
4664
+ resyncPending = false;
4665
+ this.announce(identityName);
4666
+ }
4667
+ for (let settled = await step; !settled.done; settled = await step) {
4668
+ backoffMs = WATCH_RETRY_MIN_MS;
4669
+ this.announce(identityName);
4670
+ step = stream.next();
4671
+ }
4672
+ } catch (error) {
4673
+ if (signal.aborted) return;
4674
+ resyncPending = true;
4675
+ this.log(`[${identityName}] external ours daemon notification watch failed:`, error);
4676
+ }
4677
+ if (signal.aborted) return;
4678
+ await sleep(backoffMs, signal);
4679
+ backoffMs = Math.min(backoffMs * 2, WATCH_RETRY_MAX_MS);
4680
+ }
4681
+ }
4682
+ announce(identityName) {
4683
+ for (const listener of this.listeners) {
4684
+ try {
4685
+ listener(identityName);
4686
+ } catch (error) {
4687
+ this.log(`cowork SDK notification listener failed for ${identityName}:`, error);
4688
+ }
4689
+ }
4690
+ }
4691
+ };
4692
+ }
4693
+ });
4694
+
4779
4695
  // src/contracts.ts
4780
4696
  import { createHash } from "node:crypto";
4781
4697
  function utf8Bounded(label, maximumBytes) {
@@ -4805,15 +4721,15 @@ function isStrictRfc3339(value) {
4805
4721
  function normalizeRoomName(value) {
4806
4722
  return value.trim().normalize("NFC");
4807
4723
  }
4808
- function roomIdentityName(roomName) {
4809
- return `${ROOM_IDENTITY_PREFIX}${RoomNameSchema.parse(roomName)}`;
4724
+ function roomIdentityName(roomId) {
4725
+ return `${ROOM_IDENTITY_PREFIX}${LowerCrockfordUlidSchema.parse(roomId)}`;
4810
4726
  }
4811
4727
  function legacyRoomIdentityName(roomId) {
4812
4728
  return `cowork-room-${LowerCrockfordUlidSchema.parse(roomId)}`;
4813
4729
  }
4814
4730
  function refineRoomLineage(room, context) {
4815
4731
  const pendingIdentityName = legacyRoomIdentityName(room.room_id);
4816
- const currentPendingIdentityName = room.room_name === void 0 ? void 0 : roomIdentityName(room.room_name);
4732
+ const currentPendingIdentityName = roomIdentityName(room.room_id);
4817
4733
  const exactPacketPending = room.state === "provisioning" && room.status === "packet_pending" && (room.identity_name === pendingIdentityName || room.identity_name === currentPendingIdentityName) && room.invites.length === 0 && room.seats.length === 0 && room.activated_at === void 0 && room.closed_at === void 0;
4818
4734
  if (room.identity_cid === "" && !exactPacketPending) {
4819
4735
  context.addIssue({
@@ -4940,7 +4856,7 @@ function refineMessageCategory(message, context) {
4940
4856
  }
4941
4857
  }
4942
4858
  }
4943
- var MAX_TEXT_BYTES, MAX_FILE_BYTES, MAX_HISTORY_PAGE_BYTES, MAX_MANAGEMENT_RESPONSE_BYTES, MAX_EXTERNAL_INVITE_BYTES, MAX_FILE_NAME_BYTES, MAX_MIME_BYTES, MAX_ROLE_BYTES, MAX_ROOM_NAME_CHARACTERS, NonEmptyStringSchema, PositiveSafeIntegerSchema, LowerCrockfordUlidSchema, Rfc3339Schema, RoomNameSchema, ROOM_IDENTITY_PREFIX, RoleSchema, MissionTextSchema, MessageTextSchema, FileNameSchema, FileMimeSchema, RoomStateSchema, SeatStateSchema, InviteModeSchema, DEFAULT_ROLE, InviteStateSchema, RelayStatusSchema, SeatV1Schema, SeatSchema, RoomInviteSchema, MissionV1Schema, MissionSchema, RoleBriefingSchema, RoomCommonShape, RoomV1Schema, CurrentRoomSchema, RoomSchema, CreateRoomInputSchema, UpdateRoomInputSchema, RoleBriefingSetInputSchema, RoleBriefingDeleteInputSchema, PostMessageInputSchema, ContainerIdSchema, AcceptExternalInviteInputSchema, AuthorSnapshotSchema, RecordCommonShape, AppendCommonShape, MembershipNoticeSchema, AuthorAliasSchema, MessageShape, RelayIntentShape, RelayResultStatusSchema, RelayResultShape, FileShape, MembershipIntentShape, MembershipResultShape, CloseNoticeIntentShape, CloseNoticeResultShape, MessageRecordSchema, FileRecordSchema, RelayIntentRecordSchema, RelayResultRecordSchema, MembershipIntentRecordSchema, MembershipResultRecordSchema, CloseNoticeIntentRecordSchema, CloseNoticeResultRecordSchema, RawCommunicationRecordSchema, CommunicationRecordSchema, AppendRecordSchema;
4859
+ var MAX_TEXT_BYTES, MAX_FILE_BYTES, MAX_HISTORY_PAGE_BYTES, MAX_MANAGEMENT_RESPONSE_BYTES, MAX_EXTERNAL_INVITE_BYTES, MAX_FILE_NAME_BYTES, MAX_MIME_BYTES, MAX_ROLE_BYTES, MAX_ROOM_NAME_CHARACTERS, NonEmptyStringSchema, PositiveSafeIntegerSchema, LowerCrockfordUlidSchema, Rfc3339Schema, RoomNameSchema, ROOM_IDENTITY_PREFIX, RoleSchema, MissionTextSchema, MessageTextSchema, FileNameSchema, FileMimeSchema, RoomStateSchema, SeatStateSchema, InviteModeSchema, DEFAULT_ROLE, InviteStateSchema, RelayStatusSchema, SeatV1Schema, SeatSchema, RoomInviteSchema, MissionV1Schema, MissionSchema, RoleBriefingSchema, RoomCommonShape, RoomV1Schema, CurrentRoomSchema, RoomSchema, CreateRoomInputSchema, UpdateRoomInputSchema, RoleBriefingSetInputSchema, RoleBriefingDeleteInputSchema, PostMessageInputSchema, ContainerIdSchema, AcceptExternalInviteInputSchema, AuthorSnapshotSchema, RecordCommonShape, AppendCommonShape, MembershipNoticeSchema, AuthorAliasSchema, ReplyReferenceSchema, MessageShape, RelayIntentShape, RelayResultStatusSchema, RelayResultShape, FileShape, MembershipIntentShape, MembershipResultShape, CloseNoticeIntentShape, CloseNoticeResultShape, MessageRecordSchema, FileRecordSchema, RelayIntentRecordSchema, RelayResultRecordSchema, MembershipIntentRecordSchema, MembershipResultRecordSchema, CloseNoticeIntentRecordSchema, CloseNoticeResultRecordSchema, RawCommunicationRecordSchema, CommunicationRecordSchema, AppendRecordSchema;
4944
4860
  var init_contracts = __esm({
4945
4861
  "src/contracts.ts"() {
4946
4862
  "use strict";
@@ -4973,7 +4889,7 @@ var init_contracts = __esm({
4973
4889
  });
4974
4890
  }
4975
4891
  });
4976
- ROOM_IDENTITY_PREFIX = "ours-cowork-room:";
4892
+ ROOM_IDENTITY_PREFIX = "ours-cowork-";
4977
4893
  RoleSchema = utf8Bounded("role", MAX_ROLE_BYTES);
4978
4894
  MissionTextSchema = utf8Bounded("mission text", MAX_TEXT_BYTES);
4979
4895
  MessageTextSchema = utf8Bounded("message text", MAX_TEXT_BYTES);
@@ -5325,6 +5241,10 @@ var init_contracts = __esm({
5325
5241
  participant_id: LowerCrockfordUlidSchema,
5326
5242
  alias: NonEmptyStringSchema
5327
5243
  }).strict();
5244
+ ReplyReferenceSchema = external_exports.object({
5245
+ wire_id: NonEmptyStringSchema,
5246
+ sentence: PositiveSafeIntegerSchema.optional()
5247
+ }).strict();
5328
5248
  MessageShape = {
5329
5249
  kind: external_exports.literal("message"),
5330
5250
  message_id: LowerCrockfordUlidSchema,
@@ -5349,7 +5269,8 @@ var init_contracts = __esm({
5349
5269
  }
5350
5270
  }),
5351
5271
  source_msg_id: external_exports.number().int().nonnegative().safe().optional(),
5352
- source_wire_id: NonEmptyStringSchema.optional()
5272
+ source_wire_id: NonEmptyStringSchema.optional(),
5273
+ source_reply_to: ReplyReferenceSchema.optional()
5353
5274
  };
5354
5275
  RelayIntentShape = {
5355
5276
  kind: external_exports.literal("relay_intent"),
@@ -5392,7 +5313,8 @@ var init_contracts = __esm({
5392
5313
  }
5393
5314
  }),
5394
5315
  source_file_id: external_exports.number().int().nonnegative().safe(),
5395
- source_wire_id: NonEmptyStringSchema.optional()
5316
+ source_wire_id: NonEmptyStringSchema.optional(),
5317
+ source_reply_to: ReplyReferenceSchema.optional()
5396
5318
  };
5397
5319
  MembershipIntentShape = {
5398
5320
  kind: external_exports.literal("membership_intent"),
@@ -5474,217 +5396,93 @@ var init_contracts = __esm({
5474
5396
  });
5475
5397
 
5476
5398
  // src/packets.ts
5477
- import { randomBytes } from "node:crypto";
5478
- import * as nodeFs2 from "node:fs";
5479
- import { dirname as dirname3, join as join3 } from "node:path";
5480
- function atomicWriteFileSync(target, bytes, ops = nodeFs2) {
5481
- const temp = `${target}.tmp-${process.pid}-${temporarySequence++}`;
5482
- let fileFd;
5483
- let directoryFd;
5484
- try {
5485
- fileFd = ops.openSync(temp, nodeFs2.constants.O_CREAT | nodeFs2.constants.O_TRUNC | nodeFs2.constants.O_WRONLY, 384);
5486
- ops.fchmodSync(fileFd, 384);
5487
- let offset = 0;
5488
- while (offset < bytes.byteLength) {
5489
- const written = ops.writeSync(fileFd, bytes, offset, bytes.byteLength - offset, null);
5490
- if (written <= 0) throw new Error(`short write while persisting ${target}`);
5491
- offset += written;
5492
- }
5493
- ops.fsyncSync(fileFd);
5494
- ops.closeSync(fileFd);
5495
- fileFd = void 0;
5496
- ops.renameSync(temp, target);
5497
- ops.chmodSync(target, 384);
5498
- directoryFd = ops.openSync(dirname3(target), nodeFs2.constants.O_RDONLY);
5499
- ops.fsyncSync(directoryFd);
5500
- ops.closeSync(directoryFd);
5501
- directoryFd = void 0;
5502
- } catch (error) {
5503
- if (fileFd !== void 0) {
5504
- try {
5505
- ops.closeSync(fileFd);
5506
- } catch {
5507
- }
5508
- }
5509
- if (directoryFd !== void 0) {
5510
- try {
5511
- ops.closeSync(directoryFd);
5512
- } catch {
5513
- }
5514
- }
5515
- try {
5516
- ops.rmSync(temp, { force: true });
5517
- } catch {
5518
- }
5519
- throw new PacketPersistenceError(`failed to durably persist ${target}`, { cause: error });
5520
- }
5521
- }
5522
- function renderInbox(value) {
5523
- const output = [];
5524
- if (value.IsNil()) return output;
5525
- for (let index = 0; ; index += 1) {
5526
- const message = value.Reduce(index);
5527
- if (message.IsNil()) break;
5528
- output.push({
5529
- msg_id: Number(message.Reduce("msg_id").Visualize()),
5530
- sender_id: message.Reduce("sender_id").Visualize(),
5531
- sender_name: message.Reduce("sender_name").Visualize(),
5532
- text: message.Reduce("text").Visualize(),
5533
- date: adaptTimeToRfc3339(message.Reduce("date").Visualize()),
5534
- status: message.Reduce("status").Visualize(),
5535
- wire_id: message.Reduce("wire_id").Visualize()
5536
- });
5537
- }
5538
- return output;
5539
- }
5540
- function renderFileInbox(value) {
5541
- const output = [];
5542
- if (value.IsNil()) return output;
5543
- for (let index = 0; ; index += 1) {
5544
- const file = value.Reduce(index);
5545
- if (file.IsNil()) break;
5546
- output.push({
5547
- file_id: Number(file.Reduce("file_id").Visualize()),
5548
- sender_id: file.Reduce("sender_id").Visualize(),
5549
- sender_name: file.Reduce("sender_name").Visualize(),
5550
- filename: file.Reduce("filename").Visualize(),
5551
- mime: file.Reduce("mime").Visualize(),
5552
- data: Buffer.from(file.Reduce("data").GetBinary()),
5553
- date: adaptTimeToRfc3339(file.Reduce("date").Visualize()),
5554
- status: file.Reduce("status").Visualize(),
5555
- wire_id: file.Reduce("wire_id").Visualize()
5556
- });
5557
- }
5558
- return output;
5559
- }
5560
- function renderIntegerArray(value) {
5561
- const output = [];
5562
- if (value.IsNil()) return output;
5563
- for (let index = 0; ; index += 1) {
5564
- const item = value.Reduce(index);
5565
- if (item.IsNil()) break;
5566
- output.push(Number(item.Visualize()));
5567
- }
5568
- return output;
5399
+ import { createHash as createHash2 } from "node:crypto";
5400
+ import * as fs2 from "node:fs";
5401
+ import { join as join3 } from "node:path";
5402
+ import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants } from "node:zlib";
5403
+ function sendResult(result) {
5404
+ if (result.kind === "refused" || result.kind === "migrating") return { status: "send_failed" };
5405
+ if (result.kind === "introduced") return { status: "queued" };
5406
+ return { status: "queued", wire_id: result.wireId };
5569
5407
  }
5570
- function dictionaryEntries(value) {
5571
- if (value.IsNil()) return [];
5572
- return value.GetKeys().map((key) => [key.Visualize(), value.Reduce(key)]);
5408
+ async function fileItem(file, bytes) {
5409
+ return {
5410
+ file_id: file.file_id,
5411
+ sender_id: file.from.id,
5412
+ sender_name: file.from.name,
5413
+ filename: file.filename,
5414
+ mime: file.mime,
5415
+ data: Buffer.from(bytes),
5416
+ date: normalizeDate(file.date),
5417
+ wire_id: file.wire_id,
5418
+ reply_to: cloneReply(file.reply_to)
5419
+ };
5573
5420
  }
5574
- function booleanValue(value) {
5575
- if (value.IsNil()) return false;
5576
- try {
5577
- return value.GetBoolean();
5578
- } catch {
5579
- return /true/i.test(value.Visualize());
5580
- }
5421
+ function cloneReply(reply) {
5422
+ return reply === null ? null : {
5423
+ wire_id: reply.wire_id,
5424
+ ...reply.sentence === void 0 ? {} : { sentence: reply.sentence }
5425
+ };
5581
5426
  }
5582
- function strictBooleanValue(value, label) {
5583
- if (value.IsNil()) throw new Error(`${label} must be a boolean`);
5584
- let decoded;
5585
- try {
5586
- decoded = value.GetBoolean();
5587
- } catch (error) {
5588
- throw new Error(`${label} must be a boolean`, { cause: error });
5589
- }
5590
- if (typeof decoded !== "boolean") throw new Error(`${label} must be a boolean`);
5591
- return decoded;
5427
+ function normalizeDate(value) {
5428
+ const date = new Date(value);
5429
+ if (Number.isNaN(date.valueOf())) throw new Error(`SDK returned an invalid message timestamp: ${value}`);
5430
+ return date.toISOString();
5592
5431
  }
5593
- function nilString(value) {
5594
- return value.IsNil() ? "" : value.Visualize();
5432
+ function hasOursCode(error, code) {
5433
+ return error instanceof Error && "code" in error && error.code === code;
5595
5434
  }
5596
- function adaptTimeToRfc3339(value) {
5597
- const rfc3339 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(?:Z|([+-])(\d{2}):(\d{2}))$/.exec(value);
5598
- if (rfc3339) {
5599
- const [, year2, month2, day2, hour2, minute2, second2, fraction2 = "", sign2, offsetHour2 = "0", offsetMinute = "0"] = rfc3339;
5600
- if (sign2 === "-" && offsetHour2 === "00" && offsetMinute === "00") return invalidAdaptTime(value);
5601
- return canonicalUtcTime(value, year2, month2, day2, hour2, minute2, second2, fraction2, sign2, offsetHour2, offsetMinute);
5602
- }
5603
- const native = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))? \(UTC(?:([+-])(0|[1-9]|1\d|2[0-3]))?\)$/.exec(value);
5604
- if (!native) return invalidAdaptTime(value);
5605
- const [, year, month, day, hour, minute, second, fraction = "", sign, offsetHour = "0"] = native;
5606
- if (sign === "-" && offsetHour === "0") return invalidAdaptTime(value);
5607
- return canonicalUtcTime(value, year, month, day, hour, minute, second, fraction, sign, offsetHour, "0");
5435
+ function validateRoomId(roomId) {
5436
+ if (!/^[0-7][0-9a-hjkmnp-tv-z]{25}$/.test(roomId)) throw new Error(`invalid room id "${roomId}"`);
5608
5437
  }
5609
- function canonicalUtcTime(source, yearText, monthText, dayText, hourText, minuteText, secondText, fraction, offsetSign, offsetHourText, offsetMinuteText) {
5610
- const year = Number(yearText);
5611
- const month = Number(monthText);
5612
- const day = Number(dayText);
5613
- const hour = Number(hourText);
5614
- const minute = Number(minuteText);
5615
- const second = Number(secondText);
5616
- const offsetHour = Number(offsetHourText);
5617
- const offsetMinute = Number(offsetMinuteText);
5618
- const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
5619
- const monthDays = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
5620
- if (month < 1 || month > 12 || day < 1 || day > monthDays[month - 1] || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59) return invalidAdaptTime(source);
5621
- const milliseconds = Number(fraction.slice(0, 3).padEnd(3, "0"));
5622
- const local = /* @__PURE__ */ new Date(0);
5623
- local.setUTCFullYear(year, month - 1, day);
5624
- local.setUTCHours(hour, minute, second, milliseconds);
5625
- if (local.getUTCFullYear() !== year || local.getUTCMonth() !== month - 1 || local.getUTCDate() !== day || local.getUTCHours() !== hour || local.getUTCMinutes() !== minute || local.getUTCSeconds() !== second) {
5626
- return invalidAdaptTime(source);
5438
+ function unpackInvite(encoded, maximumBytes) {
5439
+ const normalized = encoded.replace(/\s+/g, "");
5440
+ if (maximumBytes !== void 0 && Buffer.byteLength(encoded, "utf8") > maximumBytes || normalized.length === 0 || !/^[A-Za-z0-9_-]+$/.test(normalized)) {
5441
+ throw new Error("the invite blob is empty, oversized, or invalid base64url");
5627
5442
  }
5628
- const direction = offsetSign === "-" ? -1 : 1;
5629
- const offsetMilliseconds = direction * (offsetHour * 60 + offsetMinute) * 6e4;
5630
- const canonical = new Date(local.getTime() - offsetMilliseconds).toISOString();
5631
- if (!/^\d{4}-/.test(canonical)) return invalidAdaptTime(source);
5632
- return canonical;
5633
- }
5634
- function invalidAdaptTime(value) {
5635
- throw new Error(`unexpected ADAPT time visualization: ${value}`);
5636
- }
5637
- function inviteMode(value) {
5638
- const normalized = value.replace(/^\$/, "");
5639
- if (normalized === "one_time" || normalized === "public") return normalized;
5640
- throw new Error(`unexpected invite mode: ${value}`);
5641
- }
5642
- function exportSigningSecret(packet) {
5643
- return withScope((lifetime) => Buffer.from(packet.readonlyTx("::actor::export_signing_secret", lifetime).Serialize()).toString("hex"));
5644
- }
5645
- function validateRoomId(roomId) {
5646
- if (!/^[A-Za-z0-9_-]{1,128}$/.test(roomId)) throw new Error(`invalid room id: ${roomId}`);
5443
+ const compressed = Buffer.from(normalized, "base64url");
5444
+ if (compressed.length === 0) throw new Error("the invite blob is empty or invalid base64url");
5445
+ return Buffer.from(maximumBytes === void 0 ? brotliDecompressSync(compressed) : brotliDecompressSync(compressed, { maxOutputLength: maximumBytes }));
5647
5446
  }
5648
- var PacketPersistenceError, temporarySequence, PacketRegistry, HostedRoomPacket;
5447
+ var LegacyCoworkStateError, PacketRegistry, SdkRoomPacket;
5649
5448
  var init_packets = __esm({
5650
- async "src/packets.ts"() {
5449
+ "src/packets.ts"() {
5651
5450
  "use strict";
5652
- await init_adapt();
5653
5451
  init_contracts();
5654
- PacketPersistenceError = class extends Error {
5655
- constructor(message, options) {
5656
- super(message, options);
5657
- this.name = "PacketPersistenceError";
5452
+ LegacyCoworkStateError = class extends Error {
5453
+ constructor(roomId) {
5454
+ super(
5455
+ `room "${roomId}" uses the pre-1.0 custom packet format and cannot be opened by the standard ours SDK runtime; back it up with the old release, recreate the room for cowork 1.0, and re-invite its participants`
5456
+ );
5457
+ this.name = "LegacyCoworkStateError";
5658
5458
  }
5659
5459
  };
5660
- temporarySequence = 0;
5661
5460
  PacketRegistry = class {
5662
5461
  packets = /* @__PURE__ */ new Map();
5462
+ trackers = /* @__PURE__ */ new Map();
5663
5463
  host;
5664
5464
  stateDir;
5665
5465
  fs;
5666
- persistence;
5667
5466
  log;
5668
- seed;
5669
- beforeExpose;
5670
5467
  onNotify;
5671
- provisioningCheckpoint;
5672
- stagingName;
5468
+ unsubscribe;
5673
5469
  constructor(host, stateDir, options = {}) {
5674
5470
  this.host = host;
5675
5471
  this.stateDir = stateDir;
5676
- this.fs = options.fs ?? nodeFs2;
5677
- this.persistence = options.persistence ?? this.fs;
5472
+ this.fs = options.fs ?? fs2;
5678
5473
  this.log = options.log ?? (() => {
5679
5474
  });
5680
- this.seed = options.seed ?? (() => randomBytes(24).toString("hex"));
5681
- this.beforeExpose = options.beforeExpose ?? (() => {
5682
- });
5683
5475
  this.onNotify = options.onNotify ?? (() => {
5684
5476
  });
5685
- this.provisioningCheckpoint = options.provisioningCheckpoint ?? (() => {
5477
+ this.unsubscribe = host.onIdentityNotify((name) => {
5478
+ const found = [...this.packets.entries()].find(([, packet2]) => packet2.name === name);
5479
+ if (!found) return;
5480
+ const [roomId, packet] = found;
5481
+ void packet.refresh().then(
5482
+ () => this.onNotify(roomId, "message_received"),
5483
+ (error) => this.log(`[${name}] failed to refresh SDK state after notification:`, error)
5484
+ );
5686
5485
  });
5687
- this.stagingName = options.stagingName ?? (() => `live.staging-${randomBytes(16).toString("hex")}`);
5688
5486
  }
5689
5487
  get size() {
5690
5488
  return this.packets.size;
@@ -5692,563 +5490,253 @@ var init_packets = __esm({
5692
5490
  get(roomId) {
5693
5491
  return this.packets.get(roomId);
5694
5492
  }
5695
- async create(roomId, identityName = `cowork-room-${roomId}`, bio = `ours-cowork mission room ${roomId}`) {
5493
+ async create(roomId, identityName = `ours-cowork-${roomId}`, bio = `ours-cowork mission room ${roomId}`) {
5696
5494
  validateRoomId(roomId);
5697
- if (this.packets.has(roomId)) throw new Error(`room packet "${roomId}" is already hosted`);
5698
- const liveDir = this.liveDir(roomId);
5699
- if (this.hasRestorableState(roomId)) {
5495
+ this.assertStandardIdentity(roomId, identityName);
5496
+ this.assertNoLegacyState(roomId);
5497
+ if (this.packets.has(roomId)) throw new Error(`room identity "${roomId}" is already hosted`);
5498
+ const client = this.host.createClient();
5499
+ try {
5500
+ let cid;
5700
5501
  try {
5701
- return await this.restore(roomId, void 0, identityName, bio);
5502
+ cid = (await client.chooseIdentity({ name: identityName, force: false })).cid;
5702
5503
  } catch (error) {
5703
- this.log(`[${this.packetName(roomId)}] pending packet restore failed; reprovisioning:`, error);
5504
+ if (!hasOursCode(error, "NO_SUCH_IDENTITY")) throw error;
5505
+ const created = await client.createIdentity({
5506
+ name: identityName,
5507
+ bio,
5508
+ exposeLocal: false,
5509
+ localAutoAccept: true
5510
+ });
5511
+ cid = created.info.cid;
5704
5512
  }
5705
- }
5706
- this.prepareProvisioningDirectory(roomId);
5707
- let native;
5708
- try {
5709
- native = await this.host.createPacket(this.packetName(roomId), this.seed());
5710
- let room;
5711
- room = new HostedRoomPacket(
5712
- native,
5713
- () => this.saveState(native, liveDir),
5714
- this.log,
5715
- (event) => this.onNotify(roomId, event),
5716
- () => {
5717
- if (this.packets.get(roomId) === room) this.packets.delete(roomId);
5718
- }
5719
- );
5720
- atomicWriteFileSync(this.identityPath(roomId), Buffer.from(exportSigningSecret(native), "utf8"), this.persistence);
5721
- this.provisioningCheckpoint("identity");
5722
- this.saveState(native, liveDir);
5723
- this.provisioningCheckpoint("state");
5724
- this.packets.set(roomId, room);
5725
- await room.setIdentity(identityName, bio);
5726
- this.provisioningCheckpoint("identity_applied");
5727
- return room;
5513
+ const packet = new SdkRoomPacket(identityName, cid, client);
5514
+ await packet.refresh();
5515
+ this.packets.set(roomId, packet);
5516
+ this.track(roomId, identityName);
5517
+ return packet;
5728
5518
  } catch (error) {
5729
- if (native) {
5730
- try {
5731
- this.host.removePacket(native.cid);
5732
- } catch {
5733
- }
5734
- }
5735
- this.packets.delete(roomId);
5736
- throw error;
5519
+ await client.releaseLease().catch(() => {
5520
+ });
5521
+ throw new Error(`failed to provision standard SDK identity for room "${roomId}"`, { cause: error });
5737
5522
  }
5738
5523
  }
5739
- async restore(roomId, expectedCid, identityName, bio) {
5524
+ async restore(roomId, expectedCid, identityName = `ours-cowork-${roomId}`) {
5740
5525
  validateRoomId(roomId);
5741
- if (this.packets.has(roomId)) throw new Error(`room packet "${roomId}" is already hosted`);
5742
- const liveDir = this.liveDir(roomId);
5743
- const secret = this.fs.readFileSync(this.identityPath(roomId), "utf8").trim();
5744
- if (!/^[0-9a-f]+$/i.test(secret) || secret.length % 2 !== 0) {
5745
- throw new Error(`invalid signing secret for room "${roomId}"`);
5746
- }
5747
- const stateBytes = this.fs.readFileSync(this.statePath(roomId));
5748
- if (stateBytes.length === 0) throw new Error(`empty packet state for room "${roomId}"`);
5749
- const native = await this.host.createPacket(
5750
- this.packetName(roomId),
5751
- this.seed(),
5752
- secret,
5753
- { deferredExposure: true }
5754
- );
5755
- if (expectedCid !== void 0 && native.cid !== expectedCid) {
5756
- try {
5757
- this.host.removePacket(native.cid);
5758
- } catch {
5759
- }
5760
- throw new Error(
5761
- `restored room packet CID mismatch for "${roomId}": expected "${expectedCid}", found "${native.cid}"`
5762
- );
5763
- }
5764
- let room;
5765
- room = new HostedRoomPacket(
5766
- native,
5767
- () => this.saveState(native, liveDir),
5768
- this.log,
5769
- (event) => this.onNotify(roomId, event),
5770
- () => {
5771
- if (this.packets.get(roomId) === room) this.packets.delete(roomId);
5772
- }
5773
- );
5526
+ this.assertStandardIdentity(roomId, identityName);
5527
+ this.assertNoLegacyState(roomId);
5528
+ if (this.packets.has(roomId)) throw new Error(`room identity "${roomId}" is already hosted`);
5529
+ const client = this.host.createClient();
5774
5530
  try {
5775
- await withScopeAsync(async (lifetime) => {
5776
- const state = native.pw.packet.ParseValue(new Uint8Array(stateBytes)).Attach(lifetime);
5777
- await native.mutatingTx("::actor::import_state", state, lifetime);
5778
- });
5779
- native.pw.refresh_identity_proof_document();
5780
- if (identityName !== void 0 && bio !== void 0) {
5781
- await room.setIdentity(identityName, bio);
5782
- this.provisioningCheckpoint("identity_applied");
5783
- }
5784
- atomicWriteFileSync(
5785
- this.ownershipPath(roomId),
5786
- Buffer.from(`${roomId}
5787
- `, "utf8"),
5788
- this.persistence
5789
- );
5790
- await this.beforeExpose(room);
5791
- this.host.exposePacket(native.cid);
5792
- this.packets.set(roomId, room);
5793
- return room;
5531
+ const bound = await client.chooseIdentity({ name: identityName, force: false });
5532
+ if (expectedCid !== void 0 && bound.cid !== expectedCid) {
5533
+ throw new Error(`restored room identity CID mismatch: expected "${expectedCid}", found "${bound.cid}"`);
5534
+ }
5535
+ const packet = new SdkRoomPacket(identityName, bound.cid, client);
5536
+ await packet.refresh();
5537
+ this.packets.set(roomId, packet);
5538
+ this.track(roomId, identityName);
5539
+ return packet;
5794
5540
  } catch (error) {
5795
- try {
5796
- this.host.removePacket(native.cid);
5797
- } catch {
5798
- }
5541
+ await client.releaseLease().catch(() => {
5542
+ });
5799
5543
  throw error;
5800
5544
  }
5801
5545
  }
5802
5546
  async destroy(roomId) {
5803
5547
  validateRoomId(roomId);
5804
- const room = this.packets.get(roomId);
5805
- if (room) {
5806
- this.host.removePacket(room.cid);
5807
- this.packets.delete(roomId);
5808
- }
5809
- const liveDir = this.liveDir(roomId);
5810
- let removalFailure;
5548
+ const packet = this.packets.get(roomId);
5549
+ if (!packet) return [];
5811
5550
  try {
5812
- this.assertSafeRoomDirectory(roomId);
5813
- this.fs.rmSync(liveDir, { recursive: true, force: true });
5814
- this.fsyncDirectory(this.roomDir(roomId));
5551
+ await packet.destroy();
5552
+ this.packets.delete(roomId);
5553
+ return [];
5815
5554
  } catch (error) {
5816
- this.log(`[${room?.name ?? this.packetName(roomId)}] live-state removal failed:`, error);
5817
- removalFailure = error;
5818
- }
5819
- const residue = this.residue(roomId);
5820
- if (removalFailure !== void 0 && residue.length === 0) {
5821
- throw new PacketPersistenceError(
5822
- `live-state removal durability is uncertain for room "${roomId}"`,
5823
- { cause: removalFailure }
5824
- );
5555
+ throw new Error(`failed to remove standard SDK identity for room "${roomId}"`, { cause: error });
5556
+ } finally {
5557
+ this.untrack(roomId);
5825
5558
  }
5826
- return residue;
5827
5559
  }
5828
- /** Unhost runtime packets while retaining every byte required for restart. */
5829
5560
  async unhostAll() {
5561
+ this.unsubscribe();
5562
+ for (const roomId of [...this.trackers.keys()]) this.untrack(roomId);
5830
5563
  const errors = [];
5831
- for (const [roomId, room] of [...this.packets]) {
5564
+ for (const [roomId, packet] of [...this.packets]) {
5832
5565
  try {
5833
- this.host.removePacket(room.cid, new Error("cowork daemon is shutting down"));
5834
- this.packets.delete(roomId);
5566
+ await packet.close();
5835
5567
  } catch (error) {
5836
5568
  errors.push(error);
5837
5569
  }
5570
+ this.packets.delete(roomId);
5838
5571
  }
5839
- if (errors.length > 0) throw new AggregateError(errors, "failed to unhost room packets");
5840
- }
5841
- saveState(packet, liveDir) {
5842
- try {
5843
- const bytes = withScope((lifetime) => Buffer.from(packet.readonlyTx("::actor::export_state", lifetime).Serialize()));
5844
- atomicWriteFileSync(join3(liveDir, "state_data.bin"), bytes, this.persistence);
5845
- } catch (error) {
5846
- if (error instanceof PacketPersistenceError) throw error;
5847
- throw new PacketPersistenceError(`failed to export state for packet "${packet.name}"`, { cause: error });
5848
- }
5572
+ if (errors.length) throw new AggregateError(errors, "failed to release room SDK leases");
5849
5573
  }
5850
- residue(roomId) {
5851
- const liveDir = this.liveDir(roomId);
5574
+ /**
5575
+ * Hosts that watch the runtime from outside need to be told which identities
5576
+ * exist; an embedded host omits `trackIdentity` and keeps its own callback.
5577
+ */
5578
+ track(roomId, identityName) {
5579
+ const dispose = this.host.trackIdentity?.(identityName);
5580
+ if (dispose) this.trackers.set(roomId, dispose);
5581
+ }
5582
+ untrack(roomId) {
5583
+ const dispose = this.trackers.get(roomId);
5584
+ if (!dispose) return;
5585
+ this.trackers.delete(roomId);
5852
5586
  try {
5853
- this.fs.lstatSync(liveDir);
5854
- return [liveDir];
5587
+ dispose();
5855
5588
  } catch (error) {
5856
- if (error.code === "ENOENT") return [];
5857
- throw error;
5589
+ this.log(`failed to stop the notification watch for room "${roomId}":`, error);
5858
5590
  }
5859
5591
  }
5860
- packetName(roomId) {
5861
- return `cowork-room-${roomId}`;
5862
- }
5863
- roomDir(roomId) {
5864
- return join3(this.stateDir, "rooms", roomId);
5865
- }
5866
- liveDir(roomId) {
5867
- return join3(this.roomDir(roomId), "live");
5868
- }
5869
- identityPath(roomId) {
5870
- return join3(this.liveDir(roomId), "identity.key");
5871
- }
5872
- statePath(roomId) {
5873
- return join3(this.liveDir(roomId), "state_data.bin");
5874
- }
5875
- ownershipPath(roomId) {
5876
- return join3(this.liveDir(roomId), ".cowork-provisioning-v1");
5877
- }
5878
- stagingJournalPath(roomId) {
5879
- return join3(this.roomDir(roomId), ".cowork-provisioning-stage");
5880
- }
5881
- residuePath(roomId) {
5882
- return join3(this.roomDir(roomId), "provisioning-residue");
5883
- }
5884
- hasRestorableState(roomId) {
5885
- try {
5886
- const secret = this.fs.readFileSync(this.identityPath(roomId), "utf8").trim();
5887
- return /^[0-9a-f]+$/i.test(secret) && secret.length % 2 === 0 && this.fs.readFileSync(this.statePath(roomId)).length > 0;
5888
- } catch {
5889
- return false;
5592
+ assertNoLegacyState(roomId) {
5593
+ const live = join3(this.stateDir, "rooms", roomId, "live");
5594
+ if (this.fs.existsSync(join3(live, "identity.key")) || this.fs.existsSync(join3(live, "state_data.bin"))) {
5595
+ throw new LegacyCoworkStateError(roomId);
5890
5596
  }
5891
5597
  }
5892
- prepareProvisioningDirectory(roomId) {
5893
- const roomDir = this.roomDir(roomId);
5894
- const liveDir = this.liveDir(roomId);
5895
- if (!this.fs.existsSync(roomDir)) {
5896
- this.fs.mkdirSync(roomDir, { recursive: true, mode: 448 });
5897
- this.fs.chmodSync(roomDir, 448);
5898
- }
5899
- if (this.fs.existsSync(liveDir)) {
5900
- let owned = false;
5901
- try {
5902
- owned = this.fs.readFileSync(this.ownershipPath(roomId), "utf8") === `${roomId}
5903
- `;
5904
- } catch {
5905
- }
5906
- if (owned) {
5907
- this.assertSafeRoomDirectory(roomId);
5908
- this.fs.rmSync(liveDir, { recursive: true, force: true });
5909
- this.fsyncDirectory(roomDir);
5910
- } else {
5911
- this.assertSafeRoomDirectory(roomId);
5912
- const residue = this.residuePath(roomId);
5913
- if (this.fs.existsSync(residue)) {
5914
- throw new PacketPersistenceError(
5915
- `room "${roomId}" has unknown live state and an existing provisioning residue; inspect both before retrying`
5916
- );
5917
- }
5918
- this.fs.renameSync(liveDir, residue);
5919
- this.fs.chmodSync(residue, 448);
5920
- this.fsyncDirectory(roomDir);
5921
- }
5922
- }
5923
- this.cleanupOwnedStaging(roomId);
5924
- const stagingName = this.stagingName();
5925
- if (!/^live\.staging-[0-9a-f]{32}$/.test(stagingName)) {
5926
- throw new PacketPersistenceError(`invalid provisioning staging name for room "${roomId}"`);
5927
- }
5928
- this.createStagingJournal(roomId, stagingName);
5929
- const stagingDir = join3(roomDir, stagingName);
5930
- try {
5931
- this.fs.mkdirSync(stagingDir, { recursive: false, mode: 448 });
5932
- } catch (error) {
5933
- try {
5934
- this.fs.unlinkSync(this.stagingJournalPath(roomId));
5935
- this.fsyncDirectory(roomDir);
5936
- } catch (cleanupError) {
5937
- throw new AggregateError([error, cleanupError], `provisioning staging collision cleanup failed for room "${roomId}"`);
5938
- }
5939
- throw new PacketPersistenceError(`provisioning staging collision for room "${roomId}"`, { cause: error });
5940
- }
5941
- this.fs.chmodSync(stagingDir, 448);
5942
- atomicWriteFileSync(
5943
- join3(stagingDir, ".cowork-provisioning-v1"),
5944
- Buffer.from(`${roomId}
5945
- `, "utf8"),
5946
- this.persistence
5947
- );
5948
- this.provisioningCheckpoint("mkdir");
5949
- this.fs.renameSync(stagingDir, liveDir);
5950
- this.fsyncDirectory(roomDir);
5951
- this.fs.unlinkSync(this.stagingJournalPath(roomId));
5952
- this.fsyncDirectory(roomDir);
5953
- }
5954
- cleanupOwnedStaging(roomId) {
5955
- const journal = this.stagingJournalPath(roomId);
5956
- let journalFd;
5957
- try {
5958
- try {
5959
- journalFd = this.fs.openSync(
5960
- journal,
5961
- nodeFs2.constants.O_RDONLY | (nodeFs2.constants.O_NOFOLLOW ?? 0)
5962
- );
5963
- } catch (error) {
5964
- if (error.code === "ENOENT") return;
5965
- throw error;
5966
- }
5967
- const journalStat = this.fs.fstatSync(journalFd);
5968
- if (!journalStat.isFile() || journalStat.size > 128) {
5969
- throw new PacketPersistenceError(`unsafe provisioning staging journal for room "${roomId}"`);
5970
- }
5971
- const stagingName = this.fs.readFileSync(journalFd, "utf8").trim();
5972
- if (!/^live\.staging-[0-9a-f]{32}$/.test(stagingName)) {
5973
- throw new PacketPersistenceError(`invalid provisioning staging journal for room "${roomId}"`);
5974
- }
5975
- const roomDir = this.roomDir(roomId);
5976
- const stagingDir = join3(roomDir, stagingName);
5977
- if (this.fs.existsSync(stagingDir)) {
5978
- const stat = this.fs.lstatSync(stagingDir);
5979
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
5980
- throw new PacketPersistenceError(`unsafe provisioning staging path for room "${roomId}"`);
5981
- }
5982
- let owned = false;
5983
- try {
5984
- owned = this.fs.readFileSync(join3(stagingDir, ".cowork-provisioning-v1"), "utf8") === `${roomId}
5985
- `;
5986
- } catch {
5987
- }
5988
- if (owned) {
5989
- this.fs.rmSync(stagingDir, { recursive: true, force: true });
5990
- this.fsyncDirectory(roomDir);
5991
- } else {
5992
- const residue = this.residuePath(roomId);
5993
- if (this.fs.existsSync(residue)) {
5994
- throw new PacketPersistenceError(
5995
- `provisioning staging recovery for room "${roomId}" found an existing provisioning residue; preserving both`
5996
- );
5997
- }
5998
- this.fs.renameSync(stagingDir, residue);
5999
- this.fs.chmodSync(residue, 448);
6000
- this.fsyncDirectory(roomDir);
6001
- }
6002
- }
6003
- const currentJournal = this.fs.lstatSync(journal);
6004
- if (!currentJournal.isFile() || currentJournal.dev !== journalStat.dev || currentJournal.ino !== journalStat.ino) {
6005
- throw new PacketPersistenceError(
6006
- `provisioning staging journal changed during recovery for room "${roomId}"; replacement preserved`
6007
- );
6008
- }
6009
- this.fs.unlinkSync(journal);
6010
- this.fsyncDirectory(roomDir);
6011
- } finally {
6012
- if (journalFd !== void 0) this.fs.closeSync(journalFd);
6013
- }
5598
+ assertStandardIdentity(roomId, identityName) {
5599
+ if (identityName !== `ours-cowork-${roomId}`) throw new LegacyCoworkStateError(roomId);
6014
5600
  }
6015
- createStagingJournal(roomId, stagingName) {
6016
- const journal = this.stagingJournalPath(roomId);
6017
- const bytes = Buffer.from(`${stagingName}
6018
- `, "utf8");
6019
- let fd;
6020
- let created = false;
6021
- try {
6022
- fd = this.persistence.openSync(
6023
- journal,
6024
- nodeFs2.constants.O_CREAT | nodeFs2.constants.O_EXCL | nodeFs2.constants.O_WRONLY | (nodeFs2.constants.O_NOFOLLOW ?? 0),
6025
- 384
6026
- );
6027
- created = true;
6028
- this.persistence.fchmodSync(fd, 384);
6029
- let offset = 0;
6030
- while (offset < bytes.byteLength) {
6031
- const written = this.persistence.writeSync(fd, bytes, offset, bytes.byteLength - offset, null);
6032
- if (written <= 0) throw new Error(`short write while persisting ${journal}`);
6033
- offset += written;
6034
- }
6035
- this.persistence.fsyncSync(fd);
6036
- this.persistence.closeSync(fd);
6037
- fd = void 0;
6038
- this.fsyncDirectory(this.roomDir(roomId));
6039
- } catch (error) {
6040
- if (fd !== void 0) {
6041
- try {
6042
- this.persistence.closeSync(fd);
6043
- } catch {
6044
- }
6045
- }
6046
- if (created) {
6047
- try {
6048
- this.fs.unlinkSync(journal);
6049
- this.fsyncDirectory(this.roomDir(roomId));
6050
- } catch {
6051
- }
6052
- }
6053
- throw new PacketPersistenceError(`failed to establish staging ownership for room "${roomId}"`, { cause: error });
6054
- }
5601
+ };
5602
+ SdkRoomPacket = class {
5603
+ name;
5604
+ cid;
5605
+ client;
5606
+ contacts = [];
5607
+ invites = [];
5608
+ inbox = [];
5609
+ fileInbox = [];
5610
+ refreshWork;
5611
+ constructor(name, cid, client) {
5612
+ this.name = name;
5613
+ this.cid = cid;
5614
+ this.client = client;
5615
+ }
5616
+ refresh() {
5617
+ this.refreshWork ??= this.refreshUnlocked().finally(() => {
5618
+ this.refreshWork = void 0;
5619
+ });
5620
+ return this.refreshWork;
5621
+ }
5622
+ async refreshUnlocked() {
5623
+ const contacts = await this.client.listContacts();
5624
+ this.contacts = contacts.contacts.map((contact) => ({ ...contact }));
5625
+ this.invites = (await this.client.listInvites()).flatMap((invite) => invite.mode === "one_time" || invite.mode === "public" ? [{ invite_id: invite.invite_id, mode: invite.mode }] : []);
5626
+ this.inbox = (await this.client.listIncomingMessages()).filter((message) => message.status === "unread").map((message) => ({
5627
+ msg_id: message.msg_id,
5628
+ sender_id: message.sender_id,
5629
+ sender_name: message.sender_name,
5630
+ text: message.text,
5631
+ date: normalizeDate(message.date),
5632
+ wire_id: message.wire_id,
5633
+ reply_to: cloneReply(message.reply_to)
5634
+ }));
5635
+ await this.refreshFiles();
6055
5636
  }
6056
- assertSafeRoomDirectory(roomId) {
6057
- const roomDir = this.roomDir(roomId);
6058
- const stat = this.fs.lstatSync(roomDir);
6059
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
6060
- throw new Error(`room directory for "${roomId}" is not a safe directory`);
5637
+ async refreshFiles() {
5638
+ const unread = (await this.client.listIncomingFiles()).filter((file) => file.status === "unread");
5639
+ if (unread.length === 0) {
5640
+ this.fileInbox = [];
5641
+ return;
6061
5642
  }
6062
- }
6063
- fsyncDirectory(path) {
6064
- let fd;
5643
+ const ids = unread.map((file) => file.file_id);
5644
+ const wires = unread.map((file) => file.wire_id);
6065
5645
  try {
6066
- fd = this.fs.openSync(path, nodeFs2.constants.O_RDONLY | (nodeFs2.constants.O_NOFOLLOW ?? 0));
6067
- this.fs.fsyncSync(fd);
5646
+ await this.client.getFiles({ wire_ids: wires });
5647
+ this.fileInbox = await Promise.all(unread.map(async (file) => fileItem(file, await this.client.fetchFile(file.wire_id))));
6068
5648
  } finally {
6069
- if (fd !== void 0) this.fs.closeSync(fd);
5649
+ await this.client.deferFiles({ file_ids: ids }).catch(() => {
5650
+ });
6070
5651
  }
6071
5652
  }
6072
- };
6073
- HostedRoomPacket = class {
6074
- name;
6075
- cid;
6076
- packet;
6077
- constructor(packet, saveState, log, onNotify = () => {
6078
- }, onTerminal = () => {
6079
- }) {
6080
- this.packet = packet;
6081
- this.name = packet.name;
6082
- this.cid = packet.cid;
6083
- wireHandlers(packet, { onSaveState: saveState, onNotify: (event) => onNotify(event) }, log);
6084
- packet.onTerminalClose?.(onTerminal);
6085
- }
6086
- async setIdentity(identityName, bio) {
6087
- await withScopeAsync((lifetime) => this.packet.mutatingTx("::a2a_messaging::set_my_name", { name: identityName }, lifetime));
6088
- await withScopeAsync((lifetime) => this.packet.mutatingTx("::a2a_messaging::set_my_bio", { bio }, lifetime));
6089
- }
6090
5653
  async mintInvite(mode) {
6091
- return withScopeAsync(async (lifetime) => {
6092
- const result = await this.packet.mutatingTx("::a2a_messaging::generate_invite", { mode }, lifetime);
6093
- return {
6094
- blob: packInvite(Buffer.from(result.Reduce("invite").GetBinary())),
6095
- invite_id: result.Reduce("invite_id").Visualize(),
6096
- reusable: booleanValue(result.Reduce("reusable"))
6097
- };
6098
- });
5654
+ const result = await this.client.generateInvite({ mode });
5655
+ await this.refresh();
5656
+ return { blob: result.blob, invite_id: result.inviteId, reusable: mode === "public" };
6099
5657
  }
6100
5658
  async addContact(invite) {
6101
- let decoded;
6102
- try {
6103
- decoded = unpackInvite(invite, MAX_EXTERNAL_INVITE_BYTES);
6104
- } catch (error) {
6105
- throw new Error("external invite could not be decoded within the 48 KiB limit", { cause: error });
6106
- }
6107
- return withScopeAsync(async (lifetime) => {
6108
- const result = await this.packet.mutatingTx("::a2a_messaging::add_contact", {
6109
- invite: this.packet.newBinary(decoded, lifetime)
6110
- }, lifetime);
6111
- return {
6112
- invite_id: result.Reduce("invite_id").Visualize(),
6113
- container_id: result.Reduce("container_id").Visualize(),
6114
- inviter_name: result.Reduce("inviter_name").Visualize(),
6115
- pending_name: result.Reduce("pending").Visualize()
6116
- };
6117
- });
5659
+ const decoded = unpackInvite(invite, MAX_EXTERNAL_INVITE_BYTES);
5660
+ const result = await this.client.addContact({ invite });
5661
+ await this.refresh();
5662
+ return {
5663
+ invite_id: createHash2("sha256").update(decoded).digest("hex"),
5664
+ container_id: result.cid,
5665
+ inviter_name: result.display,
5666
+ pending_name: result.display
5667
+ };
6118
5668
  }
6119
5669
  async revokeInvite(inviteId) {
6120
- return withScopeAsync(async (lifetime) => {
6121
- const result = await this.packet.mutatingTx("::a2a_messaging::revoke_invite", { invite_id: inviteId }, lifetime);
6122
- return { revoked: booleanValue(result.Reduce("revoked")) };
6123
- });
5670
+ const result = await this.client.revokeInvite({ invite_id: inviteId });
5671
+ await this.refresh();
5672
+ return { revoked: result.revoked };
6124
5673
  }
6125
5674
  listInvites() {
6126
- return withScope((lifetime) => {
6127
- const value = this.packet.readonlyTx("::a2a_messaging::list_invites", lifetime);
6128
- return dictionaryEntries(value).map(([inviteId, invite]) => ({
6129
- invite_id: inviteId,
6130
- mode: inviteMode(invite.Reduce("mode").Visualize())
6131
- }));
6132
- });
5675
+ return this.invites.map((invite) => ({ ...invite }));
6133
5676
  }
6134
5677
  listContacts() {
6135
- return withScope((lifetime) => {
6136
- const value = this.packet.readonlyTx("::a2a_messaging::list_contacts", lifetime);
6137
- return dictionaryEntries(value).map(([, contact]) => ({
6138
- name: contact.Reduce("name").Visualize(),
6139
- container_id: contact.Reduce("container_id").Visualize()
6140
- }));
6141
- });
6142
- }
6143
- listContactOrigins() {
6144
- return withScope((lifetime) => {
6145
- const value = this.packet.readonlyTx("::a2a_messaging::list_contact_origins", lifetime);
6146
- return Object.fromEntries(dictionaryEntries(value).map(([cid, origin]) => [cid, {
6147
- via: origin.Reduce("via").Visualize(),
6148
- invite_id: nilString(origin.Reduce("invite_id")),
6149
- at: adaptTimeToRfc3339(origin.Reduce("at").Visualize())
6150
- }]));
6151
- });
5678
+ return this.contacts.map((contact) => ({ ...contact }));
6152
5679
  }
6153
5680
  peekInbox() {
6154
- return withScope((lifetime) => renderInbox(this.packet.readonlyTx("::actor::list_incoming_messages", lifetime)).filter((message) => message.status === "unread").map(({ status: _status, ...message }) => message));
6155
- }
6156
- async consumeInbox(expectedIds) {
6157
- return withScopeAsync(async (lifetime) => {
6158
- const result = await this.packet.mutatingTx(
6159
- "::actor::consume_messages",
6160
- { expected_ids: expectedIds },
6161
- lifetime
6162
- );
6163
- return {
6164
- consumed: renderIntegerArray(result.Reduce("consumed")),
6165
- deferred: renderIntegerArray(result.Reduce("deferred"))
6166
- };
6167
- });
5681
+ return this.inbox.map((item) => ({ ...item }));
6168
5682
  }
6169
5683
  peekFileInbox() {
6170
- return withScope((lifetime) => renderFileInbox(
6171
- this.packet.readonlyTx("::actor::list_incoming_files", lifetime)
6172
- ).filter((file) => file.status === "unread").map(({ status: _status, ...file }) => file));
5684
+ return this.fileInbox.map((item) => ({ ...item, data: Buffer.from(item.data) }));
6173
5685
  }
6174
- async consumeFileInbox(expectedIds) {
6175
- return withScopeAsync(async (lifetime) => {
6176
- const result = await this.packet.mutatingTx(
6177
- "::actor::consume_files",
6178
- { expected_ids: expectedIds },
6179
- lifetime
6180
- );
6181
- return {
6182
- consumed: renderIntegerArray(result.Reduce("consumed")),
6183
- deferred: renderIntegerArray(result.Reduce("deferred"))
6184
- };
6185
- });
5686
+ async consumeInbox(expectedIds) {
5687
+ if (expectedIds.length === 0) return { consumed: [], deferred: [] };
5688
+ const pulled = await this.client.getMessages();
5689
+ const expected = new Set(expectedIds);
5690
+ const consumed = pulled.messages.filter((message) => expected.has(message.msg_id)).map((message) => message.msg_id);
5691
+ const deferred = pulled.messages.filter((message) => !expected.has(message.msg_id)).map((message) => message.msg_id);
5692
+ if (deferred.length) await this.client.deferMessages({ msg_ids: deferred });
5693
+ await this.refresh();
5694
+ return { consumed, deferred };
6186
5695
  }
6187
- async send(contactCid, body) {
6188
- return withScopeAsync(async (lifetime) => {
6189
- const result = await this.packet.mutatingTx(
6190
- "::a2a_messaging::send_message",
6191
- { contact: contactCid, text: body },
6192
- lifetime
6193
- );
6194
- const refused = !result.Reduce("downgrade_refused").IsNil();
6195
- return refused ? { status: "send_failed" } : { status: "queued", wire_id: nilString(result.Reduce("wire_id")) || void 0 };
6196
- });
5696
+ async consumeFileInbox(expectedIds) {
5697
+ const expected = new Set(expectedIds);
5698
+ const selected = this.fileInbox.filter((file) => expected.has(file.file_id));
5699
+ if (selected.length) await this.client.getFiles({ wire_ids: selected.map((file) => file.wire_id) });
5700
+ this.fileInbox = this.fileInbox.filter((file) => !expected.has(file.file_id));
5701
+ return { consumed: selected.map((file) => file.file_id), deferred: [] };
5702
+ }
5703
+ async send(contactCid, body, replyTo) {
5704
+ return sendResult(await this.client.sendMessage({
5705
+ contact: contactCid,
5706
+ text: body,
5707
+ ...replyTo === void 0 ? {} : {
5708
+ reply_to_wire_id: replyTo.wire_id,
5709
+ ...replyTo.sentence === void 0 ? {} : { reply_to_sentence: replyTo.sentence }
5710
+ }
5711
+ }));
6197
5712
  }
6198
- async sendFile(contactCid, filename, mime, data) {
5713
+ async sendFile(contactCid, filename, mime, data, replyTo) {
6199
5714
  const validName = FileNameSchema.parse(filename);
6200
5715
  const validMime = FileMimeSchema.parse(mime);
6201
- if (data.length > MAX_FILE_BYTES) {
6202
- throw new RangeError(`room files must be at most ${MAX_FILE_BYTES} bytes (2 MiB)`);
6203
- }
6204
- return withScopeAsync(async (lifetime) => {
6205
- const result = await this.packet.mutatingTx(
6206
- "::a2a_messaging::send_file",
6207
- {
6208
- contact: contactCid,
6209
- filename: validName,
6210
- mime: validMime,
6211
- // The core contract takes bytes. A filesystem path here would make
6212
- // recovery depend on staging ownership and is deliberately forbidden.
6213
- data: this.packet.newBinary(data, lifetime)
6214
- },
6215
- lifetime
6216
- );
6217
- const refused = !result.Reduce("downgrade_refused").IsNil() || !result.Reduce("migrating").IsNil();
6218
- return refused ? { status: "send_failed" } : { status: "queued", wire_id: nilString(result.Reduce("wire_id")) || void 0 };
6219
- });
5716
+ if (data.length > MAX_FILE_BYTES) throw new RangeError(`room files must be at most ${MAX_FILE_BYTES} bytes (2 MiB)`);
5717
+ return sendResult(await this.client.sendFile({
5718
+ contact: contactCid,
5719
+ data_base64: data.toString("base64"),
5720
+ filename: validName,
5721
+ mime: validMime,
5722
+ ...replyTo === void 0 ? {} : {
5723
+ reply_to_wire_id: replyTo.wire_id,
5724
+ ...replyTo.sentence === void 0 ? {} : { reply_to_sentence: replyTo.sentence }
5725
+ }
5726
+ }));
6220
5727
  }
6221
5728
  async removeContact(contactCid) {
6222
- return withScopeAsync(async (lifetime) => {
6223
- const result = await this.packet.mutatingTx(
6224
- "::a2a_messaging::remove_contact",
6225
- { contact: contactCid },
6226
- lifetime
6227
- );
6228
- const notified = strictBooleanValue(result.Reduce("notified"), "remove_contact notified");
6229
- const keyMaterialRetained = strictBooleanValue(
6230
- result.Reduce("key_material_retained"),
6231
- "remove_contact key_material_retained"
6232
- );
6233
- if (!keyMaterialRetained) {
6234
- throw new Error("remove_contact key_material_retained must be true");
6235
- }
6236
- return {
6237
- status: notified ? "queued" : "send_failed",
6238
- notified,
6239
- key_material_retained: true
6240
- };
6241
- });
5729
+ const result = await this.client.removeContact({ contact: contactCid });
5730
+ const notified = result.notified === true;
5731
+ await this.refresh();
5732
+ return { status: notified ? "queued" : "send_failed", notified, key_material_retained: true };
6242
5733
  }
6243
- async sign(canonicalJson2) {
6244
- return withScopeAsync(async (lifetime) => {
6245
- const result = await this.packet.mutatingTx(
6246
- "::actor::sign_app_envelope",
6247
- { canonical_json: canonicalJson2 },
6248
- lifetime
6249
- );
6250
- return Buffer.from(result.Reduce("signature").GetBinary()).toString("base64url");
6251
- });
5734
+ async close() {
5735
+ await this.client.releaseLease();
5736
+ }
5737
+ async destroy() {
5738
+ await this.client.removeIdentity({ name: this.name });
5739
+ await this.client.releaseLease();
6252
5740
  }
6253
5741
  };
6254
5742
  }
@@ -6286,15 +5774,18 @@ var init_ulid = __esm({
6286
5774
  });
6287
5775
 
6288
5776
  // src/intake.ts
6289
- import { createHash as createHash2 } from "node:crypto";
5777
+ import { createHash as createHash3 } from "node:crypto";
6290
5778
  function canonicalJson(value) {
6291
5779
  const encoded = JSON.stringify(canonicalValue(value));
6292
5780
  if (encoded === void 0) throw new TypeError("canonical JSON value is not serializable");
6293
5781
  return encoded;
6294
5782
  }
6295
- async function sendSignedBody(packet, recipientIdentity, unsigned) {
6296
- const signature = await packet.sign(canonicalJson(unsigned));
6297
- return packet.send(recipientIdentity, canonicalJson({ ...unsigned, signature }));
5783
+ async function sendRoomBody(packet, recipientIdentity, unsigned) {
5784
+ return packet.send(recipientIdentity, canonicalJson(unsigned));
5785
+ }
5786
+ function sameReply(stored, observed) {
5787
+ if (stored === void 0 || observed == null) return stored === void 0 && observed == null;
5788
+ return stored.wire_id === observed.wire_id && stored.sentence === observed.sentence;
6298
5789
  }
6299
5790
  function canonicalValue(value) {
6300
5791
  if (Array.isArray(value)) return value.map(canonicalValue);
@@ -6449,11 +5940,12 @@ var init_intake = __esm({
6449
5940
  filename: parsedName.data,
6450
5941
  mime: parsedMime.data,
6451
5942
  size: bytes.length,
6452
- sha256: createHash2("sha256").update(bytes).digest("hex"),
5943
+ sha256: createHash3("sha256").update(bytes).digest("hex"),
6453
5944
  data_base64: bytes.toString("base64"),
6454
5945
  recipient_identities: recipientIdentities,
6455
5946
  source_file_id: item.file_id,
6456
- ...item.wire_id === "" ? {} : { source_wire_id: item.wire_id }
5947
+ ...item.wire_id === "" ? {} : { source_wire_id: item.wire_id },
5948
+ ...item.reply_to == null ? {} : { source_reply_to: item.reply_to }
6457
5949
  });
6458
5950
  if (appended.kind !== "file") throw new Error("storage returned the wrong participant file kind");
6459
5951
  file = appended;
@@ -6493,7 +5985,8 @@ var init_intake = __esm({
6493
5985
  text: item.text,
6494
5986
  recipient_identities: recipientIdentities,
6495
5987
  source_msg_id: item.msg_id,
6496
- ...item.wire_id === "" ? {} : { source_wire_id: item.wire_id }
5988
+ ...item.wire_id === "" ? {} : { source_wire_id: item.wire_id },
5989
+ ...item.reply_to == null ? {} : { source_reply_to: item.reply_to }
6497
5990
  });
6498
5991
  if (appended.kind !== "message") throw new Error("storage returned the wrong participant message kind");
6499
5992
  message = appended;
@@ -6518,7 +6011,7 @@ var init_intake = __esm({
6518
6011
  await this.store.save(RoomSchema.parse({ ...room, seats }));
6519
6012
  try {
6520
6013
  const unsigned = { version: 1, kind: "room_not_member", room_id: roomId };
6521
- await sendSignedBody(packet, item.sender_id, unsigned);
6014
+ await sendRoomBody(packet, item.sender_id, unsigned);
6522
6015
  } catch {
6523
6016
  }
6524
6017
  }
@@ -6602,7 +6095,7 @@ var init_intake = __esm({
6602
6095
  display_name: file.author_alias.alias,
6603
6096
  role: file.author.role
6604
6097
  };
6605
- const metadata = await sendSignedBody(packet, intent.recipient_identity, {
6098
+ const metadata = await sendRoomBody(packet, intent.recipient_identity, {
6606
6099
  version: 1,
6607
6100
  kind: "room_file",
6608
6101
  room_id: roomId,
@@ -6668,7 +6161,7 @@ var init_intake = __esm({
6668
6161
  ...message.briefing_version === void 0 ? {} : { briefing_version: message.briefing_version },
6669
6162
  ...message.membership === void 0 ? {} : { membership: message.membership }
6670
6163
  };
6671
- const outcome = await sendSignedBody(packet, intent.recipient_identity, unsigned);
6164
+ const outcome = await sendRoomBody(packet, intent.recipient_identity, unsigned);
6672
6165
  const appended = await this.store.append(roomId, {
6673
6166
  version: 1,
6674
6167
  kind: "relay_result",
@@ -6688,7 +6181,7 @@ var init_intake = __esm({
6688
6181
  const message = records.find((record) => record.kind === "message" && record.source_msg_id === item.msg_id);
6689
6182
  if (!message) return void 0;
6690
6183
  const observedWireId = item.wire_id === "" ? void 0 : item.wire_id;
6691
- if (message.source_wire_id !== observedWireId || message.author.identity !== item.sender_id || message.text !== item.text || message.at !== item.date) {
6184
+ if (message.source_wire_id !== observedWireId || !sameReply(message.source_reply_to, item.reply_to) || message.author.identity !== item.sender_id || message.text !== item.text || message.at !== item.date) {
6692
6185
  throw new Error(`inbox source ${item.msg_id} does not match its durable room message`);
6693
6186
  }
6694
6187
  return message;
@@ -6698,7 +6191,7 @@ var init_intake = __esm({
6698
6191
  if (!file) return void 0;
6699
6192
  const observedWireId = item.wire_id === "" ? void 0 : item.wire_id;
6700
6193
  const bytes = Buffer.from(item.data);
6701
- if (file.source_wire_id !== observedWireId || file.author.identity !== item.sender_id || file.filename !== item.filename || file.mime !== item.mime || file.at !== item.date || file.size !== bytes.length || file.data_base64 !== bytes.toString("base64")) {
6194
+ if (file.source_wire_id !== observedWireId || !sameReply(file.source_reply_to, item.reply_to) || file.author.identity !== item.sender_id || file.filename !== item.filename || file.mime !== item.mime || file.at !== item.date || file.size !== bytes.length || file.data_base64 !== bytes.toString("base64")) {
6702
6195
  throw new Error(`file inbox source ${item.file_id} does not match its durable room file`);
6703
6196
  }
6704
6197
  return file;
@@ -6719,7 +6212,7 @@ var init_intake = __esm({
6719
6212
  });
6720
6213
 
6721
6214
  // src/service.ts
6722
- import { createHash as createHash3 } from "node:crypto";
6215
+ import { createHash as createHash4 } from "node:crypto";
6723
6216
  function byteBoundedHistoryPage(records) {
6724
6217
  const page = [];
6725
6218
  let bytes = 2;
@@ -6754,11 +6247,11 @@ function currentContactIdentities(packet) {
6754
6247
  }
6755
6248
  var ROOM_ROLE, CreateInviteInputSchema, HistoryOptionsSchema, DeleteRoomInputSchema, RemoveParticipantInputSchema, ReplaceParticipantInputSchema, RoomServiceError, RoomService;
6756
6249
  var init_service = __esm({
6757
- async "src/service.ts"() {
6250
+ "src/service.ts"() {
6758
6251
  "use strict";
6759
6252
  init_zod();
6760
6253
  init_contracts();
6761
- await init_adapt();
6254
+ init_packets();
6762
6255
  init_intake();
6763
6256
  init_ulid();
6764
6257
  ROOM_ROLE = "room";
@@ -6824,7 +6317,7 @@ var init_service = __esm({
6824
6317
  const settings = CreateRoomInputSchema.parse(input);
6825
6318
  const roomId = LowerCrockfordUlidSchema.parse(this.nextRoomId());
6826
6319
  const roomName = settings.name ?? defaultRoomName(roomId);
6827
- const identityName = roomIdentityName(roomName);
6320
+ const identityName = roomIdentityName(roomId);
6828
6321
  return this.lock(roomId, async () => {
6829
6322
  const provisional = RoomSchema.parse({
6830
6323
  version: 2,
@@ -6907,7 +6400,7 @@ var init_service = __esm({
6907
6400
  throw new RoomServiceError(`room packet "${id}" with established CID must be restored, not created`);
6908
6401
  }
6909
6402
  try {
6910
- packet = await this.packets.restore(id, room.identity_cid);
6403
+ packet = await this.packets.restore(id, room.identity_cid, room.identity_name);
6911
6404
  } catch (error) {
6912
6405
  throw new RoomServiceError(
6913
6406
  `failed to restore established room packet "${id}": ${error instanceof Error ? error.message : String(error)}`,
@@ -7006,6 +6499,11 @@ var init_service = __esm({
7006
6499
  return this.lock(id, async () => {
7007
6500
  const room = await this.store.load(id);
7008
6501
  this.assertMutable(room, "create an invite for");
6502
+ if (room.invites.some((invite) => invite.state === "live" || invite.state === "receipt_pending")) {
6503
+ throw new RoomServiceError(
6504
+ "standard SDK rooms permit one live invitation at a time so every accepted contact has unambiguous room admission metadata"
6505
+ );
6506
+ }
7009
6507
  return this.mintInviteUnlocked(room, {
7010
6508
  mode: request.mode,
7011
6509
  role: request.role ?? DEFAULT_ROLE,
@@ -7023,7 +6521,7 @@ var init_service = __esm({
7023
6521
  } catch {
7024
6522
  throw new RoomServiceError("external invite is invalid or exceeds the 48 KiB decoded limit");
7025
6523
  }
7026
- const digest = createHash3("sha256").update(decoded).digest("hex");
6524
+ const digest = createHash4("sha256").update(decoded).digest("hex");
7027
6525
  const receipt = await this.lock(id, async () => {
7028
6526
  const room = await this.store.load(id);
7029
6527
  this.assertMutable(room, "accept an external invite for");
@@ -7089,6 +6587,11 @@ var init_service = __esm({
7089
6587
  return receipt;
7090
6588
  }
7091
6589
  async mintInviteUnlocked(room, request) {
6590
+ if (room.invites.some((invite2) => invite2.state === "live" || invite2.state === "receipt_pending")) {
6591
+ throw new RoomServiceError(
6592
+ "standard SDK rooms permit one live invitation at a time; revoke or consume the current invitation first"
6593
+ );
6594
+ }
7092
6595
  const packet = this.packet(room.room_id);
7093
6596
  const minted = await packet.mintInvite(request.mode);
7094
6597
  const invite = RoomInviteSchema.parse({
@@ -7458,7 +6961,7 @@ var init_service = __esm({
7458
6961
  }
7459
6962
  const coreInvite = this.packet(id).listInvites().find((invite) => invite.invite_id === replacementId);
7460
6963
  if (!coreInvite || coreInvite.mode !== replacement.mode) {
7461
- throw new RoomServiceError("recovered invite is no longer present in packet state");
6964
+ throw new RoomServiceError("recovered invite is no longer present in SDK identity state");
7462
6965
  }
7463
6966
  const confirmed2 = {
7464
6967
  invite_id: replacement.invite_id,
@@ -7621,7 +7124,6 @@ var init_service = __esm({
7621
7124
  for (const contact of packet.listContacts()) {
7622
7125
  if (!contactsByCid.has(contact.container_id)) contactsByCid.set(contact.container_id, contact.name);
7623
7126
  }
7624
- const origins = packet.listContactOrigins();
7625
7127
  const inviteById = new Map(room.invites.map((invite) => [invite.invite_id, invite]));
7626
7128
  const existingCids = new Set(room.seats.filter((seat) => seat.state === "active" || seat.state === "pending").map((seat) => seat.identity));
7627
7129
  const lastRemovedAt = /* @__PURE__ */ new Map();
@@ -7637,37 +7139,26 @@ var init_service = __esm({
7637
7139
  for (const [seatIndex, seat] of room.seats.entries()) {
7638
7140
  if (seat.state !== "pending") continue;
7639
7141
  const displayName = contactsByCid.get(seat.identity);
7640
- const origin = origins[seat.identity];
7641
- if (displayName === void 0 || origin === void 0) continue;
7642
- const completedExternalRedemption = origin.via === "invite_redeemed" && origin.invite_id === seat.invite_id;
7643
- const fallbackInvite = origin.via === "invite_one_time" || origin.via === "invite_public" ? inviteById.get(origin.invite_id) : void 0;
7644
- const confirmedFallback = fallbackInvite !== void 0 && fallbackInvite.state !== "receipt_pending" && (fallbackInvite.recovery_of === void 0 || fallbackInvite.recovery_confirmed === true);
7645
- if (!completedExternalRedemption && !confirmedFallback) continue;
7646
- const removedAt = lastRemovedAt.get(seat.identity);
7647
- if (fallbackInvite !== void 0 && removedAt !== void 0 && fallbackInvite.created_at <= removedAt) continue;
7648
- const predecessor = fallbackInvite?.replaces_seat === void 0 ? void 0 : room.seats.find((candidate) => candidate.participant_id === fallbackInvite.replaces_seat && candidate.state === "removed");
7649
- if (fallbackInvite?.replaces_seat !== void 0 && predecessor === void 0) continue;
7142
+ if (displayName === void 0) continue;
7650
7143
  const { alias: _alias, replaces_seat: _replacesSeat, ...base } = seat;
7651
7144
  const activated = {
7652
7145
  ...base,
7653
7146
  state: "active",
7654
7147
  display_name: displayName,
7655
- accepted_at: origin.at,
7656
- role: fallbackInvite?.role ?? seat.role,
7657
- invite_id: fallbackInvite?.invite_id ?? seat.invite_id,
7658
- ...fallbackInvite === void 0 ? _replacesSeat === void 0 ? {} : { replaces_seat: _replacesSeat } : predecessor === void 0 ? {} : { replaces_seat: predecessor.participant_id },
7659
- ...room.anonymous ? { alias: fallbackInvite === void 0 ? _alias : predecessor?.alias ?? (fallbackInvite.role === seat.role ? _alias : mintAlias(seatsWithActivations, fallbackInvite.role)) } : {}
7148
+ accepted_at: this.now(),
7149
+ ..._replacesSeat === void 0 ? {} : { replaces_seat: _replacesSeat },
7150
+ ...room.anonymous ? { alias: _alias } : {}
7660
7151
  };
7661
7152
  activatedPending.push(activated);
7662
7153
  seatsWithActivations[seatIndex] = activated;
7663
7154
  }
7664
7155
  const newSeats = [];
7156
+ const eligibleInvites = room.invites.filter((invite) => invite.state === "live" && (invite.recovery_of === void 0 || invite.recovery_confirmed === true));
7157
+ const admissionInvite = eligibleInvites.length === 1 ? eligibleInvites[0] : void 0;
7665
7158
  for (const [cid, displayName] of contactsByCid) {
7666
7159
  if (existingCids.has(cid)) continue;
7667
- const origin = origins[cid];
7668
- if (!origin || origin.via !== "invite_one_time" && origin.via !== "invite_public") continue;
7669
- const invite = inviteById.get(origin.invite_id);
7670
- if (!invite || invite.state === "receipt_pending" || invite.recovery_of !== void 0 && invite.recovery_confirmed !== true) continue;
7160
+ const invite = admissionInvite;
7161
+ if (!invite) continue;
7671
7162
  const removedAt = lastRemovedAt.get(cid);
7672
7163
  if (removedAt !== void 0 && invite.created_at <= removedAt) continue;
7673
7164
  const seated = [...seatsWithActivations, ...newSeats];
@@ -7678,7 +7169,7 @@ var init_service = __esm({
7678
7169
  display_name: displayName,
7679
7170
  role: invite.role,
7680
7171
  invite_id: invite.invite_id,
7681
- accepted_at: origin.at,
7172
+ accepted_at: this.now(),
7682
7173
  participant_id: LowerCrockfordUlidSchema.parse(generateUlid()),
7683
7174
  state: "active",
7684
7175
  ...predecessor === void 0 ? {} : { replaces_seat: predecessor.participant_id },
@@ -7935,7 +7426,7 @@ var init_service = __esm({
7935
7426
  }
7936
7427
  }
7937
7428
  isPacketPending(room) {
7938
- return room.identity_cid === "" && room.state === "provisioning" && room.status === "packet_pending" && (room.identity_name === legacyRoomIdentityName(room.room_id) || room.identity_name === roomIdentityName(room.room_name));
7429
+ return room.identity_cid === "" && room.state === "provisioning" && room.status === "packet_pending" && room.identity_name === roomIdentityName(room.room_id);
7939
7430
  }
7940
7431
  now() {
7941
7432
  return external_exports.string().datetime({ offset: true }).parse(this.nowValue());
@@ -7946,9 +7437,9 @@ var init_service = __esm({
7946
7437
 
7947
7438
  // src/storage.ts
7948
7439
  import { randomBytes as randomBytes3 } from "node:crypto";
7949
- import * as nodeFs3 from "node:fs";
7440
+ import * as nodeFs2 from "node:fs";
7950
7441
  import { AsyncLocalStorage } from "node:async_hooks";
7951
- import { dirname as dirname4, join as join4 } from "node:path";
7442
+ import { dirname as dirname2, join as join4 } from "node:path";
7952
7443
  var DIRECTORY_MODE2, FILE_MODE2, NO_FOLLOW2, utf8Decoder, CoworkStorageError, RoomQueue, CoworkStore;
7953
7444
  var init_storage = __esm({
7954
7445
  "src/storage.ts"() {
@@ -7957,7 +7448,7 @@ var init_storage = __esm({
7957
7448
  init_ulid();
7958
7449
  DIRECTORY_MODE2 = 448;
7959
7450
  FILE_MODE2 = 384;
7960
- NO_FOLLOW2 = nodeFs3.constants.O_NOFOLLOW ?? 0;
7451
+ NO_FOLLOW2 = nodeFs2.constants.O_NOFOLLOW ?? 0;
7961
7452
  utf8Decoder = new TextDecoder("utf-8", { fatal: true });
7962
7453
  CoworkStorageError = class extends Error {
7963
7454
  constructor(message, options) {
@@ -7993,7 +7484,7 @@ var init_storage = __esm({
7993
7484
  constructor(stateDir, options = {}) {
7994
7485
  if (!stateDir) throw new CoworkStorageError("state directory is required");
7995
7486
  this.stateDir = stateDir;
7996
- this.fs = options.fs ?? nodeFs3;
7487
+ this.fs = options.fs ?? nodeFs2;
7997
7488
  }
7998
7489
  mutex(roomId, work) {
7999
7490
  const validRoomId = this.roomId(roomId);
@@ -8126,7 +7617,7 @@ var init_storage = __esm({
8126
7617
  try {
8127
7618
  fd = this.fs.openSync(
8128
7619
  archivePath,
8129
- nodeFs3.constants.O_WRONLY | nodeFs3.constants.O_APPEND | NO_FOLLOW2
7620
+ nodeFs2.constants.O_WRONLY | nodeFs2.constants.O_APPEND | NO_FOLLOW2
8130
7621
  );
8131
7622
  const opened = this.validateOpenPath(fd, archivePath, "room archive", "file", true);
8132
7623
  originalSize = opened.size;
@@ -8341,7 +7832,7 @@ var init_storage = __esm({
8341
7832
  try {
8342
7833
  fd = this.fs.openSync(
8343
7834
  temp,
8344
- nodeFs3.constants.O_CREAT | nodeFs3.constants.O_EXCL | nodeFs3.constants.O_WRONLY | NO_FOLLOW2,
7835
+ nodeFs2.constants.O_CREAT | nodeFs2.constants.O_EXCL | nodeFs2.constants.O_WRONLY | NO_FOLLOW2,
8345
7836
  FILE_MODE2
8346
7837
  );
8347
7838
  this.validateOpenPath(fd, temp, `temporary ${label}`, "file", true);
@@ -8351,7 +7842,7 @@ var init_storage = __esm({
8351
7842
  this.fs.closeSync(fd);
8352
7843
  fd = void 0;
8353
7844
  this.fs.renameSync(temp, path);
8354
- this.fsyncDirectory(dirname4(path));
7845
+ this.fsyncDirectory(dirname2(path));
8355
7846
  } catch (error) {
8356
7847
  if (fd !== void 0) {
8357
7848
  try {
@@ -8447,7 +7938,7 @@ var init_storage = __esm({
8447
7938
  break;
8448
7939
  }
8449
7940
  missing.push(cursor);
8450
- const parent = dirname4(cursor);
7941
+ const parent = dirname2(cursor);
8451
7942
  if (parent === cursor) throw new CoworkStorageError(`cannot find existing parent for ${label}`);
8452
7943
  cursor = parent;
8453
7944
  }
@@ -8458,13 +7949,13 @@ var init_storage = __esm({
8458
7949
  created.push(directory);
8459
7950
  this.fs.chmodSync(directory, DIRECTORY_MODE2);
8460
7951
  this.fsyncDirectory(directory);
8461
- this.fsyncDirectory(dirname4(directory));
7952
+ this.fsyncDirectory(dirname2(directory));
8462
7953
  }
8463
7954
  } catch (error) {
8464
7955
  for (const directory of created.reverse()) {
8465
7956
  try {
8466
7957
  this.fs.rmdirSync(directory);
8467
- this.fsyncDirectory(dirname4(directory));
7958
+ this.fsyncDirectory(dirname2(directory));
8468
7959
  } catch {
8469
7960
  }
8470
7961
  }
@@ -8514,7 +8005,7 @@ var init_storage = __esm({
8514
8005
  try {
8515
8006
  fd = this.fs.openSync(
8516
8007
  path,
8517
- nodeFs3.constants.O_CREAT | nodeFs3.constants.O_EXCL | nodeFs3.constants.O_WRONLY | NO_FOLLOW2,
8008
+ nodeFs2.constants.O_CREAT | nodeFs2.constants.O_EXCL | nodeFs2.constants.O_WRONLY | NO_FOLLOW2,
8518
8009
  FILE_MODE2
8519
8010
  );
8520
8011
  this.validateOpenPath(fd, path, "room archive", "file", true);
@@ -8523,7 +8014,7 @@ var init_storage = __esm({
8523
8014
  } finally {
8524
8015
  if (fd !== void 0) this.fs.closeSync(fd);
8525
8016
  }
8526
- this.fsyncDirectory(dirname4(path));
8017
+ this.fsyncDirectory(dirname2(path));
8527
8018
  }
8528
8019
  atomicMetadataWrite(path, room) {
8529
8020
  if (this.lstatIfPresent(path)) this.assertRegularFile(path, "room metadata");
@@ -8534,7 +8025,7 @@ var init_storage = __esm({
8534
8025
  try {
8535
8026
  fd = this.fs.openSync(
8536
8027
  temp,
8537
- nodeFs3.constants.O_CREAT | nodeFs3.constants.O_EXCL | nodeFs3.constants.O_WRONLY | NO_FOLLOW2,
8028
+ nodeFs2.constants.O_CREAT | nodeFs2.constants.O_EXCL | nodeFs2.constants.O_WRONLY | NO_FOLLOW2,
8538
8029
  FILE_MODE2
8539
8030
  );
8540
8031
  this.validateOpenPath(fd, temp, "temporary room metadata", "file", true);
@@ -8544,7 +8035,7 @@ var init_storage = __esm({
8544
8035
  this.fs.closeSync(fd);
8545
8036
  fd = void 0;
8546
8037
  this.fs.renameSync(temp, path);
8547
- this.fsyncDirectory(dirname4(path));
8038
+ this.fsyncDirectory(dirname2(path));
8548
8039
  } catch (error) {
8549
8040
  if (fd !== void 0) {
8550
8041
  try {
@@ -8570,7 +8061,7 @@ var init_storage = __esm({
8570
8061
  readFileNoFollow(path, label) {
8571
8062
  let fd;
8572
8063
  try {
8573
- fd = this.fs.openSync(path, nodeFs3.constants.O_RDONLY | NO_FOLLOW2);
8064
+ fd = this.fs.openSync(path, nodeFs2.constants.O_RDONLY | NO_FOLLOW2);
8574
8065
  this.validateOpenPath(fd, path, label, "file", true);
8575
8066
  return this.fs.readFileSync(fd);
8576
8067
  } finally {
@@ -8580,7 +8071,7 @@ var init_storage = __esm({
8580
8071
  fsyncDirectory(path) {
8581
8072
  let fd;
8582
8073
  try {
8583
- fd = this.fs.openSync(path, nodeFs3.constants.O_RDONLY | NO_FOLLOW2);
8074
+ fd = this.fs.openSync(path, nodeFs2.constants.O_RDONLY | NO_FOLLOW2);
8584
8075
  this.validateOpenPath(fd, path, "directory fsync target", "directory", false);
8585
8076
  this.fs.fsyncSync(fd);
8586
8077
  } finally {
@@ -8611,743 +8102,33 @@ var init_storage = __esm({
8611
8102
  }
8612
8103
  });
8613
8104
 
8614
- // src/openapi.ts
8615
- function params(properties, required) {
8616
- return { type: "object", additionalProperties: false, properties, required: [...required] };
8617
- }
8618
- function schemaBaseName(method) {
8619
- return method.split(".").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
8620
- }
8621
- function errorResponseSpec(description, codes) {
8622
- return {
8623
- description,
8624
- content: {
8625
- "application/json": {
8626
- schema: { $ref: "#/components/schemas/RpcErrorResponse" },
8627
- example: {
8628
- version: 1,
8629
- id: "req-1",
8630
- error: { code: codes[0], message: "see the daemon response for detail" }
8631
- }
8632
- }
8633
- }
8634
- };
8635
- }
8636
- function documentResponseSpec(description, mediaType) {
8637
- return { description, content: { [mediaType]: { schema: { type: "string" } } } };
8638
- }
8639
- function buildOpenApiDocument() {
8640
- const schemas = {
8641
- RpcId: {
8642
- description: "Caller-chosen correlation id echoed in the response.",
8643
- oneOf: [
8644
- { type: "string", minLength: 1, maxLength: 256 },
8645
- { type: "integer", minimum: 0 }
8646
- ]
8647
- },
8648
- RpcError: {
8649
- type: "object",
8650
- additionalProperties: false,
8651
- required: ["code", "message"],
8652
- properties: {
8653
- code: {
8654
- type: "string",
8655
- enum: RPC_ERROR_CODES.map(([code]) => code),
8656
- description: RPC_ERROR_CODES.map(([code, text]) => `\`${code}\`: ${text}`).join(" ")
8657
- },
8658
- message: { type: "string", description: "Human-readable detail. Not machine-parsed." }
8659
- }
8660
- },
8661
- RpcSuccessResponse: {
8662
- type: "object",
8663
- additionalProperties: false,
8664
- required: ["version", "id", "result"],
8665
- properties: {
8666
- version: { type: "integer", const: 1 },
8667
- id: { $ref: "#/components/schemas/RpcId" },
8668
- result: {
8669
- description: "Method-specific result. See the individual method descriptions."
8670
- }
8671
- }
8672
- },
8673
- RpcErrorResponse: {
8674
- type: "object",
8675
- additionalProperties: false,
8676
- required: ["version", "id", "error"],
8677
- properties: {
8678
- version: { type: "integer", const: 1 },
8679
- id: {
8680
- description: "The request id, or null when it could not be recovered from the body.",
8681
- oneOf: [{ $ref: "#/components/schemas/RpcId" }, { type: "null" }]
8682
- },
8683
- error: { $ref: "#/components/schemas/RpcError" }
8684
- }
8685
- }
8686
- };
8687
- const requestSchemaNames = [];
8688
- const discriminatorMapping = {};
8689
- for (const entry of ROOM_RPC_METHODS) {
8690
- const base = schemaBaseName(entry.method);
8691
- const paramsName = `${base}Params`;
8692
- const requestName = `${base}Request`;
8693
- schemas[paramsName] = { ...entry.params, description: `Parameters for \`${entry.method}\`.` };
8694
- schemas[requestName] = {
8695
- type: "object",
8696
- additionalProperties: false,
8697
- required: ["version", "id", "method", "params"],
8698
- title: entry.summary,
8699
- description: `${entry.description}
8700
-
8701
- Result: ${entry.result}`,
8702
- properties: {
8703
- version: { type: "integer", const: 1 },
8704
- id: { $ref: "#/components/schemas/RpcId" },
8705
- method: { type: "string", const: entry.method },
8706
- params: { $ref: `#/components/schemas/${paramsName}` }
8707
- },
8708
- examples: [{ version: 1, id: "req-1", method: entry.method, params: entry.example }]
8709
- };
8710
- requestSchemaNames.push(requestName);
8711
- discriminatorMapping[entry.method] = `#/components/schemas/${requestName}`;
8712
- }
8713
- return {
8714
- openapi: "3.1.0",
8715
- info: {
8716
- title: "ours-cowork room management API",
8717
- version: API_VERSION,
8718
- summary: "Loopback REST control surface for cowork mission rooms.",
8719
- description: [
8720
- 'Every room-management operation is carried by a single REST route, `POST /rpc`, using the versioned RPC envelope `{ "version": 1, "id": ..., "method": ..., "params": ... }`. The `method` field selects the operation; the request schemas below are discriminated on it.',
8721
- "",
8722
- "The listener binds `127.0.0.1` only and has no authentication, so it accepts requests whose `Host` header is `127.0.0.1:<port>` or `localhost:<port>`, rejects cross-site fetches, and must not be exposed to other hosts through forwarding or a proxy.",
8723
- "",
8724
- "Requests must declare `Content-Type: application/json` and stay within 1 MiB. Operations that need an invite secret are not served here: they are reachable only over the daemon's private Unix socket."
8725
- ].join("\n"),
8726
- license: { name: "FSL-1.1-Apache-2.0" }
8727
- },
8728
- servers: [{ url: "/", description: "This cowork daemon, on its loopback REST port." }],
8729
- tags: [
8730
- { name: "rooms", description: "Room management over the RPC envelope." },
8731
- { name: "documentation", description: "The API description and its browser UI." }
8732
- ],
8733
- paths: {
8734
- [RPC_PATH]: {
8735
- post: {
8736
- tags: ["rooms"],
8737
- operationId: "roomManagementRpc",
8738
- summary: "Invoke a room-management method",
8739
- description: "Dispatches one room-management method. The HTTP status reflects the envelope outcome: 200 on success, 404 when the method is not served over REST, 500 on an internal failure, and 400 for every other error code \u2014 including `not_found`.",
8740
- requestBody: {
8741
- required: true,
8742
- content: {
8743
- "application/json": {
8744
- schema: {
8745
- oneOf: requestSchemaNames.map((name) => ({ $ref: `#/components/schemas/${name}` })),
8746
- discriminator: { propertyName: "method", mapping: discriminatorMapping }
8747
- }
8748
- }
8749
- }
8750
- },
8751
- responses: {
8752
- 200: {
8753
- description: "The method completed. `result` is method-specific.",
8754
- content: {
8755
- "application/json": {
8756
- schema: { $ref: "#/components/schemas/RpcSuccessResponse" }
8757
- }
8758
- }
8759
- },
8760
- 400: errorResponseSpec(
8761
- "The envelope, parameters, or room state rejected the call.",
8762
- [
8763
- "invalid_params",
8764
- "invalid_json",
8765
- "invalid_request",
8766
- "not_found",
8767
- "invalid_state",
8768
- "shutting_down"
8769
- ]
8770
- ),
8771
- 403: errorResponseSpec(
8772
- "The Host header was not a loopback console authority, the Origin did not match it, or the fetch was cross-site.",
8773
- ["forbidden"]
8774
- ),
8775
- 404: errorResponseSpec(
8776
- "The method is not part of the REST route table.",
8777
- ["method_not_found"]
8778
- ),
8779
- 413: errorResponseSpec("The request body exceeded 1 MiB.", ["request_too_large"]),
8780
- 415: errorResponseSpec(
8781
- "The request did not declare `application/json`.",
8782
- ["unsupported_media_type"]
8783
- ),
8784
- 500: errorResponseSpec("The daemon failed to complete the operation.", ["internal"])
8785
- }
8786
- }
8787
- },
8788
- [OPENAPI_DOCUMENT_PATH]: {
8789
- get: {
8790
- tags: ["documentation"],
8791
- operationId: "getOpenApiDocument",
8792
- summary: "Fetch this OpenAPI document",
8793
- description: "Returns the OpenAPI 3.1 description of the room-management REST API.",
8794
- responses: {
8795
- 200: {
8796
- description: "The OpenAPI document.",
8797
- content: { "application/json": { schema: { type: "object" } } }
8798
- }
8799
- }
8800
- }
8801
- },
8802
- [API_DOCS_PATH]: {
8803
- get: {
8804
- tags: ["documentation"],
8805
- operationId: "getApiDocsPage",
8806
- summary: "Open the API documentation UI",
8807
- description: `Serves the browser UI that renders \`${OPENAPI_DOCUMENT_PATH}\`.`,
8808
- responses: { 200: documentResponseSpec("The documentation page.", "text/html") }
8809
- }
8810
- },
8811
- [API_DOCS_SCRIPT_PATH]: {
8812
- get: {
8813
- tags: ["documentation"],
8814
- operationId: "getApiDocsScript",
8815
- summary: "Fetch the documentation UI script",
8816
- responses: { 200: documentResponseSpec("The UI script.", "text/javascript") }
8817
- }
8818
- },
8819
- [API_DOCS_STYLESHEET_PATH]: {
8820
- get: {
8821
- tags: ["documentation"],
8822
- operationId: "getApiDocsStylesheet",
8823
- summary: "Fetch the documentation UI stylesheet",
8824
- responses: { 200: documentResponseSpec("The UI stylesheet.", "text/css") }
8825
- }
8826
- }
8827
- },
8828
- components: { schemas }
8829
- };
8830
- }
8831
- function apiDocsAsset(pathname) {
8832
- switch (pathname) {
8833
- case OPENAPI_DOCUMENT_PATH:
8834
- return {
8835
- body: `${JSON.stringify(buildOpenApiDocument(), null, 2)}
8836
- `,
8837
- contentType: "application/json; charset=utf-8"
8838
- };
8839
- case API_DOCS_PATH:
8840
- return { body: DOCS_PAGE, contentType: "text/html; charset=utf-8" };
8841
- case API_DOCS_SCRIPT_PATH:
8842
- return { body: DOCS_SCRIPT, contentType: "text/javascript; charset=utf-8" };
8843
- case API_DOCS_STYLESHEET_PATH:
8844
- return { body: DOCS_STYLESHEET, contentType: "text/css; charset=utf-8" };
8845
- default:
8846
- return void 0;
8847
- }
8848
- }
8849
- var RPC_PATH, OPENAPI_DOCUMENT_PATH, API_DOCS_PATH, API_DOCS_SCRIPT_PATH, API_DOCS_STYLESHEET_PATH, API_VERSION, EXAMPLE_ROOM_ID, roomIdProperty, roleProperty, missionTextProperty, roomNameProperty, inviteModeProperty, notifyProperty, ROOM_RPC_METHODS, RPC_ERROR_CODES, DOCS_PAGE, DOCS_STYLESHEET, DOCS_SCRIPT;
8850
- var init_openapi = __esm({
8851
- "src/openapi.ts"() {
8852
- "use strict";
8853
- RPC_PATH = "/rpc";
8854
- OPENAPI_DOCUMENT_PATH = "/openapi.json";
8855
- API_DOCS_PATH = "/docs";
8856
- API_DOCS_SCRIPT_PATH = "/docs/ui.js";
8857
- API_DOCS_STYLESHEET_PATH = "/docs/ui.css";
8858
- API_VERSION = "1";
8859
- EXAMPLE_ROOM_ID = "01jd7q4h9m2v8xk3znbc5regty";
8860
- roomIdProperty = {
8861
- type: "string",
8862
- pattern: "^[0-7][0-9a-hjkmnp-tv-z]{25}$",
8863
- description: "26-character lowercase Crockford ULID identifying the room."
8864
- };
8865
- roleProperty = {
8866
- type: "string",
8867
- description: "Invite role. At most 256 UTF-8 bytes."
8868
- };
8869
- missionTextProperty = (what) => ({
8870
- type: "string",
8871
- description: `${what}. At most 262144 UTF-8 bytes.`
8872
- });
8873
- roomNameProperty = {
8874
- type: "string",
8875
- description: "Friendly room name: 1-64 Unicode characters after trimming and NFC normalization, with no Unicode control or format characters. Duplicates are allowed."
8876
- };
8877
- inviteModeProperty = {
8878
- type: "string",
8879
- enum: ["one_time", "public"],
8880
- description: "Invitation mode. `one_time` requires `min_accepts` to be 1."
8881
- };
8882
- notifyProperty = {
8883
- type: "boolean",
8884
- description: "Announce the membership change to the room. Defaults to the room's quiet setting."
8885
- };
8886
- ROOM_RPC_METHODS = [
8887
- {
8888
- method: "room.create",
8889
- summary: "Create a room",
8890
- description: "Provisions a room, its durable metadata, and its room packet. The room stays in the provisioning state until its invitation requirements are satisfied.",
8891
- params: params({
8892
- name: roomNameProperty,
8893
- goal: missionTextProperty("Mission goal"),
8894
- briefing: missionTextProperty("Mission briefing"),
8895
- anonymous: {
8896
- type: "boolean",
8897
- description: "Project participants under aliases instead of contact identities."
8898
- },
8899
- quiet_membership: {
8900
- type: "boolean",
8901
- description: "Suppress membership announcements for this room by default."
8902
- }
8903
- }, ["goal", "briefing"]),
8904
- result: "The created room record, including `room_id`, `room_name`, `state`, mission, invites, and seats.",
8905
- example: { goal: "Ship the release", briefing: "Coordinate the 1.0 cut.", name: "Release" }
8906
- },
8907
- {
8908
- method: "room.settings",
8909
- summary: "Update room settings",
8910
- description: "Updates mutable room settings. At least one setting besides `room_id` is required; omitted settings are left unchanged.",
8911
- params: params({
8912
- room_id: roomIdProperty,
8913
- name: roomNameProperty,
8914
- goal: missionTextProperty("Replacement mission goal"),
8915
- briefing: missionTextProperty("Replacement mission briefing"),
8916
- status: { type: "string", minLength: 1, description: "Operator status line." },
8917
- quiet_membership: {
8918
- type: "boolean",
8919
- description: "Suppress membership announcements for this room."
8920
- }
8921
- }, ["room_id"]),
8922
- result: "The updated room record.",
8923
- example: { room_id: EXAMPLE_ROOM_ID, name: "Release cut" }
8924
- },
8925
- {
8926
- method: "room.briefing.role.set",
8927
- summary: "Set a role briefing",
8928
- description: "Creates or replaces the briefing for one role and bumps its briefing version.",
8929
- params: params({
8930
- room_id: roomIdProperty,
8931
- role: roleProperty,
8932
- text: missionTextProperty("Role briefing text")
8933
- }, ["room_id", "role", "text"]),
8934
- result: "The updated room record with the stored role briefing.",
8935
- example: { room_id: EXAMPLE_ROOM_ID, role: "Reviewer", text: "Review every merge request." }
8936
- },
8937
- {
8938
- method: "room.briefing.role.delete",
8939
- summary: "Delete a role briefing",
8940
- description: "Removes the briefing for one role.",
8941
- params: params({ room_id: roomIdProperty, role: roleProperty }, ["room_id", "role"]),
8942
- result: "The updated room record without the removed role briefing.",
8943
- example: { room_id: EXAMPLE_ROOM_ID, role: "Reviewer" }
8944
- },
8945
- {
8946
- method: "room.invite",
8947
- summary: "Mint an invitation requirement",
8948
- description: "Mints one invitation requirement and returns its receipt. The invite secret is returned once in `blob` and is never stored in room metadata or replayed by any later call.",
8949
- params: params({
8950
- room_id: roomIdProperty,
8951
- mode: inviteModeProperty,
8952
- role: roleProperty,
8953
- min_accepts: {
8954
- type: "integer",
8955
- minimum: 1,
8956
- description: "Acceptances required before the requirement is satisfied. Must be 1 for `one_time` invites."
8957
- }
8958
- }, ["room_id", "mode", "min_accepts"]),
8959
- result: "An invite receipt: `room_id`, the recorded `invite`, the one-shot `blob` secret, and `reusable`.",
8960
- example: { room_id: EXAMPLE_ROOM_ID, mode: "one_time", role: "Reviewer", min_accepts: 1 }
8961
- },
8962
- {
8963
- method: "room.participant.remove",
8964
- summary: "Remove a participant",
8965
- description: "Removes one participant seat, bumps the membership epoch, and severs the contact. The receipt reports what actually happened, including retained key material.",
8966
- params: params({
8967
- room_id: roomIdProperty,
8968
- participant: {
8969
- type: "string",
8970
- minLength: 1,
8971
- description: "Participant id, identity, display name, or alias."
8972
- },
8973
- notify: notifyProperty
8974
- }, ["room_id", "participant"]),
8975
- result: "A removal receipt with `participant_id`, `epoch`, `status`, `notified`, and `key_material_retained`.",
8976
- example: { room_id: EXAMPLE_ROOM_ID, participant: "Reviewer-1", notify: true }
8977
- },
8978
- {
8979
- method: "room.participant.replace",
8980
- summary: "Replace a participant",
8981
- description: "Removes one participant seat and mints a replacement invitation requirement in the same operation.",
8982
- params: params({
8983
- room_id: roomIdProperty,
8984
- participant: {
8985
- type: "string",
8986
- minLength: 1,
8987
- description: "Participant id, identity, display name, or alias."
8988
- },
8989
- notify: notifyProperty,
8990
- mode: inviteModeProperty,
8991
- min_accepts: {
8992
- type: "integer",
8993
- minimum: 1,
8994
- description: "Acceptances required for the replacement invitation requirement."
8995
- }
8996
- }, ["room_id", "participant"]),
8997
- result: "An invite receipt for the replacement, extended with the `removal` receipt.",
8998
- example: { room_id: EXAMPLE_ROOM_ID, participant: "Reviewer-1", mode: "one_time" }
8999
- },
9000
- {
9001
- method: "room.revoke",
9002
- summary: "Revoke an invitation requirement",
9003
- description: "Revokes one live invitation requirement so it can no longer admit a seat.",
9004
- params: params({
9005
- room_id: roomIdProperty,
9006
- invite_id: { type: "string", minLength: 1, description: "Recorded invite identifier." }
9007
- }, ["room_id", "invite_id"]),
9008
- result: "The updated room record with the invite marked revoked.",
9009
- example: { room_id: EXAMPLE_ROOM_ID, invite_id: "inv-01" }
9010
- },
9011
- {
9012
- method: "room.recover",
9013
- summary: "Recover unusable invitations",
9014
- description: "Mints recovery invitations for requirements whose secret can no longer admit a seat. Each recovery must be confirmed before it replaces the original.",
9015
- params: params({ room_id: roomIdProperty }, ["room_id"]),
9016
- result: "The list of recovery invite receipts, each carrying `recovery_of`.",
9017
- example: { room_id: EXAMPLE_ROOM_ID }
9018
- },
9019
- {
9020
- method: "room.recover.confirm",
9021
- summary: "Confirm a recovery invitation",
9022
- description: "Confirms that a recovery invitation was handed over, retiring the invitation requirement it recovers.",
9023
- params: params({
9024
- room_id: roomIdProperty,
9025
- recovery_of: { type: "string", minLength: 1, description: "Invite id being recovered." },
9026
- invite_id: { type: "string", minLength: 1, description: "Recovery invite id to confirm." }
9027
- }, ["room_id", "recovery_of", "invite_id"]),
9028
- result: "The updated room record with the confirmed recovery.",
9029
- example: { room_id: EXAMPLE_ROOM_ID, recovery_of: "inv-01", invite_id: "inv-02" }
9030
- },
9031
- {
9032
- method: "room.list",
9033
- summary: "List rooms",
9034
- description: "Lists every room held on this host. Takes no parameters.",
9035
- params: params({}, []),
9036
- result: "An array of room records.",
9037
- example: {}
9038
- },
9039
- {
9040
- method: "room.show",
9041
- summary: "Show one room",
9042
- description: "Returns the complete durable record for one room.",
9043
- params: params({ room_id: roomIdProperty }, ["room_id"]),
9044
- result: "The room record.",
9045
- example: { room_id: EXAMPLE_ROOM_ID }
9046
- },
9047
- {
9048
- method: "room.participants",
9049
- summary: "List participants",
9050
- description: "Returns the room's seats, including pending, active, and removed states.",
9051
- params: params({ room_id: roomIdProperty }, ["room_id"]),
9052
- result: "An array of seat records.",
9053
- example: { room_id: EXAMPLE_ROOM_ID }
9054
- },
9055
- {
9056
- method: "room.history",
9057
- summary: "Read room history",
9058
- description: "Reads one page of the room's ordered communication archive. The operator view returns every record kind; the participant view returns message records only, with routing identities removed and authors reduced to alias form in anonymous rooms.",
9059
- params: params({
9060
- room_id: roomIdProperty,
9061
- after: {
9062
- type: "integer",
9063
- minimum: 0,
9064
- description: "Return records with a sequence number greater than this value."
9065
- },
9066
- limit: { type: "integer", minimum: 1, description: "Maximum number of records to return." },
9067
- view: {
9068
- type: "string",
9069
- enum: ["operator", "participant"],
9070
- description: "History projection. Defaults to the operator view."
9071
- }
9072
- }, ["room_id"]),
9073
- result: "An array of communication records, byte-bounded to one page.",
9074
- example: { room_id: EXAMPLE_ROOM_ID, after: 0, limit: 50, view: "operator" }
9075
- },
9076
- {
9077
- method: "room.message",
9078
- summary: "Post an operator message",
9079
- description: "Appends an operator message to the room archive and relays it to active seats. Authorship is assigned by the daemon.",
9080
- params: params({
9081
- room_id: roomIdProperty,
9082
- text: missionTextProperty("Message text")
9083
- }, ["room_id", "text"]),
9084
- result: "The appended message record with its `seq` and `record_id`.",
9085
- example: { room_id: EXAMPLE_ROOM_ID, text: "Standup at 10:00." }
9086
- },
9087
- {
9088
- method: "room.close",
9089
- summary: "Close a room",
9090
- description: "Closes the room forward-only. The plaintext local archive is left in place.",
9091
- params: params({ room_id: roomIdProperty }, ["room_id"]),
9092
- result: "The closed room record.",
9093
- example: { room_id: EXAMPLE_ROOM_ID }
9094
- },
9095
- {
9096
- method: "room.delete",
9097
- summary: "Delete a room",
9098
- description: "Removes this host's local room state after the room is closed. The scope is this host only.",
9099
- params: params({
9100
- room_id: roomIdProperty,
9101
- confirm: { type: "boolean", const: true, description: "Must be `true`." }
9102
- }, ["room_id", "confirm"]),
9103
- result: 'A delete receipt: `{ "version": 1, "room_id": "...", "deleted": true, "scope": "this_host" }`.',
9104
- example: { room_id: EXAMPLE_ROOM_ID, confirm: true }
9105
- }
9106
- ];
9107
- RPC_ERROR_CODES = [
9108
- ["invalid_json", "The request body was not valid JSON."],
9109
- ["invalid_request", "The RPC envelope did not match the strict envelope schema."],
9110
- ["invalid_params", "The `params` object failed method validation."],
9111
- ["not_found", "The referenced room or record does not exist."],
9112
- ["invalid_state", "The room is not in a state that permits the operation."],
9113
- ["shutting_down", "The daemon stopped accepting work."],
9114
- ["method_not_found", "The method is not served over REST."],
9115
- ["unsupported_media_type", "The request did not declare `application/json`."],
9116
- ["forbidden", "The request origin or Host header was not the loopback console origin."],
9117
- ["request_too_large", "The request body exceeded 1 MiB."],
9118
- ["internal", "The daemon failed to complete the operation."]
9119
- ];
9120
- DOCS_PAGE = `<!DOCTYPE html>
9121
- <html lang="en">
9122
- <head>
9123
- <meta charset="utf-8">
9124
- <meta name="viewport" content="width=device-width, initial-scale=1">
9125
- <title>ours-cowork room management API</title>
9126
- <link rel="stylesheet" href="${API_DOCS_STYLESHEET_PATH}">
9127
- </head>
9128
- <body>
9129
- <main id="api-docs" data-document="${OPENAPI_DOCUMENT_PATH}" data-rpc="${RPC_PATH}">
9130
- <p class="loading">Loading <code>${OPENAPI_DOCUMENT_PATH}</code>&hellip;</p>
9131
- </main>
9132
- <script src="${API_DOCS_SCRIPT_PATH}"></script>
9133
- </body>
9134
- </html>
9135
- `;
9136
- DOCS_STYLESHEET = `:root { color-scheme: light dark; --line: #8883; --accent: #2563eb; }
9137
- * { box-sizing: border-box; }
9138
- body { margin: 0; font: 15px/1.5 ui-sans-serif, system-ui, sans-serif; }
9139
- main { margin: 0 auto; max-width: 60rem; padding: 2rem 1.25rem 4rem; }
9140
- h1 { font-size: 1.6rem; margin: 0 0 .25rem; }
9141
- h2 { font-size: 1.15rem; margin: 2rem 0 .5rem; }
9142
- code, pre, textarea { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .86em; }
9143
- pre { background: #8881; border-radius: 6px; margin: 0; overflow-x: auto; padding: .6rem .75rem; }
9144
- .subtitle { color: #8889; margin: 0 0 1rem; }
9145
- .intro { border-left: 3px solid var(--accent); padding-left: .9rem; white-space: pre-wrap; }
9146
- .route { border: 1px solid var(--line); border-radius: 8px; margin-bottom: .5rem; padding: .6rem .8rem; }
9147
- .verb { background: var(--accent); border-radius: 4px; color: #fff; font-size: .72rem;
9148
- font-weight: 700; letter-spacing: .04em; margin-right: .5rem; padding: .12rem .4rem; }
9149
- details.method { border: 1px solid var(--line); border-radius: 8px; margin-bottom: .5rem; }
9150
- details.method > summary { cursor: pointer; padding: .6rem .8rem; }
9151
- details.method[open] > summary { border-bottom: 1px solid var(--line); }
9152
- summary .name { font-family: ui-monospace, monospace; font-weight: 600; }
9153
- summary .summary-text { color: #8889; margin-left: .6rem; }
9154
- .body { padding: .8rem; }
9155
- .description { margin: 0 0 .8rem; white-space: pre-wrap; }
9156
- table { border-collapse: collapse; margin-bottom: .8rem; width: 100%; }
9157
- th, td { border-bottom: 1px solid var(--line); padding: .35rem .5rem; text-align: left;
9158
- vertical-align: top; }
9159
- th { font-size: .78rem; letter-spacing: .04em; text-transform: uppercase; }
9160
- td.param { font-family: ui-monospace, monospace; white-space: nowrap; }
9161
- .required { color: #b91c1c; font-size: .75rem; margin-left: .3rem; }
9162
- textarea { border: 1px solid var(--line); border-radius: 6px; padding: .5rem; width: 100%; }
9163
- button { background: var(--accent); border: 0; border-radius: 6px; color: #fff; cursor: pointer;
9164
- font: inherit; margin: .5rem 0; padding: .4rem 1rem; }
9165
- .status { font-weight: 600; margin: .5rem 0 .35rem; }
9166
- .status.failed { color: #b91c1c; }
9167
- .error { color: #b91c1c; }
9168
- `;
9169
- DOCS_SCRIPT = `'use strict';
9170
- (function () {
9171
- var root = document.getElementById('api-docs');
9172
- var documentUrl = root.dataset.document;
9173
- var rpcUrl = root.dataset.rpc;
9174
-
9175
- function element(tag, className, text) {
9176
- var node = document.createElement(tag);
9177
- if (className) node.className = className;
9178
- if (text !== undefined) node.textContent = text;
9179
- return node;
9180
- }
9181
-
9182
- function resolveRef(spec, ref) {
9183
- return ref.replace(/^#\\//, '').split('/').reduce(function (node, part) {
9184
- return node === undefined ? undefined : node[part];
9185
- }, spec);
9186
- }
9187
-
9188
- function typeLabel(schema) {
9189
- if (!schema) return 'any';
9190
- if (schema.const !== undefined) return JSON.stringify(schema.const);
9191
- if (schema.enum) return schema.enum.map(function (value) { return JSON.stringify(value); }).join(' | ');
9192
- return schema.type || 'any';
9193
- }
9194
-
9195
- function parameterTable(schema) {
9196
- var properties = (schema && schema.properties) || {};
9197
- var names = Object.keys(properties);
9198
- if (names.length === 0) return element('p', 'description', 'No parameters.');
9199
- var required = (schema && schema.required) || [];
9200
- var table = document.createElement('table');
9201
- var head = table.insertRow();
9202
- ['Parameter', 'Type', 'Description'].forEach(function (label) {
9203
- head.appendChild(element('th', null, label));
9204
- });
9205
- names.forEach(function (name) {
9206
- var property = properties[name];
9207
- var row = table.insertRow();
9208
- var cell = element('td', 'param', name);
9209
- if (required.indexOf(name) >= 0) cell.appendChild(element('span', 'required', 'required'));
9210
- row.appendChild(cell);
9211
- row.appendChild(element('td', null, typeLabel(property)));
9212
- row.appendChild(element('td', null, property.description || ''));
9213
- });
9214
- return table;
9215
- }
9216
-
9217
- function tryItPanel(example) {
9218
- var panel = document.createElement('div');
9219
- var input = document.createElement('textarea');
9220
- input.rows = Math.min(14, JSON.stringify(example, null, 2).split('\\n').length + 1);
9221
- input.value = JSON.stringify(example, null, 2);
9222
- input.setAttribute('aria-label', 'Request body');
9223
- var send = element('button', null, 'Send request');
9224
- var output = document.createElement('div');
9225
- send.addEventListener('click', function () {
9226
- var body = input.value;
9227
- try { JSON.parse(body); } catch (error) {
9228
- output.textContent = '';
9229
- output.appendChild(element('p', 'status failed', 'Request body is not valid JSON.'));
9230
- return;
9231
- }
9232
- send.disabled = true;
9233
- output.textContent = '';
9234
- output.appendChild(element('p', 'status', 'Sending\\u2026'));
9235
- fetch(rpcUrl, {
9236
- method: 'POST',
9237
- headers: { 'content-type': 'application/json' },
9238
- body: body,
9239
- }).then(function (response) {
9240
- return response.text().then(function (text) { return { status: response.status, text: text }; });
9241
- }).then(function (result) {
9242
- var pretty = result.text;
9243
- try { pretty = JSON.stringify(JSON.parse(result.text), null, 2); } catch (error) { /* raw */ }
9244
- output.textContent = '';
9245
- output.appendChild(element(
9246
- 'p',
9247
- result.status === 200 ? 'status' : 'status failed',
9248
- 'HTTP ' + result.status
9249
- ));
9250
- output.appendChild(element('pre', null, pretty));
9251
- }).catch(function (error) {
9252
- output.textContent = '';
9253
- output.appendChild(element('p', 'status failed', 'Request failed: ' + error));
9254
- }).then(function () { send.disabled = false; });
9255
- });
9256
- panel.appendChild(element('h3', null, 'Try it'));
9257
- panel.appendChild(input);
9258
- panel.appendChild(send);
9259
- panel.appendChild(output);
9260
- return panel;
9261
- }
9262
-
9263
- function methodPanel(spec, ref) {
9264
- var schema = resolveRef(spec, ref);
9265
- var method = schema.properties.method.const;
9266
- var panel = element('details', 'method');
9267
- var summary = document.createElement('summary');
9268
- summary.appendChild(element('span', 'name', method));
9269
- summary.appendChild(element('span', 'summary-text', schema.title || ''));
9270
- panel.appendChild(summary);
9271
- var body = element('div', 'body');
9272
- body.appendChild(element('p', 'description', schema.description || ''));
9273
- body.appendChild(parameterTable(resolveRef(spec, schema.properties.params.$ref)));
9274
- body.appendChild(tryItPanel((schema.examples && schema.examples[0]) || {
9275
- version: 1, id: 'req-1', method: method, params: {},
9276
- }));
9277
- panel.appendChild(body);
9278
- return panel;
9279
- }
9280
-
9281
- function renderRoutes(spec) {
9282
- var section = document.createDocumentFragment();
9283
- Object.keys(spec.paths).forEach(function (path) {
9284
- Object.keys(spec.paths[path]).forEach(function (verb) {
9285
- var operation = spec.paths[path][verb];
9286
- var route = element('div', 'route');
9287
- var heading = document.createElement('div');
9288
- heading.appendChild(element('span', 'verb', verb.toUpperCase()));
9289
- heading.appendChild(element('code', null, path));
9290
- route.appendChild(heading);
9291
- route.appendChild(element('p', 'description', operation.summary || ''));
9292
- section.appendChild(route);
9293
- });
9294
- });
9295
- return section;
9296
- }
9297
-
9298
- function render(spec) {
9299
- root.textContent = '';
9300
- root.appendChild(element('h1', null, spec.info.title));
9301
- root.appendChild(element('p', 'subtitle', 'API version ' + spec.info.version + ' \\u00b7 OpenAPI ' + spec.openapi));
9302
- root.appendChild(element('p', 'intro', spec.info.description || ''));
9303
- root.appendChild(element('h2', null, 'Routes'));
9304
- root.appendChild(renderRoutes(spec));
9305
- root.appendChild(element('h2', null, 'Room management methods'));
9306
- var alternatives = spec.paths[rpcUrl].post.requestBody.content['application/json'].schema.oneOf;
9307
- alternatives.forEach(function (alternative) {
9308
- root.appendChild(methodPanel(spec, alternative.$ref));
9309
- });
9310
- }
9311
-
9312
- fetch(documentUrl).then(function (response) {
9313
- if (!response.ok) throw new Error('HTTP ' + response.status);
9314
- return response.json();
9315
- }).then(render).catch(function (error) {
9316
- root.textContent = '';
9317
- root.appendChild(element('p', 'error', 'Could not load ' + documentUrl + ': ' + error));
9318
- });
9319
- }());
9320
- `;
9321
- }
9322
- });
9323
-
9324
8105
  // src/web.ts
9325
- import * as nodeFs4 from "node:fs";
8106
+ import * as nodeFs3 from "node:fs";
9326
8107
  import { extname, join as join5 } from "node:path";
9327
- function loadWebAssets(root, fs2 = nodeFs4) {
8108
+ function loadWebAssets(root, fs3 = nodeFs3) {
9328
8109
  let rootIdentity;
9329
8110
  try {
9330
- rootIdentity = observeDirectory(fs2, root, "web asset root");
8111
+ rootIdentity = observeDirectory(fs3, root, "web asset root");
9331
8112
  } catch (error) {
9332
8113
  if (error.code === "ENOENT") return /* @__PURE__ */ new Map();
9333
8114
  throw error;
9334
8115
  }
9335
8116
  const manifest = /* @__PURE__ */ new Map();
9336
- manifest.set("/", loadAsset(fs2, join5(root, "index.html"), false, [rootIdentity]));
8117
+ manifest.set("/", loadAsset(fs3, join5(root, "index.html"), false, [rootIdentity]));
9337
8118
  const assetsDirectory = join5(root, "assets");
9338
- assertDirectoryIdentity(fs2, rootIdentity);
9339
- const assetsIdentity = observeDirectory(fs2, assetsDirectory, "web assets directory");
9340
- assertDirectoryIdentity(fs2, rootIdentity);
8119
+ assertDirectoryIdentity(fs3, rootIdentity);
8120
+ const assetsIdentity = observeDirectory(fs3, assetsDirectory, "web assets directory");
8121
+ assertDirectoryIdentity(fs3, rootIdentity);
9341
8122
  const directories = [rootIdentity, assetsIdentity];
9342
- assertDirectoryIdentities(fs2, directories);
9343
- const assetEntries = fs2.readdirSync(assetsDirectory, { withFileTypes: true });
9344
- assertDirectoryIdentities(fs2, directories);
8123
+ assertDirectoryIdentities(fs3, directories);
8124
+ const assetEntries = fs3.readdirSync(assetsDirectory, { withFileTypes: true });
8125
+ assertDirectoryIdentities(fs3, directories);
9345
8126
  for (const entry of assetEntries) {
9346
8127
  if (!entry.isFile() || entry.isSymbolicLink()) continue;
9347
8128
  const path = join5(assetsDirectory, entry.name);
9348
- manifest.set(`/assets/${entry.name}`, loadAsset(fs2, path, true, directories));
8129
+ manifest.set(`/assets/${entry.name}`, loadAsset(fs3, path, true, directories));
9349
8130
  }
9350
- assertDirectoryIdentities(fs2, directories);
8131
+ assertDirectoryIdentities(fs3, directories);
9351
8132
  return manifest;
9352
8133
  }
9353
8134
  function createStaticWebHandler(assets) {
@@ -9370,44 +8151,44 @@ function createStaticWebHandler(assets) {
9370
8151
  return true;
9371
8152
  };
9372
8153
  }
9373
- function loadAsset(fs2, path, immutable, directories) {
9374
- assertDirectoryIdentities(fs2, directories);
9375
- const observed = fs2.lstatSync(path);
9376
- assertDirectoryIdentities(fs2, directories);
8154
+ function loadAsset(fs3, path, immutable, directories) {
8155
+ assertDirectoryIdentities(fs3, directories);
8156
+ const observed = fs3.lstatSync(path);
8157
+ assertDirectoryIdentities(fs3, directories);
9377
8158
  if (observed.isSymbolicLink() || !observed.isFile() || observed.nlink !== 1) {
9378
8159
  throw new Error(`web asset must be a non-symbolic-link regular file: ${path}`);
9379
8160
  }
9380
8161
  let fd;
9381
8162
  try {
9382
- fd = fs2.openSync(path, nodeFs4.constants.O_RDONLY | NO_FOLLOW3);
9383
- const opened = fs2.fstatSync(fd);
9384
- assertDirectoryIdentities(fs2, directories);
8163
+ fd = fs3.openSync(path, nodeFs3.constants.O_RDONLY | NO_FOLLOW3);
8164
+ const opened = fs3.fstatSync(fd);
8165
+ assertDirectoryIdentities(fs3, directories);
9385
8166
  if (!opened.isFile() || opened.nlink !== 1 || opened.dev !== observed.dev || opened.ino !== observed.ino) {
9386
8167
  throw new Error(`web asset changed while opening: ${path}`);
9387
8168
  }
9388
- const bytes = fs2.readFileSync(fd);
9389
- assertDirectoryIdentities(fs2, directories);
8169
+ const bytes = fs3.readFileSync(fd);
8170
+ assertDirectoryIdentities(fs3, directories);
9390
8171
  return {
9391
8172
  bytes,
9392
8173
  contentType: CONTENT_TYPES.get(extname(path).toLowerCase()) ?? "application/octet-stream",
9393
8174
  immutable
9394
8175
  };
9395
8176
  } finally {
9396
- if (fd !== void 0) fs2.closeSync(fd);
8177
+ if (fd !== void 0) fs3.closeSync(fd);
9397
8178
  }
9398
8179
  }
9399
- function observeDirectory(fs2, path, label) {
9400
- const stat = fs2.lstatSync(path);
8180
+ function observeDirectory(fs3, path, label) {
8181
+ const stat = fs3.lstatSync(path);
9401
8182
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
9402
8183
  throw new Error(`${label} must be a non-symbolic-link directory`);
9403
8184
  }
9404
8185
  return { path, label, dev: stat.dev, ino: stat.ino };
9405
8186
  }
9406
- function assertDirectoryIdentities(fs2, directories) {
9407
- for (const directory of directories) assertDirectoryIdentity(fs2, directory);
8187
+ function assertDirectoryIdentities(fs3, directories) {
8188
+ for (const directory of directories) assertDirectoryIdentity(fs3, directory);
9408
8189
  }
9409
- function assertDirectoryIdentity(fs2, expected) {
9410
- const current = fs2.lstatSync(expected.path);
8190
+ function assertDirectoryIdentity(fs3, expected) {
8191
+ const current = fs3.lstatSync(expected.path);
9411
8192
  if (current.isSymbolicLink() || !current.isDirectory() || current.dev !== expected.dev || current.ino !== expected.ino) {
9412
8193
  throw new Error(`${expected.label} changed during web asset loading`);
9413
8194
  }
@@ -9446,7 +8227,7 @@ var NO_FOLLOW3, CONTENT_SECURITY_POLICY, CONTENT_TYPES;
9446
8227
  var init_web = __esm({
9447
8228
  "src/web.ts"() {
9448
8229
  "use strict";
9449
- NO_FOLLOW3 = nodeFs4.constants.O_NOFOLLOW ?? 0;
8230
+ NO_FOLLOW3 = nodeFs3.constants.O_NOFOLLOW ?? 0;
9450
8231
  CONTENT_SECURITY_POLICY = [
9451
8232
  "default-src 'self'",
9452
8233
  "script-src 'self'",
@@ -9483,12 +8264,12 @@ var init_web = __esm({
9483
8264
  import { randomBytes as randomBytes4 } from "node:crypto";
9484
8265
  import * as http from "node:http";
9485
8266
  import * as net from "node:net";
9486
- import * as nodeFs5 from "node:fs";
9487
- import { basename, dirname as dirname5, join as join6 } from "node:path";
8267
+ import * as nodeFs4 from "node:fs";
8268
+ import { basename, dirname as dirname3, join as join6 } from "node:path";
9488
8269
  function createServiceRoutes(service) {
9489
8270
  return {
9490
- "room.create": { auth: true, run: (params2) => service.createRoom(params2) },
9491
- "room.settings": { auth: true, run: (params2) => {
8271
+ "room.create": { auth: true, run: (params) => service.createRoom(params) },
8272
+ "room.settings": { auth: true, run: (params) => {
9492
8273
  const { room_id, ...input } = external_exports.object({
9493
8274
  room_id: external_exports.string(),
9494
8275
  name: external_exports.unknown().optional(),
@@ -9496,68 +8277,68 @@ function createServiceRoutes(service) {
9496
8277
  briefing: external_exports.unknown().optional(),
9497
8278
  status: external_exports.unknown().optional(),
9498
8279
  quiet_membership: external_exports.unknown().optional()
9499
- }).strict().parse(params2);
8280
+ }).strict().parse(params);
9500
8281
  return service.updateRoom(room_id, input);
9501
8282
  } },
9502
- "room.briefing.role.set": { auth: true, run: (params2) => {
9503
- const { room_id, ...input } = RoleBriefingSetParams.parse(params2);
8283
+ "room.briefing.role.set": { auth: true, run: (params) => {
8284
+ const { room_id, ...input } = RoleBriefingSetParams.parse(params);
9504
8285
  return service.setRoleBriefing(room_id, input);
9505
8286
  } },
9506
- "room.briefing.role.delete": { auth: true, run: (params2) => {
9507
- const { room_id, ...input } = RoleBriefingDeleteParams.parse(params2);
8287
+ "room.briefing.role.delete": { auth: true, run: (params) => {
8288
+ const { room_id, ...input } = RoleBriefingDeleteParams.parse(params);
9508
8289
  return service.deleteRoleBriefing(room_id, input);
9509
8290
  } },
9510
- "room.invite": { auth: true, run: (params2) => {
8291
+ "room.invite": { auth: true, run: (params) => {
9511
8292
  const { room_id, ...input } = external_exports.object({
9512
8293
  room_id: external_exports.string(),
9513
8294
  mode: external_exports.unknown(),
9514
8295
  role: external_exports.unknown().optional(),
9515
8296
  min_accepts: external_exports.unknown()
9516
- }).strict().parse(params2);
8297
+ }).strict().parse(params);
9517
8298
  return service.createInvite(room_id, input);
9518
8299
  } },
9519
- "room.participant.remove": { auth: true, run: (params2) => {
9520
- const { room_id, ...input } = ParticipantRemoveParams.parse(params2);
8300
+ "room.participant.remove": { auth: true, run: (params) => {
8301
+ const { room_id, ...input } = ParticipantRemoveParams.parse(params);
9521
8302
  return service.removeParticipant(room_id, input);
9522
8303
  } },
9523
- "room.participant.replace": { auth: true, run: (params2) => {
9524
- const { room_id, ...input } = ParticipantReplaceParams.parse(params2);
8304
+ "room.participant.replace": { auth: true, run: (params) => {
8305
+ const { room_id, ...input } = ParticipantReplaceParams.parse(params);
9525
8306
  return service.replaceParticipant(room_id, input);
9526
8307
  } },
9527
- "room.revoke": { auth: true, run: (params2) => {
9528
- const value = InviteRevokeParams.parse(params2);
8308
+ "room.revoke": { auth: true, run: (params) => {
8309
+ const value = InviteRevokeParams.parse(params);
9529
8310
  return service.revokeInvite(value.room_id, value.invite_id);
9530
8311
  } },
9531
- "room.recover": { auth: true, run: (params2) => service.recoverInvites(RoomIdParams.parse(params2).room_id) },
9532
- "room.recover.confirm": { auth: true, run: (params2) => {
9533
- const value = RecoverConfirmParams.parse(params2);
8312
+ "room.recover": { auth: true, run: (params) => service.recoverInvites(RoomIdParams.parse(params).room_id) },
8313
+ "room.recover.confirm": { auth: true, run: (params) => {
8314
+ const value = RecoverConfirmParams.parse(params);
9534
8315
  return service.confirmRecoveredInvite(value.room_id, value.recovery_of, value.invite_id);
9535
8316
  } },
9536
- "room.list": { auth: true, run: (params2) => {
9537
- external_exports.object({}).strict().parse(params2);
8317
+ "room.list": { auth: true, run: (params) => {
8318
+ external_exports.object({}).strict().parse(params);
9538
8319
  return service.listRooms();
9539
8320
  } },
9540
- "room.show": { auth: true, run: (params2) => service.showRoom(RoomIdParams.parse(params2).room_id) },
9541
- "room.participants": { auth: true, run: (params2) => service.participants(RoomIdParams.parse(params2).room_id) },
9542
- "room.history": { auth: true, run: (params2) => {
9543
- const { room_id, ...options } = HistoryParams.parse(params2);
8321
+ "room.show": { auth: true, run: (params) => service.showRoom(RoomIdParams.parse(params).room_id) },
8322
+ "room.participants": { auth: true, run: (params) => service.participants(RoomIdParams.parse(params).room_id) },
8323
+ "room.history": { auth: true, run: (params) => {
8324
+ const { room_id, ...options } = HistoryParams.parse(params);
9544
8325
  return service.history(room_id, options);
9545
8326
  } },
9546
- "room.message": { auth: true, run: (params2) => {
9547
- const { room_id, ...input } = external_exports.object({ room_id: external_exports.string(), text: external_exports.unknown() }).strict().parse(params2);
8327
+ "room.message": { auth: true, run: (params) => {
8328
+ const { room_id, ...input } = external_exports.object({ room_id: external_exports.string(), text: external_exports.unknown() }).strict().parse(params);
9548
8329
  return service.postMessage(room_id, input);
9549
8330
  } },
9550
- "room.close": { auth: true, run: (params2) => service.closeRoom(RoomIdParams.parse(params2).room_id) },
9551
- "room.delete": { auth: true, run: (params2) => {
9552
- const { room_id, ...input } = external_exports.object({ room_id: external_exports.string(), confirm: external_exports.unknown() }).strict().parse(params2);
8331
+ "room.close": { auth: true, run: (params) => service.closeRoom(RoomIdParams.parse(params).room_id) },
8332
+ "room.delete": { auth: true, run: (params) => {
8333
+ const { room_id, ...input } = external_exports.object({ room_id: external_exports.string(), confirm: external_exports.unknown() }).strict().parse(params);
9553
8334
  return service.deleteRoom(room_id, input);
9554
8335
  } }
9555
8336
  };
9556
8337
  }
9557
8338
  function createPrivateServiceRoutes(service) {
9558
8339
  return {
9559
- "room.accept": { auth: true, run: (params2) => {
9560
- const { room_id, ...input } = ExternalInviteAcceptParams.parse(params2);
8340
+ "room.accept": { auth: true, run: (params) => {
8341
+ const { room_id, ...input } = ExternalInviteAcceptParams.parse(params);
9561
8342
  return service.acceptExternalInvite(room_id, input);
9562
8343
  } }
9563
8344
  };
@@ -9577,23 +8358,6 @@ function isJsonMediaType(value) {
9577
8358
  const mediaType = value.split(";", 1)[0]?.trim().toLowerCase();
9578
8359
  return mediaType === "application/json" || mediaType?.startsWith("application/") === true && mediaType.endsWith("+json");
9579
8360
  }
9580
- async function serveApiDocs(request, response) {
9581
- const pathname = safePathname(request.url);
9582
- if (pathname === void 0) return false;
9583
- const asset = apiDocsAsset(pathname);
9584
- if (!asset) return false;
9585
- const body = Buffer.from(asset.body, "utf8");
9586
- response.writeHead(200, {
9587
- "content-type": asset.contentType,
9588
- "content-length": body.length,
9589
- "cache-control": "no-cache",
9590
- "content-security-policy": CONTENT_SECURITY_POLICY,
9591
- "x-content-type-options": "nosniff"
9592
- });
9593
- response.end(body);
9594
- await responseFinished2(response);
9595
- return true;
9596
- }
9597
8361
  function sendJson(response, status, value) {
9598
8362
  const body = Buffer.from(JSON.stringify(value), "utf8");
9599
8363
  response.writeHead(status, {
@@ -9711,7 +8475,6 @@ var init_transports = __esm({
9711
8475
  "src/transports.ts"() {
9712
8476
  "use strict";
9713
8477
  init_zod();
9714
- init_openapi();
9715
8478
  init_web();
9716
8479
  MAX_REQUEST_BYTES = 1024 * 1024;
9717
8480
  HTTP_HEADERS_TIMEOUT_MS = 5e3;
@@ -9831,7 +8594,7 @@ var init_transports = __esm({
9831
8594
  protectedPrivateReplacement;
9832
8595
  constructor(options) {
9833
8596
  this.options = options;
9834
- this.fs = options.fs ?? nodeFs5;
8597
+ this.fs = options.fs ?? nodeFs4;
9835
8598
  this.staticHandler = options.staticHandler ?? createStaticWebHandler(/* @__PURE__ */ new Map());
9836
8599
  }
9837
8600
  get restAddress() {
@@ -9978,7 +8741,6 @@ var init_transports = __esm({
9978
8741
  if (request.url !== "/rpc") {
9979
8742
  if (request.method === "GET") {
9980
8743
  activateResponse();
9981
- if (await serveApiDocs(request, response)) return;
9982
8744
  if (await this.staticHandler(request, response)) return;
9983
8745
  }
9984
8746
  activateResponse();
@@ -10127,7 +8889,7 @@ var init_transports = __esm({
10127
8889
  throw new Error(`private management socket changed during removal; replacement protected at ${quarantined.path}`);
10128
8890
  }
10129
8891
  async cleanupStalePrivateSockets() {
10130
- const directory = dirname5(this.options.socketPath);
8892
+ const directory = dirname3(this.options.socketPath);
10131
8893
  const prefix = `${basename(this.options.socketPath)}.private-`;
10132
8894
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
10133
8895
  const candidates = this.fs.readdirSync(directory).filter((name) => name.startsWith(prefix)).sort().slice(0, MAX_STALE_PRIVATE_SOCKET_CLEANUP);
@@ -10235,24 +8997,24 @@ __export(daemon_runtime_exports, {
10235
8997
  removeDaemonPid: () => removeDaemonPid,
10236
8998
  writeDaemonPid: () => writeDaemonPid
10237
8999
  });
10238
- import * as nodeFs6 from "node:fs";
9000
+ import * as nodeFs5 from "node:fs";
10239
9001
  import { join as join7 } from "node:path";
10240
- import { fileURLToPath as fileURLToPath2 } from "node:url";
9002
+ import { fileURLToPath } from "node:url";
10241
9003
  function isIntakeNotification(event) {
10242
9004
  return event === "message_received" || event === "file_received" || event === "contact_accepted" || event === "contact_added";
10243
9005
  }
10244
9006
  function createDaemonControlRoutes(control) {
10245
9007
  if (!/^[0-9a-f]{32}$/.test(control.session)) throw new TypeError("invalid daemon control session");
10246
- const requireExact = (params2, keys) => {
10247
- if (Object.keys(params2).length !== keys.length || keys.some((key) => !Object.hasOwn(params2, key))) {
9008
+ const requireExact = (params, keys) => {
9009
+ if (Object.keys(params).length !== keys.length || keys.some((key) => !Object.hasOwn(params, key))) {
10248
9010
  throw new TypeError("invalid daemon control parameters");
10249
9011
  }
10250
9012
  };
10251
9013
  return {
10252
9014
  "daemon.status": {
10253
9015
  auth: true,
10254
- run(params2) {
10255
- requireExact(params2, []);
9016
+ run(params) {
9017
+ requireExact(params, []);
10256
9018
  return {
10257
9019
  version: 1,
10258
9020
  protocol: "cowork-supervisor-control",
@@ -10263,9 +9025,9 @@ function createDaemonControlRoutes(control) {
10263
9025
  },
10264
9026
  "daemon.shutdown": {
10265
9027
  auth: true,
10266
- async run(params2) {
10267
- requireExact(params2, ["session"]);
10268
- if (params2.session !== control.session) throw new TypeError("daemon control session changed");
9028
+ async run(params) {
9029
+ requireExact(params, ["session"]);
9030
+ if (params.session !== control.session) throw new TypeError("daemon control session changed");
10269
9031
  if (!await control.requestSupervisorShutdown()) throw new Error("supervisor IPC rejected shutdown request");
10270
9032
  return { accepted: true, session: control.session };
10271
9033
  }
@@ -10273,7 +9035,7 @@ function createDaemonControlRoutes(control) {
10273
9035
  };
10274
9036
  }
10275
9037
  function acquireDaemonLock(stateDir, options = {}) {
10276
- const fs2 = options.fs ?? nodeFs6;
9038
+ const fs3 = options.fs ?? nodeFs5;
10277
9039
  const pid = options.pid ?? process.pid;
10278
9040
  const alive = options.isProcessAlive ?? isProcessAlive;
10279
9041
  const path = join7(stateDir, "daemon.lock");
@@ -10281,22 +9043,22 @@ function acquireDaemonLock(stateDir, options = {}) {
10281
9043
  let fd;
10282
9044
  let created = false;
10283
9045
  try {
10284
- fd = fs2.openSync(path, nodeFs6.constants.O_CREAT | nodeFs6.constants.O_EXCL | nodeFs6.constants.O_WRONLY | NO_FOLLOW4, FILE_MODE3);
9046
+ fd = fs3.openSync(path, nodeFs5.constants.O_CREAT | nodeFs5.constants.O_EXCL | nodeFs5.constants.O_WRONLY | NO_FOLLOW4, FILE_MODE3);
10285
9047
  created = true;
10286
- fs2.fchmodSync(fd, FILE_MODE3);
10287
- writeAll(fs2, fd, Buffer.from(`${pid}
9048
+ fs3.fchmodSync(fd, FILE_MODE3);
9049
+ writeAll(fs3, fd, Buffer.from(`${pid}
10288
9050
  `, "ascii"));
10289
- fs2.fsyncSync(fd);
10290
- const owned = fs2.fstatSync(fd);
10291
- fs2.closeSync(fd);
9051
+ fs3.fsyncSync(fd);
9052
+ const owned = fs3.fstatSync(fd);
9053
+ fs3.closeSync(fd);
10292
9054
  fd = void 0;
10293
- fsyncDirectory2(fs2, stateDir);
9055
+ fsyncDirectory2(fs3, stateDir);
10294
9056
  const pidPath = join7(stateDir, "daemon.pid");
10295
- if (lstatIfPresent2(fs2, pidPath)) {
10296
- const pidOwner = readSecurePid(fs2, pidPath, "daemon PID");
9057
+ if (lstatIfPresent2(fs3, pidPath)) {
9058
+ const pidOwner = readSecurePid(fs3, pidPath, "daemon PID");
10297
9059
  if (alive(pidOwner)) {
10298
- fs2.unlinkSync(path);
10299
- fsyncDirectory2(fs2, stateDir);
9060
+ fs3.unlinkSync(path);
9061
+ fsyncDirectory2(fs3, stateDir);
10300
9062
  throw new Error(`cowork daemon is already running with PID ${pidOwner}`);
10301
9063
  }
10302
9064
  }
@@ -10305,90 +9067,90 @@ function acquireDaemonLock(stateDir, options = {}) {
10305
9067
  release() {
10306
9068
  if (released) return;
10307
9069
  released = true;
10308
- const current = lstatIfPresent2(fs2, path);
9070
+ const current = lstatIfPresent2(fs3, path);
10309
9071
  if (!current || current.dev !== owned.dev || current.ino !== owned.ino) return;
10310
- const content = readSecurePid(fs2, path, "daemon lock");
9072
+ const content = readSecurePid(fs3, path, "daemon lock");
10311
9073
  if (content !== pid) return;
10312
- fs2.unlinkSync(path);
10313
- fsyncDirectory2(fs2, stateDir);
9074
+ fs3.unlinkSync(path);
9075
+ fsyncDirectory2(fs3, stateDir);
10314
9076
  }
10315
9077
  };
10316
9078
  } catch (error) {
10317
9079
  if (fd !== void 0) try {
10318
- fs2.closeSync(fd);
9080
+ fs3.closeSync(fd);
10319
9081
  } catch {
10320
9082
  }
10321
9083
  if (error.code !== "EEXIST") {
10322
9084
  if (created) {
10323
9085
  try {
10324
- fs2.unlinkSync(path);
10325
- fsyncDirectory2(fs2, stateDir);
9086
+ fs3.unlinkSync(path);
9087
+ fsyncDirectory2(fs3, stateDir);
10326
9088
  } catch {
10327
9089
  }
10328
9090
  }
10329
9091
  throw error;
10330
9092
  }
10331
- const observed = fs2.lstatSync(path);
10332
- const owner = readSecurePid(fs2, path, "daemon lock");
9093
+ const observed = fs3.lstatSync(path);
9094
+ const owner = readSecurePid(fs3, path, "daemon lock");
10333
9095
  if (alive(owner)) throw new Error(`cowork daemon is already running with PID ${owner}`);
10334
- const current = fs2.lstatSync(path);
9096
+ const current = fs3.lstatSync(path);
10335
9097
  if (current.dev !== observed.dev || current.ino !== observed.ino) continue;
10336
- fs2.unlinkSync(path);
10337
- fsyncDirectory2(fs2, stateDir);
9098
+ fs3.unlinkSync(path);
9099
+ fsyncDirectory2(fs3, stateDir);
10338
9100
  }
10339
9101
  }
10340
9102
  throw new Error("daemon lock changed repeatedly while acquiring it");
10341
9103
  }
10342
- function writeDaemonPid(stateDir, fs2 = nodeFs6, pid = process.pid) {
9104
+ function writeDaemonPid(stateDir, fs3 = nodeFs5, pid = process.pid) {
10343
9105
  const path = join7(stateDir, "daemon.pid");
10344
- const existing = lstatIfPresent2(fs2, path);
9106
+ const existing = lstatIfPresent2(fs3, path);
10345
9107
  if (existing) {
10346
- const owner = readSecurePid(fs2, path, "daemon PID");
9108
+ const owner = readSecurePid(fs3, path, "daemon PID");
10347
9109
  if (isProcessAlive(owner) && owner !== pid) throw new Error(`cowork daemon PID file belongs to live PID ${owner}`);
10348
- const current = fs2.lstatSync(path);
9110
+ const current = fs3.lstatSync(path);
10349
9111
  if (current.dev !== existing.dev || current.ino !== existing.ino) throw new Error("daemon PID file changed during stale-owner check");
10350
- fs2.unlinkSync(path);
9112
+ fs3.unlinkSync(path);
10351
9113
  }
10352
9114
  let fd;
10353
9115
  let created = false;
10354
9116
  try {
10355
- fd = fs2.openSync(path, nodeFs6.constants.O_CREAT | nodeFs6.constants.O_EXCL | nodeFs6.constants.O_WRONLY | NO_FOLLOW4, FILE_MODE3);
9117
+ fd = fs3.openSync(path, nodeFs5.constants.O_CREAT | nodeFs5.constants.O_EXCL | nodeFs5.constants.O_WRONLY | NO_FOLLOW4, FILE_MODE3);
10356
9118
  created = true;
10357
- fs2.fchmodSync(fd, FILE_MODE3);
10358
- writeAll(fs2, fd, Buffer.from(`${pid}
9119
+ fs3.fchmodSync(fd, FILE_MODE3);
9120
+ writeAll(fs3, fd, Buffer.from(`${pid}
10359
9121
  `, "ascii"));
10360
- fs2.fsyncSync(fd);
9122
+ fs3.fsyncSync(fd);
10361
9123
  } catch (error) {
10362
9124
  if (fd !== void 0) {
10363
9125
  try {
10364
- fs2.closeSync(fd);
9126
+ fs3.closeSync(fd);
10365
9127
  } catch {
10366
9128
  }
10367
9129
  fd = void 0;
10368
9130
  }
10369
9131
  if (created) try {
10370
- fs2.unlinkSync(path);
9132
+ fs3.unlinkSync(path);
10371
9133
  } catch {
10372
9134
  }
10373
9135
  throw error;
10374
9136
  } finally {
10375
- if (fd !== void 0) fs2.closeSync(fd);
9137
+ if (fd !== void 0) fs3.closeSync(fd);
10376
9138
  }
10377
- fsyncDirectory2(fs2, stateDir);
9139
+ fsyncDirectory2(fs3, stateDir);
10378
9140
  }
10379
- function removeDaemonPid(stateDir, fs2 = nodeFs6, pid = process.pid) {
9141
+ function removeDaemonPid(stateDir, fs3 = nodeFs5, pid = process.pid) {
10380
9142
  const path = join7(stateDir, "daemon.pid");
10381
- const observed = lstatIfPresent2(fs2, path);
9143
+ const observed = lstatIfPresent2(fs3, path);
10382
9144
  if (!observed) return;
10383
- const owner = readSecurePid(fs2, path, "daemon PID");
9145
+ const owner = readSecurePid(fs3, path, "daemon PID");
10384
9146
  if (owner !== pid) return;
10385
- const current = fs2.lstatSync(path);
9147
+ const current = fs3.lstatSync(path);
10386
9148
  if (current.dev !== observed.dev || current.ino !== observed.ino) return;
10387
- fs2.unlinkSync(path);
10388
- fsyncDirectory2(fs2, stateDir);
9149
+ fs3.unlinkSync(path);
9150
+ fsyncDirectory2(fs3, stateDir);
10389
9151
  }
10390
- function readSecurePid(fs2, path, label) {
10391
- const stat = fs2.lstatSync(path);
9152
+ function readSecurePid(fs3, path, label) {
9153
+ const stat = fs3.lstatSync(path);
10392
9154
  if (stat.isSymbolicLink() || !stat.isFile() || stat.nlink !== 1 || (stat.mode & 511) !== FILE_MODE3) {
10393
9155
  throw new Error(`${label} must be a 0600 single-link regular file`);
10394
9156
  }
@@ -10398,15 +9160,15 @@ function readSecurePid(fs2, path, label) {
10398
9160
  let fd;
10399
9161
  let text;
10400
9162
  try {
10401
- fd = fs2.openSync(path, nodeFs6.constants.O_RDONLY | NO_FOLLOW4);
10402
- const opened = fs2.fstatSync(fd);
10403
- const current = fs2.lstatSync(path);
9163
+ fd = fs3.openSync(path, nodeFs5.constants.O_RDONLY | NO_FOLLOW4);
9164
+ const opened = fs3.fstatSync(fd);
9165
+ const current = fs3.lstatSync(path);
10404
9166
  if (opened.dev !== stat.dev || opened.ino !== stat.ino || current.dev !== opened.dev || current.ino !== opened.ino) {
10405
9167
  throw new Error(`${label} changed while opening`);
10406
9168
  }
10407
- text = fs2.readFileSync(fd, "utf8");
9169
+ text = fs3.readFileSync(fd, "utf8");
10408
9170
  } finally {
10409
- if (fd !== void 0) fs2.closeSync(fd);
9171
+ if (fd !== void 0) fs3.closeSync(fd);
10410
9172
  }
10411
9173
  if (!/^[1-9][0-9]*\n$/.test(text)) throw new Error(`${label} contains an invalid PID`);
10412
9174
  const pid = Number(text.trim());
@@ -10421,26 +9183,26 @@ function isProcessAlive(pid) {
10421
9183
  return error.code === "EPERM";
10422
9184
  }
10423
9185
  }
10424
- function writeAll(fs2, fd, bytes) {
9186
+ function writeAll(fs3, fd, bytes) {
10425
9187
  let offset = 0;
10426
9188
  while (offset < bytes.length) {
10427
- const written = fs2.writeSync(fd, bytes, offset, bytes.length - offset, null);
9189
+ const written = fs3.writeSync(fd, bytes, offset, bytes.length - offset, null);
10428
9190
  if (written <= 0) throw new Error("write made no progress");
10429
9191
  offset += written;
10430
9192
  }
10431
9193
  }
10432
- function fsyncDirectory2(fs2, path) {
9194
+ function fsyncDirectory2(fs3, path) {
10433
9195
  let fd;
10434
9196
  try {
10435
- fd = fs2.openSync(path, nodeFs6.constants.O_RDONLY | NO_FOLLOW4);
10436
- fs2.fsyncSync(fd);
9197
+ fd = fs3.openSync(path, nodeFs5.constants.O_RDONLY | NO_FOLLOW4);
9198
+ fs3.fsyncSync(fd);
10437
9199
  } finally {
10438
- if (fd !== void 0) fs2.closeSync(fd);
9200
+ if (fd !== void 0) fs3.closeSync(fd);
10439
9201
  }
10440
9202
  }
10441
- function lstatIfPresent2(fs2, path) {
9203
+ function lstatIfPresent2(fs3, path) {
10442
9204
  try {
10443
- return fs2.lstatSync(path);
9205
+ return fs3.lstatSync(path);
10444
9206
  } catch (error) {
10445
9207
  if (error.code === "ENOENT") return void 0;
10446
9208
  throw error;
@@ -10451,18 +9213,18 @@ function requiresExitFrom(error) {
10451
9213
  }
10452
9214
  var FILE_MODE3, NO_FOLLOW4, DaemonBootCancelledError, DaemonShutdownError, CoworkDaemon;
10453
9215
  var init_daemon_runtime = __esm({
10454
- async "src/daemon-runtime.ts"() {
9216
+ "src/daemon-runtime.ts"() {
10455
9217
  "use strict";
10456
- await init_adapt();
10457
9218
  init_config();
10458
9219
  init_config();
10459
- await init_packets();
10460
- await init_service();
9220
+ init_ours_runtime();
9221
+ init_packets();
9222
+ init_service();
10461
9223
  init_storage();
10462
9224
  init_transports();
10463
9225
  init_web();
10464
9226
  FILE_MODE3 = 384;
10465
- NO_FOLLOW4 = nodeFs6.constants.O_NOFOLLOW ?? 0;
9227
+ NO_FOLLOW4 = nodeFs5.constants.O_NOFOLLOW ?? 0;
10466
9228
  DaemonBootCancelledError = class extends Error {
10467
9229
  constructor() {
10468
9230
  super("cowork daemon boot cancelled by shutdown");
@@ -10514,7 +9276,7 @@ var init_daemon_runtime = __esm({
10514
9276
  this.options.onStage?.("post-lock");
10515
9277
  this.checkpoint();
10516
9278
  try {
10517
- this.host = this.options.host ?? new AdaptHost(config.brokerUrl, this.options.log);
9279
+ this.host = this.options.host ?? createOursHost(config, this.options.log);
10518
9280
  this.store = this.options.store ?? new CoworkStore(config.stateDir);
10519
9281
  let serviceRef = this.options.service;
10520
9282
  this.registry = this.options.registry ?? new PacketRegistry(
@@ -10565,7 +9327,7 @@ var init_daemon_runtime = __esm({
10565
9327
  const unixDispatcher = new RpcDispatcher(unixRoutes);
10566
9328
  const restDispatcher = new RpcDispatcher(serviceRoutes);
10567
9329
  const staticHandler = createStaticWebHandler(loadWebAssets(
10568
- fileURLToPath2(new URL("./web/", import.meta.url))
9330
+ fileURLToPath(new URL("./web/", import.meta.url))
10569
9331
  ));
10570
9332
  this.transports = this.options.transports ?? new TransportServer({
10571
9333
  socketPath: runtime.socketPath,
@@ -10808,7 +9570,7 @@ async function runWorker() {
10808
9570
  await acknowledge();
10809
9571
  return shutdownComplete;
10810
9572
  }
10811
- const runtime = await init_daemon_runtime().then(() => daemon_runtime_exports);
9573
+ const runtime = await Promise.resolve().then(() => (init_daemon_runtime(), daemon_runtime_exports));
10812
9574
  if (shutdownRequested) {
10813
9575
  await acknowledge();
10814
9576
  return shutdownComplete;
@@ -10903,13 +9665,13 @@ var init_daemon_process = __esm({
10903
9665
  import { fork } from "node:child_process";
10904
9666
  import { randomBytes as randomBytes6 } from "node:crypto";
10905
9667
  import { resolve as resolve3 } from "node:path";
10906
- import { fileURLToPath as fileURLToPath3 } from "node:url";
9668
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
10907
9669
  async function runSupervisor(options = {}) {
10908
9670
  const workerEnv = { ...process.env };
10909
9671
  delete workerEnv.NODE_OPTIONS;
10910
9672
  workerEnv.OURS_COWORK_DAEMON_WORKER = "1";
10911
9673
  workerEnv.OURS_COWORK_SUPERVISOR_PID = String(process.pid);
10912
- const child = fork(fileURLToPath3(import.meta.url), [], {
9674
+ const child = fork(fileURLToPath2(import.meta.url), [], {
10913
9675
  env: workerEnv,
10914
9676
  stdio: options.quiet ? ["ignore", "ignore", "ignore", "ipc"] : ["inherit", "inherit", "inherit", "ipc"],
10915
9677
  execArgv: [],
@@ -11085,7 +9847,7 @@ var init_daemon = __esm({
11085
9847
  }
11086
9848
  };
11087
9849
  invokedPath = process.argv[1] ? resolve3(process.argv[1]) : void 0;
11088
- if (invokedPath === fileURLToPath3(import.meta.url)) {
9850
+ if (invokedPath === fileURLToPath2(import.meta.url)) {
11089
9851
  void Promise.resolve().then(() => (init_daemon_process(), daemon_process_exports)).then(({ runDaemonProcess: runDaemonProcess2 }) => runDaemonProcess2()).catch((error) => {
11090
9852
  console.error(error instanceof Error ? error.stack ?? error.message : String(error));
11091
9853
  process.exitCode = 1;