@byok-sdk/client 0.9.1 → 0.10.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/dist/bin/byok-agent.js +91 -27
- 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 +90 -26
- 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;
|
|
@@ -16426,6 +16489,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
16426
16489
|
serverUrl: config.serverUrl,
|
|
16427
16490
|
store,
|
|
16428
16491
|
deviceName: config.deviceName,
|
|
16492
|
+
machineId: config.machineId ?? (() => resolveMachineId({ productId: config.productId })),
|
|
16429
16493
|
onRevoked: () => {
|
|
16430
16494
|
connectionState = "revoked";
|
|
16431
16495
|
}
|
|
@@ -17724,7 +17788,7 @@ function connectAndHandshake(endpoint, token, opts) {
|
|
|
17724
17788
|
});
|
|
17725
17789
|
});
|
|
17726
17790
|
}
|
|
17727
|
-
function
|
|
17791
|
+
function withTimeout2(promise, ms, message) {
|
|
17728
17792
|
return new Promise((resolve, reject) => {
|
|
17729
17793
|
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
17730
17794
|
timer.unref?.();
|
|
@@ -17803,7 +17867,7 @@ function createControlClient(socket, reader, opts) {
|
|
|
17803
17867
|
async request(method, params) {
|
|
17804
17868
|
if (closed) throw new Error("control connection is closed");
|
|
17805
17869
|
const { promise } = send(method, params);
|
|
17806
|
-
const result = await
|
|
17870
|
+
const result = await withTimeout2(promise, opts.requestTimeoutMs ?? 1e4, `control request "${method}" timed out`);
|
|
17807
17871
|
return result;
|
|
17808
17872
|
},
|
|
17809
17873
|
subscribe(method, params, onEvent) {
|
|
@@ -18740,7 +18804,7 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
|
|
|
18740
18804
|
}
|
|
18741
18805
|
function createLaunchdLifecycle(def, deps = {}) {
|
|
18742
18806
|
const run = deps.run ?? defaultRunner;
|
|
18743
|
-
const
|
|
18807
|
+
const fs30 = deps.fs ?? promises;
|
|
18744
18808
|
const homedir = deps.homedir ?? (() => os4__default.homedir());
|
|
18745
18809
|
const getuid = deps.getuid ?? (() => {
|
|
18746
18810
|
if (typeof process.getuid !== "function") {
|
|
@@ -18754,7 +18818,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
18754
18818
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
18755
18819
|
async function fileExists(p) {
|
|
18756
18820
|
try {
|
|
18757
|
-
await
|
|
18821
|
+
await fs30.stat(p);
|
|
18758
18822
|
return true;
|
|
18759
18823
|
} catch {
|
|
18760
18824
|
return false;
|
|
@@ -18762,9 +18826,9 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
18762
18826
|
}
|
|
18763
18827
|
async function writePlist(program) {
|
|
18764
18828
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
18765
|
-
await
|
|
18766
|
-
await
|
|
18767
|
-
await
|
|
18829
|
+
await fs30.mkdir(path3__default.dirname(plistPath()), { recursive: true });
|
|
18830
|
+
await fs30.mkdir(def.logDir, { recursive: true });
|
|
18831
|
+
await fs30.writeFile(plistPath(), xml, "utf8");
|
|
18768
18832
|
}
|
|
18769
18833
|
async function install(opts = {}) {
|
|
18770
18834
|
await writePlist(opts.program ?? def.program);
|
|
@@ -18775,7 +18839,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
18775
18839
|
}
|
|
18776
18840
|
async function uninstall() {
|
|
18777
18841
|
await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
|
|
18778
|
-
await
|
|
18842
|
+
await fs30.rm(plistPath(), { force: true });
|
|
18779
18843
|
}
|
|
18780
18844
|
async function start() {
|
|
18781
18845
|
if (!await fileExists(plistPath())) {
|
|
@@ -18859,14 +18923,14 @@ WantedBy=default.target
|
|
|
18859
18923
|
}
|
|
18860
18924
|
function createSystemdLifecycle(def, deps = {}) {
|
|
18861
18925
|
const run = deps.run ?? defaultRunner;
|
|
18862
|
-
const
|
|
18926
|
+
const fs30 = deps.fs ?? promises;
|
|
18863
18927
|
const homedir = deps.homedir ?? (() => os4__default.homedir());
|
|
18864
18928
|
const name = sanitizeServiceName(def.name);
|
|
18865
18929
|
const unitName = `${name}.service`;
|
|
18866
18930
|
const unitPath = () => path3__default.join(homedir(), ".config", "systemd", "user", unitName);
|
|
18867
18931
|
async function fileExists(p) {
|
|
18868
18932
|
try {
|
|
18869
|
-
await
|
|
18933
|
+
await fs30.stat(p);
|
|
18870
18934
|
return true;
|
|
18871
18935
|
} catch {
|
|
18872
18936
|
return false;
|
|
@@ -18874,9 +18938,9 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
18874
18938
|
}
|
|
18875
18939
|
async function writeUnit(program) {
|
|
18876
18940
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
18877
|
-
await
|
|
18878
|
-
await
|
|
18879
|
-
await
|
|
18941
|
+
await fs30.mkdir(path3__default.dirname(unitPath()), { recursive: true });
|
|
18942
|
+
await fs30.mkdir(def.logDir, { recursive: true });
|
|
18943
|
+
await fs30.writeFile(unitPath(), unit, "utf8");
|
|
18880
18944
|
}
|
|
18881
18945
|
async function install(opts = {}) {
|
|
18882
18946
|
await writeUnit(opts.program ?? def.program);
|
|
@@ -18885,7 +18949,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
18885
18949
|
}
|
|
18886
18950
|
async function uninstall() {
|
|
18887
18951
|
await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
|
|
18888
|
-
await
|
|
18952
|
+
await fs30.rm(unitPath(), { force: true });
|
|
18889
18953
|
await run("systemctl", ["--user", "daemon-reload"]);
|
|
18890
18954
|
}
|
|
18891
18955
|
async function start() {
|
|
@@ -18948,7 +19012,7 @@ ${argXml}${cwdXml}
|
|
|
18948
19012
|
}
|
|
18949
19013
|
function createWinswLifecycle(def, deps = {}) {
|
|
18950
19014
|
const run = deps.run ?? defaultRunner;
|
|
18951
|
-
const
|
|
19015
|
+
const fs30 = deps.fs ?? promises;
|
|
18952
19016
|
const windows = def.windows;
|
|
18953
19017
|
if (!windows) {
|
|
18954
19018
|
throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
|
|
@@ -18960,7 +19024,7 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
18960
19024
|
const xmlPath = path3__default.join(installDir, `${id}.xml`);
|
|
18961
19025
|
async function fileExists(p) {
|
|
18962
19026
|
try {
|
|
18963
|
-
await
|
|
19027
|
+
await fs30.stat(p);
|
|
18964
19028
|
return true;
|
|
18965
19029
|
} catch {
|
|
18966
19030
|
return false;
|
|
@@ -18968,10 +19032,10 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
18968
19032
|
}
|
|
18969
19033
|
async function writeFiles(program) {
|
|
18970
19034
|
const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
18971
|
-
await
|
|
18972
|
-
await
|
|
18973
|
-
await
|
|
18974
|
-
await
|
|
19035
|
+
await fs30.mkdir(installDir, { recursive: true });
|
|
19036
|
+
await fs30.mkdir(def.logDir, { recursive: true });
|
|
19037
|
+
await fs30.copyFile(winswBin, exePath);
|
|
19038
|
+
await fs30.writeFile(xmlPath, xml, "utf8");
|
|
18975
19039
|
}
|
|
18976
19040
|
async function install(opts = {}) {
|
|
18977
19041
|
await writeFiles(opts.program ?? def.program);
|
|
@@ -18981,8 +19045,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
18981
19045
|
async function uninstall() {
|
|
18982
19046
|
await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
|
|
18983
19047
|
await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
|
|
18984
|
-
await
|
|
18985
|
-
await
|
|
19048
|
+
await fs30.rm(exePath, { force: true });
|
|
19049
|
+
await fs30.rm(xmlPath, { force: true });
|
|
18986
19050
|
}
|
|
18987
19051
|
async function start() {
|
|
18988
19052
|
if (!await fileExists(xmlPath)) {
|