@byok-sdk/client 0.9.1 → 0.10.1
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/dist/bin/byok-agent.js +98 -28
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/daemon/auth-manager.d.ts +8 -0
- package/dist/daemon/create-daemon.d.ts +8 -0
- package/dist/daemon/machine-id.d.ts +25 -0
- package/dist/index.js +97 -27
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -15,6 +15,14 @@ export interface AuthManagerOptions {
|
|
|
15
15
|
/** Internal-only credential custody seam. Product construction uses store.credentials. */
|
|
16
16
|
credentials?: DeviceCredentialStore | InMemoryDeviceCredentialStore;
|
|
17
17
|
deviceName?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional resolver for the client-hashed physical machine identity sent
|
|
20
|
+
* with `POST /byok/pair` (protocol §6.1). Resolved once per pair attempt; a
|
|
21
|
+
* resolver that yields `undefined` omits the field entirely rather than
|
|
22
|
+
* sending a placeholder, because the server treats its presence as
|
|
23
|
+
* permission to supersede this machine's prior active device rows.
|
|
24
|
+
*/
|
|
25
|
+
machineId?: () => Promise<string | undefined>;
|
|
18
26
|
/** Called once revocation is detected, so a caller (ConnectionManager) can stop retrying and surface the state instead of looping. */
|
|
19
27
|
onRevoked?: () => void;
|
|
20
28
|
}
|
|
@@ -91,6 +91,14 @@ export interface DaemonConfig {
|
|
|
91
91
|
productId: string;
|
|
92
92
|
serverUrl: string;
|
|
93
93
|
deviceName?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Optional override for the client-hashed physical machine identity sent
|
|
96
|
+
* with `POST /byok/pair` (protocol §6.1). Defaults to `resolveMachineId`
|
|
97
|
+
* over this product id, which probes one OS identifier and hashes it; a
|
|
98
|
+
* host that has its own machine authority can supply it here, and one that
|
|
99
|
+
* wants no supersession at all supplies `async () => undefined`.
|
|
100
|
+
*/
|
|
101
|
+
machineId?: () => Promise<string | undefined>;
|
|
94
102
|
workspaceRoot: string;
|
|
95
103
|
/**
|
|
96
104
|
* Strict Agent execution boundary. The host selects one absolute branded
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DeviceCommandRunner } from './device-credential-store';
|
|
2
|
+
export interface ResolveMachineIdOptions {
|
|
3
|
+
/** Domain separator for the digest — the same machine yields a different value per product. */
|
|
4
|
+
readonly productId: string;
|
|
5
|
+
readonly platform?: NodeJS.Platform;
|
|
6
|
+
/** Injectable process runner, same shape as the credential store's. Tests substitute a double; production gets `runDeviceCommand`. */
|
|
7
|
+
readonly run?: DeviceCommandRunner;
|
|
8
|
+
/** Injectable file read for the Linux probe, for the same reason `run` is injectable. */
|
|
9
|
+
readonly readFile?: (path: string) => Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Upper bound on the whole probe, in milliseconds (default 2000). A probe
|
|
12
|
+
* that never settles — a wedged `ioreg`, a hung registry read, an NFS-backed
|
|
13
|
+
* `/etc/machine-id` — must not stall pairing, because `AuthManager` awaits
|
|
14
|
+
* this value inline before `POST /byok/pair`. On expiry the resolution is
|
|
15
|
+
* `undefined`, the same as every other failure shape.
|
|
16
|
+
*/
|
|
17
|
+
readonly timeoutMs?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The lowercase-hex digest for this machine, or `undefined` when no OS
|
|
21
|
+
* identifier is available. Never throws: every probe failure, every
|
|
22
|
+
* unsupported platform, every empty read, and a probe that exceeds
|
|
23
|
+
* `timeoutMs` all collapse to the same `undefined`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveMachineId(options: ResolveMachineIdOptions): Promise<string | undefined>;
|
package/dist/index.js
CHANGED
|
@@ -6275,13 +6275,15 @@ var AuthManager = class {
|
|
|
6275
6275
|
}
|
|
6276
6276
|
const keyPair = existing ? { privateKey: importPrivateKeyPem(existing.devicePrivateKeyPem), publicKeyBase64Url: existing.devicePublicKey } : generateDeviceKeyPair();
|
|
6277
6277
|
const url = new URL(BYOK_PAIR_PATH, toHttpBase(this.opts.serverUrl));
|
|
6278
|
+
const machineId = await this.opts.machineId?.();
|
|
6278
6279
|
const res = await fetch(url, {
|
|
6279
6280
|
method: "POST",
|
|
6280
6281
|
headers: { "content-type": "application/json" },
|
|
6281
6282
|
body: JSON.stringify({
|
|
6282
6283
|
pairingCode,
|
|
6283
6284
|
deviceName: this.opts.deviceName ?? os4__default.hostname(),
|
|
6284
|
-
devicePublicKey: keyPair.publicKeyBase64Url
|
|
6285
|
+
devicePublicKey: keyPair.publicKeyBase64Url,
|
|
6286
|
+
...machineId === void 0 ? {} : { machineId }
|
|
6285
6287
|
})
|
|
6286
6288
|
});
|
|
6287
6289
|
if (!res.ok) {
|
|
@@ -6590,6 +6592,67 @@ var BlobClient = class {
|
|
|
6590
6592
|
throw lastFailure;
|
|
6591
6593
|
}
|
|
6592
6594
|
};
|
|
6595
|
+
var DEFAULT_TIMEOUT_MS2 = 2e3;
|
|
6596
|
+
var DARWIN_UUID_RE = /"IOPlatformUUID"\s*=\s*"([^"]+)"/u;
|
|
6597
|
+
var WIN32_GUID_RE = /MachineGuid\s+REG_SZ\s+(\S+)/u;
|
|
6598
|
+
var LINUX_MACHINE_ID_PATHS = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
|
|
6599
|
+
async function probeDarwin(run) {
|
|
6600
|
+
const result = await run("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"]);
|
|
6601
|
+
if (result.exitCode !== 0) return void 0;
|
|
6602
|
+
return DARWIN_UUID_RE.exec(result.stdout)?.[1];
|
|
6603
|
+
}
|
|
6604
|
+
async function probeLinux(readFile3) {
|
|
6605
|
+
for (const path38 of LINUX_MACHINE_ID_PATHS) {
|
|
6606
|
+
try {
|
|
6607
|
+
const contents = (await readFile3(path38)).trim();
|
|
6608
|
+
if (contents.length > 0) return contents;
|
|
6609
|
+
} catch {
|
|
6610
|
+
}
|
|
6611
|
+
}
|
|
6612
|
+
return void 0;
|
|
6613
|
+
}
|
|
6614
|
+
async function probeWin32(run) {
|
|
6615
|
+
const result = await run("reg", [
|
|
6616
|
+
"query",
|
|
6617
|
+
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
|
|
6618
|
+
"/v",
|
|
6619
|
+
"MachineGuid"
|
|
6620
|
+
]);
|
|
6621
|
+
if (result.exitCode !== 0) return void 0;
|
|
6622
|
+
return WIN32_GUID_RE.exec(result.stdout)?.[1];
|
|
6623
|
+
}
|
|
6624
|
+
async function withTimeout(probe, timeoutMs) {
|
|
6625
|
+
let timer;
|
|
6626
|
+
const expiry = new Promise((resolve) => {
|
|
6627
|
+
timer = setTimeout(() => resolve(void 0), timeoutMs);
|
|
6628
|
+
timer.unref?.();
|
|
6629
|
+
});
|
|
6630
|
+
try {
|
|
6631
|
+
return await Promise.race([probe, expiry]);
|
|
6632
|
+
} finally {
|
|
6633
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
6636
|
+
async function resolveMachineId(options) {
|
|
6637
|
+
const platform = options.platform ?? process.platform;
|
|
6638
|
+
const run = options.run ?? runDeviceCommand;
|
|
6639
|
+
const readFile3 = options.readFile ?? ((path38) => fs13.readFile(path38, "utf8"));
|
|
6640
|
+
let probe;
|
|
6641
|
+
if (platform === "darwin") probe = probeDarwin(run);
|
|
6642
|
+
else if (platform === "linux") probe = probeLinux(readFile3);
|
|
6643
|
+
else if (platform === "win32") probe = probeWin32(run);
|
|
6644
|
+
if (probe === void 0) return void 0;
|
|
6645
|
+
let raw;
|
|
6646
|
+
try {
|
|
6647
|
+
raw = await withTimeout(probe, options.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
|
|
6648
|
+
} catch {
|
|
6649
|
+
return void 0;
|
|
6650
|
+
}
|
|
6651
|
+
const trimmed = raw?.trim();
|
|
6652
|
+
if (trimmed === void 0 || trimmed.length === 0) return void 0;
|
|
6653
|
+
return createHash("sha256").update(`${options.productId}
|
|
6654
|
+
${trimmed}`, "utf8").digest("hex");
|
|
6655
|
+
}
|
|
6593
6656
|
var PRESENCE_HINTS_CAPABILITY = "presence.hints";
|
|
6594
6657
|
var CapabilityDiscoveryError = class extends Error {
|
|
6595
6658
|
constructor(message, options) {
|
|
@@ -11189,7 +11252,7 @@ async function readPinnedFile(directory, fileName, maxBytes = AGENT_MEMORY_MAX_F
|
|
|
11189
11252
|
});
|
|
11190
11253
|
}
|
|
11191
11254
|
}
|
|
11192
|
-
async function
|
|
11255
|
+
async function readFile2(context, relativePath, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
|
|
11193
11256
|
if (context.filesystem !== void 0) return context.filesystem.read(relativePath, maxBytes);
|
|
11194
11257
|
return withMemoryParent(context, relativePath, (directory, fileName) => readPinnedFile(directory, fileName, maxBytes));
|
|
11195
11258
|
}
|
|
@@ -11357,7 +11420,7 @@ var AgentMemoryService = class {
|
|
|
11357
11420
|
const context = taskContext(this.input);
|
|
11358
11421
|
const relativePath = validateAgentMemoryPath(input.path);
|
|
11359
11422
|
if (input.ifRevision !== void 0 && !revision(input.ifRevision)) throw new AgentMemoryError("ifRevision must be a sha256 content revision");
|
|
11360
|
-
const current = await
|
|
11423
|
+
const current = await readFile2(context, relativePath);
|
|
11361
11424
|
if (!current.exists) throw new AgentMemoryError("memory file does not exist");
|
|
11362
11425
|
if (input.ifRevision !== void 0 && current.revision !== input.ifRevision) throw new AgentMemoryRevisionConflictError(input.ifRevision, current.revision);
|
|
11363
11426
|
const auditWarning = await exclusiveAgentMemoryHome(context.canonicalHome, () => recordAuditWarning(context, "recall", {
|
|
@@ -11444,7 +11507,7 @@ async function captureAgentMemorySnapshot(input) {
|
|
|
11444
11507
|
const files = [];
|
|
11445
11508
|
let totalBytes = 0;
|
|
11446
11509
|
for (const relativePath of paths) {
|
|
11447
|
-
const current = await
|
|
11510
|
+
const current = await readFile2(context, relativePath);
|
|
11448
11511
|
if (!current.exists) {
|
|
11449
11512
|
if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md disappeared before snapshot");
|
|
11450
11513
|
continue;
|
|
@@ -12960,6 +13023,7 @@ var TaskRunner = class {
|
|
|
12960
13023
|
gitLease,
|
|
12961
13024
|
gitBaseline,
|
|
12962
13025
|
summaryParts: [],
|
|
13026
|
+
finalTextParts: [],
|
|
12963
13027
|
batcher: new ProgressBatcher(
|
|
12964
13028
|
(seq, events) => {
|
|
12965
13029
|
const projected = active.egressEnabled ? this.deps.agentEgress?.projectLatestValue({
|
|
@@ -13265,6 +13329,9 @@ var TaskRunner = class {
|
|
|
13265
13329
|
if (event.type === "usage") {
|
|
13266
13330
|
active.lastUsage = event;
|
|
13267
13331
|
}
|
|
13332
|
+
if (event.type === "tool_use" || event.type === "tool_result" || event.type === "needs_approval") {
|
|
13333
|
+
active.finalTextParts.length = 0;
|
|
13334
|
+
}
|
|
13268
13335
|
if (event.type === "needs_approval") {
|
|
13269
13336
|
active.batcher.flush();
|
|
13270
13337
|
const { taskId } = active;
|
|
@@ -13298,7 +13365,8 @@ var TaskRunner = class {
|
|
|
13298
13365
|
this.sendAgentMessageRecord(outbox, record);
|
|
13299
13366
|
return;
|
|
13300
13367
|
}
|
|
13301
|
-
const
|
|
13368
|
+
const finalTextRun = active.finalTextParts.join("").trim();
|
|
13369
|
+
const body = finalTextRun !== "" ? finalTextRun : finalOutput.trim();
|
|
13302
13370
|
if (outbox === void 0 || active.agentRef === void 0) {
|
|
13303
13371
|
active.pendingMessageCompletion = void 0;
|
|
13304
13372
|
await this.fail(active.taskId, "required Agent message lane is unavailable for this task", false);
|
|
@@ -13342,6 +13410,7 @@ var TaskRunner = class {
|
|
|
13342
13410
|
}
|
|
13343
13411
|
if (event.type === "progress") {
|
|
13344
13412
|
active.summaryParts.push(event.text);
|
|
13413
|
+
active.finalTextParts.push(event.text);
|
|
13345
13414
|
}
|
|
13346
13415
|
if (event.type === "artifact") {
|
|
13347
13416
|
if (active.agentRef !== void 0 && this.deps.agentEgress !== void 0) {
|
|
@@ -16426,6 +16495,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
16426
16495
|
serverUrl: config.serverUrl,
|
|
16427
16496
|
store,
|
|
16428
16497
|
deviceName: config.deviceName,
|
|
16498
|
+
machineId: config.machineId ?? (() => resolveMachineId({ productId: config.productId })),
|
|
16429
16499
|
onRevoked: () => {
|
|
16430
16500
|
connectionState = "revoked";
|
|
16431
16501
|
}
|
|
@@ -17724,7 +17794,7 @@ function connectAndHandshake(endpoint, token, opts) {
|
|
|
17724
17794
|
});
|
|
17725
17795
|
});
|
|
17726
17796
|
}
|
|
17727
|
-
function
|
|
17797
|
+
function withTimeout2(promise, ms, message) {
|
|
17728
17798
|
return new Promise((resolve, reject) => {
|
|
17729
17799
|
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
17730
17800
|
timer.unref?.();
|
|
@@ -17803,7 +17873,7 @@ function createControlClient(socket, reader, opts) {
|
|
|
17803
17873
|
async request(method, params) {
|
|
17804
17874
|
if (closed) throw new Error("control connection is closed");
|
|
17805
17875
|
const { promise } = send(method, params);
|
|
17806
|
-
const result = await
|
|
17876
|
+
const result = await withTimeout2(promise, opts.requestTimeoutMs ?? 1e4, `control request "${method}" timed out`);
|
|
17807
17877
|
return result;
|
|
17808
17878
|
},
|
|
17809
17879
|
subscribe(method, params, onEvent) {
|
|
@@ -18740,7 +18810,7 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
|
|
|
18740
18810
|
}
|
|
18741
18811
|
function createLaunchdLifecycle(def, deps = {}) {
|
|
18742
18812
|
const run = deps.run ?? defaultRunner;
|
|
18743
|
-
const
|
|
18813
|
+
const fs30 = deps.fs ?? promises;
|
|
18744
18814
|
const homedir = deps.homedir ?? (() => os4__default.homedir());
|
|
18745
18815
|
const getuid = deps.getuid ?? (() => {
|
|
18746
18816
|
if (typeof process.getuid !== "function") {
|
|
@@ -18754,7 +18824,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
18754
18824
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
18755
18825
|
async function fileExists(p) {
|
|
18756
18826
|
try {
|
|
18757
|
-
await
|
|
18827
|
+
await fs30.stat(p);
|
|
18758
18828
|
return true;
|
|
18759
18829
|
} catch {
|
|
18760
18830
|
return false;
|
|
@@ -18762,9 +18832,9 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
18762
18832
|
}
|
|
18763
18833
|
async function writePlist(program) {
|
|
18764
18834
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
18765
|
-
await
|
|
18766
|
-
await
|
|
18767
|
-
await
|
|
18835
|
+
await fs30.mkdir(path3__default.dirname(plistPath()), { recursive: true });
|
|
18836
|
+
await fs30.mkdir(def.logDir, { recursive: true });
|
|
18837
|
+
await fs30.writeFile(plistPath(), xml, "utf8");
|
|
18768
18838
|
}
|
|
18769
18839
|
async function install(opts = {}) {
|
|
18770
18840
|
await writePlist(opts.program ?? def.program);
|
|
@@ -18775,7 +18845,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
18775
18845
|
}
|
|
18776
18846
|
async function uninstall() {
|
|
18777
18847
|
await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
|
|
18778
|
-
await
|
|
18848
|
+
await fs30.rm(plistPath(), { force: true });
|
|
18779
18849
|
}
|
|
18780
18850
|
async function start() {
|
|
18781
18851
|
if (!await fileExists(plistPath())) {
|
|
@@ -18859,14 +18929,14 @@ WantedBy=default.target
|
|
|
18859
18929
|
}
|
|
18860
18930
|
function createSystemdLifecycle(def, deps = {}) {
|
|
18861
18931
|
const run = deps.run ?? defaultRunner;
|
|
18862
|
-
const
|
|
18932
|
+
const fs30 = deps.fs ?? promises;
|
|
18863
18933
|
const homedir = deps.homedir ?? (() => os4__default.homedir());
|
|
18864
18934
|
const name = sanitizeServiceName(def.name);
|
|
18865
18935
|
const unitName = `${name}.service`;
|
|
18866
18936
|
const unitPath = () => path3__default.join(homedir(), ".config", "systemd", "user", unitName);
|
|
18867
18937
|
async function fileExists(p) {
|
|
18868
18938
|
try {
|
|
18869
|
-
await
|
|
18939
|
+
await fs30.stat(p);
|
|
18870
18940
|
return true;
|
|
18871
18941
|
} catch {
|
|
18872
18942
|
return false;
|
|
@@ -18874,9 +18944,9 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
18874
18944
|
}
|
|
18875
18945
|
async function writeUnit(program) {
|
|
18876
18946
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
18877
|
-
await
|
|
18878
|
-
await
|
|
18879
|
-
await
|
|
18947
|
+
await fs30.mkdir(path3__default.dirname(unitPath()), { recursive: true });
|
|
18948
|
+
await fs30.mkdir(def.logDir, { recursive: true });
|
|
18949
|
+
await fs30.writeFile(unitPath(), unit, "utf8");
|
|
18880
18950
|
}
|
|
18881
18951
|
async function install(opts = {}) {
|
|
18882
18952
|
await writeUnit(opts.program ?? def.program);
|
|
@@ -18885,7 +18955,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
18885
18955
|
}
|
|
18886
18956
|
async function uninstall() {
|
|
18887
18957
|
await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
|
|
18888
|
-
await
|
|
18958
|
+
await fs30.rm(unitPath(), { force: true });
|
|
18889
18959
|
await run("systemctl", ["--user", "daemon-reload"]);
|
|
18890
18960
|
}
|
|
18891
18961
|
async function start() {
|
|
@@ -18948,7 +19018,7 @@ ${argXml}${cwdXml}
|
|
|
18948
19018
|
}
|
|
18949
19019
|
function createWinswLifecycle(def, deps = {}) {
|
|
18950
19020
|
const run = deps.run ?? defaultRunner;
|
|
18951
|
-
const
|
|
19021
|
+
const fs30 = deps.fs ?? promises;
|
|
18952
19022
|
const windows = def.windows;
|
|
18953
19023
|
if (!windows) {
|
|
18954
19024
|
throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
|
|
@@ -18960,7 +19030,7 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
18960
19030
|
const xmlPath = path3__default.join(installDir, `${id}.xml`);
|
|
18961
19031
|
async function fileExists(p) {
|
|
18962
19032
|
try {
|
|
18963
|
-
await
|
|
19033
|
+
await fs30.stat(p);
|
|
18964
19034
|
return true;
|
|
18965
19035
|
} catch {
|
|
18966
19036
|
return false;
|
|
@@ -18968,10 +19038,10 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
18968
19038
|
}
|
|
18969
19039
|
async function writeFiles(program) {
|
|
18970
19040
|
const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
18971
|
-
await
|
|
18972
|
-
await
|
|
18973
|
-
await
|
|
18974
|
-
await
|
|
19041
|
+
await fs30.mkdir(installDir, { recursive: true });
|
|
19042
|
+
await fs30.mkdir(def.logDir, { recursive: true });
|
|
19043
|
+
await fs30.copyFile(winswBin, exePath);
|
|
19044
|
+
await fs30.writeFile(xmlPath, xml, "utf8");
|
|
18975
19045
|
}
|
|
18976
19046
|
async function install(opts = {}) {
|
|
18977
19047
|
await writeFiles(opts.program ?? def.program);
|
|
@@ -18981,8 +19051,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
18981
19051
|
async function uninstall() {
|
|
18982
19052
|
await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
|
|
18983
19053
|
await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
|
|
18984
|
-
await
|
|
18985
|
-
await
|
|
19054
|
+
await fs30.rm(exePath, { force: true });
|
|
19055
|
+
await fs30.rm(xmlPath, { force: true });
|
|
18986
19056
|
}
|
|
18987
19057
|
async function start() {
|
|
18988
19058
|
if (!await fileExists(xmlPath)) {
|