negotium 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +11 -2
  2. package/dist/canonical-mcp-bridge.js +2 -3
  3. package/dist/canonical-mcp-bridge.js.map +1 -1
  4. package/dist/{chunk-gz12qcf0.js → chunk-a9y2rwxf.js} +2 -2
  5. package/dist/{chunk-gz12qcf0.js.map → chunk-a9y2rwxf.js.map} +3 -3
  6. package/dist/{chunk-j0kjscb7.js → chunk-s2gez3wg.js} +20 -2
  7. package/dist/{chunk-j0kjscb7.js.map → chunk-s2gez3wg.js.map} +2 -2
  8. package/dist/cron.js +292 -256
  9. package/dist/cron.js.map +23 -22
  10. package/dist/hosted-agent.js +3012 -17
  11. package/dist/hosted-agent.js.map +30 -3
  12. package/dist/main.js +31323 -36
  13. package/dist/main.js.map +198 -3
  14. package/dist/prompts.js +12 -9
  15. package/dist/prompts.js.map +5 -5
  16. package/dist/registry.js +10 -6
  17. package/dist/registry.js.map +3 -3
  18. package/dist/rollout.js +1 -1
  19. package/dist/runtime/cron/store.ts +24 -1
  20. package/dist/runtime/src/application/submit-user-message.ts +3 -0
  21. package/dist/runtime/src/index.ts +7 -0
  22. package/dist/runtime/src/mcp/runtime-spec.ts +3 -0
  23. package/dist/runtime/src/mcp/session-comm/server.ts +1 -1
  24. package/dist/runtime/src/platform/config.ts +2 -2
  25. package/dist/runtime/src/platform/mcp-config.ts +13 -0
  26. package/dist/runtime/src/prompts/builders.ts +20 -6
  27. package/dist/runtime/src/query/active-rooms.ts +2 -0
  28. package/dist/runtime/src/runtime/delivery-ack.ts +112 -0
  29. package/dist/runtime/src/runtime/errors.ts +12 -12
  30. package/dist/runtime/src/runtime/turn-runner.ts +12 -0
  31. package/dist/runtime/src/storage/api-messages.ts +7 -5
  32. package/dist/runtime/src/storage/api-topics.ts +5 -2
  33. package/dist/runtime/src/storage/forum-db.ts +4 -1
  34. package/dist/runtime/src/storage/runtime-turn-requests.ts +1 -0
  35. package/dist/runtime/src/storage/session-asks.ts +13 -5
  36. package/dist/runtime/src/storage/storage-contract.ts +54 -0
  37. package/dist/runtime/src/storage/storage-host.ts +89 -36
  38. package/dist/runtime/src/storage/storage-public-host.ts +16 -0
  39. package/dist/runtime/src/storage/storage-public.ts +21 -5
  40. package/dist/runtime/src/storage/token-stats.ts +1 -1
  41. package/dist/runtime/src/types/api.ts +2 -0
  42. package/dist/runtime/src/types.ts +2 -0
  43. package/dist/runtime/src/version.ts +1 -1
  44. package/dist/runtime-helpers.js +29 -18
  45. package/dist/runtime-helpers.js.map +8 -7
  46. package/dist/storage.js +260 -202
  47. package/dist/storage.js.map +11 -9
  48. package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +2 -0
  49. package/dist/types/packages/core/src/platform/mcp-config.d.ts +4 -0
  50. package/dist/types/packages/core/src/prompts/builders.d.ts +2 -0
  51. package/dist/types/packages/core/src/storage/api-messages.d.ts +6 -6
  52. package/dist/types/packages/core/src/storage/api-topics.d.ts +20 -2
  53. package/dist/types/packages/core/src/storage/forum-db.d.ts +2 -1
  54. package/dist/types/packages/core/src/storage/session-asks.d.ts +3 -2
  55. package/dist/types/packages/core/src/storage/storage-contract.d.ts +46 -0
  56. package/dist/types/packages/core/src/storage/storage-host.d.ts +12 -19
  57. package/dist/types/packages/core/src/storage/storage-public-host.d.ts +7 -0
  58. package/dist/types/packages/core/src/storage/storage-public.d.ts +4 -2
  59. package/dist/types/packages/core/src/types/api.d.ts +2 -0
  60. package/dist/types/packages/core/src/types.d.ts +2 -0
  61. package/dist/types/packages/core/src/version.d.ts +1 -1
  62. package/dist/types/packages/module-cron/src/store.d.ts +22 -2
  63. package/dist/vault.js +2 -2
  64. package/dist/vault.js.map +3 -3
  65. package/package.json +1 -1
  66. package/dist/chunk-01xcc856.js +0 -149
  67. package/dist/chunk-01xcc856.js.map +0 -10
  68. package/dist/chunk-1a6kv9vt.js +0 -118
  69. package/dist/chunk-1a6kv9vt.js.map +0 -10
  70. package/dist/chunk-1b62d730.js +0 -117
  71. package/dist/chunk-1b62d730.js.map +0 -10
  72. package/dist/chunk-1k7t7bnx.js +0 -97
  73. package/dist/chunk-1k7t7bnx.js.map +0 -10
  74. package/dist/chunk-28apmz4r.js +0 -431
  75. package/dist/chunk-28apmz4r.js.map +0 -11
  76. package/dist/chunk-2fvydfkx.js +0 -141
  77. package/dist/chunk-2fvydfkx.js.map +0 -10
  78. package/dist/chunk-41ygvg5k.js +0 -2387
  79. package/dist/chunk-41ygvg5k.js.map +0 -26
  80. package/dist/chunk-4q966ttx.js +0 -371
  81. package/dist/chunk-4q966ttx.js.map +0 -11
  82. package/dist/chunk-51rcsavw.js +0 -1712
  83. package/dist/chunk-51rcsavw.js.map +0 -18
  84. package/dist/chunk-59d0shyj.js +0 -103
  85. package/dist/chunk-59d0shyj.js.map +0 -10
  86. package/dist/chunk-5fzgvn79.js +0 -3536
  87. package/dist/chunk-5fzgvn79.js.map +0 -20
  88. package/dist/chunk-61px0717.js +0 -153
  89. package/dist/chunk-61px0717.js.map +0 -11
  90. package/dist/chunk-66y0hr8c.js +0 -2136
  91. package/dist/chunk-66y0hr8c.js.map +0 -29
  92. package/dist/chunk-69k45zg8.js +0 -250
  93. package/dist/chunk-69k45zg8.js.map +0 -11
  94. package/dist/chunk-6ah5farg.js +0 -130
  95. package/dist/chunk-6ah5farg.js.map +0 -10
  96. package/dist/chunk-6d4fmea5.js +0 -117
  97. package/dist/chunk-6d4fmea5.js.map +0 -12
  98. package/dist/chunk-8cahx7xc.js +0 -46
  99. package/dist/chunk-8cahx7xc.js.map +0 -10
  100. package/dist/chunk-8f20fk4n.js +0 -252
  101. package/dist/chunk-8f20fk4n.js.map +0 -10
  102. package/dist/chunk-91ckrvk8.js +0 -318
  103. package/dist/chunk-91ckrvk8.js.map +0 -10
  104. package/dist/chunk-93krf6ng.js +0 -3069
  105. package/dist/chunk-93krf6ng.js.map +0 -32
  106. package/dist/chunk-9qx1t77a.js +0 -214
  107. package/dist/chunk-9qx1t77a.js.map +0 -10
  108. package/dist/chunk-beqhdr9w.js +0 -589
  109. package/dist/chunk-beqhdr9w.js.map +0 -11
  110. package/dist/chunk-bftftsgq.js +0 -1637
  111. package/dist/chunk-bftftsgq.js.map +0 -15
  112. package/dist/chunk-c500xe9z.js +0 -33
  113. package/dist/chunk-c500xe9z.js.map +0 -10
  114. package/dist/chunk-cjsnkvq2.js +0 -23
  115. package/dist/chunk-cjsnkvq2.js.map +0 -10
  116. package/dist/chunk-cxa7yghg.js +0 -577
  117. package/dist/chunk-cxa7yghg.js.map +0 -14
  118. package/dist/chunk-edb07sjp.js +0 -567
  119. package/dist/chunk-edb07sjp.js.map +0 -14
  120. package/dist/chunk-f361dwba.js +0 -589
  121. package/dist/chunk-f361dwba.js.map +0 -11
  122. package/dist/chunk-f5xgprg1.js +0 -59
  123. package/dist/chunk-f5xgprg1.js.map +0 -10
  124. package/dist/chunk-ffefbd3r.js +0 -17
  125. package/dist/chunk-ffefbd3r.js.map +0 -10
  126. package/dist/chunk-fswhzdf5.js +0 -64
  127. package/dist/chunk-fswhzdf5.js.map +0 -10
  128. package/dist/chunk-g144wdrs.js +0 -2907
  129. package/dist/chunk-g144wdrs.js.map +0 -24
  130. package/dist/chunk-h980hb2n.js +0 -512
  131. package/dist/chunk-h980hb2n.js.map +0 -13
  132. package/dist/chunk-h9vbhhgr.js +0 -1337
  133. package/dist/chunk-h9vbhhgr.js.map +0 -16
  134. package/dist/chunk-hwy3n31w.js +0 -21
  135. package/dist/chunk-hwy3n31w.js.map +0 -9
  136. package/dist/chunk-jtrk0khr.js +0 -108
  137. package/dist/chunk-jtrk0khr.js.map +0 -10
  138. package/dist/chunk-jzz91jwh.js +0 -70
  139. package/dist/chunk-jzz91jwh.js.map +0 -10
  140. package/dist/chunk-m9e3tbr8.js +0 -708
  141. package/dist/chunk-m9e3tbr8.js.map +0 -14
  142. package/dist/chunk-mh5mexsf.js +0 -147
  143. package/dist/chunk-mh5mexsf.js.map +0 -11
  144. package/dist/chunk-npd8n03v.js +0 -2152
  145. package/dist/chunk-npd8n03v.js.map +0 -15
  146. package/dist/chunk-pg1azh6n.js +0 -2
  147. package/dist/chunk-pg1azh6n.js.map +0 -9
  148. package/dist/chunk-qwy57177.js +0 -237
  149. package/dist/chunk-qwy57177.js.map +0 -11
  150. package/dist/chunk-r31q21h5.js +0 -53
  151. package/dist/chunk-r31q21h5.js.map +0 -10
  152. package/dist/chunk-rbvctv1n.js +0 -366
  153. package/dist/chunk-rbvctv1n.js.map +0 -10
  154. package/dist/chunk-sa874j5c.js +0 -9
  155. package/dist/chunk-sa874j5c.js.map +0 -10
  156. package/dist/chunk-sctcs5zb.js +0 -281
  157. package/dist/chunk-sctcs5zb.js.map +0 -10
  158. package/dist/chunk-tsstrcfc.js +0 -122
  159. package/dist/chunk-tsstrcfc.js.map +0 -10
  160. package/dist/chunk-wk2qf31s.js +0 -58
  161. package/dist/chunk-wk2qf31s.js.map +0 -10
  162. package/dist/chunk-wx7ag4dd.js +0 -2936
  163. package/dist/chunk-wx7ag4dd.js.map +0 -20
  164. package/dist/chunk-znx1fpfd.js +0 -61
  165. package/dist/chunk-znx1fpfd.js.map +0 -10
