@abelxiaoxing/cadence 2.0.5 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/docs/service-management.md +2 -1
- package/package.json +1 -3
- package/src/index.ts +6 -2
- package/src/service-cli.mjs +273 -224
- package/src/startup-card.ts +4 -1
- package/web/dist/assets/cadence.js +1 -1
- package/web/dist/manifest.json +3 -3
- package/src/install-config.mjs +0 -59
package/src/service-cli.mjs
CHANGED
|
@@ -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
|
|
6
|
+
import { existsSync, mkdirSync as mkdirSync5 } from "node:fs";
|
|
7
7
|
import { request } from "node:http";
|
|
8
8
|
import { dirname as dirname8, join as join10, 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(
|
|
2104
|
-
const full = resolve(
|
|
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
|
-
|
|
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(
|
|
2158
|
-
if (!safeDirectory(dirname(
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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(
|
|
2232
|
-
const full = resolve(
|
|
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,
|
|
2255
|
-
const within = relative(root, resolve(
|
|
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 =
|
|
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
|
|
2286
|
-
if (!safeDirectory(
|
|
2322
|
+
for (const path2 of [namespace, this.privateRoot]) {
|
|
2323
|
+
if (!safeDirectory(path2))
|
|
2287
2324
|
continue;
|
|
2288
|
-
const st = lstatSync(
|
|
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(
|
|
2330
|
+
inspectPrivateFile(path2, max) {
|
|
2294
2331
|
this.inspectPrivate();
|
|
2295
2332
|
if (!safeDirectory(this.privateRoot))
|
|
2296
2333
|
return;
|
|
2297
2334
|
try {
|
|
2298
|
-
const st = lstatSync(
|
|
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(() =>
|
|
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
|
-
|
|
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 =
|
|
2455
|
+
const fd = openSync2(temporary, "wx", 384);
|
|
2419
2456
|
try {
|
|
2420
2457
|
writeFileSync(fd, text2);
|
|
2421
2458
|
fsyncSync(fd);
|
|
2422
2459
|
} finally {
|
|
2423
|
-
|
|
2460
|
+
closeSync2(fd);
|
|
2424
2461
|
}
|
|
2425
2462
|
this.assertOwner(lock);
|
|
2426
2463
|
const latest = readBounded(join(dir, "task.json"), MAX_RECORD_BYTES);
|
|
@@ -2475,7 +2512,7 @@ var TASK_CONFIG_SCHEMA = "cadence-assistant-checks/1";
|
|
|
2475
2512
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
2476
2513
|
import {
|
|
2477
2514
|
lstatSync as lstatSync2,
|
|
2478
|
-
mkdirSync as
|
|
2515
|
+
mkdirSync as mkdirSync3,
|
|
2479
2516
|
readFileSync,
|
|
2480
2517
|
realpathSync as realpathSync2,
|
|
2481
2518
|
renameSync as renameSync2,
|
|
@@ -2483,7 +2520,7 @@ import {
|
|
|
2483
2520
|
statSync,
|
|
2484
2521
|
writeFileSync as writeFileSync2
|
|
2485
2522
|
} from "node:fs";
|
|
2486
|
-
import { homedir as
|
|
2523
|
+
import { homedir as homedir3 } from "node:os";
|
|
2487
2524
|
import {
|
|
2488
2525
|
delimiter,
|
|
2489
2526
|
dirname as dirname2,
|
|
@@ -2511,7 +2548,7 @@ function hostSystem(root, owner) {
|
|
|
2511
2548
|
return {
|
|
2512
2549
|
platform: process.platform,
|
|
2513
2550
|
env: process.env,
|
|
2514
|
-
home:
|
|
2551
|
+
home: homedir3(),
|
|
2515
2552
|
node: process.execPath,
|
|
2516
2553
|
root,
|
|
2517
2554
|
unit: UNIT,
|
|
@@ -2545,8 +2582,8 @@ function hostSystem(root, owner) {
|
|
|
2545
2582
|
},
|
|
2546
2583
|
launchctl: (args) => run("launchctl", args),
|
|
2547
2584
|
reg: (args) => run("reg", args),
|
|
2548
|
-
launch: (
|
|
2549
|
-
const r = spawnSync2(wscript(process.env), [
|
|
2585
|
+
launch: (path2) => {
|
|
2586
|
+
const r = spawnSync2(wscript(process.env), [path2], {
|
|
2550
2587
|
stdio: "ignore",
|
|
2551
2588
|
windowsHide: true,
|
|
2552
2589
|
timeout: 20000
|
|
@@ -2634,7 +2671,7 @@ function buildUnit(u) {
|
|
|
2634
2671
|
"Documentation=https://github.com/abelxiaoxing/cadence/blob/main/docs/service-management.md",
|
|
2635
2672
|
`ConditionPathExists=${specifiers(u.cli)}`,
|
|
2636
2673
|
`ConditionPathExists=${specifiers(join2(u.agentDir, "auth.json"))}`,
|
|
2637
|
-
...u.sdk.map((
|
|
2674
|
+
...u.sdk.map((path2) => `ConditionPathExists=${specifiers(path2)}`),
|
|
2638
2675
|
"StartLimitIntervalSec=300",
|
|
2639
2676
|
"StartLimitBurst=3",
|
|
2640
2677
|
"",
|
|
@@ -2655,11 +2692,11 @@ function buildUnit(u) {
|
|
|
2655
2692
|
].join(`
|
|
2656
2693
|
`);
|
|
2657
2694
|
}
|
|
2658
|
-
function readGenerated(
|
|
2695
|
+
function readGenerated(path2, marker) {
|
|
2659
2696
|
try {
|
|
2660
|
-
if (!lstatSync2(
|
|
2697
|
+
if (!lstatSync2(path2).isFile())
|
|
2661
2698
|
return null;
|
|
2662
|
-
const bytes = readFileSync(
|
|
2699
|
+
const bytes = readFileSync(path2);
|
|
2663
2700
|
const text2 = bytes[0] === 255 && bytes[1] === 254 ? bytes.subarray(2).toString("utf16le") : bytes.toString("utf8");
|
|
2664
2701
|
return text2.startsWith(marker) ? text2 : null;
|
|
2665
2702
|
} catch (error) {
|
|
@@ -2671,9 +2708,9 @@ function readGenerated(path, marker) {
|
|
|
2671
2708
|
function unitWorkspace(text2) {
|
|
2672
2709
|
return text2?.match(/^WorkingDirectory=(.+)$/m)?.[1].replaceAll("%%", "%");
|
|
2673
2710
|
}
|
|
2674
|
-
function directory(
|
|
2711
|
+
function directory(path2) {
|
|
2675
2712
|
try {
|
|
2676
|
-
return isAbsolute2(
|
|
2713
|
+
return isAbsolute2(path2) && statSync(path2).isDirectory();
|
|
2677
2714
|
} catch {
|
|
2678
2715
|
return false;
|
|
2679
2716
|
}
|
|
@@ -2693,22 +2730,22 @@ function selectWorkspace(s, requested, kept) {
|
|
|
2693
2730
|
chosen = kept;
|
|
2694
2731
|
const landing = chosen ? effectiveWorkspace(chosen, serviceStateHome(s), s.home) : { workspace: join2(s.home, LANDING_WORKSPACE), redirected: false };
|
|
2695
2732
|
if (landing.workspace !== chosen)
|
|
2696
|
-
|
|
2733
|
+
mkdirSync3(landing.workspace, { recursive: true });
|
|
2697
2734
|
return landing.redirected ? { workspace: landing.workspace, redirectedFrom: chosen } : { workspace: landing.workspace };
|
|
2698
2735
|
}
|
|
2699
|
-
function write(
|
|
2700
|
-
|
|
2701
|
-
const temporary = `${
|
|
2736
|
+
function write(path2, content) {
|
|
2737
|
+
mkdirSync3(dirname2(path2), { recursive: true });
|
|
2738
|
+
const temporary = `${path2}.${process.pid}.tmp`;
|
|
2702
2739
|
try {
|
|
2703
2740
|
writeFileSync2(temporary, content, { mode: 420, flag: "wx" });
|
|
2704
|
-
renameSync2(temporary,
|
|
2741
|
+
renameSync2(temporary, path2);
|
|
2705
2742
|
} finally {
|
|
2706
2743
|
rmSync(temporary, { force: true });
|
|
2707
2744
|
}
|
|
2708
2745
|
}
|
|
2709
|
-
function regularFile(
|
|
2746
|
+
function regularFile(path2) {
|
|
2710
2747
|
try {
|
|
2711
|
-
return statSync(
|
|
2748
|
+
return statSync(path2).isFile();
|
|
2712
2749
|
} catch {
|
|
2713
2750
|
return false;
|
|
2714
2751
|
}
|
|
@@ -2741,10 +2778,10 @@ function configureAutostart(s, mode, requested, linger = false) {
|
|
|
2741
2778
|
return { status: "unsupported" };
|
|
2742
2779
|
if (s.systemctl(["show-environment"]).status !== 0)
|
|
2743
2780
|
return { status: "unavailable" };
|
|
2744
|
-
const
|
|
2745
|
-
const previous = readGenerated(
|
|
2781
|
+
const path2 = unitPath(s);
|
|
2782
|
+
const previous = readGenerated(path2, MARKER);
|
|
2746
2783
|
if (previous === null)
|
|
2747
|
-
return { status: "foreign", unit:
|
|
2784
|
+
return { status: "foreign", unit: path2 };
|
|
2748
2785
|
if (mode === "restart" && previous === undefined)
|
|
2749
2786
|
return { status: "absent" };
|
|
2750
2787
|
const sdk = sdkPackages(s.root);
|
|
@@ -2764,10 +2801,10 @@ function configureAutostart(s, mode, requested, linger = false) {
|
|
|
2764
2801
|
const active = s.systemctl(["is-active", "--quiet", s.unit]).status === 0;
|
|
2765
2802
|
if (active)
|
|
2766
2803
|
s.systemctl(["stop", s.unit]);
|
|
2767
|
-
write(
|
|
2804
|
+
write(path2, content);
|
|
2768
2805
|
s.systemctl(["daemon-reload"]);
|
|
2769
2806
|
const result = {
|
|
2770
|
-
unit:
|
|
2807
|
+
unit: path2,
|
|
2771
2808
|
workspace,
|
|
2772
2809
|
...redirectedFrom ? { redirectedFrom } : {}
|
|
2773
2810
|
};
|
|
@@ -2785,51 +2822,51 @@ function configureAutostart(s, mode, requested, linger = false) {
|
|
|
2785
2822
|
function stopAutostart(s) {
|
|
2786
2823
|
if (s.platform !== "linux")
|
|
2787
2824
|
return { status: "unsupported" };
|
|
2788
|
-
const
|
|
2789
|
-
const previous = readGenerated(
|
|
2825
|
+
const path2 = unitPath(s);
|
|
2826
|
+
const previous = readGenerated(path2, MARKER);
|
|
2790
2827
|
if (previous === null)
|
|
2791
|
-
return { status: "foreign", unit:
|
|
2828
|
+
return { status: "foreign", unit: path2 };
|
|
2792
2829
|
if (previous === undefined)
|
|
2793
2830
|
return { status: "absent" };
|
|
2794
2831
|
if (s.systemctl(["show-environment"]).status !== 0)
|
|
2795
|
-
return { status: "unavailable", unit:
|
|
2832
|
+
return { status: "unavailable", unit: path2 };
|
|
2796
2833
|
return {
|
|
2797
2834
|
status: s.systemctl(["stop", s.unit]).status === 0 ? "stopped" : "failed",
|
|
2798
|
-
unit:
|
|
2835
|
+
unit: path2,
|
|
2799
2836
|
workspace: unitWorkspace(previous)
|
|
2800
2837
|
};
|
|
2801
2838
|
}
|
|
2802
2839
|
function removeAutostart(s) {
|
|
2803
2840
|
if (s.platform !== "linux")
|
|
2804
2841
|
return { status: "unsupported" };
|
|
2805
|
-
const
|
|
2806
|
-
const previous = readGenerated(
|
|
2842
|
+
const path2 = unitPath(s);
|
|
2843
|
+
const previous = readGenerated(path2, MARKER);
|
|
2807
2844
|
if (previous === null)
|
|
2808
|
-
return { status: "foreign", unit:
|
|
2845
|
+
return { status: "foreign", unit: path2 };
|
|
2809
2846
|
if (previous === undefined)
|
|
2810
2847
|
return { status: "absent" };
|
|
2811
2848
|
if (s.systemctl(["show-environment"]).status !== 0)
|
|
2812
|
-
return { status: "unavailable", unit:
|
|
2849
|
+
return { status: "unavailable", unit: path2 };
|
|
2813
2850
|
if (s.systemctl(["disable", "--now", s.unit]).status !== 0)
|
|
2814
|
-
return { status: "failed", unit:
|
|
2815
|
-
rmSync(
|
|
2851
|
+
return { status: "failed", unit: path2 };
|
|
2852
|
+
rmSync(path2);
|
|
2816
2853
|
s.systemctl(["daemon-reload"]);
|
|
2817
2854
|
s.systemctl(["reset-failed", s.unit]);
|
|
2818
|
-
return { status: "removed", unit:
|
|
2855
|
+
return { status: "removed", unit: path2 };
|
|
2819
2856
|
}
|
|
2820
2857
|
function autostartStatus(s) {
|
|
2821
2858
|
if (s.platform !== "linux")
|
|
2822
2859
|
return { status: "unsupported" };
|
|
2823
|
-
const
|
|
2824
|
-
const previous = readGenerated(
|
|
2860
|
+
const path2 = unitPath(s);
|
|
2861
|
+
const previous = readGenerated(path2, MARKER);
|
|
2825
2862
|
if (previous === null)
|
|
2826
|
-
return { status: "foreign", unit:
|
|
2863
|
+
return { status: "foreign", unit: path2 };
|
|
2827
2864
|
if (previous === undefined)
|
|
2828
2865
|
return { status: "absent" };
|
|
2829
2866
|
return {
|
|
2830
2867
|
status: s.systemctl(["is-active", s.unit]).stdout.trim() || "unknown",
|
|
2831
2868
|
enabled: s.systemctl(["is-enabled", s.unit]).stdout.trim() || "unknown",
|
|
2832
|
-
unit:
|
|
2869
|
+
unit: path2,
|
|
2833
2870
|
workspace: unitWorkspace(previous),
|
|
2834
2871
|
...bootStart(s, false)
|
|
2835
2872
|
};
|
|
@@ -2844,13 +2881,13 @@ import { dirname as dirname4, isAbsolute as isAbsolute4, join as join4, relative
|
|
|
2844
2881
|
// src/service/private-files.ts
|
|
2845
2882
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
2846
2883
|
import {
|
|
2847
|
-
closeSync as
|
|
2848
|
-
constants as
|
|
2884
|
+
closeSync as closeSync3,
|
|
2885
|
+
constants as constants3,
|
|
2849
2886
|
fstatSync as fstatSync2,
|
|
2850
2887
|
fsyncSync as fsyncSync2,
|
|
2851
2888
|
lstatSync as lstatSync3,
|
|
2852
|
-
mkdirSync as
|
|
2853
|
-
openSync as
|
|
2889
|
+
mkdirSync as mkdirSync4,
|
|
2890
|
+
openSync as openSync3,
|
|
2854
2891
|
readSync as readSync2,
|
|
2855
2892
|
renameSync as renameSync3,
|
|
2856
2893
|
unlinkSync as unlinkSync2,
|
|
@@ -2898,25 +2935,25 @@ class DirectoryWalks {
|
|
|
2898
2935
|
this.walked.clear();
|
|
2899
2936
|
return false;
|
|
2900
2937
|
}
|
|
2901
|
-
remember(
|
|
2938
|
+
remember(path2, st) {
|
|
2902
2939
|
if (this.open())
|
|
2903
|
-
this.walked.set(
|
|
2940
|
+
this.walked.set(path2, { dev: st.dev, ino: st.ino });
|
|
2904
2941
|
}
|
|
2905
|
-
reuse(
|
|
2906
|
-
const walked = this.open() ? this.walked.get(
|
|
2942
|
+
reuse(path2, ownerOnly = true) {
|
|
2943
|
+
const walked = this.open() ? this.walked.get(path2) : undefined;
|
|
2907
2944
|
if (!walked)
|
|
2908
2945
|
return false;
|
|
2909
2946
|
let st;
|
|
2910
2947
|
try {
|
|
2911
|
-
st = lstatSync3(
|
|
2948
|
+
st = lstatSync3(path2);
|
|
2912
2949
|
} catch (error) {
|
|
2913
2950
|
if (!missing(error))
|
|
2914
2951
|
throw error;
|
|
2915
|
-
this.walked.delete(
|
|
2952
|
+
this.walked.delete(path2);
|
|
2916
2953
|
return false;
|
|
2917
2954
|
}
|
|
2918
2955
|
if (!st.isDirectory() || st.isSymbolicLink() || st.dev !== walked.dev || st.ino !== walked.ino) {
|
|
2919
|
-
this.walked.delete(
|
|
2956
|
+
this.walked.delete(path2);
|
|
2920
2957
|
return false;
|
|
2921
2958
|
}
|
|
2922
2959
|
privateMode(st, ownerOnly);
|
|
@@ -2931,17 +2968,17 @@ class PrivateFiles {
|
|
|
2931
2968
|
this.namespace = namespace;
|
|
2932
2969
|
this.ownerOnly = ownerOnly;
|
|
2933
2970
|
}
|
|
2934
|
-
directory(
|
|
2935
|
-
const tail = relative2(this.namespace,
|
|
2971
|
+
directory(path2, create = false, walks) {
|
|
2972
|
+
const tail = relative2(this.namespace, path2);
|
|
2936
2973
|
if (isAbsolute3(tail) || tail === ".." || tail.startsWith(`..${sep3}`))
|
|
2937
2974
|
refused();
|
|
2938
2975
|
if (!create) {
|
|
2939
|
-
const full = resolve3(
|
|
2976
|
+
const full = resolve3(path2);
|
|
2940
2977
|
if (walks?.reuse(full, this.ownerOnly))
|
|
2941
2978
|
return true;
|
|
2942
2979
|
const namespace = resolve3(this.namespace);
|
|
2943
2980
|
let inside = false, last;
|
|
2944
|
-
const found = safeDirectory(
|
|
2981
|
+
const found = safeDirectory(path2, false, (at2, st) => {
|
|
2945
2982
|
if (at2 === namespace)
|
|
2946
2983
|
inside = true;
|
|
2947
2984
|
if (inside)
|
|
@@ -2965,7 +3002,7 @@ class PrivateFiles {
|
|
|
2965
3002
|
if (!missing(error))
|
|
2966
3003
|
throw error;
|
|
2967
3004
|
try {
|
|
2968
|
-
|
|
3005
|
+
mkdirSync4(at, { mode: 448 });
|
|
2969
3006
|
} catch (mkdirError) {
|
|
2970
3007
|
if (!(mkdirError instanceof Error && ("code" in mkdirError) && mkdirError.code === "EEXIST"))
|
|
2971
3008
|
throw mkdirError;
|
|
@@ -2978,12 +3015,12 @@ class PrivateFiles {
|
|
|
2978
3015
|
}
|
|
2979
3016
|
return true;
|
|
2980
3017
|
}
|
|
2981
|
-
read(
|
|
2982
|
-
if (!this.directory(dirname3(
|
|
3018
|
+
read(path2, maximum = METADATA_BYTES, walks) {
|
|
3019
|
+
if (!this.directory(dirname3(path2), false, walks))
|
|
2983
3020
|
return;
|
|
2984
3021
|
let before;
|
|
2985
3022
|
try {
|
|
2986
|
-
before = lstatSync3(
|
|
3023
|
+
before = lstatSync3(path2);
|
|
2987
3024
|
} catch (error) {
|
|
2988
3025
|
if (missing(error))
|
|
2989
3026
|
return;
|
|
@@ -2992,7 +3029,7 @@ class PrivateFiles {
|
|
|
2992
3029
|
if (!before.isFile() || before.nlink !== 1 || before.size > maximum)
|
|
2993
3030
|
refused();
|
|
2994
3031
|
privateMode(before, this.ownerOnly);
|
|
2995
|
-
const fd =
|
|
3032
|
+
const fd = openSync3(path2, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
|
|
2996
3033
|
try {
|
|
2997
3034
|
const opened = fstatSync2(fd);
|
|
2998
3035
|
if (!same(before, opened))
|
|
@@ -3006,60 +3043,60 @@ class PrivateFiles {
|
|
|
3006
3043
|
size += n;
|
|
3007
3044
|
}
|
|
3008
3045
|
const bytes = buffer.subarray(0, size);
|
|
3009
|
-
if (size !== opened.size || !same(opened, fstatSync2(fd)) || !same(opened, lstatSync3(
|
|
3046
|
+
if (size !== opened.size || !same(opened, fstatSync2(fd)) || !same(opened, lstatSync3(path2)))
|
|
3010
3047
|
refused();
|
|
3011
3048
|
return { bytes, identity: hash(bytes), stamp: stamp(opened) };
|
|
3012
3049
|
} finally {
|
|
3013
|
-
|
|
3050
|
+
closeSync3(fd);
|
|
3014
3051
|
}
|
|
3015
3052
|
}
|
|
3016
|
-
assert(
|
|
3017
|
-
const current = this.read(
|
|
3053
|
+
assert(path2, expected, maximum = METADATA_BYTES, walks) {
|
|
3054
|
+
const current = this.read(path2, maximum, walks);
|
|
3018
3055
|
if (current?.identity !== expected?.identity || current?.stamp !== expected?.stamp)
|
|
3019
3056
|
throw new ContractError("conflict");
|
|
3020
3057
|
}
|
|
3021
|
-
create(
|
|
3058
|
+
create(path2, bytes, maximum = METADATA_BYTES) {
|
|
3022
3059
|
if (Buffer.byteLength(bytes) > maximum)
|
|
3023
3060
|
throw new ContractError("oversize");
|
|
3024
|
-
this.directory(dirname3(
|
|
3025
|
-
const fd = retryWindowsFileOperation(() =>
|
|
3061
|
+
this.directory(dirname3(path2), true);
|
|
3062
|
+
const fd = retryWindowsFileOperation(() => openSync3(path2, "wx", 384));
|
|
3026
3063
|
try {
|
|
3027
3064
|
writeFileSync3(fd, bytes);
|
|
3028
3065
|
fsyncSync2(fd);
|
|
3029
3066
|
} finally {
|
|
3030
|
-
|
|
3067
|
+
closeSync3(fd);
|
|
3031
3068
|
}
|
|
3032
|
-
this.syncDirectory(dirname3(
|
|
3033
|
-
const result = this.read(
|
|
3069
|
+
this.syncDirectory(dirname3(path2));
|
|
3070
|
+
const result = this.read(path2, maximum);
|
|
3034
3071
|
if (!result || result.identity !== hash(bytes))
|
|
3035
3072
|
refused();
|
|
3036
3073
|
return result;
|
|
3037
3074
|
}
|
|
3038
|
-
replace(
|
|
3039
|
-
this.assert(
|
|
3040
|
-
const temporary = join3(dirname3(
|
|
3075
|
+
replace(path2, expected, bytes, maximum = METADATA_BYTES) {
|
|
3076
|
+
this.assert(path2, expected, maximum);
|
|
3077
|
+
const temporary = join3(dirname3(path2), `pending-${randomUUID2()}.json`);
|
|
3041
3078
|
this.create(temporary, bytes, maximum);
|
|
3042
|
-
this.assert(
|
|
3043
|
-
retryWindowsFileOperation(() => renameSync3(temporary,
|
|
3044
|
-
this.syncDirectory(dirname3(
|
|
3045
|
-
const result = this.read(
|
|
3079
|
+
this.assert(path2, expected, maximum);
|
|
3080
|
+
retryWindowsFileOperation(() => renameSync3(temporary, path2), () => this.assert(path2, expected, maximum));
|
|
3081
|
+
this.syncDirectory(dirname3(path2));
|
|
3082
|
+
const result = this.read(path2, maximum);
|
|
3046
3083
|
if (!result || result.identity !== hash(bytes))
|
|
3047
3084
|
refused();
|
|
3048
3085
|
return result;
|
|
3049
3086
|
}
|
|
3050
|
-
remove(
|
|
3051
|
-
this.assert(
|
|
3052
|
-
retryWindowsFileOperation(() => unlinkSync2(
|
|
3053
|
-
this.syncDirectory(dirname3(
|
|
3087
|
+
remove(path2, expected, maximum = METADATA_BYTES) {
|
|
3088
|
+
this.assert(path2, expected, maximum);
|
|
3089
|
+
retryWindowsFileOperation(() => unlinkSync2(path2), () => this.assert(path2, expected, maximum));
|
|
3090
|
+
this.syncDirectory(dirname3(path2));
|
|
3054
3091
|
}
|
|
3055
|
-
syncDirectory(
|
|
3092
|
+
syncDirectory(path2) {
|
|
3056
3093
|
if (process.platform === "win32")
|
|
3057
3094
|
return;
|
|
3058
|
-
const fd =
|
|
3095
|
+
const fd = openSync3(path2, constants3.O_RDONLY | constants3.O_DIRECTORY);
|
|
3059
3096
|
try {
|
|
3060
3097
|
fsyncSync2(fd);
|
|
3061
3098
|
} finally {
|
|
3062
|
-
|
|
3099
|
+
closeSync3(fd);
|
|
3063
3100
|
}
|
|
3064
3101
|
}
|
|
3065
3102
|
}
|
|
@@ -3103,10 +3140,10 @@ var registrationKeys = [
|
|
|
3103
3140
|
];
|
|
3104
3141
|
var reservationKeys = registrationKeys.filter((k) => !["schema", "phase", "checkoutToken"].includes(k));
|
|
3105
3142
|
function pathValue(value) {
|
|
3106
|
-
const
|
|
3107
|
-
if (!isAbsolute4(
|
|
3143
|
+
const path2 = text(value, 4096, true);
|
|
3144
|
+
if (!isAbsolute4(path2))
|
|
3108
3145
|
refused();
|
|
3109
|
-
return
|
|
3146
|
+
return path2;
|
|
3110
3147
|
}
|
|
3111
3148
|
function endpoint(value, allowZero = false) {
|
|
3112
3149
|
const s = text(value, 128, true);
|
|
@@ -3165,8 +3202,8 @@ function rootsOverlap(a, b) {
|
|
|
3165
3202
|
};
|
|
3166
3203
|
return within(a, b) || within(b, a);
|
|
3167
3204
|
}
|
|
3168
|
-
function names(
|
|
3169
|
-
const dir = opendirSync(
|
|
3205
|
+
function names(path2, allowed) {
|
|
3206
|
+
const dir = opendirSync(path2);
|
|
3170
3207
|
try {
|
|
3171
3208
|
let count = 0;
|
|
3172
3209
|
for (let entry = dir.readSync();entry; entry = dir.readSync()) {
|
|
@@ -3751,10 +3788,10 @@ function disabled(s) {
|
|
|
3751
3788
|
async function configureLaunchAgent(s, mode, requested) {
|
|
3752
3789
|
if (s.uid === undefined)
|
|
3753
3790
|
return { status: "unavailable" };
|
|
3754
|
-
const
|
|
3755
|
-
const previous = readGenerated(
|
|
3791
|
+
const path2 = plistPath(s);
|
|
3792
|
+
const previous = readGenerated(path2, MARKER2);
|
|
3756
3793
|
if (previous === null)
|
|
3757
|
-
return { status: "foreign", unit:
|
|
3794
|
+
return { status: "foreign", unit: path2 };
|
|
3758
3795
|
if (mode === "restart" && previous === undefined)
|
|
3759
3796
|
return { status: "absent" };
|
|
3760
3797
|
const sdk = sdkPackages(s.root);
|
|
@@ -3764,10 +3801,10 @@ async function configureLaunchAgent(s, mode, requested) {
|
|
|
3764
3801
|
const { workspace, redirectedFrom } = selectWorkspace(s, requested, kept);
|
|
3765
3802
|
const refused2 = await settle(s, kept);
|
|
3766
3803
|
if (refused2)
|
|
3767
|
-
return { ...refused2, unit:
|
|
3804
|
+
return { ...refused2, unit: path2 };
|
|
3768
3805
|
if (loaded(s))
|
|
3769
3806
|
s.launchctl(["bootout", target(s)]);
|
|
3770
|
-
write(
|
|
3807
|
+
write(path2, buildPlist({
|
|
3771
3808
|
node: s.node,
|
|
3772
3809
|
cli: join5(s.root, "src", "service-cli.mjs"),
|
|
3773
3810
|
workspace,
|
|
@@ -3775,7 +3812,7 @@ async function configureLaunchAgent(s, mode, requested) {
|
|
|
3775
3812
|
log: join5(s.home, "Library", "Logs", "cadence.log")
|
|
3776
3813
|
}));
|
|
3777
3814
|
const result = {
|
|
3778
|
-
unit:
|
|
3815
|
+
unit: path2,
|
|
3779
3816
|
workspace,
|
|
3780
3817
|
...redirectedFrom ? { redirectedFrom } : {}
|
|
3781
3818
|
};
|
|
@@ -3785,47 +3822,47 @@ async function configureLaunchAgent(s, mode, requested) {
|
|
|
3785
3822
|
return { status: "disabled", ...result };
|
|
3786
3823
|
if (!regularFile(join5(agentDirectory(s), "auth.json")))
|
|
3787
3824
|
return { status: "waiting-for-pi-login", ...result };
|
|
3788
|
-
const started = s.launchctl(["bootstrap", domain(s),
|
|
3825
|
+
const started = s.launchctl(["bootstrap", domain(s), path2]).status === 0;
|
|
3789
3826
|
return { status: started ? "started" : "failed", ...result };
|
|
3790
3827
|
}
|
|
3791
3828
|
async function stopLaunchAgent(s) {
|
|
3792
|
-
const
|
|
3793
|
-
const previous = readGenerated(
|
|
3829
|
+
const path2 = plistPath(s);
|
|
3830
|
+
const previous = readGenerated(path2, MARKER2);
|
|
3794
3831
|
if (previous === null)
|
|
3795
|
-
return { status: "foreign", unit:
|
|
3832
|
+
return { status: "foreign", unit: path2 };
|
|
3796
3833
|
if (previous === undefined)
|
|
3797
3834
|
return { status: "absent" };
|
|
3798
3835
|
const workspace = plistWorkspace(previous);
|
|
3799
3836
|
const refused2 = await settle(s, workspace);
|
|
3800
|
-
return { ...refused2 ?? { status: "stopped", workspace }, unit:
|
|
3837
|
+
return { ...refused2 ?? { status: "stopped", workspace }, unit: path2 };
|
|
3801
3838
|
}
|
|
3802
3839
|
async function removeLaunchAgent(s) {
|
|
3803
|
-
const
|
|
3804
|
-
const previous = readGenerated(
|
|
3840
|
+
const path2 = plistPath(s);
|
|
3841
|
+
const previous = readGenerated(path2, MARKER2);
|
|
3805
3842
|
if (previous === null)
|
|
3806
|
-
return { status: "foreign", unit:
|
|
3843
|
+
return { status: "foreign", unit: path2 };
|
|
3807
3844
|
if (previous === undefined)
|
|
3808
3845
|
return { status: "absent" };
|
|
3809
3846
|
const refused2 = await settle(s, plistWorkspace(previous));
|
|
3810
3847
|
if (refused2)
|
|
3811
|
-
return { ...refused2, unit:
|
|
3848
|
+
return { ...refused2, unit: path2 };
|
|
3812
3849
|
if (s.uid !== undefined && loaded(s))
|
|
3813
3850
|
s.launchctl(["bootout", target(s)]);
|
|
3814
|
-
rmSync2(
|
|
3815
|
-
return { status: "removed", unit:
|
|
3851
|
+
rmSync2(path2);
|
|
3852
|
+
return { status: "removed", unit: path2 };
|
|
3816
3853
|
}
|
|
3817
3854
|
function launchAgentStatus(s) {
|
|
3818
|
-
const
|
|
3819
|
-
const previous = readGenerated(
|
|
3855
|
+
const path2 = plistPath(s);
|
|
3856
|
+
const previous = readGenerated(path2, MARKER2);
|
|
3820
3857
|
if (previous === null)
|
|
3821
|
-
return { status: "foreign", unit:
|
|
3858
|
+
return { status: "foreign", unit: path2 };
|
|
3822
3859
|
if (previous === undefined)
|
|
3823
3860
|
return { status: "absent" };
|
|
3824
3861
|
const printed = s.uid === undefined ? undefined : s.launchctl(["print", target(s)]);
|
|
3825
3862
|
return {
|
|
3826
3863
|
status: printed?.status !== 0 ? "unloaded" : /^\s*state = running$/m.test(printed.stdout) ? "active" : "inactive",
|
|
3827
3864
|
enabled: s.uid !== undefined && disabled(s) ? "disabled" : "enabled",
|
|
3828
|
-
unit:
|
|
3865
|
+
unit: path2,
|
|
3829
3866
|
workspace: plistWorkspace(previous)
|
|
3830
3867
|
};
|
|
3831
3868
|
}
|
|
@@ -3865,7 +3902,7 @@ var utf16 = (text2) => Buffer.concat([Buffer.from([255, 254]), Buffer.from(text2
|
|
|
3865
3902
|
function launcherWorkspace(text2) {
|
|
3866
3903
|
return text2?.match(/^shell\.CurrentDirectory = "((?:[^"]|"")*)"\r?$/m)?.[1].replaceAll('""', '"');
|
|
3867
3904
|
}
|
|
3868
|
-
var runEntry = (
|
|
3905
|
+
var runEntry = (path2, s) => `"${wscript(s.env)}" "${path2}"`;
|
|
3869
3906
|
var present = (s, key) => s.reg(["query", key, "/v", RUN_VALUE]).status === 0;
|
|
3870
3907
|
function approvedOff(s) {
|
|
3871
3908
|
const r = s.reg(["query", APPROVED_KEY, "/v", RUN_VALUE]);
|
|
@@ -3873,10 +3910,10 @@ function approvedOff(s) {
|
|
|
3873
3910
|
return !!first && (Number.parseInt(first[1], 16) & 1) === 1;
|
|
3874
3911
|
}
|
|
3875
3912
|
async function configureLoginRun(s, mode, requested) {
|
|
3876
|
-
const
|
|
3877
|
-
const previous = readGenerated(
|
|
3913
|
+
const path2 = launcherPath(s);
|
|
3914
|
+
const previous = readGenerated(path2, MARKER3);
|
|
3878
3915
|
if (previous === null)
|
|
3879
|
-
return { status: "foreign", unit:
|
|
3916
|
+
return { status: "foreign", unit: path2 };
|
|
3880
3917
|
if (previous === undefined && present(s, RUN_KEY))
|
|
3881
3918
|
return { status: "foreign", unit: `${RUN_KEY}\\${RUN_VALUE}` };
|
|
3882
3919
|
if (mode === "restart" && previous === undefined)
|
|
@@ -3888,61 +3925,61 @@ async function configureLoginRun(s, mode, requested) {
|
|
|
3888
3925
|
const { workspace, redirectedFrom } = selectWorkspace(s, requested, kept);
|
|
3889
3926
|
const refused2 = await settle(s, kept);
|
|
3890
3927
|
if (refused2)
|
|
3891
|
-
return { ...refused2, unit:
|
|
3928
|
+
return { ...refused2, unit: path2 };
|
|
3892
3929
|
const environment = s.env.PI_CODING_AGENT_DIR ? [["PI_CODING_AGENT_DIR", agentDirectory(s)]] : [];
|
|
3893
|
-
write(
|
|
3930
|
+
write(path2, utf16(buildLauncher({
|
|
3894
3931
|
node: s.node,
|
|
3895
3932
|
cli: join6(s.root, "src", "service-cli.mjs"),
|
|
3896
3933
|
workspace,
|
|
3897
3934
|
environment
|
|
3898
3935
|
})));
|
|
3899
3936
|
const result = {
|
|
3900
|
-
unit:
|
|
3937
|
+
unit: path2,
|
|
3901
3938
|
workspace,
|
|
3902
3939
|
...redirectedFrom ? { redirectedFrom } : {}
|
|
3903
3940
|
};
|
|
3904
3941
|
if (mode === "install") {
|
|
3905
|
-
const entry = ["/v", RUN_VALUE, "/t", "REG_SZ", "/d", runEntry(
|
|
3942
|
+
const entry = ["/v", RUN_VALUE, "/t", "REG_SZ", "/d", runEntry(path2, s)];
|
|
3906
3943
|
if (s.reg(["add", RUN_KEY, ...entry, "/f"]).status !== 0)
|
|
3907
3944
|
return { status: "failed", ...result };
|
|
3908
3945
|
s.reg(["delete", APPROVED_KEY, "/v", RUN_VALUE, "/f"]);
|
|
3909
3946
|
}
|
|
3910
3947
|
if (!regularFile(join6(agentDirectory(s), "auth.json")))
|
|
3911
3948
|
return { status: "waiting-for-pi-login", ...result };
|
|
3912
|
-
return { status: s.launch(
|
|
3949
|
+
return { status: s.launch(path2) ? "started" : "failed", ...result };
|
|
3913
3950
|
}
|
|
3914
3951
|
async function stopLoginRun(s) {
|
|
3915
|
-
const
|
|
3916
|
-
const previous = readGenerated(
|
|
3952
|
+
const path2 = launcherPath(s);
|
|
3953
|
+
const previous = readGenerated(path2, MARKER3);
|
|
3917
3954
|
if (previous === null)
|
|
3918
|
-
return { status: "foreign", unit:
|
|
3955
|
+
return { status: "foreign", unit: path2 };
|
|
3919
3956
|
if (previous === undefined)
|
|
3920
3957
|
return { status: "absent" };
|
|
3921
3958
|
const workspace = launcherWorkspace(previous);
|
|
3922
3959
|
const refused2 = await settle(s, workspace);
|
|
3923
|
-
return { ...refused2 ?? { status: "stopped", workspace }, unit:
|
|
3960
|
+
return { ...refused2 ?? { status: "stopped", workspace }, unit: path2 };
|
|
3924
3961
|
}
|
|
3925
3962
|
async function removeLoginRun(s) {
|
|
3926
|
-
const
|
|
3927
|
-
const previous = readGenerated(
|
|
3963
|
+
const path2 = launcherPath(s);
|
|
3964
|
+
const previous = readGenerated(path2, MARKER3);
|
|
3928
3965
|
if (previous === null)
|
|
3929
|
-
return { status: "foreign", unit:
|
|
3966
|
+
return { status: "foreign", unit: path2 };
|
|
3930
3967
|
if (previous === undefined)
|
|
3931
3968
|
return { status: "absent" };
|
|
3932
3969
|
const refused2 = await settle(s, launcherWorkspace(previous));
|
|
3933
3970
|
if (refused2)
|
|
3934
|
-
return { ...refused2, unit:
|
|
3971
|
+
return { ...refused2, unit: path2 };
|
|
3935
3972
|
for (const key of [RUN_KEY, APPROVED_KEY])
|
|
3936
3973
|
if (present(s, key))
|
|
3937
3974
|
s.reg(["delete", key, "/v", RUN_VALUE, "/f"]);
|
|
3938
|
-
rmSync3(
|
|
3939
|
-
return { status: "removed", unit:
|
|
3975
|
+
rmSync3(path2);
|
|
3976
|
+
return { status: "removed", unit: path2 };
|
|
3940
3977
|
}
|
|
3941
3978
|
async function loginRunStatus(s) {
|
|
3942
|
-
const
|
|
3943
|
-
const previous = readGenerated(
|
|
3979
|
+
const path2 = launcherPath(s);
|
|
3980
|
+
const previous = readGenerated(path2, MARKER3);
|
|
3944
3981
|
if (previous === null)
|
|
3945
|
-
return { status: "foreign", unit:
|
|
3982
|
+
return { status: "foreign", unit: path2 };
|
|
3946
3983
|
if (previous === undefined)
|
|
3947
3984
|
return { status: "absent" };
|
|
3948
3985
|
const workspace = launcherWorkspace(previous);
|
|
@@ -3950,7 +3987,7 @@ async function loginRunStatus(s) {
|
|
|
3950
3987
|
return {
|
|
3951
3988
|
status: owner === "alive" ? "active" : "inactive",
|
|
3952
3989
|
enabled: present(s, RUN_KEY) && !approvedOff(s) ? "enabled" : "disabled",
|
|
3953
|
-
unit:
|
|
3990
|
+
unit: path2,
|
|
3954
3991
|
workspace
|
|
3955
3992
|
};
|
|
3956
3993
|
}
|
|
@@ -4011,13 +4048,13 @@ async function resolveRecovery(coordination, confirmation, history) {
|
|
|
4011
4048
|
// src/history/files.ts
|
|
4012
4049
|
import { createHash as createHash4, randomBytes as randomBytes2, randomUUID as randomUUID4 } from "node:crypto";
|
|
4013
4050
|
import {
|
|
4014
|
-
closeSync as
|
|
4015
|
-
constants as
|
|
4051
|
+
closeSync as closeSync5,
|
|
4052
|
+
constants as constants5,
|
|
4016
4053
|
fstatSync as fstatSync4,
|
|
4017
4054
|
fsyncSync as fsyncSync3,
|
|
4018
4055
|
lstatSync as lstatSync5,
|
|
4019
4056
|
opendirSync as opendirSync2,
|
|
4020
|
-
openSync as
|
|
4057
|
+
openSync as openSync5,
|
|
4021
4058
|
statfsSync
|
|
4022
4059
|
} from "node:fs";
|
|
4023
4060
|
import { open } from "node:fs/promises";
|
|
@@ -4042,11 +4079,11 @@ function decodeRunAdmission(value) {
|
|
|
4042
4079
|
|
|
4043
4080
|
// src/history/read-io.ts
|
|
4044
4081
|
import {
|
|
4045
|
-
closeSync as
|
|
4046
|
-
constants as
|
|
4082
|
+
closeSync as closeSync4,
|
|
4083
|
+
constants as constants4,
|
|
4047
4084
|
fstatSync as fstatSync3,
|
|
4048
4085
|
lstatSync as lstatSync4,
|
|
4049
|
-
openSync as
|
|
4086
|
+
openSync as openSync4,
|
|
4050
4087
|
readSync as readSync3
|
|
4051
4088
|
} from "node:fs";
|
|
4052
4089
|
import { dirname as dirname5 } from "node:path";
|
|
@@ -4054,32 +4091,32 @@ function bad() {
|
|
|
4054
4091
|
throw new ContractError("corrupt-selected");
|
|
4055
4092
|
}
|
|
4056
4093
|
var stamp2 = (s) => [s.dev, s.ino, s.size, s.mode, s.uid, s.nlink, s.ctimeMs, s.mtimeMs].join(":");
|
|
4057
|
-
function selected(files,
|
|
4058
|
-
if (!files.files.directory(dirname5(
|
|
4094
|
+
function selected(files, path2, read, walks) {
|
|
4095
|
+
if (!files.files.directory(dirname5(path2), false, walks))
|
|
4059
4096
|
bad();
|
|
4060
|
-
const before = lstatSync4(
|
|
4097
|
+
const before = lstatSync4(path2);
|
|
4061
4098
|
if (!before.isFile() || before.nlink !== 1 || process.platform !== "win32" && (before.mode & 63 || process.getuid && before.uid !== process.getuid()))
|
|
4062
4099
|
bad();
|
|
4063
|
-
const fd =
|
|
4100
|
+
const fd = openSync4(path2, constants4.O_RDONLY | (constants4.O_NOFOLLOW ?? 0));
|
|
4064
4101
|
try {
|
|
4065
4102
|
if (stamp2(fstatSync3(fd)) !== stamp2(before))
|
|
4066
4103
|
bad();
|
|
4067
4104
|
const result = read(fd, before.size, stamp2(before));
|
|
4068
|
-
if (stamp2(fstatSync3(fd)) !== stamp2(before) || stamp2(lstatSync4(
|
|
4105
|
+
if (stamp2(fstatSync3(fd)) !== stamp2(before) || stamp2(lstatSync4(path2)) !== stamp2(before))
|
|
4069
4106
|
bad();
|
|
4070
4107
|
return result;
|
|
4071
4108
|
} finally {
|
|
4072
|
-
|
|
4109
|
+
closeSync4(fd);
|
|
4073
4110
|
}
|
|
4074
4111
|
}
|
|
4075
|
-
function assertIncarnation(files,
|
|
4076
|
-
selected(files,
|
|
4112
|
+
function assertIncarnation(files, path2, expected) {
|
|
4113
|
+
selected(files, path2, (_fd, _size, current) => {
|
|
4077
4114
|
if (current !== expected)
|
|
4078
4115
|
bad();
|
|
4079
4116
|
});
|
|
4080
4117
|
}
|
|
4081
|
-
function statSize(files,
|
|
4082
|
-
return selected(files,
|
|
4118
|
+
function statSize(files, path2, maximum) {
|
|
4119
|
+
return selected(files, path2, (_fd, size) => {
|
|
4083
4120
|
if (size > maximum)
|
|
4084
4121
|
bad();
|
|
4085
4122
|
return size;
|
|
@@ -4688,19 +4725,19 @@ class HistoryFiles {
|
|
|
4688
4725
|
this.files.create(this.admissionPath(value.runId), encoded(value), RECORD_BYTES);
|
|
4689
4726
|
this.syncParents(this.admissionPath(value.runId));
|
|
4690
4727
|
}
|
|
4691
|
-
readBudgeted(
|
|
4728
|
+
readBudgeted(path2, maximum, budget) {
|
|
4692
4729
|
if (!budget)
|
|
4693
|
-
return this.files.read(
|
|
4730
|
+
return this.files.read(path2, maximum);
|
|
4694
4731
|
let size;
|
|
4695
4732
|
try {
|
|
4696
|
-
size = lstatSync5(
|
|
4733
|
+
size = lstatSync5(path2).size;
|
|
4697
4734
|
} catch {
|
|
4698
|
-
return this.files.read(
|
|
4735
|
+
return this.files.read(path2, 0, budget.walks);
|
|
4699
4736
|
}
|
|
4700
4737
|
if (size > maximum)
|
|
4701
4738
|
corrupt();
|
|
4702
4739
|
budget.reserve(size);
|
|
4703
|
-
return this.files.read(
|
|
4740
|
+
return this.files.read(path2, size, budget.walks);
|
|
4704
4741
|
}
|
|
4705
4742
|
readCommand(commandId, budget) {
|
|
4706
4743
|
decodeId(commandId);
|
|
@@ -4726,18 +4763,18 @@ class HistoryFiles {
|
|
|
4726
4763
|
syncParents(file) {
|
|
4727
4764
|
if (process.platform === "win32")
|
|
4728
4765
|
return;
|
|
4729
|
-
for (let
|
|
4730
|
-
if (!this.files.directory(
|
|
4766
|
+
for (let path2 = dirname6(file);; path2 = dirname6(path2)) {
|
|
4767
|
+
if (!this.files.directory(path2))
|
|
4731
4768
|
corrupt();
|
|
4732
|
-
const fd =
|
|
4769
|
+
const fd = openSync5(path2, constants5.O_RDONLY | constants5.O_DIRECTORY);
|
|
4733
4770
|
try {
|
|
4734
4771
|
fsyncSync3(fd);
|
|
4735
4772
|
} finally {
|
|
4736
|
-
|
|
4773
|
+
closeSync5(fd);
|
|
4737
4774
|
}
|
|
4738
|
-
if (
|
|
4775
|
+
if (path2 === this.coordination.directory)
|
|
4739
4776
|
break;
|
|
4740
|
-
if (
|
|
4777
|
+
if (path2 === dirname6(path2))
|
|
4741
4778
|
corrupt();
|
|
4742
4779
|
}
|
|
4743
4780
|
}
|
|
@@ -4774,14 +4811,14 @@ class HistoryFiles {
|
|
|
4774
4811
|
this.syncParents(this.artifactPath(ref));
|
|
4775
4812
|
}
|
|
4776
4813
|
usage() {
|
|
4777
|
-
const walk = (
|
|
4778
|
-
if (depth > 8 || !this.files.directory(
|
|
4814
|
+
const walk = (path2, depth) => {
|
|
4815
|
+
if (depth > 8 || !this.files.directory(path2))
|
|
4779
4816
|
corrupt();
|
|
4780
4817
|
let bytes = 0;
|
|
4781
|
-
const dir = opendirSync2(
|
|
4818
|
+
const dir = opendirSync2(path2);
|
|
4782
4819
|
try {
|
|
4783
4820
|
for (let e = dir.readSync();e; e = dir.readSync()) {
|
|
4784
|
-
const p = join7(
|
|
4821
|
+
const p = join7(path2, e.name), st = lstatSync5(p);
|
|
4785
4822
|
if (st.isDirectory())
|
|
4786
4823
|
bytes += walk(p, depth + 1);
|
|
4787
4824
|
else {
|
|
@@ -4974,17 +5011,17 @@ class HistoryFiles {
|
|
|
4974
5011
|
this.assertRecoveryIdle();
|
|
4975
5012
|
this.validateRecovery(row.value);
|
|
4976
5013
|
const { body, trailer, marker } = this.encodeBatch(state, [row]);
|
|
4977
|
-
const segment = state.segment + 1,
|
|
5014
|
+
const segment = state.segment + 1, path2 = this.segmentPath(segment);
|
|
4978
5015
|
publicationFences.set(this.directory, { ...state });
|
|
4979
5016
|
this.activity().generation++;
|
|
4980
|
-
const before = this.files.create(
|
|
5017
|
+
const before = this.files.create(path2, "", LIMITS.segmentBytes);
|
|
4981
5018
|
let committed;
|
|
4982
|
-
const fd = await open(
|
|
5019
|
+
const fd = await open(path2, constants5.O_WRONLY | constants5.O_APPEND | (constants5.O_NOFOLLOW ?? 0));
|
|
4983
5020
|
const assertOriginal = (bytes) => {
|
|
4984
|
-
const original = fstatSync4(fd.fd), selected2 = lstatSync5(
|
|
5021
|
+
const original = fstatSync4(fd.fd), selected2 = lstatSync5(path2);
|
|
4985
5022
|
if (original.dev !== selected2.dev || original.ino !== selected2.ino || original.nlink !== 1)
|
|
4986
5023
|
corrupt();
|
|
4987
|
-
const snapshot = this.files.read(
|
|
5024
|
+
const snapshot = this.files.read(path2, LIMITS.segmentBytes);
|
|
4988
5025
|
if (!snapshot || snapshot.identity !== hash(bytes))
|
|
4989
5026
|
corrupt();
|
|
4990
5027
|
return snapshot;
|
|
@@ -4992,7 +5029,7 @@ class HistoryFiles {
|
|
|
4992
5029
|
try {
|
|
4993
5030
|
await assertCurrent();
|
|
4994
5031
|
this.assertWritable();
|
|
4995
|
-
this.files.assert(
|
|
5032
|
+
this.files.assert(path2, before, LIMITS.segmentBytes);
|
|
4996
5033
|
assertOriginal("");
|
|
4997
5034
|
await fd.writeFile(body);
|
|
4998
5035
|
await observer?.("journal");
|
|
@@ -5001,7 +5038,7 @@ class HistoryFiles {
|
|
|
5001
5038
|
assertOriginal(body);
|
|
5002
5039
|
await fd.writeFile(trailer);
|
|
5003
5040
|
await fd.sync();
|
|
5004
|
-
this.syncParents(
|
|
5041
|
+
this.syncParents(path2);
|
|
5005
5042
|
await observer?.("commit");
|
|
5006
5043
|
await assertCurrent();
|
|
5007
5044
|
this.assertWritable();
|
|
@@ -5013,7 +5050,7 @@ class HistoryFiles {
|
|
|
5013
5050
|
this.assertWritable();
|
|
5014
5051
|
if (!committed)
|
|
5015
5052
|
corrupt();
|
|
5016
|
-
this.files.assert(
|
|
5053
|
+
this.files.assert(path2, committed, LIMITS.segmentBytes);
|
|
5017
5054
|
publicationFences.delete(this.directory);
|
|
5018
5055
|
return {
|
|
5019
5056
|
snapshot: committed,
|
|
@@ -5085,20 +5122,20 @@ class HistoryFiles {
|
|
|
5085
5122
|
if (state.bytes + Buffer.byteLength(body + trailer) > LIMITS.segmentBytes || state.records + rows.length + 1 > LIMITS.segmentRecords)
|
|
5086
5123
|
segment++;
|
|
5087
5124
|
lease.assert();
|
|
5088
|
-
const
|
|
5089
|
-
let before = this.files.read(
|
|
5125
|
+
const path2 = this.segmentPath(segment);
|
|
5126
|
+
let before = this.files.read(path2, LIMITS.segmentBytes);
|
|
5090
5127
|
if (segment !== state.segment) {
|
|
5091
5128
|
if (before)
|
|
5092
5129
|
corrupt();
|
|
5093
|
-
before = this.files.create(
|
|
5130
|
+
before = this.files.create(path2, "", LIMITS.segmentBytes);
|
|
5094
5131
|
}
|
|
5095
5132
|
if (!before || segment === state.segment && (before.bytes.length !== state.bytes || before.identity !== state.identity))
|
|
5096
5133
|
corrupt();
|
|
5097
|
-
const fd = await open(
|
|
5134
|
+
const fd = await open(path2, constants5.O_WRONLY | constants5.O_APPEND | (constants5.O_NOFOLLOW ?? 0));
|
|
5098
5135
|
try {
|
|
5099
5136
|
lease.assert();
|
|
5100
|
-
this.files.assert(
|
|
5101
|
-
const opened = await fd.stat(), selected2 = lstatSync5(
|
|
5137
|
+
this.files.assert(path2, before, LIMITS.segmentBytes);
|
|
5138
|
+
const opened = await fd.stat(), selected2 = lstatSync5(path2);
|
|
5102
5139
|
if (opened.ino !== selected2.ino || opened.dev !== selected2.dev || opened.nlink !== 1)
|
|
5103
5140
|
corrupt();
|
|
5104
5141
|
beforeEffect?.();
|
|
@@ -5112,7 +5149,7 @@ class HistoryFiles {
|
|
|
5112
5149
|
await observer?.("commit");
|
|
5113
5150
|
lease.assert();
|
|
5114
5151
|
this.assertWritable();
|
|
5115
|
-
const after = this.files.read(
|
|
5152
|
+
const after = this.files.read(path2, LIMITS.segmentBytes);
|
|
5116
5153
|
if (!after || after.identity !== createHash4("sha256").update(before.bytes).update(body).update(trailer).digest("hex"))
|
|
5117
5154
|
corrupt();
|
|
5118
5155
|
return {
|
|
@@ -5133,7 +5170,7 @@ class HistoryFiles {
|
|
|
5133
5170
|
checkpoint(lease, state) {
|
|
5134
5171
|
lease.assert();
|
|
5135
5172
|
this.assertWritable();
|
|
5136
|
-
const
|
|
5173
|
+
const path2 = join7(this.directory, "checkpoint.json"), old = this.files.read(path2);
|
|
5137
5174
|
const body = encoded({
|
|
5138
5175
|
schema: "cadence-history-checkpoint/2",
|
|
5139
5176
|
storeId: this.format.storeId,
|
|
@@ -5141,9 +5178,9 @@ class HistoryFiles {
|
|
|
5141
5178
|
segment: state.segment
|
|
5142
5179
|
});
|
|
5143
5180
|
if (old)
|
|
5144
|
-
this.files.replace(
|
|
5181
|
+
this.files.replace(path2, old, body);
|
|
5145
5182
|
else
|
|
5146
|
-
this.files.create(
|
|
5183
|
+
this.files.create(path2, body);
|
|
5147
5184
|
}
|
|
5148
5185
|
}
|
|
5149
5186
|
function emptyScan() {
|
|
@@ -5543,13 +5580,13 @@ function digestHistory(files, exclude) {
|
|
|
5543
5580
|
files.assertFormat();
|
|
5544
5581
|
const digest = createHash5("sha256");
|
|
5545
5582
|
let count = 0;
|
|
5546
|
-
const walk = (
|
|
5547
|
-
if (depth > 8 || !files.files.directory(
|
|
5583
|
+
const walk = (path2, depth) => {
|
|
5584
|
+
if (depth > 8 || !files.files.directory(path2))
|
|
5548
5585
|
throw new ContractError("corrupt-selected");
|
|
5549
|
-
const directory2 = lstatSync6(
|
|
5586
|
+
const directory2 = lstatSync6(path2);
|
|
5550
5587
|
digest.update(`incarnation:${directory2.dev}:${directory2.ino}
|
|
5551
5588
|
`);
|
|
5552
|
-
const dir = opendirSync3(
|
|
5589
|
+
const dir = opendirSync3(path2), names2 = [];
|
|
5553
5590
|
try {
|
|
5554
5591
|
for (let e = dir.readSync();e; e = dir.readSync()) {
|
|
5555
5592
|
if (++count > LIMITS.dataFrames)
|
|
@@ -5560,7 +5597,7 @@ function digestHistory(files, exclude) {
|
|
|
5560
5597
|
dir.closeSync();
|
|
5561
5598
|
}
|
|
5562
5599
|
for (const name of names2.sort()) {
|
|
5563
|
-
const selected2 = join8(
|
|
5600
|
+
const selected2 = join8(path2, name);
|
|
5564
5601
|
if (selected2 === exclude)
|
|
5565
5602
|
continue;
|
|
5566
5603
|
const rel = relative4(files.directory, selected2);
|
|
@@ -5700,13 +5737,13 @@ class HistoryRecovery {
|
|
|
5700
5737
|
},
|
|
5701
5738
|
acknowledgments: { processes: true, credentials: true, storage: true }
|
|
5702
5739
|
};
|
|
5703
|
-
const
|
|
5740
|
+
const path2 = files.segmentPath(state.segment + 1);
|
|
5704
5741
|
const assertCurrent = async () => {
|
|
5705
5742
|
await confirmation.assertLocalAuthority(structuredClone(expected));
|
|
5706
5743
|
const selected2 = await inspectRecovery(this.coordination);
|
|
5707
5744
|
if (selected2.owner !== "absent")
|
|
5708
5745
|
throw new ContractError("quarantine");
|
|
5709
|
-
if (selected2.epoch !== expected.epoch || selected2.coordinationDigest !== expected.coordinationDigest || digestHistory(files,
|
|
5746
|
+
if (selected2.epoch !== expected.epoch || selected2.coordinationDigest !== expected.coordinationDigest || digestHistory(files, path2) !== expected.history?.digest || this.coordination.recoverySnapshot().digest !== expected.coordinationDigest)
|
|
5710
5747
|
throw new ContractError("conflict");
|
|
5711
5748
|
};
|
|
5712
5749
|
await confirmation.assertLocalAuthority(structuredClone(expected));
|
|
@@ -5730,8 +5767,8 @@ class HistoryRecovery {
|
|
|
5730
5767
|
const assert = () => {
|
|
5731
5768
|
files.assertWritable();
|
|
5732
5769
|
files.assertRecoveryIdle();
|
|
5733
|
-
files.files.assert(
|
|
5734
|
-
if (digestHistory(files,
|
|
5770
|
+
files.files.assert(path2, after.snapshot, LIMITS.segmentBytes);
|
|
5771
|
+
if (digestHistory(files, path2) !== expected.history?.digest)
|
|
5735
5772
|
throw new ContractError("conflict");
|
|
5736
5773
|
};
|
|
5737
5774
|
assert();
|
|
@@ -5870,7 +5907,7 @@ async function localRecovery(c, operation) {
|
|
|
5870
5907
|
}
|
|
5871
5908
|
|
|
5872
5909
|
// src/service/runtime.ts
|
|
5873
|
-
import { homedir as
|
|
5910
|
+
import { homedir as homedir4 } from "node:os";
|
|
5874
5911
|
import { dirname as dirname7, isAbsolute as isAbsolute6, join as join9, resolve as resolve4 } from "node:path";
|
|
5875
5912
|
|
|
5876
5913
|
// src/service/config.ts
|
|
@@ -6005,8 +6042,8 @@ function selectStart(options, ambient) {
|
|
|
6005
6042
|
throw new ContractError("invalid");
|
|
6006
6043
|
let fromFile;
|
|
6007
6044
|
if (options.config) {
|
|
6008
|
-
const
|
|
6009
|
-
const bytes = f.read(
|
|
6045
|
+
const path2 = resolve4(options.config), f = new PrivateFiles(dirname7(path2));
|
|
6046
|
+
const bytes = f.read(path2);
|
|
6010
6047
|
if (!bytes)
|
|
6011
6048
|
throw new ContractError("corrupt-selected");
|
|
6012
6049
|
fromFile = decodeHostConfig(json(bytes));
|
|
@@ -6020,7 +6057,7 @@ function selectStart(options, ambient) {
|
|
|
6020
6057
|
port: options.port ?? 17423,
|
|
6021
6058
|
proxy: null
|
|
6022
6059
|
},
|
|
6023
|
-
agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join9(ambient.HOME ?? ambient.USERPROFILE ??
|
|
6060
|
+
agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join9(ambient.HOME ?? ambient.USERPROFILE ?? homedir4(), ".pi", "agent")),
|
|
6024
6061
|
tokenSource: options.tokenFile ? "file" : env ? "environment" : "generated",
|
|
6025
6062
|
tokenFile: options.tokenFile ? resolve4(options.tokenFile) : null
|
|
6026
6063
|
});
|
|
@@ -6457,6 +6494,16 @@ ${lingerGuidance(result.linger)}`;
|
|
|
6457
6494
|
return "";
|
|
6458
6495
|
}
|
|
6459
6496
|
}
|
|
6497
|
+
function researchTemplate(home) {
|
|
6498
|
+
try {
|
|
6499
|
+
const result = ensureUserConfig(home);
|
|
6500
|
+
return result.created ? `Cadence created the research configuration template ${result.path}
|
|
6501
|
+
` : "";
|
|
6502
|
+
} catch (error) {
|
|
6503
|
+
return `Cadence could not create the research configuration template: ${error instanceof Error ? error.message : String(error)}
|
|
6504
|
+
`;
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6460
6507
|
var SERVER_ACTIONS = [
|
|
6461
6508
|
"install",
|
|
6462
6509
|
"status",
|
|
@@ -6485,6 +6532,8 @@ async function serverMain(argv, hostUrl) {
|
|
|
6485
6532
|
if (argv.length)
|
|
6486
6533
|
throw new ContractError("invalid");
|
|
6487
6534
|
const s = hostSystem(dirname8(dirname8(fileURLToPath(hostUrl))), ownerControl);
|
|
6535
|
+
if (action === "install")
|
|
6536
|
+
process.stderr.write(researchTemplate(s.home));
|
|
6488
6537
|
let result;
|
|
6489
6538
|
try {
|
|
6490
6539
|
result = await serverAction(s, action, requested, linger);
|
|
@@ -6592,7 +6641,7 @@ Recovery resolve requires this local native terminal, exact unchanged dead-owner
|
|
|
6592
6641
|
`);
|
|
6593
6642
|
options.workspace = landing.workspace;
|
|
6594
6643
|
if (command === "start")
|
|
6595
|
-
|
|
6644
|
+
mkdirSync5(landing.workspace, { recursive: true });
|
|
6596
6645
|
}
|
|
6597
6646
|
let result;
|
|
6598
6647
|
if (command === "start")
|