@abelxiaoxing/cadence 2.0.13 → 2.0.14

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,10 +3,11 @@
3
3
 
4
4
  // src/service/cli.ts
5
5
  import { spawn } from "node:child_process";
6
- import { existsSync as existsSync3, mkdirSync as mkdirSync7 } from "node:fs";
6
+ import { mkdirSync as mkdirSync6 } from "node:fs";
7
7
  import { request } from "node:http";
8
- import { dirname as dirname9, join as join12, resolve as resolve5 } from "node:path";
9
- import { fileURLToPath } from "node:url";
8
+ import { homedir as homedir6 } from "node:os";
9
+ import { dirname as dirname13, join as join15, resolve as resolve7 } from "node:path";
10
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
10
11
 
11
12
  // src/install-config.mjs
12
13
  import { closeSync, constants, mkdirSync, openSync, writeSync } from "node:fs";
@@ -2303,13 +2304,10 @@ function stateHomeInside(root, stateHome = defaultStateHome()) {
2303
2304
  function privateWorkspace(requested, stateHome = defaultStateHome()) {
2304
2305
  return contains(join(canonicalAmbientRoot(stateHome), "cadence-assistant-v1"), canonicalAmbientRoot(requested));
2305
2306
  }
2306
- var LANDING_WORKSPACE = "cadence";
2307
- function effectiveWorkspace(requested, stateHome, home = homedir2()) {
2308
- if (privateWorkspace(requested, stateHome))
2309
- throw new Error("Private state cannot be a workspace");
2310
- if (!stateHomeInside(resolveWorkspace(requested).root, stateHome))
2311
- return { workspace: requested, redirected: false };
2312
- return { workspace: join(home, LANDING_WORKSPACE), redirected: true };
2307
+ function effectiveWorkspace(requested, stateHome, _home) {
2308
+ if (privateWorkspace(requested, stateHome) || stateHomeInside(resolveWorkspace(requested).root, stateHome))
2309
+ throw new Error("Choose a project outside Cadence private state; no default workspace was created");
2310
+ return { workspace: requested, redirected: false };
2313
2311
  }
2314
2312
 
2315
2313
  class TaskStore {
@@ -2993,6 +2991,14 @@ function readServerConfig(path2) {
2993
2991
  return { ...file, config: decode(file.source) };
2994
2992
  });
2995
2993
  }
2994
+ function withToken(file, token) {
2995
+ const { parts, token: line } = settings(decoded(file.snapshot));
2996
+ if (!line)
2997
+ throw new ContractError("invalid");
2998
+ const indent = /^\s*/.exec(parts[line.at])?.[0] ?? "";
2999
+ parts[line.at] = `${indent}${TOKEN_KEY}=${accessToken(token)}`;
3000
+ return parts.join("");
3001
+ }
2996
3002
  function ensureServerConfig(home, options = {}) {
2997
3003
  const path2 = serverConfigPath(home, options.name);
2998
3004
  if (options.port !== undefined && (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535))
@@ -3210,18 +3216,19 @@ function buildUnit(u) {
3210
3216
  "Description=Cadence shared task service and WebUI",
3211
3217
  "Documentation=https://github.com/abelxiaoxing/cadence/blob/main/docs/service-management.md",
3212
3218
  `ConditionPathExists=${specifiers(u.cli)}`,
3213
- `ConditionPathExists=${specifiers(join4(u.agentDir, "auth.json"))}`,
3214
- ...u.sdk.map((path2) => `ConditionPathExists=${specifiers(path2)}`),
3219
+ ...u.workspace === null ? [] : [`ConditionPathExists=${specifiers(join4(u.agentDir, "auth.json"))}`],
3220
+ ...(u.workspace === null ? [] : u.sdk).map((path2) => `ConditionPathExists=${specifiers(path2)}`),
3215
3221
  "StartLimitIntervalSec=300",
3216
3222
  "StartLimitBurst=3",
3217
3223
  "",
3218
3224
  "[Service]",
3219
3225
  "Type=simple",
3220
3226
  ...u.system ? ["User=root"] : [],
3221
- `WorkingDirectory=${specifiers(u.workspace)}`,
3227
+ ...u.workspace === null ? ["# Cadence workbench gateway; no project workspace"] : [],
3228
+ `WorkingDirectory=${specifiers(u.workspace ?? dirname4(dirname4(u.cli)))}`,
3222
3229
  ...u.environment.map(([k, v]) => `Environment=${quoted(`${k}=${v}`)}`),
3223
- `ExecStart=${command(["start", "--foreground", "--workspace", u.workspace, "--server-config", u.serverConfig])}`,
3224
- `ExecStop=-${command(["stop", "--workspace", u.workspace])}`,
3230
+ `ExecStart=${command([...u.workspace === null ? ["workbench", "start", "--foreground"] : ["start", "--foreground", "--workspace", u.workspace], "--server-config", u.serverConfig])}`,
3231
+ `ExecStop=-${command(u.workspace === null ? ["workbench", "stop", "--server-config", u.serverConfig] : ["stop", "--workspace", u.workspace])}`,
3225
3232
  "KillMode=mixed",
3226
3233
  "TimeoutStopSec=90",
3227
3234
  "Restart=on-failure",
@@ -3247,6 +3254,8 @@ function readGenerated(path2, marker) {
3247
3254
  }
3248
3255
  }
3249
3256
  function unitWorkspace(text2) {
3257
+ if (text2?.includes("# Cadence workbench gateway; no project workspace"))
3258
+ return null;
3250
3259
  return text2?.match(/^WorkingDirectory=(.+)$/m)?.[1].replaceAll("%%", "%");
3251
3260
  }
3252
3261
  function directory(path2) {
@@ -3260,19 +3269,15 @@ function serviceStateHome(s) {
3260
3269
  const state = s.platform === "win32" ? s.env.LOCALAPPDATA : s.env.XDG_STATE_HOME;
3261
3270
  return state && isAbsolute3(state) ? state : join4(s.home, ...s.platform === "win32" ? ["AppData", "Local"] : [".local", "state"]);
3262
3271
  }
3263
- function selectWorkspace(s, requested, kept, create = true) {
3264
- let chosen;
3265
- if (requested) {
3266
- chosen = expandHome(requested, s.home);
3267
- if (!directory(chosen))
3268
- throw new ContractError("invalid");
3269
- chosen = resolve3(chosen);
3270
- } else if (kept && directory(kept))
3271
- chosen = kept;
3272
- const landing = chosen ? effectiveWorkspace(chosen, serviceStateHome(s), s.home) : { workspace: join4(s.home, LANDING_WORKSPACE), redirected: false };
3273
- if (create && landing.workspace !== chosen)
3274
- mkdirSync5(landing.workspace, { recursive: true });
3275
- return landing.redirected ? { workspace: landing.workspace, redirectedFrom: chosen } : { workspace: landing.workspace };
3272
+ function selectWorkspace(s, requested, kept, _create = false) {
3273
+ const chosen = requested ? expandHome(requested, s.home) : kept;
3274
+ if (!chosen)
3275
+ return { workspace: null };
3276
+ if (!directory(chosen))
3277
+ throw new ContractError("invalid");
3278
+ return {
3279
+ workspace: effectiveWorkspace(resolve3(chosen), serviceStateHome(s)).workspace
3280
+ };
3276
3281
  }
3277
3282
  function write(path2, content) {
3278
3283
  mkdirSync5(dirname4(path2), { recursive: true });
@@ -3292,9 +3297,9 @@ function regularFile(path2) {
3292
3297
  }
3293
3298
  }
3294
3299
  async function settle(s, workspace) {
3295
- if (!workspace)
3300
+ if (workspace === undefined)
3296
3301
  return;
3297
- const status = await s.stopOwner(workspace);
3302
+ const status = await s.stopOwner(workspace, serviceConfigPath(s));
3298
3303
  if (status === "stopped")
3299
3304
  return;
3300
3305
  return {
@@ -3326,9 +3331,9 @@ function configureAutostart(s, mode, requested, linger = false) {
3326
3331
  if (mode === "restart" && previous === undefined)
3327
3332
  return { status: "absent" };
3328
3333
  const sdk = sdkPackages(s.root);
3329
- if (!sdk)
3334
+ const { workspace, redirectedFrom } = selectWorkspace(s, requested, mode === "install" && requested === undefined ? null : unitWorkspace(previous));
3335
+ if (workspace !== null && !sdk)
3330
3336
  return { status: "missing-pi-sdk" };
3331
- const { workspace, redirectedFrom } = selectWorkspace(s, requested, unitWorkspace(previous));
3332
3337
  const agentDir = agentDirectory(s);
3333
3338
  const content = buildUnit({
3334
3339
  node: s.node,
@@ -3336,7 +3341,7 @@ function configureAutostart(s, mode, requested, linger = false) {
3336
3341
  workspace,
3337
3342
  serverConfig: serviceConfigPath(s),
3338
3343
  agentDir,
3339
- sdk,
3344
+ sdk: sdk ?? [],
3340
3345
  environment: serviceEnvironment(s)
3341
3346
  });
3342
3347
  const enabled = s.systemctl(["is-enabled", s.unit]).stdout.trim();
@@ -3359,7 +3364,7 @@ function configureAutostart(s, mode, requested, linger = false) {
3359
3364
  if (mode === "install" && enabled !== "enabled" && s.systemctl(["enable", s.unit]).status !== 0)
3360
3365
  return { status: "failed", ...result };
3361
3366
  const boot = () => mode === "install" ? bootStart(s, linger) : {};
3362
- if (!regularFile(join4(agentDir, "auth.json")))
3367
+ if (workspace !== null && !regularFile(join4(agentDir, "auth.json")))
3363
3368
  return { status: "waiting-for-pi-login", ...result, ...boot() };
3364
3369
  s.systemctl(["reset-failed", s.unit]);
3365
3370
  const started = s.systemctl(["start", s.unit]).status === 0;
@@ -3604,7 +3609,8 @@ class ServiceCoordination {
3604
3609
  "access-config.json",
3605
3610
  "access.json",
3606
3611
  "host-config.json",
3607
- "history-v2"
3612
+ "history-v2",
3613
+ "conversations-v1"
3608
3614
  ]);
3609
3615
  this.files.directory(join5(this.directory, "history-v2"), false, walks);
3610
3616
  }
@@ -4037,218 +4043,1072 @@ class OwnerLease {
4037
4043
  }
4038
4044
  }
4039
4045
 
4040
- // src/service/launchd.ts
4041
- import { rmSync as rmSync2 } from "node:fs";
4042
- import { join as join6 } from "node:path";
4043
- var LABEL = "com.abelxiaoxing.cadence";
4044
- var MARKER2 = `<?xml version="1.0" encoding="UTF-8"?>
4045
- <!-- Generated by @abelxiaoxing/cadence`;
4046
- function plistPath(s) {
4047
- return join6(s.home, "Library", "LaunchAgents", `${LABEL}.plist`);
4048
- }
4049
- var xml = (value) => checked(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
4050
- function buildPlist(u) {
4051
- const string = (value) => ` <string>${xml(value)}</string>`;
4052
- return [
4053
- '<?xml version="1.0" encoding="UTF-8"?>',
4054
- "<!-- Generated by @abelxiaoxing/cadence; `cadence server install` and `restart` rewrite this file. -->",
4055
- '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
4056
- '<plist version="1.0">',
4057
- "<dict>",
4058
- "\t<key>Label</key>",
4059
- ` <string>${LABEL}</string>`,
4060
- "\t<key>ProgramArguments</key>",
4061
- "\t<array>",
4062
- ...[
4063
- u.node,
4064
- u.cli,
4065
- "start",
4066
- "--foreground",
4067
- "--workspace",
4068
- u.workspace,
4069
- "--server-config",
4070
- u.serverConfig
4071
- ].map(string),
4072
- "\t</array>",
4073
- "\t<key>WorkingDirectory</key>",
4074
- ` <string>${xml(u.workspace)}</string>`,
4075
- "\t<key>EnvironmentVariables</key>",
4076
- "\t<dict>",
4077
- ...u.environment.flatMap(([k, v]) => [
4078
- ` <key>${xml(k)}</key>`,
4079
- string(v)
4080
- ]),
4081
- "\t</dict>",
4082
- "\t<key>RunAtLoad</key>",
4083
- "\t<true/>",
4084
- "\t<key>ProcessType</key>",
4085
- "\t<string>Background</string>",
4086
- "\t<key>StandardOutPath</key>",
4087
- ` <string>${xml(u.log)}</string>`,
4088
- "\t<key>StandardErrorPath</key>",
4089
- ` <string>${xml(u.log)}</string>`,
4090
- "</dict>",
4091
- "</plist>",
4092
- ""
4093
- ].join(`
4094
- `);
4095
- }
4096
- function plistWorkspace(text2) {
4097
- const m = text2?.match(/^\t<key>WorkingDirectory<\/key>\n\t<string>([^<]*)<\/string>$/m);
4098
- return m?.[1].replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
4046
+ // src/service/gateway-cli.ts
4047
+ import { homedir as homedir5 } from "node:os";
4048
+ import { dirname as dirname8, join as join9, resolve as resolve6 } from "node:path";
4049
+ import { fileURLToPath } from "node:url";
4050
+
4051
+ // src/service/config.ts
4052
+ import { isIP } from "node:net";
4053
+ function decodeHttpPolicy(value) {
4054
+ const o = closed(value, ["schema", "host", "port", "proxy"]);
4055
+ if (o.host !== "127.0.0.1" && o.host !== "::1")
4056
+ throw new ContractError("invalid");
4057
+ let proxy = null;
4058
+ if (o.proxy !== null) {
4059
+ const p = closed(o.proxy, ["origin", "trustedAddresses"]);
4060
+ const origin = text(p.origin, 256, true);
4061
+ let url;
4062
+ try {
4063
+ url = new URL(origin);
4064
+ } catch {
4065
+ throw new ContractError("invalid");
4066
+ }
4067
+ if (url.protocol !== "https:" || url.origin !== origin || url.username || url.password)
4068
+ throw new ContractError("invalid");
4069
+ const trustedAddresses = list(p.trustedAddresses, 8, (v) => {
4070
+ const address = text(v, 64, true);
4071
+ if (!isIP(address) || !loopbackAddress(address))
4072
+ throw new ContractError("invalid");
4073
+ return address;
4074
+ });
4075
+ if (!trustedAddresses.length || new Set(trustedAddresses).size !== trustedAddresses.length)
4076
+ throw new ContractError("invalid");
4077
+ proxy = { origin, trustedAddresses };
4078
+ }
4079
+ return {
4080
+ schema: literal(o.schema, "cadence-service-http/1"),
4081
+ host: o.host,
4082
+ port: integer(o.port, 0, 65535),
4083
+ proxy
4084
+ };
4099
4085
  }
4100
- var domain = (s) => `gui/${s.uid}`;
4101
- var target = (s) => `${domain(s)}/${LABEL}`;
4102
- var loaded = (s) => s.launchctl(["print", target(s)]).status === 0;
4103
- function disabled(s) {
4104
- return new RegExp(`"${LABEL.replaceAll(".", "\\.")}" => (true|disabled)`).test(s.launchctl(["print-disabled", domain(s)]).stdout);
4086
+ function loopbackAddress(host) {
4087
+ const mapped = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(host);
4088
+ const h = mapped ? mapped[1] : host;
4089
+ if (h.includes(":"))
4090
+ return h === "::1";
4091
+ const parts = h.split(".");
4092
+ return parts.length === 4 && parts.every((p) => /^\d{1,3}$/.test(p) && Number(p) <= 255) && Number(parts[0]) === 127;
4105
4093
  }
4106
- async function configureLaunchAgent(s, mode, requested) {
4107
- if (s.uid === undefined)
4108
- return { status: "unavailable" };
4109
- const path2 = plistPath(s);
4110
- const previous = readGenerated(path2, MARKER2);
4111
- if (previous === null)
4112
- return { status: "foreign", unit: path2 };
4113
- if (mode === "restart" && previous === undefined)
4114
- return { status: "absent" };
4115
- const sdk = sdkPackages(s.root);
4116
- if (!sdk)
4117
- return { status: "missing-pi-sdk" };
4118
- const kept = plistWorkspace(previous);
4119
- const { workspace, redirectedFrom } = selectWorkspace(s, requested, kept);
4120
- const refused2 = await settle(s, kept);
4121
- if (refused2)
4122
- return { ...refused2, unit: path2 };
4123
- if (loaded(s))
4124
- s.launchctl(["bootout", target(s)]);
4125
- write(path2, buildPlist({
4126
- node: s.node,
4127
- cli: join6(s.root, "src", "service-cli.mjs"),
4128
- workspace,
4129
- serverConfig: serverConfigPath(s.home),
4130
- environment: serviceEnvironment(s),
4131
- log: join6(s.home, "Library", "Logs", "cadence.log")
4132
- }));
4133
- const result = {
4134
- unit: path2,
4135
- workspace,
4136
- ...redirectedFrom ? { redirectedFrom } : {}
4137
- };
4138
- if (mode === "install")
4139
- s.launchctl(["enable", target(s)]);
4140
- else if (disabled(s))
4141
- return { status: "disabled", ...result };
4142
- if (!regularFile(join6(agentDirectory(s), "auth.json")))
4143
- return { status: "waiting-for-pi-login", ...result };
4144
- const started = s.launchctl(["bootstrap", domain(s), path2]).status === 0;
4145
- return { status: started ? "started" : "failed", ...result };
4094
+ function localOrigin(policy, actualPort) {
4095
+ integer(actualPort, 1, 65535);
4096
+ return `http://${policy.host === "::1" ? "[::1]" : policy.host}:${actualPort}`;
4146
4097
  }
4147
- async function stopLaunchAgent(s) {
4148
- const path2 = plistPath(s);
4149
- const previous = readGenerated(path2, MARKER2);
4150
- if (previous === null)
4151
- return { status: "foreign", unit: path2 };
4152
- if (previous === undefined)
4153
- return { status: "absent" };
4154
- const workspace = plistWorkspace(previous);
4155
- const refused2 = await settle(s, workspace);
4156
- return { ...refused2 ?? { status: "stopped", workspace }, unit: path2 };
4098
+
4099
+ // src/service/gateway-store.ts
4100
+ import { randomBytes as randomBytes4, randomUUID as randomUUID5 } from "node:crypto";
4101
+ import { opendirSync as opendirSync2 } from "node:fs";
4102
+ import { join as join7, resolve as resolve4 } from "node:path";
4103
+
4104
+ // src/service/auth.ts
4105
+ import {
4106
+ createHash as createHash3,
4107
+ randomBytes as randomBytes3,
4108
+ randomUUID as randomUUID4,
4109
+ timingSafeEqual
4110
+ } from "node:crypto";
4111
+ import { dirname as dirname6, isAbsolute as isAbsolute5, join as join6 } from "node:path";
4112
+ function sameSecret(candidate, expected) {
4113
+ const a = createHash3("sha256").update(candidate).digest();
4114
+ const b = createHash3("sha256").update(expected).digest();
4115
+ return timingSafeEqual(a, b);
4116
+ }
4117
+ function buildTokenCookie(name, encodedValue, maxAge, secure) {
4118
+ return `${name}=${encodedValue}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${maxAge}${secure ? "; Secure" : ""}`;
4119
+ }
4120
+ function decodeCookieToken(raw) {
4121
+ if (!raw)
4122
+ return "";
4123
+ try {
4124
+ return decodeURIComponent(raw);
4125
+ } catch {
4126
+ return raw;
4127
+ }
4157
4128
  }
4158
- async function removeLaunchAgent(s) {
4159
- const path2 = plistPath(s);
4160
- const previous = readGenerated(path2, MARKER2);
4161
- if (previous === null)
4162
- return { status: "foreign", unit: path2 };
4163
- if (previous === undefined)
4164
- return { status: "absent" };
4165
- const refused2 = await settle(s, plistWorkspace(previous));
4166
- if (refused2)
4167
- return { ...refused2, unit: path2 };
4168
- if (s.uid !== undefined && loaded(s))
4169
- s.launchctl(["bootout", target(s)]);
4170
- rmSync2(path2);
4171
- return { status: "removed", unit: path2 };
4129
+ function cookieTokens(header, name) {
4130
+ if (!header)
4131
+ return [];
4132
+ let found = false;
4133
+ const out = [];
4134
+ for (const part of header.split(";")) {
4135
+ const [key, ...rest] = part.trim().split("=");
4136
+ if (key !== name)
4137
+ continue;
4138
+ if (found)
4139
+ throw new ContractError("invalid");
4140
+ found = true;
4141
+ const raw = rest.join("=").trim();
4142
+ out.push(raw, decodeCookieToken(raw));
4143
+ }
4144
+ return out;
4172
4145
  }
4173
- function launchAgentStatus(s) {
4174
- const path2 = plistPath(s);
4175
- const previous = readGenerated(path2, MARKER2);
4176
- if (previous === null)
4177
- return { status: "foreign", unit: path2 };
4178
- if (previous === undefined)
4179
- return { status: "absent" };
4180
- const printed = s.uid === undefined ? undefined : s.launchctl(["print", target(s)]);
4146
+ function sourceConfig(source) {
4147
+ const given = [source.environment, source.tokenFile, source.serverConfig];
4148
+ if (given.filter((v) => v !== undefined).length > 1)
4149
+ throw new ContractError("invalid");
4150
+ if (source.environment !== undefined)
4151
+ accessToken(source.environment);
4152
+ if (source.serverConfig !== undefined)
4153
+ accessToken(source.token);
4154
+ const path2 = source.tokenFile ?? source.serverConfig;
4155
+ if (path2 !== undefined && (!isAbsolute5(path2) || path2.includes("\x00")))
4156
+ throw new ContractError("invalid");
4181
4157
  return {
4182
- status: printed?.status !== 0 ? "unloaded" : /^\s*state = running$/m.test(printed.stdout) ? "active" : "inactive",
4183
- enabled: s.uid !== undefined && disabled(s) ? "disabled" : "enabled",
4184
- unit: path2,
4185
- workspace: plistWorkspace(previous)
4158
+ schema: "cadence-service-access-config/1",
4159
+ source: source.environment !== undefined ? "environment" : source.tokenFile !== undefined ? "file" : source.serverConfig !== undefined ? "config" : "generated",
4160
+ path: path2 ?? null
4186
4161
  };
4187
4162
  }
4188
-
4189
- // src/service/login-run.ts
4190
- import { rmSync as rmSync3 } from "node:fs";
4191
- import { isAbsolute as isAbsolute5, join as join7 } from "node:path";
4192
- var RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
4193
- var APPROVED_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run";
4194
- var RUN_VALUE = "Cadence";
4195
- var MARKER3 = "' Generated by @abelxiaoxing/cadence";
4196
- function launcherPath(s) {
4197
- const local = s.env.LOCALAPPDATA;
4198
- return join7(local && isAbsolute5(local) ? local : join7(s.home, "AppData", "Local"), "cadence-server", "cadence.vbs");
4163
+ function readSource(config) {
4164
+ if (config.source === "file" && config.path)
4165
+ readExternal(config.path);
4199
4166
  }
4200
- var literal2 = (value) => `"${checked(value).replaceAll('"', '""')}"`;
4201
- function argument2(value) {
4202
- if (checked(value).includes('"'))
4167
+ function selectedMaterial(files, directory2, source) {
4168
+ const config = sourceConfig(source);
4169
+ if (config.source !== "config")
4203
4170
  throw new ContractError("invalid");
4204
- return `"${value}"`;
4205
- }
4206
- function buildLauncher(u) {
4207
- const command = `${argument2(u.node)} ${argument2(u.cli)} start --workspace ${argument2(u.workspace)} --server-config ${argument2(u.serverConfig)}`;
4208
- return [
4209
- `${MARKER3}; \`cadence server install\` and \`restart\` rewrite this file.`,
4210
- `' Started at login by ${RUN_KEY}\\${RUN_VALUE}; window style 0 hides the console.`,
4211
- 'Set shell = CreateObject("WScript.Shell")',
4212
- ...u.environment.length ? ['Set env = shell.Environment("Process")'] : [],
4213
- ...u.environment.map(([k, v]) => `env(${literal2(k)}) = ${literal2(v)}`),
4214
- `shell.CurrentDirectory = ${literal2(u.workspace)}`,
4215
- `shell.Run ${literal2(command)}, 0, False`,
4216
- ""
4217
- ].join(`\r
4218
- `);
4219
- }
4220
- var utf16 = (text2) => Buffer.concat([Buffer.from([255, 254]), Buffer.from(text2, "utf16le")]);
4221
- function launcherWorkspace(text2) {
4222
- return text2?.match(/^shell\.CurrentDirectory = "((?:[^"]|"")*)"\r?$/m)?.[1].replaceAll('""', '"');
4223
- }
4224
- var runEntry = (path2, s) => `"${wscript(s.env)}" "${path2}"`;
4225
- var present = (s, key) => s.reg(["query", key, "/v", RUN_VALUE]).status === 0;
4226
- function approvedOff(s) {
4227
- const r = s.reg(["query", APPROVED_KEY, "/v", RUN_VALUE]);
4228
- const first = r.status === 0 && /REG_BINARY\s+([0-9A-F]{2})/i.exec(r.stdout);
4229
- return !!first && (Number.parseInt(first[1], 16) & 1) === 1;
4171
+ const marker = join6(directory2, "access-config.json"), access = join6(directory2, "access.json");
4172
+ const markerFile = files.read(marker), accessFile = files.read(access);
4173
+ if (!markerFile || !accessFile)
4174
+ throw new ContractError("corrupt-selected");
4175
+ const previous = closed(json(markerFile), ["schema", "source", "path"]);
4176
+ literal(previous.schema, "cadence-service-access-config/1");
4177
+ oneOf(previous.source, ["generated", "environment", "file", "config"]);
4178
+ const r = closed(json(accessFile), ["schema", "generation", "token"]);
4179
+ literal(r.schema, "cadence-service-access/1");
4180
+ const generation = integer(r.generation, 1);
4181
+ if (previous.source === "generated" !== (r.token !== null))
4182
+ throw new ContractError("corrupt-selected");
4183
+ return {
4184
+ config,
4185
+ previous,
4186
+ generation,
4187
+ marker,
4188
+ markerFile,
4189
+ access,
4190
+ accessFile
4191
+ };
4230
4192
  }
4231
- async function configureLoginRun(s, mode, requested) {
4232
- const path2 = launcherPath(s);
4233
- const previous = readGenerated(path2, MARKER3);
4234
- if (previous === null)
4235
- return { status: "foreign", unit: path2 };
4236
- if (previous === undefined && present(s, RUN_KEY))
4237
- return { status: "foreign", unit: `${RUN_KEY}\\${RUN_VALUE}` };
4238
- if (mode === "restart" && previous === undefined)
4239
- return { status: "absent" };
4240
- const sdk = sdkPackages(s.root);
4241
- if (!sdk)
4242
- return { status: "missing-pi-sdk" };
4243
- const kept = launcherWorkspace(previous);
4244
- const { workspace, redirectedFrom } = selectWorkspace(s, requested, kept);
4245
- const refused2 = await settle(s, kept);
4246
- if (refused2)
4247
- return { ...refused2, unit: path2 };
4248
- const environment = s.env.PI_CODING_AGENT_DIR ? [["PI_CODING_AGENT_DIR", agentDirectory(s)]] : [];
4193
+
4194
+ class AccessMaterial {
4195
+ files;
4196
+ directory;
4197
+ assertOwner;
4198
+ configFile;
4199
+ accessFile;
4200
+ externalFile;
4201
+ external;
4202
+ record;
4203
+ secret;
4204
+ fenced = false;
4205
+ listeners = new Set;
4206
+ config;
4207
+ static validateSource(source) {
4208
+ readSource(sourceConfig(source));
4209
+ }
4210
+ static initialize(files, directory2, source = {}) {
4211
+ const config = sourceConfig(source);
4212
+ const marker = join6(directory2, "access-config.json"), access = join6(directory2, "access.json");
4213
+ if (files.read(marker) || files.read(access) || files.read(join6(directory2, "registration.json")) || files.read(join6(directory2, "owner.lock")))
4214
+ throw new ContractError("conflict");
4215
+ readSource(config);
4216
+ files.create(marker, encoded(config));
4217
+ files.create(access, encoded({
4218
+ schema: "cadence-service-access/1",
4219
+ generation: 1,
4220
+ token: config.source === "generated" ? randomBytes3(32).toString("base64url") : null
4221
+ }));
4222
+ }
4223
+ static validateReselection(files, directory2, source) {
4224
+ selectedMaterial(files, directory2, source);
4225
+ }
4226
+ static reselect(files, directory2, source) {
4227
+ const m = selectedMaterial(files, directory2, source);
4228
+ if (encoded(m.previous) === encoded(m.config))
4229
+ return;
4230
+ files.replace(m.marker, m.markerFile, encoded(m.config));
4231
+ files.replace(m.access, m.accessFile, encoded({
4232
+ schema: "cadence-service-access/1",
4233
+ generation: integer(m.generation + 1, 1),
4234
+ token: null
4235
+ }));
4236
+ }
4237
+ constructor(files, directory2, source = {}, assertOwner) {
4238
+ this.files = files;
4239
+ this.directory = directory2;
4240
+ this.assertOwner = assertOwner;
4241
+ this.config = sourceConfig(source);
4242
+ const configFile = files.read(join6(directory2, "access-config.json"));
4243
+ const accessFile = files.read(join6(directory2, "access.json"));
4244
+ if (!configFile || !accessFile)
4245
+ throw new ContractError("corrupt-selected");
4246
+ const c = closed(json(configFile), ["schema", "source", "path"]);
4247
+ if (encoded(c) !== encoded(this.config))
4248
+ throw new ContractError("conflict");
4249
+ const r = closed(json(accessFile), ["schema", "generation", "token"]);
4250
+ this.record = {
4251
+ schema: literal(r.schema, "cadence-service-access/1"),
4252
+ generation: integer(r.generation, 1),
4253
+ token: r.token === null ? null : accessToken(r.token)
4254
+ };
4255
+ if (this.config.source === "generated" !== (this.record.token !== null))
4256
+ throw new ContractError("corrupt-selected");
4257
+ if (this.config.source === "file" && this.config.path) {
4258
+ const loaded = readExternal(this.config.path);
4259
+ this.external = loaded.files;
4260
+ this.externalFile = loaded.file;
4261
+ this.secret = loaded.token;
4262
+ } else
4263
+ this.secret = accessToken(this.record.token ?? source.environment ?? source.token);
4264
+ this.configFile = configFile;
4265
+ this.accessFile = accessFile;
4266
+ }
4267
+ get generation() {
4268
+ return this.record.generation;
4269
+ }
4270
+ get available() {
4271
+ return !this.fenced;
4272
+ }
4273
+ matches(candidate) {
4274
+ return !this.fenced && utf8Bytes(candidate) <= TOKEN_BYTES && sameSecret(candidate, this.secret);
4275
+ }
4276
+ onRevoke(listener) {
4277
+ this.listeners.add(listener);
4278
+ return () => {
4279
+ this.listeners.delete(listener);
4280
+ };
4281
+ }
4282
+ revoke() {
4283
+ for (const listener of this.listeners) {
4284
+ try {
4285
+ listener();
4286
+ } catch {
4287
+ this.fenced = true;
4288
+ }
4289
+ }
4290
+ }
4291
+ assertCurrent() {
4292
+ try {
4293
+ this.assert();
4294
+ } catch {
4295
+ this.fenced = true;
4296
+ this.revoke();
4297
+ throw new ContractError("quarantine");
4298
+ }
4299
+ }
4300
+ assert() {
4301
+ if (this.fenced)
4302
+ throw new ContractError("quarantine");
4303
+ this.files.assert(join6(this.directory, "access-config.json"), this.configFile);
4304
+ this.files.assert(join6(this.directory, "access.json"), this.accessFile);
4305
+ if (this.config.source === "file" && this.config.path)
4306
+ this.external?.assert(this.config.path, this.externalFile, TOKEN_BYTES);
4307
+ }
4308
+ redactionLiterals() {
4309
+ this.assertCurrent();
4310
+ return [this.secret];
4311
+ }
4312
+ tokenLink(origin) {
4313
+ this.assert();
4314
+ const url = new URL(origin);
4315
+ if (url.origin !== origin)
4316
+ throw new ContractError("invalid");
4317
+ return `${origin}/?token=${encodeURIComponent(this.secret)}`;
4318
+ }
4319
+ assertRotation() {
4320
+ this.assertOwner?.();
4321
+ this.assert();
4322
+ }
4323
+ rotate() {
4324
+ if (this.config.source === "environment")
4325
+ throw new ContractError("unsupported-context");
4326
+ let configured;
4327
+ if (this.config.source === "config" && this.config.path) {
4328
+ configured = readServerConfig(this.config.path);
4329
+ if (!sameSecret(configured.config.token, this.secret))
4330
+ throw new ContractError("conflict");
4331
+ }
4332
+ try {
4333
+ this.assertRotation();
4334
+ const secret = randomBytes3(32).toString("base64url");
4335
+ const generation = integer(this.record.generation + 1, 1);
4336
+ if (this.config.path && this.external && this.externalFile)
4337
+ this.externalFile = this.external.replace(this.config.path, this.externalFile, secret, TOKEN_BYTES);
4338
+ if (configured)
4339
+ configured.files.replace(configured.real, configured.snapshot, withToken(configured, secret), CONFIG_BYTES);
4340
+ const record = {
4341
+ ...this.record,
4342
+ generation,
4343
+ token: this.config.source === "generated" ? secret : null
4344
+ };
4345
+ this.assertRotation();
4346
+ this.accessFile = this.files.replace(join6(this.directory, "access.json"), this.accessFile, encoded(record));
4347
+ this.assertRotation();
4348
+ this.record = record;
4349
+ this.secret = secret;
4350
+ this.revoke();
4351
+ if (this.fenced)
4352
+ throw new ContractError("quarantine");
4353
+ } catch {
4354
+ this.fenced = true;
4355
+ this.revoke();
4356
+ throw new ContractError("quarantine");
4357
+ }
4358
+ }
4359
+ }
4360
+ function readExternal(path2) {
4361
+ const files = new PrivateFiles(dirname6(path2));
4362
+ const file = files.read(path2, TOKEN_BYTES);
4363
+ if (!file)
4364
+ throw new ContractError("corrupt-selected");
4365
+ try {
4366
+ return {
4367
+ files,
4368
+ file,
4369
+ token: accessToken(new TextDecoder("utf-8", { fatal: true }).decode(file.bytes))
4370
+ };
4371
+ } catch {
4372
+ throw new ContractError("corrupt-selected");
4373
+ }
4374
+ }
4375
+ class HttpAuthentication {
4376
+ material;
4377
+ epoch;
4378
+ localCapability;
4379
+ csrf = randomUUID4();
4380
+ cookieName;
4381
+ constructor(material, epoch, workspaceId, localCapability) {
4382
+ this.material = material;
4383
+ this.epoch = epoch;
4384
+ this.localCapability = localCapability;
4385
+ decodeId(epoch);
4386
+ decodeId(workspaceId);
4387
+ if (!/^[A-Za-z0-9_-]{43}$/.test(localCapability))
4388
+ throw new ContractError("invalid");
4389
+ this.cookieName = `cadence_${workspaceId.replaceAll("-", "")}`;
4390
+ }
4391
+ bootstrap(value, boundary) {
4392
+ this.material.assertCurrent();
4393
+ const h = boundary.headers;
4394
+ const site = h.get("sec-fetch-site");
4395
+ if (site !== undefined && !["same-origin", "none"].includes(site) && (!["same-site", "cross-site"].includes(site) || h.get("sec-fetch-mode") !== "navigate" || h.get("sec-fetch-dest") !== "document"))
4396
+ throw new ContractError("forbidden");
4397
+ if (!this.material.matches(value))
4398
+ throw new ContractError("unauthenticated");
4399
+ return buildTokenCookie(this.cookieName, encodeURIComponent(value), LIMITS.browserLifetimeMs / 1000, boundary.secure);
4400
+ }
4401
+ authenticate(boundary, attachmentId, mutation = false, websocket = false) {
4402
+ this.material.assertCurrent();
4403
+ if (!this.material.available)
4404
+ throw new ContractError("quarantine");
4405
+ const h = boundary.headers;
4406
+ const local = h.get("x-cadence-local");
4407
+ if (local !== undefined) {
4408
+ if (!boundary.local || h.has("cookie") || [...h.keys()].some((name) => name.startsWith("sec-fetch-") && (name !== "sec-fetch-mode" || h.get(name) !== "cors")) || !sameSecret(local, this.localCapability))
4409
+ throw new ContractError("forbidden");
4410
+ } else {
4411
+ if (h.has("authorization") || h.has("x-cadence-token"))
4412
+ throw new ContractError("forbidden");
4413
+ if (!cookieTokens(h.get("cookie"), this.cookieName).some((v) => this.material.matches(v)))
4414
+ throw new ContractError("unauthenticated");
4415
+ if (h.has("sec-fetch-site") && !["same-origin", "none"].includes(h.get("sec-fetch-site") ?? ""))
4416
+ throw new ContractError("forbidden");
4417
+ if ((mutation || websocket) && h.get("origin") !== boundary.origin)
4418
+ throw new ContractError("forbidden");
4419
+ if (mutation && h.get("x-cadence-csrf") !== this.csrf)
4420
+ throw new ContractError("forbidden");
4421
+ }
4422
+ return {
4423
+ attachmentId: decodeId(attachmentId),
4424
+ authority: local === undefined ? "browser" : "local",
4425
+ accessGeneration: this.material.generation
4426
+ };
4427
+ }
4428
+ current(caller) {
4429
+ this.material.assertCurrent();
4430
+ if (!this.material.available || caller.accessGeneration !== this.material.generation)
4431
+ throw new ContractError("unauthenticated");
4432
+ }
4433
+ }
4434
+
4435
+ // src/service/contracts/gateway.ts
4436
+ function decodeProjectEntry(value) {
4437
+ const o = closed(value, ["id", "path", "cwd", "epoch"]);
4438
+ return {
4439
+ id: decodeHash(o.id),
4440
+ path: text(o.path, 4096, true),
4441
+ cwd: text(o.cwd, 4096, true),
4442
+ epoch: decodeId(o.epoch)
4443
+ };
4444
+ }
4445
+
4446
+ // src/service/gateway-store.ts
4447
+ function decodeGatewayInput(value) {
4448
+ const o = closed(value, [
4449
+ "kind",
4450
+ "serverConfig",
4451
+ "stateHome",
4452
+ "agentDir",
4453
+ "policy"
4454
+ ]);
4455
+ const absolute = (v) => {
4456
+ const p = text(v, 4096, true);
4457
+ if (resolve4(p) !== p)
4458
+ throw new ContractError("invalid");
4459
+ return p;
4460
+ };
4461
+ return {
4462
+ kind: literal(o.kind, "workbench"),
4463
+ serverConfig: absolute(o.serverConfig),
4464
+ stateHome: absolute(o.stateHome),
4465
+ agentDir: absolute(o.agentDir),
4466
+ policy: decodeHttpPolicy(o.policy)
4467
+ };
4468
+ }
4469
+ function decodeGatewayProject(value) {
4470
+ const project = decodeProjectEntry(value);
4471
+ if (resolve4(project.path) !== project.path || resolve4(project.cwd) !== project.cwd)
4472
+ throw new ContractError("corrupt-selected");
4473
+ return project;
4474
+ }
4475
+ function registration(value) {
4476
+ const o = closed(value, [
4477
+ "schema",
4478
+ "epoch",
4479
+ "id",
4480
+ "endpoint",
4481
+ "capability",
4482
+ "selection",
4483
+ "pid",
4484
+ "phase"
4485
+ ]);
4486
+ const endpoint2 = text(o.endpoint, 128, true), capability = text(o.capability, 43, true);
4487
+ if (!/^http:\/\/(?:127\.0\.0\.1|\[::1\]):[1-9][0-9]{0,4}$/.test(endpoint2) || !/^[A-Za-z0-9_-]{43}$/.test(capability))
4488
+ throw new ContractError("corrupt-selected");
4489
+ return {
4490
+ schema: literal(o.schema, "cadence-workbench-registration/1"),
4491
+ epoch: decodeId(o.epoch),
4492
+ id: decodeId(o.id),
4493
+ endpoint: endpoint2,
4494
+ capability,
4495
+ selection: decodeHash(o.selection),
4496
+ pid: integer(o.pid, 1),
4497
+ phase: oneOf(o.phase, ["alive", "stopping", "stopped", "quarantined"])
4498
+ };
4499
+ }
4500
+
4501
+ class GatewayStore {
4502
+ serverConfig;
4503
+ files;
4504
+ directory;
4505
+ stateHome;
4506
+ lock;
4507
+ selected;
4508
+ inputFile;
4509
+ value;
4510
+ constructor(serverConfig, stateHome = defaultStateHome()) {
4511
+ this.serverConfig = serverConfig;
4512
+ this.stateHome = canonicalAmbientRoot(stateHome);
4513
+ this.files = new PrivateFiles(join7(this.stateHome, "cadence-assistant-v1"));
4514
+ this.directory = join7(this.files.namespace, "workbenches-v1", hash(resolve4(serverConfig)));
4515
+ }
4516
+ path(name) {
4517
+ return join7(this.directory, name);
4518
+ }
4519
+ read() {
4520
+ const file = this.files.read(this.path("gateway.json"));
4521
+ if (!file) {
4522
+ if (this.files.directory(this.directory))
4523
+ throw new ContractError("corrupt-selected");
4524
+ return;
4525
+ }
4526
+ const value = registration(json(file));
4527
+ const selected = this.files.read(this.path("input.json"));
4528
+ if (!selected || hash(selected.bytes) !== value.selection)
4529
+ throw new ContractError("corrupt-selected");
4530
+ decodeGatewayInput(json(selected));
4531
+ return value;
4532
+ }
4533
+ localAccess() {
4534
+ const value = this.read();
4535
+ if (!value || value.phase === "stopped")
4536
+ throw new ContractError("unavailable");
4537
+ return {
4538
+ endpoint: value.endpoint,
4539
+ epoch: value.epoch,
4540
+ capability: value.capability
4541
+ };
4542
+ }
4543
+ inspect() {
4544
+ const value = this.read();
4545
+ if (!value)
4546
+ return { status: "absent" };
4547
+ return {
4548
+ status: value.phase === "stopped" && this.files.read(this.path("gateway.lock")) ? "stopping" : value.phase,
4549
+ epoch: value.epoch
4550
+ };
4551
+ }
4552
+ acquire(endpoint2, token, input) {
4553
+ const previous = this.read();
4554
+ if (previous && previous.phase !== "stopped")
4555
+ throw new ContractError("quarantine");
4556
+ this.lock = this.files.create(this.path("gateway.lock"), encoded({ epoch: randomUUID5() }));
4557
+ const source = { serverConfig: this.serverConfig, token };
4558
+ if (!previous)
4559
+ AccessMaterial.initialize(this.files, this.directory, source);
4560
+ const access = new AccessMaterial(this.files, this.directory, source, () => this.assert());
4561
+ this.selected = this.files.read(this.path("gateway.json"));
4562
+ if (previous && (!this.selected || encoded(registration(json(this.selected))) !== encoded(previous)))
4563
+ throw new ContractError("conflict");
4564
+ const beforeInput = this.files.read(this.path("input.json"));
4565
+ const inputBytes = encoded(decodeGatewayInput(input));
4566
+ this.inputFile = beforeInput ? this.files.replace(this.path("input.json"), beforeInput, inputBytes) : this.files.create(this.path("input.json"), inputBytes);
4567
+ this.value = {
4568
+ schema: "cadence-workbench-registration/1",
4569
+ epoch: randomUUID5(),
4570
+ id: previous?.id ?? randomUUID5(),
4571
+ endpoint: endpoint2,
4572
+ capability: randomBytes4(32).toString("base64url"),
4573
+ selection: hash(inputBytes),
4574
+ pid: process.pid,
4575
+ phase: "alive"
4576
+ };
4577
+ this.selected = this.selected ? this.files.replace(this.path("gateway.json"), this.selected, encoded(this.value)) : this.files.create(this.path("gateway.json"), encoded(this.value));
4578
+ return { registration: this.value, access };
4579
+ }
4580
+ assert() {
4581
+ if (!this.lock || !this.selected)
4582
+ throw new ContractError("unavailable");
4583
+ this.files.assert(this.path("gateway.lock"), this.lock);
4584
+ this.files.assert(this.path("input.json"), this.inputFile);
4585
+ this.files.assert(this.path("gateway.json"), this.selected);
4586
+ }
4587
+ phase(phase) {
4588
+ this.assert();
4589
+ this.value = { ...this.value, phase };
4590
+ this.selected = this.files.replace(this.path("gateway.json"), this.selected, encoded(this.value));
4591
+ }
4592
+ release() {
4593
+ this.phase("stopped");
4594
+ this.files.remove(this.path("gateway.lock"), this.lock);
4595
+ }
4596
+ projects() {
4597
+ const directory2 = this.path("projects");
4598
+ if (!this.files.directory(directory2))
4599
+ return [];
4600
+ const stream = opendirSync2(directory2), out = [];
4601
+ try {
4602
+ for (let entry = stream.readSync();entry; entry = stream.readSync()) {
4603
+ if (!entry.isFile() || !/^[a-f0-9]{64}\.json$/.test(entry.name) || out.length >= 128)
4604
+ throw new ContractError("corrupt-selected");
4605
+ const file = this.files.read(join7(directory2, entry.name));
4606
+ if (!file)
4607
+ throw new ContractError("corrupt-selected");
4608
+ const project = decodeGatewayProject(json(file));
4609
+ if (entry.name !== `${project.id}.json` || hash(project.path) !== project.id)
4610
+ throw new ContractError("corrupt-selected");
4611
+ out.push(project);
4612
+ }
4613
+ } finally {
4614
+ stream.closeSync();
4615
+ }
4616
+ return out;
4617
+ }
4618
+ saveProject(project) {
4619
+ this.assert();
4620
+ const p = decodeGatewayProject(project), path2 = this.path(`projects/${p.id}.json`), before = this.files.read(path2);
4621
+ if (!before && this.projects().length >= 128)
4622
+ throw new ContractError("capacity");
4623
+ if (before) {
4624
+ const old = decodeGatewayProject(json(before));
4625
+ if (old.id !== p.id || old.path !== p.path)
4626
+ throw new ContractError("conflict");
4627
+ this.files.replace(path2, before, encoded(p));
4628
+ } else
4629
+ this.files.create(path2, encoded(p));
4630
+ }
4631
+ }
4632
+
4633
+ // src/service/runtime.ts
4634
+ import { homedir as homedir4 } from "node:os";
4635
+ import { dirname as dirname7, isAbsolute as isAbsolute6, join as join8, resolve as resolve5 } from "node:path";
4636
+ var STARTUP_INPUT_BYTES = 16 * 1024;
4637
+ function absolute(value) {
4638
+ const result = text(value, 4096, true);
4639
+ if (!isAbsolute6(result))
4640
+ throw new ContractError("invalid");
4641
+ return result;
4642
+ }
4643
+ function decodeHostConfig(value) {
4644
+ const o = closed(value, [
4645
+ "schema",
4646
+ "http",
4647
+ "cwd",
4648
+ "agentDir",
4649
+ "tokenSource",
4650
+ "tokenFile"
4651
+ ]);
4652
+ const tokenSource = oneOf(o.tokenSource, [
4653
+ "generated",
4654
+ "environment",
4655
+ "file",
4656
+ "config"
4657
+ ]);
4658
+ const tokenFile = o.tokenFile === null ? null : absolute(o.tokenFile);
4659
+ if ((tokenSource === "file" || tokenSource === "config") !== !!tokenFile)
4660
+ throw new ContractError("invalid");
4661
+ return {
4662
+ schema: literal(o.schema, "cadence-service-host/1"),
4663
+ http: decodeHttpPolicy(o.http),
4664
+ cwd: absolute(o.cwd),
4665
+ agentDir: absolute(o.agentDir),
4666
+ tokenSource,
4667
+ tokenFile
4668
+ };
4669
+ }
4670
+ function readHostConfig(c) {
4671
+ const owner = c.configuration();
4672
+ if (!owner)
4673
+ return;
4674
+ const snapshot = c.files.read(c.path("host-config.json"));
4675
+ if (!snapshot)
4676
+ throw new ContractError("corrupt-selected");
4677
+ const config = decodeHostConfig(json(snapshot));
4678
+ if (!config.http.port || owner.endpoint !== localOrigin(config.http, config.http.port) || owner.policyIdentity !== hash(encoded(config)))
4679
+ throw new ContractError("conflict");
4680
+ return config;
4681
+ }
4682
+ function hostEnvironment(ambient) {
4683
+ let route;
4684
+ const facts = networkSnapshot(ambient);
4685
+ try {
4686
+ if (facts.NODE_OPTIONS.present)
4687
+ throw new Error;
4688
+ route = sdkNetworkEnvironment(facts, networkSnapshot(undefined));
4689
+ } catch {
4690
+ throw new ContractError("unsupported-context");
4691
+ }
4692
+ const result = Object.create(null);
4693
+ for (const key of Object.keys(ambient)) {
4694
+ const d = Object.getOwnPropertyDescriptor(ambient, key);
4695
+ if (!d || !("value" in d) || typeof d.value !== "string")
4696
+ throw new ContractError("invalid");
4697
+ if (/^(?:CADENCE_|PI_|ABEL_|TRELLIS_|GIT_)/i.test(key) || networkKeys.some((name) => name === key))
4698
+ continue;
4699
+ result[key] = d.value;
4700
+ }
4701
+ if (route.redactionLiterals.length) {
4702
+ result.HTTP_PROXY = route.env.HTTP_PROXY;
4703
+ result.HTTPS_PROXY = route.env.HTTPS_PROXY;
4704
+ result.NODE_USE_ENV_PROXY = "1";
4705
+ }
4706
+ result.PI_OFFLINE = "1";
4707
+ result.NO_COLOR = "1";
4708
+ if (!sameNetwork(facts, networkSnapshot(ambient)))
4709
+ throw new ContractError("stale");
4710
+ return result;
4711
+ }
4712
+ function selectStart(options, ambient) {
4713
+ const workspace = resolveWorkspace(options.workspace), c = new ServiceCoordination(workspace.root, options.stateHome);
4714
+ const old = readHostConfig(c);
4715
+ const env = Object.getOwnPropertyDescriptor(ambient, "CADENCE_WEB_TOKEN");
4716
+ if (env && (!("value" in env) || typeof env.value !== "string"))
4717
+ throw new ContractError("invalid");
4718
+ let fromFile;
4719
+ if (options.config) {
4720
+ const path2 = resolve5(options.config), f = new PrivateFiles(dirname7(path2));
4721
+ const bytes = f.read(path2);
4722
+ if (!bytes)
4723
+ throw new ContractError("corrupt-selected");
4724
+ fromFile = decodeHostConfig(json(bytes));
4725
+ }
4726
+ const base = old ?? fromFile ?? decodeHostConfig({
4727
+ schema: "cadence-service-host/1",
4728
+ cwd: workspace.cwd,
4729
+ http: {
4730
+ schema: "cadence-service-http/1",
4731
+ host: options.host ?? "127.0.0.1",
4732
+ port: options.port ?? 17423,
4733
+ proxy: null
4734
+ },
4735
+ agentDir: resolve5(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join8(ambient.HOME ?? ambient.USERPROFILE ?? homedir4(), ".pi", "agent")),
4736
+ tokenSource: options.tokenFile ? "file" : env ? "environment" : "generated",
4737
+ tokenFile: options.tokenFile ? resolve5(options.tokenFile) : null
4738
+ });
4739
+ const server = options.serverConfig !== undefined ? resolve5(options.serverConfig) : old?.tokenSource === "config" ? old.tokenFile : null;
4740
+ let selected = base;
4741
+ if (server) {
4742
+ if (options.tokenFile !== undefined)
4743
+ throw new ContractError("conflict");
4744
+ const { port } = readServerConfig(server).config;
4745
+ selected = decodeHostConfig({
4746
+ ...base,
4747
+ http: { ...base.http, port },
4748
+ tokenSource: "config",
4749
+ tokenFile: server
4750
+ });
4751
+ }
4752
+ if (fromFile && encoded(fromFile) !== encoded(selected) || options.port !== undefined && options.port !== selected.http.port || options.host !== undefined && options.host !== selected.http.host || options.agentDir !== undefined && resolve5(options.agentDir) !== selected.agentDir || options.tokenFile !== undefined && resolve5(options.tokenFile) !== selected.tokenFile || env && selected.tokenSource !== "environment" || selected.tokenSource === "environment" && !env || resolveWorkspace(selected.cwd).root !== workspace.root)
4753
+ throw new ContractError("conflict");
4754
+ return {
4755
+ workspace: workspace.root,
4756
+ stateHome: c.stateHome,
4757
+ config: selected,
4758
+ environmentToken: env?.value ?? null
4759
+ };
4760
+ }
4761
+ function decodeHostInput(value) {
4762
+ const o = closed(value, [
4763
+ "workspace",
4764
+ "stateHome",
4765
+ "config",
4766
+ "environmentToken"
4767
+ ]);
4768
+ const config = decodeHostConfig(o.config);
4769
+ const environmentToken = o.environmentToken === null ? null : text(o.environmentToken, 1024, true);
4770
+ if (config.tokenSource === "environment" !== (environmentToken !== null))
4771
+ throw new ContractError("invalid");
4772
+ return {
4773
+ workspace: absolute(o.workspace),
4774
+ stateHome: absolute(o.stateHome),
4775
+ config,
4776
+ environmentToken
4777
+ };
4778
+ }
4779
+ function portValue(value) {
4780
+ if (!/^(0|[1-9][0-9]{0,4})$/.test(value))
4781
+ throw new ContractError("invalid");
4782
+ return integer(Number(value), 0, 65535);
4783
+ }
4784
+ var STARTUP_STAGES = [
4785
+ "package",
4786
+ "pi-agent-directory",
4787
+ "pi-auth",
4788
+ "pi-models",
4789
+ "pi-runtime",
4790
+ "service"
4791
+ ];
4792
+ function decodeStartupReport(value) {
4793
+ const o = closed(value, ["status", "error", "stage"]);
4794
+ return {
4795
+ status: oneOf(o.status, ["refused", "quarantined"]),
4796
+ error: oneOf(o.error, ERROR_CODES),
4797
+ stage: oneOf(o.stage, STARTUP_STAGES)
4798
+ };
4799
+ }
4800
+
4801
+ // src/service/gateway-cli.ts
4802
+ async function gatewayMain(argv, hostUrl) {
4803
+ const operation = argv.shift();
4804
+ if (![
4805
+ "start",
4806
+ "status",
4807
+ "diagnose",
4808
+ "stop",
4809
+ "token-link",
4810
+ "token-rotate"
4811
+ ].includes(operation ?? ""))
4812
+ throw new ContractError("invalid");
4813
+ let config = serverConfigPath(homedir5()), stateHome = defaultStateHome(), agentDir, policyFile;
4814
+ let foreground = false, reveal = false;
4815
+ const seen = new Set;
4816
+ while (argv.length) {
4817
+ const key = argv.shift();
4818
+ if (seen.has(key))
4819
+ throw new ContractError("invalid");
4820
+ seen.add(key);
4821
+ if (key === "--foreground" && operation === "start")
4822
+ foreground = true;
4823
+ else if (key === "--reveal" && operation === "token-link")
4824
+ reveal = true;
4825
+ else if (argv[0] && ["--server-config", "--state-home", "--agent-dir", "--config"].includes(key)) {
4826
+ const value = resolve6(argv.shift());
4827
+ if (key === "--server-config")
4828
+ config = value;
4829
+ if (key === "--state-home")
4830
+ stateHome = value;
4831
+ if (key === "--agent-dir")
4832
+ agentDir = value;
4833
+ if (key === "--config")
4834
+ policyFile = value;
4835
+ } else
4836
+ throw new ContractError("invalid");
4837
+ }
4838
+ const store = new GatewayStore(config, stateHome);
4839
+ let result;
4840
+ if (operation === "start") {
4841
+ const network = networkSnapshot(process.env), env = hostEnvironment(process.env);
4842
+ if (!seen.has("--server-config"))
4843
+ ensureServerConfig(homedir5());
4844
+ const server = readServerConfig(config).config;
4845
+ const selectedFile = store.files.read(store.path("input.json"));
4846
+ const selected = selectedFile ? decodeGatewayInput(json(selectedFile)) : undefined;
4847
+ let policy = selected?.policy ?? decodeHttpPolicy({
4848
+ schema: "cadence-service-http/1",
4849
+ host: "127.0.0.1",
4850
+ port: server.port,
4851
+ proxy: null
4852
+ });
4853
+ if (policyFile) {
4854
+ const file = new PrivateFiles(dirname8(policyFile)).read(policyFile);
4855
+ if (!file)
4856
+ throw new ContractError("invalid");
4857
+ policy = decodeHttpPolicy(json(file));
4858
+ }
4859
+ const input = decodeGatewayInput({
4860
+ kind: "workbench",
4861
+ serverConfig: config,
4862
+ stateHome: store.stateHome,
4863
+ agentDir: agentDir ?? selected?.agentDir ?? resolve6(process.env.PI_CODING_AGENT_DIR ?? join9(homedir5(), ".pi", "agent")),
4864
+ policy: { ...policy, port: server.port }
4865
+ });
4866
+ const status = await serviceStatus(store);
4867
+ if (status && typeof status === "object" && "status" in status && status.status === "alive") {
4868
+ if (!selected || encoded(selected) !== encoded(input))
4869
+ throw new ContractError("conflict");
4870
+ result = status;
4871
+ } else
4872
+ result = await launchHost(input, store, foreground, hostUrl, dirname8(dirname8(fileURLToPath(hostUrl))), env, network);
4873
+ if (!foreground && result && typeof result === "object" && "status" in result && result.status === "alive") {
4874
+ const reply = closed(await nativeCall(store, "token-link", true), [
4875
+ "link"
4876
+ ]);
4877
+ process.stderr.write(`Cadence workbench: ${text(reply.link, 4096, true)}
4878
+ No project or default workspace was created.
4879
+ `);
4880
+ }
4881
+ } else if (operation === "stop")
4882
+ result = await stopService(store);
4883
+ else if (operation === "status" || operation === "diagnose")
4884
+ result = await serviceStatus(store, operation === "diagnose");
4885
+ else {
4886
+ if (operation === "token-link" && !reveal)
4887
+ throw new ContractError("forbidden");
4888
+ result = await nativeCall(store, operation, reveal);
4889
+ }
4890
+ if (result !== null)
4891
+ process.stdout.write(`${JSON.stringify(result)}
4892
+ `);
4893
+ if (result && typeof result === "object" && "status" in result && ["unconfirmed", "unavailable", "quarantined", "refused"].includes(String(result.status)))
4894
+ process.exitCode = 1;
4895
+ }
4896
+
4897
+ // src/service/launchd.ts
4898
+ import { rmSync as rmSync2 } from "node:fs";
4899
+ import { dirname as dirname9, join as join10 } from "node:path";
4900
+ var LABEL = "com.abelxiaoxing.cadence";
4901
+ var MARKER2 = `<?xml version="1.0" encoding="UTF-8"?>
4902
+ <!-- Generated by @abelxiaoxing/cadence`;
4903
+ function plistPath(s) {
4904
+ return join10(s.home, "Library", "LaunchAgents", `${LABEL}.plist`);
4905
+ }
4906
+ var xml = (value) => checked(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
4907
+ function buildPlist(u) {
4908
+ const string = (value) => ` <string>${xml(value)}</string>`;
4909
+ return [
4910
+ '<?xml version="1.0" encoding="UTF-8"?>',
4911
+ "<!-- Generated by @abelxiaoxing/cadence; `cadence server install` and `restart` rewrite this file. -->",
4912
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
4913
+ '<plist version="1.0">',
4914
+ "<dict>",
4915
+ "\t<key>Label</key>",
4916
+ ` <string>${LABEL}</string>`,
4917
+ "\t<key>ProgramArguments</key>",
4918
+ "\t<array>",
4919
+ ...[
4920
+ u.node,
4921
+ u.cli,
4922
+ ...u.workspace === null ? ["workbench", "start", "--foreground"] : ["start", "--foreground", "--workspace", u.workspace],
4923
+ "--server-config",
4924
+ u.serverConfig
4925
+ ].map(string),
4926
+ "\t</array>",
4927
+ ...u.workspace === null ? ["<!-- Cadence workbench gateway; no project workspace -->"] : [],
4928
+ "\t<key>WorkingDirectory</key>",
4929
+ ` <string>${xml(u.workspace ?? dirname9(dirname9(u.cli)))}</string>`,
4930
+ "\t<key>EnvironmentVariables</key>",
4931
+ "\t<dict>",
4932
+ ...u.environment.flatMap(([k, v]) => [
4933
+ ` <key>${xml(k)}</key>`,
4934
+ string(v)
4935
+ ]),
4936
+ "\t</dict>",
4937
+ "\t<key>RunAtLoad</key>",
4938
+ "\t<true/>",
4939
+ "\t<key>ProcessType</key>",
4940
+ "\t<string>Background</string>",
4941
+ "\t<key>StandardOutPath</key>",
4942
+ ` <string>${xml(u.log)}</string>`,
4943
+ "\t<key>StandardErrorPath</key>",
4944
+ ` <string>${xml(u.log)}</string>`,
4945
+ "</dict>",
4946
+ "</plist>",
4947
+ ""
4948
+ ].join(`
4949
+ `);
4950
+ }
4951
+ function plistWorkspace(text2) {
4952
+ if (text2?.includes("<!-- Cadence workbench gateway; no project workspace -->"))
4953
+ return null;
4954
+ const m = text2?.match(/^\t<key>WorkingDirectory<\/key>\n\t<string>([^<]*)<\/string>$/m);
4955
+ return m?.[1].replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
4956
+ }
4957
+ var domain = (s) => `gui/${s.uid}`;
4958
+ var target = (s) => `${domain(s)}/${LABEL}`;
4959
+ var loaded = (s) => s.launchctl(["print", target(s)]).status === 0;
4960
+ function disabled(s) {
4961
+ return new RegExp(`"${LABEL.replaceAll(".", "\\.")}" => (true|disabled)`).test(s.launchctl(["print-disabled", domain(s)]).stdout);
4962
+ }
4963
+ async function configureLaunchAgent(s, mode, requested) {
4964
+ if (s.uid === undefined)
4965
+ return { status: "unavailable" };
4966
+ const path2 = plistPath(s);
4967
+ const previous = readGenerated(path2, MARKER2);
4968
+ if (previous === null)
4969
+ return { status: "foreign", unit: path2 };
4970
+ if (mode === "restart" && previous === undefined)
4971
+ return { status: "absent" };
4972
+ const sdk = sdkPackages(s.root);
4973
+ const kept = plistWorkspace(previous);
4974
+ const { workspace, redirectedFrom } = selectWorkspace(s, requested, mode === "install" && requested === undefined ? null : kept);
4975
+ if (workspace !== null && !sdk)
4976
+ return { status: "missing-pi-sdk" };
4977
+ const refused2 = await settle(s, kept);
4978
+ if (refused2)
4979
+ return { ...refused2, unit: path2 };
4980
+ if (loaded(s))
4981
+ s.launchctl(["bootout", target(s)]);
4982
+ write(path2, buildPlist({
4983
+ node: s.node,
4984
+ cli: join10(s.root, "src", "service-cli.mjs"),
4985
+ workspace,
4986
+ serverConfig: serverConfigPath(s.home),
4987
+ environment: serviceEnvironment(s),
4988
+ log: join10(s.home, "Library", "Logs", "cadence.log")
4989
+ }));
4990
+ const result = {
4991
+ unit: path2,
4992
+ workspace,
4993
+ ...redirectedFrom ? { redirectedFrom } : {}
4994
+ };
4995
+ if (mode === "install")
4996
+ s.launchctl(["enable", target(s)]);
4997
+ else if (disabled(s))
4998
+ return { status: "disabled", ...result };
4999
+ if (workspace !== null && !regularFile(join10(agentDirectory(s), "auth.json")))
5000
+ return { status: "waiting-for-pi-login", ...result };
5001
+ const started = s.launchctl(["bootstrap", domain(s), path2]).status === 0;
5002
+ return { status: started ? "started" : "failed", ...result };
5003
+ }
5004
+ async function stopLaunchAgent(s) {
5005
+ const path2 = plistPath(s);
5006
+ const previous = readGenerated(path2, MARKER2);
5007
+ if (previous === null)
5008
+ return { status: "foreign", unit: path2 };
5009
+ if (previous === undefined)
5010
+ return { status: "absent" };
5011
+ const workspace = plistWorkspace(previous);
5012
+ const refused2 = await settle(s, workspace);
5013
+ return { ...refused2 ?? { status: "stopped", workspace }, unit: path2 };
5014
+ }
5015
+ async function removeLaunchAgent(s) {
5016
+ const path2 = plistPath(s);
5017
+ const previous = readGenerated(path2, MARKER2);
5018
+ if (previous === null)
5019
+ return { status: "foreign", unit: path2 };
5020
+ if (previous === undefined)
5021
+ return { status: "absent" };
5022
+ const refused2 = await settle(s, plistWorkspace(previous));
5023
+ if (refused2)
5024
+ return { ...refused2, unit: path2 };
5025
+ if (s.uid !== undefined && loaded(s))
5026
+ s.launchctl(["bootout", target(s)]);
5027
+ rmSync2(path2);
5028
+ return { status: "removed", unit: path2 };
5029
+ }
5030
+ function launchAgentStatus(s) {
5031
+ const path2 = plistPath(s);
5032
+ const previous = readGenerated(path2, MARKER2);
5033
+ if (previous === null)
5034
+ return { status: "foreign", unit: path2 };
5035
+ if (previous === undefined)
5036
+ return { status: "absent" };
5037
+ const printed = s.uid === undefined ? undefined : s.launchctl(["print", target(s)]);
5038
+ return {
5039
+ status: printed?.status !== 0 ? "unloaded" : /^\s*state = running$/m.test(printed.stdout) ? "active" : "inactive",
5040
+ enabled: s.uid !== undefined && disabled(s) ? "disabled" : "enabled",
5041
+ unit: path2,
5042
+ workspace: plistWorkspace(previous)
5043
+ };
5044
+ }
5045
+
5046
+ // src/service/login-run.ts
5047
+ import { rmSync as rmSync3 } from "node:fs";
5048
+ import { dirname as dirname10, isAbsolute as isAbsolute7, join as join11 } from "node:path";
5049
+ var RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
5050
+ var APPROVED_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run";
5051
+ var RUN_VALUE = "Cadence";
5052
+ var MARKER3 = "' Generated by @abelxiaoxing/cadence";
5053
+ function launcherPath(s) {
5054
+ const local = s.env.LOCALAPPDATA;
5055
+ return join11(local && isAbsolute7(local) ? local : join11(s.home, "AppData", "Local"), "cadence-server", "cadence.vbs");
5056
+ }
5057
+ var literal2 = (value) => `"${checked(value).replaceAll('"', '""')}"`;
5058
+ function argument2(value) {
5059
+ if (checked(value).includes('"'))
5060
+ throw new ContractError("invalid");
5061
+ return `"${value}"`;
5062
+ }
5063
+ function buildLauncher(u) {
5064
+ const command = `${argument2(u.node)} ${argument2(u.cli)} ${u.workspace === null ? "workbench start" : `start --workspace ${argument2(u.workspace)}`} --server-config ${argument2(u.serverConfig)}`;
5065
+ return [
5066
+ `${MARKER3}; \`cadence server install\` and \`restart\` rewrite this file.`,
5067
+ `' Started at login by ${RUN_KEY}\\${RUN_VALUE}; window style 0 hides the console.`,
5068
+ 'Set shell = CreateObject("WScript.Shell")',
5069
+ ...u.environment.length ? ['Set env = shell.Environment("Process")'] : [],
5070
+ ...u.environment.map(([k, v]) => `env(${literal2(k)}) = ${literal2(v)}`),
5071
+ ...u.workspace === null ? ["' Cadence workbench gateway; no project workspace"] : [],
5072
+ `shell.CurrentDirectory = ${literal2(u.workspace ?? dirname10(dirname10(u.cli)))}`,
5073
+ `shell.Run ${literal2(command)}, 0, False`,
5074
+ ""
5075
+ ].join(`\r
5076
+ `);
5077
+ }
5078
+ var utf16 = (text2) => Buffer.concat([Buffer.from([255, 254]), Buffer.from(text2, "utf16le")]);
5079
+ function launcherWorkspace(text2) {
5080
+ if (text2?.includes("' Cadence workbench gateway; no project workspace"))
5081
+ return null;
5082
+ return text2?.match(/^shell\.CurrentDirectory = "((?:[^"]|"")*)"\r?$/m)?.[1].replaceAll('""', '"');
5083
+ }
5084
+ var runEntry = (path2, s) => `"${wscript(s.env)}" "${path2}"`;
5085
+ var present = (s, key) => s.reg(["query", key, "/v", RUN_VALUE]).status === 0;
5086
+ function approvedOff(s) {
5087
+ const r = s.reg(["query", APPROVED_KEY, "/v", RUN_VALUE]);
5088
+ const first = r.status === 0 && /REG_BINARY\s+([0-9A-F]{2})/i.exec(r.stdout);
5089
+ return !!first && (Number.parseInt(first[1], 16) & 1) === 1;
5090
+ }
5091
+ async function configureLoginRun(s, mode, requested) {
5092
+ const path2 = launcherPath(s);
5093
+ const previous = readGenerated(path2, MARKER3);
5094
+ if (previous === null)
5095
+ return { status: "foreign", unit: path2 };
5096
+ if (previous === undefined && present(s, RUN_KEY))
5097
+ return { status: "foreign", unit: `${RUN_KEY}\\${RUN_VALUE}` };
5098
+ if (mode === "restart" && previous === undefined)
5099
+ return { status: "absent" };
5100
+ const sdk = sdkPackages(s.root);
5101
+ const kept = launcherWorkspace(previous);
5102
+ const { workspace, redirectedFrom } = selectWorkspace(s, requested, mode === "install" && requested === undefined ? null : kept);
5103
+ if (workspace !== null && !sdk)
5104
+ return { status: "missing-pi-sdk" };
5105
+ const refused2 = await settle(s, kept);
5106
+ if (refused2)
5107
+ return { ...refused2, unit: path2 };
5108
+ const environment = s.env.PI_CODING_AGENT_DIR ? [["PI_CODING_AGENT_DIR", agentDirectory(s)]] : [];
4249
5109
  write(path2, utf16(buildLauncher({
4250
5110
  node: s.node,
4251
- cli: join7(s.root, "src", "service-cli.mjs"),
5111
+ cli: join11(s.root, "src", "service-cli.mjs"),
4252
5112
  workspace,
4253
5113
  serverConfig: serverConfigPath(s.home),
4254
5114
  environment
@@ -4264,7 +5124,7 @@ async function configureLoginRun(s, mode, requested) {
4264
5124
  return { status: "failed", ...result };
4265
5125
  s.reg(["delete", APPROVED_KEY, "/v", RUN_VALUE, "/f"]);
4266
5126
  }
4267
- if (!regularFile(join7(agentDirectory(s), "auth.json")))
5127
+ if (workspace !== null && !regularFile(join11(agentDirectory(s), "auth.json")))
4268
5128
  return { status: "waiting-for-pi-login", ...result };
4269
5129
  return { status: s.launch(path2) ? "started" : "failed", ...result };
4270
5130
  }
@@ -4303,7 +5163,7 @@ async function loginRunStatus(s) {
4303
5163
  if (previous === undefined)
4304
5164
  return { status: "absent" };
4305
5165
  const workspace = launcherWorkspace(previous);
4306
- const owner = workspace ? await s.ownerStatus(workspace) : "unavailable";
5166
+ const owner = workspace !== undefined ? await s.ownerStatus(workspace, serverConfigPath(s.home)) : "unavailable";
4307
5167
  return {
4308
5168
  status: owner === "alive" ? "active" : "inactive",
4309
5169
  enabled: present(s, RUN_KEY) && !approvedOff(s) ? "enabled" : "disabled",
@@ -4316,9 +5176,9 @@ async function loginRunStatus(s) {
4316
5176
  import { isatty } from "node:tty";
4317
5177
 
4318
5178
  // src/history/recovery.ts
4319
- import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
4320
- import { lstatSync as lstatSync7, opendirSync as opendirSync3 } from "node:fs";
4321
- import { join as join9, relative as relative4 } from "node:path";
5179
+ import { createHash as createHash6, randomUUID as randomUUID7 } from "node:crypto";
5180
+ import { lstatSync as lstatSync7, opendirSync as opendirSync4 } from "node:fs";
5181
+ import { join as join13, relative as relative4 } from "node:path";
4322
5182
 
4323
5183
  // src/service/recovery.ts
4324
5184
  async function inspectRecovery(coordination, history) {
@@ -4366,19 +5226,19 @@ async function resolveRecovery(coordination, confirmation, history) {
4366
5226
  }
4367
5227
 
4368
5228
  // src/history/files.ts
4369
- import { createHash as createHash4, randomBytes as randomBytes3, randomUUID as randomUUID4 } from "node:crypto";
5229
+ import { createHash as createHash5, randomBytes as randomBytes5, randomUUID as randomUUID6 } from "node:crypto";
4370
5230
  import {
4371
5231
  closeSync as closeSync5,
4372
5232
  constants as constants5,
4373
5233
  fstatSync as fstatSync4,
4374
5234
  fsyncSync as fsyncSync3,
4375
5235
  lstatSync as lstatSync6,
4376
- opendirSync as opendirSync2,
5236
+ opendirSync as opendirSync3,
4377
5237
  openSync as openSync5,
4378
5238
  statfsSync
4379
5239
  } from "node:fs";
4380
5240
  import { open } from "node:fs/promises";
4381
- import { dirname as dirname7, join as join8 } from "node:path";
5241
+ import { dirname as dirname12, join as join12 } from "node:path";
4382
5242
 
4383
5243
  // src/history/admission.ts
4384
5244
  function decodeRunAdmission(value) {
@@ -4406,13 +5266,13 @@ import {
4406
5266
  openSync as openSync4,
4407
5267
  readSync as readSync3
4408
5268
  } from "node:fs";
4409
- import { dirname as dirname6 } from "node:path";
5269
+ import { dirname as dirname11 } from "node:path";
4410
5270
  function bad() {
4411
5271
  throw new ContractError("corrupt-selected");
4412
5272
  }
4413
5273
  var stamp2 = (s) => [s.dev, s.ino, s.size, s.mode, s.uid, s.nlink, s.ctimeMs, s.mtimeMs].join(":");
4414
5274
  function selected(files, path2, read, walks) {
4415
- if (!files.files.directory(dirname6(path2), false, walks))
5275
+ if (!files.files.directory(dirname11(path2), false, walks))
4416
5276
  bad();
4417
5277
  const before = lstatSync5(path2);
4418
5278
  if (!before.isFile() || before.nlink !== 1 || process.platform !== "win32" && (before.mode & 63 || process.getuid && before.uid !== process.getuid()))
@@ -4444,7 +5304,7 @@ function statSize(files, path2, maximum) {
4444
5304
  }
4445
5305
 
4446
5306
  // src/history/types.ts
4447
- import { createHash as createHash3 } from "node:crypto";
5307
+ import { createHash as createHash4 } from "node:crypto";
4448
5308
  var RECORD_BYTES = LIMITS.controlBytes + 64 * 1024;
4449
5309
  var LEGACY_FORMAT = "cadence-history-store/2";
4450
5310
  var FORMAT = "cadence-history-store/3";
@@ -4810,7 +5670,7 @@ function batchArtifacts(rows) {
4810
5670
  }
4811
5671
  var decodedLines = new Map;
4812
5672
  var decodedBytes = 0;
4813
- var lineKey = (text2) => createHash3("sha256").update(text2).digest("hex");
5673
+ var lineKey = (text2) => createHash4("sha256").update(text2).digest("hex");
4814
5674
  function remember(text2, value) {
4815
5675
  const key = lineKey(text2);
4816
5676
  if (decodedLines.has(key))
@@ -4909,14 +5769,14 @@ class HistoryFiles {
4909
5769
  this.coordination = coordination;
4910
5770
  this.format = format;
4911
5771
  this.formatSnapshot = formatSnapshot;
4912
- this.directory = join8(coordination.directory, "history-v2");
5772
+ this.directory = join12(coordination.directory, "history-v2");
4913
5773
  this.files = coordination.files;
4914
5774
  }
4915
5775
  static open(coordination) {
4916
- const dir = join8(coordination.directory, "history-v2");
5776
+ const dir = join12(coordination.directory, "history-v2");
4917
5777
  if (!coordination.files.directory(dir))
4918
5778
  return;
4919
- const file = coordination.files.read(join8(dir, "format.json"));
5779
+ const file = coordination.files.read(join12(dir, "format.json"));
4920
5780
  if (!file)
4921
5781
  corrupt();
4922
5782
  const format = decodeFormat(json(file));
@@ -4932,29 +5792,29 @@ class HistoryFiles {
4932
5792
  existing.assertWritable();
4933
5793
  return existing;
4934
5794
  }
4935
- const dir = join8(c.directory, "history-v2");
5795
+ const dir = join12(c.directory, "history-v2");
4936
5796
  const format = {
4937
5797
  schema: FORMAT,
4938
- storeId: randomUUID4(),
4939
- workspaceId: randomUUID4(),
5798
+ storeId: randomUUID6(),
5799
+ workspaceId: randomUUID6(),
4940
5800
  rootIdentity: hash(c.store.root)
4941
5801
  };
4942
5802
  c.files.directory(dir, true);
4943
5803
  for (const name of ["commands", "ledger", "runs", "indexes"]) {
4944
5804
  lease.assert();
4945
- c.files.directory(join8(dir, name), true);
5805
+ c.files.directory(join12(dir, name), true);
4946
5806
  }
4947
5807
  lease.assert();
4948
- const keyPath = join8(dir, "read-proof-key.json");
5808
+ const keyPath = join12(dir, "read-proof-key.json");
4949
5809
  c.files.create(keyPath, encoded({
4950
5810
  schema: "cadence-history-read-key/1",
4951
5811
  storeId: format.storeId,
4952
- key: randomBytes3(32).toString("base64url")
5812
+ key: randomBytes5(32).toString("base64url")
4953
5813
  }));
4954
5814
  lease.assert();
4955
- const snapshot = c.files.create(join8(dir, "format.json"), encoded(format));
5815
+ const snapshot = c.files.create(join12(dir, "format.json"), encoded(format));
4956
5816
  const result = new HistoryFiles(c, format, snapshot);
4957
- result.syncParents(join8(dir, "format.json"));
5817
+ result.syncParents(join12(dir, "format.json"));
4958
5818
  result.assertWritable();
4959
5819
  return result;
4960
5820
  }
@@ -4989,7 +5849,7 @@ class HistoryFiles {
4989
5849
  throw new ContractError("quarantine");
4990
5850
  }
4991
5851
  assertFormat() {
4992
- this.files.assert(join8(this.directory, "format.json"), this.formatSnapshot);
5852
+ this.files.assert(join12(this.directory, "format.json"), this.formatSnapshot);
4993
5853
  }
4994
5854
  readProofMaterial(budget) {
4995
5855
  if (this.format.schema !== FORMAT)
@@ -4998,7 +5858,7 @@ class HistoryFiles {
4998
5858
  this.assertReadFormat(budget);
4999
5859
  else
5000
5860
  this.assertFormat();
5001
- const selected2 = this.readBudgeted(join8(this.directory, "read-proof-key.json"), 512, budget);
5861
+ const selected2 = this.readBudgeted(join12(this.directory, "read-proof-key.json"), 512, budget);
5002
5862
  if (!selected2 || this.proofSnapshot && (selected2.identity !== this.proofSnapshot.identity || selected2.stamp !== this.proofSnapshot.stamp))
5003
5863
  corrupt();
5004
5864
  try {
@@ -5026,17 +5886,17 @@ class HistoryFiles {
5026
5886
  return;
5027
5887
  }
5028
5888
  this.assertFormat();
5029
- assertIncarnation(this, join8(this.directory, "read-proof-key.json"), this.proofSnapshot.stamp);
5889
+ assertIncarnation(this, join12(this.directory, "read-proof-key.json"), this.proofSnapshot.stamp);
5030
5890
  }
5031
5891
  segmentPath(segment) {
5032
- return join8(this.directory, "ledger", `${String(segment).padStart(12, "0")}.jsonl`);
5892
+ return join12(this.directory, "ledger", `${String(segment).padStart(12, "0")}.jsonl`);
5033
5893
  }
5034
5894
  artifactPath(ref) {
5035
- return join8(this.directory, "runs", ref.runId, "artifacts", ref.artifactId, `${ref.part}.part`);
5895
+ return join12(this.directory, "runs", ref.runId, "artifacts", ref.artifactId, `${ref.part}.part`);
5036
5896
  }
5037
5897
  admissionPath(runId) {
5038
5898
  decodeId(runId);
5039
- return join8(this.directory, "runs", runId, "admission.json");
5899
+ return join12(this.directory, "runs", runId, "admission.json");
5040
5900
  }
5041
5901
  writeAdmission(lease, input) {
5042
5902
  lease.assert();
@@ -5062,7 +5922,7 @@ class HistoryFiles {
5062
5922
  readCommand(commandId, budget) {
5063
5923
  decodeId(commandId);
5064
5924
  this.assertReadFormat(budget);
5065
- return this.readBudgeted(join8(this.directory, "commands", commandId, "intent.json"), RECORD_BYTES, budget);
5925
+ return this.readBudgeted(join12(this.directory, "commands", commandId, "intent.json"), RECORD_BYTES, budget);
5066
5926
  }
5067
5927
  readAdmission(runId, identity, budget) {
5068
5928
  return this.readAdmissionIdentity(runId, hash(canonicalJson(identity)), budget);
@@ -5083,7 +5943,7 @@ class HistoryFiles {
5083
5943
  syncParents(file) {
5084
5944
  if (process.platform === "win32")
5085
5945
  return;
5086
- for (let path2 = dirname7(file);; path2 = dirname7(path2)) {
5946
+ for (let path2 = dirname12(file);; path2 = dirname12(path2)) {
5087
5947
  if (!this.files.directory(path2))
5088
5948
  corrupt();
5089
5949
  const fd = openSync5(path2, constants5.O_RDONLY | constants5.O_DIRECTORY);
@@ -5094,13 +5954,13 @@ class HistoryFiles {
5094
5954
  }
5095
5955
  if (path2 === this.coordination.directory)
5096
5956
  break;
5097
- if (path2 === dirname7(path2))
5957
+ if (path2 === dirname12(path2))
5098
5958
  corrupt();
5099
5959
  }
5100
5960
  }
5101
5961
  assertReadFormat(budget) {
5102
5962
  budget.reserve(this.formatSnapshot.bytes.length);
5103
- this.files.assert(join8(this.directory, "format.json"), this.formatSnapshot, this.formatSnapshot.bytes.length);
5963
+ this.files.assert(join12(this.directory, "format.json"), this.formatSnapshot, this.formatSnapshot.bytes.length);
5104
5964
  }
5105
5965
  assertPublished(segment, end, seq) {
5106
5966
  const fence = publicationFences.get(this.directory);
@@ -5135,10 +5995,10 @@ class HistoryFiles {
5135
5995
  if (depth > 8 || !this.files.directory(path2))
5136
5996
  corrupt();
5137
5997
  let bytes = 0;
5138
- const dir = opendirSync2(path2);
5998
+ const dir = opendirSync3(path2);
5139
5999
  try {
5140
6000
  for (let e = dir.readSync();e; e = dir.readSync()) {
5141
- const p = join8(path2, e.name), st = lstatSync6(p);
6001
+ const p = join12(path2, e.name), st = lstatSync6(p);
5142
6002
  if (st.isDirectory())
5143
6003
  bytes += walk(p, depth + 1);
5144
6004
  else {
@@ -5173,10 +6033,10 @@ class HistoryFiles {
5173
6033
  state.bytes = 0;
5174
6034
  state.identity = null;
5175
6035
  let count = 0;
5176
- const ledger = join8(this.directory, "ledger");
6036
+ const ledger = join12(this.directory, "ledger");
5177
6037
  if (!this.files.directory(ledger))
5178
6038
  corrupt();
5179
- const dir = opendirSync2(ledger);
6039
+ const dir = opendirSync3(ledger);
5180
6040
  try {
5181
6041
  for (let e = dir.readSync();e; e = dir.readSync()) {
5182
6042
  budget?.reserve(0);
@@ -5210,7 +6070,7 @@ class HistoryFiles {
5210
6070
  state.bytes = file.bytes.length;
5211
6071
  state.identity = file.identity;
5212
6072
  state.records = 0;
5213
- let at = 0, pending = [], pendingBytes = 0, checksum = createHash4("sha256");
6073
+ let at = 0, pending = [], pendingBytes = 0, checksum = createHash5("sha256");
5214
6074
  while (at < file.bytes.length) {
5215
6075
  budget?.reserve(0);
5216
6076
  const end = file.bytes.indexOf(10, at);
@@ -5278,7 +6138,7 @@ class HistoryFiles {
5278
6138
  };
5279
6139
  pending = [];
5280
6140
  pendingBytes = 0;
5281
- checksum = createHash4("sha256");
6141
+ checksum = createHash5("sha256");
5282
6142
  }
5283
6143
  if (pending.length)
5284
6144
  state.tail = true;
@@ -5470,7 +6330,7 @@ class HistoryFiles {
5470
6330
  lease.assert();
5471
6331
  this.assertWritable();
5472
6332
  const after = this.files.read(path2, LIMITS.segmentBytes);
5473
- if (!after || after.identity !== createHash4("sha256").update(before.bytes).update(body).update(trailer).digest("hex"))
6333
+ if (!after || after.identity !== createHash5("sha256").update(before.bytes).update(body).update(trailer).digest("hex"))
5474
6334
  corrupt();
5475
6335
  return {
5476
6336
  committedSeq: marker.last,
@@ -5490,7 +6350,7 @@ class HistoryFiles {
5490
6350
  checkpoint(lease, state) {
5491
6351
  lease.assert();
5492
6352
  this.assertWritable();
5493
- const path2 = join8(this.directory, "checkpoint.json"), old = this.files.read(path2);
6353
+ const path2 = join12(this.directory, "checkpoint.json"), old = this.files.read(path2);
5494
6354
  const body = encoded({
5495
6355
  schema: "cadence-history-checkpoint/2",
5496
6356
  storeId: this.format.storeId,
@@ -5898,7 +6758,7 @@ function projectRunObservation(runId, epoch, o, child) {
5898
6758
  // src/history/recovery.ts
5899
6759
  function digestHistory(files, exclude) {
5900
6760
  files.assertFormat();
5901
- const digest = createHash5("sha256");
6761
+ const digest = createHash6("sha256");
5902
6762
  let count = 0;
5903
6763
  const walk = (path2, depth) => {
5904
6764
  if (depth > 8 || !files.files.directory(path2))
@@ -5906,7 +6766,7 @@ function digestHistory(files, exclude) {
5906
6766
  const directory2 = lstatSync7(path2);
5907
6767
  digest.update(`incarnation:${directory2.dev}:${directory2.ino}
5908
6768
  `);
5909
- const dir = opendirSync3(path2), names2 = [];
6769
+ const dir = opendirSync4(path2), names2 = [];
5910
6770
  try {
5911
6771
  for (let e = dir.readSync();e; e = dir.readSync()) {
5912
6772
  if (++count > LIMITS.dataFrames)
@@ -5917,7 +6777,7 @@ function digestHistory(files, exclude) {
5917
6777
  dir.closeSync();
5918
6778
  }
5919
6779
  for (const name of names2.sort()) {
5920
- const selected2 = join9(path2, name);
6780
+ const selected2 = join13(path2, name);
5921
6781
  if (selected2 === exclude)
5922
6782
  continue;
5923
6783
  const rel = relative4(files.directory, selected2);
@@ -6040,7 +6900,7 @@ class HistoryRecovery {
6040
6900
  const current = await inspectRecovery(this.coordination, this);
6041
6901
  if (JSON.stringify(current) !== JSON.stringify(expected))
6042
6902
  throw new ContractError("conflict");
6043
- const state = files.inspect(), recoveryId = randomUUID5();
6903
+ const state = files.inspect(), recoveryId = randomUUID7();
6044
6904
  const bridge = {
6045
6905
  kind: "recovery",
6046
6906
  recoveryId,
@@ -6071,7 +6931,7 @@ class HistoryRecovery {
6071
6931
  throw new ContractError("conflict");
6072
6932
  const row = {
6073
6933
  kind: "record",
6074
- id: randomUUID5(),
6934
+ id: randomUUID7(),
6075
6935
  seq: String(BigInt(bridge.previous.highSeq) + 1n),
6076
6936
  value: bridge
6077
6937
  };
@@ -6135,7 +6995,7 @@ async function confirm(expected) {
6135
6995
  `resolve ${expected.epoch}`
6136
6996
  ]
6137
6997
  ];
6138
- await new Promise((resolve4, reject) => {
6998
+ await new Promise((resolve7, reject) => {
6139
6999
  let index = 0, line = "", done = false;
6140
7000
  const raw = process.stdin.isRaw;
6141
7001
  const finish = (ok) => {
@@ -6151,7 +7011,7 @@ async function confirm(expected) {
6151
7011
  process.stdout.write(`\x1B[?2004l
6152
7012
  `);
6153
7013
  if (ok)
6154
- resolve4();
7014
+ resolve7();
6155
7015
  else
6156
7016
  reject(new ContractError("forbidden"));
6157
7017
  };
@@ -6196,12 +7056,12 @@ async function localRecovery(c, operation) {
6196
7056
  native();
6197
7057
  const history = new HistoryRecovery(c);
6198
7058
  const expected = await inspectRecovery(c, history);
6199
- const registration = c.recoverySnapshot().registration;
7059
+ const registration2 = c.recoverySnapshot().registration;
6200
7060
  const view = {
6201
7061
  ...expected,
6202
7062
  process: {
6203
- pid: registration.pid,
6204
- host: diagnosticText(registration.host, 255)
7063
+ pid: registration2.pid,
7064
+ host: diagnosticText(registration2.host, 255)
6205
7065
  },
6206
7066
  notice: warning
6207
7067
  };
@@ -6226,227 +7086,9 @@ async function localRecovery(c, operation) {
6226
7086
  }, history);
6227
7087
  }
6228
7088
 
6229
- // src/service/runtime.ts
6230
- import { homedir as homedir4 } from "node:os";
6231
- import { dirname as dirname8, isAbsolute as isAbsolute6, join as join10, resolve as resolve4 } from "node:path";
6232
-
6233
- // src/service/config.ts
6234
- import { isIP } from "node:net";
6235
- function decodeHttpPolicy(value) {
6236
- const o = closed(value, ["schema", "host", "port", "proxy"]);
6237
- if (o.host !== "127.0.0.1" && o.host !== "::1")
6238
- throw new ContractError("invalid");
6239
- let proxy = null;
6240
- if (o.proxy !== null) {
6241
- const p = closed(o.proxy, ["origin", "trustedAddresses"]);
6242
- const origin = text(p.origin, 256, true);
6243
- let url;
6244
- try {
6245
- url = new URL(origin);
6246
- } catch {
6247
- throw new ContractError("invalid");
6248
- }
6249
- if (url.protocol !== "https:" || url.origin !== origin || url.username || url.password)
6250
- throw new ContractError("invalid");
6251
- const trustedAddresses = list(p.trustedAddresses, 8, (v) => {
6252
- const address = text(v, 64, true);
6253
- if (!isIP(address) || !loopbackAddress(address))
6254
- throw new ContractError("invalid");
6255
- return address;
6256
- });
6257
- if (!trustedAddresses.length || new Set(trustedAddresses).size !== trustedAddresses.length)
6258
- throw new ContractError("invalid");
6259
- proxy = { origin, trustedAddresses };
6260
- }
6261
- return {
6262
- schema: literal(o.schema, "cadence-service-http/1"),
6263
- host: o.host,
6264
- port: integer(o.port, 0, 65535),
6265
- proxy
6266
- };
6267
- }
6268
- function loopbackAddress(host) {
6269
- const mapped = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(host);
6270
- const h = mapped ? mapped[1] : host;
6271
- if (h.includes(":"))
6272
- return h === "::1";
6273
- const parts = h.split(".");
6274
- return parts.length === 4 && parts.every((p) => /^\d{1,3}$/.test(p) && Number(p) <= 255) && Number(parts[0]) === 127;
6275
- }
6276
- function localOrigin(policy, actualPort) {
6277
- integer(actualPort, 1, 65535);
6278
- return `http://${policy.host === "::1" ? "[::1]" : policy.host}:${actualPort}`;
6279
- }
6280
-
6281
- // src/service/runtime.ts
6282
- var STARTUP_INPUT_BYTES = 16 * 1024;
6283
- function absolute(value) {
6284
- const result = text(value, 4096, true);
6285
- if (!isAbsolute6(result))
6286
- throw new ContractError("invalid");
6287
- return result;
6288
- }
6289
- function decodeHostConfig(value) {
6290
- const o = closed(value, [
6291
- "schema",
6292
- "http",
6293
- "cwd",
6294
- "agentDir",
6295
- "tokenSource",
6296
- "tokenFile"
6297
- ]);
6298
- const tokenSource = oneOf(o.tokenSource, [
6299
- "generated",
6300
- "environment",
6301
- "file",
6302
- "config"
6303
- ]);
6304
- const tokenFile = o.tokenFile === null ? null : absolute(o.tokenFile);
6305
- if ((tokenSource === "file" || tokenSource === "config") !== !!tokenFile)
6306
- throw new ContractError("invalid");
6307
- return {
6308
- schema: literal(o.schema, "cadence-service-host/1"),
6309
- http: decodeHttpPolicy(o.http),
6310
- cwd: absolute(o.cwd),
6311
- agentDir: absolute(o.agentDir),
6312
- tokenSource,
6313
- tokenFile
6314
- };
6315
- }
6316
- function readHostConfig(c) {
6317
- const owner = c.configuration();
6318
- if (!owner)
6319
- return;
6320
- const snapshot = c.files.read(c.path("host-config.json"));
6321
- if (!snapshot)
6322
- throw new ContractError("corrupt-selected");
6323
- const config = decodeHostConfig(json(snapshot));
6324
- if (!config.http.port || owner.endpoint !== localOrigin(config.http, config.http.port) || owner.policyIdentity !== hash(encoded(config)))
6325
- throw new ContractError("conflict");
6326
- return config;
6327
- }
6328
- function hostEnvironment(ambient) {
6329
- let route;
6330
- const facts = networkSnapshot(ambient);
6331
- try {
6332
- if (facts.NODE_OPTIONS.present)
6333
- throw new Error;
6334
- route = sdkNetworkEnvironment(facts, networkSnapshot(undefined));
6335
- } catch {
6336
- throw new ContractError("unsupported-context");
6337
- }
6338
- const result = Object.create(null);
6339
- for (const key of Object.keys(ambient)) {
6340
- const d = Object.getOwnPropertyDescriptor(ambient, key);
6341
- if (!d || !("value" in d) || typeof d.value !== "string")
6342
- throw new ContractError("invalid");
6343
- if (/^(?:CADENCE_|PI_|ABEL_|TRELLIS_|GIT_)/i.test(key) || networkKeys.some((name) => name === key))
6344
- continue;
6345
- result[key] = d.value;
6346
- }
6347
- if (route.redactionLiterals.length) {
6348
- result.HTTP_PROXY = route.env.HTTP_PROXY;
6349
- result.HTTPS_PROXY = route.env.HTTPS_PROXY;
6350
- result.NODE_USE_ENV_PROXY = "1";
6351
- }
6352
- result.PI_OFFLINE = "1";
6353
- result.NO_COLOR = "1";
6354
- if (!sameNetwork(facts, networkSnapshot(ambient)))
6355
- throw new ContractError("stale");
6356
- return result;
6357
- }
6358
- function selectStart(options, ambient) {
6359
- const workspace = resolveWorkspace(options.workspace), c = new ServiceCoordination(workspace.root, options.stateHome);
6360
- const old = readHostConfig(c);
6361
- const env = Object.getOwnPropertyDescriptor(ambient, "CADENCE_WEB_TOKEN");
6362
- if (env && (!("value" in env) || typeof env.value !== "string"))
6363
- throw new ContractError("invalid");
6364
- let fromFile;
6365
- if (options.config) {
6366
- const path2 = resolve4(options.config), f = new PrivateFiles(dirname8(path2));
6367
- const bytes = f.read(path2);
6368
- if (!bytes)
6369
- throw new ContractError("corrupt-selected");
6370
- fromFile = decodeHostConfig(json(bytes));
6371
- }
6372
- const base = old ?? fromFile ?? decodeHostConfig({
6373
- schema: "cadence-service-host/1",
6374
- cwd: workspace.cwd,
6375
- http: {
6376
- schema: "cadence-service-http/1",
6377
- host: options.host ?? "127.0.0.1",
6378
- port: options.port ?? 17423,
6379
- proxy: null
6380
- },
6381
- agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join10(ambient.HOME ?? ambient.USERPROFILE ?? homedir4(), ".pi", "agent")),
6382
- tokenSource: options.tokenFile ? "file" : env ? "environment" : "generated",
6383
- tokenFile: options.tokenFile ? resolve4(options.tokenFile) : null
6384
- });
6385
- const server = options.serverConfig !== undefined ? resolve4(options.serverConfig) : old?.tokenSource === "config" ? old.tokenFile : null;
6386
- let selected2 = base;
6387
- if (server) {
6388
- if (options.tokenFile !== undefined)
6389
- throw new ContractError("conflict");
6390
- const { port } = readServerConfig(server).config;
6391
- selected2 = decodeHostConfig({
6392
- ...base,
6393
- http: { ...base.http, port },
6394
- tokenSource: "config",
6395
- tokenFile: server
6396
- });
6397
- }
6398
- 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)
6399
- throw new ContractError("conflict");
6400
- return {
6401
- workspace: workspace.root,
6402
- stateHome: c.stateHome,
6403
- config: selected2,
6404
- environmentToken: env?.value ?? null
6405
- };
6406
- }
6407
- function decodeHostInput(value) {
6408
- const o = closed(value, [
6409
- "workspace",
6410
- "stateHome",
6411
- "config",
6412
- "environmentToken"
6413
- ]);
6414
- const config = decodeHostConfig(o.config);
6415
- const environmentToken = o.environmentToken === null ? null : text(o.environmentToken, 1024, true);
6416
- if (config.tokenSource === "environment" !== (environmentToken !== null))
6417
- throw new ContractError("invalid");
6418
- return {
6419
- workspace: absolute(o.workspace),
6420
- stateHome: absolute(o.stateHome),
6421
- config,
6422
- environmentToken
6423
- };
6424
- }
6425
- function portValue(value) {
6426
- if (!/^(0|[1-9][0-9]{0,4})$/.test(value))
6427
- throw new ContractError("invalid");
6428
- return integer(Number(value), 0, 65535);
6429
- }
6430
- var STARTUP_STAGES = [
6431
- "package",
6432
- "pi-agent-directory",
6433
- "pi-auth",
6434
- "pi-models",
6435
- "pi-runtime",
6436
- "service"
6437
- ];
6438
- function decodeStartupReport(value) {
6439
- const o = closed(value, ["status", "error", "stage"]);
6440
- return {
6441
- status: oneOf(o.status, ["refused", "quarantined"]),
6442
- error: oneOf(o.error, ERROR_CODES),
6443
- stage: oneOf(o.stage, STARTUP_STAGES)
6444
- };
6445
- }
6446
-
6447
7089
  // src/service/system-service.ts
6448
- import { existsSync as existsSync2, mkdirSync as mkdirSync6, realpathSync as realpathSync5, rmSync as rmSync4 } from "node:fs";
6449
- import { isAbsolute as isAbsolute7, join as join11 } from "node:path";
7090
+ import { existsSync as existsSync2, realpathSync as realpathSync5, rmSync as rmSync4 } from "node:fs";
7091
+ import { isAbsolute as isAbsolute8, join as join14 } from "node:path";
6450
7092
  var MARKER4 = "# Generated by @abelxiaoxing/cadence";
6451
7093
  function boundary(s) {
6452
7094
  if (s.platform !== "linux" || s.scope !== "system")
@@ -6460,7 +7102,7 @@ function legacyPath(s) {
6460
7102
  function observed(s) {
6461
7103
  const path2 = unitPath(s);
6462
7104
  let previous = readGenerated(path2, MARKER4);
6463
- if (typeof previous === "string" && (!isAbsolute7(unitWorkspace(previous) ?? "") || !previous.includes(`
7105
+ if (typeof previous === "string" && (unitWorkspace(previous) !== null && !isAbsolute8(unitWorkspace(previous) ?? "") || !previous.includes(`
6464
7106
  User=root
6465
7107
  `)))
6466
7108
  previous = null;
@@ -6489,13 +7131,13 @@ function sameStateEnvironment(s, previous) {
6489
7131
  values.set(key, value.slice(at + 1).replaceAll("%%", "%"));
6490
7132
  }
6491
7133
  const state = s.env.XDG_STATE_HOME;
6492
- return values.get("HOME") === s.home && values.get("XDG_STATE_HOME") === (state && isAbsolute7(state) ? state : undefined);
7134
+ return values.get("HOME") === s.home && values.get("XDG_STATE_HOME") === (state && isAbsolute8(state) ? state : undefined);
6493
7135
  } catch {
6494
7136
  return false;
6495
7137
  }
6496
7138
  }
6497
7139
  async function ownerBoundary(s, workspace, managed) {
6498
- const status = await s.ownerStatus(workspace);
7140
+ const status = await s.ownerStatus(workspace, serviceConfigPath(s));
6499
7141
  if (managed) {
6500
7142
  if (status === "absent")
6501
7143
  return { status: "unavailable", workspace };
@@ -6522,21 +7164,21 @@ async function configureSystemService(s, mode, requested) {
6522
7164
  if (mode === "restart" && previous === undefined)
6523
7165
  return { status: "absent", scope: "system" };
6524
7166
  const sdk = sdkPackages(s.root);
6525
- if (!sdk)
6526
- return { status: "missing-pi-sdk", scope: "system" };
6527
- if (previous && !existsSync2(unitWorkspace(previous)))
7167
+ if (previous && unitWorkspace(previous) && !existsSync2(unitWorkspace(previous)))
6528
7168
  return { status: "workspace-changed", ...result };
6529
- const selected2 = selectWorkspace(s, requested, unitWorkspace(previous), false);
6530
- const selectedReal = existsSync2(selected2.workspace) ? realpathSync5(selected2.workspace) : undefined;
7169
+ const selected2 = selectWorkspace(s, requested, mode === "install" && requested === undefined ? null : unitWorkspace(previous), false);
7170
+ if (selected2.workspace !== null && !sdk)
7171
+ return { status: "missing-pi-sdk", scope: "system" };
7172
+ const selectedReal = selected2.workspace && existsSync2(selected2.workspace) ? realpathSync5(selected2.workspace) : undefined;
6531
7173
  const agentDir = agentDirectory(s);
6532
7174
  const content = buildUnit({
6533
7175
  system: true,
6534
7176
  node: s.node,
6535
- cli: join11(s.root, "src", "service-cli.mjs"),
7177
+ cli: join14(s.root, "src", "service-cli.mjs"),
6536
7178
  workspace: selected2.workspace,
6537
7179
  serverConfig: serviceConfigPath(s),
6538
7180
  agentDir,
6539
- sdk,
7181
+ sdk: sdk ?? [],
6540
7182
  environment: serviceEnvironment(s)
6541
7183
  });
6542
7184
  if (previous && !sameStateEnvironment(s, previous))
@@ -6546,13 +7188,13 @@ async function configureSystemService(s, mode, requested) {
6546
7188
  return { status: "unavailable", ...result };
6547
7189
  if (active.status === 0 && previous === undefined)
6548
7190
  return { status: "foreign", ...result };
6549
- if (selectedReal && (previous === undefined || selected2.workspace !== unitWorkspace(previous))) {
7191
+ if ((selected2.workspace === null || selectedReal) && (previous === undefined || selected2.workspace !== unitWorkspace(previous))) {
6550
7192
  const target2 = await ownerBoundary(s, selected2.workspace, false);
6551
7193
  if (target2)
6552
7194
  return { ...selected2, ...target2, ...result };
6553
7195
  }
6554
7196
  const workspace = unitWorkspace(previous);
6555
- if (workspace) {
7197
+ if (workspace !== undefined) {
6556
7198
  const refused3 = await ownerBoundary(s, workspace, active.status === 0);
6557
7199
  if (refused3)
6558
7200
  return { ...refused3, ...result };
@@ -6562,7 +7204,7 @@ async function configureSystemService(s, mode, requested) {
6562
7204
  return { ...unsettled, ...result };
6563
7205
  if (readGenerated(path2, MARKER4) !== previous)
6564
7206
  return { status: "foreign", ...result };
6565
- if (selectedReal) {
7207
+ if (selectedReal && selected2.workspace) {
6566
7208
  try {
6567
7209
  if (realpathSync5(selected2.workspace) !== selectedReal)
6568
7210
  return { status: "workspace-changed", ...result };
@@ -6576,13 +7218,12 @@ async function configureSystemService(s, mode, requested) {
6576
7218
  workspace: unitWorkspace(previous),
6577
7219
  ...result
6578
7220
  };
6579
- mkdirSync6(selected2.workspace, { recursive: true });
6580
7221
  write(path2, content);
6581
7222
  if (s.systemctl(["daemon-reload"]).status !== 0)
6582
7223
  return { status: "failed", ...result, ...selected2 };
6583
7224
  if (mode === "install" && s.systemctl(["enable", s.unit]).status !== 0)
6584
7225
  return { status: "failed", ...result, ...selected2 };
6585
- if (!regularFile(join11(agentDir, "auth.json")))
7226
+ if (selected2.workspace !== null && !regularFile(join14(agentDir, "auth.json")))
6586
7227
  return { status: "waiting-for-pi-login", ...result, ...selected2 };
6587
7228
  s.systemctl(["reset-failed", s.unit]);
6588
7229
  return {
@@ -6651,12 +7292,12 @@ function systemServiceStatus(s) {
6651
7292
  }
6652
7293
 
6653
7294
  // src/service/cli.ts
6654
- function nativeCall(c, operation, reveal = false) {
7295
+ function nativeCall(c, operation, reveal = false, controlBody) {
6655
7296
  const access = c.localAccess();
6656
7297
  const url = new URL(`${access.endpoint}/local/v1/admin/${operation}`);
6657
7298
  const write2 = !["status", "diagnose"].includes(operation);
6658
- const body = write2 ? JSON.stringify(reveal ? { reveal: true } : {}) : undefined;
6659
- return new Promise((resolve6, reject) => {
7299
+ const body = write2 ? JSON.stringify(controlBody ?? (reveal ? { reveal: true } : {})) : undefined;
7300
+ return new Promise((resolve8, reject) => {
6660
7301
  let timer;
6661
7302
  const fail = () => {
6662
7303
  clearTimeout(timer);
@@ -6697,7 +7338,7 @@ function nativeCall(c, operation, reveal = false) {
6697
7338
  throw new ContractError("stale");
6698
7339
  }
6699
7340
  clearTimeout(timer);
6700
- resolve6(reply.value);
7341
+ resolve8(reply.value);
6701
7342
  } catch {
6702
7343
  fail();
6703
7344
  }
@@ -6712,7 +7353,7 @@ function nativeCall(c, operation, reveal = false) {
6712
7353
  req.end(body);
6713
7354
  });
6714
7355
  }
6715
- var pause = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
7356
+ var pause = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
6716
7357
  async function serviceStatus(c, diagnose = false) {
6717
7358
  const observed2 = c.inspect();
6718
7359
  if (observed2.status === "absent" || observed2.status === "stopped")
@@ -6774,10 +7415,10 @@ function startupGuidance(result, input) {
6774
7415
  return `Pi's agent directory ${agent} must be an existing real directory (not a symlink) owned by you.
6775
7416
  `;
6776
7417
  case "pi-auth":
6777
- return `${join12(agent, "auth.json")} must be your own single-link regular file holding valid Pi credentials; log in to Pi first.
7418
+ return `${join15(agent, "auth.json")} must be your own single-link regular file holding valid Pi credentials; log in to Pi first.
6778
7419
  `;
6779
7420
  case "pi-models":
6780
- return `${join12(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.
7421
+ return `${join15(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.
6781
7422
  `;
6782
7423
  case "pi-runtime":
6783
7424
  return `Loading Pi's SDK model runtime failed after the owner registered (optional SDK packages missing, or sources the SDK refuses); it stays quarantined.
@@ -6810,31 +7451,38 @@ async function startService(options, foreground, hostUrl) {
6810
7451
  throw new ContractError("conflict");
6811
7452
  return existing;
6812
7453
  }
7454
+ return launchHost(input, c, foreground, hostUrl, input.config.cwd, env, network);
7455
+ }
7456
+ async function launchHost(input, c, foreground, hostUrl, cwd, env, network) {
7457
+ const serialized = `${JSON.stringify(input)}
7458
+ `;
7459
+ if (Buffer.byteLength(serialized) > STARTUP_INPUT_BYTES)
7460
+ throw new ContractError("oversize");
6813
7461
  if (!sameNetwork(network, networkSnapshot(process.env)))
6814
7462
  throw new ContractError("stale");
6815
7463
  const child = spawn(process.execPath, [
6816
7464
  ...env.NODE_USE_ENV_PROXY === "1" ? ["--use-env-proxy"] : [],
6817
- fileURLToPath(hostUrl)
7465
+ fileURLToPath2(hostUrl)
6818
7466
  ], {
6819
- cwd: input.config.cwd,
7467
+ cwd,
6820
7468
  env,
6821
7469
  detached: !foreground,
6822
7470
  windowsHide: true,
6823
7471
  stdio: ["pipe", "pipe", "ignore"]
6824
7472
  });
6825
7473
  let completed = false;
6826
- const closure = new Promise((resolve6) => child.once("close", (code2, signal) => {
7474
+ const closure = new Promise((resolve8) => child.once("close", (code2, signal) => {
6827
7475
  completed = true;
6828
7476
  if (foreground && (code2 !== 0 || signal !== null))
6829
7477
  process.exitCode = 1;
6830
- resolve6();
7478
+ resolve8();
6831
7479
  }));
6832
- const startup = new Promise((resolve6) => {
7480
+ const startup = new Promise((resolve8) => {
6833
7481
  let bytes = "", resolved = false;
6834
7482
  const finish = (value) => {
6835
7483
  if (!resolved) {
6836
7484
  resolved = true;
6837
- resolve6(value);
7485
+ resolve8(value);
6838
7486
  }
6839
7487
  };
6840
7488
  child.on("error", () => finish({ status: "unconfirmed" }));
@@ -6874,12 +7522,12 @@ async function startService(options, foreground, hostUrl) {
6874
7522
  let timer;
6875
7523
  const result = await Promise.race([
6876
7524
  startup,
6877
- new Promise((resolve6) => {
6878
- timer = setTimeout(() => resolve6({ status: "unconfirmed" }), LIMITS.startWaitMs);
7525
+ new Promise((resolve8) => {
7526
+ timer = setTimeout(() => resolve8({ status: "unconfirmed" }), LIMITS.startWaitMs);
6879
7527
  })
6880
7528
  ]);
6881
7529
  clearTimeout(timer);
6882
- const guidance = startupGuidance(result, input);
7530
+ const guidance = "kind" in input ? "" : startupGuidance(result, input);
6883
7531
  if (guidance)
6884
7532
  process.stderr.write(guidance);
6885
7533
  child.stdout.destroy();
@@ -6896,12 +7544,12 @@ async function startService(options, foreground, hostUrl) {
6896
7544
  }
6897
7545
  return result;
6898
7546
  }
6899
- async function observeStarted(workspace) {
7547
+ async function observeStarted(workspace, config) {
6900
7548
  const deadline = performance.now() + LIMITS.startWaitMs + 2000;
6901
7549
  let service = "unconfirmed";
6902
7550
  while (performance.now() < deadline) {
6903
7551
  try {
6904
- const c = new ServiceCoordination(workspace);
7552
+ const c = workspace === null ? new GatewayStore(config ?? serverConfigPath(homedir6())) : new ServiceCoordination(workspace);
6905
7553
  const observed2 = await serviceStatus(c);
6906
7554
  if (observed2 && typeof observed2 === "object" && "status" in observed2)
6907
7555
  service = String(observed2.status);
@@ -6914,26 +7562,26 @@ async function observeStarted(workspace) {
6914
7562
  }
6915
7563
  return { service };
6916
7564
  }
6917
- async function privateLink(workspace, stateHome) {
7565
+ async function privateLink(workspace, stateHome, config) {
6918
7566
  try {
6919
- const value = closed(await nativeCall(new ServiceCoordination(workspace, stateHome), "token-link", true), ["link"]);
7567
+ const value = closed(await nativeCall(workspace === null ? new GatewayStore(config ?? serverConfigPath(homedir6()), stateHome) : new ServiceCoordination(workspace, stateHome), "token-link", true), ["link"]);
6920
7568
  return text(value.link, 4096, true);
6921
7569
  } catch {
6922
7570
  return;
6923
7571
  }
6924
7572
  }
6925
7573
  var ownerControl = {
6926
- async ownerStatus(workspace) {
7574
+ async ownerStatus(workspace, config) {
6927
7575
  try {
6928
- const observed2 = await serviceStatus(new ServiceCoordination(workspace));
7576
+ const observed2 = await serviceStatus(workspace === null ? new GatewayStore(config ?? serverConfigPath(homedir6())) : new ServiceCoordination(workspace));
6929
7577
  return observed2 && typeof observed2 === "object" && "status" in observed2 ? String(observed2.status) : "unavailable";
6930
7578
  } catch {
6931
7579
  return "unavailable";
6932
7580
  }
6933
7581
  },
6934
- async stopOwner(workspace) {
7582
+ async stopOwner(workspace, config) {
6935
7583
  try {
6936
- const c = new ServiceCoordination(workspace);
7584
+ const c = workspace === null ? new GatewayStore(config ?? serverConfigPath(homedir6())) : new ServiceCoordination(workspace);
6937
7585
  const observed2 = await serviceStatus(c);
6938
7586
  const status = observed2 && typeof observed2 === "object" && "status" in observed2 ? String(observed2.status) : "unavailable";
6939
7587
  if (status === "absent" || status === "stopped")
@@ -6978,7 +7626,7 @@ async function serverAction(s, action, requested, linger) {
6978
7626
  }
6979
7627
  }
6980
7628
  function serverGuidance(s, action, result, link) {
6981
- const cli = piUserInstall(s) ? `node ${JSON.stringify(join12(s.root, "src", "service-cli.mjs"))}` : "cadence";
7629
+ const cli = piUserInstall(s) ? `node ${JSON.stringify(join15(s.root, "src", "service-cli.mjs"))}` : "cadence";
6982
7630
  const note = result.redirectedFrom ? `Cadence workspace: ${result.workspace} (${result.redirectedFrom} contains Cadence's private state)
6983
7631
  ` : "";
6984
7632
  return note + serverStatusGuidance(s, action, result, cli, link);
@@ -7010,6 +7658,11 @@ function managerDetail(s) {
7010
7658
  }
7011
7659
  function serverStatusGuidance(s, action, result, cli, link) {
7012
7660
  const workspace = JSON.stringify(result.workspace ?? "");
7661
+ if (result.workspace === null && result.status === "started")
7662
+ return `Cadence unified workbench: ${link ?? result.endpoint ?? result.service ?? "unconfirmed"}
7663
+ No default workspace was created. Projects are opened explicitly.
7664
+ Manage: ${cli} server status|restart|stop|uninstall
7665
+ `;
7013
7666
  const flags = `${s.scope === "system" ? " --system" : s.platform === "linux" ? " --user" : ""}${serviceName(s) ? ` --name ${serviceName(s)}` : ""}`;
7014
7667
  const manage = `Manage: ${cli} server status|restart|stop|uninstall${flags}`;
7015
7668
  const when = s.scope === "system" ? "at boot, as root (no linger)" : s.platform === "linux" ? "at boot or login" : "at login";
@@ -7035,7 +7688,7 @@ ${manage}
7035
7688
  ${linger}`;
7036
7689
  }
7037
7690
  case "waiting-for-pi-login":
7038
- return `Cadence is registered for ${result.workspace}; it starts once Pi has ${join12(agentDirectory(s), "auth.json")}.
7691
+ return `Cadence is registered for ${result.workspace}; it starts once Pi has ${join15(agentDirectory(s), "auth.json")}.
7039
7692
  Log in to Pi, then: ${cli} server restart${flags}
7040
7693
  ${config}
7041
7694
  ${lingerGuidance(result.linger)}`;
@@ -7131,7 +7784,7 @@ async function serverMain(argv, hostUrl) {
7131
7784
  else if (flag === "--name" && argv[0])
7132
7785
  name = argv.shift();
7133
7786
  else if (flag === "--workspace" && action === "install" && argv[0])
7134
- requested = resolve5(argv.shift());
7787
+ requested = resolve7(argv.shift());
7135
7788
  else if (flag === "--port" && action === "install" && argv[0]) {
7136
7789
  port = portValue(argv.shift());
7137
7790
  if (!port)
@@ -7139,7 +7792,7 @@ async function serverMain(argv, hostUrl) {
7139
7792
  } else
7140
7793
  throw new ContractError("invalid");
7141
7794
  }
7142
- const s = hostSystem(dirname9(dirname9(fileURLToPath(hostUrl))), ownerControl, {
7795
+ const s = hostSystem(dirname13(dirname13(fileURLToPath2(hostUrl))), ownerControl, {
7143
7796
  scope,
7144
7797
  name
7145
7798
  });
@@ -7184,10 +7837,10 @@ async function serverMain(argv, hostUrl) {
7184
7837
  throw error;
7185
7838
  }
7186
7839
  let link;
7187
- if (result.status === "started" && result.workspace) {
7188
- Object.assign(result, await observeStarted(result.workspace));
7840
+ if (result.status === "started" && result.workspace !== undefined) {
7841
+ Object.assign(result, await observeStarted(result.workspace, serviceConfigPath(s)));
7189
7842
  if (result.service === "alive")
7190
- link = await privateLink(result.workspace);
7843
+ link = await privateLink(result.workspace, undefined, serviceConfigPath(s));
7191
7844
  }
7192
7845
  process.stderr.write(serverGuidance(s, action, result, link));
7193
7846
  process.stdout.write(`${JSON.stringify(result)}
@@ -7217,14 +7870,14 @@ async function projectMain(argv, hostUrl) {
7217
7870
  while (argv.length) {
7218
7871
  const flag = argv.shift();
7219
7872
  if (!flag.startsWith("--") && !selected2) {
7220
- options.workspace = resolve5(flag);
7873
+ options.workspace = resolve7(flag);
7221
7874
  selected2 = true;
7222
7875
  } else if (!seen.has(flag) && flag === "--create") {
7223
7876
  seen.add(flag);
7224
7877
  create = true;
7225
7878
  } else if (!seen.has(flag) && (flag === "--state-home" || flag === "--agent-dir") && argv[0]) {
7226
7879
  seen.add(flag);
7227
- const value = resolve5(argv.shift());
7880
+ const value = resolve7(argv.shift());
7228
7881
  if (flag === "--state-home")
7229
7882
  options.stateHome = value;
7230
7883
  else
@@ -7235,14 +7888,8 @@ async function projectMain(argv, hostUrl) {
7235
7888
  if (privateWorkspace(options.workspace, options.stateHome))
7236
7889
  throw new ContractError("forbidden");
7237
7890
  if (create)
7238
- mkdirSync7(options.workspace, { recursive: true });
7239
- const landing = effectiveWorkspace(options.workspace, options.stateHome);
7240
- if (landing.redirected) {
7241
- process.stderr.write(`Cadence workspace: ${landing.workspace} (${options.workspace} contains Cadence's private state)
7242
- `);
7243
- mkdirSync7(landing.workspace, { recursive: true });
7244
- }
7245
- options.workspace = landing.workspace;
7891
+ mkdirSync6(options.workspace, { recursive: true });
7892
+ effectiveWorkspace(options.workspace, options.stateHome);
7246
7893
  const c = new ServiceCoordination(options.workspace, options.stateHome);
7247
7894
  const kept = readHostConfig(c);
7248
7895
  if (!kept)
@@ -7270,8 +7917,25 @@ async function managementMain(argv, hostUrl) {
7270
7917
  if (!process.versions.node || process.versions.bun)
7271
7918
  throw new ContractError("unsupported-context");
7272
7919
  const command = argv.shift();
7920
+ if (command === "workbench") {
7921
+ await gatewayMain(argv, hostUrl);
7922
+ return;
7923
+ }
7924
+ if ([
7925
+ "start",
7926
+ "status",
7927
+ "diagnose",
7928
+ "stop",
7929
+ "token-link",
7930
+ "token-rotate"
7931
+ ].includes(command ?? "") && !argv.includes("--workspace")) {
7932
+ await gatewayMain([command, ...argv], hostUrl);
7933
+ return;
7934
+ }
7273
7935
  if (command === "--help" && !argv.length) {
7274
7936
  process.stdout.write(`cadence start|status|diagnose|stop|token-link --reveal|token-rotate|recover inspect|recover resolve [--workspace PATH] [--state-home PATH]
7937
+ Without --workspace these commands address the unified workbench (except project recovery); no default project directory is created.
7938
+ cadence workbench start|status|diagnose|stop|token-link --reveal|token-rotate [--server-config PATH] [--state-home PATH]
7275
7939
  cadence server install [--workspace PATH] [--no-linger]|status|restart|stop|uninstall
7276
7940
  Linux server options: --system (root, no linger) or --user; --name NAME selects an independent service; install also accepts --port PORT (required for a new named service). Root defaults to --system. No automatic sudo or profile migration.
7277
7941
  cadence project open [PATH] [--create] [--agent-dir PATH] [--state-home PATH]
@@ -7349,23 +8013,11 @@ Recovery resolve requires this local native terminal, exact unchanged dead-owner
7349
8013
  else
7350
8014
  throw new ContractError("invalid");
7351
8015
  }
7352
- const landing = effectiveWorkspace(options.workspace, options.stateHome);
7353
- if (landing.redirected) {
7354
- if (seen.has("--workspace"))
7355
- process.stderr.write(`Cadence workspace: ${landing.workspace} (${options.workspace} contains Cadence's private state)
7356
- `);
7357
- options.workspace = landing.workspace;
7358
- if (command === "start")
7359
- mkdirSync7(landing.workspace, { recursive: true });
7360
- }
8016
+ effectiveWorkspace(options.workspace, options.stateHome);
7361
8017
  let result;
7362
8018
  if (command === "start")
7363
8019
  result = await startService(options, foreground, hostUrl);
7364
- else if (landing.redirected && !existsSync3(landing.workspace)) {
7365
- if (command !== "status" && command !== "diagnose")
7366
- throw new ContractError("unavailable");
7367
- result = { status: "absent" };
7368
- } else {
8020
+ else {
7369
8021
  const c = new ServiceCoordination(options.workspace, options.stateHome);
7370
8022
  if (command === "recover" && recovery)
7371
8023
  result = await localRecovery(c, recovery);
@@ -7383,8 +8035,6 @@ Recovery resolve requires this local native terminal, exact unchanged dead-owner
7383
8035
  else
7384
8036
  throw new ContractError("invalid");
7385
8037
  }
7386
- if (landing.redirected && result && typeof result === "object")
7387
- result = { ...result, workspace: landing.workspace };
7388
8038
  if (result !== null)
7389
8039
  process.stdout.write(`${JSON.stringify(result)}
7390
8040
  `);