@abelxiaoxing/cadence 2.0.5 → 2.0.7

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.
@@ -3,11 +3,48 @@
3
3
 
4
4
  // src/service/cli.ts
5
5
  import { spawn } from "node:child_process";
6
- import { existsSync, mkdirSync as mkdirSync4 } from "node:fs";
6
+ import { existsSync, mkdirSync as mkdirSync5 } from "node:fs";
7
7
  import { request } from "node:http";
8
- import { dirname as dirname8, join as join10, resolve as resolve5 } from "node:path";
8
+ import { dirname as dirname9, join as join11, resolve as resolve5 } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
 
11
+ // src/install-config.mjs
12
+ import { closeSync, constants, mkdirSync, openSync, writeSync } from "node:fs";
13
+ import { homedir } from "node:os";
14
+ import path from "node:path";
15
+ var CONFIG_TEMPLATE = `# Cadence research configuration. Keep this file local and never commit it.
16
+ # Context7 works anonymously with its default URL.
17
+ # CONTEXT7_API_URL=https://context7.com/api/v2
18
+ # CONTEXT7_API_KEY=
19
+
20
+ # Required when using the Grok search skill.
21
+ GROK_API_URL=https://api.x.ai/v1
22
+ GROK_API_KEY=
23
+
24
+ # Optional Tavily integration.
25
+ # TAVILY_ENABLED=true
26
+ # TAVILY_API_URL=https://api.tavily.com
27
+ # TAVILY_API_KEY=
28
+ `;
29
+ function ensureUserConfig(home = homedir()) {
30
+ const directory = path.join(home, ".pi", "agent", "cadence");
31
+ const file = path.join(directory, ".env");
32
+ mkdirSync(directory, { recursive: true, mode: 448 });
33
+ try {
34
+ const fd = openSync(file, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384);
35
+ try {
36
+ writeSync(fd, CONFIG_TEMPLATE, undefined, "utf8");
37
+ } finally {
38
+ closeSync(fd);
39
+ }
40
+ return { created: true, path: file };
41
+ } catch (error) {
42
+ if (error?.code === "EEXIST")
43
+ return { created: false, path: file };
44
+ throw error;
45
+ }
46
+ }
47
+
11
48
  // src/task-types.ts
12
49
  var TASK_SCHEMA = "cadence-workflow-assistant/1";
13
50
  var MAX_RECORD_BYTES = 128 * 1024;
