@abelxiaoxing/cadence 2.0.15 → 2.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -20
- package/docs/service-management.md +58 -5
- package/package.json +2 -2
- package/src/service/coordination.ts +26 -9
- package/src/service-cli.mjs +1326 -922
- package/src/service-host.mjs +326 -254
- package/web/dist/assets/cadence.css +1 -1
- package/web/dist/assets/cadence.js +11 -11
- package/web/dist/manifest.json +6 -6
package/src/service-host.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createServer as createServer2,
|
|
10
10
|
request as request2
|
|
11
11
|
} from "node:http";
|
|
12
|
-
import { homedir as
|
|
12
|
+
import { homedir as homedir5 } from "node:os";
|
|
13
13
|
import { WebSocket, WebSocketServer as WebSocketServer2 } from "ws";
|
|
14
14
|
|
|
15
15
|
// src/service-assets.ts
|
|
@@ -5353,7 +5353,7 @@ function sdkNetworkEnvironment(ambient, scoped) {
|
|
|
5353
5353
|
|
|
5354
5354
|
// src/service/coordination.ts
|
|
5355
5355
|
import { randomBytes as randomBytes2, randomUUID as randomUUID4 } from "node:crypto";
|
|
5356
|
-
import { opendirSync
|
|
5356
|
+
import { opendirSync } from "node:fs";
|
|
5357
5357
|
import { hostname as hostname2 } from "node:os";
|
|
5358
5358
|
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join6, relative as relative3, sep as sep3 } from "node:path";
|
|
5359
5359
|
var registrationKeys = [
|
|
@@ -5489,19 +5489,25 @@ function recoveryDigest(registry, selected) {
|
|
|
5489
5489
|
}
|
|
5490
5490
|
|
|
5491
5491
|
class ServiceCoordination {
|
|
5492
|
-
|
|
5492
|
+
project;
|
|
5493
5493
|
files;
|
|
5494
|
-
directory;
|
|
5495
5494
|
registryDirectory;
|
|
5496
5495
|
stateHome;
|
|
5497
5496
|
constructor(workspace, stateHome) {
|
|
5498
|
-
this.
|
|
5499
|
-
|
|
5497
|
+
this.project = workspace === null ? undefined : new TaskStore(resolveWorkspace(workspace).root, stateHome);
|
|
5498
|
+
this.stateHome = this.project ? dirname5(dirname5(this.project.privateRoot)) : canonicalAmbientRoot(stateHome ?? defaultStateHome());
|
|
5499
|
+
const namespace = join6(this.stateHome, "cadence-assistant-v1");
|
|
5500
5500
|
this.files = new PrivateFiles(namespace);
|
|
5501
|
-
this.stateHome = dirname5(namespace);
|
|
5502
|
-
this.directory = join6(this.store.privateRoot, "service-v1");
|
|
5503
5501
|
this.registryDirectory = join6(namespace, "service-registry-v1");
|
|
5504
5502
|
}
|
|
5503
|
+
get store() {
|
|
5504
|
+
if (!this.project)
|
|
5505
|
+
throw new ContractError("forbidden");
|
|
5506
|
+
return this.project;
|
|
5507
|
+
}
|
|
5508
|
+
get directory() {
|
|
5509
|
+
return join6(this.store.privateRoot, "service-v1");
|
|
5510
|
+
}
|
|
5505
5511
|
path(name) {
|
|
5506
5512
|
return join6(this.directory, name);
|
|
5507
5513
|
}
|
|
@@ -5522,7 +5528,7 @@ class ServiceCoordination {
|
|
|
5522
5528
|
const o = closed(json(snapshot, REGISTRY_BYTES), ["schema", "entries"]);
|
|
5523
5529
|
const entries = list(o.entries, REGISTRY_ENTRIES, decodeReservation);
|
|
5524
5530
|
for (const [i, row] of entries.entries()) {
|
|
5525
|
-
if (row.stateHome !== this.stateHome ||
|
|
5531
|
+
if (row.stateHome !== this.stateHome || canonicalAmbientRoot(row.root) !== row.root)
|
|
5526
5532
|
refused();
|
|
5527
5533
|
if (entries.slice(0, i).some((other) => rootsOverlap(row.root, other.root) || row.endpoint === other.endpoint))
|
|
5528
5534
|
refused();
|
|
@@ -5714,6 +5720,7 @@ class ServiceCoordination {
|
|
|
5714
5720
|
}
|
|
5715
5721
|
}
|
|
5716
5722
|
acquire(input, resolvedEndpoint = input.endpoint, initializeNew, reselectExisting) {
|
|
5723
|
+
this.store;
|
|
5717
5724
|
const config = decodeOwnerConfig(input);
|
|
5718
5725
|
const bound = endpoint(resolvedEndpoint);
|
|
5719
5726
|
if (!endpointMatches(config.endpoint, bound))
|
|
@@ -6135,6 +6142,7 @@ class OwnerLease {
|
|
|
6135
6142
|
// src/service/gateway-store.ts
|
|
6136
6143
|
import { randomBytes as randomBytes3, randomUUID as randomUUID5 } from "node:crypto";
|
|
6137
6144
|
import { opendirSync as opendirSync2 } from "node:fs";
|
|
6145
|
+
import { hostname as hostname3 } from "node:os";
|
|
6138
6146
|
import { join as join7, resolve as resolve3 } from "node:path";
|
|
6139
6147
|
|
|
6140
6148
|
// src/service/contracts/gateway.ts
|
|
@@ -6178,6 +6186,8 @@ function decodeGatewayProject(value) {
|
|
|
6178
6186
|
return project;
|
|
6179
6187
|
}
|
|
6180
6188
|
function registration(value) {
|
|
6189
|
+
const hasHost = !!value && typeof value === "object" && Object.hasOwn(value, "host");
|
|
6190
|
+
const hasRecovery = !!value && typeof value === "object" && Object.hasOwn(value, "recovery");
|
|
6181
6191
|
const o = closed(value, [
|
|
6182
6192
|
"schema",
|
|
6183
6193
|
"epoch",
|
|
@@ -6186,8 +6196,22 @@ function registration(value) {
|
|
|
6186
6196
|
"capability",
|
|
6187
6197
|
"selection",
|
|
6188
6198
|
"pid",
|
|
6189
|
-
"phase"
|
|
6199
|
+
"phase",
|
|
6200
|
+
...hasHost ? ["host"] : [],
|
|
6201
|
+
...hasRecovery ? ["recovery"] : []
|
|
6190
6202
|
]);
|
|
6203
|
+
let recovery;
|
|
6204
|
+
if (hasRecovery) {
|
|
6205
|
+
const r = closed(o.recovery, ["receipt", "sha256", "stamp"]);
|
|
6206
|
+
const receipt = text(r.receipt, 128, true);
|
|
6207
|
+
if (!/^recovery-v1\/[a-f0-9]{64}\/(?:0|[1-9][0-9]{0,2})\.json$/.test(receipt))
|
|
6208
|
+
throw new ContractError("corrupt-selected");
|
|
6209
|
+
recovery = {
|
|
6210
|
+
receipt,
|
|
6211
|
+
sha256: decodeHash(r.sha256),
|
|
6212
|
+
stamp: text(r.stamp, 512, true)
|
|
6213
|
+
};
|
|
6214
|
+
}
|
|
6191
6215
|
const endpoint2 = text(o.endpoint, 128, true), capability = text(o.capability, 43, true);
|
|
6192
6216
|
if (!/^http:\/\/(?:127\.0\.0\.1|\[::1\]):[1-9][0-9]{0,4}$/.test(endpoint2) || !/^[A-Za-z0-9_-]{43}$/.test(capability))
|
|
6193
6217
|
throw new ContractError("corrupt-selected");
|
|
@@ -6198,7 +6222,9 @@ function registration(value) {
|
|
|
6198
6222
|
endpoint: endpoint2,
|
|
6199
6223
|
capability,
|
|
6200
6224
|
selection: decodeHash(o.selection),
|
|
6201
|
-
pid: integer(o.pid, 1),
|
|
6225
|
+
pid: integer(o.pid, 1, 2147483647),
|
|
6226
|
+
...hasHost ? { host: text(o.host, 255, true) } : {},
|
|
6227
|
+
...recovery ? { recovery } : {},
|
|
6202
6228
|
phase: oneOf(o.phase, ["alive", "stopping", "stopped", "quarantined"])
|
|
6203
6229
|
};
|
|
6204
6230
|
}
|
|
@@ -6258,6 +6284,8 @@ class GatewayStore {
|
|
|
6258
6284
|
const previous = this.read();
|
|
6259
6285
|
if (previous && previous.phase !== "stopped")
|
|
6260
6286
|
throw new ContractError("quarantine");
|
|
6287
|
+
if (previous?.recovery)
|
|
6288
|
+
this.associatedProjects();
|
|
6261
6289
|
this.lock = this.files.create(this.path("gateway.lock"), encoded({ epoch: randomUUID5() }));
|
|
6262
6290
|
const source = { serverConfig: this.serverConfig, token };
|
|
6263
6291
|
if (!previous)
|
|
@@ -6277,6 +6305,8 @@ class GatewayStore {
|
|
|
6277
6305
|
capability: randomBytes3(32).toString("base64url"),
|
|
6278
6306
|
selection: hash2(inputBytes),
|
|
6279
6307
|
pid: process.pid,
|
|
6308
|
+
host: hostname3(),
|
|
6309
|
+
...previous?.recovery ? { recovery: previous.recovery } : {},
|
|
6280
6310
|
phase: "alive"
|
|
6281
6311
|
};
|
|
6282
6312
|
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));
|
|
@@ -6320,6 +6350,21 @@ class GatewayStore {
|
|
|
6320
6350
|
}
|
|
6321
6351
|
return out;
|
|
6322
6352
|
}
|
|
6353
|
+
associatedProjects() {
|
|
6354
|
+
const projects = this.projects(), recovery = this.read()?.recovery;
|
|
6355
|
+
if (!recovery)
|
|
6356
|
+
return projects;
|
|
6357
|
+
const receipt = this.files.read(this.path(recovery.receipt), 65536);
|
|
6358
|
+
if (!receipt || receipt.identity !== recovery.sha256 || receipt.stamp !== recovery.stamp)
|
|
6359
|
+
throw new ContractError("corrupt-selected");
|
|
6360
|
+
const value = json(receipt, 65536);
|
|
6361
|
+
literal(value.schema, "cadence-workbench-recovery/1");
|
|
6362
|
+
const retired = list(value.retiredProjects, 128, (entry) => {
|
|
6363
|
+
const row = closed(entry, ["id", "stamp"]);
|
|
6364
|
+
return { id: decodeHash(row.id), stamp: text(row.stamp, 512, true) };
|
|
6365
|
+
});
|
|
6366
|
+
return projects.filter((project) => !retired.some((row) => row.id === project.id && row.stamp === this.files.read(this.path(`projects/${project.id}.json`))?.stamp));
|
|
6367
|
+
}
|
|
6323
6368
|
saveProject(project) {
|
|
6324
6369
|
this.assert();
|
|
6325
6370
|
const p = decodeGatewayProject(project), path = this.path(`projects/${p.id}.json`), before = this.files.read(path);
|
|
@@ -6335,205 +6380,10 @@ class GatewayStore {
|
|
|
6335
6380
|
}
|
|
6336
6381
|
}
|
|
6337
6382
|
|
|
6338
|
-
// src/service/runtime.ts
|
|
6339
|
-
import { homedir as homedir2 } from "node:os";
|
|
6340
|
-
import { dirname as dirname6, isAbsolute as isAbsolute5, join as join8, resolve as resolve4 } from "node:path";
|
|
6341
|
-
var STARTUP_INPUT_BYTES = 16 * 1024;
|
|
6342
|
-
function absolute(value) {
|
|
6343
|
-
const result = text(value, 4096, true);
|
|
6344
|
-
if (!isAbsolute5(result))
|
|
6345
|
-
throw new ContractError("invalid");
|
|
6346
|
-
return result;
|
|
6347
|
-
}
|
|
6348
|
-
function decodeHostConfig(value) {
|
|
6349
|
-
const o = closed(value, [
|
|
6350
|
-
"schema",
|
|
6351
|
-
"http",
|
|
6352
|
-
"cwd",
|
|
6353
|
-
"agentDir",
|
|
6354
|
-
"tokenSource",
|
|
6355
|
-
"tokenFile"
|
|
6356
|
-
]);
|
|
6357
|
-
const tokenSource = oneOf(o.tokenSource, [
|
|
6358
|
-
"generated",
|
|
6359
|
-
"environment",
|
|
6360
|
-
"file",
|
|
6361
|
-
"config"
|
|
6362
|
-
]);
|
|
6363
|
-
const tokenFile = o.tokenFile === null ? null : absolute(o.tokenFile);
|
|
6364
|
-
if ((tokenSource === "file" || tokenSource === "config") !== !!tokenFile)
|
|
6365
|
-
throw new ContractError("invalid");
|
|
6366
|
-
return {
|
|
6367
|
-
schema: literal(o.schema, "cadence-service-host/1"),
|
|
6368
|
-
http: decodeHttpPolicy(o.http),
|
|
6369
|
-
cwd: absolute(o.cwd),
|
|
6370
|
-
agentDir: absolute(o.agentDir),
|
|
6371
|
-
tokenSource,
|
|
6372
|
-
tokenFile
|
|
6373
|
-
};
|
|
6374
|
-
}
|
|
6375
|
-
function reselectable(old, next) {
|
|
6376
|
-
const kept = (c) => encoded({
|
|
6377
|
-
...c,
|
|
6378
|
-
http: { ...c.http, port: 0 },
|
|
6379
|
-
tokenSource: null,
|
|
6380
|
-
tokenFile: null
|
|
6381
|
-
});
|
|
6382
|
-
return next.tokenSource === "config" && kept(old) === kept(next);
|
|
6383
|
-
}
|
|
6384
|
-
function readHostConfig(c) {
|
|
6385
|
-
const owner = c.configuration();
|
|
6386
|
-
if (!owner)
|
|
6387
|
-
return;
|
|
6388
|
-
const snapshot = c.files.read(c.path("host-config.json"));
|
|
6389
|
-
if (!snapshot)
|
|
6390
|
-
throw new ContractError("corrupt-selected");
|
|
6391
|
-
const config = decodeHostConfig(json(snapshot));
|
|
6392
|
-
if (!config.http.port || owner.endpoint !== localOrigin(config.http, config.http.port) || owner.policyIdentity !== hash2(encoded(config)))
|
|
6393
|
-
throw new ContractError("conflict");
|
|
6394
|
-
return config;
|
|
6395
|
-
}
|
|
6396
|
-
function hostEnvironment(ambient) {
|
|
6397
|
-
let route;
|
|
6398
|
-
const facts = networkSnapshot(ambient);
|
|
6399
|
-
try {
|
|
6400
|
-
if (facts.NODE_OPTIONS.present)
|
|
6401
|
-
throw new Error;
|
|
6402
|
-
route = sdkNetworkEnvironment(facts, networkSnapshot(undefined));
|
|
6403
|
-
} catch {
|
|
6404
|
-
throw new ContractError("unsupported-context");
|
|
6405
|
-
}
|
|
6406
|
-
const result = Object.create(null);
|
|
6407
|
-
for (const key of Object.keys(ambient)) {
|
|
6408
|
-
const d = Object.getOwnPropertyDescriptor(ambient, key);
|
|
6409
|
-
if (!d || !("value" in d) || typeof d.value !== "string")
|
|
6410
|
-
throw new ContractError("invalid");
|
|
6411
|
-
if (/^(?:CADENCE_|PI_|ABEL_|TRELLIS_|GIT_)/i.test(key) || networkKeys.some((name) => name === key))
|
|
6412
|
-
continue;
|
|
6413
|
-
result[key] = d.value;
|
|
6414
|
-
}
|
|
6415
|
-
if (route.redactionLiterals.length) {
|
|
6416
|
-
result.HTTP_PROXY = route.env.HTTP_PROXY;
|
|
6417
|
-
result.HTTPS_PROXY = route.env.HTTPS_PROXY;
|
|
6418
|
-
result.NODE_USE_ENV_PROXY = "1";
|
|
6419
|
-
}
|
|
6420
|
-
result.PI_OFFLINE = "1";
|
|
6421
|
-
result.NO_COLOR = "1";
|
|
6422
|
-
if (!sameNetwork(facts, networkSnapshot(ambient)))
|
|
6423
|
-
throw new ContractError("stale");
|
|
6424
|
-
return result;
|
|
6425
|
-
}
|
|
6426
|
-
function selectStart(options, ambient) {
|
|
6427
|
-
const workspace = resolveWorkspace(options.workspace), c = new ServiceCoordination(workspace.root, options.stateHome);
|
|
6428
|
-
const old = readHostConfig(c);
|
|
6429
|
-
const env = Object.getOwnPropertyDescriptor(ambient, "CADENCE_WEB_TOKEN");
|
|
6430
|
-
if (env && (!("value" in env) || typeof env.value !== "string"))
|
|
6431
|
-
throw new ContractError("invalid");
|
|
6432
|
-
let fromFile;
|
|
6433
|
-
if (options.config) {
|
|
6434
|
-
const path = resolve4(options.config), f = new PrivateFiles(dirname6(path));
|
|
6435
|
-
const bytes = f.read(path);
|
|
6436
|
-
if (!bytes)
|
|
6437
|
-
throw new ContractError("corrupt-selected");
|
|
6438
|
-
fromFile = decodeHostConfig(json(bytes));
|
|
6439
|
-
}
|
|
6440
|
-
const base = old ?? fromFile ?? decodeHostConfig({
|
|
6441
|
-
schema: "cadence-service-host/1",
|
|
6442
|
-
cwd: workspace.cwd,
|
|
6443
|
-
http: {
|
|
6444
|
-
schema: "cadence-service-http/1",
|
|
6445
|
-
host: options.host ?? "127.0.0.1",
|
|
6446
|
-
port: options.port ?? 17423,
|
|
6447
|
-
proxy: null
|
|
6448
|
-
},
|
|
6449
|
-
agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join8(ambient.HOME ?? ambient.USERPROFILE ?? homedir2(), ".pi", "agent")),
|
|
6450
|
-
tokenSource: options.tokenFile ? "file" : env ? "environment" : "generated",
|
|
6451
|
-
tokenFile: options.tokenFile ? resolve4(options.tokenFile) : null
|
|
6452
|
-
});
|
|
6453
|
-
const server = options.serverConfig !== undefined ? resolve4(options.serverConfig) : old?.tokenSource === "config" ? old.tokenFile : null;
|
|
6454
|
-
let selected = base;
|
|
6455
|
-
if (server) {
|
|
6456
|
-
if (options.tokenFile !== undefined)
|
|
6457
|
-
throw new ContractError("conflict");
|
|
6458
|
-
const { port } = readServerConfig(server).config;
|
|
6459
|
-
selected = decodeHostConfig({
|
|
6460
|
-
...base,
|
|
6461
|
-
http: { ...base.http, port },
|
|
6462
|
-
tokenSource: "config",
|
|
6463
|
-
tokenFile: server
|
|
6464
|
-
});
|
|
6465
|
-
}
|
|
6466
|
-
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 && resolve4(options.agentDir) !== selected.agentDir || options.tokenFile !== undefined && resolve4(options.tokenFile) !== selected.tokenFile || env && selected.tokenSource !== "environment" || selected.tokenSource === "environment" && !env || resolveWorkspace(selected.cwd).root !== workspace.root)
|
|
6467
|
-
throw new ContractError("conflict");
|
|
6468
|
-
return {
|
|
6469
|
-
workspace: workspace.root,
|
|
6470
|
-
stateHome: c.stateHome,
|
|
6471
|
-
config: selected,
|
|
6472
|
-
environmentToken: env?.value ?? null
|
|
6473
|
-
};
|
|
6474
|
-
}
|
|
6475
|
-
function decodeHostInput(value) {
|
|
6476
|
-
const o = closed(value, [
|
|
6477
|
-
"workspace",
|
|
6478
|
-
"stateHome",
|
|
6479
|
-
"config",
|
|
6480
|
-
"environmentToken"
|
|
6481
|
-
]);
|
|
6482
|
-
const config = decodeHostConfig(o.config);
|
|
6483
|
-
const environmentToken = o.environmentToken === null ? null : text(o.environmentToken, 1024, true);
|
|
6484
|
-
if (config.tokenSource === "environment" !== (environmentToken !== null))
|
|
6485
|
-
throw new ContractError("invalid");
|
|
6486
|
-
return {
|
|
6487
|
-
workspace: absolute(o.workspace),
|
|
6488
|
-
stateHome: absolute(o.stateHome),
|
|
6489
|
-
config,
|
|
6490
|
-
environmentToken
|
|
6491
|
-
};
|
|
6492
|
-
}
|
|
6493
|
-
var STARTUP_STAGES = [
|
|
6494
|
-
"package",
|
|
6495
|
-
"pi-agent-directory",
|
|
6496
|
-
"pi-auth",
|
|
6497
|
-
"pi-models",
|
|
6498
|
-
"pi-runtime",
|
|
6499
|
-
"service"
|
|
6500
|
-
];
|
|
6501
|
-
|
|
6502
|
-
class StartupRefusal extends ContractError {
|
|
6503
|
-
stage;
|
|
6504
|
-
quarantined;
|
|
6505
|
-
constructor(code2, stage, quarantined = false) {
|
|
6506
|
-
super(code2);
|
|
6507
|
-
this.stage = stage;
|
|
6508
|
-
this.quarantined = quarantined;
|
|
6509
|
-
}
|
|
6510
|
-
}
|
|
6511
|
-
function startupReport(error) {
|
|
6512
|
-
if (error instanceof StartupRefusal)
|
|
6513
|
-
return {
|
|
6514
|
-
status: error.quarantined ? "quarantined" : "refused",
|
|
6515
|
-
error: error.code,
|
|
6516
|
-
stage: error.stage
|
|
6517
|
-
};
|
|
6518
|
-
return {
|
|
6519
|
-
status: "refused",
|
|
6520
|
-
error: error instanceof ContractError ? error.code : "unavailable",
|
|
6521
|
-
stage: "service"
|
|
6522
|
-
};
|
|
6523
|
-
}
|
|
6524
|
-
function decodeStartupReport(value) {
|
|
6525
|
-
const o = closed(value, ["status", "error", "stage"]);
|
|
6526
|
-
return {
|
|
6527
|
-
status: oneOf(o.status, ["refused", "quarantined"]),
|
|
6528
|
-
error: oneOf(o.error, ERROR_CODES),
|
|
6529
|
-
stage: oneOf(o.stage, STARTUP_STAGES)
|
|
6530
|
-
};
|
|
6531
|
-
}
|
|
6532
|
-
|
|
6533
6383
|
// src/history/recovery.ts
|
|
6534
6384
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "node:crypto";
|
|
6535
6385
|
import { lstatSync as lstatSync7, opendirSync as opendirSync4 } from "node:fs";
|
|
6536
|
-
import { join as
|
|
6386
|
+
import { join as join9, relative as relative4 } from "node:path";
|
|
6537
6387
|
|
|
6538
6388
|
// src/service/recovery.ts
|
|
6539
6389
|
async function inspectRecovery(coordination, history) {
|
|
@@ -6565,7 +6415,7 @@ import {
|
|
|
6565
6415
|
statfsSync
|
|
6566
6416
|
} from "node:fs";
|
|
6567
6417
|
import { open } from "node:fs/promises";
|
|
6568
|
-
import { dirname as
|
|
6418
|
+
import { dirname as dirname7, join as join8 } from "node:path";
|
|
6569
6419
|
|
|
6570
6420
|
// src/history/admission.ts
|
|
6571
6421
|
function decodeRunAdmission(value) {
|
|
@@ -6594,13 +6444,13 @@ import {
|
|
|
6594
6444
|
openSync as openSync3,
|
|
6595
6445
|
readSync as readSync3
|
|
6596
6446
|
} from "node:fs";
|
|
6597
|
-
import { dirname as
|
|
6447
|
+
import { dirname as dirname6 } from "node:path";
|
|
6598
6448
|
function bad() {
|
|
6599
6449
|
throw new ContractError("corrupt-selected");
|
|
6600
6450
|
}
|
|
6601
6451
|
var stamp2 = (s) => [s.dev, s.ino, s.size, s.mode, s.uid, s.nlink, s.ctimeMs, s.mtimeMs].join(":");
|
|
6602
6452
|
function selected(files, path, read, walks) {
|
|
6603
|
-
if (!files.files.directory(
|
|
6453
|
+
if (!files.files.directory(dirname6(path), false, walks))
|
|
6604
6454
|
bad();
|
|
6605
6455
|
const before = lstatSync5(path);
|
|
6606
6456
|
if (!before.isFile() || before.nlink !== 1 || process.platform !== "win32" && (before.mode & 63 || process.getuid && before.uid !== process.getuid()))
|
|
@@ -7134,7 +6984,7 @@ async function retryStalled(read) {
|
|
|
7134
6984
|
} catch (error) {
|
|
7135
6985
|
if (!(error instanceof StalledQuantum) || retry >= STALL_RETRIES)
|
|
7136
6986
|
throw error;
|
|
7137
|
-
await new Promise((
|
|
6987
|
+
await new Promise((resolve4) => setImmediate(resolve4));
|
|
7138
6988
|
}
|
|
7139
6989
|
}
|
|
7140
6990
|
}
|
|
@@ -7155,14 +7005,14 @@ class HistoryFiles {
|
|
|
7155
7005
|
this.coordination = coordination;
|
|
7156
7006
|
this.format = format;
|
|
7157
7007
|
this.formatSnapshot = formatSnapshot;
|
|
7158
|
-
this.directory =
|
|
7008
|
+
this.directory = join8(coordination.directory, "history-v2");
|
|
7159
7009
|
this.files = coordination.files;
|
|
7160
7010
|
}
|
|
7161
7011
|
static open(coordination) {
|
|
7162
|
-
const dir =
|
|
7012
|
+
const dir = join8(coordination.directory, "history-v2");
|
|
7163
7013
|
if (!coordination.files.directory(dir))
|
|
7164
7014
|
return;
|
|
7165
|
-
const file = coordination.files.read(
|
|
7015
|
+
const file = coordination.files.read(join8(dir, "format.json"));
|
|
7166
7016
|
if (!file)
|
|
7167
7017
|
corrupt();
|
|
7168
7018
|
const format = decodeFormat(json(file));
|
|
@@ -7178,7 +7028,7 @@ class HistoryFiles {
|
|
|
7178
7028
|
existing.assertWritable();
|
|
7179
7029
|
return existing;
|
|
7180
7030
|
}
|
|
7181
|
-
const dir =
|
|
7031
|
+
const dir = join8(c.directory, "history-v2");
|
|
7182
7032
|
const format = {
|
|
7183
7033
|
schema: FORMAT,
|
|
7184
7034
|
storeId: randomUUID6(),
|
|
@@ -7188,19 +7038,19 @@ class HistoryFiles {
|
|
|
7188
7038
|
c.files.directory(dir, true);
|
|
7189
7039
|
for (const name of ["commands", "ledger", "runs", "indexes"]) {
|
|
7190
7040
|
lease.assert();
|
|
7191
|
-
c.files.directory(
|
|
7041
|
+
c.files.directory(join8(dir, name), true);
|
|
7192
7042
|
}
|
|
7193
7043
|
lease.assert();
|
|
7194
|
-
const keyPath =
|
|
7044
|
+
const keyPath = join8(dir, "read-proof-key.json");
|
|
7195
7045
|
c.files.create(keyPath, encoded({
|
|
7196
7046
|
schema: "cadence-history-read-key/1",
|
|
7197
7047
|
storeId: format.storeId,
|
|
7198
7048
|
key: randomBytes4(32).toString("base64url")
|
|
7199
7049
|
}));
|
|
7200
7050
|
lease.assert();
|
|
7201
|
-
const snapshot = c.files.create(
|
|
7051
|
+
const snapshot = c.files.create(join8(dir, "format.json"), encoded(format));
|
|
7202
7052
|
const result = new HistoryFiles(c, format, snapshot);
|
|
7203
|
-
result.syncParents(
|
|
7053
|
+
result.syncParents(join8(dir, "format.json"));
|
|
7204
7054
|
result.assertWritable();
|
|
7205
7055
|
return result;
|
|
7206
7056
|
}
|
|
@@ -7235,7 +7085,7 @@ class HistoryFiles {
|
|
|
7235
7085
|
throw new ContractError("quarantine");
|
|
7236
7086
|
}
|
|
7237
7087
|
assertFormat() {
|
|
7238
|
-
this.files.assert(
|
|
7088
|
+
this.files.assert(join8(this.directory, "format.json"), this.formatSnapshot);
|
|
7239
7089
|
}
|
|
7240
7090
|
readProofMaterial(budget) {
|
|
7241
7091
|
if (this.format.schema !== FORMAT)
|
|
@@ -7244,7 +7094,7 @@ class HistoryFiles {
|
|
|
7244
7094
|
this.assertReadFormat(budget);
|
|
7245
7095
|
else
|
|
7246
7096
|
this.assertFormat();
|
|
7247
|
-
const selected2 = this.readBudgeted(
|
|
7097
|
+
const selected2 = this.readBudgeted(join8(this.directory, "read-proof-key.json"), 512, budget);
|
|
7248
7098
|
if (!selected2 || this.proofSnapshot && (selected2.identity !== this.proofSnapshot.identity || selected2.stamp !== this.proofSnapshot.stamp))
|
|
7249
7099
|
corrupt();
|
|
7250
7100
|
try {
|
|
@@ -7272,17 +7122,17 @@ class HistoryFiles {
|
|
|
7272
7122
|
return;
|
|
7273
7123
|
}
|
|
7274
7124
|
this.assertFormat();
|
|
7275
|
-
assertIncarnation(this,
|
|
7125
|
+
assertIncarnation(this, join8(this.directory, "read-proof-key.json"), this.proofSnapshot.stamp);
|
|
7276
7126
|
}
|
|
7277
7127
|
segmentPath(segment) {
|
|
7278
|
-
return
|
|
7128
|
+
return join8(this.directory, "ledger", `${String(segment).padStart(12, "0")}.jsonl`);
|
|
7279
7129
|
}
|
|
7280
7130
|
artifactPath(ref) {
|
|
7281
|
-
return
|
|
7131
|
+
return join8(this.directory, "runs", ref.runId, "artifacts", ref.artifactId, `${ref.part}.part`);
|
|
7282
7132
|
}
|
|
7283
7133
|
admissionPath(runId) {
|
|
7284
7134
|
decodeId(runId);
|
|
7285
|
-
return
|
|
7135
|
+
return join8(this.directory, "runs", runId, "admission.json");
|
|
7286
7136
|
}
|
|
7287
7137
|
writeAdmission(lease, input) {
|
|
7288
7138
|
lease.assert();
|
|
@@ -7308,7 +7158,7 @@ class HistoryFiles {
|
|
|
7308
7158
|
readCommand(commandId, budget) {
|
|
7309
7159
|
decodeId(commandId);
|
|
7310
7160
|
this.assertReadFormat(budget);
|
|
7311
|
-
return this.readBudgeted(
|
|
7161
|
+
return this.readBudgeted(join8(this.directory, "commands", commandId, "intent.json"), RECORD_BYTES, budget);
|
|
7312
7162
|
}
|
|
7313
7163
|
readAdmission(runId, identity, budget) {
|
|
7314
7164
|
return this.readAdmissionIdentity(runId, hash2(canonicalJson(identity)), budget);
|
|
@@ -7329,7 +7179,7 @@ class HistoryFiles {
|
|
|
7329
7179
|
syncParents(file) {
|
|
7330
7180
|
if (process.platform === "win32")
|
|
7331
7181
|
return;
|
|
7332
|
-
for (let path =
|
|
7182
|
+
for (let path = dirname7(file);; path = dirname7(path)) {
|
|
7333
7183
|
if (!this.files.directory(path))
|
|
7334
7184
|
corrupt();
|
|
7335
7185
|
const fd = openSync4(path, constants4.O_RDONLY | constants4.O_DIRECTORY);
|
|
@@ -7340,13 +7190,13 @@ class HistoryFiles {
|
|
|
7340
7190
|
}
|
|
7341
7191
|
if (path === this.coordination.directory)
|
|
7342
7192
|
break;
|
|
7343
|
-
if (path ===
|
|
7193
|
+
if (path === dirname7(path))
|
|
7344
7194
|
corrupt();
|
|
7345
7195
|
}
|
|
7346
7196
|
}
|
|
7347
7197
|
assertReadFormat(budget) {
|
|
7348
7198
|
budget.reserve(this.formatSnapshot.bytes.length);
|
|
7349
|
-
this.files.assert(
|
|
7199
|
+
this.files.assert(join8(this.directory, "format.json"), this.formatSnapshot, this.formatSnapshot.bytes.length);
|
|
7350
7200
|
}
|
|
7351
7201
|
assertPublished(segment, end, seq) {
|
|
7352
7202
|
const fence = publicationFences.get(this.directory);
|
|
@@ -7384,7 +7234,7 @@ class HistoryFiles {
|
|
|
7384
7234
|
const dir = opendirSync3(path);
|
|
7385
7235
|
try {
|
|
7386
7236
|
for (let e = dir.readSync();e; e = dir.readSync()) {
|
|
7387
|
-
const p =
|
|
7237
|
+
const p = join8(path, e.name), st = lstatSync6(p);
|
|
7388
7238
|
if (st.isDirectory())
|
|
7389
7239
|
bytes += walk(p, depth + 1);
|
|
7390
7240
|
else {
|
|
@@ -7419,7 +7269,7 @@ class HistoryFiles {
|
|
|
7419
7269
|
state.bytes = 0;
|
|
7420
7270
|
state.identity = null;
|
|
7421
7271
|
let count = 0;
|
|
7422
|
-
const ledger =
|
|
7272
|
+
const ledger = join8(this.directory, "ledger");
|
|
7423
7273
|
if (!this.files.directory(ledger))
|
|
7424
7274
|
corrupt();
|
|
7425
7275
|
const dir = opendirSync3(ledger);
|
|
@@ -7736,7 +7586,7 @@ class HistoryFiles {
|
|
|
7736
7586
|
checkpoint(lease, state) {
|
|
7737
7587
|
lease.assert();
|
|
7738
7588
|
this.assertWritable();
|
|
7739
|
-
const path =
|
|
7589
|
+
const path = join8(this.directory, "checkpoint.json"), old = this.files.read(path);
|
|
7740
7590
|
const body = encoded({
|
|
7741
7591
|
schema: "cadence-history-checkpoint/2",
|
|
7742
7592
|
storeId: this.format.storeId,
|
|
@@ -8343,7 +8193,7 @@ function digestHistory(files, exclude) {
|
|
|
8343
8193
|
dir.closeSync();
|
|
8344
8194
|
}
|
|
8345
8195
|
for (const name of names2.sort()) {
|
|
8346
|
-
const selected2 =
|
|
8196
|
+
const selected2 = join9(path, name);
|
|
8347
8197
|
if (selected2 === exclude)
|
|
8348
8198
|
continue;
|
|
8349
8199
|
const rel = relative4(files.directory, selected2);
|
|
@@ -8531,6 +8381,201 @@ class HistoryRecovery {
|
|
|
8531
8381
|
}
|
|
8532
8382
|
}
|
|
8533
8383
|
|
|
8384
|
+
// src/service/runtime.ts
|
|
8385
|
+
import { homedir as homedir2 } from "node:os";
|
|
8386
|
+
import { dirname as dirname8, isAbsolute as isAbsolute5, join as join10, resolve as resolve4 } from "node:path";
|
|
8387
|
+
var STARTUP_INPUT_BYTES = 16 * 1024;
|
|
8388
|
+
function absolute(value) {
|
|
8389
|
+
const result = text(value, 4096, true);
|
|
8390
|
+
if (!isAbsolute5(result))
|
|
8391
|
+
throw new ContractError("invalid");
|
|
8392
|
+
return result;
|
|
8393
|
+
}
|
|
8394
|
+
function decodeHostConfig(value) {
|
|
8395
|
+
const o = closed(value, [
|
|
8396
|
+
"schema",
|
|
8397
|
+
"http",
|
|
8398
|
+
"cwd",
|
|
8399
|
+
"agentDir",
|
|
8400
|
+
"tokenSource",
|
|
8401
|
+
"tokenFile"
|
|
8402
|
+
]);
|
|
8403
|
+
const tokenSource = oneOf(o.tokenSource, [
|
|
8404
|
+
"generated",
|
|
8405
|
+
"environment",
|
|
8406
|
+
"file",
|
|
8407
|
+
"config"
|
|
8408
|
+
]);
|
|
8409
|
+
const tokenFile = o.tokenFile === null ? null : absolute(o.tokenFile);
|
|
8410
|
+
if ((tokenSource === "file" || tokenSource === "config") !== !!tokenFile)
|
|
8411
|
+
throw new ContractError("invalid");
|
|
8412
|
+
return {
|
|
8413
|
+
schema: literal(o.schema, "cadence-service-host/1"),
|
|
8414
|
+
http: decodeHttpPolicy(o.http),
|
|
8415
|
+
cwd: absolute(o.cwd),
|
|
8416
|
+
agentDir: absolute(o.agentDir),
|
|
8417
|
+
tokenSource,
|
|
8418
|
+
tokenFile
|
|
8419
|
+
};
|
|
8420
|
+
}
|
|
8421
|
+
function reselectable(old, next) {
|
|
8422
|
+
const kept = (c) => encoded({
|
|
8423
|
+
...c,
|
|
8424
|
+
http: { ...c.http, port: 0 },
|
|
8425
|
+
tokenSource: null,
|
|
8426
|
+
tokenFile: null
|
|
8427
|
+
});
|
|
8428
|
+
return next.tokenSource === "config" && kept(old) === kept(next);
|
|
8429
|
+
}
|
|
8430
|
+
function readHostConfig(c) {
|
|
8431
|
+
const owner = c.configuration();
|
|
8432
|
+
if (!owner)
|
|
8433
|
+
return;
|
|
8434
|
+
const snapshot = c.files.read(c.path("host-config.json"));
|
|
8435
|
+
if (!snapshot)
|
|
8436
|
+
throw new ContractError("corrupt-selected");
|
|
8437
|
+
const config = decodeHostConfig(json(snapshot));
|
|
8438
|
+
if (!config.http.port || owner.endpoint !== localOrigin(config.http, config.http.port) || owner.policyIdentity !== hash2(encoded(config)))
|
|
8439
|
+
throw new ContractError("conflict");
|
|
8440
|
+
return config;
|
|
8441
|
+
}
|
|
8442
|
+
function hostEnvironment(ambient) {
|
|
8443
|
+
let route;
|
|
8444
|
+
const facts = networkSnapshot(ambient);
|
|
8445
|
+
try {
|
|
8446
|
+
if (facts.NODE_OPTIONS.present)
|
|
8447
|
+
throw new Error;
|
|
8448
|
+
route = sdkNetworkEnvironment(facts, networkSnapshot(undefined));
|
|
8449
|
+
} catch {
|
|
8450
|
+
throw new ContractError("unsupported-context");
|
|
8451
|
+
}
|
|
8452
|
+
const result = Object.create(null);
|
|
8453
|
+
for (const key of Object.keys(ambient)) {
|
|
8454
|
+
const d = Object.getOwnPropertyDescriptor(ambient, key);
|
|
8455
|
+
if (!d || !("value" in d) || typeof d.value !== "string")
|
|
8456
|
+
throw new ContractError("invalid");
|
|
8457
|
+
if (/^(?:CADENCE_|PI_|ABEL_|TRELLIS_|GIT_)/i.test(key) || networkKeys.some((name) => name === key))
|
|
8458
|
+
continue;
|
|
8459
|
+
result[key] = d.value;
|
|
8460
|
+
}
|
|
8461
|
+
if (route.redactionLiterals.length) {
|
|
8462
|
+
result.HTTP_PROXY = route.env.HTTP_PROXY;
|
|
8463
|
+
result.HTTPS_PROXY = route.env.HTTPS_PROXY;
|
|
8464
|
+
result.NODE_USE_ENV_PROXY = "1";
|
|
8465
|
+
}
|
|
8466
|
+
result.PI_OFFLINE = "1";
|
|
8467
|
+
result.NO_COLOR = "1";
|
|
8468
|
+
if (!sameNetwork(facts, networkSnapshot(ambient)))
|
|
8469
|
+
throw new ContractError("stale");
|
|
8470
|
+
return result;
|
|
8471
|
+
}
|
|
8472
|
+
function selectStart(options, ambient) {
|
|
8473
|
+
const workspace = resolveWorkspace(options.workspace), c = new ServiceCoordination(workspace.root, options.stateHome);
|
|
8474
|
+
const old = readHostConfig(c);
|
|
8475
|
+
const env = Object.getOwnPropertyDescriptor(ambient, "CADENCE_WEB_TOKEN");
|
|
8476
|
+
if (env && (!("value" in env) || typeof env.value !== "string"))
|
|
8477
|
+
throw new ContractError("invalid");
|
|
8478
|
+
let fromFile;
|
|
8479
|
+
if (options.config) {
|
|
8480
|
+
const path = resolve4(options.config), f = new PrivateFiles(dirname8(path));
|
|
8481
|
+
const bytes = f.read(path);
|
|
8482
|
+
if (!bytes)
|
|
8483
|
+
throw new ContractError("corrupt-selected");
|
|
8484
|
+
fromFile = decodeHostConfig(json(bytes));
|
|
8485
|
+
}
|
|
8486
|
+
const base = old ?? fromFile ?? decodeHostConfig({
|
|
8487
|
+
schema: "cadence-service-host/1",
|
|
8488
|
+
cwd: workspace.cwd,
|
|
8489
|
+
http: {
|
|
8490
|
+
schema: "cadence-service-http/1",
|
|
8491
|
+
host: options.host ?? "127.0.0.1",
|
|
8492
|
+
port: options.port ?? 17423,
|
|
8493
|
+
proxy: null
|
|
8494
|
+
},
|
|
8495
|
+
agentDir: resolve4(options.agentDir ?? ambient.PI_CODING_AGENT_DIR ?? join10(ambient.HOME ?? ambient.USERPROFILE ?? homedir2(), ".pi", "agent")),
|
|
8496
|
+
tokenSource: options.tokenFile ? "file" : env ? "environment" : "generated",
|
|
8497
|
+
tokenFile: options.tokenFile ? resolve4(options.tokenFile) : null
|
|
8498
|
+
});
|
|
8499
|
+
const server = options.serverConfig !== undefined ? resolve4(options.serverConfig) : old?.tokenSource === "config" ? old.tokenFile : null;
|
|
8500
|
+
let selected2 = base;
|
|
8501
|
+
if (server) {
|
|
8502
|
+
if (options.tokenFile !== undefined)
|
|
8503
|
+
throw new ContractError("conflict");
|
|
8504
|
+
const { port } = readServerConfig(server).config;
|
|
8505
|
+
selected2 = decodeHostConfig({
|
|
8506
|
+
...base,
|
|
8507
|
+
http: { ...base.http, port },
|
|
8508
|
+
tokenSource: "config",
|
|
8509
|
+
tokenFile: server
|
|
8510
|
+
});
|
|
8511
|
+
}
|
|
8512
|
+
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)
|
|
8513
|
+
throw new ContractError("conflict");
|
|
8514
|
+
return {
|
|
8515
|
+
workspace: workspace.root,
|
|
8516
|
+
stateHome: c.stateHome,
|
|
8517
|
+
config: selected2,
|
|
8518
|
+
environmentToken: env?.value ?? null
|
|
8519
|
+
};
|
|
8520
|
+
}
|
|
8521
|
+
function decodeHostInput(value) {
|
|
8522
|
+
const o = closed(value, [
|
|
8523
|
+
"workspace",
|
|
8524
|
+
"stateHome",
|
|
8525
|
+
"config",
|
|
8526
|
+
"environmentToken"
|
|
8527
|
+
]);
|
|
8528
|
+
const config = decodeHostConfig(o.config);
|
|
8529
|
+
const environmentToken = o.environmentToken === null ? null : text(o.environmentToken, 1024, true);
|
|
8530
|
+
if (config.tokenSource === "environment" !== (environmentToken !== null))
|
|
8531
|
+
throw new ContractError("invalid");
|
|
8532
|
+
return {
|
|
8533
|
+
workspace: absolute(o.workspace),
|
|
8534
|
+
stateHome: absolute(o.stateHome),
|
|
8535
|
+
config,
|
|
8536
|
+
environmentToken
|
|
8537
|
+
};
|
|
8538
|
+
}
|
|
8539
|
+
var STARTUP_STAGES = [
|
|
8540
|
+
"package",
|
|
8541
|
+
"pi-agent-directory",
|
|
8542
|
+
"pi-auth",
|
|
8543
|
+
"pi-models",
|
|
8544
|
+
"pi-runtime",
|
|
8545
|
+
"service"
|
|
8546
|
+
];
|
|
8547
|
+
|
|
8548
|
+
class StartupRefusal extends ContractError {
|
|
8549
|
+
stage;
|
|
8550
|
+
quarantined;
|
|
8551
|
+
constructor(code2, stage, quarantined = false) {
|
|
8552
|
+
super(code2);
|
|
8553
|
+
this.stage = stage;
|
|
8554
|
+
this.quarantined = quarantined;
|
|
8555
|
+
}
|
|
8556
|
+
}
|
|
8557
|
+
function startupReport(error) {
|
|
8558
|
+
if (error instanceof StartupRefusal)
|
|
8559
|
+
return {
|
|
8560
|
+
status: error.quarantined ? "quarantined" : "refused",
|
|
8561
|
+
error: error.code,
|
|
8562
|
+
stage: error.stage
|
|
8563
|
+
};
|
|
8564
|
+
return {
|
|
8565
|
+
status: "refused",
|
|
8566
|
+
error: error instanceof ContractError ? error.code : "unavailable",
|
|
8567
|
+
stage: "service"
|
|
8568
|
+
};
|
|
8569
|
+
}
|
|
8570
|
+
function decodeStartupReport(value) {
|
|
8571
|
+
const o = closed(value, ["status", "error", "stage"]);
|
|
8572
|
+
return {
|
|
8573
|
+
status: oneOf(o.status, ["refused", "quarantined"]),
|
|
8574
|
+
error: oneOf(o.error, ERROR_CODES),
|
|
8575
|
+
stage: oneOf(o.stage, STARTUP_STAGES)
|
|
8576
|
+
};
|
|
8577
|
+
}
|
|
8578
|
+
|
|
8534
8579
|
// src/service/cli.ts
|
|
8535
8580
|
function nativeCall(c, operation, reveal = false, controlBody) {
|
|
8536
8581
|
const access = c.localAccess();
|
|
@@ -8625,7 +8670,12 @@ async function stopService(c) {
|
|
|
8625
8670
|
}
|
|
8626
8671
|
const deadline = performance.now() + LIMITS.stopWaitMs;
|
|
8627
8672
|
while (performance.now() < deadline) {
|
|
8628
|
-
|
|
8673
|
+
let observed;
|
|
8674
|
+
try {
|
|
8675
|
+
observed = c.inspect();
|
|
8676
|
+
} catch {
|
|
8677
|
+
observed = { status: "unavailable" };
|
|
8678
|
+
}
|
|
8629
8679
|
if ("epoch" in observed && observed.epoch === epoch && observed.status === "stopped")
|
|
8630
8680
|
return { status: "stopped", epoch };
|
|
8631
8681
|
if (observed.status === "quarantined")
|
|
@@ -8639,6 +8689,15 @@ async function stopService(c) {
|
|
|
8639
8689
|
function failedStatus(result) {
|
|
8640
8690
|
return !!(result && typeof result === "object" && ("status" in result) && ["unconfirmed", "unavailable", "quarantined", "refused"].includes(String(result.status)));
|
|
8641
8691
|
}
|
|
8692
|
+
function managementExitCode(result, foreground = false) {
|
|
8693
|
+
if (foreground) {
|
|
8694
|
+
try {
|
|
8695
|
+
decodeStartupReport(result);
|
|
8696
|
+
return 78;
|
|
8697
|
+
} catch {}
|
|
8698
|
+
}
|
|
8699
|
+
return 1;
|
|
8700
|
+
}
|
|
8642
8701
|
function serverConfigHint(error) {
|
|
8643
8702
|
return `Cadence changed nothing: ${error.path} ${error.reason}.
|
|
8644
8703
|
Fix it and run the command again; the file holds the WebUI port and token as ${PORT_KEY}=<1-65535> and ${TOKEN_KEY}=<at least 32 characters>, one line each.
|
|
@@ -8801,6 +8860,8 @@ async function launchHost(input, c, foreground, hostUrl, cwd, env, network) {
|
|
|
8801
8860
|
process.stdout.write(`${JSON.stringify(result)}
|
|
8802
8861
|
`);
|
|
8803
8862
|
await closure;
|
|
8863
|
+
if (failedStatus(result))
|
|
8864
|
+
process.exitCode = managementExitCode(result, true);
|
|
8804
8865
|
return null;
|
|
8805
8866
|
}
|
|
8806
8867
|
return result;
|
|
@@ -9709,8 +9770,9 @@ import {
|
|
|
9709
9770
|
opendirSync as opendirSync5,
|
|
9710
9771
|
openSync as openSync5,
|
|
9711
9772
|
readSync as readSync4,
|
|
9712
|
-
realpathSync as
|
|
9773
|
+
realpathSync as realpathSync3
|
|
9713
9774
|
} from "node:fs";
|
|
9775
|
+
import { homedir as homedir4 } from "node:os";
|
|
9714
9776
|
import { dirname as dirname10, isAbsolute as isAbsolute6, join as join12, relative as relative5, resolve as resolve6, sep as sep4 } from "node:path";
|
|
9715
9777
|
|
|
9716
9778
|
// src/worker-redaction.mjs
|
|
@@ -10084,7 +10146,7 @@ class WorkbenchFiles {
|
|
|
10084
10146
|
excluded;
|
|
10085
10147
|
constructor(root, excluded, secrets) {
|
|
10086
10148
|
this.secrets = secrets;
|
|
10087
|
-
this.root =
|
|
10149
|
+
this.root = realpathSync3(root);
|
|
10088
10150
|
this.excluded = excluded.flatMap((path) => [
|
|
10089
10151
|
resolve6(path),
|
|
10090
10152
|
canonicalAmbientRoot(path)
|
|
@@ -10100,7 +10162,7 @@ class WorkbenchFiles {
|
|
|
10100
10162
|
directory(path) {
|
|
10101
10163
|
if (!isAbsolute6(path) || !safeDirectory(path))
|
|
10102
10164
|
throw new ContractError("invalid");
|
|
10103
|
-
const canonical =
|
|
10165
|
+
const canonical = realpathSync3(path);
|
|
10104
10166
|
this.allowed(canonical);
|
|
10105
10167
|
return canonical;
|
|
10106
10168
|
}
|
|
@@ -10115,8 +10177,18 @@ class WorkbenchFiles {
|
|
|
10115
10177
|
throw new ContractError("forbidden");
|
|
10116
10178
|
return target;
|
|
10117
10179
|
}
|
|
10180
|
+
browsed(path) {
|
|
10181
|
+
const full = /^~(?:[\\/]|$)/.test(path) ? join12(homedir4(), path.slice(1)) : path || this.root;
|
|
10182
|
+
try {
|
|
10183
|
+
return this.directory(full);
|
|
10184
|
+
} catch (error) {
|
|
10185
|
+
if (error instanceof Error && error.message === "Unsafe directory")
|
|
10186
|
+
throw new ContractError("forbidden");
|
|
10187
|
+
throw error;
|
|
10188
|
+
}
|
|
10189
|
+
}
|
|
10118
10190
|
list(path, directories = false) {
|
|
10119
|
-
const selected2 = directories ? this.
|
|
10191
|
+
const selected2 = directories ? this.browsed(path) : this.selected(path);
|
|
10120
10192
|
if (!safeDirectory(selected2) || sanitizeText(selected2, this.secrets()) !== selected2)
|
|
10121
10193
|
throw new ContractError("forbidden");
|
|
10122
10194
|
const entries = [], dir = opendirSync5(selected2);
|
|
@@ -10309,7 +10381,7 @@ Content-Length: 0\r
|
|
|
10309
10381
|
server.close();
|
|
10310
10382
|
throw error;
|
|
10311
10383
|
}
|
|
10312
|
-
const files = new WorkbenchFiles(
|
|
10384
|
+
const files = new WorkbenchFiles(homedir5(), [store.files.namespace, input.agentDir], () => acquired.access.redactionLiterals());
|
|
10313
10385
|
const assert = () => {
|
|
10314
10386
|
store.assert();
|
|
10315
10387
|
if (!accepting)
|
|
@@ -10378,7 +10450,7 @@ Content-Length: 0\r
|
|
|
10378
10450
|
return result;
|
|
10379
10451
|
}
|
|
10380
10452
|
function binding(id) {
|
|
10381
|
-
const project = store.
|
|
10453
|
+
const project = store.associatedProjects().find((p) => p.id === id);
|
|
10382
10454
|
if (!project)
|
|
10383
10455
|
throw new ContractError("unavailable");
|
|
10384
10456
|
const c = new ServiceCoordination(project.path, input.stateHome), local = c.localAccess();
|
|
@@ -10421,7 +10493,7 @@ Content-Length: 0\r
|
|
|
10421
10493
|
const originals = await Promise.allSettled([...opens.values()].map((o) => o.result));
|
|
10422
10494
|
const uncertainOpening = originals.some((r) => r.status === "rejected" && !(r.reason instanceof ProjectOpenRefusal && r.reason.noOwner));
|
|
10423
10495
|
const retired = await Promise.allSettled([...retirements]);
|
|
10424
|
-
const targets = store.
|
|
10496
|
+
const targets = store.associatedProjects();
|
|
10425
10497
|
await joinProjectStops(targets, async (p) => {
|
|
10426
10498
|
const c = new ServiceCoordination(p.path, input.stateHome), observed = await serviceStatus(c);
|
|
10427
10499
|
const status = observed && typeof observed === "object" && "status" in observed ? observed.status : "unavailable";
|
|
@@ -10669,7 +10741,7 @@ Content-Length: 0\r
|
|
|
10669
10741
|
kind: "workbench",
|
|
10670
10742
|
epoch: auth.epoch,
|
|
10671
10743
|
csrf: auth.csrf,
|
|
10672
|
-
home:
|
|
10744
|
+
home: homedir5(),
|
|
10673
10745
|
projects: store.projects(),
|
|
10674
10746
|
build: build2
|
|
10675
10747
|
});
|
|
@@ -17317,7 +17389,7 @@ import {
|
|
|
17317
17389
|
constants as constants6,
|
|
17318
17390
|
fsyncSync as fsyncSync4,
|
|
17319
17391
|
openSync as openSync6,
|
|
17320
|
-
realpathSync as
|
|
17392
|
+
realpathSync as realpathSync4
|
|
17321
17393
|
} from "node:fs";
|
|
17322
17394
|
import { dirname as dirname12, isAbsolute as isAbsolute7, join as join16, relative as relative6, sep as sep5 } from "node:path";
|
|
17323
17395
|
function summary(text2, maximum, runId) {
|
|
@@ -17368,7 +17440,7 @@ function publicationRecord(store, reader, intent, link, run, request3, expected,
|
|
|
17368
17440
|
else {
|
|
17369
17441
|
if (action.kind !== "create" || !request3.cwd || store.list().length >= 1000)
|
|
17370
17442
|
bad11();
|
|
17371
|
-
const cwd =
|
|
17443
|
+
const cwd = realpathSync4(request3.cwd), within = relative6(store.root, cwd);
|
|
17372
17444
|
if (isAbsolute7(within) || within === ".." || within.startsWith(`..${sep5}`))
|
|
17373
17445
|
bad11();
|
|
17374
17446
|
record2 = {
|
|
@@ -19446,7 +19518,7 @@ class OwnerModelChoice {
|
|
|
19446
19518
|
}
|
|
19447
19519
|
|
|
19448
19520
|
// src/service/model-config.ts
|
|
19449
|
-
import { lstatSync as lstatSync11, realpathSync as
|
|
19521
|
+
import { lstatSync as lstatSync11, realpathSync as realpathSync5 } from "node:fs";
|
|
19450
19522
|
import { dirname as dirname13, isAbsolute as isAbsolute8, join as join18, resolve as resolve7 } from "node:path";
|
|
19451
19523
|
var CONFIG_BYTES2 = 1024 * 1024;
|
|
19452
19524
|
var ENTRY_LIMIT = 256;
|
|
@@ -19520,7 +19592,7 @@ function piSource(agentDir, home, cwd, env) {
|
|
|
19520
19592
|
if (selected2 === "~" || selected2.startsWith("~/"))
|
|
19521
19593
|
selected2 = home + selected2.slice(1);
|
|
19522
19594
|
selected2 = resolve7(cwd, selected2);
|
|
19523
|
-
const directory = join18(
|
|
19595
|
+
const directory = join18(realpathSync5(dirname13(selected2)), selected2.slice(dirname13(selected2).length + 1));
|
|
19524
19596
|
if (!lstatSync11(directory).isDirectory() || lstatSync11(directory).isSymbolicLink())
|
|
19525
19597
|
refuse2();
|
|
19526
19598
|
const files = new PrivateFiles(directory, false);
|
|
@@ -20958,7 +21030,7 @@ class ServiceOwner {
|
|
|
20958
21030
|
|
|
20959
21031
|
// src/service/task-actions.ts
|
|
20960
21032
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
20961
|
-
import { realpathSync as
|
|
21033
|
+
import { realpathSync as realpathSync6 } from "node:fs";
|
|
20962
21034
|
|
|
20963
21035
|
// src/task-diff.ts
|
|
20964
21036
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -24132,9 +24204,9 @@ class TaskActions {
|
|
|
24132
24204
|
selection: structuredClone(options.selection),
|
|
24133
24205
|
ownerSecrets: [...options.ownerSecrets]
|
|
24134
24206
|
};
|
|
24135
|
-
this.cwd =
|
|
24207
|
+
this.cwd = realpathSync6(options.cwd);
|
|
24136
24208
|
const config = readHostConfig(options.host.coordination);
|
|
24137
|
-
if (!config ||
|
|
24209
|
+
if (!config || realpathSync6(config.cwd) !== this.cwd)
|
|
24138
24210
|
throw new ContractError("unsupported-context");
|
|
24139
24211
|
this.launchCwd = config.cwd;
|
|
24140
24212
|
this.checkEnvironment = options.host.checkEnvironment;
|
|
@@ -24258,7 +24330,7 @@ class TaskActions {
|
|
|
24258
24330
|
if (this.closed || this.options.host.owner.state !== "idle" || this.options.host.writer.quarantined)
|
|
24259
24331
|
throw new ContractError("quarantine");
|
|
24260
24332
|
this.options.host.owner.lease.assert();
|
|
24261
|
-
if (!this.sources.current() ||
|
|
24333
|
+
if (!this.sources.current() || realpathSync6(this.cwd) !== this.cwd || realpathSync6(this.launchCwd) !== this.cwd || this.options.host.checkEnvironment !== this.checkEnvironment)
|
|
24262
24334
|
throw new ContractError("stale");
|
|
24263
24335
|
}
|
|
24264
24336
|
choiceSnapshot() {
|
|
@@ -24374,7 +24446,7 @@ class TaskActions {
|
|
|
24374
24446
|
const access = this.currentAccess();
|
|
24375
24447
|
const committedSeq = snapshot ?? h.writer.committedSeq;
|
|
24376
24448
|
const selected2 = taskId ? h.coordination.store.loadSnapshot(taskId) : null;
|
|
24377
|
-
if (selected2 &&
|
|
24449
|
+
if (selected2 && realpathSync6(selected2.record.cwd) !== this.cwd)
|
|
24378
24450
|
throw new ContractError("unsupported-context");
|
|
24379
24451
|
const secrets = this.viewSecrets();
|
|
24380
24452
|
const roles = this.sources.roles.map(({ role: role2, sha256 }) => ({
|
|
@@ -24880,7 +24952,7 @@ class TaskActions {
|
|
|
24880
24952
|
this.assertChoice(expected);
|
|
24881
24953
|
held.assertHeld();
|
|
24882
24954
|
const actual = h.coordination.store.loadSnapshot(task.id), want = published ?? task;
|
|
24883
|
-
if (!actual || actual.identity !== want.identity || actual.record.revision !== want.revision ||
|
|
24955
|
+
if (!actual || actual.identity !== want.identity || actual.record.revision !== want.revision || realpathSync6(actual.record.cwd) !== this.cwd || this.generation !== expected.checkoutGeneration || h.writer.controlRevision(task.id) !== expected.controlRev + 1 || this.scheduler.activeTask(task.id))
|
|
24884
24956
|
throw new ContractError("stale");
|
|
24885
24957
|
const changes2 = captureChanges(h.coordination.store.root);
|
|
24886
24958
|
if (changes2.fingerprint !== expected.observation.identity || changes2.complete !== expected.observation.complete)
|
|
@@ -25065,7 +25137,7 @@ import { join as join23 } from "node:path";
|
|
|
25065
25137
|
|
|
25066
25138
|
// src/service/conversations.ts
|
|
25067
25139
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
25068
|
-
import { lstatSync as lstatSync13, opendirSync as opendirSync7, realpathSync as
|
|
25140
|
+
import { lstatSync as lstatSync13, opendirSync as opendirSync7, realpathSync as realpathSync7, statfsSync as statfsSync2 } from "node:fs";
|
|
25069
25141
|
import { dirname as dirname14, join as join22 } from "node:path";
|
|
25070
25142
|
var TURN_BYTES = 384 * 1024;
|
|
25071
25143
|
var META_BYTES = 64 * 1024;
|
|
@@ -25186,7 +25258,7 @@ class Conversations {
|
|
|
25186
25258
|
constructor(o) {
|
|
25187
25259
|
this.o = o;
|
|
25188
25260
|
this.root = join22(o.coordination.directory, "conversations-v1");
|
|
25189
|
-
this.cwd =
|
|
25261
|
+
this.cwd = realpathSync7(o.cwd);
|
|
25190
25262
|
}
|
|
25191
25263
|
currentDirectory() {
|
|
25192
25264
|
const current2 = resolveWorkspace(this.o.cwd);
|