@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.
@@ -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 readFile(context, relativePath, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
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 readFile(context, relativePath);
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 readFile(context, relativePath);
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 withTimeout(promise, ms, message) {
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 withTimeout(promise, opts.requestTimeoutMs ?? 1e4, `control request "${method}" timed out`);
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 fs29 = deps.fs ?? promises;
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 fs29.stat(p);
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 fs29.mkdir(path3__default.dirname(plistPath()), { recursive: true });
18766
- await fs29.mkdir(def.logDir, { recursive: true });
18767
- await fs29.writeFile(plistPath(), xml, "utf8");
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 fs29.rm(plistPath(), { force: true });
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 fs29 = deps.fs ?? promises;
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 fs29.stat(p);
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 fs29.mkdir(path3__default.dirname(unitPath()), { recursive: true });
18878
- await fs29.mkdir(def.logDir, { recursive: true });
18879
- await fs29.writeFile(unitPath(), unit, "utf8");
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 fs29.rm(unitPath(), { force: true });
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 fs29 = deps.fs ?? promises;
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 fs29.stat(p);
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 fs29.mkdir(installDir, { recursive: true });
18972
- await fs29.mkdir(def.logDir, { recursive: true });
18973
- await fs29.copyFile(winswBin, exePath);
18974
- await fs29.writeFile(xmlPath, xml, "utf8");
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 fs29.rm(exePath, { force: true });
18985
- await fs29.rm(xmlPath, { force: true });
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)) {