@byok-sdk/client 0.1.1 → 0.2.0
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 +4 -4
- package/dist/adapters/claude/resolve-bin.d.ts +2 -2
- package/dist/adapters/codex/codex-adapter.d.ts +4 -3
- package/dist/adapters/index.js +79 -44
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/pi/events.d.ts +10 -37
- package/dist/adapters/pi/permission-mapping.d.ts +4 -20
- package/dist/adapters/pi/resolve-bin.d.ts +18 -14
- package/dist/adapters/pi/rpc-client.d.ts +1 -4
- package/dist/bin/byok-agent.js +155 -119
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/index.js +135 -100
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/create-service-lifecycle.d.ts +2 -2
- package/package.json +6 -5
package/dist/bin/byok-agent.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFile, spawn, spawnSync } from 'child_process';
|
|
3
3
|
import { randomUUID, createHash, randomBytes, timingSafeEqual, createPrivateKey, generateKeyPairSync, sign, createHmac } from 'crypto';
|
|
4
|
-
import { readFileSync, promises, linkSync, fstatSync, lstatSync, unlinkSync, constants, readSync, openSync, writeFileSync, fchmodSync, fsyncSync, closeSync, opendirSync, realpathSync, mkdirSync,
|
|
5
|
-
import
|
|
4
|
+
import { readFileSync, promises, linkSync, fstatSync, lstatSync, unlinkSync, constants, readSync, openSync, writeFileSync, fchmodSync, fsyncSync, closeSync, opendirSync, existsSync, realpathSync, mkdirSync, renameSync, chmodSync, statSync, readdirSync } from 'fs';
|
|
5
|
+
import path20, { join, isAbsolute } from 'path';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import { TASK_STATES, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, createEnvelope, MAX_MESSAGES_PER_BATCH, RuntimeIdSchema, PROTOCOL_VERSION, decodeEnvelope, MessagesSendResponseSchema, parseMessage, UnknownMessageTypeError } from '@byok-sdk/protocol';
|
|
8
8
|
import { promisify } from 'util';
|
|
@@ -72,7 +72,7 @@ function gitEnvironment(readOnly) {
|
|
|
72
72
|
return env;
|
|
73
73
|
}
|
|
74
74
|
function stableGitWorkspaceOwnerId(storeDir, productId) {
|
|
75
|
-
const identity = `${
|
|
75
|
+
const identity = `${path20.resolve(storeDir)}\\0${productId}`;
|
|
76
76
|
return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
|
|
77
77
|
}
|
|
78
78
|
var GUIDANCE = [
|
|
@@ -84,11 +84,11 @@ var GUIDANCE = [
|
|
|
84
84
|
"Leave incomplete work visible for recovery."
|
|
85
85
|
].join("\n");
|
|
86
86
|
function canonical(value) {
|
|
87
|
-
return
|
|
87
|
+
return path20.resolve(value);
|
|
88
88
|
}
|
|
89
89
|
function isContained(root, candidate) {
|
|
90
|
-
const relative =
|
|
91
|
-
return relative === "" || !relative.startsWith(`..${
|
|
90
|
+
const relative = path20.relative(root, candidate);
|
|
91
|
+
return relative === "" || !relative.startsWith(`..${path20.sep}`) && !path20.isAbsolute(relative);
|
|
92
92
|
}
|
|
93
93
|
function bounded(value, max) {
|
|
94
94
|
return Buffer.byteLength(value, "utf8") <= max ? value : value.slice(0, max);
|
|
@@ -193,7 +193,7 @@ var GitWorkspaceManager = class {
|
|
|
193
193
|
await this.ensureOwnerMarker();
|
|
194
194
|
}
|
|
195
195
|
async ensureOwnerMarker() {
|
|
196
|
-
const markerPath =
|
|
196
|
+
const markerPath = path20.join(this.workspaceRoot, OWNER_MARKER);
|
|
197
197
|
let existing;
|
|
198
198
|
try {
|
|
199
199
|
existing = JSON.parse(await promises.readFile(markerPath, "utf8"));
|
|
@@ -352,7 +352,7 @@ ${instruction}`;
|
|
|
352
352
|
if (error instanceof GitWorkspaceError || code !== "ENOENT" && code !== "ENOTDIR") {
|
|
353
353
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
354
354
|
}
|
|
355
|
-
const parent =
|
|
355
|
+
const parent = path20.dirname(current);
|
|
356
356
|
if (parent === current) {
|
|
357
357
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
358
358
|
}
|
|
@@ -410,7 +410,7 @@ async function atomicWriteFile(filePath, data, options = {}) {
|
|
|
410
410
|
await target.close();
|
|
411
411
|
}
|
|
412
412
|
if (process.platform !== "win32") {
|
|
413
|
-
const directory = await promises.open(
|
|
413
|
+
const directory = await promises.open(path20.dirname(filePath), "r");
|
|
414
414
|
try {
|
|
415
415
|
await directory.sync();
|
|
416
416
|
} finally {
|
|
@@ -571,7 +571,7 @@ function isProtected(record) {
|
|
|
571
571
|
var GitWorkspaceStore = class {
|
|
572
572
|
constructor(storeDir, options = {}) {
|
|
573
573
|
this.storeDir = storeDir;
|
|
574
|
-
this.filePath =
|
|
574
|
+
this.filePath = path20.join(storeDir, FILE_NAME);
|
|
575
575
|
this.maxRecords = Math.max(1, Math.floor(options.maxRecords ?? MAX_RECORDS));
|
|
576
576
|
}
|
|
577
577
|
storeDir;
|
|
@@ -701,19 +701,50 @@ var GitWorkspaceStore = class {
|
|
|
701
701
|
await atomicWriteFile(this.filePath, JSON.stringify(ledger, null, 2), { mode: 384 });
|
|
702
702
|
}
|
|
703
703
|
};
|
|
704
|
-
|
|
705
|
-
|
|
704
|
+
var PI_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
|
|
705
|
+
function readPackageJson(dir) {
|
|
706
|
+
const candidate = path20.join(dir, "package.json");
|
|
707
|
+
if (!existsSync(candidate)) return void 0;
|
|
708
|
+
try {
|
|
709
|
+
return JSON.parse(readFileSync(candidate, "utf8"));
|
|
710
|
+
} catch {
|
|
711
|
+
return void 0;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
706
714
|
function resolvePiBin() {
|
|
707
715
|
const override = process.env.BYOK_PI_BIN;
|
|
708
716
|
if (override) {
|
|
709
|
-
return { command: override, source: "
|
|
717
|
+
return { command: override, source: "env" };
|
|
710
718
|
}
|
|
711
|
-
|
|
719
|
+
try {
|
|
720
|
+
const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);
|
|
721
|
+
let dir = path20.dirname(fileURLToPath(mainEntryUrl));
|
|
722
|
+
for (let depth = 0; depth < 6; depth++) {
|
|
723
|
+
const pkg = readPackageJson(dir);
|
|
724
|
+
if (pkg?.name === PI_PACKAGE_NAME) {
|
|
725
|
+
const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi;
|
|
726
|
+
if (binRel) {
|
|
727
|
+
return { command: path20.join(dir, binRel), source: "package" };
|
|
728
|
+
}
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
const parent = path20.dirname(dir);
|
|
732
|
+
if (parent === dir) break;
|
|
733
|
+
dir = parent;
|
|
734
|
+
}
|
|
735
|
+
} catch (cause) {
|
|
736
|
+
throw new Error(
|
|
737
|
+
`Required ${PI_PACKAGE_NAME} could not be resolved; install @byok-sdk/client dependencies or set BYOK_PI_BIN to a Node 22.19+ pi sidecar`,
|
|
738
|
+
{ cause }
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
throw new Error(
|
|
742
|
+
`Required ${PI_PACKAGE_NAME} does not expose the pi CLI; reinstall the pinned dependency or set BYOK_PI_BIN to a Node 22.19+ pi sidecar`
|
|
743
|
+
);
|
|
712
744
|
}
|
|
713
745
|
|
|
714
746
|
// src/adapters/pi/permission-mapping.ts
|
|
715
747
|
var READONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
716
|
-
var DEFAULT_ACTIVE_TOOLS = ["read", "bash", "edit", "write"];
|
|
717
748
|
function mapPermissionPolicyToPiArgs(policy) {
|
|
718
749
|
if (policy.network === false) {
|
|
719
750
|
return {
|
|
@@ -732,22 +763,18 @@ function mapPermissionPolicyToPiArgs(policy) {
|
|
|
732
763
|
const denyTools = policy.denyTools ?? [];
|
|
733
764
|
if (policy.mode === "readonly") {
|
|
734
765
|
const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS.includes(tool)) : [...READONLY_TOOLS];
|
|
735
|
-
|
|
736
|
-
return {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const effective = subtractDenied(base, denyTools);
|
|
741
|
-
return { ok: true, args: effective.length === 0 ? ["--no-tools"] : ["--tools", effective.join(",")] };
|
|
766
|
+
if (base.length === 0) return { ok: true, args: ["--no-tools"] };
|
|
767
|
+
return {
|
|
768
|
+
ok: true,
|
|
769
|
+
args: ["--tools", base.join(","), ...denyTools.length > 0 ? ["--exclude-tools", denyTools.join(",")] : []]
|
|
770
|
+
};
|
|
742
771
|
}
|
|
772
|
+
const args = [];
|
|
743
773
|
if (policy.allowTools && policy.allowTools.length > 0) {
|
|
744
|
-
|
|
774
|
+
args.push("--tools", policy.allowTools.join(","));
|
|
745
775
|
}
|
|
746
|
-
|
|
747
|
-
}
|
|
748
|
-
function subtractDenied(tools, denyTools) {
|
|
749
|
-
const denied = new Set(denyTools);
|
|
750
|
-
return tools.filter((tool) => !denied.has(tool));
|
|
776
|
+
if (denyTools.length > 0) args.push("--exclude-tools", denyTools.join(","));
|
|
777
|
+
return { ok: true, args };
|
|
751
778
|
}
|
|
752
779
|
|
|
753
780
|
// src/adapters/pi/events.ts
|
|
@@ -772,7 +799,7 @@ function mapPiMessageToAgentEvent(msg) {
|
|
|
772
799
|
output: { result: msg.result, isError: msg.isError === true }
|
|
773
800
|
};
|
|
774
801
|
}
|
|
775
|
-
case "
|
|
802
|
+
case "agent_settled":
|
|
776
803
|
return { type: "turn_end" };
|
|
777
804
|
/**
|
|
778
805
|
* `artifact` is NOT a real pi RPC message — pi's own `write` tool only
|
|
@@ -808,16 +835,22 @@ function mapPiMessageToAgentEvent(msg) {
|
|
|
808
835
|
// `recordUnmappedFrame`) can tell "known, expected, silently ignored"
|
|
809
836
|
// apart from "genuinely never seen before" (falls to `default` below).
|
|
810
837
|
case "agent_start":
|
|
838
|
+
case "agent_end":
|
|
839
|
+
// one low-level run; `agent_settled` is BYOK completion
|
|
811
840
|
case "turn_start":
|
|
812
841
|
case "turn_end":
|
|
813
|
-
// pi's own per-LLM-turn boundary, not ours
|
|
842
|
+
// pi's own per-LLM-turn boundary, not ours
|
|
814
843
|
case "message_start":
|
|
815
844
|
case "message_end":
|
|
845
|
+
case "bash_execution_update":
|
|
816
846
|
case "tool_execution_update":
|
|
817
847
|
case "queue_update":
|
|
818
848
|
case "compaction_start":
|
|
819
849
|
case "compaction_end":
|
|
820
850
|
case "auto_retry_start":
|
|
851
|
+
case "summarization_retry_scheduled":
|
|
852
|
+
case "summarization_retry_attempt_start":
|
|
853
|
+
case "summarization_retry_finished":
|
|
821
854
|
case "session_info_changed":
|
|
822
855
|
case "thinking_level_changed":
|
|
823
856
|
return void 0;
|
|
@@ -827,15 +860,20 @@ function mapPiMessageToAgentEvent(msg) {
|
|
|
827
860
|
}
|
|
828
861
|
var ROUTINE_PI_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
829
862
|
"agent_start",
|
|
863
|
+
"agent_end",
|
|
830
864
|
"turn_start",
|
|
831
865
|
"turn_end",
|
|
832
866
|
"message_start",
|
|
833
867
|
"message_end",
|
|
868
|
+
"bash_execution_update",
|
|
834
869
|
"tool_execution_update",
|
|
835
870
|
"queue_update",
|
|
836
871
|
"compaction_start",
|
|
837
872
|
"compaction_end",
|
|
838
873
|
"auto_retry_start",
|
|
874
|
+
"summarization_retry_scheduled",
|
|
875
|
+
"summarization_retry_attempt_start",
|
|
876
|
+
"summarization_retry_finished",
|
|
839
877
|
"session_info_changed",
|
|
840
878
|
"thinking_level_changed"
|
|
841
879
|
]);
|
|
@@ -969,10 +1007,7 @@ var PiRpcClient = class {
|
|
|
969
1007
|
* traffic. Logs once per distinct type (not per occurrence, so a
|
|
970
1008
|
* repeating unmapped type can't spam stdout); the running tally is also
|
|
971
1009
|
* folded into this client's exit-time error message (`buildExitError`) so
|
|
972
|
-
* a post-mortem on a failed/hung task has it without
|
|
973
|
-
* scraping. This is the exact mechanism that would have turned this
|
|
974
|
-
* task's root-cause hang (`agent_end` arriving with no mapping) into a
|
|
975
|
-
* one-line, immediate warning instead of a silent stall.
|
|
1010
|
+
* a post-mortem on a failed/hung task has it without separate log scraping.
|
|
976
1011
|
*/
|
|
977
1012
|
recordUnmappedFrame(type) {
|
|
978
1013
|
const next = (this.unmappedFrameCounts.get(type) ?? 0) + 1;
|
|
@@ -1113,8 +1148,8 @@ var PiAdapter = class {
|
|
|
1113
1148
|
options;
|
|
1114
1149
|
id = "pi";
|
|
1115
1150
|
async detect() {
|
|
1116
|
-
const bin = this.resolveBin();
|
|
1117
1151
|
try {
|
|
1152
|
+
const bin = this.resolveBin();
|
|
1118
1153
|
const { stdout, stderr } = await execFileAsync(bin.command, ["--version"], { timeout: DETECT_TIMEOUT_MS });
|
|
1119
1154
|
const version = stdout.trim() || stderr.trim();
|
|
1120
1155
|
const authPresent = KNOWN_PROVIDER_ENV_VARS.some((name) => process.env[name] !== void 0);
|
|
@@ -1260,7 +1295,7 @@ function resolveApprovalMcpBin() {
|
|
|
1260
1295
|
if (override) {
|
|
1261
1296
|
return { command: override, args: [], source: "env" };
|
|
1262
1297
|
}
|
|
1263
|
-
const distBin =
|
|
1298
|
+
const distBin = path20.join(path20.dirname(fileURLToPath(import.meta.url)), "bin", "byok-approval-mcp.js");
|
|
1264
1299
|
return { command: process.execPath, args: [distBin], source: "dist" };
|
|
1265
1300
|
}
|
|
1266
1301
|
|
|
@@ -1291,7 +1326,7 @@ function mapPermissionPolicyToClaudeArgs(policy) {
|
|
|
1291
1326
|
}
|
|
1292
1327
|
if (policy.mode === "readonly") {
|
|
1293
1328
|
const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS2.includes(tool)) : [...READONLY_TOOLS2];
|
|
1294
|
-
const effective =
|
|
1329
|
+
const effective = subtractDenied(base, denyTools);
|
|
1295
1330
|
return { ok: true, args: ["--permission-mode", "default", "--tools", effective.join(",")] };
|
|
1296
1331
|
}
|
|
1297
1332
|
if (denyTools.length > 0) {
|
|
@@ -1308,7 +1343,7 @@ function mapPermissionPolicyToClaudeArgs(policy) {
|
|
|
1308
1343
|
}
|
|
1309
1344
|
return { ok: true, args };
|
|
1310
1345
|
}
|
|
1311
|
-
function
|
|
1346
|
+
function subtractDenied(tools, denyTools) {
|
|
1312
1347
|
const denied = new Set(denyTools);
|
|
1313
1348
|
return tools.filter((tool) => !denied.has(tool));
|
|
1314
1349
|
}
|
|
@@ -1340,7 +1375,7 @@ var EXTENSION_CONTENT_TYPES = {
|
|
|
1340
1375
|
".yml": "application/yaml"
|
|
1341
1376
|
};
|
|
1342
1377
|
function guessContentType(filePath) {
|
|
1343
|
-
const ext =
|
|
1378
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
1344
1379
|
return EXTENSION_CONTENT_TYPES[ext] ?? "application/octet-stream";
|
|
1345
1380
|
}
|
|
1346
1381
|
function mapAssistant(msg, correlation) {
|
|
@@ -1412,11 +1447,11 @@ function tryBuildArtifactEvent(msg, workspaceDir) {
|
|
|
1412
1447
|
const filePath = toolUseResult && typeof toolUseResult.filePath === "string" ? toolUseResult.filePath : void 0;
|
|
1413
1448
|
if (!filePath) return void 0;
|
|
1414
1449
|
const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;
|
|
1415
|
-
const fileDir =
|
|
1450
|
+
const fileDir = path20.dirname(filePath);
|
|
1416
1451
|
const realFileDir = tryRealpath(fileDir) ?? fileDir;
|
|
1417
|
-
const realFilePath =
|
|
1418
|
-
const relative =
|
|
1419
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
1452
|
+
const realFilePath = path20.join(realFileDir, path20.basename(filePath));
|
|
1453
|
+
const relative = path20.relative(realWorkspaceDir, realFilePath);
|
|
1454
|
+
if (relative === "" || relative.startsWith("..") || path20.isAbsolute(relative)) {
|
|
1420
1455
|
return void 0;
|
|
1421
1456
|
}
|
|
1422
1457
|
return { type: "artifact", name: relative, contentType: guessContentType(filePath) };
|
|
@@ -1715,10 +1750,10 @@ var ClaudeAdapter = class {
|
|
|
1715
1750
|
);
|
|
1716
1751
|
}
|
|
1717
1752
|
const approvalMcpBin = (this.options.resolveApprovalMcpBin ?? resolveApprovalMcpBin)();
|
|
1718
|
-
approvalMcpConfigDir = await promises.mkdtemp(
|
|
1753
|
+
approvalMcpConfigDir = await promises.mkdtemp(path20.join(os.tmpdir(), "byok-approval-mcp-"));
|
|
1719
1754
|
await promises.chmod(approvalMcpConfigDir, 448).catch(() => {
|
|
1720
1755
|
});
|
|
1721
|
-
const mcpConfigPath =
|
|
1756
|
+
const mcpConfigPath = path20.join(approvalMcpConfigDir, "mcp-config.json");
|
|
1722
1757
|
const mcpConfig = {
|
|
1723
1758
|
mcpServers: {
|
|
1724
1759
|
[APPROVAL_MCP_SERVER_NAME]: {
|
|
@@ -2065,8 +2100,8 @@ function extractArtifactEvents(changes, workspaceDir) {
|
|
|
2065
2100
|
const absolutePath = typeof change.path === "string" ? change.path : void 0;
|
|
2066
2101
|
const kind = typeof change.kind === "string" ? change.kind : void 0;
|
|
2067
2102
|
if (!absolutePath || kind === "delete") continue;
|
|
2068
|
-
const relative =
|
|
2069
|
-
if (relative.length === 0 || relative.startsWith("..") ||
|
|
2103
|
+
const relative = path20.relative(workspaceDir, absolutePath);
|
|
2104
|
+
if (relative.length === 0 || relative.startsWith("..") || path20.isAbsolute(relative)) continue;
|
|
2070
2105
|
events.push({ type: "artifact", name: relative, contentType: guessContentType2(relative) });
|
|
2071
2106
|
}
|
|
2072
2107
|
return events;
|
|
@@ -2087,7 +2122,7 @@ var CONTENT_TYPE_BY_EXTENSION = {
|
|
|
2087
2122
|
".csv": "text/csv"
|
|
2088
2123
|
};
|
|
2089
2124
|
function guessContentType2(relativePath) {
|
|
2090
|
-
return CONTENT_TYPE_BY_EXTENSION[
|
|
2125
|
+
return CONTENT_TYPE_BY_EXTENSION[path20.extname(relativePath).toLowerCase()] ?? "application/octet-stream";
|
|
2091
2126
|
}
|
|
2092
2127
|
function extractErrorMessage(rawError) {
|
|
2093
2128
|
if (typeof rawError === "string") return rawError;
|
|
@@ -2248,9 +2283,10 @@ var CodexAdapter = class {
|
|
|
2248
2283
|
* Two independently-verified channel gotchas apply here, the "pi lesson"
|
|
2249
2284
|
* yet again:
|
|
2250
2285
|
* - `codex login status`'s human-readable "Logged in using ChatGPT"
|
|
2251
|
-
* message prints on STDERR, not stdout
|
|
2252
|
-
*
|
|
2253
|
-
*
|
|
2286
|
+
* message prints on STDERR, not stdout — both streams are checked
|
|
2287
|
+
* here for exactly that reason. pi's `--version` is the same class of
|
|
2288
|
+
* hazard from the other direction: its channel has moved between pi
|
|
2289
|
+
* releases (see ../pi/pi-adapter.ts), so neither stream is assumed.
|
|
2254
2290
|
* - The NOT-logged-in message/exit-code shape was deliberately never
|
|
2255
2291
|
* empirically tested: this machine has a real, live ChatGPT login, and
|
|
2256
2292
|
* running `codex logout` to observe the negative case would have
|
|
@@ -2693,12 +2729,12 @@ var DeviceStore = class _DeviceStore {
|
|
|
2693
2729
|
*/
|
|
2694
2730
|
constructor(storeDir, secureDirOptions) {
|
|
2695
2731
|
this.secureDirOptions = secureDirOptions;
|
|
2696
|
-
this.filePath =
|
|
2732
|
+
this.filePath = path20.join(storeDir, "device.json");
|
|
2697
2733
|
}
|
|
2698
2734
|
secureDirOptions;
|
|
2699
2735
|
filePath;
|
|
2700
2736
|
static defaultDir(productId) {
|
|
2701
|
-
return
|
|
2737
|
+
return path20.join(os.homedir(), ".byok", productId);
|
|
2702
2738
|
}
|
|
2703
2739
|
/**
|
|
2704
2740
|
* Resolve the one store pathname every daemon/CLI component must share.
|
|
@@ -2707,7 +2743,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
2707
2743
|
* cwd to pin a quarantine directory inode.
|
|
2708
2744
|
*/
|
|
2709
2745
|
static resolveDir(productId, configured) {
|
|
2710
|
-
return
|
|
2746
|
+
return path20.resolve(configured ?? _DeviceStore.defaultDir(productId));
|
|
2711
2747
|
}
|
|
2712
2748
|
async load() {
|
|
2713
2749
|
const opened = await this.openBounded();
|
|
@@ -2748,7 +2784,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
2748
2784
|
}
|
|
2749
2785
|
}
|
|
2750
2786
|
async save(record) {
|
|
2751
|
-
const storeDir =
|
|
2787
|
+
const storeDir = path20.dirname(this.filePath);
|
|
2752
2788
|
await ensureSecureDir(storeDir, this.secureDirOptions);
|
|
2753
2789
|
await atomicWriteFile(this.filePath, JSON.stringify(record, null, 2), { mode: 384 });
|
|
2754
2790
|
}
|
|
@@ -3182,19 +3218,19 @@ function shortHash(input) {
|
|
|
3182
3218
|
return createHash("sha256").update(input, "utf8").digest("hex").slice(0, 16);
|
|
3183
3219
|
}
|
|
3184
3220
|
function controlSocketPath(storeDir) {
|
|
3185
|
-
const candidate =
|
|
3221
|
+
const candidate = path20.join(storeDir, "control.sock");
|
|
3186
3222
|
if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT) return candidate;
|
|
3187
|
-
return
|
|
3223
|
+
return path20.join(os.tmpdir(), `byok-${shortHash(storeDir)}`, "sock");
|
|
3188
3224
|
}
|
|
3189
3225
|
function controlPipeName(productId, storeDir) {
|
|
3190
|
-
const id = shortHash(`${productId}|${
|
|
3226
|
+
const id = shortHash(`${productId}|${path20.resolve(storeDir)}`);
|
|
3191
3227
|
return `\\\\.\\pipe\\byok-${id}`;
|
|
3192
3228
|
}
|
|
3193
3229
|
function controlEndpointPath(productId, storeDir, platform = process.platform) {
|
|
3194
3230
|
return platform === "win32" ? controlPipeName(productId, storeDir) : controlSocketPath(storeDir);
|
|
3195
3231
|
}
|
|
3196
3232
|
function controlTokenPath(storeDir) {
|
|
3197
|
-
return
|
|
3233
|
+
return path20.join(storeDir, "control.token");
|
|
3198
3234
|
}
|
|
3199
3235
|
var SERVER_PROOF_LABEL = "byok-control-server|";
|
|
3200
3236
|
var CLIENT_AUTH_LABEL = "byok-control-client|";
|
|
@@ -3351,7 +3387,7 @@ async function assertOwnedPrivateDir(dir) {
|
|
|
3351
3387
|
}
|
|
3352
3388
|
async function bindControlEndpoint(server, endpoint) {
|
|
3353
3389
|
if (process.platform !== "win32") {
|
|
3354
|
-
const endpointDir =
|
|
3390
|
+
const endpointDir = path20.dirname(endpoint);
|
|
3355
3391
|
await promises.mkdir(endpointDir, { recursive: true, mode: 448 });
|
|
3356
3392
|
await promises.chmod(endpointDir, 448).catch(() => {
|
|
3357
3393
|
});
|
|
@@ -4632,7 +4668,7 @@ function sameFileState2(left, right) {
|
|
|
4632
4668
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
4633
4669
|
}
|
|
4634
4670
|
async function openOperationalHealthFile(storeDir) {
|
|
4635
|
-
const filePath =
|
|
4671
|
+
const filePath = path20.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
4636
4672
|
let namedBefore;
|
|
4637
4673
|
try {
|
|
4638
4674
|
namedBefore = await promises.lstat(filePath, { bigint: true });
|
|
@@ -4674,7 +4710,7 @@ var OperationalHealthTracker = class {
|
|
|
4674
4710
|
#writeTail = Promise.resolve();
|
|
4675
4711
|
#started = false;
|
|
4676
4712
|
constructor(storeDir, options = {}) {
|
|
4677
|
-
this.#filePath =
|
|
4713
|
+
this.#filePath = path20.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
4678
4714
|
this.#windowMs = options.windowMs ?? 6e4;
|
|
4679
4715
|
this.#failureThreshold = options.failureThreshold ?? 3;
|
|
4680
4716
|
this.#maxFailures = options.maxFailures ?? 128;
|
|
@@ -4761,7 +4797,7 @@ var OperationalHealthTracker = class {
|
|
|
4761
4797
|
async #load() {
|
|
4762
4798
|
let opened;
|
|
4763
4799
|
try {
|
|
4764
|
-
opened = await openOperationalHealthFile(
|
|
4800
|
+
opened = await openOperationalHealthFile(path20.dirname(this.#filePath));
|
|
4765
4801
|
} catch (err) {
|
|
4766
4802
|
throw new Error("operational health state could not be read");
|
|
4767
4803
|
}
|
|
@@ -4797,7 +4833,7 @@ var OperationalHealthTracker = class {
|
|
|
4797
4833
|
if (!this.#state) return;
|
|
4798
4834
|
const body = JSON.stringify(this.#state, null, 2);
|
|
4799
4835
|
this.#writeTail = this.#writeTail.then(async () => {
|
|
4800
|
-
await ensureSecureDir(
|
|
4836
|
+
await ensureSecureDir(path20.dirname(this.#filePath));
|
|
4801
4837
|
await atomicWriteFile(this.#filePath, body, { mode: 384, fsync: true });
|
|
4802
4838
|
});
|
|
4803
4839
|
try {
|
|
@@ -5153,8 +5189,8 @@ async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */
|
|
|
5153
5189
|
await mutex.close().catch(() => void 0);
|
|
5154
5190
|
throw err;
|
|
5155
5191
|
}
|
|
5156
|
-
const ownerPath =
|
|
5157
|
-
const reclaimPath =
|
|
5192
|
+
const ownerPath = path20.join(storeDir, DAEMON_OWNER_FILENAME);
|
|
5193
|
+
const reclaimPath = path20.join(storeDir, RECLAIM_FILENAME);
|
|
5158
5194
|
const record = {
|
|
5159
5195
|
version: 2,
|
|
5160
5196
|
pid: process.pid,
|
|
@@ -5231,7 +5267,7 @@ var CursorStore = class {
|
|
|
5231
5267
|
storeDir;
|
|
5232
5268
|
fileFor(serverUrl, deviceId) {
|
|
5233
5269
|
const key = createHash("sha256").update(`${serverUrl}::${deviceId}`).digest("hex").slice(0, 32);
|
|
5234
|
-
return
|
|
5270
|
+
return path20.join(this.storeDir, `cursor-${key}.json`);
|
|
5235
5271
|
}
|
|
5236
5272
|
async load(serverUrl, deviceId) {
|
|
5237
5273
|
let raw;
|
|
@@ -5251,7 +5287,7 @@ var CursorStore = class {
|
|
|
5251
5287
|
}
|
|
5252
5288
|
async save(serverUrl, deviceId, cursor) {
|
|
5253
5289
|
const file = this.fileFor(serverUrl, deviceId);
|
|
5254
|
-
await promises.mkdir(
|
|
5290
|
+
await promises.mkdir(path20.dirname(file), { recursive: true, mode: 448 });
|
|
5255
5291
|
await atomicWriteFile(file, JSON.stringify({ cursor }));
|
|
5256
5292
|
}
|
|
5257
5293
|
/** Remove any persisted cursor for (serverUrl, deviceId) — a no-op if none exists. Called from `pair()` (finding F5) so a device that's about to be replaced never leaves a cursor a future, unrelated device could somehow inherit. */
|
|
@@ -5547,7 +5583,7 @@ var SessionWorkspaceStore = class {
|
|
|
5547
5583
|
*/
|
|
5548
5584
|
queue = Promise.resolve();
|
|
5549
5585
|
constructor(storeDir) {
|
|
5550
|
-
this.filePath =
|
|
5586
|
+
this.filePath = path20.join(storeDir, "session-workspaces.json");
|
|
5551
5587
|
}
|
|
5552
5588
|
async get(sessionRef) {
|
|
5553
5589
|
return this.enqueue(async () => {
|
|
@@ -5605,7 +5641,7 @@ var SessionWorkspaceStore = class {
|
|
|
5605
5641
|
}
|
|
5606
5642
|
}
|
|
5607
5643
|
async save(all) {
|
|
5608
|
-
const dir =
|
|
5644
|
+
const dir = path20.dirname(this.filePath);
|
|
5609
5645
|
await promises.mkdir(dir, { recursive: true, mode: 448 });
|
|
5610
5646
|
const tmpPath = `${this.filePath}.${process.pid}-${tmpSeq2++}.tmp`;
|
|
5611
5647
|
try {
|
|
@@ -5710,9 +5746,9 @@ function isSqliteAvailable() {
|
|
|
5710
5746
|
}
|
|
5711
5747
|
}
|
|
5712
5748
|
var SECURE_FILE_MODE = 384;
|
|
5713
|
-
function openJournalDatabase(
|
|
5749
|
+
function openJournalDatabase(path25, busyTimeoutMs, faults) {
|
|
5714
5750
|
const { DatabaseSync } = loadSqliteModule();
|
|
5715
|
-
const db = new DatabaseSync(
|
|
5751
|
+
const db = new DatabaseSync(path25, { timeout: busyTimeoutMs });
|
|
5716
5752
|
try {
|
|
5717
5753
|
faults?.onStep?.("after-open");
|
|
5718
5754
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL;");
|
|
@@ -5855,9 +5891,9 @@ var RECEIVED_STATE = "received";
|
|
|
5855
5891
|
function byteLength(value) {
|
|
5856
5892
|
return Buffer.byteLength(value, "utf8");
|
|
5857
5893
|
}
|
|
5858
|
-
function fileBytes(
|
|
5894
|
+
function fileBytes(path25) {
|
|
5859
5895
|
try {
|
|
5860
|
-
return statSync(
|
|
5896
|
+
return statSync(path25).size;
|
|
5861
5897
|
} catch {
|
|
5862
5898
|
return 0;
|
|
5863
5899
|
}
|
|
@@ -7108,8 +7144,8 @@ function estimateEventBytes(event) {
|
|
|
7108
7144
|
}
|
|
7109
7145
|
async function openArtifact(workspaceDir, name) {
|
|
7110
7146
|
const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
|
|
7111
|
-
const candidate =
|
|
7112
|
-
const prefix = realWorkspaceDir.endsWith(
|
|
7147
|
+
const candidate = path20.resolve(realWorkspaceDir, name);
|
|
7148
|
+
const prefix = realWorkspaceDir.endsWith(path20.sep) ? realWorkspaceDir : realWorkspaceDir + path20.sep;
|
|
7113
7149
|
if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
|
|
7114
7150
|
return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
|
|
7115
7151
|
}
|
|
@@ -7507,7 +7543,7 @@ var TaskRunner = class {
|
|
|
7507
7543
|
const sameProtocolTask = ledger?.taskId === taskId;
|
|
7508
7544
|
const interruptedOldTask = ledger?.phase === "interrupted" && sameProtocolTask;
|
|
7509
7545
|
const activeDifferentTask = ledger !== void 0 && ledger.taskId !== taskId && (ledger.phase === "preparing" || ledger.phase === "active");
|
|
7510
|
-
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== payload.sessionRef ||
|
|
7546
|
+
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== payload.sessionRef || path20.resolve(ledger.workspaceDir) !== path20.resolve(known.workspaceDir) || interruptedOldTask || activeDifferentTask) {
|
|
7511
7547
|
this.decline(taskId, "session is incompatible with Git workspace mode", true);
|
|
7512
7548
|
return;
|
|
7513
7549
|
}
|
|
@@ -7522,7 +7558,7 @@ var TaskRunner = class {
|
|
|
7522
7558
|
return;
|
|
7523
7559
|
}
|
|
7524
7560
|
} else {
|
|
7525
|
-
workspaceDir =
|
|
7561
|
+
workspaceDir = path20.join(this.deps.workspaceRoot, taskId);
|
|
7526
7562
|
}
|
|
7527
7563
|
try {
|
|
7528
7564
|
gitLease = await gitManager.acquireLease(workspaceDir, payload.sessionRef);
|
|
@@ -7532,7 +7568,7 @@ var TaskRunner = class {
|
|
|
7532
7568
|
}
|
|
7533
7569
|
} else if (!this.deps.gitWorkspaceManager && !this.deps.gitWorkspaceStore) {
|
|
7534
7570
|
known = payload.sessionRef ? await this.deps.sessionWorkspaces.get(payload.sessionRef) : void 0;
|
|
7535
|
-
workspaceDir = known?.workspaceDir ??
|
|
7571
|
+
workspaceDir = known?.workspaceDir ?? path20.join(this.deps.workspaceRoot, taskId);
|
|
7536
7572
|
plainWorkspaceNeedsResolve = true;
|
|
7537
7573
|
} else {
|
|
7538
7574
|
this.decline(taskId, "workspace mode is unavailable", true);
|
|
@@ -8404,7 +8440,7 @@ var TaskRunner = class {
|
|
|
8404
8440
|
}
|
|
8405
8441
|
/** `reuseDir`, when set (a known sessionRef's recorded workspace), is used verbatim instead of a fresh `workspaceRoot/<taskId>` directory — `mkdir recursive` is idempotent either way, so ensuring-exists is safe to do unconditionally. */
|
|
8406
8442
|
async resolveWorkspaceDir(taskId, reuseDir) {
|
|
8407
|
-
const dir = reuseDir ??
|
|
8443
|
+
const dir = reuseDir ?? path20.join(this.deps.workspaceRoot, taskId);
|
|
8408
8444
|
await promises.mkdir(dir, { recursive: true });
|
|
8409
8445
|
return dir;
|
|
8410
8446
|
}
|
|
@@ -9260,8 +9296,8 @@ function generateLaunchdPlist(def) {
|
|
|
9260
9296
|
const { label, program, logDir } = def;
|
|
9261
9297
|
const args = [program.command, ...program.args];
|
|
9262
9298
|
const cwd = program.cwd ?? os.homedir();
|
|
9263
|
-
const outLog =
|
|
9264
|
-
const errLog =
|
|
9299
|
+
const outLog = path20.join(logDir, `${label}.out.log`);
|
|
9300
|
+
const errLog = path20.join(logDir, `${label}.err.log`);
|
|
9265
9301
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
9266
9302
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
9267
9303
|
<plist version="1.0">
|
|
@@ -9302,7 +9338,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
9302
9338
|
return process.getuid();
|
|
9303
9339
|
});
|
|
9304
9340
|
const label = sanitizeServiceName(def.name);
|
|
9305
|
-
const plistPath = () =>
|
|
9341
|
+
const plistPath = () => path20.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
9306
9342
|
const domainTarget = () => `gui/${getuid()}`;
|
|
9307
9343
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
9308
9344
|
async function fileExists(p) {
|
|
@@ -9315,7 +9351,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
9315
9351
|
}
|
|
9316
9352
|
async function writePlist(program) {
|
|
9317
9353
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
9318
|
-
await fs19.mkdir(
|
|
9354
|
+
await fs19.mkdir(path20.dirname(plistPath()), { recursive: true });
|
|
9319
9355
|
await fs19.mkdir(def.logDir, { recursive: true });
|
|
9320
9356
|
await fs19.writeFile(plistPath(), xml, "utf8");
|
|
9321
9357
|
}
|
|
@@ -9389,8 +9425,8 @@ function generateSystemdUnit(def) {
|
|
|
9389
9425
|
assertNoControlChars(displayName, "displayName");
|
|
9390
9426
|
const cwd = program.cwd ?? os.homedir();
|
|
9391
9427
|
assertNoControlChars(cwd, "program.cwd");
|
|
9392
|
-
const outLog =
|
|
9393
|
-
const errLog =
|
|
9428
|
+
const outLog = path20.join(logDir, `${name}.out.log`);
|
|
9429
|
+
const errLog = path20.join(logDir, `${name}.err.log`);
|
|
9394
9430
|
assertNoControlChars(outLog, "logDir");
|
|
9395
9431
|
assertNoControlChars(errLog, "logDir");
|
|
9396
9432
|
const execStart = [program.command, ...program.args].map(quoteSystemdArg).join(" ");
|
|
@@ -9416,7 +9452,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
9416
9452
|
const homedir = deps.homedir ?? (() => os.homedir());
|
|
9417
9453
|
const name = sanitizeServiceName(def.name);
|
|
9418
9454
|
const unitName = `${name}.service`;
|
|
9419
|
-
const unitPath = () =>
|
|
9455
|
+
const unitPath = () => path20.join(homedir(), ".config", "systemd", "user", unitName);
|
|
9420
9456
|
async function fileExists(p) {
|
|
9421
9457
|
try {
|
|
9422
9458
|
await fs19.stat(p);
|
|
@@ -9427,7 +9463,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
9427
9463
|
}
|
|
9428
9464
|
async function writeUnit(program) {
|
|
9429
9465
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
9430
|
-
await fs19.mkdir(
|
|
9466
|
+
await fs19.mkdir(path20.dirname(unitPath()), { recursive: true });
|
|
9431
9467
|
await fs19.mkdir(def.logDir, { recursive: true });
|
|
9432
9468
|
await fs19.writeFile(unitPath(), unit, "utf8");
|
|
9433
9469
|
}
|
|
@@ -9509,8 +9545,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
9509
9545
|
const winswBin = windows.winswBin;
|
|
9510
9546
|
const id = sanitizeServiceName(def.name);
|
|
9511
9547
|
const installDir = windows.installDir ?? def.logDir;
|
|
9512
|
-
const exePath =
|
|
9513
|
-
const xmlPath =
|
|
9548
|
+
const exePath = path20.join(installDir, `${id}.exe`);
|
|
9549
|
+
const xmlPath = path20.join(installDir, `${id}.xml`);
|
|
9514
9550
|
async function fileExists(p) {
|
|
9515
9551
|
try {
|
|
9516
9552
|
await fs19.stat(p);
|
|
@@ -10284,7 +10320,7 @@ function safeProtocol(serverUrl) {
|
|
|
10284
10320
|
}
|
|
10285
10321
|
}
|
|
10286
10322
|
async function inspectDevice(storeDir) {
|
|
10287
|
-
const filePath =
|
|
10323
|
+
const filePath = path20.join(storeDir, "device.json");
|
|
10288
10324
|
let pathStat;
|
|
10289
10325
|
try {
|
|
10290
10326
|
pathStat = await promises.lstat(filePath);
|
|
@@ -10364,11 +10400,11 @@ async function copyOpenFileBounded(source, expected, destinationPath) {
|
|
|
10364
10400
|
}
|
|
10365
10401
|
}
|
|
10366
10402
|
async function inspectJournal(storeDir) {
|
|
10367
|
-
const journalPath =
|
|
10403
|
+
const journalPath = path20.join(storeDir, JOURNAL_DB_FILENAME);
|
|
10368
10404
|
try {
|
|
10369
10405
|
const mainIdentity = await regularFileIdentity(journalPath);
|
|
10370
10406
|
if (mainIdentity === void 0) return { status: "missing" };
|
|
10371
|
-
const walIdentity = await regularFileIdentity(
|
|
10407
|
+
const walIdentity = await regularFileIdentity(path20.join(storeDir, `${JOURNAL_DB_FILENAME}-wal`));
|
|
10372
10408
|
let sizeBytes = Number(mainIdentity.size);
|
|
10373
10409
|
let walBytes = walIdentity === void 0 ? void 0 : Number(walIdentity.size);
|
|
10374
10410
|
if (!isSqliteAvailable()) {
|
|
@@ -10376,7 +10412,7 @@ async function inspectJournal(storeDir) {
|
|
|
10376
10412
|
}
|
|
10377
10413
|
const componentNames = [JOURNAL_DB_FILENAME, `${JOURNAL_DB_FILENAME}-wal`, `${JOURNAL_DB_FILENAME}-shm`];
|
|
10378
10414
|
const initial = /* @__PURE__ */ new Map();
|
|
10379
|
-
for (const name of componentNames) initial.set(name, await regularFileIdentity(
|
|
10415
|
+
for (const name of componentNames) initial.set(name, await regularFileIdentity(path20.join(storeDir, name)));
|
|
10380
10416
|
const snapshotMain = initial.get(JOURNAL_DB_FILENAME);
|
|
10381
10417
|
if (!snapshotMain) return { status: "unavailable", reason: "journal changed during diagnostics snapshot" };
|
|
10382
10418
|
sizeBytes = Number(snapshotMain.size);
|
|
@@ -10388,7 +10424,7 @@ async function inspectJournal(storeDir) {
|
|
|
10388
10424
|
let handle;
|
|
10389
10425
|
try {
|
|
10390
10426
|
handle = await promises.open(
|
|
10391
|
-
|
|
10427
|
+
path20.join(storeDir, name),
|
|
10392
10428
|
constants.O_RDONLY | constants.O_NONBLOCK | (constants.O_NOFOLLOW ?? 0)
|
|
10393
10429
|
);
|
|
10394
10430
|
} catch (err) {
|
|
@@ -10417,28 +10453,28 @@ async function inspectJournal(storeDir) {
|
|
|
10417
10453
|
reason: "journal exceeds the bounded diagnostics copy limit"
|
|
10418
10454
|
};
|
|
10419
10455
|
}
|
|
10420
|
-
const tempDir = await promises.mkdtemp(
|
|
10456
|
+
const tempDir = await promises.mkdtemp(path20.join(os.tmpdir(), "byok-journal-inspect-"));
|
|
10421
10457
|
const { DatabaseSync } = loadSqliteModule();
|
|
10422
10458
|
let db;
|
|
10423
10459
|
try {
|
|
10424
10460
|
for (const name of componentNames) {
|
|
10425
10461
|
const component = opened.get(name);
|
|
10426
|
-
if (component && !await copyOpenFileBounded(component.handle, component.identity,
|
|
10462
|
+
if (component && !await copyOpenFileBounded(component.handle, component.identity, path20.join(tempDir, name))) {
|
|
10427
10463
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
10428
10464
|
}
|
|
10429
10465
|
}
|
|
10430
10466
|
for (const [name, component] of opened) {
|
|
10431
|
-
if (!sameIdentity(component.identity, identityFromBigIntStat(await component.handle.stat({ bigint: true }))) || !sameIdentity(component.identity, await regularFileIdentity(
|
|
10467
|
+
if (!sameIdentity(component.identity, identityFromBigIntStat(await component.handle.stat({ bigint: true }))) || !sameIdentity(component.identity, await regularFileIdentity(path20.join(storeDir, name)))) {
|
|
10432
10468
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
10433
10469
|
}
|
|
10434
10470
|
}
|
|
10435
10471
|
for (const name of componentNames) {
|
|
10436
|
-
if (!opened.has(name) && await regularFileIdentity(
|
|
10472
|
+
if (!opened.has(name) && await regularFileIdentity(path20.join(storeDir, name)) !== void 0) {
|
|
10437
10473
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
10438
10474
|
}
|
|
10439
10475
|
}
|
|
10440
10476
|
const header = Buffer.alloc(16);
|
|
10441
|
-
const copiedHandle = await promises.open(
|
|
10477
|
+
const copiedHandle = await promises.open(path20.join(tempDir, JOURNAL_DB_FILENAME), "r");
|
|
10442
10478
|
try {
|
|
10443
10479
|
const { bytesRead } = await copiedHandle.read(header, 0, header.length, 0);
|
|
10444
10480
|
if (bytesRead !== 16 || header.toString("binary") !== "SQLite format 3\0") {
|
|
@@ -10447,7 +10483,7 @@ async function inspectJournal(storeDir) {
|
|
|
10447
10483
|
} finally {
|
|
10448
10484
|
await copiedHandle.close();
|
|
10449
10485
|
}
|
|
10450
|
-
db = new DatabaseSync(
|
|
10486
|
+
db = new DatabaseSync(path20.join(tempDir, JOURNAL_DB_FILENAME), { readOnly: true });
|
|
10451
10487
|
const result = db.prepare("PRAGMA quick_check(1)").get();
|
|
10452
10488
|
if (result?.quick_check !== "ok") {
|
|
10453
10489
|
return { status: "corrupt", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal quick_check failed" };
|
|
@@ -10482,7 +10518,7 @@ async function inspectWorkspace(workspaceRoot) {
|
|
|
10482
10518
|
}
|
|
10483
10519
|
}
|
|
10484
10520
|
function readPinnedQuarantineFile(name, maxBytes, includeBytes, budget) {
|
|
10485
|
-
if (
|
|
10521
|
+
if (path20.basename(name) !== name || name === "." || name === "..") {
|
|
10486
10522
|
throw new Error("quarantine manifest contains an invalid evidence name");
|
|
10487
10523
|
}
|
|
10488
10524
|
const namedBefore = lstatSync(name, { bigint: true });
|
|
@@ -10586,10 +10622,10 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
|
|
|
10586
10622
|
if (isJournalQuarantineManifest(parsed)) {
|
|
10587
10623
|
const manifestBase = manifestName.slice(0, -".manifest.json".length);
|
|
10588
10624
|
const boundNames = parsed.files.map((file) => {
|
|
10589
|
-
if (
|
|
10625
|
+
if (path20.dirname(path20.resolve(file)) !== path20.resolve(".")) {
|
|
10590
10626
|
throw new Error("journal quarantine manifest points outside quarantine");
|
|
10591
10627
|
}
|
|
10592
|
-
return
|
|
10628
|
+
return path20.basename(file);
|
|
10593
10629
|
});
|
|
10594
10630
|
if (!boundNames.includes(manifestBase)) {
|
|
10595
10631
|
throw new Error("journal quarantine manifest is not bound to its primary database evidence");
|
|
@@ -10629,7 +10665,7 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
|
|
|
10629
10665
|
}
|
|
10630
10666
|
}
|
|
10631
10667
|
async function inspectQuarantine(storeDir) {
|
|
10632
|
-
const dir =
|
|
10668
|
+
const dir = path20.join(storeDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
10633
10669
|
let directory;
|
|
10634
10670
|
try {
|
|
10635
10671
|
directory = await promises.lstat(dir, { bigint: true });
|
|
@@ -10700,7 +10736,7 @@ function checksFor(snapshot) {
|
|
|
10700
10736
|
];
|
|
10701
10737
|
}
|
|
10702
10738
|
async function collectDiagnostics(config, storeDir, options = {}) {
|
|
10703
|
-
const resolvedStoreDir =
|
|
10739
|
+
const resolvedStoreDir = path20.resolve(storeDir);
|
|
10704
10740
|
const adapters = options.adapters ?? defaultRuntimeAdapters(config.runtimeAllowlist);
|
|
10705
10741
|
const connectControl = options.connectControl ?? connectControlClient;
|
|
10706
10742
|
const [device, probedRuntimes, health, journal, workspace, quarantine, controlConnection] = await Promise.all([
|
|
@@ -10875,7 +10911,7 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
10875
10911
|
unlinkSync(sourcePath);
|
|
10876
10912
|
sourceRemoved = true;
|
|
10877
10913
|
if (process.platform !== "win32") {
|
|
10878
|
-
const directoryFd = openSync(
|
|
10914
|
+
const directoryFd = openSync(path20.dirname(sourcePath), constants.O_RDONLY);
|
|
10879
10915
|
try {
|
|
10880
10916
|
fsyncSync(directoryFd);
|
|
10881
10917
|
} finally {
|
|
@@ -10914,10 +10950,10 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
10914
10950
|
}
|
|
10915
10951
|
}
|
|
10916
10952
|
async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
10917
|
-
const resolvedStoreDir =
|
|
10953
|
+
const resolvedStoreDir = path20.resolve(storeDir);
|
|
10918
10954
|
const owner = await acquireDaemonOwner(resolvedStoreDir, "doctor", options.clock);
|
|
10919
10955
|
try {
|
|
10920
|
-
const sourcePath =
|
|
10956
|
+
const sourcePath = path20.join(resolvedStoreDir, OPERATIONAL_HEALTH_FILENAME);
|
|
10921
10957
|
let opened;
|
|
10922
10958
|
try {
|
|
10923
10959
|
opened = await openOperationalHealthFile(resolvedStoreDir);
|
|
@@ -10934,7 +10970,7 @@ async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
|
10934
10970
|
}
|
|
10935
10971
|
const sourceStat = await source.stat({ bigint: true });
|
|
10936
10972
|
if (!sourceStat.isFile()) throw new Error("operational health state is not a regular file; refusing quarantine");
|
|
10937
|
-
const quarantineDir =
|
|
10973
|
+
const quarantineDir = path20.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
10938
10974
|
try {
|
|
10939
10975
|
const existing = await promises.lstat(quarantineDir);
|
|
10940
10976
|
if (!existing.isDirectory() || existing.isSymbolicLink()) {
|
|
@@ -11035,8 +11071,8 @@ function buildServiceDefinition(config, configPath, rest) {
|
|
|
11035
11071
|
const name = argValue(rest, "--name") ?? config.productId;
|
|
11036
11072
|
const agentBin = argValue(rest, "--agent-bin") ?? process.argv[1] ?? "byok-agent";
|
|
11037
11073
|
const nodeBin = argValue(rest, "--node-bin") ?? process.execPath;
|
|
11038
|
-
const absoluteConfigPath =
|
|
11039
|
-
const logDir =
|
|
11074
|
+
const absoluteConfigPath = path20.resolve(configPath);
|
|
11075
|
+
const logDir = path20.join(resolveStoreDir(config), "service-logs");
|
|
11040
11076
|
const definition = {
|
|
11041
11077
|
name,
|
|
11042
11078
|
displayName: config.branding?.displayName ?? config.productName,
|
|
@@ -11090,7 +11126,7 @@ async function runServiceStatusCommand(config, configPath, rest, deps = {}) {
|
|
|
11090
11126
|
log(`detail: ${status.detail.trim() || "(none)"}`);
|
|
11091
11127
|
}
|
|
11092
11128
|
function auditLogPath(storeDir) {
|
|
11093
|
-
return
|
|
11129
|
+
return path20.join(storeDir, "audit.jsonl");
|
|
11094
11130
|
}
|
|
11095
11131
|
var AUDIT_LOG_MODE = 384;
|
|
11096
11132
|
var AUDIT_STORE_DIR_MODE = 448;
|
|
@@ -11912,11 +11948,11 @@ async function createSupportBundle(config, storeDir, options = {}) {
|
|
|
11912
11948
|
};
|
|
11913
11949
|
}
|
|
11914
11950
|
async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
11915
|
-
const dir =
|
|
11951
|
+
const dir = path20.dirname(outputPath);
|
|
11916
11952
|
const parentStat = await promises.stat(dir);
|
|
11917
11953
|
if (!parentStat.isDirectory()) throw new Error("support bundle output parent is not a directory");
|
|
11918
|
-
const privateDir =
|
|
11919
|
-
const tempPath =
|
|
11954
|
+
const privateDir = path20.join(dir, `.${path20.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
|
|
11955
|
+
const tempPath = path20.join(privateDir, "bundle.tmp");
|
|
11920
11956
|
try {
|
|
11921
11957
|
await promises.mkdir(privateDir, { mode: 448 });
|
|
11922
11958
|
await ensureSecureDir(privateDir, secureFileOptions);
|
|
@@ -11946,7 +11982,7 @@ async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
|
11946
11982
|
// src/bin/commands/support-bundle.ts
|
|
11947
11983
|
async function runSupportBundleCommand(config, options) {
|
|
11948
11984
|
if (!options.outputPath) throw new Error("support-bundle requires --output <path>");
|
|
11949
|
-
const outputPath =
|
|
11985
|
+
const outputPath = path20.resolve(options.outputPath);
|
|
11950
11986
|
const bundle = await createSupportBundle(config, resolveStoreDir(config), options);
|
|
11951
11987
|
await writeSupportBundle(outputPath, bundle);
|
|
11952
11988
|
const log = options.log ?? ((line) => console.log(line));
|
|
@@ -11982,8 +12018,8 @@ async function runTasksFollowCommand(config, deps) {
|
|
|
11982
12018
|
});
|
|
11983
12019
|
return;
|
|
11984
12020
|
}
|
|
11985
|
-
const
|
|
11986
|
-
await followAuditLog(
|
|
12021
|
+
const path25 = auditLogPath(storeDir);
|
|
12022
|
+
await followAuditLog(path25, (event) => log(formatDaemonEventLine(event)), {
|
|
11987
12023
|
signal: deps.signal,
|
|
11988
12024
|
pollIntervalMs: deps.pollIntervalMs,
|
|
11989
12025
|
fromEnd: true
|