@@ -2049,13 +2086,13 @@ class CaptureBudgetLease {
2049
2086
  import { spawnSync } from "node:child_process";
2050
2087
  import { createHash, randomUUID } from "node:crypto";
2051
2088
  import {
2052
- closeSync,
2053
- constants,
2089
+ closeSync as closeSync2,
2090
+ constants as constants2,
2054
2091
  fstatSync,
2055
2092
  fsyncSync,
2056
2093
  lstatSync,
2057
- mkdirSync,
2058
- openSync,
2094
+ mkdirSync as mkdirSync2,
2095
+ openSync as openSync2,
2059
2096
  readdirSync,
2060
2097
  readSync,
2061
2098
  realpathSync,
@@ -2063,7 +2100,7 @@ import {
2063
2100
  unlinkSync,
2064
2101
  writeFileSync
2065
2102
  } from "node:fs";
2066
- import { homedir, hostname } from "node:os";
2103
+ import { homedir as homedir2, hostname } from "node:os";
2067
2104
  import {
2068
2105
  dirname,
2069
2106
  isAbsolute,
@@ -2100,8 +2137,8 @@ function parseRecord(data) {
2100
2137
  function absent(error) {
2101
2138
  return object(error) && error.code === "ENOENT";
2102
2139
  }
2103
- function safeDirectory(path, create = false, inspectExisting) {
2104
- const full = resolve(path);
2140
+ function safeDirectory(path2, create = false, inspectExisting) {
2141
+ const full = resolve(path2);
2105
2142
  const root = parse(full).root;
2106
2143
  if (inspectExisting) {
2107
2144
  const st = lstatSync(root);
@@ -2124,7 +2161,7 @@ function safeDirectory(path, create = false, inspectExisting) {
2124
2161
  if (!create)
2125
2162
  return false;
2126
2163
  try {
2127
- mkdirSync(at, { mode: 448 });
2164
+ mkdirSync2(at, { mode: 448 });
2128
2165
  } catch (mkdirError) {
2129
2166
  if (!object(mkdirError) || mkdirError.code !== "EEXIST")
2130
2167
  throw mkdirError;
@@ -2154,15 +2191,15 @@ function retryWindowsFileOperation(operation, recheck) {
2154
2191
  }
2155
2192
  }
2156
2193
  }
2157
- function readBounded(path, max, packaged = false) {
2158
- if (!safeDirectory(dirname(path)))
2194
+ function readBounded(path2, max, packaged = false) {
2195
+ if (!safeDirectory(dirname(path2)))
2159
2196
  return;
2160
2197
  let fd;
2161
2198
  try {
2162
- const st = lstatSync(path);
2199
+ const st = lstatSync(path2);
2163
2200
  if (!st.isFile() || st.isSymbolicLink() || !packaged && st.nlink !== 1 || st.size > max)
2164
2201
  throw new Error("Unsafe or oversized file");
2165
- fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
2202
+ fd = openSync2(path2, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0));
2166
2203
  } catch (error) {
2167
2204
  if (absent(error))
2168
2205
  return;
@@ -2184,7 +2221,7 @@ function readBounded(path, max, packaged = false) {
2184
2221
  throw new Error("Oversized file");
2185
2222
  return buffer.subarray(0, size);
2186
2223
  } finally {
2187
- closeSync(fd);
2224
+ closeSync2(fd);
2188
2225
  }
2189
2226
  }
2190
2227
  function gitEnvironment() {
@@ -2219,7 +2256,7 @@ function defaultStateHome() {
2219
2256
  const candidate = process.platform === "win32" ? process.env.LOCALAPPDATA : process.env.XDG_STATE_HOME;
2220
2257
  if (candidate && !isAbsolute(candidate))
2221
2258
  throw new Error("State home must be absolute");
2222
- return candidate ?? join(homedir(), process.platform === "win32" ? "AppData/Local" : ".local/state");
2259
+ return candidate ?? join(homedir2(), process.platform === "win32" ? "AppData/Local" : ".local/state");
2223
2260
  }
2224
2261
  function recordText(record) {
2225
2262
  return `${JSON.stringify(record, null, 2)}
@@ -2228,8 +2265,8 @@ function recordText(record) {
2228
2265
  function contentIdentity(text2) {
2229
2266
  return createHash("sha256").update(text2).digest("hex");
2230
2267
  }
2231
- function canonicalAmbientRoot(path) {
2232
- const full = resolve(path);
2268
+ function canonicalAmbientRoot(path2) {
2269
+ const full = resolve(path2);
2233
2270
  try {
2234
2271
  return realpathSync(full);
2235
2272
  } catch (error) {
@@ -2251,8 +2288,8 @@ function canonicalAmbientRoot(path) {
2251
2288
  function privateRootFor(canonicalRoot, stateHome) {
2252
2289
  return join(canonicalAmbientRoot(stateHome), "cadence-assistant-v1", createHash("sha256").update(canonicalRoot).digest("hex"));
2253
2290
  }
2254
- function contains(root, path) {
2255
- const within = relative(root, resolve(path));
2291
+ function contains(root, path2) {
2292
+ const within = relative(root, resolve(path2));
2256
2293
  return !within || !within.startsWith(`..${sep}`) && within !== ".." && !isAbsolute(within);
2257
2294
  }
2258
2295
  function stateHomeInside(root, stateHome = defaultStateHome()) {
@@ -2260,7 +2297,7 @@ function stateHomeInside(root, stateHome = defaultStateHome()) {
2260
2297
  return contains(canonical, privateRootFor(canonical, stateHome));
2261
2298
  }
2262
2299
  var LANDING_WORKSPACE = "cadence";
2263
- function effectiveWorkspace(requested, stateHome, home = homedir()) {
2300
+ function effectiveWorkspace(requested, stateHome, home = homedir2()) {
2264
2301
  if (!stateHomeInside(resolveWorkspace(requested).root, stateHome))
2265
2302
  return { workspace: requested, redirected: false };
2266
2303
  return { workspace: join(home, LANDING_WORKSPACE), redirected: true };
@@ -2282,20 +2319,20 @@ class TaskStore {
2282
2319
  }
2283
2320
  inspectPrivate() {
2284
2321
  const namespace = dirname(this.privateRoot);
2285
- for (const path of [namespace, this.privateRoot]) {
2286
- if (!safeDirectory(path))
2322
+ for (const path2 of [namespace, this.privateRoot]) {
2323
+ if (!safeDirectory(path2))
2287
2324
  continue;
2288
- const st = lstatSync(path);
2325
+ const st = lstatSync(path2);
2289
2326
  if (process.platform !== "win32" && ((st.mode & 63) !== 0 || process.getuid && st.uid !== process.getuid()))
2290
2327
  throw new Error("Private state permissions refused");
2291
2328
  }
2292
2329
  }
2293
- inspectPrivateFile(path, max) {
2330
+ inspectPrivateFile(path2, max) {
2294
2331
  this.inspectPrivate();
2295
2332
  if (!safeDirectory(this.privateRoot))
2296
2333
  return;
2297
2334
  try {
2298
- const st = lstatSync(path);
2335
+ const st = lstatSync(path2);
2299
2336
  if (!st.isFile() || st.isSymbolicLink() || st.nlink !== 1 || st.size > max || process.platform !== "win32" && ((st.mode & 63) !== 0 || process.getuid && st.uid !== process.getuid()))
2300
2337
  throw new Error("Unsafe private state file");
2301
2338
  } catch (error) {
@@ -2326,7 +2363,7 @@ class TaskStore {
2326
2363
  };
2327
2364
  let fd;
2328
2365
  try {
2329
- fd = retryWindowsFileOperation(() => openSync(this.lockPath, "wx", 384));
2366
+ fd = retryWindowsFileOperation(() => openSync2(this.lockPath, "wx", 384));
2330
2367
  } catch {
2331
2368
  throw new Error("Checkout busy or unresolved lock; inspect and explicitly recover only a dead owner");
2332
2369
  }
@@ -2334,7 +2371,7 @@ class TaskStore {
2334
2371
  writeFileSync(fd, JSON.stringify(lock));
2335
2372
  fsyncSync(fd);
2336
2373
  } finally {
2337
- closeSync(fd);
2374
+ closeSync2(fd);
2338
2375
  }
2339
2376
  return lock;
2340
2377
  }
@@ -2415,12 +2452,12 @@ class TaskStore {
2415
2452
  if (!current && readdirSync(dir).length)
2416
2453
  throw new Error("Unknown task directory");
2417
2454
  const temporary = join(dir, `task.${randomUUID()}.tmp`);
2418
- const fd = openSync(temporary, "wx", 384);
2455
+ const fd = openSync2(temporary, "wx", 384);
2419
2456
  try {
2420
2457
  writeFileSync(fd, text2);
2421
2458
  fsyncSync(fd);
2422
2459
  } finally {
2423
- closeSync(fd);
2460
+ closeSync2(fd);
2424
2461
  }
2425
2462
  this.assertOwner(lock);
2426
2463
  const latest = readBounded(join(dir, "task.json"), MAX_RECORD_BYTES);
@@ -2474,25 +2511,420 @@ var TASK_CONFIG_SCHEMA = "cadence-assistant-checks/1";
2474
2511
  // src/service/autostart.ts
2475
2512
  import { spawnSync as spawnSync2 } from "node:child_process";
2476
2513
  import {
2477
- lstatSync as lstatSync2,
2478
- mkdirSync as mkdirSync2,
2514
+ lstatSync as lstatSync3,
2515
+ mkdirSync as mkdirSync4,
2479
2516
  readFileSync,
2480
2517
  realpathSync as realpathSync2,
2481
- renameSync as renameSync2,
2518
+ renameSync as renameSync3,
2482
2519
  rmSync,
2483
2520
  statSync,
2484
- writeFileSync as writeFileSync2
2521
+ writeFileSync as writeFileSync3
2485
2522
  } from "node:fs";
2486
- import { homedir as homedir2 } from "node:os";
2523
+ import { homedir as homedir3 } from "node:os";
2487
2524
  import {
2488
2525
  delimiter,
2489
- dirname as dirname2,
2490
- isAbsolute as isAbsolute2,
2491
- join as join2,
2492
- resolve as resolve2,
2493
- sep as sep2,
2526
+ dirname as dirname4,
2527
+ isAbsolute as isAbsolute3,
2528
+ join as join4,
2529
+ resolve as resolve3,
2530
+ sep as sep3,
2494
2531
  win32
2495
2532
  } from "node:path";
2533
+
2534
+ // src/service/server-config.ts
2535
+ import { randomBytes } from "node:crypto";
2536
+ import { dirname as dirname3, join as join3 } from "node:path";
2537
+
2538
+ // skills/_shared/load-config.mjs
2539
+ function parseEnvFile(content) {
2540
+ const values = {};
2541
+ const lines = content.replaceAll(`\r
2542
+ `, `
2543
+ `).replaceAll("\r", `
2544
+ `).split(`
2545
+ `);
2546
+ for (let index = 0;index < lines.length; index += 1) {
2547
+ const line = lines[index].trim();
2548
+ if (!line || line.startsWith("#"))
2549
+ continue;
2550
+ const separator = line.indexOf("=");
2551
+ if (separator <= 0)
2552
+ throw new Error(`Invalid configuration syntax at line ${index + 1}`);
2553
+ const name = line.slice(0, separator).trim();
2554
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
2555
+ throw new Error(`Invalid configuration name at line ${index + 1}`);
2556
+ }
2557
+ let value = line.slice(separator + 1).trim();
2558
+ const quote = value[0];
2559
+ if (quote === '"' || quote === "'") {
2560
+ if (value.at(-1) !== quote) {
2561
+ throw new Error(`Unterminated quoted configuration value at line ${index + 1}`);
2562
+ }
2563
+ value = value.slice(1, -1);
2564
+ }
2565
+ values[name] = value;
2566
+ }
2567
+ return values;
2568
+ }
2569
+
2570
+ // src/service/private-files.ts
2571
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
2572
+ import {
2573
+ closeSync as closeSync3,
2574
+ constants as constants3,
2575
+ fstatSync as fstatSync2,
2576
+ fsyncSync as fsyncSync2,
2577
+ lstatSync as lstatSync2,
2578
+ mkdirSync as mkdirSync3,
2579
+ openSync as openSync3,
2580
+ readSync as readSync2,
2581
+ renameSync as renameSync2,
2582
+ unlinkSync as unlinkSync2,
2583
+ writeFileSync as writeFileSync2
2584
+ } from "node:fs";
2585
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
2586
+ var METADATA_BYTES = 16 * 1024;
2587
+ var REGISTRY_BYTES = 1024 * 1024;
2588
+ var REGISTRY_ENTRIES = 128;
2589
+ function refused() {
2590
+ throw new ContractError("corrupt-selected");
2591
+ }
2592
+ function hash(bytes) {
2593
+ return createHash2("sha256").update(bytes).digest("hex");
2594
+ }
2595
+ function privateMode(st, ownerOnly = true) {
2596
+ if (process.platform !== "win32" && (ownerOnly && (st.mode & 63) !== 0 || process.getuid && st.uid !== process.getuid()))
2597
+ refused();
2598
+ }
2599
+ function same(a, b) {
2600
+ return a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.ctimeMs === b.ctimeMs && a.mtimeMs === b.mtimeMs && a.mode === b.mode && a.uid === b.uid && a.nlink === b.nlink;
2601
+ }
2602
+ function missing(error) {
2603
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
2604
+ }
2605
+ var job = 0;
2606
+ var advancing = false;
2607
+ function currentJob() {
2608
+ if (!advancing) {
2609
+ advancing = true;
2610
+ queueMicrotask(() => {
2611
+ job++;
2612
+ advancing = false;
2613
+ });
2614
+ }
2615
+ return job;
2616
+ }
2617
+
2618
+ class DirectoryWalks {
2619
+ job = currentJob();
2620
+ walked = new Map;
2621
+ open() {
2622
+ if (this.job === job)
2623
+ return true;
2624
+ this.walked.clear();
2625
+ return false;
2626
+ }
2627
+ remember(path2, st) {
2628
+ if (this.open())
2629
+ this.walked.set(path2, { dev: st.dev, ino: st.ino });
2630
+ }
2631
+ reuse(path2, ownerOnly = true) {
2632
+ const walked = this.open() ? this.walked.get(path2) : undefined;
2633
+ if (!walked)
2634
+ return false;
2635
+ let st;
2636
+ try {
2637
+ st = lstatSync2(path2);
2638
+ } catch (error) {
2639
+ if (!missing(error))
2640
+ throw error;
2641
+ this.walked.delete(path2);
2642
+ return false;
2643
+ }
2644
+ if (!st.isDirectory() || st.isSymbolicLink() || st.dev !== walked.dev || st.ino !== walked.ino) {
2645
+ this.walked.delete(path2);
2646
+ return false;
2647
+ }
2648
+ privateMode(st, ownerOnly);
2649
+ return true;
2650
+ }
2651
+ }
2652
+
2653
+ class PrivateFiles {
2654
+ namespace;
2655
+ ownerOnly;
2656
+ constructor(namespace, ownerOnly = true) {
2657
+ this.namespace = namespace;
2658
+ this.ownerOnly = ownerOnly;
2659
+ }
2660
+ directory(path2, create = false, walks) {
2661
+ const tail = relative2(this.namespace, path2);
2662
+ if (isAbsolute2(tail) || tail === ".." || tail.startsWith(`..${sep2}`))
2663
+ refused();
2664
+ if (!create) {
2665
+ const full = resolve2(path2);
2666
+ if (walks?.reuse(full, this.ownerOnly))
2667
+ return true;
2668
+ const namespace = resolve2(this.namespace);
2669
+ let inside = false, last;
2670
+ const found = safeDirectory(path2, false, (at2, st) => {
2671
+ if (at2 === namespace)
2672
+ inside = true;
2673
+ if (inside)
2674
+ privateMode(st, this.ownerOnly);
2675
+ last = st;
2676
+ });
2677
+ if (found && last)
2678
+ walks?.remember(full, last);
2679
+ return found;
2680
+ }
2681
+ if (!safeDirectory(this.namespace, true))
2682
+ return false;
2683
+ privateMode(lstatSync2(this.namespace), this.ownerOnly);
2684
+ let at = this.namespace;
2685
+ for (const part of tail.split(sep2).filter(Boolean)) {
2686
+ at = join2(at, part);
2687
+ let st;
2688
+ try {
2689
+ st = lstatSync2(at);
2690
+ } catch (error) {
2691
+ if (!missing(error))
2692
+ throw error;
2693
+ try {
2694
+ mkdirSync3(at, { mode: 448 });
2695
+ } catch (mkdirError) {
2696
+ if (!(mkdirError instanceof Error && ("code" in mkdirError) && mkdirError.code === "EEXIST"))
2697
+ throw mkdirError;
2698
+ }
2699
+ st = lstatSync2(at);
2700
+ }
2701
+ if (!st.isDirectory() || st.isSymbolicLink())
2702
+ throw new Error("Unsafe directory");
2703
+ privateMode(st, this.ownerOnly);
2704
+ }
2705
+ return true;
2706
+ }
2707
+ read(path2, maximum = METADATA_BYTES, walks) {
2708
+ if (!this.directory(dirname2(path2), false, walks))
2709
+ return;
2710
+ let before;
2711
+ try {
2712
+ before = lstatSync2(path2);
2713
+ } catch (error) {
2714
+ if (missing(error))
2715
+ return;
2716
+ return refused();
2717
+ }
2718
+ if (!before.isFile() || before.nlink !== 1 || before.size > maximum)
2719
+ refused();
2720
+ privateMode(before, this.ownerOnly);
2721
+ const fd = openSync3(path2, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
2722
+ try {
2723
+ const opened = fstatSync2(fd);
2724
+ if (!same(before, opened))
2725
+ refused();
2726
+ const buffer = Buffer.alloc(opened.size + 1);
2727
+ let size = 0;
2728
+ while (size < buffer.length) {
2729
+ const n = readSync2(fd, buffer, size, buffer.length - size, null);
2730
+ if (!n)
2731
+ break;
2732
+ size += n;
2733
+ }
2734
+ const bytes = buffer.subarray(0, size);
2735
+ if (size !== opened.size || !same(opened, fstatSync2(fd)) || !same(opened, lstatSync2(path2)))
2736
+ refused();
2737
+ return { bytes, identity: hash(bytes), stamp: stamp(opened) };
2738
+ } finally {
2739
+ closeSync3(fd);
2740
+ }
2741
+ }
2742
+ assert(path2, expected, maximum = METADATA_BYTES, walks) {
2743
+ const current = this.read(path2, maximum, walks);
2744
+ if (current?.identity !== expected?.identity || current?.stamp !== expected?.stamp)
2745
+ throw new ContractError("conflict");
2746
+ }
2747
+ create(path2, bytes, maximum = METADATA_BYTES) {
2748
+ if (Buffer.byteLength(bytes) > maximum)
2749
+ throw new ContractError("oversize");
2750
+ this.directory(dirname2(path2), true);
2751
+ const fd = retryWindowsFileOperation(() => openSync3(path2, "wx", 384));
2752
+ try {
2753
+ writeFileSync2(fd, bytes);
2754
+ fsyncSync2(fd);
2755
+ } finally {
2756
+ closeSync3(fd);
2757
+ }
2758
+ this.syncDirectory(dirname2(path2));
2759
+ const result = this.read(path2, maximum);
2760
+ if (!result || result.identity !== hash(bytes))
2761
+ refused();
2762
+ return result;
2763
+ }
2764
+ replace(path2, expected, bytes, maximum = METADATA_BYTES) {
2765
+ this.assert(path2, expected, maximum);
2766
+ const temporary = join2(dirname2(path2), `pending-${randomUUID2()}.json`);
2767
+ this.create(temporary, bytes, maximum);
2768
+ this.assert(path2, expected, maximum);
2769
+ retryWindowsFileOperation(() => renameSync2(temporary, path2), () => this.assert(path2, expected, maximum));
2770
+ this.syncDirectory(dirname2(path2));
2771
+ const result = this.read(path2, maximum);
2772
+ if (!result || result.identity !== hash(bytes))
2773
+ refused();
2774
+ return result;
2775
+ }
2776
+ remove(path2, expected, maximum = METADATA_BYTES) {
2777
+ this.assert(path2, expected, maximum);
2778
+ retryWindowsFileOperation(() => unlinkSync2(path2), () => this.assert(path2, expected, maximum));
2779
+ this.syncDirectory(dirname2(path2));
2780
+ }
2781
+ syncDirectory(path2) {
2782
+ if (process.platform === "win32")
2783
+ return;
2784
+ const fd = openSync3(path2, constants3.O_RDONLY | constants3.O_DIRECTORY);
2785
+ try {
2786
+ fsyncSync2(fd);
2787
+ } finally {
2788
+ closeSync3(fd);
2789
+ }
2790
+ }
2791
+ }
2792
+ function stamp(st) {
2793
+ return [
2794
+ st.dev,
2795
+ st.ino,
2796
+ st.size,
2797
+ st.mode,
2798
+ st.uid,
2799
+ st.nlink,
2800
+ st.ctimeMs,
2801
+ st.mtimeMs
2802
+ ].join(":");
2803
+ }
2804
+ function json(snapshot, maximum = METADATA_BYTES) {
2805
+ try {
2806
+ return parseWireJson(new TextDecoder("utf-8", { fatal: true }).decode(snapshot.bytes), maximum);
2807
+ } catch {
2808
+ return refused();
2809
+ }
2810
+ }
2811
+ function encoded(value) {
2812
+ return `${JSON.stringify(value)}
2813
+ `;
2814
+ }
2815
+
2816
+ // src/service/server-config.ts
2817
+ var DEFAULT_PORT = 17423;
2818
+ var PORT_KEY = "CADENCE_PORT";
2819
+ var TOKEN_KEY = "CADENCE_TOKEN";
2820
+ var CONFIG_BYTES = 64 * 1024;
2821
+ var HEADER = [
2822
+ "# Cadence WebUI for `cadence server install`: edit, then run `cadence server restart`.",
2823
+ "# The token admits anyone who has it to your workspace; keep it private."
2824
+ ];
2825
+ var TOKEN_BYTES = 1024;
2826
+ function accessToken(value) {
2827
+ const result = text(value, TOKEN_BYTES, true);
2828
+ if (utf8Bytes(result) < 32 || /[\p{Cc}\p{Cf}]/u.test(result))
2829
+ throw new ContractError("invalid");
2830
+ return result;
2831
+ }
2832
+ function serverConfigPath(home) {
2833
+ return join3(home, ".pi", "agent", "cadence", ".env");
2834
+ }
2835
+ function settings(source) {
2836
+ parseEnvFile(source);
2837
+ const parts = source.split(/(\r\n|\r|\n)/);
2838
+ const found = {};
2839
+ for (let at = 0;at < parts.length; at += 2) {
2840
+ const line = parts[at].trim();
2841
+ if (!line || line.startsWith("#"))
2842
+ continue;
2843
+ const separator = line.indexOf("=");
2844
+ const key = line.slice(0, separator).trim();
2845
+ const name = key === PORT_KEY ? "port" : key === TOKEN_KEY ? "token" : undefined;
2846
+ if (!name)
2847
+ continue;
2848
+ if (found[name])
2849
+ throw new ContractError("invalid");
2850
+ let value = line.slice(separator + 1).trim();
2851
+ if (value[0] === '"' || value[0] === "'")
2852
+ value = value.slice(1, -1);
2853
+ found[name] = { at, value };
2854
+ }
2855
+ return { parts, ...found };
2856
+ }
2857
+ function decodeServerConfig(source) {
2858
+ const { port, token } = settings(source);
2859
+ if (!port || !/^[1-9][0-9]{0,4}$/.test(port.value))
2860
+ throw new ContractError("invalid");
2861
+ if (!token)
2862
+ throw new ContractError("invalid");
2863
+ return {
2864
+ port: integer(Number(port.value), 1, 65535),
2865
+ token: accessToken(token.value)
2866
+ };
2867
+ }
2868
+
2869
+ class ServerConfigRefusal extends ContractError {
2870
+ path;
2871
+ constructor(path2) {
2872
+ super("corrupt-selected");
2873
+ this.path = path2;
2874
+ }
2875
+ }
2876
+ function decoded(snapshot) {
2877
+ return new TextDecoder("utf-8", { fatal: true }).decode(snapshot.bytes);
2878
+ }
2879
+ function readServerConfig(path2) {
2880
+ try {
2881
+ const files = new PrivateFiles(dirname3(path2));
2882
+ const snapshot = files.read(path2, CONFIG_BYTES);
2883
+ if (!snapshot)
2884
+ throw new ContractError("corrupt-selected");
2885
+ return {
2886
+ path: path2,
2887
+ config: decodeServerConfig(decoded(snapshot)),
2888
+ files,
2889
+ snapshot
2890
+ };
2891
+ } catch {
2892
+ throw new ServerConfigRefusal(path2);
2893
+ }
2894
+ }
2895
+ function ensureServerConfig(home) {
2896
+ const path2 = serverConfigPath(home);
2897
+ const added = [];
2898
+ try {
2899
+ ensureUserConfig(home);
2900
+ const files = new PrivateFiles(dirname3(path2));
2901
+ const snapshot = files.read(path2, CONFIG_BYTES);
2902
+ if (!snapshot)
2903
+ throw new ContractError("corrupt-selected");
2904
+ const source = decoded(snapshot);
2905
+ const found = settings(source);
2906
+ const lines = [];
2907
+ if (!found.port)
2908
+ lines.push(`${PORT_KEY}=${DEFAULT_PORT}`);
2909
+ if (!found.token)
2910
+ lines.push(`${TOKEN_KEY}=${randomBytes(32).toString("base64url")}`);
2911
+ if (lines.length) {
2912
+ const eol = source.includes(`\r
2913
+ `) ? `\r
2914
+ ` : `
2915
+ `;
2916
+ const ending = !source || /[\r\n]$/.test(source) ? "" : eol;
2917
+ const gap = source ? eol : "";
2918
+ files.replace(path2, snapshot, `${source}${ending}${gap}${[...HEADER, ...lines].join(eol)}${eol}`, CONFIG_BYTES);
2919
+ added.push(...lines.map((line) => line.slice(0, line.indexOf("="))));
2920
+ }
2921
+ } catch {
2922
+ throw new ServerConfigRefusal(path2);
2923
+ }
2924
+ return { ...readServerConfig(path2), added };
2925
+ }
2926
+
2927
+ // src/service/autostart.ts
2496
2928
  var UNIT = "cadence.service";
2497
2929
  var MARKER = "# Generated by @abelxiaoxing/cadence";
2498
2930
  function wscript(env) {
@@ -2511,7 +2943,7 @@ function hostSystem(root, owner) {
2511
2943
  return {
2512
2944
  platform: process.platform,
2513
2945
  env: process.env,
2514
- home: homedir2(),
2946
+ home: homedir3(),
2515
2947
  node: process.execPath,
2516
2948
  root,
2517
2949
  unit: UNIT,
@@ -2545,8 +2977,8 @@ function hostSystem(root, owner) {
2545
2977
  },
2546
2978
  launchctl: (args) => run("launchctl", args),
2547
2979
  reg: (args) => run("reg", args),
2548
- launch: (path) => {
2549
- const r = spawnSync2(wscript(process.env), [path], {
2980
+ launch: (path2) => {
2981
+ const r = spawnSync2(wscript(process.env), [path2], {
2550
2982
  stdio: "ignore",
2551
2983
  windowsHide: true,
2552
2984
  timeout: 20000
@@ -2559,15 +2991,15 @@ function hostSystem(root, owner) {
2559
2991
  function expandHome(value, home) {
2560
2992
  if (value === "~")
2561
2993
  return home;
2562
- return value.startsWith("~/") ? join2(home, value.slice(2)) : value;
2994
+ return value.startsWith("~/") ? join4(home, value.slice(2)) : value;
2563
2995
  }
2564
2996
  function agentDirectory(s) {
2565
2997
  const selected = s.env.PI_CODING_AGENT_DIR;
2566
- return resolve2(selected ? expandHome(selected, s.home) : join2(s.home, ".pi", "agent"));
2998
+ return resolve3(selected ? expandHome(selected, s.home) : join4(s.home, ".pi", "agent"));
2567
2999
  }
2568
3000
  function piUserInstall(s) {
2569
3001
  try {
2570
- const managed = join2(agentDirectory(s), "npm", "node_modules", "@abelxiaoxing", "cadence");
3002
+ const managed = join4(agentDirectory(s), "npm", "node_modules", "@abelxiaoxing", "cadence");
2571
3003
  return realpathSync2(managed) === realpathSync2(s.root);
2572
3004
  } catch {
2573
3005
  return false;
@@ -2576,18 +3008,18 @@ function piUserInstall(s) {
2576
3008
  function sdkPackages(root) {
2577
3009
  let start;
2578
3010
  try {
2579
- start = realpathSync2(join2(root, "src"));
3011
+ start = realpathSync2(join4(root, "src"));
2580
3012
  } catch {
2581
3013
  return;
2582
3014
  }
2583
3015
  const found = [];
2584
3016
  for (const name of ["pi-coding-agent", "pi-ai"]) {
2585
3017
  let hit;
2586
- for (let dir = start;!hit; dir = dirname2(dir)) {
2587
- const candidate = join2(dir, "node_modules", "@earendil-works", name, "package.json");
3018
+ for (let dir = start;!hit; dir = dirname4(dir)) {
3019
+ const candidate = join4(dir, "node_modules", "@earendil-works", name, "package.json");
2588
3020
  if (regularFile(candidate))
2589
3021
  hit = candidate;
2590
- else if (dirname2(dir) === dir)
3022
+ else if (dirname4(dir) === dir)
2591
3023
  return;
2592
3024
  }
2593
3025
  found.push(hit);
@@ -2596,10 +3028,10 @@ function sdkPackages(root) {
2596
3028
  }
2597
3029
  function unitPath(s) {
2598
3030
  const data = s.env.XDG_DATA_HOME;
2599
- return join2(data && isAbsolute2(data) ? data : join2(s.home, ".local", "share"), "systemd", "user", s.unit);
3031
+ return join4(data && isAbsolute3(data) ? data : join4(s.home, ".local", "share"), "systemd", "user", s.unit);
2600
3032
  }
2601
3033
  function servicePath(value) {
2602
- const kept = (value ?? "").split(delimiter).filter((entry) => isAbsolute2(entry) && !entry.endsWith(`${sep2}node_modules${sep2}.bin`) && !entry.includes(`${sep2}node-gyp-bin`));
3034
+ const kept = (value ?? "").split(delimiter).filter((entry) => isAbsolute3(entry) && !entry.endsWith(`${sep3}node_modules${sep3}.bin`) && !entry.includes(`${sep3}node-gyp-bin`));
2603
3035
  return kept.length ? [...new Set(kept)].join(delimiter) : "/usr/local/bin:/usr/bin:/bin";
2604
3036
  }
2605
3037
  function serviceEnvironment(s) {
@@ -2610,7 +3042,7 @@ function serviceEnvironment(s) {
2610
3042
  out.push([key, value]);
2611
3043
  }
2612
3044
  const state = s.env.XDG_STATE_HOME;
2613
- if (state && isAbsolute2(state))
3045
+ if (state && isAbsolute3(state))
2614
3046
  out.push(["XDG_STATE_HOME", state]);
2615
3047
  if (s.env.PI_CODING_AGENT_DIR)
2616
3048
  out.push(["PI_CODING_AGENT_DIR", agentDirectory(s)]);
@@ -2633,8 +3065,8 @@ function buildUnit(u) {
2633
3065
  "Description=Cadence shared task service and WebUI",
2634
3066
  "Documentation=https://github.com/abelxiaoxing/cadence/blob/main/docs/service-management.md",
2635
3067
  `ConditionPathExists=${specifiers(u.cli)}`,
2636
- `ConditionPathExists=${specifiers(join2(u.agentDir, "auth.json"))}`,
2637
- ...u.sdk.map((path) => `ConditionPathExists=${specifiers(path)}`),
3068
+ `ConditionPathExists=${specifiers(join4(u.agentDir, "auth.json"))}`,
3069
+ ...u.sdk.map((path2) => `ConditionPathExists=${specifiers(path2)}`),
2638
3070
  "StartLimitIntervalSec=300",
2639
3071
  "StartLimitBurst=3",
2640
3072
  "",
@@ -2642,7 +3074,7 @@ function buildUnit(u) {
2642
3074
  "Type=simple",
2643
3075
  `WorkingDirectory=${specifiers(u.workspace)}`,
2644
3076
  ...u.environment.map(([k, v]) => `Environment=${quoted(`${k}=${v}`)}`),
2645
- `ExecStart=${command(["start", "--foreground", "--workspace", u.workspace])}`,
3077
+ `ExecStart=${command(["start", "--foreground", "--workspace", u.workspace, "--server-config", u.serverConfig])}`,
2646
3078
  `ExecStop=-${command(["stop", "--workspace", u.workspace])}`,
2647
3079
  "KillMode=mixed",
2648
3080
  "TimeoutStopSec=90",
@@ -2655,11 +3087,11 @@ function buildUnit(u) {
2655
3087
  ].join(`
2656
3088
  `);
2657
3089
  }
2658
- function readGenerated(path, marker) {
3090
+ function readGenerated(path2, marker) {
2659
3091
  try {
2660
- if (!lstatSync2(path).isFile())
3092
+ if (!lstatSync3(path2).isFile())
2661
3093
  return null;
2662
- const bytes = readFileSync(path);
3094
+ const bytes = readFileSync(path2);
2663
3095
  const text2 = bytes[0] === 255 && bytes[1] === 254 ? bytes.subarray(2).toString("utf16le") : bytes.toString("utf8");
2664
3096
  return text2.startsWith(marker) ? text2 : null;
2665
3097
  } catch (error) {
@@ -2671,16 +3103,16 @@ function readGenerated(path, marker) {
2671
3103
  function unitWorkspace(text2) {
2672
3104
  return text2?.match(/^WorkingDirectory=(.+)$/m)?.[1].replaceAll("%%", "%");
2673
3105
  }
2674
- function directory(path) {
3106
+ function directory(path2) {
2675
3107
  try {
2676
- return isAbsolute2(path) && statSync(path).isDirectory();
3108
+ return isAbsolute3(path2) && statSync(path2).isDirectory();
2677
3109
  } catch {
2678
3110
  return false;
2679
3111
  }
2680
3112
  }
2681
3113
  function serviceStateHome(s) {
2682
3114
  const state = s.platform === "win32" ? s.env.LOCALAPPDATA : s.env.XDG_STATE_HOME;
2683
- return state && isAbsolute2(state) ? state : join2(s.home, ...s.platform === "win32" ? ["AppData", "Local"] : [".local", "state"]);
3115
+ return state && isAbsolute3(state) ? state : join4(s.home, ...s.platform === "win32" ? ["AppData", "Local"] : [".local", "state"]);
2684
3116
  }
2685
3117
  function selectWorkspace(s, requested, kept) {
2686
3118
  let chosen;
@@ -2688,27 +3120,27 @@ function selectWorkspace(s, requested, kept) {
2688
3120
  chosen = expandHome(requested, s.home);
2689
3121
  if (!directory(chosen))
2690
3122
  throw new ContractError("invalid");
2691
- chosen = resolve2(chosen);
3123
+ chosen = resolve3(chosen);
2692
3124
  } else if (kept && directory(kept))
2693
3125
  chosen = kept;
2694
- const landing = chosen ? effectiveWorkspace(chosen, serviceStateHome(s), s.home) : { workspace: join2(s.home, LANDING_WORKSPACE), redirected: false };
3126
+ const landing = chosen ? effectiveWorkspace(chosen, serviceStateHome(s), s.home) : { workspace: join4(s.home, LANDING_WORKSPACE), redirected: false };
2695
3127
  if (landing.workspace !== chosen)
2696
- mkdirSync2(landing.workspace, { recursive: true });
3128
+ mkdirSync4(landing.workspace, { recursive: true });
2697
3129
  return landing.redirected ? { workspace: landing.workspace, redirectedFrom: chosen } : { workspace: landing.workspace };
2698
3130
  }
2699
- function write(path, content) {
2700
- mkdirSync2(dirname2(path), { recursive: true });
2701
- const temporary = `${path}.${process.pid}.tmp`;
3131
+ function write(path2, content) {
3132
+ mkdirSync4(dirname4(path2), { recursive: true });
3133
+ const temporary = `${path2}.${process.pid}.tmp`;
2702
3134
  try {
2703
- writeFileSync2(temporary, content, { mode: 420, flag: "wx" });
2704
- renameSync2(temporary, path);
3135
+ writeFileSync3(temporary, content, { mode: 420, flag: "wx" });
3136
+ renameSync3(temporary, path2);
2705
3137
  } finally {
2706
3138
  rmSync(temporary, { force: true });
2707
3139
  }
2708
3140
  }
2709
- function regularFile(path) {
3141
+ function regularFile(path2) {
2710
3142
  try {
2711
- return statSync(path).isFile();
3143
+ return statSync(path2).isFile();
2712
3144
  } catch {
2713
3145
  return false;
2714
3146
  }
@@ -2741,10 +3173,10 @@ function configureAutostart(s, mode, requested, linger = false) {
2741
3173
  return { status: "unsupported" };
2742
3174
  if (s.systemctl(["show-environment"]).status !== 0)
2743
3175
  return { status: "unavailable" };
2744
- const path = unitPath(s);
2745
- const previous = readGenerated(path, MARKER);
3176
+ const path2 = unitPath(s);
3177
+ const previous = readGenerated(path2, MARKER);
2746
3178
  if (previous === null)
2747
- return { status: "foreign", unit: path };
3179
+ return { status: "foreign", unit: path2 };
2748
3180
  if (mode === "restart" && previous === undefined)
2749
3181
  return { status: "absent" };
2750
3182
  const sdk = sdkPackages(s.root);
@@ -2754,8 +3186,9 @@ function configureAutostart(s, mode, requested, linger = false) {
2754
3186
  const agentDir = agentDirectory(s);
2755
3187
  const content = buildUnit({
2756
3188
  node: s.node,
2757
- cli: join2(s.root, "src", "service-cli.mjs"),
3189
+ cli: join4(s.root, "src", "service-cli.mjs"),
2758
3190
  workspace,
3191
+ serverConfig: serverConfigPath(s.home),
2759
3192
  agentDir,
2760
3193
  sdk,
2761
3194
  environment: serviceEnvironment(s)
@@ -2764,10 +3197,10 @@ function configureAutostart(s, mode, requested, linger = false) {
2764
3197
  const active = s.systemctl(["is-active", "--quiet", s.unit]).status === 0;
2765
3198
  if (active)
2766
3199
  s.systemctl(["stop", s.unit]);
2767
- write(path, content);
3200
+ write(path2, content);
2768
3201
  s.systemctl(["daemon-reload"]);
2769
3202
  const result = {
2770
- unit: path,
3203
+ unit: path2,
2771
3204
  workspace,
2772
3205
  ...redirectedFrom ? { redirectedFrom } : {}
2773
3206
  };
@@ -2776,7 +3209,7 @@ function configureAutostart(s, mode, requested, linger = false) {
2776
3209
  if (mode === "install" && enabled !== "enabled" && s.systemctl(["enable", s.unit]).status !== 0)
2777
3210
  return { status: "failed", ...result };
2778
3211
  const boot = () => mode === "install" ? bootStart(s, linger) : {};
2779
- if (!regularFile(join2(agentDir, "auth.json")))
3212
+ if (!regularFile(join4(agentDir, "auth.json")))
2780
3213
  return { status: "waiting-for-pi-login", ...result, ...boot() };
2781
3214
  s.systemctl(["reset-failed", s.unit]);
2782
3215
  const started = s.systemctl(["start", s.unit]).status === 0;
@@ -2785,309 +3218,61 @@ function configureAutostart(s, mode, requested, linger = false) {
2785
3218
  function stopAutostart(s) {
2786
3219
  if (s.platform !== "linux")
2787
3220
  return { status: "unsupported" };
2788
- const path = unitPath(s);
2789
- const previous = readGenerated(path, MARKER);
3221
+ const path2 = unitPath(s);
3222
+ const previous = readGenerated(path2, MARKER);
2790
3223
  if (previous === null)
2791
- return { status: "foreign", unit: path };
3224
+ return { status: "foreign", unit: path2 };
2792
3225
  if (previous === undefined)
2793
3226
  return { status: "absent" };
2794
3227
  if (s.systemctl(["show-environment"]).status !== 0)
2795
- return { status: "unavailable", unit: path };
3228
+ return { status: "unavailable", unit: path2 };
2796
3229
  return {
2797
3230
  status: s.systemctl(["stop", s.unit]).status === 0 ? "stopped" : "failed",
2798
- unit: path,
3231
+ unit: path2,
2799
3232
  workspace: unitWorkspace(previous)
2800
3233
  };
2801
3234
  }
2802
3235
  function removeAutostart(s) {
2803
3236
  if (s.platform !== "linux")
2804
3237
  return { status: "unsupported" };
2805
- const path = unitPath(s);
2806
- const previous = readGenerated(path, MARKER);
3238
+ const path2 = unitPath(s);
3239
+ const previous = readGenerated(path2, MARKER);
2807
3240
  if (previous === null)
2808
- return { status: "foreign", unit: path };
3241
+ return { status: "foreign", unit: path2 };
2809
3242
  if (previous === undefined)
2810
3243
  return { status: "absent" };
2811
3244
  if (s.systemctl(["show-environment"]).status !== 0)
2812
- return { status: "unavailable", unit: path };
3245
+ return { status: "unavailable", unit: path2 };
2813
3246
  if (s.systemctl(["disable", "--now", s.unit]).status !== 0)
2814
- return { status: "failed", unit: path };
2815
- rmSync(path);
3247
+ return { status: "failed", unit: path2 };
3248
+ rmSync(path2);
2816
3249
  s.systemctl(["daemon-reload"]);
2817
3250
  s.systemctl(["reset-failed", s.unit]);
2818
- return { status: "removed", unit: path };
3251
+ return { status: "removed", unit: path2 };
2819
3252
  }
2820
3253
  function autostartStatus(s) {
2821
3254
  if (s.platform !== "linux")
2822
3255
  return { status: "unsupported" };
2823
- const path = unitPath(s);
2824
- const previous = readGenerated(path, MARKER);
3256
+ const path2 = unitPath(s);
3257
+ const previous = readGenerated(path2, MARKER);
2825
3258
  if (previous === null)
2826
- return { status: "foreign", unit: path };
3259
+ return { status: "foreign", unit: path2 };
2827
3260
  if (previous === undefined)
2828
3261
  return { status: "absent" };
2829
3262
  return {
2830
3263
  status: s.systemctl(["is-active", s.unit]).stdout.trim() || "unknown",
2831
3264
  enabled: s.systemctl(["is-enabled", s.unit]).stdout.trim() || "unknown",
2832
- unit: path,
3265
+ unit: path2,
2833
3266
  workspace: unitWorkspace(previous),
2834
3267
  ...bootStart(s, false)
2835
3268
  };
2836
3269
  }
2837
3270
 
2838
3271
  // src/service/coordination.ts
2839
- import { randomBytes, randomUUID as randomUUID3 } from "node:crypto";
3272
+ import { randomBytes as randomBytes2, randomUUID as randomUUID3 } from "node:crypto";
2840
3273
  import { opendirSync, realpathSync as realpathSync3 } from "node:fs";
2841
3274
  import { hostname as hostname2 } from "node:os";
2842
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join4, relative as relative3, sep as sep4 } from "node:path";
2843
-
2844
- // src/service/private-files.ts
2845
- import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
2846
- import {
2847
- closeSync as closeSync2,
2848
- constants as constants2,
2849
- fstatSync as fstatSync2,
2850
- fsyncSync as fsyncSync2,
2851
- lstatSync as lstatSync3,
2852
- mkdirSync as mkdirSync3,
2853
- openSync as openSync2,
2854
- readSync as readSync2,
2855
- renameSync as renameSync3,
2856
- unlinkSync as unlinkSync2,
2857
- writeFileSync as writeFileSync3
2858
- } from "node:fs";
2859
- import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3, relative as relative2, resolve as resolve3, sep as sep3 } from "node:path";
2860
- var METADATA_BYTES = 16 * 1024;
2861
- var REGISTRY_BYTES = 1024 * 1024;
2862
- var REGISTRY_ENTRIES = 128;
2863
- function refused() {
2864
- throw new ContractError("corrupt-selected");
2865
- }
2866
- function hash(bytes) {
2867
- return createHash2("sha256").update(bytes).digest("hex");
2868
- }
2869
- function privateMode(st, ownerOnly = true) {
2870
- if (process.platform !== "win32" && (ownerOnly && (st.mode & 63) !== 0 || process.getuid && st.uid !== process.getuid()))
2871
- refused();
2872
- }
2873
- function same(a, b) {
2874
- return a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.ctimeMs === b.ctimeMs && a.mtimeMs === b.mtimeMs && a.mode === b.mode && a.uid === b.uid && a.nlink === b.nlink;
2875
- }
2876
- function missing(error) {
2877
- return error instanceof Error && "code" in error && error.code === "ENOENT";
2878
- }
2879
- var job = 0;
2880
- var advancing = false;
2881
- function currentJob() {
2882
- if (!advancing) {
2883
- advancing = true;
2884
- queueMicrotask(() => {
2885
- job++;
2886
- advancing = false;
2887
- });
2888
- }
2889
- return job;
2890
- }
2891
-
2892
- class DirectoryWalks {
2893
- job = currentJob();
2894
- walked = new Map;
2895
- open() {
2896
- if (this.job === job)
2897
- return true;
2898
- this.walked.clear();
2899
- return false;
2900
- }
2901
- remember(path, st) {
2902
- if (this.open())
2903
- this.walked.set(path, { dev: st.dev, ino: st.ino });
2904
- }
2905
- reuse(path, ownerOnly = true) {
2906
- const walked = this.open() ? this.walked.get(path) : undefined;
2907
- if (!walked)
2908
- return false;
2909
- let st;
2910
- try {
2911
- st = lstatSync3(path);
2912
- } catch (error) {
2913
- if (!missing(error))
2914
- throw error;
2915
- this.walked.delete(path);
2916
- return false;
2917
- }
2918
- if (!st.isDirectory() || st.isSymbolicLink() || st.dev !== walked.dev || st.ino !== walked.ino) {
2919
- this.walked.delete(path);
2920
- return false;
2921
- }
2922
- privateMode(st, ownerOnly);
2923
- return true;
2924
- }
2925
- }
2926
-
2927
- class PrivateFiles {
2928
- namespace;
2929
- ownerOnly;
2930
- constructor(namespace, ownerOnly = true) {
2931
- this.namespace = namespace;
2932
- this.ownerOnly = ownerOnly;
2933
- }
2934
- directory(path, create = false, walks) {
2935
- const tail = relative2(this.namespace, path);
2936
- if (isAbsolute3(tail) || tail === ".." || tail.startsWith(`..${sep3}`))
2937
- refused();
2938
- if (!create) {
2939
- const full = resolve3(path);
2940
- if (walks?.reuse(full, this.ownerOnly))
2941
- return true;
2942
- const namespace = resolve3(this.namespace);
2943
- let inside = false, last;
2944
- const found = safeDirectory(path, false, (at2, st) => {
2945
- if (at2 === namespace)
2946
- inside = true;
2947
- if (inside)
2948
- privateMode(st, this.ownerOnly);
2949
- last = st;
2950
- });
2951
- if (found && last)
2952
- walks?.remember(full, last);
2953
- return found;
2954
- }
2955
- if (!safeDirectory(this.namespace, true))
2956
- return false;
2957
- privateMode(lstatSync3(this.namespace), this.ownerOnly);
2958
- let at = this.namespace;
2959
- for (const part of tail.split(sep3).filter(Boolean)) {
2960
- at = join3(at, part);
2961
- let st;
2962
- try {
2963
- st = lstatSync3(at);
2964
- } catch (error) {
2965
- if (!missing(error))
2966
- throw error;
2967
- try {
2968
- mkdirSync3(at, { mode: 448 });
2969
- } catch (mkdirError) {
2970
- if (!(mkdirError instanceof Error && ("code" in mkdirError) && mkdirError.code === "EEXIST"))
2971
- throw mkdirError;
2972
- }
2973
- st = lstatSync3(at);
2974
- }
2975
- if (!st.isDirectory() || st.isSymbolicLink())
2976
- throw new Error("Unsafe directory");
2977
- privateMode(st, this.ownerOnly);
2978
- }
2979
- return true;
2980
- }
2981
- read(path, maximum = METADATA_BYTES, walks) {
2982
- if (!this.directory(dirname3(path), false, walks))
2983
- return;
2984
- let before;
2985
- try {
2986
- before = lstatSync3(path);
2987
- } catch (error) {
2988
- if (missing(error))
2989
- return;
2990
- return refused();
2991
- }
2992
- if (!before.isFile() || before.nlink !== 1 || before.size > maximum)
2993
- refused();
2994
- privateMode(before, this.ownerOnly);
2995
- const fd = openSync2(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0));
2996
- try {
2997
- const opened = fstatSync2(fd);
2998
- if (!same(before, opened))
2999
- refused();
3000
- const buffer = Buffer.alloc(opened.size + 1);
3001
- let size = 0;
3002
- while (size < buffer.length) {
3003
- const n = readSync2(fd, buffer, size, buffer.length - size, null);
3004
- if (!n)
3005
- break;
3006
- size += n;
3007
- }
3008
- const bytes = buffer.subarray(0, size);
3009
- if (size !== opened.size || !same(opened, fstatSync2(fd)) || !same(opened, lstatSync3(path)))
3010
- refused();
3011
- return { bytes, identity: hash(bytes), stamp: stamp(opened) };
3012
- } finally {
3013
- closeSync2(fd);
3014
- }
3015
- }
3016
- assert(path, expected, maximum = METADATA_BYTES, walks) {
3017
- const current = this.read(path, maximum, walks);
3018
- if (current?.identity !== expected?.identity || current?.stamp !== expected?.stamp)
3019
- throw new ContractError("conflict");
3020
- }
3021
- create(path, bytes, maximum = METADATA_BYTES) {
3022
- if (Buffer.byteLength(bytes) > maximum)
3023
- throw new ContractError("oversize");
3024
- this.directory(dirname3(path), true);
3025
- const fd = retryWindowsFileOperation(() => openSync2(path, "wx", 384));
3026
- try {
3027
- writeFileSync3(fd, bytes);
3028
- fsyncSync2(fd);
3029
- } finally {
3030
- closeSync2(fd);
3031
- }
3032
- this.syncDirectory(dirname3(path));
3033
- const result = this.read(path, maximum);
3034
- if (!result || result.identity !== hash(bytes))
3035
- refused();
3036
- return result;
3037
- }
3038
- replace(path, expected, bytes, maximum = METADATA_BYTES) {
3039
- this.assert(path, expected, maximum);
3040
- const temporary = join3(dirname3(path), `pending-${randomUUID2()}.json`);
3041
- this.create(temporary, bytes, maximum);
3042
- this.assert(path, expected, maximum);
3043
- retryWindowsFileOperation(() => renameSync3(temporary, path), () => this.assert(path, expected, maximum));
3044
- this.syncDirectory(dirname3(path));
3045
- const result = this.read(path, maximum);
3046
- if (!result || result.identity !== hash(bytes))
3047
- refused();
3048
- return result;
3049
- }
3050
- remove(path, expected, maximum = METADATA_BYTES) {
3051
- this.assert(path, expected, maximum);
3052
- retryWindowsFileOperation(() => unlinkSync2(path), () => this.assert(path, expected, maximum));
3053
- this.syncDirectory(dirname3(path));
3054
- }
3055
- syncDirectory(path) {
3056
- if (process.platform === "win32")
3057
- return;
3058
- const fd = openSync2(path, constants2.O_RDONLY | constants2.O_DIRECTORY);
3059
- try {
3060
- fsyncSync2(fd);
3061
- } finally {
3062
- closeSync2(fd);
3063
- }
3064
- }
3065
- }
3066
- function stamp(st) {
3067
- return [
3068
- st.dev,
3069
- st.ino,
3070
- st.size,
3071
- st.mode,
3072
- st.uid,
3073
- st.nlink,
3074
- st.ctimeMs,
3075
- st.mtimeMs
3076
- ].join(":");
3077
- }
3078
- function json(snapshot, maximum = METADATA_BYTES) {
3079
- try {
3080
- return parseWireJson(new TextDecoder("utf-8", { fatal: true }).decode(snapshot.bytes), maximum);
3081
- } catch {
3082
- return refused();
3083
- }
3084
- }
3085
- function encoded(value) {
3086
- return `${JSON.stringify(value)}
3087
- `;
3088
- }
3089
-
3090
- // src/service/coordination.ts
3275
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join5, relative as relative3, sep as sep4 } from "node:path";
3091
3276
  var registrationKeys = [
3092
3277
  "schema",
3093
3278
  "root",
@@ -3103,10 +3288,10 @@ var registrationKeys = [
3103
3288
  ];
3104
3289
  var reservationKeys = registrationKeys.filter((k) => !["schema", "phase", "checkoutToken"].includes(k));
3105
3290
  function pathValue(value) {
3106
- const path = text(value, 4096, true);
3107
- if (!isAbsolute4(path))
3291
+ const path2 = text(value, 4096, true);
3292
+ if (!isAbsolute4(path2))
3108
3293
  refused();
3109
- return path;
3294
+ return path2;
3110
3295
  }
3111
3296
  function endpoint(value, allowZero = false) {
3112
3297
  const s = text(value, 128, true);
@@ -3165,8 +3350,8 @@ function rootsOverlap(a, b) {
3165
3350
  };
3166
3351
  return within(a, b) || within(b, a);
3167
3352
  }
3168
- function names(path, allowed) {
3169
- const dir = opendirSync(path);
3353
+ function names(path2, allowed) {
3354
+ const dir = opendirSync(path2);
3170
3355
  try {
3171
3356
  let count = 0;
3172
3357
  for (let entry = dir.readSync();entry; entry = dir.readSync()) {
@@ -3213,17 +3398,17 @@ class ServiceCoordination {
3213
3398
  stateHome;
3214
3399
  constructor(workspace, stateHome) {
3215
3400
  this.store = new TaskStore(resolveWorkspace(workspace).root, stateHome);
3216
- const namespace = dirname4(this.store.privateRoot);
3401
+ const namespace = dirname5(this.store.privateRoot);
3217
3402
  this.files = new PrivateFiles(namespace);
3218
- this.stateHome = dirname4(namespace);
3219
- this.directory = join4(this.store.privateRoot, "service-v1");
3220
- this.registryDirectory = join4(namespace, "service-registry-v1");
3403
+ this.stateHome = dirname5(namespace);
3404
+ this.directory = join5(this.store.privateRoot, "service-v1");
3405
+ this.registryDirectory = join5(namespace, "service-registry-v1");
3221
3406
  }
3222
3407
  path(name) {
3223
- return join4(this.directory, name);
3408
+ return join5(this.directory, name);
3224
3409
  }
3225
3410
  registryPath(name) {
3226
- return join4(this.registryDirectory, name);
3411
+ return join5(this.registryDirectory, name);
3227
3412
  }
3228
3413
  registry() {
3229
3414
  if (!this.files.directory(this.registryDirectory))
@@ -3271,7 +3456,7 @@ class ServiceCoordination {
3271
3456
  "host-config.json",
3272
3457
  "history-v2"
3273
3458
  ]);
3274
- this.files.directory(join4(this.directory, "history-v2"), false, walks);
3459
+ this.files.directory(join5(this.directory, "history-v2"), false, walks);
3275
3460
  }
3276
3461
  selected() {
3277
3462
  const walks = new DirectoryWalks;
@@ -3431,7 +3616,7 @@ class ServiceCoordination {
3431
3616
  throw error;
3432
3617
  }
3433
3618
  }
3434
- acquire(input, resolvedEndpoint = input.endpoint, initializeNew) {
3619
+ acquire(input, resolvedEndpoint = input.endpoint, initializeNew, reselectExisting) {
3435
3620
  const config = decodeOwnerConfig(input);
3436
3621
  const bound = endpoint(resolvedEndpoint);
3437
3622
  if (!endpointMatches(config.endpoint, bound))
@@ -3446,7 +3631,8 @@ class ServiceCoordination {
3446
3631
  throw new ContractError("quarantine");
3447
3632
  if (this.store.inspectLock())
3448
3633
  throw new ContractError("conflict");
3449
- if (old && old.config.identity !== hash(encoded(config)))
3634
+ const reselect = !!old && old.config.identity !== hash(encoded(config));
3635
+ if (reselect && (!reselectExisting || !old?.hostConfig))
3450
3636
  throw new ContractError("conflict");
3451
3637
  const row = {
3452
3638
  root: this.store.root,
@@ -3463,7 +3649,17 @@ class ServiceCoordination {
3463
3649
  ...registry.value,
3464
3650
  entries: [...registry.value.entries, row]
3465
3651
  }), REGISTRY_BYTES);
3466
- this.initialize(config);
3652
+ if (old?.hostConfig && reselect && reselectExisting) {
3653
+ reselectExisting(old.hostConfig);
3654
+ const saved = this.files.replace(this.path("config.json"), old.config, encoded(config));
3655
+ this.files.replace(this.path("format.json"), old.manifest, encoded({
3656
+ schema: "cadence-service-format/1",
3657
+ root: this.store.root,
3658
+ stateHome: this.stateHome,
3659
+ configIdentity: saved.identity
3660
+ }));
3661
+ } else
3662
+ this.initialize(config);
3467
3663
  if (!old)
3468
3664
  initializeNew?.();
3469
3665
  const checkout = this.store.acquire();
@@ -3480,7 +3676,7 @@ class ServiceCoordination {
3480
3676
  const capability = this.files.create(this.path("local-capability.json"), encoded({
3481
3677
  schema: "cadence-service-local-capability/1",
3482
3678
  epoch: row.epoch,
3483
- capability: randomBytes(32).toString("base64url")
3679
+ capability: randomBytes2(32).toString("base64url")
3484
3680
  }));
3485
3681
  const value = {
3486
3682
  schema: "cadence-service-registration/1",
@@ -3693,12 +3889,12 @@ class OwnerLease {
3693
3889
 
3694
3890
  // src/service/launchd.ts
3695
3891
  import { rmSync as rmSync2 } from "node:fs";
3696
- import { join as join5 } from "node:path";
3892
+ import { join as join6 } from "node:path";
3697
3893
  var LABEL = "com.abelxiaoxing.cadence";
3698
3894
  var MARKER2 = `<?xml version="1.0" encoding="UTF-8"?>
3699
3895
  <!-- Generated by @abelxiaoxing/cadence`;
3700
3896
  function plistPath(s) {
3701
- return join5(s.home, "Library", "LaunchAgents", `${LABEL}.plist`);
3897
+ return join6(s.home, "Library", "LaunchAgents", `${LABEL}.plist`);
3702
3898
  }
3703
3899
  var xml = (value) => checked(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
3704
3900
  function buildPlist(u) {
@@ -3713,7 +3909,16 @@ function buildPlist(u) {
3713
3909
  ` <string>${LABEL}</string>`,
3714
3910
  "\t<key>ProgramArguments</key>",
3715
3911
  "\t<array>",
3716
- ...[u.node, u.cli, "start", "--foreground", "--workspace", u.workspace].map(string),
3912
+ ...[
3913
+ u.node,
3914
+ u.cli,
3915
+ "start",
3916
+ "--foreground",
3917
+ "--workspace",
3918
+ u.workspace,
3919
+ "--server-config",
3920
+ u.serverConfig
3921
+ ].map(string),
3717
3922
  "\t</array>",
3718
3923
  "\t<key>WorkingDirectory</key>",
3719
3924
  ` <string>${xml(u.workspace)}</string>`,
@@ -3751,10 +3956,10 @@ function disabled(s) {
3751
3956
  async function configureLaunchAgent(s, mode, requested) {
3752
3957
  if (s.uid === undefined)
3753
3958
  return { status: "unavailable" };
3754
- const path = plistPath(s);
3755
- const previous = readGenerated(path, MARKER2);
3959
+ const path2 = plistPath(s);
3960
+ const previous = readGenerated(path2, MARKER2);
3756
3961
  if (previous === null)
3757
- return { status: "foreign", unit: path };
3962
+ return { status: "foreign", unit: path2 };
3758
3963
  if (mode === "restart" && previous === undefined)
3759
3964
  return { status: "absent" };
3760
3965
  const sdk = sdkPackages(s.root);
@@ -3764,18 +3969,19 @@ async function configureLaunchAgent(s, mode, requested) {
3764
3969
  const { workspace, redirectedFrom } = selectWorkspace(s, requested, kept);
3765
3970
  const refused2 = await settle(s, kept);
3766
3971
  if (refused2)
3767
- return { ...refused2, unit: path };
3972
+ return { ...refused2, unit: path2 };
3768
3973
  if (loaded(s))
3769
3974
  s.launchctl(["bootout", target(s)]);
3770
- write(path, buildPlist({
3975
+ write(path2, buildPlist({
3771
3976
  node: s.node,
3772
- cli: join5(s.root, "src", "service-cli.mjs"),
3977
+ cli: join6(s.root, "src", "service-cli.mjs"),
3773
3978
  workspace,
3979
+ serverConfig: serverConfigPath(s.home),
3774
3980
  environment: serviceEnvironment(s),
3775
- log: join5(s.home, "Library", "Logs", "cadence.log")
3981
+ log: join6(s.home, "Library", "Logs", "cadence.log")
3776
3982
  }));
3777
3983
  const result = {
3778
- unit: path,
3984
+ unit: path2,
3779
3985
  workspace,
3780
3986
  ...redirectedFrom ? { redirectedFrom } : {}
3781
3987
  };
@@ -3783,63 +3989,63 @@ async function configureLaunchAgent(s, mode, requested) {
3783
3989
  s.launchctl(["enable", target(s)]);
3784
3990
  else if (disabled(s))
3785
3991
  return { status: "disabled", ...result };
3786
- if (!regularFile(join5(agentDirectory(s), "auth.json")))
3992
+ if (!regularFile(join6(agentDirectory(s), "auth.json")))
3787
3993
  return { status: "waiting-for-pi-login", ...result };
3788
- const started = s.launchctl(["bootstrap", domain(s), path]).status === 0;
3994
+ const started = s.launchctl(["bootstrap", domain(s), path2]).status === 0;
3789
3995
  return { status: started ? "started" : "failed", ...result };
3790
3996
  }
3791
3997
  async function stopLaunchAgent(s) {
3792
- const path = plistPath(s);
3793
- const previous = readGenerated(path, MARKER2);
3998
+ const path2 = plistPath(s);
3999
+ const previous = readGenerated(path2, MARKER2);
3794
4000
  if (previous === null)
3795
- return { status: "foreign", unit: path };
4001
+ return { status: "foreign", unit: path2 };
3796
4002
  if (previous === undefined)
3797
4003
  return { status: "absent" };
3798
4004
  const workspace = plistWorkspace(previous);
3799
4005
  const refused2 = await settle(s, workspace);
3800
- return { ...refused2 ?? { status: "stopped", workspace }, unit: path };
4006
+ return { ...refused2 ?? { status: "stopped", workspace }, unit: path2 };
3801
4007
  }
3802
4008
  async function removeLaunchAgent(s) {
3803
- const path = plistPath(s);
3804
- const previous = readGenerated(path, MARKER2);
4009
+ const path2 = plistPath(s);
4010
+ const previous = readGenerated(path2, MARKER2);
3805
4011
  if (previous === null)
3806
- return { status: "foreign", unit: path };
4012
+ return { status: "foreign", unit: path2 };
3807
4013
  if (previous === undefined)
3808
4014
  return { status: "absent" };
3809
4015
  const refused2 = await settle(s, plistWorkspace(previous));
3810
4016
  if (refused2)
3811
- return { ...refused2, unit: path };
4017
+ return { ...refused2, unit: path2 };
3812
4018
  if (s.uid !== undefined && loaded(s))
3813
4019
  s.launchctl(["bootout", target(s)]);
3814
- rmSync2(path);
3815
- return { status: "removed", unit: path };
4020
+ rmSync2(path2);
4021
+ return { status: "removed", unit: path2 };
3816
4022
  }
3817
4023
  function launchAgentStatus(s) {
3818
- const path = plistPath(s);
3819
- const previous = readGenerated(path, MARKER2);
4024
+ const path2 = plistPath(s);
4025
+ const previous = readGenerated(path2, MARKER2);
3820
4026
  if (previous === null)
3821
- return { status: "foreign", unit: path };
4027
+ return { status: "foreign", unit: path2 };
3822
4028
  if (previous === undefined)
3823
4029
  return { status: "absent" };
3824
4030
  const printed = s.uid === undefined ? undefined : s.launchctl(["print", target(s)]);
3825
4031
  return {
3826
4032
  status: printed?.status !== 0 ? "unloaded" : /^\s*state = running$/m.test(printed.stdout) ? "active" : "inactive",
3827
4033
  enabled: s.uid !== undefined && disabled(s) ? "disabled" : "enabled",
3828
- unit: path,
4034
+ unit: path2,
3829
4035
  workspace: plistWorkspace(previous)
3830
4036
  };
3831
4037
  }
3832
4038
 
3833
4039
  // src/service/login-run.ts
3834
4040
  import { rmSync as rmSync3 } from "node:fs";
3835
- import { isAbsolute as isAbsolute5, join as join6 } from "node:path";
4041
+ import { isAbsolute as isAbsolute5, join as join7 } from "node:path";
3836
4042
  var RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
3837
4043
  var APPROVED_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run";
3838
4044
  var RUN_VALUE = "Cadence";
3839
4045
  var MARKER3 = "' Generated by @abelxiaoxing/cadence";
3840
4046
  function launcherPath(s) {
3841
4047
  const local = s.env.LOCALAPPDATA;
3842
- return join6(local && isAbsolute5(local) ? local : join6(s.home, "AppData", "Local"), "cadence-server", "cadence.vbs");
4048
+ return join7(local && isAbsolute5(local) ? local : join7(s.home, "AppData", "Local"), "cadence-server", "cadence.vbs");
3843
4049
  }
3844
4050
  var literal2 = (value) => `"${checked(value).replaceAll('"', '""')}"`;
3845
4051
  function argument2(value) {
@@ -3848,7 +4054,7 @@ function argument2(value) {
3848
4054
  return `"${value}"`;
3849
4055
  }
3850
4056
  function buildLauncher(u) {
3851
- const command = `${argument2(u.node)} ${argument2(u.cli)} start --workspace ${argument2(u.workspace)}`;
4057
+ const command = `${argument2(u.node)} ${argument2(u.cli)} start --workspace ${argument2(u.workspace)} --server-config ${argument2(u.serverConfig)}`;
3852
4058
  return [
3853
4059
  `${MARKER3}; \`cadence server install\` and \`restart\` rewrite this file.`,
3854
4060
  `' Started at login by ${RUN_KEY}\\${RUN_VALUE}; window style 0 hides the console.`,
@@ -3865,7 +4071,7 @@ var utf16 = (text2) => Buffer.concat([Buffer.from([255, 254]), Buffer.from(text2
3865
4071
  function launcherWorkspace(text2) {
3866
4072
  return text2?.match(/^shell\.CurrentDirectory = "((?:[^"]|"")*)"\r?$/m)?.[1].replaceAll('""', '"');
3867
4073
  }
3868
- var runEntry = (path, s) => `"${wscript(s.env)}" "${path}"`;
4074
+ var runEntry = (path2, s) => `"${wscript(s.env)}" "${path2}"`;
3869
4075
  var present = (s, key) => s.reg(["query", key, "/v", RUN_VALUE]).status === 0;
3870
4076
  function approvedOff(s) {
3871
4077
  const r = s.reg(["query", APPROVED_KEY, "/v", RUN_VALUE]);
@@ -3873,10 +4079,10 @@ function approvedOff(s) {
3873
4079
  return !!first && (Number.parseInt(first[1], 16) & 1) === 1;
3874
4080
  }
3875
4081
  async function configureLoginRun(s, mode, requested) {
3876
- const path = launcherPath(s);
3877
- const previous = readGenerated(path, MARKER3);
4082
+ const path2 = launcherPath(s);
4083
+ const previous = readGenerated(path2, MARKER3);
3878
4084
  if (previous === null)
3879
- return { status: "foreign", unit: path };
4085
+ return { status: "foreign", unit: path2 };
3880
4086
  if (previous === undefined && present(s, RUN_KEY))
3881
4087
  return { status: "foreign", unit: `${RUN_KEY}\\${RUN_VALUE}` };
3882
4088
  if (mode === "restart" && previous === undefined)
@@ -3888,61 +4094,62 @@ async function configureLoginRun(s, mode, requested) {
3888
4094
  const { workspace, redirectedFrom } = selectWorkspace(s, requested, kept);
3889
4095
  const refused2 = await settle(s, kept);
3890
4096
  if (refused2)
3891
- return { ...refused2, unit: path };
4097
+ return { ...refused2, unit: path2 };
3892
4098
  const environment = s.env.PI_CODING_AGENT_DIR ? [["PI_CODING_AGENT_DIR", agentDirectory(s)]] : [];
3893
- write(path, utf16(buildLauncher({
4099
+ write(path2, utf16(buildLauncher({
3894
4100
  node: s.node,
3895
- cli: join6(s.root, "src", "service-cli.mjs"),
4101
+ cli: join7(s.root, "src", "service-cli.mjs"),
3896
4102
  workspace,
4103
+ serverConfig: serverConfigPath(s.home),
3897
4104
  environment
3898
4105
  })));
3899
4106
  const result = {
3900
- unit: path,
4107
+ unit: path2,
3901
4108
  workspace,
3902
4109
  ...redirectedFrom ? { redirectedFrom } : {}
3903
4110
  };
3904
4111
  if (mode === "install") {
3905
- const entry = ["/v", RUN_VALUE, "/t", "REG_SZ", "/d", runEntry(path, s)];
4112
+ const entry = ["/v", RUN_VALUE, "/t", "REG_SZ", "/d", runEntry(path2, s)];
3906
4113
  if (s.reg(["add", RUN_KEY, ...entry, "/f"]).status !== 0)
3907
4114
  return { status: "failed", ...result };
3908
4115
  s.reg(["delete", APPROVED_KEY, "/v", RUN_VALUE, "/f"]);
3909
4116
  }
3910
- if (!regularFile(join6(agentDirectory(s), "auth.json")))
4117
+ if (!regularFile(join7(agentDirectory(s), "auth.json")))
3911
4118
  return { status: "waiting-for-pi-login", ...result };
3912
- return { status: s.launch(path) ? "started" : "failed", ...result };
4119
+ return { status: s.launch(path2) ? "started" : "failed", ...result };
3913
4120
  }
3914
4121
  async function stopLoginRun(s) {
3915
- const path = launcherPath(s);
3916
- const previous = readGenerated(path, MARKER3);
4122
+ const path2 = launcherPath(s);
4123
+ const previous = readGenerated(path2, MARKER3);
3917
4124
  if (previous === null)
3918
- return { status: "foreign", unit: path };
4125
+ return { status: "foreign", unit: path2 };
3919
4126
  if (previous === undefined)
3920
4127
  return { status: "absent" };
3921
4128
  const workspace = launcherWorkspace(previous);
3922
4129
  const refused2 = await settle(s, workspace);
3923
- return { ...refused2 ?? { status: "stopped", workspace }, unit: path };
4130
+ return { ...refused2 ?? { status: "stopped", workspace }, unit: path2 };
3924
4131
  }
3925
4132
  async function removeLoginRun(s) {
3926
- const path = launcherPath(s);
3927
- const previous = readGenerated(path, MARKER3);
4133
+ const path2 = launcherPath(s);
4134
+ const previous = readGenerated(path2, MARKER3);
3928
4135
  if (previous === null)
3929
- return { status: "foreign", unit: path };
4136
+ return { status: "foreign", unit: path2 };
3930
4137
  if (previous === undefined)
3931
4138
  return { status: "absent" };
3932
4139
  const refused2 = await settle(s, launcherWorkspace(previous));
3933
4140
  if (refused2)
3934
- return { ...refused2, unit: path };
4141
+ return { ...refused2, unit: path2 };
3935
4142
  for (const key of [RUN_KEY, APPROVED_KEY])
3936
4143
  if (present(s, key))
3937
4144
  s.reg(["delete", key, "/v", RUN_VALUE, "/f"]);
3938
- rmSync3(path);
3939
- return { status: "removed", unit: path };
4145
+ rmSync3(path2);
4146
+ return { status: "removed", unit: path2 };
3940
4147
  }
3941
4148
  async function loginRunStatus(s) {
3942
- const path = launcherPath(s);
3943
- const previous = readGenerated(path, MARKER3);
4149
+ const path2 = launcherPath(s);
4150
+ const previous = readGenerated(path2, MARKER3);
3944
4151
  if (previous === null)
3945
- return { status: "foreign", unit: path };
4152
+ return { status: "foreign", unit: path2 };
3946
4153
  if (previous === undefined)
3947
4154
  return { status: "absent" };
3948
4155
  const workspace = launcherWorkspace(previous);
@@ -3950,7 +4157,7 @@ async function loginRunStatus(s) {
3950
4157
  return {
3951
4158
  status: owner === "alive" ? "active" : "inactive",
3952
4159
  enabled: present(s, RUN_KEY) && !approvedOff(s) ? "enabled" : "disabled",
3953
- unit: path,
4160
+ unit: path2,
3954
4161
  workspace
3955
4162
  };
3956
4163
  }
@@ -3961,7 +4168,7 @@ import { isatty } from "node:tty";
3961
4168
  // src/history/recovery.ts
3962
4169
  import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
3963
4170
  import { lstatSync as lstatSync6, opendirSync as opendirSync3 } from "node:fs";
3964
- import { join as join8, relative as relative4 } from "node:path";
4171
+ import { join as join9, relative as relative4 } from "node:path";
3965
4172
 
3966
4173
  // src/service/recovery.ts
3967
4174
  async function inspectRecovery(coordination, history) {
@@ -4009,19 +4216,19 @@ async function resolveRecovery(coordination, confirmation, history) {
4009
4216
  }
4010
4217
 
4011
4218
  // src/history/files.ts
4012
- import { createHash as createHash4, randomBytes as randomBytes2, randomUUID as randomUUID4 } from "node:crypto";
4219
+ import { createHash as createHash4, randomBytes as randomBytes3, randomUUID as randomUUID4 } from "node:crypto";
4013
4220
  import {
4014
- closeSync as closeSync4,
4015
- constants as constants4,
4221
+ closeSync as closeSync5,
4222
+ constants as constants5,
4016
4223
  fstatSync as fstatSync4,
4017
4224
  fsyncSync as fsyncSync3,
4018
4225
  lstatSync as lstatSync5,
4019
4226
  opendirSync as opendirSync2,
4020
- openSync as openSync4,
4227
+ openSync as openSync5,
4021
4228
  statfsSync
4022
4229
  } from "node:fs";
4023
4230
  import { open } from "node:fs/promises";
4024
- import { dirname as dirname6, join as join7 } from "node:path";
4231
+ import { dirname as dirname7, join as join8 } from "node:path";
4025
4232
 
4026
4233
  // src/history/admission.ts
4027
4234
  function decodeRunAdmission(value) {
@@ -4042,44 +4249,44 @@ function decodeRunAdmission(value) {
4042
4249
 
4043
4250
  // src/history/read-io.ts
4044
4251
  import {
4045
- closeSync as closeSync3,
4046
- constants as constants3,
4252
+ closeSync as closeSync4,
4253
+ constants as constants4,
4047
4254
  fstatSync as fstatSync3,
4048
4255
  lstatSync as lstatSync4,
4049
- openSync as openSync3,
4256
+ openSync as openSync4,
4050
4257
  readSync as readSync3
4051
4258
  } from "node:fs";
4052
- import { dirname as dirname5 } from "node:path";
4259
+ import { dirname as dirname6 } from "node:path";
4053
4260
  function bad() {
4054
4261
  throw new ContractError("corrupt-selected");
4055
4262
  }
4056
4263
  var stamp2 = (s) => [s.dev, s.ino, s.size, s.mode, s.uid, s.nlink, s.ctimeMs, s.mtimeMs].join(":");
4057
- function selected(files, path, read, walks) {
4058
- if (!files.files.directory(dirname5(path), false, walks))
4264
+ function selected(files, path2, read, walks) {
4265
+ if (!files.files.directory(dirname6(path2), false, walks))
4059
4266
  bad();
4060
- const before = lstatSync4(path);
4267
+ const before = lstatSync4(path2);
4061
4268
  if (!before.isFile() || before.nlink !== 1 || process.platform !== "win32" && (before.mode & 63 || process.getuid && before.uid !== process.getuid()))
4062
4269
  bad();
4063
- const fd = openSync3(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
4270
+ const fd = openSync4(path2, constants4.O_RDONLY | (constants4.O_NOFOLLOW ?? 0));
4064
4271
  try {
4065
4272
  if (stamp2(fstatSync3(fd)) !== stamp2(before))
4066
4273
  bad();
4067
4274
  const result = read(fd, before.size, stamp2(before));
4068
- if (stamp2(fstatSync3(fd)) !== stamp2(before) || stamp2(lstatSync4(path)) !== stamp2(before))
4275
+ if (stamp2(fstatSync3(fd)) !== stamp2(before) || stamp2(lstatSync4(path2)) !== stamp2(before))
4069
4276
  bad();
4070
4277
  return result;
4071
4278
  } finally {
4072
- closeSync3(fd);
4279
+ closeSync4(fd);
4073
4280
  }
4074
4281
  }
4075
- function assertIncarnation(files, path, expected) {
4076
- selected(files, path, (_fd, _size, current) => {
4282
+ function assertIncarnation(files, path2, expected) {
4283
+ selected(files, path2, (_fd, _size, current) => {
4077
4284
  if (current !== expected)
4078
4285
  bad();
4079
4286
  });
4080
4287
  }
4081
- function statSize(files, path, maximum) {
4082
- return selected(files, path, (_fd, size) => {
4288
+ function statSize(files, path2, maximum) {
4289
+ return selected(files, path2, (_fd, size) => {
4083
4290
  if (size > maximum)
4084
4291
  bad();
4085
4292
  return size;
@@ -4552,14 +4759,14 @@ class HistoryFiles {
4552
4759
  this.coordination = coordination;
4553
4760
  this.format = format;
4554
4761
  this.formatSnapshot = formatSnapshot;
4555
- this.directory = join7(coordination.directory, "history-v2");
4762
+ this.directory = join8(coordination.directory, "history-v2");
4556
4763
  this.files = coordination.files;
4557
4764
  }
4558
4765
  static open(coordination) {
4559
- const dir = join7(coordination.directory, "history-v2");
4766
+ const dir = join8(coordination.directory, "history-v2");
4560
4767
  if (!coordination.files.directory(dir))
4561
4768
  return;
4562
- const file = coordination.files.read(join7(dir, "format.json"));
4769
+ const file = coordination.files.read(join8(dir, "format.json"));
4563
4770
  if (!file)
4564
4771
  corrupt();
4565
4772
  const format = decodeFormat(json(file));
@@ -4575,7 +4782,7 @@ class HistoryFiles {
4575
4782
  existing.assertWritable();
4576
4783
  return existing;
4577
4784
  }
4578
- const dir = join7(c.directory, "history-v2");
4785
+ const dir = join8(c.directory, "history-v2");
4579
4786
  const format = {
4580
4787
  schema: FORMAT,
4581
4788
  storeId: randomUUID4(),
@@ -4585,19 +4792,19 @@ class HistoryFiles {
4585
4792
  c.files.directory(dir, true);
4586
4793
  for (const name of ["commands", "ledger", "runs", "indexes"]) {
4587
4794
  lease.assert();
4588
- c.files.directory(join7(dir, name), true);
4795
+ c.files.directory(join8(dir, name), true);
4589
4796
  }
4590
4797
  lease.assert();
4591
- const keyPath = join7(dir, "read-proof-key.json");
4798
+ const keyPath = join8(dir, "read-proof-key.json");
4592
4799
  c.files.create(keyPath, encoded({
4593
4800
  schema: "cadence-history-read-key/1",
4594
4801
  storeId: format.storeId,
4595
- key: randomBytes2(32).toString("base64url")
4802
+ key: randomBytes3(32).toString("base64url")
4596
4803
  }));
4597
4804
  lease.assert();
4598
- const snapshot = c.files.create(join7(dir, "format.json"), encoded(format));
4805
+ const snapshot = c.files.create(join8(dir, "format.json"), encoded(format));
4599
4806
  const result = new HistoryFiles(c, format, snapshot);
4600
- result.syncParents(join7(dir, "format.json"));
4807
+ result.syncParents(join8(dir, "format.json"));
4601
4808
  result.assertWritable();
4602
4809
  return result;
4603
4810
  }
@@ -4632,7 +4839,7 @@ class HistoryFiles {
4632
4839
  throw new ContractError("quarantine");
4633
4840
  }
4634
4841
  assertFormat() {
4635
- this.files.assert(join7(this.directory, "format.json"), this.formatSnapshot);
4842
+ this.files.assert(join8(this.directory, "format.json"), this.formatSnapshot);
4636
4843
  }
4637
4844
  readProofMaterial(budget) {
4638
4845
  if (this.format.schema !== FORMAT)
@@ -4641,7 +4848,7 @@ class HistoryFiles {
4641
4848
  this.assertReadFormat(budget);
4642
4849
  else
4643
4850
  this.assertFormat();
4644
- const selected2 = this.readBudgeted(join7(this.directory, "read-proof-key.json"), 512, budget);
4851
+ const selected2 = this.readBudgeted(join8(this.directory, "read-proof-key.json"), 512, budget);
4645
4852
  if (!selected2 || this.proofSnapshot && (selected2.identity !== this.proofSnapshot.identity || selected2.stamp !== this.proofSnapshot.stamp))
4646
4853
  corrupt();
4647
4854
  try {
@@ -4669,17 +4876,17 @@ class HistoryFiles {
4669
4876
  return;
4670
4877
  }
4671
4878
  this.assertFormat();
4672
- assertIncarnation(this, join7(this.directory, "read-proof-key.json"), this.proofSnapshot.stamp);
4879
+ assertIncarnation(this, join8(this.directory, "read-proof-key.json"), this.proofSnapshot.stamp);
4673
4880
  }
4674
4881
  segmentPath(segment) {
4675
- return join7(this.directory, "ledger", `${String(segment).padStart(12, "0")}.jsonl`);
4882
+ return join8(this.directory, "ledger", `${String(segment).padStart(12, "0")}.jsonl`);
4676
4883
  }
4677
4884
  artifactPath(ref) {
4678
- return join7(this.directory, "runs", ref.runId, "artifacts", ref.artifactId, `${ref.part}.part`);
4885
+ return join8(this.directory, "runs", ref.runId, "artifacts", ref.artifactId, `${ref.part}.part`);
4679
4886
  }
4680
4887
  admissionPath(runId) {
4681
4888
  decodeId(runId);
4682
- return join7(this.directory, "runs", runId, "admission.json");
4889
+ return join8(this.directory, "runs", runId, "admission.json");
4683
4890
  }
4684
4891
  writeAdmission(lease, input) {
4685
4892
  lease.assert();
@@ -4688,24 +4895,24 @@ class HistoryFiles {
4688
4895
  this.files.create(this.admissionPath(value.runId), encoded(value), RECORD_BYTES);
4689
4896
  this.syncParents(this.admissionPath(value.runId));
4690
4897
  }
4691
- readBudgeted(path, maximum, budget) {
4898
+ readBudgeted(path2, maximum, budget) {
4692
4899
  if (!budget)
4693
- return this.files.read(path, maximum);
4900
+ return this.files.read(path2, maximum);
4694
4901
  let size;
4695
4902
  try {
4696
- size = lstatSync5(path).size;
4903
+ size = lstatSync5(path2).size;
4697
4904
  } catch {
4698
- return this.files.read(path, 0, budget.walks);
4905
+ return this.files.read(path2, 0, budget.walks);
4699
4906
  }
4700
4907
  if (size > maximum)
4701
4908
  corrupt();
4702
4909
  budget.reserve(size);
4703
- return this.files.read(path, size, budget.walks);
4910
+ return this.files.read(path2, size, budget.walks);
4704
4911
  }
4705
4912
  readCommand(commandId, budget) {
4706
4913
  decodeId(commandId);
4707
4914
  this.assertReadFormat(budget);
4708
- return this.readBudgeted(join7(this.directory, "commands", commandId, "intent.json"), RECORD_BYTES, budget);
4915
+ return this.readBudgeted(join8(this.directory, "commands", commandId, "intent.json"), RECORD_BYTES, budget);
4709
4916
  }
4710
4917
  readAdmission(runId, identity, budget) {
4711
4918
  return this.readAdmissionIdentity(runId, hash(canonicalJson(identity)), budget);
@@ -4726,24 +4933,24 @@ class HistoryFiles {
4726
4933
  syncParents(file) {
4727
4934
  if (process.platform === "win32")
4728
4935
  return;
4729
- for (let path = dirname6(file);; path = dirname6(path)) {
4730
- if (!this.files.directory(path))
4936
+ for (let path2 = dirname7(file);; path2 = dirname7(path2)) {
4937
+ if (!this.files.directory(path2))
4731
4938
  corrupt();
4732
- const fd = openSync4(path, constants4.O_RDONLY | constants4.O_DIRECTORY);
4939
+ const fd = openSync5(path2, constants5.O_RDONLY | constants5.O_DIRECTORY);
4733
4940
  try {
4734
4941
  fsyncSync3(fd);
4735
4942
  } finally {
4736
- closeSync4(fd);
4943
+ closeSync5(fd);
4737
4944
  }
4738
- if (path === this.coordination.directory)
4945
+ if (path2 === this.coordination.directory)
4739
4946
  break;
4740
- if (path === dirname6(path))
4947
+ if (path2 === dirname7(path2))
4741
4948
  corrupt();
4742
4949
  }
4743
4950
  }
4744
4951
  assertReadFormat(budget) {
4745
4952
  budget.reserve(this.formatSnapshot.bytes.length);
4746
- this.files.assert(join7(this.directory, "format.json"), this.formatSnapshot, this.formatSnapshot.bytes.length);
4953
+ this.files.assert(join8(this.directory, "format.json"), this.formatSnapshot, this.formatSnapshot.bytes.length);
4747
4954
  }
4748
4955
  assertPublished(segment, end, seq) {
4749
4956
  const fence = publicationFences.get(this.directory);
@@ -4774,14 +4981,14 @@ class HistoryFiles {
4774
4981
  this.syncParents(this.artifactPath(ref));
4775
4982
  }
4776
4983
  usage() {
4777
- const walk = (path, depth) => {
4778
- if (depth > 8 || !this.files.directory(path))
4984
+ const walk = (path2, depth) => {
4985
+ if (depth > 8 || !this.files.directory(path2))
4779
4986
  corrupt();
4780
4987
  let bytes = 0;
4781
- const dir = opendirSync2(path);
4988
+ const dir = opendirSync2(path2);
4782
4989
  try {
4783
4990
  for (let e = dir.readSync();e; e = dir.readSync()) {
4784
- const p = join7(path, e.name), st = lstatSync5(p);
4991
+ const p = join8(path2, e.name), st = lstatSync5(p);
4785
4992
  if (st.isDirectory())
4786
4993
  bytes += walk(p, depth + 1);
4787
4994
  else {
@@ -4816,7 +5023,7 @@ class HistoryFiles {
4816
5023
  state.bytes = 0;
4817
5024
  state.identity = null;
4818
5025
  let count = 0;
4819
- const ledger = join7(this.directory, "ledger");
5026
+ const ledger = join8(this.directory, "ledger");
4820
5027
  if (!this.files.directory(ledger))
4821
5028
  corrupt();
4822
5029
  const dir = opendirSync2(ledger);
@@ -4864,9 +5071,9 @@ class HistoryFiles {
4864
5071
  const line = file.bytes.subarray(at, end + 1);
4865
5072
  if (line.length > RECORD_BYTES)
4866
5073
  corrupt();
4867
- let decoded;
5074
+ let decoded2;
4868
5075
  try {
4869
- decoded = decodeJournalText(new TextDecoder("utf-8", { fatal: true }).decode(line));
5076
+ decoded2 = decodeJournalText(new TextDecoder("utf-8", { fatal: true }).decode(line));
4870
5077
  } catch {
4871
5078
  corrupt();
4872
5079
  }
@@ -4874,29 +5081,29 @@ class HistoryFiles {
4874
5081
  state.records++;
4875
5082
  if (state.records > LIMITS.segmentRecords)
4876
5083
  corrupt();
4877
- if (decoded.kind === "record") {
5084
+ if (decoded2.kind === "record") {
4878
5085
  let base = state.committedSeq;
4879
- if (decoded.value.kind === "recovery") {
4880
- if (pending.length || at !== line.length || n !== decoded.value.previous.segment + 1)
5086
+ if (decoded2.value.kind === "recovery") {
5087
+ if (pending.length || at !== line.length || n !== decoded2.value.previous.segment + 1)
4881
5088
  corrupt();
4882
- if (decoded.value.previous.segment !== preceding.segment || decoded.value.previous.bytes !== preceding.bytes || decoded.value.previous.sha256 !== (preceding.identity ?? hash("")) || decoded.value.previous.committedSeq !== preceding.committedSeq || decoded.value.previous.highSeq !== (preceding.highSeq ?? preceding.committedSeq) || decoded.value.previous.committedBytes !== (preceding.committedBytes ?? 0))
5089
+ if (decoded2.value.previous.segment !== preceding.segment || decoded2.value.previous.bytes !== preceding.bytes || decoded2.value.previous.sha256 !== (preceding.identity ?? hash("")) || decoded2.value.previous.committedSeq !== preceding.committedSeq || decoded2.value.previous.highSeq !== (preceding.highSeq ?? preceding.committedSeq) || decoded2.value.previous.committedBytes !== (preceding.committedBytes ?? 0))
4883
5090
  corrupt();
4884
- base = decoded.value.previous.highSeq;
5091
+ base = decoded2.value.previous.highSeq;
4885
5092
  } else if (preceding.tail && state.committedBytes === 0)
4886
5093
  corrupt();
4887
5094
  const next = BigInt(base) + BigInt(pending.length) + 1n;
4888
- if (decoded.seq !== String(next))
5095
+ if (decoded2.seq !== String(next))
4889
5096
  corrupt();
4890
- state.highSeq = decoded.seq;
5097
+ state.highSeq = decoded2.seq;
4891
5098
  pendingBytes += line.length;
4892
5099
  if (pendingBytes > 2 * RECORD_BYTES)
4893
5100
  corrupt();
4894
- pending.push(decoded);
5101
+ pending.push(decoded2);
4895
5102
  checksum.update(line);
4896
5103
  continue;
4897
5104
  }
4898
5105
  const artifacts = batchArtifacts(pending);
4899
- if (!pending.length || !validBatchSequence(pending, decoded.previous) || decoded.previous !== state.committedSeq || decoded.first !== pending[0].seq || decoded.last !== pending.at(-1)?.seq || decoded.count !== pending.length || decoded.sha256 !== checksum.digest("hex") || canonicalJson(decoded.artifacts, RECORD_BYTES) !== canonicalJson(artifacts, RECORD_BYTES))
5106
+ if (!pending.length || !validBatchSequence(pending, decoded2.previous) || decoded2.previous !== state.committedSeq || decoded2.first !== pending[0].seq || decoded2.last !== pending.at(-1)?.seq || decoded2.count !== pending.length || decoded2.sha256 !== checksum.digest("hex") || canonicalJson(decoded2.artifacts, RECORD_BYTES) !== canonicalJson(artifacts, RECORD_BYTES))
4900
5107
  corrupt();
4901
5108
  if (verifyArtifacts)
4902
5109
  for (const artifact of artifacts)
@@ -4909,12 +5116,12 @@ class HistoryFiles {
4909
5116
  if (e.kind === "owner" && e.value.kind === "fact" && e.value.fact.kind === "parent-admitted")
4910
5117
  this.readAdmission(e.value.fact.runId, e.value.fact.identity, budget);
4911
5118
  }
4912
- state.committedSeq = decoded.last;
5119
+ state.committedSeq = decoded2.last;
4913
5120
  state.committedBytes = at;
4914
- state.navigationRoot = decoded.navigationRoot;
5121
+ state.navigationRoot = decoded2.navigationRoot;
4915
5122
  yield {
4916
5123
  rows: pending,
4917
- marker: decoded,
5124
+ marker: decoded2,
4918
5125
  segment: n,
4919
5126
  identity: file.identity,
4920
5127
  segmentBytes: file.bytes.length
@@ -4974,17 +5181,17 @@ class HistoryFiles {
4974
5181
  this.assertRecoveryIdle();
4975
5182
  this.validateRecovery(row.value);
4976
5183
  const { body, trailer, marker } = this.encodeBatch(state, [row]);
4977
- const segment = state.segment + 1, path = this.segmentPath(segment);
5184
+ const segment = state.segment + 1, path2 = this.segmentPath(segment);
4978
5185
  publicationFences.set(this.directory, { ...state });
4979
5186
  this.activity().generation++;
4980
- const before = this.files.create(path, "", LIMITS.segmentBytes);
5187
+ const before = this.files.create(path2, "", LIMITS.segmentBytes);
4981
5188
  let committed;
4982
- const fd = await open(path, constants4.O_WRONLY | constants4.O_APPEND | (constants4.O_NOFOLLOW ?? 0));
5189
+ const fd = await open(path2, constants5.O_WRONLY | constants5.O_APPEND | (constants5.O_NOFOLLOW ?? 0));
4983
5190
  const assertOriginal = (bytes) => {
4984
- const original = fstatSync4(fd.fd), selected2 = lstatSync5(path);
5191
+ const original = fstatSync4(fd.fd), selected2 = lstatSync5(path2);
4985
5192
  if (original.dev !== selected2.dev || original.ino !== selected2.ino || original.nlink !== 1)
4986
5193
  corrupt();
4987
- const snapshot = this.files.read(path, LIMITS.segmentBytes);
5194
+ const snapshot = this.files.read(path2, LIMITS.segmentBytes);
4988
5195
  if (!snapshot || snapshot.identity !== hash(bytes))
4989
5196
  corrupt();
4990
5197
  return snapshot;
@@ -4992,7 +5199,7 @@ class HistoryFiles {
4992
5199
  try {
4993
5200
  await assertCurrent();
4994
5201
  this.assertWritable();
4995
- this.files.assert(path, before, LIMITS.segmentBytes);
5202
+ this.files.assert(path2, before, LIMITS.segmentBytes);
4996
5203
  assertOriginal("");
4997
5204
  await fd.writeFile(body);
4998
5205
  await observer?.("journal");
@@ -5001,7 +5208,7 @@ class HistoryFiles {
5001
5208
  assertOriginal(body);
5002
5209
  await fd.writeFile(trailer);
5003
5210
  await fd.sync();
5004
- this.syncParents(path);
5211
+ this.syncParents(path2);
5005
5212
  await observer?.("commit");
5006
5213
  await assertCurrent();
5007
5214
  this.assertWritable();
@@ -5013,7 +5220,7 @@ class HistoryFiles {
5013
5220
  this.assertWritable();
5014
5221
  if (!committed)
5015
5222
  corrupt();
5016
- this.files.assert(path, committed, LIMITS.segmentBytes);
5223
+ this.files.assert(path2, committed, LIMITS.segmentBytes);
5017
5224
  publicationFences.delete(this.directory);
5018
5225
  return {
5019
5226
  snapshot: committed,
@@ -5085,20 +5292,20 @@ class HistoryFiles {
5085
5292
  if (state.bytes + Buffer.byteLength(body + trailer) > LIMITS.segmentBytes || state.records + rows.length + 1 > LIMITS.segmentRecords)
5086
5293
  segment++;
5087
5294
  lease.assert();
5088
- const path = this.segmentPath(segment);
5089
- let before = this.files.read(path, LIMITS.segmentBytes);
5295
+ const path2 = this.segmentPath(segment);
5296
+ let before = this.files.read(path2, LIMITS.segmentBytes);
5090
5297
  if (segment !== state.segment) {
5091
5298
  if (before)
5092
5299
  corrupt();
5093
- before = this.files.create(path, "", LIMITS.segmentBytes);
5300
+ before = this.files.create(path2, "", LIMITS.segmentBytes);
5094
5301
  }
5095
5302
  if (!before || segment === state.segment && (before.bytes.length !== state.bytes || before.identity !== state.identity))
5096
5303
  corrupt();
5097
- const fd = await open(path, constants4.O_WRONLY | constants4.O_APPEND | (constants4.O_NOFOLLOW ?? 0));
5304
+ const fd = await open(path2, constants5.O_WRONLY | constants5.O_APPEND | (constants5.O_NOFOLLOW ?? 0));
5098
5305
  try {
5099
5306
  lease.assert();
5100
- this.files.assert(path, before, LIMITS.segmentBytes);
5101
- const opened = await fd.stat(), selected2 = lstatSync5(path);
5307
+ this.files.assert(path2, before, LIMITS.segmentBytes);
5308
+ const opened = await fd.stat(), selected2 = lstatSync5(path2);
5102
5309
  if (opened.ino !== selected2.ino || opened.dev !== selected2.dev || opened.nlink !== 1)
5103
5310
  corrupt();
5104
5311
  beforeEffect?.();
@@ -5112,7 +5319,7 @@ class HistoryFiles {
5112
5319
  await observer?.("commit");
5113
5320
  lease.assert();
5114
5321
  this.assertWritable();
5115
- const after = this.files.read(path, LIMITS.segmentBytes);
5322
+ const after = this.files.read(path2, LIMITS.segmentBytes);
5116
5323
  if (!after || after.identity !== createHash4("sha256").update(before.bytes).update(body).update(trailer).digest("hex"))
5117
5324
  corrupt();
5118
5325
  return {
@@ -5133,7 +5340,7 @@ class HistoryFiles {
5133
5340
  checkpoint(lease, state) {
5134
5341
  lease.assert();
5135
5342
  this.assertWritable();
5136
- const path = join7(this.directory, "checkpoint.json"), old = this.files.read(path);
5343
+ const path2 = join8(this.directory, "checkpoint.json"), old = this.files.read(path2);
5137
5344
  const body = encoded({
5138
5345
  schema: "cadence-history-checkpoint/2",
5139
5346
  storeId: this.format.storeId,
@@ -5141,9 +5348,9 @@ class HistoryFiles {
5141
5348
  segment: state.segment
5142
5349
  });
5143
5350
  if (old)
5144
- this.files.replace(path, old, body);
5351
+ this.files.replace(path2, old, body);
5145
5352
  else
5146
- this.files.create(path, body);
5353
+ this.files.create(path2, body);
5147
5354
  }
5148
5355
  }
5149
5356
  function emptyScan() {
@@ -5543,13 +5750,13 @@ function digestHistory(files, exclude) {
5543
5750
  files.assertFormat();
5544
5751
  const digest = createHash5("sha256");
5545
5752
  let count = 0;
5546
- const walk = (path, depth) => {
5547
- if (depth > 8 || !files.files.directory(path))
5753
+ const walk = (path2, depth) => {
5754
+ if (depth > 8 || !files.files.directory(path2))
5548
5755
  throw new ContractError("corrupt-selected");
5549
- const directory2 = lstatSync6(path);
5756
+ const directory2 = lstatSync6(path2);
5550
5757
  digest.update(`incarnation:${directory2.dev}:${directory2.ino}
5551
5758
  `);
5552
- const dir = opendirSync3(path), names2 = [];
5759
+ const dir = opendirSync3(path2), names2 = [];
5553
5760
  try {
5554
5761
  for (let e = dir.readSync();e; e = dir.readSync()) {
5555
5762
  if (++count > LIMITS.dataFrames)
@@ -5560,7 +5767,7 @@ function digestHistory(files, exclude) {
5560
5767
  dir.closeSync();
5561
5768
  }
5562
5769
  for (const name of names2.sort()) {
5563
- const selected2 = join8(path, name);
5770
+ const selected2 = join9(path2, name);
5564
5771
  if (selected2 === exclude)
5565
5772
  continue;
5566
5773
  const rel = relative4(files.directory, selected2);
@@ -5700,13 +5907,13 @@ class HistoryRecovery {
5700
5907
  },
5701
5908
  acknowledgments: { processes: true, credentials: true, storage: true }
5702
5909
  };
5703
- const path = files.segmentPath(state.segment + 1);
5910
+ const path2 = files.segmentPath(state.segment + 1);
5704
5911
  const assertCurrent = async () => {
5705
5912
  await confirmation.assertLocalAuthority(structuredClone(expected));
5706
5913
  const selected2 = await inspectRecovery(this.coordination);
5707
5914
  if (selected2.owner !== "absent")
5708
5915
  throw new ContractError("quarantine");
5709
- if (selected2.epoch !== expected.epoch || selected2.coordinationDigest !== expected.coordinationDigest || digestHistory(files, path) !== expected.history?.digest || this.coordination.recoverySnapshot().digest !== expected.coordinationDigest)
5916
+ if (selected2.epoch !== expected.epoch || selected2.coordinationDigest !== expected.coordinationDigest || digestHistory(files, path2) !== expected.history?.digest || this.coordination.recoverySnapshot().digest !== expected.coordinationDigest)
5710
5917
  throw new ContractError("conflict");
5711
5918
  };
5712
5919
  await confirmation.assertLocalAuthority(structuredClone(expected));
@@ -5730,8 +5937,8 @@ class HistoryRecovery {
5730
5937
  const assert = () => {
5731
5938
  files.assertWritable();
5732
5939
  files.assertRecoveryIdle();
5733
- files.files.assert(path, after.snapshot, LIMITS.segmentBytes);
5734
- if (digestHistory(files, path) !== expected.history?.digest)
5940
+ files.files.assert(path2, after.snapshot, LIMITS.segmentBytes);
5941
+ if (digestHistory(files, path2) !== expected.history?.digest)
5735
5942
  throw new ContractError("conflict");
5736
5943
  };
5737
5944
  assert();
@@ -5870,8 +6077,8 @@ async function localRecovery(c, operation) {
5870
6077
  }
5871
6078
 
5872
6079
  // src/service/runtime.ts
5873
- import { homedir as homedir3 } from "node:os";
5874
- import { dirname as dirname7, isAbsolute as isAbsolute6, join as join9, resolve as resolve4 } from "node:path";
6080
+ import { homedir as homedir4 } from "node:os";
6081
+ import { dirname as dirname8, isAbsolute as isAbsolute6, join as join10, resolve as resolve4 } from "node:path";
5875
6082
 
5876
6083
  // src/service/config.ts
5877
6084
  import { isIP } from "node:net";
@@ -5941,10 +6148,11 @@ function decodeHostConfig(value) {
5941
6148
  const tokenSource = oneOf(o.tokenSource, [
5942
6149
  "generated",
5943
6150
  "environment",
5944
- "file"
6151
+ "file",
6152
+ "config"
5945
6153
  ]);
5946
6154
  const tokenFile = o.tokenFile === null ? null : absolute(o.tokenFile);
5947
- if (o.tokenSource === "file" !== (tokenFile !== null))
6155
+ if ((tokenSource === "file" || tokenSource === "config") !== !!tokenFile)
5948
6156
  throw new ContractError("invalid");
5949
6157
  return {
5950
6158
  schema: literal(o.schema, "cadence-service-host/1"),
@@ -6005,13 +6213,13 @@ function selectStart(options, ambient) {
6005
6213
  throw new ContractError("invalid");
6006
6214
  let fromFile;
6007
6215
  if (options.config) {
6008
- const path = resolve4(options.config), f = new PrivateFiles(dirname7(path));
6009
- const bytes = f.read(path);
6216
+ const path2 = resolve4(options.config), f = new PrivateFiles(dirname8(path2));
6217
+ const bytes = f.read(path2);
6010
6218
  if (!bytes)
6011
6219
  throw new ContractError("corrupt-selected");
6012
6220
  fromFile = decodeHostConfig(json(bytes));
6013
6221
  }
6014
- const selected2 = old ?? fromFile ?? decodeHostConfig({
6222
+ const base = old ?? fromFile ?? decodeHostConfig({
6015
6223
  schema: "cadence-service-host/1",
6016
6224
  cwd: workspace.cwd,
6017
6225
  http: {
@@ -6020,10 +6228,23 @@ function selectStart(options, ambient) {
6020
6228
  port: options.port ?? 17423,
6021
6229
  proxy: null
6022
6230
  },
6023
- agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join9(ambient.HOME ?? ambient.USERPROFILE ?? homedir3(), ".pi", "agent")),
6231
+ agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join10(ambient.HOME ?? ambient.USERPROFILE ?? homedir4(), ".pi", "agent")),
6024
6232
  tokenSource: options.tokenFile ? "file" : env ? "environment" : "generated",
6025
6233
  tokenFile: options.tokenFile ? resolve4(options.tokenFile) : null
6026
6234
  });
6235
+ const server = options.serverConfig !== undefined ? resolve4(options.serverConfig) : old?.tokenSource === "config" ? old.tokenFile : null;
6236
+ let selected2 = base;
6237
+ if (server) {
6238
+ if (options.tokenFile !== undefined)
6239
+ throw new ContractError("conflict");
6240
+ const { port } = readServerConfig(server).config;
6241
+ selected2 = decodeHostConfig({
6242
+ ...base,
6243
+ http: { ...base.http, port },
6244
+ tokenSource: "config",
6245
+ tokenFile: server
6246
+ });
6247
+ }
6027
6248
  if (fromFile && encoded(fromFile) !== encoded(selected2) || options.port !== undefined && options.port !== selected2.http.port || options.host !== undefined && options.host !== selected2.http.host || options.agentDir !== undefined && resolve4(options.agentDir) !== selected2.agentDir || options.tokenFile !== undefined && resolve4(options.tokenFile) !== selected2.tokenFile || env && selected2.tokenSource !== "environment" || selected2.tokenSource === "environment" && !env || resolveWorkspace(selected2.cwd).root !== workspace.root)
6028
6249
  throw new ContractError("conflict");
6029
6250
  return {
@@ -6174,6 +6395,10 @@ async function stopService(c) {
6174
6395
  function failedStatus(result) {
6175
6396
  return !!(result && typeof result === "object" && ("status" in result) && ["unconfirmed", "unavailable", "quarantined", "refused"].includes(String(result.status)));
6176
6397
  }
6398
+ function serverConfigHint(path2) {
6399
+ return `Cadence changed nothing: ${path2} must be your own private regular file (chmod 600, in a chmod 700 directory on Linux and macOS) in KEY=value lines with exactly one ${PORT_KEY}=<1-65535> and one ${TOKEN_KEY}=<at least 32 characters>.
6400
+ `;
6401
+ }
6177
6402
  function startupGuidance(result, input) {
6178
6403
  let report;
6179
6404
  try {
@@ -6192,10 +6417,10 @@ function startupGuidance(result, input) {
6192
6417
  return `Pi's agent directory ${agent} must be an existing real directory (not a symlink) owned by you.
6193
6418
  `;
6194
6419
  case "pi-auth":
6195
- return `${join10(agent, "auth.json")} must be your own single-link regular file holding valid Pi credentials; log in to Pi first.
6420
+ return `${join11(agent, "auth.json")} must be your own single-link regular file holding valid Pi credentials; log in to Pi first.
6196
6421
  `;
6197
6422
  case "pi-models":
6198
- return `${join10(agent, "models.json")} must be your own single-link regular file using only fields Cadence's pinned Pi SDK supports; a field only newer Pi understands is refused.
6423
+ return `${join11(agent, "models.json")} must be your own single-link regular file using only fields Cadence's pinned Pi SDK supports; a field only newer Pi understands is refused.
6199
6424
  `;
6200
6425
  case "pi-runtime":
6201
6426
  return `Loading Pi's SDK model runtime failed after the owner registered (optional SDK packages missing, or sources the SDK refuses); it stays quarantined.
@@ -6208,15 +6433,26 @@ ${inspect}` : "";
6208
6433
  async function startService(options, foreground, hostUrl) {
6209
6434
  const network = networkSnapshot(process.env);
6210
6435
  const env = hostEnvironment(process.env);
6211
- const input = decodeHostInput(selectStart(options, process.env));
6436
+ let input;
6437
+ try {
6438
+ input = decodeHostInput(selectStart(options, process.env));
6439
+ } catch (error) {
6440
+ if (error instanceof ServerConfigRefusal)
6441
+ process.stderr.write(serverConfigHint(error.path));
6442
+ throw error;
6443
+ }
6212
6444
  const serialized = `${JSON.stringify(input)}
6213
6445
  `;
6214
6446
  if (Buffer.byteLength(serialized) > STARTUP_INPUT_BYTES)
6215
6447
  throw new ContractError("oversize");
6216
6448
  const c = new ServiceCoordination(input.workspace, input.stateHome);
6217
6449
  const existing = await serviceStatus(c);
6218
- if (existing && typeof existing === "object" && "status" in existing && existing.status === "alive")
6450
+ if (existing && typeof existing === "object" && "status" in existing && existing.status === "alive") {
6451
+ const running = readHostConfig(c);
6452
+ if (running && encoded(running) !== encoded(input.config))
6453
+ throw new ContractError("conflict");
6219
6454
  return existing;
6455
+ }
6220
6456
  if (!sameNetwork(network, networkSnapshot(process.env)))
6221
6457
  throw new ContractError("stale");
6222
6458
  const child = spawn(process.execPath, [
@@ -6321,6 +6557,14 @@ async function observeStarted(workspace) {
6321
6557
  }
6322
6558
  return { service };
6323
6559
  }
6560
+ async function privateLink(workspace) {
6561
+ try {
6562
+ const value = closed(await nativeCall(new ServiceCoordination(workspace), "token-link", true), ["link"]);
6563
+ return text(value.link, 4096, true);
6564
+ } catch {
6565
+ return;
6566
+ }
6567
+ }
6324
6568
  var ownerControl = {
6325
6569
  async ownerStatus(workspace) {
6326
6570
  try {
@@ -6369,11 +6613,11 @@ async function serverAction(s, action, requested, linger) {
6369
6613
  return { status: "unsupported" };
6370
6614
  }
6371
6615
  }
6372
- function serverGuidance(s, action, result) {
6373
- const cli = piUserInstall(s) ? `node ${JSON.stringify(join10(s.root, "src", "service-cli.mjs"))}` : "cadence";
6616
+ function serverGuidance(s, action, result, link) {
6617
+ const cli = piUserInstall(s) ? `node ${JSON.stringify(join11(s.root, "src", "service-cli.mjs"))}` : "cadence";
6374
6618
  const note = result.redirectedFrom ? `Cadence workspace: ${result.workspace} (${result.redirectedFrom} contains Cadence's private state)
6375
6619
  ` : "";
6376
- return note + serverStatusGuidance(s, action, result, cli);
6620
+ return note + serverStatusGuidance(s, action, result, cli, link);
6377
6621
  }
6378
6622
  function lingerGuidance(linger) {
6379
6623
  switch (linger) {
@@ -6400,10 +6644,11 @@ function managerDetail(s) {
6400
6644
  return `run its launcher to see why: wscript ${JSON.stringify(launcherPath(s))}`;
6401
6645
  }
6402
6646
  }
6403
- function serverStatusGuidance(s, action, result, cli) {
6647
+ function serverStatusGuidance(s, action, result, cli, link) {
6404
6648
  const workspace = JSON.stringify(result.workspace ?? "");
6405
6649
  const manage = `Manage: ${cli} server status|restart|stop|uninstall`;
6406
6650
  const when = s.platform === "linux" ? "at boot or login" : "at login";
6651
+ const config = `Port and token: ${PORT_KEY} and ${TOKEN_KEY} in ${serverConfigPath(s.home)} (edit them, then: ${cli} server restart)`;
6407
6652
  switch (result.status) {
6408
6653
  case "started": {
6409
6654
  const linger = lingerGuidance(result.linger);
@@ -6412,14 +6657,22 @@ function serverStatusGuidance(s, action, result, cli) {
6412
6657
  Inspect: ${cli} diagnose --workspace ${workspace}
6413
6658
  ${manage}
6414
6659
  ${linger}`;
6415
- return `Cadence WebUI: ${result.endpoint} (workspace ${result.workspace}; starts ${when})
6660
+ if (!link)
6661
+ return `Cadence WebUI: ${result.endpoint} (workspace ${result.workspace}; starts ${when})
6416
6662
  Enter with your private link: ${cli} token-link --workspace ${workspace} --reveal
6663
+ ${config}
6664
+ ${manage}
6665
+ ${linger}`;
6666
+ return `Cadence WebUI: ${link}
6667
+ Open this private link (workspace ${result.workspace}; starts ${when}); its token admits anyone who has it.
6668
+ ${config}
6417
6669
  ${manage}
6418
6670
  ${linger}`;
6419
6671
  }
6420
6672
  case "waiting-for-pi-login":
6421
- return `Cadence is registered for ${result.workspace}; it starts once Pi has ${join10(agentDirectory(s), "auth.json")}.
6673
+ return `Cadence is registered for ${result.workspace}; it starts once Pi has ${join11(agentDirectory(s), "auth.json")}.
6422
6674
  Log in to Pi, then: ${cli} server restart
6675
+ ${config}
6423
6676
  ${lingerGuidance(result.linger)}`;
6424
6677
  case "stopped":
6425
6678
  return action === "stop" ? `Cadence stopped; it starts again ${when}, or now with: ${cli} server restart
@@ -6457,6 +6710,16 @@ ${lingerGuidance(result.linger)}`;
6457
6710
  return "";
6458
6711
  }
6459
6712
  }
6713
+ function researchTemplate(home) {
6714
+ try {
6715
+ const result = ensureUserConfig(home);
6716
+ return result.created ? `Cadence created the research configuration template ${result.path}
6717
+ ` : "";
6718
+ } catch (error) {
6719
+ return `Cadence could not create the research configuration template: ${error instanceof Error ? error.message : String(error)}
6720
+ `;
6721
+ }
6722
+ }
6460
6723
  var SERVER_ACTIONS = [
6461
6724
  "install",
6462
6725
  "status",
@@ -6484,7 +6747,21 @@ async function serverMain(argv, hostUrl) {
6484
6747
  }
6485
6748
  if (argv.length)
6486
6749
  throw new ContractError("invalid");
6487
- const s = hostSystem(dirname8(dirname8(fileURLToPath(hostUrl))), ownerControl);
6750
+ const s = hostSystem(dirname9(dirname9(fileURLToPath(hostUrl))), ownerControl);
6751
+ if (action === "install")
6752
+ process.stderr.write(researchTemplate(s.home));
6753
+ if (action === "install" || action === "restart") {
6754
+ try {
6755
+ const { added } = ensureServerConfig(s.home);
6756
+ if (added.length)
6757
+ process.stderr.write(`Cadence added ${added.map((key) => key === PORT_KEY ? `${key}=${DEFAULT_PORT}` : `a random private ${key}`).join(" and ")} to ${serverConfigPath(s.home)}
6758
+ `);
6759
+ } catch (error) {
6760
+ if (error instanceof ServerConfigRefusal)
6761
+ process.stderr.write(serverConfigHint(error.path));
6762
+ throw error;
6763
+ }
6764
+ }
6488
6765
  let result;
6489
6766
  try {
6490
6767
  result = await serverAction(s, action, requested, linger);
@@ -6494,9 +6771,13 @@ async function serverMain(argv, hostUrl) {
6494
6771
  `);
6495
6772
  throw error;
6496
6773
  }
6497
- if (result.status === "started" && result.workspace)
6774
+ let link;
6775
+ if (result.status === "started" && result.workspace) {
6498
6776
  Object.assign(result, await observeStarted(result.workspace));
6499
- process.stderr.write(serverGuidance(s, action, result));
6777
+ if (result.service === "alive")
6778
+ link = await privateLink(result.workspace);
6779
+ }
6780
+ process.stderr.write(serverGuidance(s, action, result, link));
6500
6781
  process.stdout.write(`${JSON.stringify(result)}
6501
6782
  `);
6502
6783
  if ([
@@ -6518,7 +6799,7 @@ async function managementMain(argv, hostUrl) {
6518
6799
  if (command === "--help" && !argv.length) {
6519
6800
  process.stdout.write(`cadence start|status|diagnose|stop|token-link --reveal|token-rotate|recover inspect|recover resolve [--workspace PATH] [--state-home PATH]
6520
6801
  cadence server install [--workspace PATH] [--no-linger]|status|restart|stop|uninstall
6521
- Start is explicit, or the background service that server install registers: a systemd user unit on Linux, started at boot through linger (--no-linger or CADENCE_LINGER=0 keeps it to login), a LaunchAgent on macOS and a Run entry on Windows, both started at login. Server restart, stop and uninstall use the orderly Stop. cadence-service is the same command. Default detached or --foreground. Start accepts --config, --agent-dir, --host, --port, --token-file.
6802
+ Start is explicit, or the background service that server install registers: a systemd user unit on Linux, started at boot through linger (--no-linger or CADENCE_LINGER=0 keeps it to login), a LaunchAgent on macOS and a Run entry on Windows, both started at login. Server restart, stop and uninstall use the orderly Stop. cadence-service is the same command. Default detached or --foreground. Start accepts --config, --agent-dir, --host, --port, --token-file, --server-config (the .env whose CADENCE_PORT and CADENCE_TOKEN the background service uses).
6522
6803
  Return/quit detaches clients; only explicit Cancel/Stop requests stop. Unconfirmed/quarantined is not stopped.
6523
6804
  Recovery resolve requires this local native terminal, exact unchanged dead-owner resources and separate process/credential/storage acknowledgments. No --yes, network recovery, killing, repair or cleanup.
6524
6805
  `);
@@ -6578,6 +6859,8 @@ Recovery resolve requires this local native terminal, exact unchanged dead-owner
6578
6859
  options.agentDir = value;
6579
6860
  else if (flag === "--token-file")
6580
6861
  options.tokenFile = value;
6862
+ else if (flag === "--server-config")
6863
+ options.serverConfig = value;
6581
6864
  else if (flag === "--port")
6582
6865
  options.port = portValue(value);
6583
6866
  else if (flag === "--host" && (value === "127.0.0.1" || value === "::1"))
@@ -6592,7 +6875,7 @@ Recovery resolve requires this local native terminal, exact unchanged dead-owner
6592
6875
  `);
6593
6876
  options.workspace = landing.workspace;
6594
6877
  if (command === "start")
6595
- mkdirSync4(landing.workspace, { recursive: true });
6878
+ mkdirSync5(landing.workspace, { recursive: true });
6596
6879
  }
6597
6880
  let result;
6598
6881
  if (command === "start")