@@ -1,371 +0,0 @@
1
- // @bun
2
- import"./chunk-93krf6ng.js";
3
- import"./chunk-fswhzdf5.js";
4
- import"./chunk-cxa7yghg.js";
5
- import"./chunk-59d0shyj.js";
6
- import"./chunk-rbvctv1n.js";
7
- import"./chunk-1k7t7bnx.js";
8
- import"./chunk-g144wdrs.js";
9
- import"./chunk-91ckrvk8.js";
10
- import"./chunk-66y0hr8c.js";
11
- import"./chunk-h9vbhhgr.js";
12
- import"./chunk-f361dwba.js";
13
- import"./chunk-8cahx7xc.js";
14
- import"./chunk-6d4fmea5.js";
15
- import"./chunk-61px0717.js";
16
- import"./chunk-tsstrcfc.js";
17
- import"./chunk-6ah5farg.js";
18
- import"./chunk-41ygvg5k.js";
19
- import"./chunk-m9e3tbr8.js";
20
- import"./chunk-mh5mexsf.js";
21
- import {
22
- DATA_DIR
23
- } from "./chunk-qwy57177.js";
24
- import"./chunk-j0kjscb7.js";
25
- import"./chunk-h980hb2n.js";
26
- import {
27
- logger
28
- } from "./chunk-ffefbd3r.js";
29
- import"./chunk-pg1azh6n.js";
30
- import"./chunk-1a6kv9vt.js";
31
- import"./chunk-f5xgprg1.js";
32
- import"./chunk-hwy3n31w.js";
33
-
34
- // ../../adapters/otium/src/join.ts
35
- import { createHash, randomUUID } from "crypto";
36
- import {
37
- chmodSync,
38
- closeSync,
39
- existsSync,
40
- fsyncSync,
41
- linkSync,
42
- lstatSync,
43
- mkdirSync,
44
- openSync,
45
- readFileSync,
46
- renameSync,
47
- rmSync,
48
- statSync,
49
- unlinkSync,
50
- writeFileSync
51
- } from "fs";
52
- import { dirname, resolve } from "path";
53
-
54
- // ../../adapters/otium/src/secure-transport.ts
55
- function isLoopbackHostname(hostname) {
56
- const normalized = hostname.toLowerCase();
57
- if (normalized === "localhost" || normalized === "::1" || normalized === "[::1]")
58
- return true;
59
- const octets = normalized.split(".");
60
- return octets.length === 4 && octets[0] === "127" && octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255);
61
- }
62
- function credentialTransportUrl(value, label) {
63
- let url;
64
- try {
65
- url = new URL(value);
66
- } catch {
67
- throw new Error(`${label} is not a valid URL`);
68
- }
69
- if (url.username || url.password)
70
- throw new Error(`${label} must not contain URL credentials`);
71
- return url;
72
- }
73
- function assertSecureCentralUrl(value) {
74
- const url = credentialTransportUrl(value, "Otium central URL");
75
- if (url.protocol === "https:")
76
- return;
77
- if (url.protocol === "http:" && isLoopbackHostname(url.hostname))
78
- return;
79
- throw new Error("Otium central requires HTTPS or loopback HTTP");
80
- }
81
- function assertSecureRelayUrl(value) {
82
- const url = credentialTransportUrl(value, "Otium relay URL");
83
- if (url.protocol === "https:" || url.protocol === "wss:")
84
- return;
85
- if ((url.protocol === "http:" || url.protocol === "ws:") && isLoopbackHostname(url.hostname)) {
86
- return;
87
- }
88
- throw new Error("Otium relay requires HTTPS/WSS or loopback HTTP/WS");
89
- }
90
-
91
- // ../../adapters/otium/src/join.ts
92
- function joinFilePath() {
93
- return resolve(DATA_DIR, "otium-join.json");
94
- }
95
- function isHttpUrl(value) {
96
- return /^https?:\/\//.test(value);
97
- }
98
- function isRelayUrl(value) {
99
- return /^(?:https?|wss?):\/\//.test(value);
100
- }
101
- function normalizeJoin(raw) {
102
- const central = typeof raw.central === "string" ? raw.central.trim().replace(/\/+$/, "") : "";
103
- const relay = typeof raw.relay === "string" ? raw.relay.trim().replace(/\/+$/, "") : "";
104
- const cellId = typeof raw.cellId === "string" ? raw.cellId.trim() : "";
105
- const secret = typeof raw.secret === "string" ? raw.secret.trim() : "";
106
- if (!central || !isHttpUrl(central)) {
107
- throw new Error("invite code is missing a valid http(s) central URL");
108
- }
109
- if (relay && !isRelayUrl(relay))
110
- throw new Error("invite code has an invalid relay URL");
111
- if (!cellId)
112
- throw new Error("invite code is missing cellId");
113
- if (!secret)
114
- throw new Error("invite code is missing secret");
115
- assertSecureCentralUrl(central);
116
- if (relay)
117
- assertSecureRelayUrl(relay);
118
- return {
119
- ...typeof raw.v === "number" ? { v: raw.v } : {},
120
- central,
121
- ...relay ? { relay } : {},
122
- cellId,
123
- secret
124
- };
125
- }
126
- function parseInviteCode(code) {
127
- const trimmed = code.trim();
128
- if (!trimmed)
129
- throw new Error("invite code is empty");
130
- let decoded;
131
- try {
132
- decoded = Buffer.from(trimmed, "base64url").toString("utf-8");
133
- } catch {
134
- throw new Error("invite code is not valid base64url");
135
- }
136
- let parsed;
137
- try {
138
- parsed = JSON.parse(decoded);
139
- } catch {
140
- throw new Error("invite code does not decode to JSON");
141
- }
142
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
143
- throw new Error("invite code does not decode to a JSON object");
144
- }
145
- return normalizeJoin(parsed);
146
- }
147
- var JOIN_LOCK_STALE_MS = 30000;
148
- function joinLockPath() {
149
- return resolve(DATA_DIR, ".otium-join.lock");
150
- }
151
- function processIsAlive(pid) {
152
- if (!Number.isSafeInteger(pid) || pid <= 0)
153
- return false;
154
- try {
155
- process.kill(pid, 0);
156
- return true;
157
- } catch (error) {
158
- return error.code !== "ESRCH";
159
- }
160
- }
161
- function withJoinCredentialLock(operation) {
162
- const lockPath = joinLockPath();
163
- const ownerPath = resolve(lockPath, "owner.json");
164
- const owner = { pid: process.pid, token: randomUUID() };
165
- mkdirSync(dirname(lockPath), { recursive: true });
166
- for (let attempt = 0;; attempt += 1) {
167
- let created = false;
168
- try {
169
- mkdirSync(lockPath, { mode: 448 });
170
- created = true;
171
- writeFileSync(ownerPath, `${JSON.stringify(owner)}
172
- `, { mode: 384 });
173
- const ownerFd = openSync(ownerPath, "r");
174
- try {
175
- fsyncSync(ownerFd);
176
- } finally {
177
- closeSync(ownerFd);
178
- }
179
- break;
180
- } catch (error) {
181
- if (created) {
182
- rmSync(lockPath, { recursive: true, force: true });
183
- throw error;
184
- }
185
- if (error.code !== "EEXIST")
186
- throw error;
187
- let current = null;
188
- try {
189
- current = JSON.parse(readFileSync(ownerPath, "utf8"));
190
- } catch {}
191
- let ageMs;
192
- try {
193
- ageMs = Date.now() - statSync(lockPath).mtimeMs;
194
- } catch (statError) {
195
- if (statError.code === "ENOENT")
196
- continue;
197
- throw statError;
198
- }
199
- if (current && processIsAlive(current.pid) || !current && ageMs <= JOIN_LOCK_STALE_MS) {
200
- throw new Error(`another Otium join credential operation is in progress at ${lockPath}`);
201
- }
202
- if (attempt > 0) {
203
- throw new Error(`could not recover stale Otium join credential lock at ${lockPath}`);
204
- }
205
- const stalePath = `${lockPath}.stale.${process.pid}.${randomUUID()}`;
206
- try {
207
- renameSync(lockPath, stalePath);
208
- rmSync(stalePath, { recursive: true, force: true });
209
- } catch (staleError) {
210
- if (staleError.code !== "ENOENT")
211
- throw staleError;
212
- }
213
- }
214
- }
215
- try {
216
- return operation();
217
- } finally {
218
- try {
219
- const current = JSON.parse(readFileSync(ownerPath, "utf8"));
220
- if (current.pid === owner.pid && current.token === owner.token) {
221
- rmSync(lockPath, { recursive: true, force: true });
222
- }
223
- } catch {}
224
- }
225
- }
226
- function joinsEqual(left, right) {
227
- return left.central === right.central && left.relay === right.relay && left.cellId === right.cellId && left.secret === right.secret;
228
- }
229
- function normalizedJoin(join) {
230
- return normalizeJoin({
231
- v: join.v,
232
- central: join.central,
233
- relay: join.relay,
234
- cellId: join.cellId,
235
- secret: join.secret
236
- });
237
- }
238
- function joinCredentialDigest(join) {
239
- return createHash("sha256").update(JSON.stringify(normalizedJoin(join))).digest("base64url");
240
- }
241
- function readPersistedJoin(path = joinFilePath()) {
242
- if (!existsSync(path))
243
- return null;
244
- const parsed = JSON.parse(readFileSync(path, "utf-8"));
245
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
246
- throw new Error("persisted join credentials are not a JSON object");
247
- }
248
- return normalizeJoin(parsed);
249
- }
250
- function isJoinPersisted(join) {
251
- try {
252
- const persisted = readPersistedJoin();
253
- return persisted !== null && joinsEqual(persisted, normalizedJoin(join));
254
- } catch {
255
- return false;
256
- }
257
- }
258
- function saveJoinWhileLocked(join, options = {}) {
259
- const path = joinFilePath();
260
- const directory = dirname(path);
261
- const normalized = normalizedJoin(join);
262
- mkdirSync(directory, { recursive: true });
263
- if (existsSync(path)) {
264
- if (lstatSync(path).isSymbolicLink()) {
265
- throw new Error(`refusing to replace symlinked Otium join file at ${path}`);
266
- }
267
- let existing = null;
268
- try {
269
- existing = readPersistedJoin(path);
270
- } catch (error) {
271
- if (!options.replaceExisting) {
272
- throw new Error(`existing Otium join file at ${path} is invalid; pass --replace to replace it`, { cause: error });
273
- }
274
- }
275
- if (existing && joinsEqual(existing, normalized)) {
276
- chmodSync(path, 384);
277
- const fileFd = openSync(path, "r");
278
- try {
279
- fsyncSync(fileFd);
280
- } finally {
281
- closeSync(fileFd);
282
- }
283
- const directoryFd = openSync(directory, "r");
284
- try {
285
- fsyncSync(directoryFd);
286
- } finally {
287
- closeSync(directoryFd);
288
- }
289
- return path;
290
- }
291
- if (!options.replaceExisting) {
292
- throw new Error(`this node is already joined${existing ? ` as ${existing.cellId}` : " with an invalid join file"}; pass --replace to replace its credentials`);
293
- }
294
- }
295
- const temporaryPath = resolve(directory, `.otium-join.json.${process.pid}.${randomUUID()}.tmp`);
296
- let fd;
297
- try {
298
- fd = openSync(temporaryPath, "wx", 384);
299
- writeFileSync(fd, `${JSON.stringify(normalized, null, 2)}
300
- `, "utf8");
301
- fsyncSync(fd);
302
- closeSync(fd);
303
- fd = undefined;
304
- if (options.replaceExisting) {
305
- renameSync(temporaryPath, path);
306
- } else {
307
- linkSync(temporaryPath, path);
308
- unlinkSync(temporaryPath);
309
- }
310
- chmodSync(path, 384);
311
- const directoryFd = openSync(directory, "r");
312
- try {
313
- fsyncSync(directoryFd);
314
- } finally {
315
- closeSync(directoryFd);
316
- }
317
- } catch (error) {
318
- if (fd !== undefined)
319
- closeSync(fd);
320
- if (existsSync(temporaryPath))
321
- unlinkSync(temporaryPath);
322
- throw error;
323
- }
324
- return path;
325
- }
326
- function saveJoin(join, options = {}) {
327
- return withJoinCredentialLock(() => saveJoinWhileLocked(join, options));
328
- }
329
- function loadJoin() {
330
- const central = process.env.OTIUM_CENTRAL_URL?.trim();
331
- const cellId = process.env.OTIUM_CELL_ID?.trim();
332
- const secret = process.env.OTIUM_CELL_SECRET?.trim();
333
- const relay = process.env.OTIUM_RELAY_URL?.trim();
334
- if (central && cellId && secret) {
335
- try {
336
- return normalizeJoin({ central, relay, cellId, secret });
337
- } catch (err) {
338
- logger.warn({ err }, "otium: invalid OTIUM_CENTRAL_URL/OTIUM_CELL_ID/OTIUM_CELL_SECRET env");
339
- return null;
340
- }
341
- }
342
- if (central || cellId || secret) {
343
- logger.warn("otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together \u2014 ignoring partial env");
344
- }
345
- const path = joinFilePath();
346
- if (!existsSync(path))
347
- return null;
348
- try {
349
- const parsed = JSON.parse(readFileSync(path, "utf-8"));
350
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
351
- return null;
352
- return normalizeJoin(parsed);
353
- } catch (err) {
354
- logger.warn({ err, path }, "otium: failed to read join file");
355
- return null;
356
- }
357
- }
358
- export {
359
- withJoinCredentialLock,
360
- saveJoinWhileLocked,
361
- saveJoin,
362
- parseInviteCode,
363
- loadJoin,
364
- joinFilePath,
365
- joinCredentialDigest,
366
- isJoinPersisted
367
- };
368
-
369
- export { assertSecureCentralUrl, assertSecureRelayUrl, joinFilePath, parseInviteCode, withJoinCredentialLock, joinCredentialDigest, isJoinPersisted, saveJoinWhileLocked, saveJoin, loadJoin };
370
-
371
- //# debugId=4EF2651906A11C3664756E2164756E21
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../adapters/otium/src/join.ts", "../../../adapters/otium/src/secure-transport.ts"],
4
- "sourcesContent": [
5
- "/**\n * otium node join credentials — the v0 \"invite code\" is a base64url JSON\n * bundle `{ v, central, cellId, secret }` produced by the hub operator\n * (scripts/otium-experiment/hub-setup.ts). `negotium otium join <code>` decodes it\n * and persists it under `${DATA_DIR}/otium-join.json` (0600); `serve` mounts\n * the peer routes whenever that file (or the env triple) is present.\n */\n\nimport { createHash, randomUUID } from \"node:crypto\";\nimport {\n chmodSync,\n closeSync,\n existsSync,\n fsyncSync,\n linkSync,\n lstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DATA_DIR, logger } from \"@negotium/core\";\nimport { assertSecureCentralUrl, assertSecureRelayUrl } from \"@/secure-transport\";\n\nexport interface OtiumJoin {\n /** Invite code format version (v0 bundles carry 1). */\n v?: number;\n /** central-api origin, e.g. \"http://127.0.0.1:4600\". */\n central: string;\n /** Optional relay origin used for outbound NAT traversal. */\n relay?: string;\n /** This node's runtime cell id (cell_…). */\n cellId: string;\n /** This node's runtime cell secret (rcs_…) — never leaves this process. */\n secret: string;\n}\n\nexport function joinFilePath(): string {\n return resolve(DATA_DIR, \"otium-join.json\");\n}\n\nfunction isHttpUrl(value: string): boolean {\n return /^https?:\\/\\//.test(value);\n}\n\nfunction isRelayUrl(value: string): boolean {\n return /^(?:https?|wss?):\\/\\//.test(value);\n}\n\nfunction normalizeJoin(raw: Record<string, unknown>): OtiumJoin {\n const central = typeof raw.central === \"string\" ? raw.central.trim().replace(/\\/+$/, \"\") : \"\";\n const relay = typeof raw.relay === \"string\" ? raw.relay.trim().replace(/\\/+$/, \"\") : \"\";\n const cellId = typeof raw.cellId === \"string\" ? raw.cellId.trim() : \"\";\n const secret = typeof raw.secret === \"string\" ? raw.secret.trim() : \"\";\n if (!central || !isHttpUrl(central)) {\n throw new Error(\"invite code is missing a valid http(s) central URL\");\n }\n if (relay && !isRelayUrl(relay)) throw new Error(\"invite code has an invalid relay URL\");\n if (!cellId) throw new Error(\"invite code is missing cellId\");\n if (!secret) throw new Error(\"invite code is missing secret\");\n assertSecureCentralUrl(central);\n if (relay) assertSecureRelayUrl(relay);\n return {\n ...(typeof raw.v === \"number\" ? { v: raw.v } : {}),\n central,\n ...(relay ? { relay } : {}),\n cellId,\n secret,\n };\n}\n\n/** Decode a v0 invite code: base64url(JSON {v, central, cellId, secret}). */\nexport function parseInviteCode(code: string): OtiumJoin {\n const trimmed = code.trim();\n if (!trimmed) throw new Error(\"invite code is empty\");\n let decoded: string;\n try {\n decoded = Buffer.from(trimmed, \"base64url\").toString(\"utf-8\");\n } catch {\n throw new Error(\"invite code is not valid base64url\");\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n throw new Error(\"invite code does not decode to JSON\");\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\"invite code does not decode to a JSON object\");\n }\n return normalizeJoin(parsed as Record<string, unknown>);\n}\n\nexport interface SaveJoinOptions {\n /** Explicitly replace credentials for a different cell/workspace. */\n replaceExisting?: boolean;\n}\n\ninterface JoinLockOwner {\n pid: number;\n token: string;\n}\n\nconst JOIN_LOCK_STALE_MS = 30_000;\n\nfunction joinLockPath(): string {\n return resolve(DATA_DIR, \".otium-join.lock\");\n}\n\nfunction processIsAlive(pid: number): boolean {\n if (!Number.isSafeInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== \"ESRCH\";\n }\n}\n\n/** @internal Cross-process critical section for join/enrollment credential state. */\nexport function withJoinCredentialLock<T>(operation: () => T): T {\n const lockPath = joinLockPath();\n const ownerPath = resolve(lockPath, \"owner.json\");\n const owner: JoinLockOwner = { pid: process.pid, token: randomUUID() };\n mkdirSync(dirname(lockPath), { recursive: true });\n\n for (let attempt = 0; ; attempt += 1) {\n let created = false;\n try {\n mkdirSync(lockPath, { mode: 0o700 });\n created = true;\n writeFileSync(ownerPath, `${JSON.stringify(owner)}\\n`, { mode: 0o600 });\n const ownerFd = openSync(ownerPath, \"r\");\n try {\n fsyncSync(ownerFd);\n } finally {\n closeSync(ownerFd);\n }\n break;\n } catch (error) {\n if (created) {\n rmSync(lockPath, { recursive: true, force: true });\n throw error;\n }\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n let current: JoinLockOwner | null = null;\n try {\n current = JSON.parse(readFileSync(ownerPath, \"utf8\")) as JoinLockOwner;\n } catch {\n // A live acquirer may be between mkdir and owner write. Only recover an\n // ownerless lock after it has been stale long enough.\n }\n let ageMs: number;\n try {\n ageMs = Date.now() - statSync(lockPath).mtimeMs;\n } catch (statError) {\n if ((statError as NodeJS.ErrnoException).code === \"ENOENT\") continue;\n throw statError;\n }\n if ((current && processIsAlive(current.pid)) || (!current && ageMs <= JOIN_LOCK_STALE_MS)) {\n throw new Error(`another Otium join credential operation is in progress at ${lockPath}`);\n }\n if (attempt > 0) {\n throw new Error(`could not recover stale Otium join credential lock at ${lockPath}`);\n }\n const stalePath = `${lockPath}.stale.${process.pid}.${randomUUID()}`;\n try {\n renameSync(lockPath, stalePath);\n rmSync(stalePath, { recursive: true, force: true });\n } catch (staleError) {\n if ((staleError as NodeJS.ErrnoException).code !== \"ENOENT\") throw staleError;\n }\n }\n }\n\n try {\n return operation();\n } finally {\n try {\n const current = JSON.parse(readFileSync(ownerPath, \"utf8\")) as JoinLockOwner;\n if (current.pid === owner.pid && current.token === owner.token) {\n rmSync(lockPath, { recursive: true, force: true });\n }\n } catch {\n // Never remove a lock whose ownership can no longer be proven.\n }\n }\n}\n\nfunction joinsEqual(left: OtiumJoin, right: OtiumJoin): boolean {\n return (\n left.central === right.central &&\n left.relay === right.relay &&\n left.cellId === right.cellId &&\n left.secret === right.secret\n );\n}\n\nfunction normalizedJoin(join: OtiumJoin): OtiumJoin {\n return normalizeJoin({\n v: join.v,\n central: join.central,\n relay: join.relay,\n cellId: join.cellId,\n secret: join.secret,\n });\n}\n\n/** Stable digest used to bind pending enrollment material to one exact join. */\nexport function joinCredentialDigest(join: OtiumJoin): string {\n return createHash(\"sha256\")\n .update(JSON.stringify(normalizedJoin(join)))\n .digest(\"base64url\");\n}\n\nfunction readPersistedJoin(path = joinFilePath()): OtiumJoin | null {\n if (!existsSync(path)) return null;\n const parsed = JSON.parse(readFileSync(path, \"utf-8\"));\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\"persisted join credentials are not a JSON object\");\n }\n return normalizeJoin(parsed as Record<string, unknown>);\n}\n\n/** True only when the join file on disk contains these exact credentials. */\nexport function isJoinPersisted(join: OtiumJoin): boolean {\n try {\n const persisted = readPersistedJoin();\n return persisted !== null && joinsEqual(persisted, normalizedJoin(join));\n } catch {\n return false;\n }\n}\n\n/**\n * Persist join credentials atomically (0600 — the cell secret is a bearer\n * credential). Re-saving the same join is idempotent. Replacing a different\n * join must be explicit so an enrollment retry cannot silently detach a node.\n */\n/** @internal Caller must hold withJoinCredentialLock. */\nexport function saveJoinWhileLocked(join: OtiumJoin, options: SaveJoinOptions = {}): string {\n const path = joinFilePath();\n const directory = dirname(path);\n const normalized = normalizedJoin(join);\n mkdirSync(directory, { recursive: true });\n\n if (existsSync(path)) {\n if (lstatSync(path).isSymbolicLink()) {\n throw new Error(`refusing to replace symlinked Otium join file at ${path}`);\n }\n let existing: OtiumJoin | null = null;\n try {\n existing = readPersistedJoin(path)!;\n } catch (error) {\n if (!options.replaceExisting) {\n throw new Error(\n `existing Otium join file at ${path} is invalid; pass --replace to replace it`,\n { cause: error },\n );\n }\n }\n if (existing && joinsEqual(existing, normalized)) {\n chmodSync(path, 0o600);\n const fileFd = openSync(path, \"r\");\n try {\n fsyncSync(fileFd);\n } finally {\n closeSync(fileFd);\n }\n const directoryFd = openSync(directory, \"r\");\n try {\n fsyncSync(directoryFd);\n } finally {\n closeSync(directoryFd);\n }\n return path;\n }\n if (!options.replaceExisting) {\n throw new Error(\n `this node is already joined${existing ? ` as ${existing.cellId}` : \" with an invalid join file\"}; pass --replace to replace its credentials`,\n );\n }\n }\n\n const temporaryPath = resolve(directory, `.otium-join.json.${process.pid}.${randomUUID()}.tmp`);\n let fd: number | undefined;\n try {\n fd = openSync(temporaryPath, \"wx\", 0o600);\n writeFileSync(fd, `${JSON.stringify(normalized, null, 2)}\\n`, \"utf8\");\n fsyncSync(fd);\n closeSync(fd);\n fd = undefined;\n if (options.replaceExisting) {\n renameSync(temporaryPath, path);\n } else {\n // Unlike rename(), link fails when another process won the initial join\n // race, so a non-explicit save can never overwrite its credentials.\n linkSync(temporaryPath, path);\n unlinkSync(temporaryPath);\n }\n chmodSync(path, 0o600);\n const directoryFd = openSync(directory, \"r\");\n try {\n fsyncSync(directoryFd);\n } finally {\n closeSync(directoryFd);\n }\n } catch (error) {\n if (fd !== undefined) closeSync(fd);\n if (existsSync(temporaryPath)) unlinkSync(temporaryPath);\n throw error;\n }\n return path;\n}\n\nexport function saveJoin(join: OtiumJoin, options: SaveJoinOptions = {}): string {\n return withJoinCredentialLock(() => saveJoinWhileLocked(join, options));\n}\n\n/**\n * Load join credentials. Env triple (OTIUM_CENTRAL_URL / OTIUM_CELL_ID /\n * OTIUM_CELL_SECRET) wins when all three are set — same values `negotium\n * join` persists, useful for tests and multi-node-on-one-box. Fail-closed:\n * a partial env triple or a corrupt file yields null (worker stays off).\n */\nexport function loadJoin(): OtiumJoin | null {\n const central = process.env.OTIUM_CENTRAL_URL?.trim();\n const cellId = process.env.OTIUM_CELL_ID?.trim();\n const secret = process.env.OTIUM_CELL_SECRET?.trim();\n const relay = process.env.OTIUM_RELAY_URL?.trim();\n if (central && cellId && secret) {\n try {\n return normalizeJoin({ central, relay, cellId, secret });\n } catch (err) {\n logger.warn({ err }, \"otium: invalid OTIUM_CENTRAL_URL/OTIUM_CELL_ID/OTIUM_CELL_SECRET env\");\n return null;\n }\n }\n if (central || cellId || secret) {\n logger.warn(\n \"otium: OTIUM_CENTRAL_URL, OTIUM_CELL_ID, OTIUM_CELL_SECRET must be set together — ignoring partial env\",\n );\n }\n const path = joinFilePath();\n if (!existsSync(path)) return null;\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf-8\"));\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n return normalizeJoin(parsed as Record<string, unknown>);\n } catch (err) {\n logger.warn({ err, path }, \"otium: failed to read join file\");\n return null;\n }\n}\n",
6
- "function isLoopbackHostname(hostname: string): boolean {\n const normalized = hostname.toLowerCase();\n if (normalized === \"localhost\" || normalized === \"::1\" || normalized === \"[::1]\") return true;\n const octets = normalized.split(\".\");\n return (\n octets.length === 4 &&\n octets[0] === \"127\" &&\n octets.every((octet) => /^\\d{1,3}$/.test(octet) && Number(octet) <= 255)\n );\n}\n\nfunction credentialTransportUrl(value: string, label: string): URL {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(`${label} is not a valid URL`);\n }\n if (url.username || url.password) throw new Error(`${label} must not contain URL credentials`);\n return url;\n}\n\nexport function assertSecureCentralUrl(value: string): void {\n const url = credentialTransportUrl(value, \"Otium central URL\");\n if (url.protocol === \"https:\") return;\n if (url.protocol === \"http:\" && isLoopbackHostname(url.hostname)) return;\n throw new Error(\"Otium central requires HTTPS or loopback HTTP\");\n}\n\nexport function assertSecureRelayUrl(value: string): void {\n const url = credentialTransportUrl(value, \"Otium relay URL\");\n if (url.protocol === \"https:\" || url.protocol === \"wss:\") return;\n if ((url.protocol === \"http:\" || url.protocol === \"ws:\") && isLoopbackHostname(url.hostname)) {\n return;\n }\n throw new Error(\"Otium relay requires HTTPS/WSS or loopback HTTP/WS\");\n}\n"
7
- ],
8
- "mappings": "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",
9
- "debugId": "4EF2651906A11C3664756E2164756E21",
10
- "names": []
11
- }