@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/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFile, spawn, spawnSync } from 'child_process';
|
|
2
2
|
import { createHash, randomUUID, sign, createPrivateKey, generateKeyPairSync, randomBytes, createHmac, timingSafeEqual } from 'crypto';
|
|
3
|
-
import { promises, mkdirSync, existsSync, renameSync, writeFileSync, chmodSync, statSync, readdirSync, linkSync, fstatSync, lstatSync, unlinkSync, constants, realpathSync } from 'fs';
|
|
4
|
-
import
|
|
3
|
+
import { promises, mkdirSync, existsSync, renameSync, writeFileSync, chmodSync, statSync, readdirSync, linkSync, fstatSync, lstatSync, unlinkSync, constants, readFileSync, realpathSync } from 'fs';
|
|
4
|
+
import path16, { join, isAbsolute } from 'path';
|
|
5
5
|
import os6 from 'os';
|
|
6
6
|
import { partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, createEnvelope, MAX_MESSAGES_PER_BATCH, RuntimeIdSchema, PROTOCOL_VERSION, decodeEnvelope, MessagesSendResponseSchema, parseMessage, UnknownMessageTypeError } from '@byok-sdk/protocol';
|
|
7
7
|
import { promisify } from 'util';
|
|
@@ -71,7 +71,7 @@ function gitEnvironment(readOnly) {
|
|
|
71
71
|
return env;
|
|
72
72
|
}
|
|
73
73
|
function stableGitWorkspaceOwnerId(storeDir, productId) {
|
|
74
|
-
const identity = `${
|
|
74
|
+
const identity = `${path16.resolve(storeDir)}\\0${productId}`;
|
|
75
75
|
return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
|
|
76
76
|
}
|
|
77
77
|
var GUIDANCE = [
|
|
@@ -83,11 +83,11 @@ var GUIDANCE = [
|
|
|
83
83
|
"Leave incomplete work visible for recovery."
|
|
84
84
|
].join("\n");
|
|
85
85
|
function canonical(value) {
|
|
86
|
-
return
|
|
86
|
+
return path16.resolve(value);
|
|
87
87
|
}
|
|
88
88
|
function isContained(root, candidate) {
|
|
89
|
-
const relative =
|
|
90
|
-
return relative === "" || !relative.startsWith(`..${
|
|
89
|
+
const relative = path16.relative(root, candidate);
|
|
90
|
+
return relative === "" || !relative.startsWith(`..${path16.sep}`) && !path16.isAbsolute(relative);
|
|
91
91
|
}
|
|
92
92
|
function bounded(value, max) {
|
|
93
93
|
return Buffer.byteLength(value, "utf8") <= max ? value : value.slice(0, max);
|
|
@@ -192,7 +192,7 @@ var GitWorkspaceManager = class {
|
|
|
192
192
|
await this.ensureOwnerMarker();
|
|
193
193
|
}
|
|
194
194
|
async ensureOwnerMarker() {
|
|
195
|
-
const markerPath =
|
|
195
|
+
const markerPath = path16.join(this.workspaceRoot, OWNER_MARKER);
|
|
196
196
|
let existing;
|
|
197
197
|
try {
|
|
198
198
|
existing = JSON.parse(await promises.readFile(markerPath, "utf8"));
|
|
@@ -351,7 +351,7 @@ ${instruction}`;
|
|
|
351
351
|
if (error instanceof GitWorkspaceError || code !== "ENOENT" && code !== "ENOTDIR") {
|
|
352
352
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
353
353
|
}
|
|
354
|
-
const parent =
|
|
354
|
+
const parent = path16.dirname(current);
|
|
355
355
|
if (parent === current) {
|
|
356
356
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
357
357
|
}
|
|
@@ -416,7 +416,7 @@ async function atomicWriteFile(filePath, data, options = {}) {
|
|
|
416
416
|
await target.close();
|
|
417
417
|
}
|
|
418
418
|
if (process.platform !== "win32") {
|
|
419
|
-
const directory = await promises.open(
|
|
419
|
+
const directory = await promises.open(path16.dirname(filePath), "r");
|
|
420
420
|
try {
|
|
421
421
|
await directory.sync();
|
|
422
422
|
} finally {
|
|
@@ -541,7 +541,7 @@ function isProtected(record) {
|
|
|
541
541
|
var GitWorkspaceStore = class {
|
|
542
542
|
constructor(storeDir, options = {}) {
|
|
543
543
|
this.storeDir = storeDir;
|
|
544
|
-
this.filePath =
|
|
544
|
+
this.filePath = path16.join(storeDir, FILE_NAME);
|
|
545
545
|
this.maxRecords = Math.max(1, Math.floor(options.maxRecords ?? MAX_RECORDS));
|
|
546
546
|
}
|
|
547
547
|
storeDir;
|
|
@@ -671,20 +671,50 @@ var GitWorkspaceStore = class {
|
|
|
671
671
|
await atomicWriteFile(this.filePath, JSON.stringify(ledger, null, 2), { mode: 384 });
|
|
672
672
|
}
|
|
673
673
|
};
|
|
674
|
-
|
|
675
|
-
// src/adapters/pi/resolve-bin.ts
|
|
676
674
|
var PI_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
|
|
675
|
+
function readPackageJson(dir) {
|
|
676
|
+
const candidate = path16.join(dir, "package.json");
|
|
677
|
+
if (!existsSync(candidate)) return void 0;
|
|
678
|
+
try {
|
|
679
|
+
return JSON.parse(readFileSync(candidate, "utf8"));
|
|
680
|
+
} catch {
|
|
681
|
+
return void 0;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
677
684
|
function resolvePiBin() {
|
|
678
685
|
const override = process.env.BYOK_PI_BIN;
|
|
679
686
|
if (override) {
|
|
680
|
-
return { command: override, source: "
|
|
687
|
+
return { command: override, source: "env" };
|
|
681
688
|
}
|
|
682
|
-
|
|
689
|
+
try {
|
|
690
|
+
const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);
|
|
691
|
+
let dir = path16.dirname(fileURLToPath(mainEntryUrl));
|
|
692
|
+
for (let depth = 0; depth < 6; depth++) {
|
|
693
|
+
const pkg = readPackageJson(dir);
|
|
694
|
+
if (pkg?.name === PI_PACKAGE_NAME) {
|
|
695
|
+
const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi;
|
|
696
|
+
if (binRel) {
|
|
697
|
+
return { command: path16.join(dir, binRel), source: "package" };
|
|
698
|
+
}
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
const parent = path16.dirname(dir);
|
|
702
|
+
if (parent === dir) break;
|
|
703
|
+
dir = parent;
|
|
704
|
+
}
|
|
705
|
+
} catch (cause) {
|
|
706
|
+
throw new Error(
|
|
707
|
+
`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`,
|
|
708
|
+
{ cause }
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
throw new Error(
|
|
712
|
+
`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`
|
|
713
|
+
);
|
|
683
714
|
}
|
|
684
715
|
|
|
685
716
|
// src/adapters/pi/permission-mapping.ts
|
|
686
717
|
var READONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
687
|
-
var DEFAULT_ACTIVE_TOOLS = ["read", "bash", "edit", "write"];
|
|
688
718
|
function mapPermissionPolicyToPiArgs(policy) {
|
|
689
719
|
if (policy.network === false) {
|
|
690
720
|
return {
|
|
@@ -703,22 +733,18 @@ function mapPermissionPolicyToPiArgs(policy) {
|
|
|
703
733
|
const denyTools = policy.denyTools ?? [];
|
|
704
734
|
if (policy.mode === "readonly") {
|
|
705
735
|
const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS.includes(tool)) : [...READONLY_TOOLS];
|
|
706
|
-
|
|
707
|
-
return {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const effective = subtractDenied(base, denyTools);
|
|
712
|
-
return { ok: true, args: effective.length === 0 ? ["--no-tools"] : ["--tools", effective.join(",")] };
|
|
736
|
+
if (base.length === 0) return { ok: true, args: ["--no-tools"] };
|
|
737
|
+
return {
|
|
738
|
+
ok: true,
|
|
739
|
+
args: ["--tools", base.join(","), ...denyTools.length > 0 ? ["--exclude-tools", denyTools.join(",")] : []]
|
|
740
|
+
};
|
|
713
741
|
}
|
|
742
|
+
const args = [];
|
|
714
743
|
if (policy.allowTools && policy.allowTools.length > 0) {
|
|
715
|
-
|
|
744
|
+
args.push("--tools", policy.allowTools.join(","));
|
|
716
745
|
}
|
|
717
|
-
|
|
718
|
-
}
|
|
719
|
-
function subtractDenied(tools, denyTools) {
|
|
720
|
-
const denied = new Set(denyTools);
|
|
721
|
-
return tools.filter((tool) => !denied.has(tool));
|
|
746
|
+
if (denyTools.length > 0) args.push("--exclude-tools", denyTools.join(","));
|
|
747
|
+
return { ok: true, args };
|
|
722
748
|
}
|
|
723
749
|
|
|
724
750
|
// src/adapters/pi/events.ts
|
|
@@ -743,7 +769,7 @@ function mapPiMessageToAgentEvent(msg) {
|
|
|
743
769
|
output: { result: msg.result, isError: msg.isError === true }
|
|
744
770
|
};
|
|
745
771
|
}
|
|
746
|
-
case "
|
|
772
|
+
case "agent_settled":
|
|
747
773
|
return { type: "turn_end" };
|
|
748
774
|
/**
|
|
749
775
|
* `artifact` is NOT a real pi RPC message — pi's own `write` tool only
|
|
@@ -779,16 +805,22 @@ function mapPiMessageToAgentEvent(msg) {
|
|
|
779
805
|
// `recordUnmappedFrame`) can tell "known, expected, silently ignored"
|
|
780
806
|
// apart from "genuinely never seen before" (falls to `default` below).
|
|
781
807
|
case "agent_start":
|
|
808
|
+
case "agent_end":
|
|
809
|
+
// one low-level run; `agent_settled` is BYOK completion
|
|
782
810
|
case "turn_start":
|
|
783
811
|
case "turn_end":
|
|
784
|
-
// pi's own per-LLM-turn boundary, not ours
|
|
812
|
+
// pi's own per-LLM-turn boundary, not ours
|
|
785
813
|
case "message_start":
|
|
786
814
|
case "message_end":
|
|
815
|
+
case "bash_execution_update":
|
|
787
816
|
case "tool_execution_update":
|
|
788
817
|
case "queue_update":
|
|
789
818
|
case "compaction_start":
|
|
790
819
|
case "compaction_end":
|
|
791
820
|
case "auto_retry_start":
|
|
821
|
+
case "summarization_retry_scheduled":
|
|
822
|
+
case "summarization_retry_attempt_start":
|
|
823
|
+
case "summarization_retry_finished":
|
|
792
824
|
case "session_info_changed":
|
|
793
825
|
case "thinking_level_changed":
|
|
794
826
|
return void 0;
|
|
@@ -798,15 +830,20 @@ function mapPiMessageToAgentEvent(msg) {
|
|
|
798
830
|
}
|
|
799
831
|
var ROUTINE_PI_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
800
832
|
"agent_start",
|
|
833
|
+
"agent_end",
|
|
801
834
|
"turn_start",
|
|
802
835
|
"turn_end",
|
|
803
836
|
"message_start",
|
|
804
837
|
"message_end",
|
|
838
|
+
"bash_execution_update",
|
|
805
839
|
"tool_execution_update",
|
|
806
840
|
"queue_update",
|
|
807
841
|
"compaction_start",
|
|
808
842
|
"compaction_end",
|
|
809
843
|
"auto_retry_start",
|
|
844
|
+
"summarization_retry_scheduled",
|
|
845
|
+
"summarization_retry_attempt_start",
|
|
846
|
+
"summarization_retry_finished",
|
|
810
847
|
"session_info_changed",
|
|
811
848
|
"thinking_level_changed"
|
|
812
849
|
]);
|
|
@@ -940,10 +977,7 @@ var PiRpcClient = class {
|
|
|
940
977
|
* traffic. Logs once per distinct type (not per occurrence, so a
|
|
941
978
|
* repeating unmapped type can't spam stdout); the running tally is also
|
|
942
979
|
* folded into this client's exit-time error message (`buildExitError`) so
|
|
943
|
-
* a post-mortem on a failed/hung task has it without
|
|
944
|
-
* scraping. This is the exact mechanism that would have turned this
|
|
945
|
-
* task's root-cause hang (`agent_end` arriving with no mapping) into a
|
|
946
|
-
* one-line, immediate warning instead of a silent stall.
|
|
980
|
+
* a post-mortem on a failed/hung task has it without separate log scraping.
|
|
947
981
|
*/
|
|
948
982
|
recordUnmappedFrame(type) {
|
|
949
983
|
const next = (this.unmappedFrameCounts.get(type) ?? 0) + 1;
|
|
@@ -1084,8 +1118,8 @@ var PiAdapter = class {
|
|
|
1084
1118
|
options;
|
|
1085
1119
|
id = "pi";
|
|
1086
1120
|
async detect() {
|
|
1087
|
-
const bin = this.resolveBin();
|
|
1088
1121
|
try {
|
|
1122
|
+
const bin = this.resolveBin();
|
|
1089
1123
|
const { stdout, stderr } = await execFileAsync(bin.command, ["--version"], { timeout: DETECT_TIMEOUT_MS });
|
|
1090
1124
|
const version = stdout.trim() || stderr.trim();
|
|
1091
1125
|
const authPresent = KNOWN_PROVIDER_ENV_VARS.some((name) => process.env[name] !== void 0);
|
|
@@ -1231,7 +1265,7 @@ function resolveApprovalMcpBin() {
|
|
|
1231
1265
|
if (override) {
|
|
1232
1266
|
return { command: override, args: [], source: "env" };
|
|
1233
1267
|
}
|
|
1234
|
-
const distBin =
|
|
1268
|
+
const distBin = path16.join(path16.dirname(fileURLToPath(import.meta.url)), "bin", "byok-approval-mcp.js");
|
|
1235
1269
|
return { command: process.execPath, args: [distBin], source: "dist" };
|
|
1236
1270
|
}
|
|
1237
1271
|
|
|
@@ -1262,7 +1296,7 @@ function mapPermissionPolicyToClaudeArgs(policy) {
|
|
|
1262
1296
|
}
|
|
1263
1297
|
if (policy.mode === "readonly") {
|
|
1264
1298
|
const base = policy.allowTools ? policy.allowTools.filter((tool) => READONLY_TOOLS2.includes(tool)) : [...READONLY_TOOLS2];
|
|
1265
|
-
const effective =
|
|
1299
|
+
const effective = subtractDenied(base, denyTools);
|
|
1266
1300
|
return { ok: true, args: ["--permission-mode", "default", "--tools", effective.join(",")] };
|
|
1267
1301
|
}
|
|
1268
1302
|
if (denyTools.length > 0) {
|
|
@@ -1279,7 +1313,7 @@ function mapPermissionPolicyToClaudeArgs(policy) {
|
|
|
1279
1313
|
}
|
|
1280
1314
|
return { ok: true, args };
|
|
1281
1315
|
}
|
|
1282
|
-
function
|
|
1316
|
+
function subtractDenied(tools, denyTools) {
|
|
1283
1317
|
const denied = new Set(denyTools);
|
|
1284
1318
|
return tools.filter((tool) => !denied.has(tool));
|
|
1285
1319
|
}
|
|
@@ -1311,7 +1345,7 @@ var EXTENSION_CONTENT_TYPES = {
|
|
|
1311
1345
|
".yml": "application/yaml"
|
|
1312
1346
|
};
|
|
1313
1347
|
function guessContentType(filePath) {
|
|
1314
|
-
const ext =
|
|
1348
|
+
const ext = path16.extname(filePath).toLowerCase();
|
|
1315
1349
|
return EXTENSION_CONTENT_TYPES[ext] ?? "application/octet-stream";
|
|
1316
1350
|
}
|
|
1317
1351
|
function mapAssistant(msg, correlation) {
|
|
@@ -1383,11 +1417,11 @@ function tryBuildArtifactEvent(msg, workspaceDir) {
|
|
|
1383
1417
|
const filePath = toolUseResult && typeof toolUseResult.filePath === "string" ? toolUseResult.filePath : void 0;
|
|
1384
1418
|
if (!filePath) return void 0;
|
|
1385
1419
|
const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;
|
|
1386
|
-
const fileDir =
|
|
1420
|
+
const fileDir = path16.dirname(filePath);
|
|
1387
1421
|
const realFileDir = tryRealpath(fileDir) ?? fileDir;
|
|
1388
|
-
const realFilePath =
|
|
1389
|
-
const relative =
|
|
1390
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
1422
|
+
const realFilePath = path16.join(realFileDir, path16.basename(filePath));
|
|
1423
|
+
const relative = path16.relative(realWorkspaceDir, realFilePath);
|
|
1424
|
+
if (relative === "" || relative.startsWith("..") || path16.isAbsolute(relative)) {
|
|
1391
1425
|
return void 0;
|
|
1392
1426
|
}
|
|
1393
1427
|
return { type: "artifact", name: relative, contentType: guessContentType(filePath) };
|
|
@@ -1686,10 +1720,10 @@ var ClaudeAdapter = class {
|
|
|
1686
1720
|
);
|
|
1687
1721
|
}
|
|
1688
1722
|
const approvalMcpBin = (this.options.resolveApprovalMcpBin ?? resolveApprovalMcpBin)();
|
|
1689
|
-
approvalMcpConfigDir = await promises.mkdtemp(
|
|
1723
|
+
approvalMcpConfigDir = await promises.mkdtemp(path16.join(os6.tmpdir(), "byok-approval-mcp-"));
|
|
1690
1724
|
await promises.chmod(approvalMcpConfigDir, 448).catch(() => {
|
|
1691
1725
|
});
|
|
1692
|
-
const mcpConfigPath =
|
|
1726
|
+
const mcpConfigPath = path16.join(approvalMcpConfigDir, "mcp-config.json");
|
|
1693
1727
|
const mcpConfig = {
|
|
1694
1728
|
mcpServers: {
|
|
1695
1729
|
[APPROVAL_MCP_SERVER_NAME]: {
|
|
@@ -2036,8 +2070,8 @@ function extractArtifactEvents(changes, workspaceDir) {
|
|
|
2036
2070
|
const absolutePath = typeof change.path === "string" ? change.path : void 0;
|
|
2037
2071
|
const kind = typeof change.kind === "string" ? change.kind : void 0;
|
|
2038
2072
|
if (!absolutePath || kind === "delete") continue;
|
|
2039
|
-
const relative =
|
|
2040
|
-
if (relative.length === 0 || relative.startsWith("..") ||
|
|
2073
|
+
const relative = path16.relative(workspaceDir, absolutePath);
|
|
2074
|
+
if (relative.length === 0 || relative.startsWith("..") || path16.isAbsolute(relative)) continue;
|
|
2041
2075
|
events.push({ type: "artifact", name: relative, contentType: guessContentType2(relative) });
|
|
2042
2076
|
}
|
|
2043
2077
|
return events;
|
|
@@ -2058,7 +2092,7 @@ var CONTENT_TYPE_BY_EXTENSION = {
|
|
|
2058
2092
|
".csv": "text/csv"
|
|
2059
2093
|
};
|
|
2060
2094
|
function guessContentType2(relativePath) {
|
|
2061
|
-
return CONTENT_TYPE_BY_EXTENSION[
|
|
2095
|
+
return CONTENT_TYPE_BY_EXTENSION[path16.extname(relativePath).toLowerCase()] ?? "application/octet-stream";
|
|
2062
2096
|
}
|
|
2063
2097
|
function extractErrorMessage(rawError) {
|
|
2064
2098
|
if (typeof rawError === "string") return rawError;
|
|
@@ -2219,9 +2253,10 @@ var CodexAdapter = class {
|
|
|
2219
2253
|
* Two independently-verified channel gotchas apply here, the "pi lesson"
|
|
2220
2254
|
* yet again:
|
|
2221
2255
|
* - `codex login status`'s human-readable "Logged in using ChatGPT"
|
|
2222
|
-
* message prints on STDERR, not stdout
|
|
2223
|
-
*
|
|
2224
|
-
*
|
|
2256
|
+
* message prints on STDERR, not stdout — both streams are checked
|
|
2257
|
+
* here for exactly that reason. pi's `--version` is the same class of
|
|
2258
|
+
* hazard from the other direction: its channel has moved between pi
|
|
2259
|
+
* releases (see ../pi/pi-adapter.ts), so neither stream is assumed.
|
|
2225
2260
|
* - The NOT-logged-in message/exit-code shape was deliberately never
|
|
2226
2261
|
* empirically tested: this machine has a real, live ChatGPT login, and
|
|
2227
2262
|
* running `codex logout` to observe the negative case would have
|
|
@@ -2664,12 +2699,12 @@ var DeviceStore = class _DeviceStore {
|
|
|
2664
2699
|
*/
|
|
2665
2700
|
constructor(storeDir, secureDirOptions) {
|
|
2666
2701
|
this.secureDirOptions = secureDirOptions;
|
|
2667
|
-
this.filePath =
|
|
2702
|
+
this.filePath = path16.join(storeDir, "device.json");
|
|
2668
2703
|
}
|
|
2669
2704
|
secureDirOptions;
|
|
2670
2705
|
filePath;
|
|
2671
2706
|
static defaultDir(productId) {
|
|
2672
|
-
return
|
|
2707
|
+
return path16.join(os6.homedir(), ".byok", productId);
|
|
2673
2708
|
}
|
|
2674
2709
|
/**
|
|
2675
2710
|
* Resolve the one store pathname every daemon/CLI component must share.
|
|
@@ -2678,7 +2713,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
2678
2713
|
* cwd to pin a quarantine directory inode.
|
|
2679
2714
|
*/
|
|
2680
2715
|
static resolveDir(productId, configured) {
|
|
2681
|
-
return
|
|
2716
|
+
return path16.resolve(configured ?? _DeviceStore.defaultDir(productId));
|
|
2682
2717
|
}
|
|
2683
2718
|
async load() {
|
|
2684
2719
|
const opened = await this.openBounded();
|
|
@@ -2719,7 +2754,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
2719
2754
|
}
|
|
2720
2755
|
}
|
|
2721
2756
|
async save(record) {
|
|
2722
|
-
const storeDir =
|
|
2757
|
+
const storeDir = path16.dirname(this.filePath);
|
|
2723
2758
|
await ensureSecureDir(storeDir, this.secureDirOptions);
|
|
2724
2759
|
await atomicWriteFile(this.filePath, JSON.stringify(record, null, 2), { mode: 384 });
|
|
2725
2760
|
}
|
|
@@ -3153,19 +3188,19 @@ function shortHash(input) {
|
|
|
3153
3188
|
return createHash("sha256").update(input, "utf8").digest("hex").slice(0, 16);
|
|
3154
3189
|
}
|
|
3155
3190
|
function controlSocketPath(storeDir) {
|
|
3156
|
-
const candidate =
|
|
3191
|
+
const candidate = path16.join(storeDir, "control.sock");
|
|
3157
3192
|
if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT) return candidate;
|
|
3158
|
-
return
|
|
3193
|
+
return path16.join(os6.tmpdir(), `byok-${shortHash(storeDir)}`, "sock");
|
|
3159
3194
|
}
|
|
3160
3195
|
function controlPipeName(productId, storeDir) {
|
|
3161
|
-
const id = shortHash(`${productId}|${
|
|
3196
|
+
const id = shortHash(`${productId}|${path16.resolve(storeDir)}`);
|
|
3162
3197
|
return `\\\\.\\pipe\\byok-${id}`;
|
|
3163
3198
|
}
|
|
3164
3199
|
function controlEndpointPath(productId, storeDir, platform = process.platform) {
|
|
3165
3200
|
return platform === "win32" ? controlPipeName(productId, storeDir) : controlSocketPath(storeDir);
|
|
3166
3201
|
}
|
|
3167
3202
|
function controlTokenPath(storeDir) {
|
|
3168
|
-
return
|
|
3203
|
+
return path16.join(storeDir, "control.token");
|
|
3169
3204
|
}
|
|
3170
3205
|
var SERVER_PROOF_LABEL = "byok-control-server|";
|
|
3171
3206
|
var CLIENT_AUTH_LABEL = "byok-control-client|";
|
|
@@ -3310,7 +3345,7 @@ async function assertOwnedPrivateDir(dir) {
|
|
|
3310
3345
|
}
|
|
3311
3346
|
async function bindControlEndpoint(server, endpoint) {
|
|
3312
3347
|
if (process.platform !== "win32") {
|
|
3313
|
-
const endpointDir =
|
|
3348
|
+
const endpointDir = path16.dirname(endpoint);
|
|
3314
3349
|
await promises.mkdir(endpointDir, { recursive: true, mode: 448 });
|
|
3315
3350
|
await promises.chmod(endpointDir, 448).catch(() => {
|
|
3316
3351
|
});
|
|
@@ -4591,7 +4626,7 @@ function sameFileState2(left, right) {
|
|
|
4591
4626
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
4592
4627
|
}
|
|
4593
4628
|
async function openOperationalHealthFile(storeDir) {
|
|
4594
|
-
const filePath =
|
|
4629
|
+
const filePath = path16.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
4595
4630
|
let namedBefore;
|
|
4596
4631
|
try {
|
|
4597
4632
|
namedBefore = await promises.lstat(filePath, { bigint: true });
|
|
@@ -4633,7 +4668,7 @@ var OperationalHealthTracker = class {
|
|
|
4633
4668
|
#writeTail = Promise.resolve();
|
|
4634
4669
|
#started = false;
|
|
4635
4670
|
constructor(storeDir, options = {}) {
|
|
4636
|
-
this.#filePath =
|
|
4671
|
+
this.#filePath = path16.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
4637
4672
|
this.#windowMs = options.windowMs ?? 6e4;
|
|
4638
4673
|
this.#failureThreshold = options.failureThreshold ?? 3;
|
|
4639
4674
|
this.#maxFailures = options.maxFailures ?? 128;
|
|
@@ -4720,7 +4755,7 @@ var OperationalHealthTracker = class {
|
|
|
4720
4755
|
async #load() {
|
|
4721
4756
|
let opened;
|
|
4722
4757
|
try {
|
|
4723
|
-
opened = await openOperationalHealthFile(
|
|
4758
|
+
opened = await openOperationalHealthFile(path16.dirname(this.#filePath));
|
|
4724
4759
|
} catch (err) {
|
|
4725
4760
|
throw new Error("operational health state could not be read");
|
|
4726
4761
|
}
|
|
@@ -4756,7 +4791,7 @@ var OperationalHealthTracker = class {
|
|
|
4756
4791
|
if (!this.#state) return;
|
|
4757
4792
|
const body = JSON.stringify(this.#state, null, 2);
|
|
4758
4793
|
this.#writeTail = this.#writeTail.then(async () => {
|
|
4759
|
-
await ensureSecureDir(
|
|
4794
|
+
await ensureSecureDir(path16.dirname(this.#filePath));
|
|
4760
4795
|
await atomicWriteFile(this.#filePath, body, { mode: 384, fsync: true });
|
|
4761
4796
|
});
|
|
4762
4797
|
try {
|
|
@@ -5075,8 +5110,8 @@ async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */
|
|
|
5075
5110
|
await mutex.close().catch(() => void 0);
|
|
5076
5111
|
throw err;
|
|
5077
5112
|
}
|
|
5078
|
-
const ownerPath =
|
|
5079
|
-
const reclaimPath =
|
|
5113
|
+
const ownerPath = path16.join(storeDir, DAEMON_OWNER_FILENAME);
|
|
5114
|
+
const reclaimPath = path16.join(storeDir, RECLAIM_FILENAME);
|
|
5080
5115
|
const record = {
|
|
5081
5116
|
version: 2,
|
|
5082
5117
|
pid: process.pid,
|
|
@@ -5153,7 +5188,7 @@ var CursorStore = class {
|
|
|
5153
5188
|
storeDir;
|
|
5154
5189
|
fileFor(serverUrl, deviceId) {
|
|
5155
5190
|
const key = createHash("sha256").update(`${serverUrl}::${deviceId}`).digest("hex").slice(0, 32);
|
|
5156
|
-
return
|
|
5191
|
+
return path16.join(this.storeDir, `cursor-${key}.json`);
|
|
5157
5192
|
}
|
|
5158
5193
|
async load(serverUrl, deviceId) {
|
|
5159
5194
|
let raw;
|
|
@@ -5173,7 +5208,7 @@ var CursorStore = class {
|
|
|
5173
5208
|
}
|
|
5174
5209
|
async save(serverUrl, deviceId, cursor) {
|
|
5175
5210
|
const file = this.fileFor(serverUrl, deviceId);
|
|
5176
|
-
await promises.mkdir(
|
|
5211
|
+
await promises.mkdir(path16.dirname(file), { recursive: true, mode: 448 });
|
|
5177
5212
|
await atomicWriteFile(file, JSON.stringify({ cursor }));
|
|
5178
5213
|
}
|
|
5179
5214
|
/** 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. */
|
|
@@ -5469,7 +5504,7 @@ var SessionWorkspaceStore = class {
|
|
|
5469
5504
|
*/
|
|
5470
5505
|
queue = Promise.resolve();
|
|
5471
5506
|
constructor(storeDir) {
|
|
5472
|
-
this.filePath =
|
|
5507
|
+
this.filePath = path16.join(storeDir, "session-workspaces.json");
|
|
5473
5508
|
}
|
|
5474
5509
|
async get(sessionRef) {
|
|
5475
5510
|
return this.enqueue(async () => {
|
|
@@ -5527,7 +5562,7 @@ var SessionWorkspaceStore = class {
|
|
|
5527
5562
|
}
|
|
5528
5563
|
}
|
|
5529
5564
|
async save(all) {
|
|
5530
|
-
const dir =
|
|
5565
|
+
const dir = path16.dirname(this.filePath);
|
|
5531
5566
|
await promises.mkdir(dir, { recursive: true, mode: 448 });
|
|
5532
5567
|
const tmpPath = `${this.filePath}.${process.pid}-${tmpSeq2++}.tmp`;
|
|
5533
5568
|
try {
|
|
@@ -5632,9 +5667,9 @@ function isSqliteAvailable() {
|
|
|
5632
5667
|
}
|
|
5633
5668
|
}
|
|
5634
5669
|
var SECURE_FILE_MODE = 384;
|
|
5635
|
-
function openJournalDatabase(
|
|
5670
|
+
function openJournalDatabase(path20, busyTimeoutMs, faults) {
|
|
5636
5671
|
const { DatabaseSync } = loadSqliteModule();
|
|
5637
|
-
const db = new DatabaseSync(
|
|
5672
|
+
const db = new DatabaseSync(path20, { timeout: busyTimeoutMs });
|
|
5638
5673
|
try {
|
|
5639
5674
|
faults?.onStep?.("after-open");
|
|
5640
5675
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL;");
|
|
@@ -5777,9 +5812,9 @@ var RECEIVED_STATE = "received";
|
|
|
5777
5812
|
function byteLength(value) {
|
|
5778
5813
|
return Buffer.byteLength(value, "utf8");
|
|
5779
5814
|
}
|
|
5780
|
-
function fileBytes(
|
|
5815
|
+
function fileBytes(path20) {
|
|
5781
5816
|
try {
|
|
5782
|
-
return statSync(
|
|
5817
|
+
return statSync(path20).size;
|
|
5783
5818
|
} catch {
|
|
5784
5819
|
return 0;
|
|
5785
5820
|
}
|
|
@@ -7033,8 +7068,8 @@ function estimateEventBytes(event) {
|
|
|
7033
7068
|
}
|
|
7034
7069
|
async function openArtifact(workspaceDir, name) {
|
|
7035
7070
|
const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
|
|
7036
|
-
const candidate =
|
|
7037
|
-
const prefix = realWorkspaceDir.endsWith(
|
|
7071
|
+
const candidate = path16.resolve(realWorkspaceDir, name);
|
|
7072
|
+
const prefix = realWorkspaceDir.endsWith(path16.sep) ? realWorkspaceDir : realWorkspaceDir + path16.sep;
|
|
7038
7073
|
if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
|
|
7039
7074
|
return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
|
|
7040
7075
|
}
|
|
@@ -7432,7 +7467,7 @@ var TaskRunner = class {
|
|
|
7432
7467
|
const sameProtocolTask = ledger?.taskId === taskId;
|
|
7433
7468
|
const interruptedOldTask = ledger?.phase === "interrupted" && sameProtocolTask;
|
|
7434
7469
|
const activeDifferentTask = ledger !== void 0 && ledger.taskId !== taskId && (ledger.phase === "preparing" || ledger.phase === "active");
|
|
7435
|
-
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== payload.sessionRef ||
|
|
7470
|
+
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== payload.sessionRef || path16.resolve(ledger.workspaceDir) !== path16.resolve(known.workspaceDir) || interruptedOldTask || activeDifferentTask) {
|
|
7436
7471
|
this.decline(taskId, "session is incompatible with Git workspace mode", true);
|
|
7437
7472
|
return;
|
|
7438
7473
|
}
|
|
@@ -7447,7 +7482,7 @@ var TaskRunner = class {
|
|
|
7447
7482
|
return;
|
|
7448
7483
|
}
|
|
7449
7484
|
} else {
|
|
7450
|
-
workspaceDir =
|
|
7485
|
+
workspaceDir = path16.join(this.deps.workspaceRoot, taskId);
|
|
7451
7486
|
}
|
|
7452
7487
|
try {
|
|
7453
7488
|
gitLease = await gitManager.acquireLease(workspaceDir, payload.sessionRef);
|
|
@@ -7457,7 +7492,7 @@ var TaskRunner = class {
|
|
|
7457
7492
|
}
|
|
7458
7493
|
} else if (!this.deps.gitWorkspaceManager && !this.deps.gitWorkspaceStore) {
|
|
7459
7494
|
known = payload.sessionRef ? await this.deps.sessionWorkspaces.get(payload.sessionRef) : void 0;
|
|
7460
|
-
workspaceDir = known?.workspaceDir ??
|
|
7495
|
+
workspaceDir = known?.workspaceDir ?? path16.join(this.deps.workspaceRoot, taskId);
|
|
7461
7496
|
plainWorkspaceNeedsResolve = true;
|
|
7462
7497
|
} else {
|
|
7463
7498
|
this.decline(taskId, "workspace mode is unavailable", true);
|
|
@@ -8329,7 +8364,7 @@ var TaskRunner = class {
|
|
|
8329
8364
|
}
|
|
8330
8365
|
/** `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. */
|
|
8331
8366
|
async resolveWorkspaceDir(taskId, reuseDir) {
|
|
8332
|
-
const dir = reuseDir ??
|
|
8367
|
+
const dir = reuseDir ?? path16.join(this.deps.workspaceRoot, taskId);
|
|
8333
8368
|
await promises.mkdir(dir, { recursive: true });
|
|
8334
8369
|
return dir;
|
|
8335
8370
|
}
|
|
@@ -9231,8 +9266,8 @@ var TruthMemoryClient = class {
|
|
|
9231
9266
|
#requestId;
|
|
9232
9267
|
#allowedObjectDownloadOrigins;
|
|
9233
9268
|
async listManifest(query = {}) {
|
|
9234
|
-
const
|
|
9235
|
-
const response = await this.#proofFetch(
|
|
9269
|
+
const path20 = manifestPath(query);
|
|
9270
|
+
const response = await this.#proofFetch(path20, {
|
|
9236
9271
|
method: "GET",
|
|
9237
9272
|
operation: "truth.list",
|
|
9238
9273
|
resource: "records",
|
|
@@ -9339,9 +9374,9 @@ var TruthMemoryClient = class {
|
|
|
9339
9374
|
}
|
|
9340
9375
|
async #write(kind, recordKey, requestId, payload, expectedPrimary, expectedSnapshots) {
|
|
9341
9376
|
assertDistinctExpectedWrites([expectedPrimary, ...expectedSnapshots]);
|
|
9342
|
-
const
|
|
9377
|
+
const path20 = recordPath(kind, recordKey);
|
|
9343
9378
|
const body = new TextEncoder().encode(JSON.stringify(payload));
|
|
9344
|
-
const response = await this.#proofFetch(
|
|
9379
|
+
const response = await this.#proofFetch(path20, {
|
|
9345
9380
|
method: "PUT",
|
|
9346
9381
|
operation: "truth.write",
|
|
9347
9382
|
resource: `${kind}/${recordKey}`,
|
|
@@ -9367,8 +9402,8 @@ var TruthMemoryClient = class {
|
|
|
9367
9402
|
};
|
|
9368
9403
|
}
|
|
9369
9404
|
async #readVerified(listed) {
|
|
9370
|
-
const
|
|
9371
|
-
const response = await this.#proofFetch(
|
|
9405
|
+
const path20 = recordPath(listed.kind, listed.recordKey);
|
|
9406
|
+
const response = await this.#proofFetch(path20, {
|
|
9372
9407
|
method: "GET",
|
|
9373
9408
|
operation: "truth.read",
|
|
9374
9409
|
resource: `${listed.kind}/${listed.recordKey}`,
|
|
@@ -9429,16 +9464,16 @@ var TruthMemoryClient = class {
|
|
|
9429
9464
|
}
|
|
9430
9465
|
return { ...listed, bytes };
|
|
9431
9466
|
}
|
|
9432
|
-
async #proofFetch(
|
|
9467
|
+
async #proofFetch(path20, request) {
|
|
9433
9468
|
const proof = await this.options.signer.sign({
|
|
9434
9469
|
method: request.method,
|
|
9435
|
-
path:
|
|
9470
|
+
path: path20,
|
|
9436
9471
|
operation: request.operation,
|
|
9437
9472
|
resource: request.resource,
|
|
9438
9473
|
requestId: request.requestId,
|
|
9439
9474
|
body: request.body
|
|
9440
9475
|
});
|
|
9441
|
-
const response = await this.#fetch(new URL(
|
|
9476
|
+
const response = await this.#fetch(new URL(path20, this.#base), {
|
|
9442
9477
|
method: request.method,
|
|
9443
9478
|
headers: {
|
|
9444
9479
|
...request.headers,
|
|
@@ -9449,7 +9484,7 @@ var TruthMemoryClient = class {
|
|
|
9449
9484
|
if (!response.ok) {
|
|
9450
9485
|
throw new TruthMemoryClientError(
|
|
9451
9486
|
"truth_http_failed",
|
|
9452
|
-
`truth request ${request.method} ${
|
|
9487
|
+
`truth request ${request.method} ${path20} failed with HTTP ${response.status}`,
|
|
9453
9488
|
response.status
|
|
9454
9489
|
);
|
|
9455
9490
|
}
|
|
@@ -9708,8 +9743,8 @@ function generateLaunchdPlist(def) {
|
|
|
9708
9743
|
const { label, program, logDir } = def;
|
|
9709
9744
|
const args = [program.command, ...program.args];
|
|
9710
9745
|
const cwd = program.cwd ?? os6.homedir();
|
|
9711
|
-
const outLog =
|
|
9712
|
-
const errLog =
|
|
9746
|
+
const outLog = path16.join(logDir, `${label}.out.log`);
|
|
9747
|
+
const errLog = path16.join(logDir, `${label}.err.log`);
|
|
9713
9748
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
9714
9749
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
9715
9750
|
<plist version="1.0">
|
|
@@ -9750,7 +9785,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
9750
9785
|
return process.getuid();
|
|
9751
9786
|
});
|
|
9752
9787
|
const label = sanitizeServiceName(def.name);
|
|
9753
|
-
const plistPath = () =>
|
|
9788
|
+
const plistPath = () => path16.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
9754
9789
|
const domainTarget = () => `gui/${getuid()}`;
|
|
9755
9790
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
9756
9791
|
async function fileExists(p) {
|
|
@@ -9763,7 +9798,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
9763
9798
|
}
|
|
9764
9799
|
async function writePlist(program) {
|
|
9765
9800
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
9766
|
-
await fs15.mkdir(
|
|
9801
|
+
await fs15.mkdir(path16.dirname(plistPath()), { recursive: true });
|
|
9767
9802
|
await fs15.mkdir(def.logDir, { recursive: true });
|
|
9768
9803
|
await fs15.writeFile(plistPath(), xml, "utf8");
|
|
9769
9804
|
}
|
|
@@ -9837,8 +9872,8 @@ function generateSystemdUnit(def) {
|
|
|
9837
9872
|
assertNoControlChars(displayName, "displayName");
|
|
9838
9873
|
const cwd = program.cwd ?? os6.homedir();
|
|
9839
9874
|
assertNoControlChars(cwd, "program.cwd");
|
|
9840
|
-
const outLog =
|
|
9841
|
-
const errLog =
|
|
9875
|
+
const outLog = path16.join(logDir, `${name}.out.log`);
|
|
9876
|
+
const errLog = path16.join(logDir, `${name}.err.log`);
|
|
9842
9877
|
assertNoControlChars(outLog, "logDir");
|
|
9843
9878
|
assertNoControlChars(errLog, "logDir");
|
|
9844
9879
|
const execStart = [program.command, ...program.args].map(quoteSystemdArg).join(" ");
|
|
@@ -9864,7 +9899,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
9864
9899
|
const homedir = deps.homedir ?? (() => os6.homedir());
|
|
9865
9900
|
const name = sanitizeServiceName(def.name);
|
|
9866
9901
|
const unitName = `${name}.service`;
|
|
9867
|
-
const unitPath = () =>
|
|
9902
|
+
const unitPath = () => path16.join(homedir(), ".config", "systemd", "user", unitName);
|
|
9868
9903
|
async function fileExists(p) {
|
|
9869
9904
|
try {
|
|
9870
9905
|
await fs15.stat(p);
|
|
@@ -9875,7 +9910,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
9875
9910
|
}
|
|
9876
9911
|
async function writeUnit(program) {
|
|
9877
9912
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
9878
|
-
await fs15.mkdir(
|
|
9913
|
+
await fs15.mkdir(path16.dirname(unitPath()), { recursive: true });
|
|
9879
9914
|
await fs15.mkdir(def.logDir, { recursive: true });
|
|
9880
9915
|
await fs15.writeFile(unitPath(), unit, "utf8");
|
|
9881
9916
|
}
|
|
@@ -9957,8 +9992,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
9957
9992
|
const winswBin = windows.winswBin;
|
|
9958
9993
|
const id = sanitizeServiceName(def.name);
|
|
9959
9994
|
const installDir = windows.installDir ?? def.logDir;
|
|
9960
|
-
const exePath =
|
|
9961
|
-
const xmlPath =
|
|
9995
|
+
const exePath = path16.join(installDir, `${id}.exe`);
|
|
9996
|
+
const xmlPath = path16.join(installDir, `${id}.xml`);
|
|
9962
9997
|
async function fileExists(p) {
|
|
9963
9998
|
try {
|
|
9964
9999
|
await fs15.stat(p);
|