jishushell 0.4.24 → 0.4.30

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 (167) hide show
  1. package/INSTALL-NOTICE +11 -0
  2. package/apps/browserless-chromium-container.yaml +78 -0
  3. package/apps/hermes-container.yaml +36 -2
  4. package/apps/ollama-binary.yaml +91 -90
  5. package/apps/ollama-cpu-container.yaml +8 -1
  6. package/apps/ollama-with-hollama-binary.yaml +91 -90
  7. package/apps/openclaw-binary.yaml +30 -1
  8. package/apps/openclaw-container.yaml +37 -2
  9. package/apps/openclaw-with-ollama-container.yaml +11 -2
  10. package/apps/openclaw-with-searxng-container.yaml +22 -2
  11. package/apps/openwebui-container.yaml +45 -1
  12. package/apps/playwright-container.yaml +7 -1
  13. package/apps/searxng-container.yaml +54 -4
  14. package/dist/cli/app.js +79 -9
  15. package/dist/cli/app.js.map +1 -1
  16. package/dist/cli/doctor.d.ts +12 -12
  17. package/dist/cli/doctor.js +242 -55
  18. package/dist/cli/doctor.js.map +1 -1
  19. package/dist/cli/llm.d.ts +4 -3
  20. package/dist/cli/llm.js +4 -3
  21. package/dist/cli/llm.js.map +1 -1
  22. package/dist/cli/panel.d.ts +6 -5
  23. package/dist/cli/panel.js +10 -9
  24. package/dist/cli/panel.js.map +1 -1
  25. package/dist/control.d.ts +7 -6
  26. package/dist/control.js +7 -6
  27. package/dist/control.js.map +1 -1
  28. package/dist/routes/agent-apps.d.ts +1 -1
  29. package/dist/routes/agent-apps.js +1 -1
  30. package/dist/routes/apps.js +44 -11
  31. package/dist/routes/apps.js.map +1 -1
  32. package/dist/routes/auth.js +3 -0
  33. package/dist/routes/auth.js.map +1 -1
  34. package/dist/routes/instances.js +787 -16
  35. package/dist/routes/instances.js.map +1 -1
  36. package/dist/routes/llm.js +24 -35
  37. package/dist/routes/llm.js.map +1 -1
  38. package/dist/routes/setup.js +1 -1
  39. package/dist/routes/setup.js.map +1 -1
  40. package/dist/server.d.ts +9 -0
  41. package/dist/server.js +410 -17
  42. package/dist/server.js.map +1 -1
  43. package/dist/services/agent-apps/catalog.js +4 -3
  44. package/dist/services/agent-apps/catalog.js.map +1 -1
  45. package/dist/services/agent-apps/index.d.ts +1 -1
  46. package/dist/services/agent-apps/index.js +1 -1
  47. package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
  48. package/dist/services/agent-apps/installers/adapter.js +1 -1
  49. package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
  50. package/dist/services/agent-apps/installers/shell-script.js +3 -3
  51. package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
  52. package/dist/services/agent-apps/types.d.ts +2 -2
  53. package/dist/services/agent-apps/types.js +1 -1
  54. package/dist/services/app/app-manager.d.ts +24 -1
  55. package/dist/services/app/app-manager.js +664 -116
  56. package/dist/services/app/app-manager.js.map +1 -1
  57. package/dist/services/app/hermes-agent-manager.js +6 -4
  58. package/dist/services/app/hermes-agent-manager.js.map +1 -1
  59. package/dist/services/app/provide-resolver.d.ts +29 -0
  60. package/dist/services/app/provide-resolver.js +112 -0
  61. package/dist/services/app/provide-resolver.js.map +1 -0
  62. package/dist/services/capability-endpoint-validator.d.ts +41 -0
  63. package/dist/services/capability-endpoint-validator.js +104 -0
  64. package/dist/services/capability-endpoint-validator.js.map +1 -0
  65. package/dist/services/capability-health.d.ts +16 -0
  66. package/dist/services/capability-health.js +121 -0
  67. package/dist/services/capability-health.js.map +1 -0
  68. package/dist/services/capability-registry.d.ts +106 -0
  69. package/dist/services/capability-registry.js +313 -0
  70. package/dist/services/capability-registry.js.map +1 -0
  71. package/dist/services/connection-apply.d.ts +89 -0
  72. package/dist/services/connection-apply.js +421 -0
  73. package/dist/services/connection-apply.js.map +1 -0
  74. package/dist/services/connection-resolver.d.ts +65 -0
  75. package/dist/services/connection-resolver.js +281 -0
  76. package/dist/services/connection-resolver.js.map +1 -0
  77. package/dist/services/connection-transactor.d.ts +37 -0
  78. package/dist/services/connection-transactor.js +341 -0
  79. package/dist/services/connection-transactor.js.map +1 -0
  80. package/dist/services/instance-manager.d.ts +13 -0
  81. package/dist/services/instance-manager.js +137 -23
  82. package/dist/services/instance-manager.js.map +1 -1
  83. package/dist/services/llm-proxy/index.d.ts +16 -2
  84. package/dist/services/llm-proxy/index.js +48 -44
  85. package/dist/services/llm-proxy/index.js.map +1 -1
  86. package/dist/services/llm-proxy/probe.d.ts +6 -0
  87. package/dist/services/llm-proxy/probe.js +85 -0
  88. package/dist/services/llm-proxy/probe.js.map +1 -0
  89. package/dist/services/llm-proxy/ssrf.d.ts +1 -0
  90. package/dist/services/llm-proxy/ssrf.js +18 -7
  91. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  92. package/dist/services/nomad-manager.js +375 -16
  93. package/dist/services/nomad-manager.js.map +1 -1
  94. package/dist/services/process-manager.js +1 -1
  95. package/dist/services/process-manager.js.map +1 -1
  96. package/dist/services/runtime/adapters/hermes.d.ts +30 -1
  97. package/dist/services/runtime/adapters/hermes.js +218 -5
  98. package/dist/services/runtime/adapters/hermes.js.map +1 -1
  99. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
  100. package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
  101. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
  102. package/dist/services/runtime/adapters/openclaw.d.ts +87 -0
  103. package/dist/services/runtime/adapters/openclaw.js +250 -2
  104. package/dist/services/runtime/adapters/openclaw.js.map +1 -1
  105. package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
  106. package/dist/services/runtime/mcp-shims/firewall.js +129 -0
  107. package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
  108. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
  109. package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
  110. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
  111. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
  112. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
  113. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
  114. package/dist/services/runtime/migrations.d.ts +8 -0
  115. package/dist/services/runtime/migrations.js +100 -0
  116. package/dist/services/runtime/migrations.js.map +1 -1
  117. package/dist/services/runtime/types.d.ts +15 -0
  118. package/dist/services/setup-manager.js +6 -6
  119. package/dist/services/setup-manager.js.map +1 -1
  120. package/dist/services/suggestions.d.ts +27 -0
  121. package/dist/services/suggestions.js +133 -0
  122. package/dist/services/suggestions.js.map +1 -0
  123. package/dist/services/task-registry.js +4 -2
  124. package/dist/services/task-registry.js.map +1 -1
  125. package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
  126. package/dist/services/telemetry/device-fingerprint.js +1 -1
  127. package/dist/services/types-shim.d.ts +16 -0
  128. package/dist/services/types-shim.js +2 -0
  129. package/dist/services/types-shim.js.map +1 -0
  130. package/dist/types.d.ts +171 -1
  131. package/dist/utils/instance-lock.d.ts +22 -0
  132. package/dist/utils/instance-lock.js +48 -0
  133. package/dist/utils/instance-lock.js.map +1 -0
  134. package/dist/utils/safe-json.js +55 -22
  135. package/dist/utils/safe-json.js.map +1 -1
  136. package/install/jishu-install.sh +323 -27
  137. package/install/jishu-uninstall.sh +353 -20
  138. package/package.json +3 -1
  139. package/public/assets/Dashboard-rkWp-CXd.js +1 -0
  140. package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-_GHoklgo.js} +1 -1
  141. package/public/assets/HermesConfigForm-anDnwUp_.js +4 -0
  142. package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-ZU9_-hDr.js} +1 -1
  143. package/public/assets/InstanceDetail-CN0FH1aw.js +92 -0
  144. package/public/assets/{Login-BWsZH2mu.js → Login-BItXqYAJ.js} +1 -1
  145. package/public/assets/NewInstance-BousE6kY.js +1 -0
  146. package/public/assets/ProviderRecommendations-DFYj7Fb6.js +1 -0
  147. package/public/assets/Settings-Bttc6QmM.js +1 -0
  148. package/public/assets/Setup-Bsxx1zgj.js +1 -0
  149. package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-DPZpAKgO.js} +2 -2
  150. package/public/assets/index-8xZy1z5k.css +1 -0
  151. package/public/assets/index-Dw3HhUYE.js +19 -0
  152. package/public/assets/providers-DtNXh9JD.js +1 -0
  153. package/public/assets/registry-5s2UB6is.js +2 -0
  154. package/public/index.html +2 -2
  155. package/scripts/check-app-spec.mjs +443 -0
  156. package/scripts/check-i18n.mjs +154 -0
  157. package/scripts/run.sh +4 -4
  158. package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
  159. package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
  160. package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
  161. package/public/assets/NewInstance-BCIrAd86.js +0 -1
  162. package/public/assets/Settings-xkDcduFz.js +0 -1
  163. package/public/assets/Setup-Cfuwj4gV.js +0 -1
  164. package/public/assets/index-CPhVFEsx.css +0 -1
  165. package/public/assets/index-DQsM6Joa.js +0 -19
  166. package/public/assets/providers-V-vwrExZ.js +0 -1
  167. package/public/assets/registry-B4UFJdpA.js +0 -2
