@byok-sdk/client 0.5.0 → 0.6.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.
@@ -5,7 +5,7 @@ import { readFileSync, promises, linkSync, fstatSync, lstatSync, unlinkSync, con
5
5
  import path20, { isAbsolute, join } from 'path';
6
6
  import os from 'os';
7
7
  import { DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, nonceSigningBytes, CapabilityDeclarationSchema, hasCapability, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
8
- import { TASK_STATES, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, createEnvelope, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, RESULT_DOCUMENT_MAX_BYTES, PROTOCOL_VERSION, decodeEnvelope, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, BYOK_EVENTS_PATH, parseMessage, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
8
+ import { TASK_STATES, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, BYOK_EVENTS_PATH, parseMessage, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
9
9
  import { promisify } from 'util';
10
10
  import { fileURLToPath } from 'url';
11
11
  import 'readline';
@@ -77,6 +77,28 @@ var SteerUnsupportedError = class extends Error {
77
77
  }
78
78
  };
79
79
 
80
+ // src/release-identity.ts
81
+ var LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH = 128;
82
+ var LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH = 128;
83
+ var STRICT_SEMVER_PATTERN = new RegExp(
84
+ "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
85
+ );
86
+ var BUILD_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
87
+ function resolveLocalAgentReleaseIdentity(input) {
88
+ if (input === void 0 || typeof input.version !== "string" || input.version.length > LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH || !STRICT_SEMVER_PATTERN.test(input.version)) {
89
+ throw new Error("DaemonConfig.localAgentRelease.version must be canonical strict SemVer");
90
+ }
91
+ if (input.buildId !== void 0 && (typeof input.buildId !== "string" || input.buildId.length > LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH || !BUILD_ID_PATTERN.test(input.buildId))) {
92
+ throw new Error(
93
+ `DaemonConfig.localAgentRelease.buildId must be 1-${LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH} safe opaque characters`
94
+ );
95
+ }
96
+ return Object.freeze({
97
+ version: input.version,
98
+ ...input.buildId === void 0 ? {} : { buildId: input.buildId }
99
+ });
100
+ }
101
+
80
102
  // src/runtime-failure.ts
81
103
  var RUNTIME_EXECUTION_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeExecutionFailure/v1");
82
104
  var RUNTIME_DISPOSAL_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeDisposalFailure/v1");
@@ -4419,7 +4441,20 @@ var PresencePublisher = class {
4419
4441
  headers: { "content-type": "application/json" },
4420
4442
  body: JSON.stringify({
4421
4443
  level: "online",
4422
- ...this.opts.configuredToolsets === void 0 ? {} : { configuredToolsets: this.opts.configuredToolsets }
4444
+ ...this.opts.configuredToolsets === void 0 ? {} : { configuredToolsets: this.opts.configuredToolsets },
4445
+ ...this.opts.clientVersion === void 0 ? {} : { clientVersion: this.opts.clientVersion },
4446
+ ...this.opts.protocolVersions === void 0 ? {} : { protocolVersions: [...this.opts.protocolVersions] },
4447
+ // Presence is a lightweight readiness fact, not a second
4448
+ // capability-negotiation channel. Keep its projection identical
4449
+ // to the persisted presence contract: the conn.hello snapshot's
4450
+ // runtime identity/version/auth facts, with no capabilities blob.
4451
+ ...this.opts.runtimes === void 0 ? {} : {
4452
+ runtimes: this.opts.runtimes.map(({ id, version, authPresent }) => ({
4453
+ id,
4454
+ ...version === void 0 ? {} : { version },
4455
+ ...authPresent === void 0 ? {} : { authPresent }
4456
+ }))
4457
+ }
4423
4458
  })
4424
4459
  },
4425
4460
  this.opts.auth
@@ -5145,6 +5180,7 @@ var WsTransport = class {
5145
5180
  capabilities: this.opts.capabilities,
5146
5181
  deviceId: this.opts.deviceId,
5147
5182
  productId: this.opts.productId,
5183
+ clientVersion: this.opts.clientVersion,
5148
5184
  runtimes: this.opts.runtimes,
5149
5185
  configuredToolsets: this.opts.configuredToolsets === void 0 ? void 0 : [...this.opts.configuredToolsets],
5150
5186
  cursor: this.opts.getCursor?.()
@@ -5235,6 +5271,7 @@ var ConnectionManager = class {
5235
5271
  deviceId: opts.deviceId,
5236
5272
  productId: opts.productId,
5237
5273
  capabilities: opts.capabilities,
5274
+ clientVersion: opts.clientVersion,
5238
5275
  runtimes: opts.runtimes,
5239
5276
  configuredToolsets: opts.configuredToolsets,
5240
5277
  getCursor: () => this.cursor,
@@ -8508,6 +8545,9 @@ var DEFAULT_MAX_TASK_OUTPUT_BYTES = 64 * 1024 * 1024;
8508
8545
  function isKnownRuntimeId(id) {
8509
8546
  return RuntimeIdSchema.safeParse(id).success;
8510
8547
  }
8548
+ function terminalUsageNumber(value, maximum) {
8549
+ return value !== void 0 && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
8550
+ }
8511
8551
  var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
8512
8552
  function orderByPreference(candidates, preference) {
8513
8553
  const rank = new Map(preference.map((id, index) => [id, index]));
@@ -8807,7 +8847,13 @@ var TaskRunner = class {
8807
8847
  const timeoutMs = this.deps.shutdownInterruptTimeoutMs ?? DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS;
8808
8848
  await raceSettleFirst(() => active.session.interrupt(), timeoutMs);
8809
8849
  if (this.tasks.get(active.taskId) !== active) return true;
8810
- this.deps.send(createEnvelope("task.fail", { reason, retryable }, { taskId: active.taskId }));
8850
+ this.deps.send(
8851
+ createEnvelope(
8852
+ "task.fail",
8853
+ { reason, retryable, ...this.terminalInferenceUsagePayload(active) },
8854
+ { taskId: active.taskId }
8855
+ )
8856
+ );
8811
8857
  return this.finish(active.taskId);
8812
8858
  }
8813
8859
  /** Graceful-shutdown caller of {@link teardownActiveTask} — see `shutdownActiveTasks`'s own doc comment. `retryable: true`: nothing about the task/policy itself was ever at fault, only this device's own availability right now. */
@@ -9185,7 +9231,8 @@ var TaskRunner = class {
9185
9231
  this.deps.batcherOptions
9186
9232
  ),
9187
9233
  approvalQueue: [],
9188
- outputBytesSoFar: 0
9234
+ outputBytesSoFar: 0,
9235
+ startedAtMs: Date.now()
9189
9236
  };
9190
9237
  if (this.pendingCancelled.has(taskId)) {
9191
9238
  const reason = this.pendingCancelled.get(taskId);
@@ -9197,7 +9244,13 @@ var TaskRunner = class {
9197
9244
  } catch {
9198
9245
  }
9199
9246
  await this.updateGitPhaseBestEffort(gitWorkspaceId, "cancelled");
9200
- this.deps.send(createEnvelope("task.cancelled", { reason }, { taskId }));
9247
+ this.deps.send(
9248
+ createEnvelope(
9249
+ "task.cancelled",
9250
+ { reason, ...this.terminalInferenceUsagePayload(active) },
9251
+ { taskId }
9252
+ )
9253
+ );
9201
9254
  await this.finish(taskId);
9202
9255
  return;
9203
9256
  }
@@ -9270,6 +9323,9 @@ var TaskRunner = class {
9270
9323
  );
9271
9324
  return;
9272
9325
  }
9326
+ if (event.type === "usage") {
9327
+ active.lastUsage = event;
9328
+ }
9273
9329
  if (event.type === "needs_approval") {
9274
9330
  active.batcher.flush();
9275
9331
  const { taskId } = active;
@@ -9322,7 +9378,8 @@ var TaskRunner = class {
9322
9378
  // (where the codec actually serializes it), so a contextual
9323
9379
  // `toJSON(key)` or an unstable getter cannot make the wire
9324
9380
  // bytes differ from what the cap gate approved.
9325
- ...outcome.document !== void 0 ? { document: outcome.document } : {}
9381
+ ...outcome.document !== void 0 ? { document: outcome.document } : {},
9382
+ ...this.terminalInferenceUsagePayload(active)
9326
9383
  },
9327
9384
  { taskId: active.taskId, sessionRef: active.session.sessionRef }
9328
9385
  )
@@ -9439,7 +9496,13 @@ var TaskRunner = class {
9439
9496
  } catch {
9440
9497
  }
9441
9498
  await this.observeGit(active, "salvage");
9442
- this.deps.send(createEnvelope("task.cancelled", { reason }, { taskId }));
9499
+ this.deps.send(
9500
+ createEnvelope(
9501
+ "task.cancelled",
9502
+ { reason, ...this.terminalInferenceUsagePayload(active) },
9503
+ { taskId }
9504
+ )
9505
+ );
9443
9506
  await this.finish(taskId);
9444
9507
  }
9445
9508
  /** M3-B: bounded insert for `pendingCancelled` — see its class-level doc comment and `MAX_TRACKED_TASK_IDS`. Evicts the oldest SAFE-TO-EVICT entry once over cap — see `evictPendingCancelled` (finding #5: not simply "the oldest entry", which could be an in-flight offer's own cancel marker). */
@@ -9877,7 +9940,13 @@ var TaskRunner = class {
9877
9940
  } catch {
9878
9941
  }
9879
9942
  await this.observeGit(active, "salvage");
9880
- this.deps.send(createEnvelope("task.fail", { reason: reason ?? "rejected", retryable: false }, { taskId }));
9943
+ this.deps.send(
9944
+ createEnvelope(
9945
+ "task.fail",
9946
+ { reason: reason ?? "rejected", retryable: false, ...this.terminalInferenceUsagePayload(active) },
9947
+ { taskId }
9948
+ )
9949
+ );
9881
9950
  await this.finish(taskId);
9882
9951
  }
9883
9952
  /** Pre-claim, fail-closed rejection (protocol §3.2) — never claims first. */
@@ -9895,9 +9964,44 @@ var TaskRunner = class {
9895
9964
  return;
9896
9965
  }
9897
9966
  if (active) await this.observeGit(active, "salvage");
9898
- this.deps.send(createEnvelope("task.fail", { reason, retryable }, { taskId }));
9967
+ this.deps.send(
9968
+ createEnvelope(
9969
+ "task.fail",
9970
+ active === void 0 ? { reason, retryable } : { reason, retryable, ...this.terminalInferenceUsagePayload(active) },
9971
+ { taskId }
9972
+ )
9973
+ );
9899
9974
  await this.finish(taskId);
9900
9975
  }
9976
+ /**
9977
+ * Build the optional terminal observation from facts this running daemon
9978
+ * actually has. No offered `dispatchSelection` is echoed here: it is a
9979
+ * requested execution target, not an adapter-reported provider/model fact.
9980
+ * The bundled adapter event contracts currently expose token observations
9981
+ * (Codex and Claude) but no provider/model observation, so those keys stay
9982
+ * absent. Pi exposes no native usage observation, so its terminal payload
9983
+ * omits this optional block rather than fabricating a usage observation from
9984
+ * independently known runtime, elapsed duration, or Local Agent version.
9985
+ */
9986
+ terminalInferenceUsagePayload(active) {
9987
+ const release = this.deps.localAgentRelease;
9988
+ const runtimeId = active.adapter.descriptor.id;
9989
+ if (release === void 0 || active.lastUsage === void 0 || !isKnownRuntimeId(runtimeId)) return {};
9990
+ const nowMs = Date.now();
9991
+ const durationMs = terminalUsageNumber(nowMs - active.startedAtMs, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS);
9992
+ const promptTokens = terminalUsageNumber(active.lastUsage?.inputTokens, TERMINAL_INFERENCE_USAGE_MAX_TOKENS);
9993
+ const completionTokens = terminalUsageNumber(active.lastUsage?.outputTokens, TERMINAL_INFERENCE_USAGE_MAX_TOKENS);
9994
+ return {
9995
+ usage: {
9996
+ runtime: runtimeId,
9997
+ clientVersion: release.version,
9998
+ reportedAt: new Date(nowMs).toISOString(),
9999
+ ...promptTokens === void 0 ? {} : { promptTokens },
10000
+ ...completionTokens === void 0 ? {} : { completionTokens },
10001
+ ...durationMs === void 0 ? {} : { durationMs }
10002
+ }
10003
+ };
10004
+ }
9901
10005
  /**
9902
10006
  * additive-minor (`task.complete.document`): the whole daemon-side gate
9903
10007
  * between a configured {@link ResultDocumentExtractor} and the wire —
@@ -10426,6 +10530,7 @@ function createDaemonWithAdapters(config, adapters, overrides = {}) {
10426
10530
  return buildDaemonWithAdapters(config, adapters, overrides);
10427
10531
  }
10428
10532
  function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProbe) {
10533
+ const localAgentRelease = resolveLocalAgentReleaseIdentity(config.localAgentRelease);
10429
10534
  const mcpToolsets = resolveMcpToolsets(config.mcpToolsets);
10430
10535
  const configuredToolsets = Object.freeze(
10431
10536
  [...mcpToolsets?.keys() ?? []].sort()
@@ -10539,6 +10644,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10539
10644
  let controlServerHandle;
10540
10645
  let daemonOwnerLease;
10541
10646
  let presencePublisher;
10647
+ let detectedRuntimeFacts = [];
10542
10648
  let presenceDiscovery;
10543
10649
  let presenceDiscoveryInFlight = false;
10544
10650
  let shutdownPromise;
@@ -10672,6 +10778,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10672
10778
  Promise.resolve(new BlobClient(config.serverUrl, auth))
10673
10779
  ]);
10674
10780
  observer.noteRuntimesDetected(runtimes);
10781
+ detectedRuntimeFacts = runtimes;
10675
10782
  const capabilities = computeCapabilities(adapters);
10676
10783
  const journalIdentity = config.hostedJournal ? { tenantId: config.hostedJournal.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
10677
10784
  const sendEnvelope = activeJournal && journalIdentity ? (envelope) => {
@@ -10749,6 +10856,10 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10749
10856
  approvalRegistry,
10750
10857
  storeDir,
10751
10858
  productId: config.productId,
10859
+ // U2 terminal inference usage consumes the one U4a-resolved,
10860
+ // process-immutable identity captured above. This is composition-only:
10861
+ // TaskRunner receives no config, manifest, or second identity resolver.
10862
+ localAgentRelease,
10752
10863
  approvalTimeoutMs: overrides.approvalTimeoutMs,
10753
10864
  // Finding F5(a): see TaskRunnerDeps.shutdownInterruptTimeoutMs's own doc comment.
10754
10865
  shutdownInterruptTimeoutMs: overrides.shutdown?.taskInterruptTimeoutMs,
@@ -10794,6 +10905,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10794
10905
  deviceId: record.deviceId,
10795
10906
  productId: config.productId,
10796
10907
  capabilities,
10908
+ clientVersion: localAgentRelease.version,
10797
10909
  runtimes,
10798
10910
  configuredToolsets,
10799
10911
  auth,
@@ -10883,6 +10995,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10883
10995
  serverUrl: config.serverUrl,
10884
10996
  auth,
10885
10997
  configuredToolsets,
10998
+ clientVersion: localAgentRelease.version,
10999
+ protocolVersions: [PROTOCOL_VERSION],
11000
+ runtimes: detectedRuntimeFacts,
10886
11001
  ...presenceCadence,
10887
11002
  onDegraded: (reason) => console.warn(`[byok/client] ${reason}`)
10888
11003
  });
@@ -11064,6 +11179,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
11064
11179
  const activeTasks = observer.tasks().filter((task) => TASK_TRANSITIONS[task.state].length > 0).map((task) => ({ taskId: task.taskId, state: task.state }));
11065
11180
  const pendingApprovals = approvalRegistry.list();
11066
11181
  return {
11182
+ localAgentRelease,
11067
11183
  pid: process.pid,
11068
11184
  uptimeMs: startedAt !== void 0 ? Date.now() - startedAt : 0,
11069
11185
  paired: auth.deviceId !== void 0,
@@ -11262,6 +11378,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
11262
11378
  }
11263
11379
  function status() {
11264
11380
  return {
11381
+ localAgentRelease,
11265
11382
  paired: auth.deviceId !== void 0,
11266
11383
  connected: connectionState === "open",
11267
11384
  degraded: connection?.isTransportDegraded() ?? false,
@@ -11891,6 +12008,13 @@ function createServiceLifecycle(def, opts = {}) {
11891
12008
  throw new UnsupportedServicePlatformError(platform);
11892
12009
  }
11893
12010
  }
12011
+
12012
+ // src/bin/official-release.ts
12013
+ var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
12014
+ version: "0.6.0"
12015
+ });
12016
+
12017
+ // src/bin/config.ts
11894
12018
  var REQUIRED_FIELDS = ["productName", "productId", "serverUrl", "workspaceRoot"];
11895
12019
  var ConfigError = class extends Error {
11896
12020
  constructor(message) {
@@ -11913,6 +12037,9 @@ function loadConfig(configPath, overrides = {}) {
11913
12037
  throw new ConfigError(`config at "${configPath}" is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
11914
12038
  }
11915
12039
  }
12040
+ if (Object.prototype.hasOwnProperty.call(base, "localAgentRelease") || Object.prototype.hasOwnProperty.call(overrides, "localAgentRelease")) {
12041
+ throw new ConfigError('config field "localAgentRelease" is distribution-owned and must not be supplied');
12042
+ }
11916
12043
  const merged = { ...base, ...overrides };
11917
12044
  if (merged.gitWorkspace !== void 0) {
11918
12045
  try {
@@ -11926,7 +12053,7 @@ function loadConfig(configPath, overrides = {}) {
11926
12053
  throw new ConfigError(`config is missing required field "${field}"`);
11927
12054
  }
11928
12055
  }
11929
- return merged;
12056
+ return { ...merged, localAgentRelease: OFFICIAL_LOCAL_AGENT_RELEASE };
11930
12057
  }
11931
12058
  function resolveStoreDir(config) {
11932
12059
  return DeviceStore.resolveDir(config.productId, config.storeDir);
@@ -12100,6 +12227,9 @@ function formatStatusLines(view) {
12100
12227
  const lines = [];
12101
12228
  const label = view.branding?.displayName ?? view.productName;
12102
12229
  lines.push(`product: ${label} (${view.productId})`);
12230
+ lines.push(
12231
+ `local-agent-release: ${view.localAgentRelease.version}${view.localAgentRelease.buildId ? ` buildId=${view.localAgentRelease.buildId}` : ""}`
12232
+ );
12103
12233
  if (view.branding?.supportUrl) lines.push(`support: ${view.branding.supportUrl}`);
12104
12234
  lines.push(`paired: ${view.paired ? "yes" : "no"}${view.deviceId ? ` deviceId=${view.deviceId}` : ""}`);
12105
12235
  lines.push(
@@ -12115,8 +12245,10 @@ function formatStatusLines(view) {
12115
12245
  return lines;
12116
12246
  }
12117
12247
  function formatLiveStatusLines(live) {
12248
+ const liveRelease = live.localAgentRelease;
12118
12249
  const lines = [
12119
12250
  `live: pid=${live.pid} uptimeMs=${live.uptimeMs} transport=${live.transport}`,
12251
+ liveRelease ? `live-local-agent-release: ${liveRelease.version}${liveRelease.buildId ? ` buildId=${liveRelease.buildId}` : ""}` : "live-local-agent-release: unknown",
12120
12252
  `live-paired: ${live.paired ? "yes" : "no"}${live.deviceId ? ` deviceId=${live.deviceId}` : ""}`,
12121
12253
  `live-runtimes: ${live.runtimeIds.length ? live.runtimeIds.join(",") : "(none)"}`
12122
12254
  ];
@@ -13805,6 +13937,7 @@ async function runStatusCommand(config, deps = {}) {
13805
13937
  ]);
13806
13938
  const tasks = deriveTasksFromEvents(events);
13807
13939
  const view = {
13940
+ localAgentRelease: config.localAgentRelease,
13808
13941
  productName: config.productName,
13809
13942
  productId: config.productId,
13810
13943
  branding: config.branding,
@@ -14233,11 +14366,17 @@ async function runWorkspacesCommand(config, deps = {}) {
14233
14366
  for (const record of records) log(formatWorkspaceLine(record, deps.showPaths === true));
14234
14367
  }
14235
14368
 
14369
+ // src/bin/version.ts
14370
+ function runVersionCommand(log = console.log) {
14371
+ log(OFFICIAL_LOCAL_AGENT_RELEASE.version);
14372
+ }
14373
+
14236
14374
  // src/bin/byok-agent.ts
14237
14375
  function usage() {
14238
14376
  console.error(
14239
14377
  [
14240
14378
  "Usage:",
14379
+ " byok-agent --version",
14241
14380
  " byok-agent pair <code> --server <url> [--config <path>]",
14242
14381
  " byok-agent start [--config <path>] (or BYOK_CONFIG env var)",
14243
14382
  " byok-agent status [--config <path>]",
@@ -14273,6 +14412,10 @@ function abortOnSignal() {
14273
14412
  }
14274
14413
  async function main() {
14275
14414
  const [, , command, ...rest] = process.argv;
14415
+ if (command === "--version") {
14416
+ runVersionCommand();
14417
+ return;
14418
+ }
14276
14419
  if (command === "pair") {
14277
14420
  const [code] = positionalArgs(rest, ["--server", "--config"]);
14278
14421
  const server = argValue(rest, "--server");