package/dist/types.d.ts CHANGED
@@ -26,6 +26,12 @@ export interface ServiceResult {
26
26
  eval_id?: string;
27
27
  /** Port allocation metadata from adapters (process-manager / nomad-manager). */
28
28
  port_allocation?: unknown;
29
+ /**
30
+ * Stable error code for callers that prefer machine-readable tags over
31
+ * regex-matching localized error messages. Populated by PR 3
32
+ * resolveConnections (`MISSING_REQUIRED_CONNECTION` / `AMBIGUOUS_CONNECTION` / etc).
33
+ */
34
+ code?: string;
29
35
  }
30
36
  export interface ExecResult {
31
37
  stdout: string;
@@ -157,6 +163,16 @@ export interface AppTask {
157
163
  after?: string[];
158
164
  /** Volumes: string format "src:dest[:ro]" or object { source, target, readonly? } */
159
165
  volumes?: Array<string | AppTaskVolume>;
166
+ /**
167
+ * Container task user spec. Format: "uid:gid" (numeric) or "uid". Special
168
+ * value "host" resolves to the panel process's `process.getuid()`:`getgid()`
169
+ * at job-build time — keeps bind-mounted data dirs writable by the panel
170
+ * user (typically `pi`) without forcing the container to run as root and
171
+ * without needing `chown`/`CAP_DAC_OVERRIDE` gymnastics. Mirrors how the
172
+ * hermes / openclaw adapters set `User`. When omitted on container tasks
173
+ * the unified builder falls back to "host" by default.
174
+ */
175
+ user?: string;
160
176
  }
161
177
  export interface AppTerminalCommandPreset {
162
178
  cmd: string;
@@ -173,25 +189,165 @@ export interface AppTerminalConfig {
173
189
  timeout_ms?: number;
174
190
  commands?: Record<string, Array<string | AppTerminalCommandPreset>>;
175
191
  }
192
+ /**
193
+ * Health-probe spec attached to a provide. When omitted, the prober derives
194
+ * a default strategy from `protocol` (§5.6: http→HEAD, ws/tcp→connect,
195
+ * mcp→initialize, terminal→no-op).
196
+ */
197
+ export interface ProvideHealthSpec {
198
+ kind?: "http" | "tcp" | "mcp" | "none";
199
+ /** HTTP path to probe (defaults to "/"). Ignored for non-http kinds. */
200
+ path?: string;
201
+ /** Custom port to probe; defaults to the provide's resolved hostPort. */
202
+ port?: number;
203
+ }
176
204
  export interface AppProvide {
177
205
  capability: string;
206
+ /** Task that exposes this capability — required for resolveProvideEndpoint
207
+ * (§5.5) to pick the right port when an app has multiple service tasks. */
208
+ task?: string;
209
+ /** Optional port-name disambiguator when multiple ports share a number. */
210
+ portName?: string;
178
211
  port?: number;
179
212
  path?: string;
180
213
  url?: string;
181
- protocol?: "http" | "https" | "tcp" | "udp" | (string & {});
214
+ protocol?: "http" | "https" | "tcp" | "udp" | "ws" | "wss" | "sse" | "mcp" | "terminal" | (string & {});
182
215
  visibility?: string;
183
216
  description?: string;
184
217
  terminal?: AppTerminalConfig;
218
+ /** Optional explicit health probe; defaults derived from `protocol` (§5.6). */
219
+ health?: ProvideHealthSpec;
220
+ /**
221
+ * §17 (PR 8) — canonical MCP tool surface. Filled when this capability
222
+ * is meant to be re-exposed to an LLM agent through an MCP server. The
223
+ * jishushell MCP firewall uses these fields verbatim — upstream
224
+ * `tools/list` descriptions are discarded — to prevent third-party
225
+ * packages from prompt-injecting the LLM via their own tool wording.
226
+ * When absent, adapters fall back to the legacy per-capability path
227
+ * (PR 7 in-tree shim or direct `npx <pkg>` wiring).
228
+ */
229
+ tool_schema?: ToolSchema;
230
+ /**
231
+ * §6 — describes how a consumer that binds this `provides[]` capability
232
+ * should authenticate when calling the endpoint. Optional. Absent = no auth.
233
+ */
234
+ auth?: AppProvideAuth;
235
+ }
236
+ /**
237
+ * §6 — describes how a consumer that binds this `provides[]` capability
238
+ * should authenticate when calling the endpoint. Optional. Absent = no auth.
239
+ *
240
+ * `tokenSource` is a small DSL the apply hook resolves at apply time:
241
+ * "instance.env.<NAME>" — read from the provider instance's runtime env
242
+ * "instance.config.<path>" — read from the provider's openclaw.json (json-pointer)
243
+ * "proxy.token" — read from the embedded llm-proxy's per-instance token
244
+ *
245
+ * Validation rules (PR A): `tokenSource` is required when kind !== "none".
246
+ * At the type level we keep it optional because the validator catches violations.
247
+ */
248
+ export interface AppProvideAuth {
249
+ kind: "none" | "bearer" | "header" | "query";
250
+ tokenSource?: string;
251
+ headerName?: string;
252
+ tokenPrefix?: string;
253
+ }
254
+ /**
255
+ * §17.3.1 — what the LLM sees for an MCP-class capability.
256
+ *
257
+ * `name` / `description` / `parameters` are forwarded verbatim by the
258
+ * MCP firewall's `tools/list` response, replacing whatever the upstream
259
+ * package would have advertised. `upstream` tells the firewall how to
260
+ * spawn the actual implementation.
261
+ */
262
+ export interface ToolSchema {
263
+ /** MCP tool name. Must match `^[a-z][a-z0-9_]*$`. */
264
+ name: string;
265
+ /**
266
+ * Human-language description shown to the LLM. Keep minimal —
267
+ * "news / recent events / today" style suggestions invite query
268
+ * rewriting in some models (see §17.1).
269
+ */
270
+ description: string;
271
+ /**
272
+ * MCP `inputSchema`. Stored under the `parameters` key in yaml for
273
+ * brevity; the firewall publishes it as `inputSchema` on the wire.
274
+ */
275
+ parameters: {
276
+ type: "object";
277
+ properties: Record<string, Record<string, any>>;
278
+ required?: string[];
279
+ };
280
+ /**
281
+ * How the firewall spawns the actual upstream MCP server. Args are
282
+ * passed through unchanged. `env_template` values may reference
283
+ * `${SLOT}` placeholders that the writeMcpEntry helper resolves
284
+ * against the connection-resolver's env output before serializing
285
+ * into firewall config.
286
+ */
287
+ upstream: {
288
+ command: string;
289
+ args?: string[];
290
+ env_template?: Record<string, string>;
291
+ };
292
+ /**
293
+ * When true (default), wrap each `tools/call` result in
294
+ * `{untrusted: true, source: <capability_id>, content: ...}` so the
295
+ * LLM treats output as untrusted data, not instructions. Mirrors
296
+ * OpenClaw's `wrapWebContent` defense.
297
+ */
298
+ wrap_outputs?: boolean;
185
299
  }
186
300
  export interface AppRequire {
187
301
  capability: string;
188
302
  inject_as: string;
189
303
  required?: boolean;
304
+ /**
305
+ * Multi-candidate semantics. `"one"` (default) lets the user pick exactly
306
+ * one provider; `"many"` allows multiple providers (e.g. several MCP
307
+ * servers merged together).
308
+ */
309
+ cardinality?: "one" | "many";
310
+ /**
311
+ * LLM apply mode (§7.1.2). Only meaningful when `capability` resolves to
312
+ * the `llm` category. `"proxy-upstream"` writes the consumer instance's
313
+ * `x-jishushell.proxy.upstream` (OpenClaw / Hermes default); `"openai-env"`
314
+ * injects `OPENAI_API_BASE_URL` / `OPENAI_API_KEY` directly into runtime
315
+ * env (Open WebUI / generic OpenAI clients). Omit to fall back to the
316
+ * adapter-type default.
317
+ */
318
+ apply?: "proxy-upstream" | "openai-env";
319
+ }
320
+ /**
321
+ * Persisted binding state. Stored under `instance.json.connections[slot]`.
322
+ * `null` denotes user-explicit disconnect (do not auto-fall-back).
323
+ * Missing slot = "never set" (legacy single-candidate fallback may apply).
324
+ */
325
+ export interface InstanceConnectionSingle {
326
+ kind: "single";
327
+ providerId: string;
328
+ capability: string;
329
+ /** LLM-only: which model the user picked. */
330
+ selectedModelId?: string;
331
+ }
332
+ export interface InstanceConnectionMany {
333
+ kind: "many";
334
+ providers: Array<{
335
+ providerId: string;
336
+ capability: string;
337
+ }>;
338
+ }
339
+ export type InstanceConnection = InstanceConnectionSingle | InstanceConnectionMany | null;
340
+ export type InstanceConnections = Record<string, InstanceConnection>;
341
+ export interface DismissedSuggestion {
342
+ slot: string;
343
+ until: string;
190
344
  }
191
345
  export type AppLifecycleStep = {
192
346
  run: string;
193
347
  timeout_ms?: number;
194
348
  successIfCommandExists?: string;
349
+ sudo?: boolean;
350
+ ifFileExists?: string;
195
351
  } | {
196
352
  downloadImage: string;
197
353
  timeout_ms?: number;
@@ -207,6 +363,12 @@ export type AppLifecycleStep = {
207
363
  deleteBinary: string;
208
364
  } | {
209
365
  mkdir: string;
366
+ } | {
367
+ chown: {
368
+ path: string;
369
+ owner: string;
370
+ recursive?: boolean;
371
+ };
210
372
  } | {
211
373
  deleteDir: string;
212
374
  } | {
@@ -217,6 +379,14 @@ export interface AppLifecycle {
217
379
  pre_install?: AppLifecycleStep[];
218
380
  /** Steps run during `app install`, before the job is created. */
219
381
  install?: AppLifecycleStep[];
382
+ /**
383
+ * Steps run on every app start, immediately before the Nomad job is
384
+ * submitted. Use for idempotent invariants the runtime depends on but
385
+ * that may drift between starts (e.g. chowning a bind-mount to match
386
+ * the container's runtime user when `cap_drop: ALL` strips
387
+ * CAP_DAC_OVERRIDE).
388
+ */
389
+ pre_start?: AppLifecycleStep[];
220
390
  /** Steps run during `app uninstall`, before files are removed. */
221
391
  uninstall?: AppLifecycleStep[];
222
392
  /** Steps run after the Nomad job transitions to `running`. */
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Per-instance promise-chain mutex for serializing operations that mutate
3
+ * a single instance's durable state across multiple files (instance.json,
4
+ * provider.env, openclaw.json, mcporter.json, …).
5
+ *
6
+ * Mirrors the existing pattern in `llm-proxy/index.ts:_configSaveLocks`:
7
+ * each `withInstanceLock(id, fn)` call chains onto whatever promise was
8
+ * last queued for that id, so concurrent callers see strict FIFO ordering.
9
+ * The map self-cleans entries when the chain settles to keep memory flat.
10
+ *
11
+ * Use this for any code path that performs more than one file write on a
12
+ * given instance id (PUT /connections transactor, startApp, stopApp,
13
+ * restartApp). Operations on different instance ids never block each other.
14
+ */
15
+ /**
16
+ * Run `fn` exclusively against `instanceId`. Returns `fn`'s value (or
17
+ * propagates its rejection). Pending callers form a FIFO chain — each
18
+ * waits for prior ones to settle, even if they reject.
19
+ */
20
+ export declare function withInstanceLock<T>(instanceId: string, fn: () => Promise<T>): Promise<T>;
21
+ /** Test-only helper for verifying the chain is empty between cases. */
22
+ export declare function __instanceLockSize(): number;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Per-instance promise-chain mutex for serializing operations that mutate
3
+ * a single instance's durable state across multiple files (instance.json,
4
+ * provider.env, openclaw.json, mcporter.json, …).
5
+ *
6
+ * Mirrors the existing pattern in `llm-proxy/index.ts:_configSaveLocks`:
7
+ * each `withInstanceLock(id, fn)` call chains onto whatever promise was
8
+ * last queued for that id, so concurrent callers see strict FIFO ordering.
9
+ * The map self-cleans entries when the chain settles to keep memory flat.
10
+ *
11
+ * Use this for any code path that performs more than one file write on a
12
+ * given instance id (PUT /connections transactor, startApp, stopApp,
13
+ * restartApp). Operations on different instance ids never block each other.
14
+ */
15
+ const _instanceLocks = new Map();
16
+ /**
17
+ * Run `fn` exclusively against `instanceId`. Returns `fn`'s value (or
18
+ * propagates its rejection). Pending callers form a FIFO chain — each
19
+ * waits for prior ones to settle, even if they reject.
20
+ */
21
+ export async function withInstanceLock(instanceId, fn) {
22
+ const prev = _instanceLocks.get(instanceId) ?? Promise.resolve();
23
+ // Swallow prior rejections so the chain doesn't break — each caller still
24
+ // gets its own promise's result. The map only tracks ordering, not health.
25
+ const current = prev.catch(() => undefined).then(() => fn());
26
+ // Identity check uses the same reference we store; .finally() returns a
27
+ // new promise, so we must store `current` and attach the cleanup as a
28
+ // side-effect, not store the .finally() result. The trailing .catch()
29
+ // swallows the cleanup-promise's rejection — without it Node logs an
30
+ // "unhandled rejection" each time `fn()` throws even though the caller
31
+ // already handled it via the returned `current`.
32
+ current
33
+ .finally(() => {
34
+ if (_instanceLocks.get(instanceId) === current) {
35
+ _instanceLocks.delete(instanceId);
36
+ }
37
+ })
38
+ .catch(() => {
39
+ // intentional no-op — caller handles the original rejection
40
+ });
41
+ _instanceLocks.set(instanceId, current);
42
+ return current;
43
+ }
44
+ /** Test-only helper for verifying the chain is empty between cases. */
45
+ export function __instanceLockSize() {
46
+ return _instanceLocks.size;
47
+ }
48
+ //# sourceMappingURL=instance-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-lock.js","sourceRoot":"","sources":["../../src/utils/instance-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,EAA4B,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,UAAkB,EAClB,EAAoB;IAEpB,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACjE,0EAA0E;IAC1E,2EAA2E;IAC3E,MAAM,OAAO,GAAe,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACzE,wEAAwE;IACxE,sEAAsE;IACtE,sEAAsE;IACtE,qEAAqE;IACrE,uEAAuE;IACvE,iDAAiD;IACjD,OAAO;SACJ,OAAO,CAAC,GAAG,EAAE;QACZ,IAAI,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,OAAO,EAAE,CAAC;YAC/C,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC;SACD,KAAK,CAAC,GAAG,EAAE;QACV,4DAA4D;IAC9D,CAAC,CAAC,CAAC;IACL,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACxC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB;IAChC,OAAO,cAAc,CAAC,IAAI,CAAC;AAC7B,CAAC"}
@@ -1,4 +1,4 @@
1
- import { chmodSync, existsSync, readFileSync, writeFileSync, renameSync, copyFileSync, openSync, fdatasyncSync, closeSync, mkdirSync } from "fs";
1
+ import { chmodSync, existsSync, readFileSync, writeFileSync, renameSync, copyFileSync, openSync, fdatasyncSync, closeSync, mkdirSync, rmSync } from "fs";
2
2
  import { dirname } from "path";
3
3
  const BACKUP_SUFFIXES = [".bak", ".bak.1", ".bak.2"];
4
4
  function recoverJson(path, label, mode) {
@@ -48,34 +48,67 @@ function withWriteLock(path, fn) {
48
48
  }
49
49
  }
50
50
  function atomicWriteJson(path, data, fsync, mode) {
51
- mkdirSync(dirname(path), { recursive: true });
52
- const tmp = path + ".tmp";
51
+ const dir = dirname(path);
52
+ mkdirSync(dir, { recursive: true });
53
+ const tmp = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
53
54
  const json = typeof data === "string" ? data : JSON.stringify(data);
54
- writeFileSync(tmp, json, { mode });
55
- chmodSync(tmp, mode);
56
- JSON.parse(readFileSync(tmp, "utf-8"));
57
- if (fsync) {
58
- try {
59
- const fd = openSync(tmp, "r");
60
- fdatasyncSync(fd);
61
- closeSync(fd);
55
+ try {
56
+ writeFileSync(tmp, json, { mode });
57
+ chmodSync(tmp, mode);
58
+ JSON.parse(readFileSync(tmp, "utf-8"));
59
+ if (fsync) {
60
+ let fd = null;
61
+ try {
62
+ fd = openSync(tmp, "r");
63
+ fdatasyncSync(fd);
64
+ }
65
+ catch { }
66
+ finally {
67
+ if (fd !== null)
68
+ try {
69
+ closeSync(fd);
70
+ }
71
+ catch { }
72
+ }
62
73
  }
63
- catch { }
64
- }
65
- if (existsSync(path)) {
66
- try {
67
- if (existsSync(path + ".bak.1"))
68
- copyFileSync(path + ".bak.1", path + ".bak.2");
74
+ if (existsSync(path)) {
75
+ try {
76
+ if (existsSync(path + ".bak.1"))
77
+ copyFileSync(path + ".bak.1", path + ".bak.2");
78
+ }
79
+ catch { }
80
+ try {
81
+ if (existsSync(path + ".bak"))
82
+ copyFileSync(path + ".bak", path + ".bak.1");
83
+ }
84
+ catch { }
85
+ copyFileSync(path, path + ".bak");
69
86
  }
70
- catch { }
87
+ renameSync(tmp, path);
88
+ chmodSync(path, mode);
89
+ if (fsync) {
90
+ let dirFd = null;
91
+ try {
92
+ dirFd = openSync(dir, "r");
93
+ fdatasyncSync(dirFd);
94
+ }
95
+ catch { }
96
+ finally {
97
+ if (dirFd !== null)
98
+ try {
99
+ closeSync(dirFd);
100
+ }
101
+ catch { }
102
+ }
103
+ }
104
+ }
105
+ catch (err) {
71
106
  try {
72
- if (existsSync(path + ".bak"))
73
- copyFileSync(path + ".bak", path + ".bak.1");
107
+ rmSync(tmp, { force: true });
74
108
  }
75
109
  catch { }
76
- copyFileSync(path, path + ".bak");
110
+ throw err;
77
111
  }
78
- renameSync(tmp, path);
79
112
  }
80
113
  export function safeWriteJson(path, data, fsync = false) {
81
114
  withWriteLock(path, () => atomicWriteJson(path, data, fsync, 0o644));
@@ -1 +1 @@
1
- {"version":3,"file":"safe-json.js","sourceRoot":"","sources":["../../src/utils/safe-json.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"safe-json.js","sourceRoot":"","sources":["../../src/utils/safe-json.ts"],"names":[],"mappings":"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"}