@sailorbridge/client 0.2.2 → 0.2.3

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/src/cli.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
- // .dist-releases/1784862378006-1249051/src/cli.js
5
+ // .dist-releases/1784888160293-2271563/src/cli.js
6
6
  import { hostname as hostname3 } from "node:os";
7
7
  import { writeSync } from "node:fs";
8
- // .dist-releases/1784862378006-1249051/src/auth/host-pairing.js
8
+ // .dist-releases/1784888160293-2271563/src/auth/host-pairing.js
9
9
  import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
10
10
  import { homedir, hostname, platform } from "node:os";
11
11
  import path from "node:path";
@@ -220,7 +220,7 @@ function _assertObject(value) {
220
220
  throw new Error("host pairing response must be an object");
221
221
  return value;
222
222
  }
223
- // .dist-releases/1784862378006-1249051/src/host/repo-tip-reporter.js
223
+ // .dist-releases/1784888160293-2271563/src/host/repo-tip-reporter.js
224
224
  import { createHash } from "node:crypto";
225
225
  import { existsSync, mkdirSync } from "node:fs";
226
226
  import { homedir as homedir2 } from "node:os";
@@ -402,10 +402,10 @@ ${error.message}`));
402
402
  proc.on("close", (code) => finish(code ?? 1));
403
403
  });
404
404
  }
405
- // .dist-releases/1784862378006-1249051/src/host/request-consumer.js
405
+ // .dist-releases/1784888160293-2271563/src/host/request-consumer.js
406
406
  import { hostname as hostname2 } from "node:os";
407
407
 
408
- // .dist-releases/1784862378006-1249051/src/driver/secrets.js
408
+ // .dist-releases/1784888160293-2271563/src/driver/secrets.js
409
409
  function redactSensitiveText(text, env = {}, explicitSecrets = []) {
410
410
  let redacted = text;
411
411
  for (const [key, value] of Object.entries(env)) {
@@ -424,16 +424,16 @@ function isSensitiveEnvKey(key) {
424
424
  return /(TOKEN|KEY|SECRET|AUTHORIZATION|PASSWORD)/i.test(key);
425
425
  }
426
426
 
427
- // .dist-releases/1784862378006-1249051/src/version.js
428
- var CLIENT_VERSION = "0.2.2";
429
- var CLIENT_GIT_SHA = "f943ffe8e731-dirty";
427
+ // .dist-releases/1784888160293-2271563/src/version.js
428
+ var CLIENT_VERSION = "0.2.3";
429
+ var CLIENT_GIT_SHA = "ed59f81313f2-dirty";
430
430
  var CLIENT_BUILD_DIRTY = true;
431
431
  var CLIENT_VERSION_LABEL = formatClientVersion(CLIENT_VERSION, CLIENT_GIT_SHA);
432
432
  function formatClientVersion(version, gitSha) {
433
433
  return `${version}+git.${gitSha}`;
434
434
  }
435
435
 
436
- // .dist-releases/1784862378006-1249051/src/host/request-consumer.js
436
+ // .dist-releases/1784888160293-2271563/src/host/request-consumer.js
437
437
  var MAX_ERROR_LENGTH = 500;
438
438
  function defaultHostIdentity(config) {
439
439
  const captainId = config.captainId?.trim();
@@ -455,7 +455,8 @@ async function runHostRequestOnce(options) {
455
455
  hostId: options.hostId,
456
456
  displayName: options.displayName,
457
457
  clientVersion: CLIENT_VERSION,
458
- clientGitSha: CLIENT_GIT_SHA
458
+ clientGitSha: CLIENT_GIT_SHA,
459
+ ...options.updateError === undefined ? {} : { updateError: options.updateError }
459
460
  });
460
461
  if (options.workspaceInventory) {
461
462
  try {
@@ -488,7 +489,7 @@ function limitErrorMessage(error, explicitSecrets) {
488
489
  const trimmed = redactSensitiveText(message, process.env, explicitSecrets).trim() || "host request starter failed";
489
490
  return trimmed.length <= MAX_ERROR_LENGTH ? trimmed : `${trimmed.slice(0, MAX_ERROR_LENGTH - 3)}...`;
490
491
  }
491
- // .dist-releases/1784862378006-1249051/src/product/start-request.js
492
+ // .dist-releases/1784888160293-2271563/src/product/start-request.js
492
493
  async function createAgentStartRequest(options) {
493
494
  const apiUrl = _required(options.apiUrl, "product API URL").replace(/\/+$/u, "");
494
495
  const accessToken = _required(options.accessToken, "product API bearer token");
@@ -548,7 +549,7 @@ function _string(value, label) {
548
549
  throw new Error(`${label} must be a non-empty string`);
549
550
  return value;
550
551
  }
551
- // .dist-releases/1784862378006-1249051/src/product/pair.js
552
+ // .dist-releases/1784888160293-2271563/src/product/pair.js
552
553
  async function createPair(options) {
553
554
  const apiUrl = _required2(options.apiUrl, "product API URL").replace(/\/+$/u, "");
554
555
  const accessToken = _required2(options.accessToken, "product API bearer token");
@@ -625,7 +626,7 @@ function _string2(value, label) {
625
626
  throw new Error(`${label} must be a non-empty string`);
626
627
  return value;
627
628
  }
628
- // .dist-releases/1784862378006-1249051/src/product/worker.js
629
+ // .dist-releases/1784888160293-2271563/src/product/worker.js
629
630
  async function createWorker(options) {
630
631
  const apiUrl = _required3(options.apiUrl, "product API URL").replace(/\/+$/u, "");
631
632
  const accessToken = _required3(options.accessToken, "product API bearer token");
@@ -689,7 +690,7 @@ function _string3(value, label) {
689
690
  throw new Error(`${label} must be a non-empty string`);
690
691
  return value;
691
692
  }
692
- // .dist-releases/1784862378006-1249051/src/driver/events.js
693
+ // .dist-releases/1784888160293-2271563/src/driver/events.js
693
694
  class DriverEventEmitter {
694
695
  callbacks = new Map;
695
696
  on(type, callback) {
@@ -704,7 +705,7 @@ class DriverEventEmitter {
704
705
  }
705
706
  }
706
707
  }
707
- // .dist-releases/1784862378006-1249051/src/driver/async-queue.js
708
+ // .dist-releases/1784888160293-2271563/src/driver/async-queue.js
708
709
  class AsyncQueue {
709
710
  values = [];
710
711
  waiters = [];
@@ -743,7 +744,7 @@ class AsyncQueue {
743
744
  }
744
745
  }
745
746
 
746
- // .dist-releases/1784862378006-1249051/src/driver/types.js
747
+ // .dist-releases/1784888160293-2271563/src/driver/types.js
747
748
  class DriverUnsupportedError extends Error {
748
749
  constructor(message) {
749
750
  super(message);
@@ -758,7 +759,7 @@ class DriverTargetNotReadyError extends Error {
758
759
  }
759
760
  }
760
761
 
761
- // .dist-releases/1784862378006-1249051/src/driver/sdk-loader.js
762
+ // .dist-releases/1784888160293-2271563/src/driver/sdk-loader.js
762
763
  async function loadSdk(loader, packageName, env = {}, installHint = `Install the optional SDK dependency ${packageName} and its peers for this transport.`) {
763
764
  try {
764
765
  return await loader();
@@ -768,7 +769,7 @@ async function loadSdk(loader, packageName, env = {}, installHint = `Install the
768
769
  }
769
770
  }
770
771
 
771
- // .dist-releases/1784862378006-1249051/src/driver/claude/driver.js
772
+ // .dist-releases/1784888160293-2271563/src/driver/claude/driver.js
772
773
  class ClaudeAgentSdkDriver {
773
774
  options;
774
775
  capabilities = { structured: true, resume: true, attach: false };
@@ -944,7 +945,7 @@ function asRecord(value) {
944
945
  function stringValue(value) {
945
946
  return typeof value === "string" ? value : undefined;
946
947
  }
947
- // .dist-releases/1784862378006-1249051/src/driver/codex/driver.js
948
+ // .dist-releases/1784888160293-2271563/src/driver/codex/driver.js
948
949
  class CodexSdkDriver {
949
950
  options;
950
951
  capabilities = { structured: true, resume: true, attach: false };
@@ -1125,7 +1126,7 @@ function asRecord2(value) {
1125
1126
  function stringValue2(value) {
1126
1127
  return typeof value === "string" ? value : undefined;
1127
1128
  }
1128
- // .dist-releases/1784862378006-1249051/src/driver/liveness-heuristics.js
1129
+ // .dist-releases/1784888160293-2271563/src/driver/liveness-heuristics.js
1129
1130
  var BUSY_MARKERS = ["Working (", "Waiting for background", "Pasted Content", "tab to queue"];
1130
1131
  var ANSI_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\)/g;
1131
1132
  function stripAnsi(text) {
@@ -1152,7 +1153,7 @@ function classifyInteractiveOutput(input) {
1152
1153
  function isOutputStable(now, lastOutputAt, idleStableMs) {
1153
1154
  return Boolean(lastOutputAt && now.getTime() - lastOutputAt.getTime() >= idleStableMs);
1154
1155
  }
1155
- // .dist-releases/1784862378006-1249051/src/driver/pty/loader.js
1156
+ // .dist-releases/1784888160293-2271563/src/driver/pty/loader.js
1156
1157
  async function loadNodePty(loader = defaultNodePtyLoader) {
1157
1158
  try {
1158
1159
  return await loader();
@@ -1178,7 +1179,7 @@ async function defaultNodePtyLoader() {
1178
1179
  return loaded;
1179
1180
  }
1180
1181
 
1181
- // .dist-releases/1784862378006-1249051/src/driver/pty/driver.js
1182
+ // .dist-releases/1784888160293-2271563/src/driver/pty/driver.js
1182
1183
  class PtyDriver {
1183
1184
  options;
1184
1185
  capabilities = { structured: false, resume: false, attach: false };
@@ -1330,12 +1331,12 @@ function tail(text, lines) {
1330
1331
  function sleep(ms) {
1331
1332
  return new Promise((resolve) => setTimeout(resolve, ms));
1332
1333
  }
1333
- // .dist-releases/1784862378006-1249051/src/driver/tmux/driver.js
1334
+ // .dist-releases/1784888160293-2271563/src/driver/tmux/driver.js
1334
1335
  import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
1335
1336
  import { tmpdir } from "node:os";
1336
1337
  import { join } from "node:path";
1337
1338
 
1338
- // .dist-releases/1784862378006-1249051/src/driver/tmux/launch.js
1339
+ // .dist-releases/1784888160293-2271563/src/driver/tmux/launch.js
1339
1340
  var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
1340
1341
  var PANE_BOOTSTRAP_COMMAND = 'tmux set-option -t "$TMUX_PANE" remain-on-exit on >/dev/null 2>&1 || true; tmux wait-for -S "$1"; shift; exec "$@"';
1341
1342
  function buildTmuxNewSessionArgs(input) {
@@ -1358,7 +1359,7 @@ function validateEnv(env) {
1358
1359
  }
1359
1360
  }
1360
1361
 
1361
- // .dist-releases/1784862378006-1249051/src/driver/tmux/paste.js
1362
+ // .dist-releases/1784888160293-2271563/src/driver/tmux/paste.js
1362
1363
  function buildDispatchPlan(text, useTyping) {
1363
1364
  return {
1364
1365
  mode: useTyping ? "type-literal" : "paste-buffer",
@@ -1382,7 +1383,7 @@ function arraysEqual(left, right) {
1382
1383
  return left.length === right.length && left.every((item, index) => item === right[index]);
1383
1384
  }
1384
1385
 
1385
- // .dist-releases/1784862378006-1249051/src/driver/tmux/liveness.js
1386
+ // .dist-releases/1784888160293-2271563/src/driver/tmux/liveness.js
1386
1387
  var SHELL_COMMANDS = new Set(["", "bash", "zsh", "sh", "fish", "pwsh", "powershell"]);
1387
1388
  function classifyTmuxLiveness(input) {
1388
1389
  if (!input.hasSession) {
@@ -1402,7 +1403,7 @@ function isTmuxProcessAlive(command, runningCommands) {
1402
1403
  return !SHELL_COMMANDS.has(command);
1403
1404
  }
1404
1405
 
1405
- // .dist-releases/1784862378006-1249051/src/driver/tmux/runner.js
1406
+ // .dist-releases/1784888160293-2271563/src/driver/tmux/runner.js
1406
1407
  import { spawn as spawn2 } from "node:child_process";
1407
1408
  var DEFAULT_TIMEOUT_MS = 15000;
1408
1409
 
@@ -1527,7 +1528,7 @@ class TmuxClient {
1527
1528
  }
1528
1529
  }
1529
1530
 
1530
- // .dist-releases/1784862378006-1249051/src/driver/tmux/driver.js
1531
+ // .dist-releases/1784888160293-2271563/src/driver/tmux/driver.js
1531
1532
  var ATTACHED_STOP_GRACE_MS = 1e4;
1532
1533
  var DEFAULT_STOP_TIMEOUT_MS = 15000;
1533
1534
  var DEFAULT_READY_TIMEOUT_MS = 90000;
@@ -1922,12 +1923,12 @@ function isMissingTmuxSessionError(error) {
1922
1923
  ${error.result.stderr}`.toLowerCase();
1923
1924
  return text.includes("can't find session") || text.includes("cannot find session") || text.includes("no such session") || text.includes("session not found");
1924
1925
  }
1925
- // .dist-releases/1784862378006-1249051/src/relay/client.js
1926
+ // .dist-releases/1784888160293-2271563/src/relay/client.js
1926
1927
  import { Buffer as Buffer2 } from "node:buffer";
1927
1928
  import http from "node:http";
1928
1929
  import https from "node:https";
1929
1930
 
1930
- // .dist-releases/1784862378006-1249051/src/relay/errors.js
1931
+ // .dist-releases/1784888160293-2271563/src/relay/errors.js
1931
1932
  class RelayError extends Error {
1932
1933
  method;
1933
1934
  path;
@@ -1964,7 +1965,7 @@ class RelayProtocolError extends RelayError {
1964
1965
  }
1965
1966
  }
1966
1967
 
1967
- // .dist-releases/1784862378006-1249051/src/worker/protocol.js
1968
+ // .dist-releases/1784888160293-2271563/src/worker/protocol.js
1968
1969
  var DEFAULT_RELAY_URL = DEFAULT_PRODUCT_API_URL;
1969
1970
  var DEFAULT_WORKER = "host";
1970
1971
  var DEFAULT_WORKDIR = "~/sailorbridge-workdir";
@@ -1994,7 +1995,7 @@ function effectiveJobKind(kind, spec) {
1994
1995
  return kind === "stream" || spec.stream === true ? "stream" : kind;
1995
1996
  }
1996
1997
 
1997
- // .dist-releases/1784862378006-1249051/src/relay/client.js
1998
+ // .dist-releases/1784888160293-2271563/src/relay/client.js
1998
1999
  var JSON_CONTENT_TYPE = "application/json";
1999
2000
  var MAX_MAILBOX_LIMIT = 16;
2000
2001
  var MAX_WAIT_TIMEOUT_SEC = 120;
@@ -2161,13 +2162,15 @@ class RelayClient {
2161
2162
  }
2162
2163
  return { status: "queued", ids: response.ids, recipients: response.recipients };
2163
2164
  }
2164
- async postSessionReply(session, text, target = { kind: "captain" }, inReplyToMessageId, tag = "REPLY") {
2165
+ async postSessionReply(session, text, target = { kind: "captain" }, inReplyToMessageId, tag = "REPLY", origin) {
2165
2166
  requireSegment(session, "session");
2166
2167
  const toAgent = target.kind === "captain" ? "captain" : target.agentName;
2167
2168
  requireNonEmpty(toAgent, "reply target");
2168
2169
  requireNonEmpty(tag, "reply tag");
2169
2170
  if (inReplyToMessageId !== undefined)
2170
2171
  requireNonEmpty(inReplyToMessageId, "in_reply_to_message_id");
2172
+ if (origin !== undefined && target.kind !== "captain")
2173
+ throw new Error("origin is only valid for captain replies");
2171
2174
  const response = await this.requestJson("POST", `/api/v1/mailbox/session/${segment(session)}`, {
2172
2175
  tag,
2173
2176
  to_agents: [toAgent],
@@ -2175,13 +2178,18 @@ class RelayClient {
2175
2178
  payload_json: text,
2176
2179
  ...inReplyToMessageId === undefined ? {} : {
2177
2180
  in_reply_to_message_id: inReplyToMessageId
2178
- }
2181
+ },
2182
+ ...origin === undefined ? {} : { origin }
2179
2183
  });
2180
- const expectedStatus = target.kind === "captain" ? "recorded" : "queued";
2181
- if (response.status !== expectedStatus || !isNonEmptyStringArray(response.ids) || !isStringArray(response.recipients)) {
2182
- throw new RelayProtocolError("POST", `/api/v1/mailbox/session/${segment(session)}`, "invalid session reply response");
2184
+ const validRecipients = isStringArray(response.recipients) && response.recipients.length === 1 && response.recipients[0] === toAgent;
2185
+ if (origin !== undefined && response.status === "skipped") {
2186
+ if (!validRecipients || typeof response.reason !== "string" || !response.reason) {
2187
+ throw new RelayProtocolError("POST", `/api/v1/mailbox/session/${segment(session)}`, "invalid session reply skip response");
2188
+ }
2189
+ return { ids: [], recipients: response.recipients, skippedReason: response.reason };
2183
2190
  }
2184
- if (response.recipients.length !== 1 || response.recipients[0] !== toAgent) {
2191
+ const expectedStatus = target.kind === "captain" ? "recorded" : "queued";
2192
+ if (response.status !== expectedStatus || !isNonEmptyStringArray(response.ids) || !validRecipients) {
2185
2193
  throw new RelayProtocolError("POST", `/api/v1/mailbox/session/${segment(session)}`, "invalid session reply response");
2186
2194
  }
2187
2195
  return { ids: response.ids, recipients: response.recipients };
@@ -2370,7 +2378,8 @@ class RelayClient {
2370
2378
  host_id: body.hostId,
2371
2379
  display_name: body.displayName,
2372
2380
  ...body.clientVersion === undefined ? {} : { client_version: body.clientVersion },
2373
- ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha }
2381
+ ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha },
2382
+ ...body.updateError === undefined ? {} : { update_error: body.updateError }
2374
2383
  });
2375
2384
  return parseClientHostReport(response, "POST", path3);
2376
2385
  }
@@ -2384,7 +2393,8 @@ class RelayClient {
2384
2393
  const response = await this.requestJson("POST", path3, {
2385
2394
  ...body.displayName === undefined ? {} : { display_name: body.displayName },
2386
2395
  ...body.clientVersion === undefined ? {} : { client_version: body.clientVersion },
2387
- ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha }
2396
+ ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha },
2397
+ ...body.updateError === undefined ? {} : { update_error: body.updateError }
2388
2398
  });
2389
2399
  return parseClientHostReport(response, "POST", path3);
2390
2400
  }
@@ -3471,6 +3481,8 @@ function optionalClientHostMetadata(host, method, path3) {
3471
3481
  const minimum = optionalClientHostString(host, "minimum_client_version", method, path3);
3472
3482
  if (minimum === null)
3473
3483
  throw new RelayProtocolError(method, path3, "host.minimum_client_version must be a string");
3484
+ const latest = optionalClientHostString(host, "latest_client_version", method, path3);
3485
+ const updateError = optionalClientHostString(host, "update_error", method, path3);
3474
3486
  const compatibility = host.client_compatibility;
3475
3487
  if (compatibility !== undefined && compatibility !== "current" && compatibility !== "outdated" && compatibility !== "unknown" && compatibility !== "development") {
3476
3488
  throw new RelayProtocolError(method, path3, "host.client_compatibility is invalid");
@@ -3479,7 +3491,9 @@ function optionalClientHostMetadata(host, method, path3) {
3479
3491
  ...version === undefined ? {} : { client_version: version },
3480
3492
  ...gitSha === undefined ? {} : { client_git_sha: gitSha },
3481
3493
  ...minimum === undefined ? {} : { minimum_client_version: minimum },
3482
- ...compatibility === undefined ? {} : { client_compatibility: compatibility }
3494
+ ...latest === undefined ? {} : { latest_client_version: latest },
3495
+ ...compatibility === undefined ? {} : { client_compatibility: compatibility },
3496
+ ...updateError === undefined ? {} : { update_error: updateError }
3483
3497
  };
3484
3498
  }
3485
3499
  function optionalClientHostString(host, field, method, path3) {
@@ -4047,7 +4061,7 @@ function redact(value, token) {
4047
4061
  function bytesToBase64(bytes) {
4048
4062
  return Buffer2.from(bytes).toString("base64");
4049
4063
  }
4050
- // .dist-releases/1784862378006-1249051/src/relay/config.js
4064
+ // .dist-releases/1784888160293-2271563/src/relay/config.js
4051
4065
  import { existsSync as existsSync2, readFileSync } from "node:fs";
4052
4066
  import { homedir as homedir3 } from "node:os";
4053
4067
  import path3 from "node:path";
@@ -4122,7 +4136,7 @@ function storedCaptainId(value) {
4122
4136
  }
4123
4137
  return;
4124
4138
  }
4125
- // .dist-releases/1784862378006-1249051/src/supervisor/retirement.js
4139
+ // .dist-releases/1784888160293-2271563/src/supervisor/retirement.js
4126
4140
  var DEFAULT_THRESHOLD = 3;
4127
4141
  var DEFAULT_WINDOW_MS = 30000;
4128
4142
  var DEFAULT_COOLDOWN_MS = 1000;
@@ -4197,7 +4211,7 @@ function isAuthoritativeRetirementReason(reason) {
4197
4211
  return reason === "monitor_fetch" || reason === "agent_session_patch";
4198
4212
  }
4199
4213
 
4200
- // .dist-releases/1784862378006-1249051/src/supervisor/wait.js
4214
+ // .dist-releases/1784888160293-2271563/src/supervisor/wait.js
4201
4215
  async function waitForWakeableTimeout(ms, setWake, sleep3) {
4202
4216
  if (sleep3) {
4203
4217
  await Promise.race([
@@ -4228,7 +4242,7 @@ async function waitForWakeableTimeout(ms, setWake, sleep3) {
4228
4242
  setWake(null);
4229
4243
  }
4230
4244
 
4231
- // .dist-releases/1784862378006-1249051/src/supervisor/mailbox.js
4245
+ // .dist-releases/1784888160293-2271563/src/supervisor/mailbox.js
4232
4246
  var CLEAR_TAG = "CLEAR";
4233
4247
  var REFRESH_TAG = "WORKFLOW-REFRESH";
4234
4248
  var SYNC_TAG = "SYNC";
@@ -4299,7 +4313,9 @@ class MailboxSupervisor {
4299
4313
  let handled = await this.dispatchBatch(controls, true);
4300
4314
  if (this.stopped)
4301
4315
  return handled;
4302
- const messages = await this.claimOwnedMessages(true, undefined, true, this.limit, [...MAILBOX_CLAIM_EXCLUDED_TAGS]);
4316
+ const preview = await this.claimOwnedMessages(true);
4317
+ handled += await this.dispatchBatch(preview.filter(isControl));
4318
+ const messages = preview.filter((message) => !isControl(message));
4303
4319
  this.restoreDoorbellAttempts(messages);
4304
4320
  this.pruneDoorbellAttempts(messages);
4305
4321
  if (messages.length === 0)
@@ -4722,7 +4738,7 @@ function doorbellToken(messageId) {
4722
4738
  function refreshCue(_cli) {
4723
4739
  return `${REFRESH_TAG}
4724
4740
 
4725
- Run \`sail refresh\` before handling mailbox work.`;
4741
+ Run \`sail refresh\` now to reload your role and instructions, then continue.`;
4726
4742
  }
4727
4743
  function saveContextCue(agentName, checkpointId) {
4728
4744
  return [
@@ -4764,19 +4780,19 @@ function ordinaryDeliveryAttempt(error) {
4764
4780
  function _isMissingModerator(error) {
4765
4781
  return error instanceof RelayHttpError && error.status === 400 && error.body.includes("no agents match");
4766
4782
  }
4767
- // .dist-releases/1784862378006-1249051/src/supervisor/runtime.js
4783
+ // .dist-releases/1784888160293-2271563/src/supervisor/runtime.js
4768
4784
  import path9 from "node:path";
4769
4785
  import { execFile } from "node:child_process";
4770
4786
  import { promisify } from "node:util";
4771
4787
 
4772
- // .dist-releases/1784862378006-1249051/src/host/managed-materialization.js
4788
+ // .dist-releases/1784888160293-2271563/src/host/managed-materialization.js
4773
4789
  import { createHash as createHash2, randomBytes } from "node:crypto";
4774
4790
  import { constants as fsConstants } from "node:fs";
4775
4791
  import { lstat, mkdir as mkdir3, open, readdir, rename, rm as rm2 } from "node:fs/promises";
4776
4792
  import { homedir as homedir5 } from "node:os";
4777
4793
  import path5 from "node:path";
4778
4794
 
4779
- // .dist-releases/1784862378006-1249051/src/host/memory-stub.js
4795
+ // .dist-releases/1784888160293-2271563/src/host/memory-stub.js
4780
4796
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
4781
4797
  import { homedir as homedir4 } from "node:os";
4782
4798
  import path4 from "node:path";
@@ -4923,7 +4939,7 @@ function _safePathLabel(value, field) {
4923
4939
  return trimmed;
4924
4940
  }
4925
4941
 
4926
- // .dist-releases/1784862378006-1249051/src/host/managed-materialization.js
4942
+ // .dist-releases/1784888160293-2271563/src/host/managed-materialization.js
4927
4943
  var MANIFEST_FILE = ".sailorbridge-managed-manifest.json";
4928
4944
  var BANNER_PREFIX = "# SailorBridge managed file";
4929
4945
  var HASH_RE = /^sha256:[a-f0-9]{64}$/u;
@@ -5449,7 +5465,7 @@ function _expandHome2(value) {
5449
5465
  return value;
5450
5466
  }
5451
5467
 
5452
- // .dist-releases/1784862378006-1249051/src/host/credential-guardian.js
5468
+ // .dist-releases/1784888160293-2271563/src/host/credential-guardian.js
5453
5469
  import { lstat as lstat2, readFile as readFile3, rename as rename2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
5454
5470
  import path6 from "node:path";
5455
5471
  var DEFAULT_CREDENTIAL_SWEEP_INTERVAL_MS = 60000;
@@ -5595,7 +5611,7 @@ function _tmpSibling(filePath) {
5595
5611
  return path6.join(path6.dirname(filePath), `.${path6.basename(filePath)}.sb-guardian.${process.pid}.${tmpSequence}.tmp`);
5596
5612
  }
5597
5613
 
5598
- // .dist-releases/1784862378006-1249051/src/supervisor/resolve.js
5614
+ // .dist-releases/1784888160293-2271563/src/supervisor/resolve.js
5599
5615
  var DEFAULT_CAPTURE_LINES = 200;
5600
5616
  var MAX_CAPTURE_LINES = 1000;
5601
5617
 
@@ -5801,7 +5817,7 @@ function defaultSleep(ms) {
5801
5817
  return new Promise((resolve) => setTimeout(resolve, ms));
5802
5818
  }
5803
5819
 
5804
- // .dist-releases/1784862378006-1249051/src/supervisor/transcript-backfill.js
5820
+ // .dist-releases/1784888160293-2271563/src/supervisor/transcript-backfill.js
5805
5821
  import { createHash as createHash3 } from "node:crypto";
5806
5822
  import {
5807
5823
  closeSync,
@@ -6220,6 +6236,17 @@ class TranscriptBackfillWorker {
6220
6236
  store.rememberPostedEvent(source, event.eventId, item.nextOffset, fingerprint);
6221
6237
  this.clearPendingIfUnchanged(pendingMessageId);
6222
6238
  this.resetRetry();
6239
+ if (result.skippedReason !== undefined) {
6240
+ this.log("transcript_backfill_relay_skipped", {
6241
+ agent: this.options.agentName,
6242
+ engine: this.options.engine,
6243
+ source,
6244
+ offset: item.startOffset,
6245
+ eventId: event.eventId,
6246
+ reason: result.skippedReason
6247
+ });
6248
+ break;
6249
+ }
6223
6250
  count += 1;
6224
6251
  this.log("transcript_backfill_posted", {
6225
6252
  agent: this.options.agentName,
@@ -6248,7 +6275,8 @@ class TranscriptBackfillWorker {
6248
6275
  throw new Error("transcript backfill requires session");
6249
6276
  const target = this.pending?.replyTarget ?? { kind: "captain" };
6250
6277
  const inReplyToMessageId = target.kind === "captain" ? this.pending?.messageId : undefined;
6251
- return this.options.relay.postSessionReply(this.options.session, event.text, target, inReplyToMessageId);
6278
+ const origin = target.kind === "captain" ? "transcript-backfill" : undefined;
6279
+ return this.options.relay.postSessionReply(this.options.session, event.text, target, inReplyToMessageId, "REPLY", origin);
6252
6280
  }
6253
6281
  clearPendingIfUnchanged(pendingMessageId) {
6254
6282
  if (this.pending && this.pending.messageId === pendingMessageId)
@@ -6458,7 +6486,7 @@ function errorMessage4(error) {
6458
6486
  return error instanceof Error ? error.message : String(error);
6459
6487
  }
6460
6488
 
6461
- // .dist-releases/1784862378006-1249051/src/supervisor/usage-collector.js
6489
+ // .dist-releases/1784888160293-2271563/src/supervisor/usage-collector.js
6462
6490
  import { statSync as statSync2 } from "node:fs";
6463
6491
  import path8 from "node:path";
6464
6492
  var SUPPORTED_USAGE_ENGINES = new Set(["claude", "codex"]);
@@ -6867,7 +6895,7 @@ function sourceUnreadableReason(source) {
6867
6895
  }
6868
6896
  }
6869
6897
 
6870
- // .dist-releases/1784862378006-1249051/src/supervisor/runtime.js
6898
+ // .dist-releases/1784888160293-2271563/src/supervisor/runtime.js
6871
6899
  var RESOLVE_CAPTURE_LINES = 200;
6872
6900
  var SHORT_LIVED_EXIT_MS = 5000;
6873
6901
  var STARTUP_OBSERVATION_MS = 1e4;
@@ -7537,7 +7565,7 @@ function errorMessage5(error) {
7537
7565
  function formatRestartCommand(command) {
7538
7566
  return command.map((part) => /^[A-Za-z0-9_./:=@%+-]+$/.test(part) ? part : `'${part.replaceAll("'", "'\\''")}'`).join(" ");
7539
7567
  }
7540
- // .dist-releases/1784862378006-1249051/src/supervisor/transport.js
7568
+ // .dist-releases/1784888160293-2271563/src/supervisor/transport.js
7541
7569
  var SUPPORTED_PHASE1_CLI_KINDS = ["claude", "codex"];
7542
7570
  var PHASE1_ENGINE_GATE_MESSAGE = "Only claude and codex engines are available in this release; opencode and custom CLI engines will open in the next release.";
7543
7571
  var PHASE1_TRANSPORT_GATE_MESSAGE = "Only tmux transport is available in this release; sdk, headless, and pty transports will open in the next release.";
@@ -7745,7 +7773,7 @@ function hasCodexSafetyDriverOptions(options) {
7745
7773
  return options?.codex?.sandboxMode !== undefined || options?.codex?.approvalPolicy !== undefined || options?.codex?.yolo !== undefined;
7746
7774
  }
7747
7775
 
7748
- // .dist-releases/1784862378006-1249051/src/supervisor/cli-options.js
7776
+ // .dist-releases/1784888160293-2271563/src/supervisor/cli-options.js
7749
7777
  function parseSuperviseArgs(argv) {
7750
7778
  const split = argv.indexOf("--");
7751
7779
  const flagArgs = split >= 0 ? argv.slice(0, split) : argv;
@@ -7897,7 +7925,7 @@ function optionalBoolean(value, label) {
7897
7925
  return false;
7898
7926
  throw new Error(`${label} must be true or false`);
7899
7927
  }
7900
- // .dist-releases/1784862378006-1249051/src/supervisor/tmux-availability.js
7928
+ // .dist-releases/1784888160293-2271563/src/supervisor/tmux-availability.js
7901
7929
  import { spawn as spawn3 } from "node:child_process";
7902
7930
  import { createInterface } from "node:readline";
7903
7931
  function isTmuxAvailable(spawnFn = spawn3) {
@@ -8014,7 +8042,7 @@ function _tmuxMissingMessage(platform2) {
8014
8042
  const how = platform2 === "darwin" ? "brew install tmux" : platform2 === "linux" ? "install tmux via your package manager (e.g. sudo apt-get install -y tmux)" : "install tmux";
8015
8043
  return `tmux is required in this release but was not found. Install tmux (${how}) and re-run.`;
8016
8044
  }
8017
- // .dist-releases/1784862378006-1249051/src/output-format.js
8045
+ // .dist-releases/1784888160293-2271563/src/output-format.js
8018
8046
  function parseOutputArgs(argv) {
8019
8047
  const separator = argv.indexOf("--");
8020
8048
  const localArgv = separator < 0 ? argv : argv.slice(0, separator);
@@ -8107,7 +8135,7 @@ ${normalized.split(`
8107
8135
  `)}`;
8108
8136
  }
8109
8137
 
8110
- // .dist-releases/1784862378006-1249051/src/plain-output-fields.js
8138
+ // .dist-releases/1784888160293-2271563/src/plain-output-fields.js
8111
8139
  var PREVIEW_LIMIT = 20;
8112
8140
  var UNSAFE_DISPLAY_CODE_POINT = /[\u007f-\u009f\u2028-\u202e\u2066-\u2069]/gu;
8113
8141
  function renderTaskPlain(command, value) {
@@ -8515,7 +8543,7 @@ function _omissionLine(total) {
8515
8543
  return total > PREVIEW_LIMIT ? `Omitted ${total - PREVIEW_LIMIT}; rerun with --format json for the complete response.` : undefined;
8516
8544
  }
8517
8545
 
8518
- // .dist-releases/1784862378006-1249051/src/host/cli.js
8546
+ // .dist-releases/1784888160293-2271563/src/host/cli.js
8519
8547
  var HOST_RELAY_SUBCOMMANDS = new Set(["consume", "heartbeat", "list", "register"]);
8520
8548
  async function runHostCommandFromCli(client, argv, config, io) {
8521
8549
  const sub = argv[0] ?? "list";
@@ -8753,7 +8781,7 @@ function optionalDisplayName(value) {
8753
8781
  const trimmed = value?.trim();
8754
8782
  return trimmed || undefined;
8755
8783
  }
8756
- // .dist-releases/1784862378006-1249051/src/host/service.js
8784
+ // .dist-releases/1784888160293-2271563/src/host/service.js
8757
8785
  import { execFile as execFileCb } from "node:child_process";
8758
8786
  import { createHash as createHash4 } from "node:crypto";
8759
8787
  import { constants as fsConstants2 } from "node:fs";
@@ -8779,6 +8807,18 @@ async function installHostService(options, deps = {}) {
8779
8807
  return installSystemd(flags, runtime, paths, command);
8780
8808
  return installLaunchd(flags, runtime, paths, command);
8781
8809
  }
8810
+ async function restartRunningHostService(deps = {}) {
8811
+ const runtime = serviceRuntime(deps);
8812
+ if (runtime.platform !== "linux" && runtime.platform !== "darwin") {
8813
+ return { restarted: false, reason: "unsupported-platform" };
8814
+ }
8815
+ const paths = servicePaths(runtime, { dryRun: false });
8816
+ if (!await _isManagedHostService(runtime, paths))
8817
+ return { restarted: false, reason: "not-managed" };
8818
+ if (runtime.platform === "linux")
8819
+ return _restartRunningSystemd(runtime, paths);
8820
+ return _restartRunningLaunchd(runtime, paths);
8821
+ }
8782
8822
  var UNSUPPORTED_SERVICE_PLATFORM = "host service supports Linux systemd and macOS launchd only";
8783
8823
  async function runHostServiceCommandFromCli(argv, io, deps = {}) {
8784
8824
  const action = argv[0] ?? "status";
@@ -8968,6 +9008,32 @@ async function statusLaunchd(runtime, paths, io) {
8968
9008
  const installed = await fileExists(runtime, paths.file);
8969
9009
  _writeServiceOutput(io, { status: active ? "active" : "inactive", installed, platform: "launchd", file: paths.file, linger: "not-applicable" });
8970
9010
  }
9011
+ async function _restartRunningSystemd(runtime, paths) {
9012
+ const active = await optionalExec(runtime, "systemctl", ["--user", "is-active", paths.serviceName]);
9013
+ if (active?.stdout.trim() !== "active")
9014
+ return { restarted: false, reason: "not-running" };
9015
+ await runtime.execFile("systemctl", ["--user", "restart", paths.serviceName]);
9016
+ return { restarted: true, platform: "systemd", target: paths.serviceName };
9017
+ }
9018
+ async function _restartRunningLaunchd(runtime, paths) {
9019
+ const target = launchdServiceTarget(runtime.uid, paths.launchdLabel);
9020
+ if (!await optionalExec(runtime, "launchctl", ["print", target])) {
9021
+ return { restarted: false, reason: "not-running" };
9022
+ }
9023
+ await runtime.execFile("launchctl", ["kickstart", "-k", target]);
9024
+ return { restarted: true, platform: "launchd", target: paths.launchdLabel };
9025
+ }
9026
+ async function _isManagedHostService(runtime, paths) {
9027
+ try {
9028
+ const definition = await _readDefinition(runtime, paths.file);
9029
+ if (!definition)
9030
+ return false;
9031
+ const manager = runtime.platform === "linux" ? "systemd" : "launchd";
9032
+ return _isLegacyHostDefinition(definition, manager);
9033
+ } catch {
9034
+ return false;
9035
+ }
9036
+ }
8971
9037
  function serviceRuntime(deps) {
8972
9038
  const info = userInfo();
8973
9039
  return {
@@ -9105,8 +9171,8 @@ function _isLegacyHostDefinition(text, manager) {
9105
9171
  function _isSailorBridgeEntry(value) {
9106
9172
  if (!value)
9107
9173
  return false;
9108
- const segments = value.replace(/\\/gu, "/").split("/").filter(Boolean);
9109
- return segments.at(-1) === "cli.js" && segments.at(-2) === "dist" && segments.at(-3) === "client" && segments.at(-4) === "@sailorbridge";
9174
+ const normalized = `/${value.replace(/\\/gu, "/").split("/").filter(Boolean).join("/")}`;
9175
+ return normalized.endsWith("/@sailorbridge/client/dist/cli.js") || normalized.endsWith("/@sailorbridge/client/dist/src/cli.js");
9110
9176
  }
9111
9177
  function _systemdExecStart(text) {
9112
9178
  const values = [...text.matchAll(/^\s*ExecStart\s*=(.*)$/gmu)].map((match) => match[1]?.trim() ?? "").filter(Boolean);
@@ -9236,7 +9302,7 @@ function writeServiceLog(io, event, fields) {
9236
9302
  io.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...fields ?? {} })}
9237
9303
  `);
9238
9304
  }
9239
- // .dist-releases/1784862378006-1249051/src/host/git-remote.js
9305
+ // .dist-releases/1784888160293-2271563/src/host/git-remote.js
9240
9306
  var GIT_SECRET_PATH_MARKERS = [
9241
9307
  "token",
9242
9308
  "credential",
@@ -9328,7 +9394,7 @@ function looksLikeCredential(segment2) {
9328
9394
  return value.length >= 32 && /^[A-Za-z0-9_-]+$/u.test(value);
9329
9395
  }
9330
9396
 
9331
- // .dist-releases/1784862378006-1249051/src/host/start-spec.js
9397
+ // .dist-releases/1784888160293-2271563/src/host/start-spec.js
9332
9398
  var TRANSPORTS = ["auto", "sdk", "pty", "tmux", "headless"];
9333
9399
  var SUPPORTED_PHASE1_ENGINES = ["claude", "codex"];
9334
9400
  var ENGINE_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/u;
@@ -9578,7 +9644,7 @@ function _trimmed(value, label) {
9578
9644
  throw new Error(`${label} must not be empty`);
9579
9645
  return trimmed;
9580
9646
  }
9581
- // .dist-releases/1784862378006-1249051/src/host/workspace.js
9647
+ // .dist-releases/1784888160293-2271563/src/host/workspace.js
9582
9648
  import { spawn as spawn4 } from "node:child_process";
9583
9649
  import { existsSync as existsSync4 } from "node:fs";
9584
9650
  import { mkdir as mkdir5, readdir as readdir2, rename as rename4, stat } from "node:fs/promises";
@@ -9809,7 +9875,7 @@ function _safeSegment(value) {
9809
9875
  throw new Error("workspace path segment is empty after sanitizing");
9810
9876
  return segment2;
9811
9877
  }
9812
- // .dist-releases/1784862378006-1249051/src/host/workspace-inventory.js
9878
+ // .dist-releases/1784888160293-2271563/src/host/workspace-inventory.js
9813
9879
  import { access as access2 } from "node:fs/promises";
9814
9880
  import { constants } from "node:fs";
9815
9881
  import { execFile as execFile2 } from "node:child_process";
@@ -9901,25 +9967,179 @@ async function canAccess(path12, mode, deps) {
9901
9967
  return false;
9902
9968
  }
9903
9969
  }
9904
- // .dist-releases/1784862378006-1249051/src/host/host-config.js
9905
- import { chmod as chmod3, mkdir as mkdir6, readFile as readFile5, writeFile as writeFile6 } from "node:fs/promises";
9906
- import { homedir as homedir10 } from "node:os";
9970
+ // .dist-releases/1784888160293-2271563/src/upgrade.js
9971
+ import { spawn as spawn5 } from "node:child_process";
9972
+ import { execFile as execFileCb2 } from "node:child_process";
9973
+ import { readFile as readFile5 } from "node:fs/promises";
9907
9974
  import path12 from "node:path";
9975
+ import { promisify as promisify4 } from "node:util";
9976
+ var execFileDefault2 = promisify4(execFileCb2);
9977
+ var CLIENT_PACKAGE = "@sailorbridge/client";
9978
+ var LATEST_PACKAGE = `${CLIENT_PACKAGE}@latest`;
9979
+ async function runUpgradeFromCli(argv, io, deps = {}) {
9980
+ if (argv.length)
9981
+ throw new Error(`upgrade takes no arguments: ${argv.join(" ")}`);
9982
+ const before = deps.currentVersion ?? CLIENT_VERSION;
9983
+ await (deps.installLatest ?? installLatestClientPackage)();
9984
+ const after = await (deps.readInstalledVersion ?? readInstalledClientVersion)();
9985
+ if (before === after) {
9986
+ io.stdout.write(`SailorBridge client is already up to date (${after}).
9987
+ `);
9988
+ return;
9989
+ }
9990
+ const service = await (deps.restartHostService ?? restartRunningHostService)();
9991
+ _writeUpgradeResult(io.stdout, before, after, service);
9992
+ }
9993
+ function installLatestClientPackage() {
9994
+ return new Promise((resolve2, reject) => {
9995
+ const child = spawn5("npm", ["i", "-g", LATEST_PACKAGE], { stdio: "inherit" });
9996
+ child.once("error", reject);
9997
+ child.once("close", (code, signal) => {
9998
+ if (code === 0)
9999
+ resolve2();
10000
+ else
10001
+ reject(new Error(`npm i -g ${LATEST_PACKAGE} failed (${signal ?? `exit ${code ?? "unknown"}`})`));
10002
+ });
10003
+ });
10004
+ }
10005
+ async function readInstalledClientVersion() {
10006
+ const { stdout } = await execFileDefault2("npm", ["root", "-g"]);
10007
+ const root = String(stdout).trim();
10008
+ if (!root)
10009
+ throw new Error("npm root -g returned an empty path after upgrade");
10010
+ const packageJson = path12.join(root, "@sailorbridge", "client", "package.json");
10011
+ const parsed = JSON.parse(await readFile5(packageJson, "utf8"));
10012
+ const version = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : undefined;
10013
+ if (typeof version !== "string" || !version.trim()) {
10014
+ throw new Error(`${packageJson} has no valid version after upgrade`);
10015
+ }
10016
+ return version.trim();
10017
+ }
10018
+ function _writeUpgradeResult(output, before, after, service) {
10019
+ if (service.restarted) {
10020
+ output.write(`Updated SailorBridge client from ${before} to ${after} and restarted ${service.target} (${service.platform}).
10021
+ `);
10022
+ return;
10023
+ }
10024
+ output.write(`Updated SailorBridge client from ${before} to ${after}. No running SailorBridge-managed host service was found; stop and rerun 'sailorbridge host run' to load ${after}.
10025
+ `);
10026
+ }
10027
+
10028
+ // .dist-releases/1784888160293-2271563/src/host/auto-update.js
10029
+ var AUTO_UPDATE_HARD_DEADLINE_MS = 24 * 60 * 60 * 1000;
10030
+ var AUTO_UPDATE_ATTEMPT_COOLDOWN_MS = 60 * 1000;
10031
+ var MAX_UPDATE_ERROR_LENGTH = 500;
10032
+ function createAutoUpdater(options) {
10033
+ const logger = options.logger ?? (() => {});
10034
+ const deps = options.deps ?? {};
10035
+ const now = deps.now ?? (() => Date.now());
10036
+ const currentVersion = deps.currentVersion ?? CLIENT_VERSION;
10037
+ const installLatest = deps.installLatest ?? installLatestClientPackage;
10038
+ const readInstalledVersion = deps.readInstalledVersion ?? readInstalledClientVersion;
10039
+ const restartHostService = deps.restartHostService ?? restartRunningHostService;
10040
+ let pendingSince;
10041
+ let lastAttemptAt;
10042
+ let updateError;
10043
+ let installedVersion;
10044
+ const _attempt = async (reason, target) => {
10045
+ if (lastAttemptAt !== undefined && now() - lastAttemptAt < AUTO_UPDATE_ATTEMPT_COOLDOWN_MS)
10046
+ return;
10047
+ lastAttemptAt = now();
10048
+ logger("host_auto_update_start", { reason, target, current: currentVersion });
10049
+ try {
10050
+ await installLatest();
10051
+ const after = await readInstalledVersion();
10052
+ updateError = undefined;
10053
+ installedVersion = after;
10054
+ if (after === currentVersion) {
10055
+ logger("host_auto_update_noop", { reason, target, installed: after });
10056
+ return;
10057
+ }
10058
+ const report = await restartHostService();
10059
+ if (report.restarted) {
10060
+ logger("host_auto_update_restarting", { from: currentVersion, to: after, platform: report.platform, target: report.target });
10061
+ } else {
10062
+ logger("host_auto_update_installed_pending_restart", { from: currentVersion, to: after, reason: report.reason });
10063
+ }
10064
+ } catch (error) {
10065
+ const message = error instanceof Error ? error.message : String(error);
10066
+ updateError = message.length <= MAX_UPDATE_ERROR_LENGTH ? message : `${message.slice(0, MAX_UPDATE_ERROR_LENGTH - 3)}...`;
10067
+ logger("host_auto_update_error", { reason, target, error: updateError });
10068
+ }
10069
+ };
10070
+ return {
10071
+ async onHostReport(host) {
10072
+ if (!options.enabled)
10073
+ return;
10074
+ const latest = host.latest_client_version;
10075
+ if (!latest || compareReleaseSemver(latest, currentVersion) !== 1) {
10076
+ pendingSince = undefined;
10077
+ return;
10078
+ }
10079
+ const installedCmp = installedVersion === undefined ? null : compareReleaseSemver(installedVersion, latest);
10080
+ if (installedCmp === 0 || installedCmp === 1)
10081
+ return;
10082
+ if (pendingSince === undefined) {
10083
+ pendingSince = now();
10084
+ logger("host_auto_update_pending", { latest, current: currentVersion });
10085
+ }
10086
+ const forced = now() - pendingSince >= AUTO_UPDATE_HARD_DEADLINE_MS;
10087
+ if (options.isActive() && !forced)
10088
+ return;
10089
+ if (options.isMidWork())
10090
+ return;
10091
+ await _attempt(forced ? "deadline-forced" : "scheduled", latest);
10092
+ },
10093
+ async onUpgradeRequired(detail) {
10094
+ if (!options.enabled) {
10095
+ logger("host_auto_update_disabled_upgrade_required", { detail });
10096
+ return;
10097
+ }
10098
+ if (options.isMidWork())
10099
+ return;
10100
+ await _attempt("relay-rejected", null);
10101
+ },
10102
+ pendingUpdateError() {
10103
+ return updateError;
10104
+ }
10105
+ };
10106
+ }
10107
+ function compareReleaseSemver(a, b) {
10108
+ const left = _releaseTuple(a);
10109
+ const right = _releaseTuple(b);
10110
+ if (left === null || right === null)
10111
+ return null;
10112
+ for (let i = 0;i < 3; i += 1) {
10113
+ if (left[i] !== right[i])
10114
+ return left[i] < right[i] ? -1 : 1;
10115
+ }
10116
+ return 0;
10117
+ }
10118
+ function _releaseTuple(value) {
10119
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(value);
10120
+ if (!match)
10121
+ return null;
10122
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
10123
+ }
10124
+ // .dist-releases/1784888160293-2271563/src/host/host-config.js
10125
+ import { chmod as chmod3, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
10126
+ import { homedir as homedir10 } from "node:os";
10127
+ import path13 from "node:path";
9908
10128
  var HOST_CONFIG_KIND = "sailorbridge-host-config";
9909
10129
  function defaultHostConfigPath() {
9910
- return path12.join(homedir10(), ".sailorbridge", "host-config.json");
10130
+ return path13.join(homedir10(), ".sailorbridge", "host-config.json");
9911
10131
  }
9912
10132
  function normalizeWorkspacePath(entry) {
9913
10133
  const trimmed = entry.trim();
9914
10134
  if (!trimmed)
9915
10135
  throw new Error("workspace path must not be empty");
9916
10136
  const expanded = trimmed === "~" ? homedir10() : trimmed.startsWith("~/") ? `${homedir10()}${trimmed.slice(1)}` : trimmed;
9917
- return path12.resolve(expanded);
10137
+ return path13.resolve(expanded);
9918
10138
  }
9919
10139
  async function loadHostConfig(configPath = defaultHostConfigPath()) {
9920
10140
  let text;
9921
10141
  try {
9922
- text = await readFile5(configPath, "utf8");
10142
+ text = await readFile6(configPath, "utf8");
9923
10143
  } catch (error) {
9924
10144
  if (error.code === "ENOENT")
9925
10145
  return { kind: HOST_CONFIG_KIND, workspaces: [] };
@@ -9928,8 +10148,8 @@ async function loadHostConfig(configPath = defaultHostConfigPath()) {
9928
10148
  return _parseHostConfig(text, configPath);
9929
10149
  }
9930
10150
  async function saveHostConfig(configPath, config) {
9931
- await mkdir6(path12.dirname(configPath), { recursive: true, mode: 448 });
9932
- await chmod3(path12.dirname(configPath), 448);
10151
+ await mkdir6(path13.dirname(configPath), { recursive: true, mode: 448 });
10152
+ await chmod3(path13.dirname(configPath), 448);
9933
10153
  await writeFile6(configPath, `${JSON.stringify(config, null, 2)}
9934
10154
  `, { mode: 384 });
9935
10155
  await chmod3(configPath, 384);
@@ -9939,7 +10159,7 @@ async function addHostWorkspace(configPath, workspacePath) {
9939
10159
  const config = await loadHostConfig(configPath);
9940
10160
  if (config.workspaces.includes(normalized))
9941
10161
  return { config, path: normalized, added: false };
9942
- const next = { kind: HOST_CONFIG_KIND, workspaces: [...config.workspaces, normalized] };
10162
+ const next = { ...config, workspaces: [...config.workspaces, normalized] };
9943
10163
  await saveHostConfig(configPath, next);
9944
10164
  return { config: next, path: normalized, added: true };
9945
10165
  }
@@ -9960,7 +10180,7 @@ async function removeHostWorkspace(configPath, workspacePath) {
9960
10180
  const config = await loadHostConfig(configPath);
9961
10181
  if (!config.workspaces.includes(normalized))
9962
10182
  return { config, path: normalized, removed: false };
9963
- const next = { kind: HOST_CONFIG_KIND, workspaces: config.workspaces.filter((entry) => entry !== normalized) };
10183
+ const next = { ...config, workspaces: config.workspaces.filter((entry) => entry !== normalized) };
9964
10184
  await saveHostConfig(configPath, next);
9965
10185
  return { config: next, path: normalized, removed: true };
9966
10186
  }
@@ -9984,9 +10204,16 @@ function _parseHostConfig(text, configPath) {
9984
10204
  throw new Error(`host config workspaces[${index}] must be a non-empty string`);
9985
10205
  return value.trim();
9986
10206
  });
9987
- return { kind: HOST_CONFIG_KIND, workspaces };
10207
+ if (record.auto_update !== undefined && typeof record.auto_update !== "boolean") {
10208
+ throw new Error(`host config at ${configPath} auto_update must be a boolean`);
10209
+ }
10210
+ return {
10211
+ kind: HOST_CONFIG_KIND,
10212
+ workspaces,
10213
+ ...record.auto_update === undefined ? {} : { auto_update: record.auto_update }
10214
+ };
9988
10215
  }
9989
- // .dist-releases/1784862378006-1249051/src/host/host-workspace-cli.js
10216
+ // .dist-releases/1784888160293-2271563/src/host/host-workspace-cli.js
9990
10217
  async function runHostWorkspaceCommandFromCli(argv, io, deps = {}) {
9991
10218
  const configPath = deps.configPath ?? defaultHostConfigPath();
9992
10219
  const sub = argv[0] ?? "list";
@@ -10030,7 +10257,7 @@ function _renderAddPlain(result) {
10030
10257
  return result.note ? `${status} ${result.path}
10031
10258
  ${result.note}` : `${status} ${result.path}`;
10032
10259
  }
10033
- // .dist-releases/1784862378006-1249051/src/host/agent-env.js
10260
+ // .dist-releases/1784888160293-2271563/src/host/agent-env.js
10034
10261
  var RUNTIME_NATIVE_HOME_ENV = new Set(["CLAUDE_CONFIG_DIR", "CODEX_HOME"]);
10035
10262
  function buildAgentEnv(options) {
10036
10263
  const env = {};
@@ -10059,25 +10286,25 @@ function buildAgentEnv(options) {
10059
10286
  }
10060
10287
  return env;
10061
10288
  }
10062
- // .dist-releases/1784862378006-1249051/src/host/runtime-env.js
10063
- import path16 from "node:path";
10289
+ // .dist-releases/1784888160293-2271563/src/host/runtime-env.js
10290
+ import path17 from "node:path";
10064
10291
 
10065
- // .dist-releases/1784862378006-1249051/src/host/agent-defaults.js
10066
- import { chmod as chmod4, mkdir as mkdir7, readFile as readFile7, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
10292
+ // .dist-releases/1784888160293-2271563/src/host/agent-defaults.js
10293
+ import { chmod as chmod4, mkdir as mkdir7, readFile as readFile8, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
10067
10294
  import { homedir as homedir11 } from "node:os";
10068
- import path14 from "node:path";
10295
+ import path15 from "node:path";
10069
10296
 
10070
- // .dist-releases/1784862378006-1249051/src/host/claude-settings.js
10071
- import { readFile as readFile6 } from "node:fs/promises";
10072
- import path13 from "node:path";
10297
+ // .dist-releases/1784888160293-2271563/src/host/claude-settings.js
10298
+ import { readFile as readFile7 } from "node:fs/promises";
10299
+ import path14 from "node:path";
10073
10300
  async function readHostClaudeSettings(claudeConfigDir) {
10074
- const settings = await _readSettingsObject(path13.join(claudeConfigDir, "settings.json"));
10301
+ const settings = await _readSettingsObject(path14.join(claudeConfigDir, "settings.json"));
10075
10302
  return { env: _stringValues(settings.env), apiKeyHelper: _nonEmptyString(settings.apiKeyHelper) };
10076
10303
  }
10077
10304
  async function _readSettingsObject(filePath) {
10078
10305
  let text;
10079
10306
  try {
10080
- text = await readFile6(filePath, "utf8");
10307
+ text = await readFile7(filePath, "utf8");
10081
10308
  } catch (error) {
10082
10309
  if (error.code === "ENOENT")
10083
10310
  return {};
@@ -10108,7 +10335,7 @@ function _nonEmptyString(value) {
10108
10335
  return typeof value === "string" && value.trim() ? value : null;
10109
10336
  }
10110
10337
 
10111
- // .dist-releases/1784862378006-1249051/src/host/agent-defaults.js
10338
+ // .dist-releases/1784888160293-2271563/src/host/agent-defaults.js
10112
10339
  var CODEX_BLOCK_BEGIN = "# sailorbridge-terse-defaults:begin";
10113
10340
  var CODEX_BLOCK_END = "# sailorbridge-terse-defaults:end";
10114
10341
  var CODEX_MODEL_BLOCK_BEGIN = "# sailorbridge-host-model-settings:begin";
@@ -10760,23 +10987,23 @@ function _warnStatusLine(message) {
10760
10987
  `);
10761
10988
  }
10762
10989
  function resolveClaudeStatusLineCommand(runtime = { execPath: process.execPath, argv1: process.argv[1] }) {
10763
- const name = path14.basename(runtime.execPath);
10764
- const prefix = name === "node" || name === "bun" ? [path14.resolve(runtime.execPath), _requireEntry(runtime.argv1)] : [path14.resolve(runtime.execPath)];
10990
+ const name = path15.basename(runtime.execPath);
10991
+ const prefix = name === "node" || name === "bun" ? [path15.resolve(runtime.execPath), _requireEntry(runtime.argv1)] : [path15.resolve(runtime.execPath)];
10765
10992
  return [...prefix.map(_shellSingleQuote), "host", "statusline"].join(" ");
10766
10993
  }
10767
10994
  function _requireEntry(argv1) {
10768
10995
  const entry = argv1?.trim();
10769
10996
  if (!entry)
10770
10997
  throw new Error("cannot resolve claude statusline command: client entry point is unknown");
10771
- return path14.resolve(entry);
10998
+ return path15.resolve(entry);
10772
10999
  }
10773
11000
  async function _materializeClaudeDefaults(homeDir, workspaceDir, statusLineCommand, warn = _warnStatusLine, sourceHomeDir) {
10774
- const settingsPath = path14.join(homeDir, "settings.json");
10775
- const stylePath = path14.join(homeDir, "output-styles", "sailorbridge-terse.md");
10776
- const hooksDir = path14.join(homeDir, "hooks");
10777
- const hygienePath = path14.join(hooksDir, "tool-output-hygiene.py");
10778
- const refsPath = path14.join(hooksDir, "stale-refs-guard.py");
10779
- await mkdir7(path14.dirname(stylePath), { recursive: true });
11001
+ const settingsPath = path15.join(homeDir, "settings.json");
11002
+ const stylePath = path15.join(homeDir, "output-styles", "sailorbridge-terse.md");
11003
+ const hooksDir = path15.join(homeDir, "hooks");
11004
+ const hygienePath = path15.join(hooksDir, "tool-output-hygiene.py");
11005
+ const refsPath = path15.join(hooksDir, "stale-refs-guard.py");
11006
+ await mkdir7(path15.dirname(stylePath), { recursive: true });
10780
11007
  await _writeManagedFile(stylePath, CLAUDE_TERSE_STYLE, false);
10781
11008
  await mkdir7(hooksDir, { recursive: true });
10782
11009
  await _writeManagedFile(hygienePath, TOOL_OUTPUT_HYGIENE, true);
@@ -10791,7 +11018,7 @@ async function _materializeClaudeDefaults(homeDir, workspaceDir, statusLineComma
10791
11018
  { matcher: "Bash", command: `python3 ${_shellSingleQuote(refsPath)}` }
10792
11019
  ]);
10793
11020
  _ensureClaudeStatusLine(settings, statusLineCommand ?? resolveClaudeStatusLineCommand(), warn);
10794
- await mkdir7(path14.dirname(settingsPath), { recursive: true });
11021
+ await mkdir7(path15.dirname(settingsPath), { recursive: true });
10795
11022
  await writeFile7(settingsPath, JSON.stringify(settings, null, 2) + `
10796
11023
  `, { mode: 384 });
10797
11024
  if (workspaceDir)
@@ -10806,10 +11033,10 @@ async function _mergeHostClaudeEnv(settings, sourceHomeDir) {
10806
11033
  settings.env = { ..._jsonObject(settings.env, "Claude settings env"), ...hostEnv };
10807
11034
  }
10808
11035
  async function _trustClaudeWorkspace(homeDir, workspaceDir) {
10809
- const configPath = path14.join(homeDir, ".claude.json");
11036
+ const configPath = path15.join(homeDir, ".claude.json");
10810
11037
  const config = await _readJsonObject(configPath);
10811
11038
  const projects = _jsonObject(config.projects, "Claude config projects");
10812
- const projectPath = path14.resolve(_expandHome3(workspaceDir));
11039
+ const projectPath = path15.resolve(_expandHome3(workspaceDir));
10813
11040
  for (const trustedPath of await _claudeTrustRoots(projectPath)) {
10814
11041
  const project = _jsonObject(projects[trustedPath], `Claude config project ${trustedPath}`);
10815
11042
  project.hasTrustDialogAccepted = true;
@@ -10817,12 +11044,12 @@ async function _trustClaudeWorkspace(homeDir, workspaceDir) {
10817
11044
  }
10818
11045
  config.projects = projects;
10819
11046
  config.hasCompletedOnboarding = true;
10820
- await mkdir7(path14.dirname(configPath), { recursive: true });
11047
+ await mkdir7(path15.dirname(configPath), { recursive: true });
10821
11048
  await writeFile7(configPath, JSON.stringify(config, null, 2) + `
10822
11049
  `, { mode: 384 });
10823
11050
  }
10824
11051
  async function _claudeTrustRoots(projectPath) {
10825
- const pointer = path14.join(projectPath, ".git");
11052
+ const pointer = path15.join(projectPath, ".git");
10826
11053
  let stats;
10827
11054
  try {
10828
11055
  stats = await stat2(pointer);
@@ -10831,12 +11058,12 @@ async function _claudeTrustRoots(projectPath) {
10831
11058
  }
10832
11059
  if (!stats.isFile())
10833
11060
  return [projectPath];
10834
- const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile7(pointer, "utf8"))?.[1];
11061
+ const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile8(pointer, "utf8"))?.[1];
10835
11062
  if (!gitdir) {
10836
11063
  throw new Error(`workspace ${projectPath} has an unreadable .git pointer — cannot pre-trust its main repository for claude`);
10837
11064
  }
10838
- const resolved = path14.resolve(projectPath, gitdir);
10839
- const marker = `${path14.sep}.git${path14.sep}worktrees${path14.sep}`;
11065
+ const resolved = path15.resolve(projectPath, gitdir);
11066
+ const marker = `${path15.sep}.git${path15.sep}worktrees${path15.sep}`;
10840
11067
  const markerIndex = resolved.indexOf(marker);
10841
11068
  if (markerIndex === -1)
10842
11069
  return [projectPath];
@@ -10865,26 +11092,26 @@ function _claudeStatusLineShouldBeReplaced(command) {
10865
11092
  if (CLAUDE_STATUSLINE_SUBCOMMAND_RE.test(trimmed))
10866
11093
  return true;
10867
11094
  const first = trimmed.split(/\s+/u)[0] ?? "";
10868
- return SAIL_CLAUDE_STATUSLINE_BASENAMES.has(path14.basename(first));
11095
+ return SAIL_CLAUDE_STATUSLINE_BASENAMES.has(path15.basename(first));
10869
11096
  }
10870
11097
  function _claudeStatusLineConfig(command) {
10871
11098
  return { type: "command", command, refreshInterval: 20 };
10872
11099
  }
10873
11100
  async function _materializeCodexDefaults(homeDir, workspaceDir, sourceHomeDir, warn = _warnStatusLine) {
10874
- const configPath = path14.join(homeDir, "config.toml");
11101
+ const configPath = path15.join(homeDir, "config.toml");
10875
11102
  let current = await _readTextIfExists2(configPath);
10876
11103
  current = current.replace(new RegExp(`${_escapeRegExp(CODEX_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_BLOCK_END)}\\n?`, "u"), "");
10877
11104
  current = current.replace(new RegExp(`${_escapeRegExp(CODEX_TRUST_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_TRUST_BLOCK_END)}\\n?`, "u"), "");
10878
11105
  current = await _refreshCodexModelSettings(current, sourceHomeDir);
10879
11106
  current = _removeTopLevelTomlKeys(current, CODEX_UPDATE_CHECK_KEYS);
10880
- const trustedWorkspace = workspaceDir ? path14.resolve(_expandHome3(workspaceDir)) : undefined;
11107
+ const trustedWorkspace = workspaceDir ? path15.resolve(_expandHome3(workspaceDir)) : undefined;
10881
11108
  const trust = trustedWorkspace ? _setExistingCodexProjectTrust(current, trustedWorkspace) : { text: current, found: false };
10882
11109
  current = trust.text;
10883
11110
  const missing = Object.entries(CODEX_DEFAULTS).filter(([key]) => !_hasTopLevelTomlKey(current, key));
10884
11111
  const defaultsBlock = _codexManagedBlock(missing);
10885
11112
  const trustBlock = trustedWorkspace && !trust.found ? _codexTrustBlock(trustedWorkspace) : "";
10886
11113
  const next = _ensureCodexStatusLine(_appendTomlBlock(_insertBeforeFirstTomlTable(current, defaultsBlock), trustBlock), warn);
10887
- await mkdir7(path14.dirname(configPath), { recursive: true });
11114
+ await mkdir7(path15.dirname(configPath), { recursive: true });
10888
11115
  await writeFile7(configPath, next, { mode: 384 });
10889
11116
  }
10890
11117
  function _ensureCodexStatusLine(text, warn) {
@@ -10934,7 +11161,7 @@ ${CODEX_CONTEXT_STATUS_LINE}`);
10934
11161
  async function _refreshCodexModelSettings(current, sourceHomeDir) {
10935
11162
  if (!sourceHomeDir)
10936
11163
  return current;
10937
- const source = await _readTextIfExists2(path14.join(sourceHomeDir, "config.toml"));
11164
+ const source = await _readTextIfExists2(path15.join(sourceHomeDir, "config.toml"));
10938
11165
  const entries = _codexModelEntries(source);
10939
11166
  const withoutManaged = current.replace(new RegExp(`${_escapeRegExp(CODEX_MODEL_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_MODEL_BLOCK_END)}\\n?`, "u"), "");
10940
11167
  const keys = new Set(entries.map(({ key }) => key));
@@ -11201,7 +11428,7 @@ async function _writeManagedFile(filePath, content, executable) {
11201
11428
  if (existing && existing !== content && !existing.includes(MANAGED_MARKER2) && !_matchesMarkerlessManagedContent(existing, content)) {
11202
11429
  throw new Error(`refusing to overwrite unmanaged file: ${filePath}`);
11203
11430
  }
11204
- await mkdir7(path14.dirname(filePath), { recursive: true });
11431
+ await mkdir7(path15.dirname(filePath), { recursive: true });
11205
11432
  await writeFile7(filePath, content, { mode: executable ? 493 : 384 });
11206
11433
  if (executable)
11207
11434
  await chmod4(filePath, 493);
@@ -11217,13 +11444,13 @@ async function _readTextIfExists2(filePath) {
11217
11444
  return "";
11218
11445
  throw error;
11219
11446
  }
11220
- return readFile7(filePath, "utf8");
11447
+ return readFile8(filePath, "utf8");
11221
11448
  }
11222
11449
  function _expandHome3(value) {
11223
11450
  if (value === "~")
11224
11451
  return homedir11();
11225
11452
  if (value.startsWith("~/"))
11226
- return path14.join(homedir11(), value.slice(2));
11453
+ return path15.join(homedir11(), value.slice(2));
11227
11454
  return value;
11228
11455
  }
11229
11456
  function _shellSingleQuote(value) {
@@ -11238,10 +11465,10 @@ function _escapeRegExp(value) {
11238
11465
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
11239
11466
  }
11240
11467
 
11241
- // .dist-releases/1784862378006-1249051/src/host/engine-auth.js
11468
+ // .dist-releases/1784888160293-2271563/src/host/engine-auth.js
11242
11469
  import { lstat as lstat4, mkdir as mkdir8, readlink, realpath, stat as stat3, symlink as symlink2, unlink as unlink2 } from "node:fs/promises";
11243
11470
  import { homedir as homedir12 } from "node:os";
11244
- import path15 from "node:path";
11471
+ import path16 from "node:path";
11245
11472
  class EngineNotLoggedInError extends Error {
11246
11473
  constructor(engine, authPath) {
11247
11474
  super(`engine-auth-missing[${engine}]: ${_notLoggedInMessage(engine, authPath)}`);
@@ -11256,9 +11483,9 @@ async function provisionEngineAuth(options) {
11256
11483
  return null;
11257
11484
  throw new EngineNotLoggedInError(options.engine, source);
11258
11485
  }
11259
- const target = path15.join(options.homeDir, engineAuthFileName(options.engine));
11486
+ const target = path16.join(options.homeDir, engineAuthFileName(options.engine));
11260
11487
  const sharedTargetPath = await realpath(source);
11261
- await mkdir8(path15.dirname(target), { recursive: true });
11488
+ await mkdir8(path16.dirname(target), { recursive: true });
11262
11489
  await _ensureSymlink(target, source, sharedTargetPath);
11263
11490
  return { linkPath: target, sharedTargetPath };
11264
11491
  }
@@ -11267,7 +11494,7 @@ async function _hasEnvBasedAuth(engine, env) {
11267
11494
  return _nonEmpty(env.OPENAI_API_KEY);
11268
11495
  if (_nonEmpty(env.ANTHROPIC_AUTH_TOKEN) || _nonEmpty(env.ANTHROPIC_API_KEY))
11269
11496
  return true;
11270
- const settings = await readHostClaudeSettings(path15.dirname(engineAuthSourcePath("claude", env)));
11497
+ const settings = await readHostClaudeSettings(path16.dirname(engineAuthSourcePath("claude", env)));
11271
11498
  return _nonEmpty(settings.env.ANTHROPIC_AUTH_TOKEN) || _nonEmpty(settings.env.ANTHROPIC_API_KEY) || settings.apiKeyHelper !== null;
11272
11499
  }
11273
11500
  function _nonEmpty(value) {
@@ -11277,12 +11504,12 @@ function _notLoggedInMessage(engine, authPath) {
11277
11504
  if (engine === "codex") {
11278
11505
  return `engine is not authenticated: codex has no usable credential source. Provide either (1) auth.json at ${authPath} via codex login, or (2) OPENAI_API_KEY in the process environment.`;
11279
11506
  }
11280
- const settingsPath = path15.join(path15.dirname(authPath), "settings.json");
11507
+ const settingsPath = path16.join(path16.dirname(authPath), "settings.json");
11281
11508
  return `engine is not authenticated: claude has no usable credential source. Provide one of: (1) ${authPath} via claude login; ` + `(2) ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY or apiKeyHelper in ${settingsPath}; ` + `(3) ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY in the process environment.`;
11282
11509
  }
11283
11510
  function engineAuthSourcePath(engine, env = process.env) {
11284
11511
  const spec = _engineAuthSpec(engine, env);
11285
- return path15.join(spec.sourceDir, spec.fileName);
11512
+ return path16.join(spec.sourceDir, spec.fileName);
11286
11513
  }
11287
11514
  function engineAuthFileName(engine) {
11288
11515
  return _engineAuthSpec(engine, {}).fileName;
@@ -11290,9 +11517,9 @@ function engineAuthFileName(engine) {
11290
11517
  function _engineAuthSpec(engine, env) {
11291
11518
  const home = env.HOME?.trim() || homedir12();
11292
11519
  if (engine === "codex") {
11293
- return { sourceDir: env.CODEX_HOME?.trim() || path15.join(home, ".codex"), fileName: "auth.json" };
11520
+ return { sourceDir: env.CODEX_HOME?.trim() || path16.join(home, ".codex"), fileName: "auth.json" };
11294
11521
  }
11295
- return { sourceDir: env.CLAUDE_CONFIG_DIR?.trim() || path15.join(home, ".claude"), fileName: ".credentials.json" };
11522
+ return { sourceDir: env.CLAUDE_CONFIG_DIR?.trim() || path16.join(home, ".claude"), fileName: ".credentials.json" };
11296
11523
  }
11297
11524
  async function _isReadableAuthFile(filePath) {
11298
11525
  try {
@@ -11321,14 +11548,14 @@ async function _ensureSymlink(linkPath, targetPath, sharedTargetPath) {
11321
11548
  return;
11322
11549
  }
11323
11550
  const current = await readlink(linkPath);
11324
- const resolved = path15.resolve(path15.dirname(linkPath), current);
11325
- if (resolved === path15.resolve(targetPath))
11551
+ const resolved = path16.resolve(path16.dirname(linkPath), current);
11552
+ if (resolved === path16.resolve(targetPath))
11326
11553
  return;
11327
11554
  await unlink2(linkPath);
11328
11555
  await symlink2(targetPath, linkPath);
11329
11556
  }
11330
11557
 
11331
- // .dist-releases/1784862378006-1249051/src/host/runtime-env.js
11558
+ // .dist-releases/1784888160293-2271563/src/host/runtime-env.js
11332
11559
  async function materializeAgentRuntimeEnv(options) {
11333
11560
  const env = buildAgentEnv({
11334
11561
  hostEnv: options.hostEnv,
@@ -11347,7 +11574,7 @@ async function materializeAgentRuntimeEnv(options) {
11347
11574
  agentName: options.agentName,
11348
11575
  engine: memoryStub.engine,
11349
11576
  homeDir: memoryStub.homeDir,
11350
- sourceHomeDir: path16.dirname(engineAuthSourcePath(memoryStub.engine, options.hostEnv))
11577
+ sourceHomeDir: path17.dirname(engineAuthSourcePath(memoryStub.engine, options.hostEnv))
11351
11578
  });
11352
11579
  const credentialGuard = await provisionEngineAuth({
11353
11580
  engine: memoryStub.engine,
@@ -11368,7 +11595,7 @@ function _ensureRootSandboxEscape(env, engine, uid) {
11368
11595
  function _currentUid() {
11369
11596
  return typeof process.getuid === "function" ? process.getuid() : null;
11370
11597
  }
11371
- // .dist-releases/1784862378006-1249051/src/host/context-statusline.js
11598
+ // .dist-releases/1784888160293-2271563/src/host/context-statusline.js
11372
11599
  function renderClaudeContextStatusLine(payload) {
11373
11600
  const percent = _usedPercentage(payload);
11374
11601
  if (percent === null)
@@ -11406,12 +11633,12 @@ function _bar(percent) {
11406
11633
  const filled = Math.min(10, Math.max(0, Math.trunc(percent / 10)));
11407
11634
  return "█".repeat(filled) + "░".repeat(10 - filled);
11408
11635
  }
11409
- // .dist-releases/1784862378006-1249051/src/host/scratch-sweeper.js
11636
+ // .dist-releases/1784888160293-2271563/src/host/scratch-sweeper.js
11410
11637
  import { execFile as execFile3 } from "node:child_process";
11411
11638
  import { lstat as lstat5, readdir as readdir3, realpath as realpath2, unlink as unlink3 } from "node:fs/promises";
11412
- import path17 from "node:path";
11413
- import { promisify as promisify4 } from "node:util";
11414
- var execFileAsync3 = promisify4(execFile3);
11639
+ import path18 from "node:path";
11640
+ import { promisify as promisify5 } from "node:util";
11641
+ var execFileAsync3 = promisify5(execFile3);
11415
11642
  var SCRATCH_SUBDIR = ".agent/tmp";
11416
11643
  var SCRATCH_SWEEP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
11417
11644
  var SCRATCH_SWEEP_MAX_BYTES = 512 * 1024 * 1024;
@@ -11423,8 +11650,8 @@ function scratchSweepEnabled(env) {
11423
11650
  return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
11424
11651
  }
11425
11652
  async function sweepWorkspaceScratch(options) {
11426
- const root = path17.resolve(options.workspaceRoot);
11427
- const tmpDir = path17.join(root, SCRATCH_SUBDIR);
11653
+ const root = path18.resolve(options.workspaceRoot);
11654
+ const tmpDir = path18.join(root, SCRATCH_SUBDIR);
11428
11655
  const state = await _tmpDirState(root, tmpDir);
11429
11656
  if (state === "missing")
11430
11657
  return EMPTY;
@@ -11462,7 +11689,7 @@ async function _trackedScratchFiles(root, options) {
11462
11689
  const tracked = new Set;
11463
11690
  for (const rel of stdout.split("\x00"))
11464
11691
  if (rel)
11465
- tracked.add(path17.join(root, rel));
11692
+ tracked.add(path18.join(root, rel));
11466
11693
  return tracked;
11467
11694
  } catch (error) {
11468
11695
  options.logger?.("host_scratch_sweep_git_failed", { workspace: root, error: _errno(error) });
@@ -11472,7 +11699,7 @@ async function _trackedScratchFiles(root, options) {
11472
11699
  async function _collectScratchFiles(dir, root, logger) {
11473
11700
  const out = [];
11474
11701
  for (const entry of await readdir3(dir, { withFileTypes: true })) {
11475
- const abs = path17.join(dir, entry.name);
11702
+ const abs = path18.join(dir, entry.name);
11476
11703
  if (!_within(root, abs)) {
11477
11704
  logger?.("host_scratch_sweep_out_of_bounds", { workspace: root, path: abs });
11478
11705
  continue;
@@ -11538,8 +11765,8 @@ async function _runGit2(root, args) {
11538
11765
  return String(stdout);
11539
11766
  }
11540
11767
  function _within(root, target) {
11541
- const rel = path17.relative(root, target);
11542
- return rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel);
11768
+ const rel = path18.relative(root, target);
11769
+ return rel === "" || !rel.startsWith("..") && !path18.isAbsolute(rel);
11543
11770
  }
11544
11771
  function _isEnoent(error) {
11545
11772
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
@@ -11549,7 +11776,7 @@ function _errno(error) {
11549
11776
  return String(error.code);
11550
11777
  return error instanceof Error ? error.message : String(error);
11551
11778
  }
11552
- // .dist-releases/1784862378006-1249051/src/host/launcher.js
11779
+ // .dist-releases/1784888160293-2271563/src/host/launcher.js
11553
11780
  var MAX_TMUX_SESSION_SUFFIX = 100;
11554
11781
  async function launchSupervisedAgent(input, deps = {}) {
11555
11782
  const spec = parseAgentStartSpec(input.spec);
@@ -11700,10 +11927,10 @@ async function _tmuxSessionExists(session) {
11700
11927
  throw error;
11701
11928
  }
11702
11929
  }
11703
- // .dist-releases/1784862378006-1249051/src/worker/capability.js
11930
+ // .dist-releases/1784888160293-2271563/src/worker/capability.js
11704
11931
  import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
11705
11932
  import os from "node:os";
11706
- import path18 from "node:path";
11933
+ import path19 from "node:path";
11707
11934
  import { spawnSync } from "node:child_process";
11708
11935
  var TOOLS = ["podman", "git", "kubectl", "perf", "bpftrace"];
11709
11936
  var GPU_TAGS = new Set(["intel-b60", "nvidia-h100", "none"]);
@@ -11803,10 +12030,10 @@ function parseOsReleaseLine(line) {
11803
12030
  }
11804
12031
  function isReadonly(dir) {
11805
12032
  try {
11806
- const root = path18.resolve(dir);
12033
+ const root = path19.resolve(dir);
11807
12034
  mkdirSync3(root, { recursive: true });
11808
- const temp = mkdtempSync(path18.join(root, ".sailorbridge-write-test-"));
11809
- writeFileSync2(path18.join(temp, "probe"), "");
12035
+ const temp = mkdtempSync(path19.join(root, ".sailorbridge-write-test-"));
12036
+ writeFileSync2(path19.join(temp, "probe"), "");
11810
12037
  rmSync(temp, { recursive: true, force: true });
11811
12038
  return false;
11812
12039
  } catch {
@@ -11831,10 +12058,10 @@ function commandExists(command) {
11831
12058
  return commandPath(command) !== null;
11832
12059
  }
11833
12060
  function commandPath(command) {
11834
- if (command.includes(path18.sep))
12061
+ if (command.includes(path19.sep))
11835
12062
  return command;
11836
- for (const entry of (process.env.PATH ?? "").split(path18.delimiter)) {
11837
- const candidate = path18.join(entry, command);
12063
+ for (const entry of (process.env.PATH ?? "").split(path19.delimiter)) {
12064
+ const candidate = path19.join(entry, command);
11838
12065
  try {
11839
12066
  accessSync(candidate, constants2.X_OK);
11840
12067
  return candidate;
@@ -11843,7 +12070,7 @@ function commandPath(command) {
11843
12070
  return null;
11844
12071
  }
11845
12072
 
11846
- // .dist-releases/1784862378006-1249051/src/host/worker-start-spec.js
12073
+ // .dist-releases/1784888160293-2271563/src/host/worker-start-spec.js
11847
12074
  var MAX_TAGS = 32;
11848
12075
  var TAG_PATTERN = /^[a-z0-9][a-z0-9=._:/-]{0,63}$/iu;
11849
12076
  var RESERVED_TAG_KEYS = new Set(["os", "kernel", "arch", "gpu", "mode", "role", "tool"]);
@@ -11903,16 +12130,16 @@ function _trimmed2(value, label) {
11903
12130
  return trimmed;
11904
12131
  }
11905
12132
 
11906
- // .dist-releases/1784862378006-1249051/src/host/worker-launcher.js
12133
+ // .dist-releases/1784888160293-2271563/src/host/worker-launcher.js
11907
12134
  import { statSync as statSync3 } from "node:fs";
11908
12135
  import os4 from "node:os";
11909
- import path21 from "node:path";
11910
- // .dist-releases/1784862378006-1249051/src/worker/client.js
12136
+ import path22 from "node:path";
12137
+ // .dist-releases/1784888160293-2271563/src/worker/client.js
11911
12138
  import { readFileSync as readFileSync5 } from "node:fs";
11912
12139
  import os3 from "node:os";
11913
- import path20 from "node:path";
12140
+ import path21 from "node:path";
11914
12141
 
11915
- // .dist-releases/1784862378006-1249051/src/worker/errors.js
12142
+ // .dist-releases/1784888160293-2271563/src/worker/errors.js
11916
12143
  class LeaseFencingRejected extends Error {
11917
12144
  status;
11918
12145
  constructor(status, message) {
@@ -11926,10 +12153,10 @@ class WorkerHttpError extends Error {
11926
12153
  method;
11927
12154
  path;
11928
12155
  status;
11929
- constructor(method, path19, status, message) {
12156
+ constructor(method, path20, status, message) {
11930
12157
  super(message || `HTTP ${status}`);
11931
12158
  this.method = method;
11932
- this.path = path19;
12159
+ this.path = path20;
11933
12160
  this.status = status;
11934
12161
  this.name = "WorkerHttpError";
11935
12162
  }
@@ -11938,10 +12165,10 @@ class WorkerHttpError extends Error {
11938
12165
  class WorkerNetworkError extends Error {
11939
12166
  method;
11940
12167
  path;
11941
- constructor(method, path19, message) {
12168
+ constructor(method, path20, message) {
11942
12169
  super(message);
11943
12170
  this.method = method;
11944
- this.path = path19;
12171
+ this.path = path20;
11945
12172
  this.name = "WorkerNetworkError";
11946
12173
  }
11947
12174
  }
@@ -11949,14 +12176,14 @@ var LEASE_FENCING_BAD_REQUEST_MESSAGES = [
11949
12176
  "lease_token is required",
11950
12177
  "lease_token must be a non-empty string"
11951
12178
  ];
11952
- function raiseForLeaseFencingOrStatus(method, path19, status, body) {
12179
+ function raiseForLeaseFencingOrStatus(method, path20, status, body) {
11953
12180
  if (status === 409)
11954
12181
  throw new LeaseFencingRejected(status, body);
11955
12182
  if (status === 400 && LEASE_FENCING_BAD_REQUEST_MESSAGES.some((message) => body.includes(message))) {
11956
12183
  throw new LeaseFencingRejected(status, body);
11957
12184
  }
11958
12185
  if (status >= 300)
11959
- throw new WorkerHttpError(method, path19, status, body);
12186
+ throw new WorkerHttpError(method, path20, status, body);
11960
12187
  }
11961
12188
  function isNetworkError(error) {
11962
12189
  if (error instanceof WorkerNetworkError)
@@ -11968,23 +12195,23 @@ function isNetworkError(error) {
11968
12195
  const code = error.code;
11969
12196
  return code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EPIPE" || error.name === "AbortError";
11970
12197
  }
11971
- function countsTowardWorkerSessionRebuild(path19) {
11972
- if (path19 === "/api/v1/workers/register")
12198
+ function countsTowardWorkerSessionRebuild(path20) {
12199
+ if (path20 === "/api/v1/workers/register")
11973
12200
  return false;
11974
- if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path19))
12201
+ if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path20))
11975
12202
  return false;
11976
12203
  return true;
11977
12204
  }
11978
12205
 
11979
- // .dist-releases/1784862378006-1249051/src/worker/executor.js
11980
- import { spawn as spawn5 } from "node:child_process";
12206
+ // .dist-releases/1784888160293-2271563/src/worker/executor.js
12207
+ import { spawn as spawn6 } from "node:child_process";
11981
12208
  import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, realpathSync, rmSync as rmSync2, statfsSync, writeFileSync as writeFileSync3 } from "node:fs";
11982
12209
  import { tmpdir as tmpdir2, loadavg } from "node:os";
11983
12210
  import os2 from "node:os";
11984
- import path19 from "node:path";
12211
+ import path20 from "node:path";
11985
12212
  import { TextDecoder } from "node:util";
11986
12213
 
11987
- // .dist-releases/1784862378006-1249051/src/worker/async-queue.js
12214
+ // .dist-releases/1784888160293-2271563/src/worker/async-queue.js
11988
12215
  class AsyncByteQueue {
11989
12216
  maxSize;
11990
12217
  values = [];
@@ -12044,7 +12271,7 @@ class AsyncByteQueue {
12044
12271
  }
12045
12272
  }
12046
12273
 
12047
- // .dist-releases/1784862378006-1249051/src/worker/http.js
12274
+ // .dist-releases/1784888160293-2271563/src/worker/http.js
12048
12275
  import { request as httpRequest } from "node:http";
12049
12276
  import { request as httpsRequest } from "node:https";
12050
12277
  import { URLSearchParams as URLSearchParams2 } from "node:url";
@@ -12074,27 +12301,27 @@ class WorkerHttpClient {
12074
12301
  throw new Error("token is required");
12075
12302
  this.relayUrl = relayUrl.replace(/\/+$/u, "");
12076
12303
  }
12077
- async getJson(path19, query, timeouts, options = {}) {
12304
+ async getJson(path20, query, timeouts, options = {}) {
12078
12305
  const suffix = new URLSearchParams2(query).toString();
12079
- return this.requestJson("GET", suffix ? `${path19}?${suffix}` : path19, undefined, timeouts, options);
12306
+ return this.requestJson("GET", suffix ? `${path20}?${suffix}` : path20, undefined, timeouts, options);
12080
12307
  }
12081
- async postJson(path19, value, timeouts) {
12082
- return this.requestJson("POST", path19, { type: "json", value }, timeouts, {});
12308
+ async postJson(path20, value, timeouts) {
12309
+ return this.requestJson("POST", path20, { type: "json", value }, timeouts, {});
12083
12310
  }
12084
- async postBytes(path19, body, headers, timeouts) {
12085
- const response = await this.request("POST", path19, { type: "bytes", value: body }, timeouts, headers);
12086
- raiseForLeaseFencingOrStatus("POST", path19, response.status, response.text);
12311
+ async postBytes(path20, body, headers, timeouts) {
12312
+ const response = await this.request("POST", path20, { type: "bytes", value: body }, timeouts, headers);
12313
+ raiseForLeaseFencingOrStatus("POST", path20, response.status, response.text);
12087
12314
  }
12088
- async requestJson(method, path19, body, timeouts, options) {
12089
- const response = await this.request(method, path19, body, timeouts, {});
12090
- const errorPath = pathOnly(path19);
12315
+ async requestJson(method, path20, body, timeouts, options) {
12316
+ const response = await this.request(method, path20, body, timeouts, {});
12317
+ const errorPath = pathOnly(path20);
12091
12318
  if (options.leaseFencing === false && response.status >= 300)
12092
12319
  throw new WorkerHttpError(method, errorPath, response.status, response.text);
12093
12320
  raiseForLeaseFencingOrStatus(method, errorPath, response.status, response.text);
12094
12321
  return parseJson(method, errorPath, response.text);
12095
12322
  }
12096
- async request(method, path19, body, timeouts, extraHeaders) {
12097
- const url = new URL(`${this.relayUrl}${path19}`);
12323
+ async request(method, path20, body, timeouts, extraHeaders) {
12324
+ const url = new URL(`${this.relayUrl}${path20}`);
12098
12325
  const headers = this.headers(body, extraHeaders);
12099
12326
  return sendRequest(method, url, body, headers, timeouts, this.signal);
12100
12327
  }
@@ -12221,7 +12448,7 @@ function waitForRequestDrain(req) {
12221
12448
  req.once("close", onClose);
12222
12449
  });
12223
12450
  }
12224
- function parseJson(method, path19, text) {
12451
+ function parseJson(method, path20, text) {
12225
12452
  try {
12226
12453
  const parsed = JSON.parse(text);
12227
12454
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -12230,15 +12457,15 @@ function parseJson(method, path19, text) {
12230
12457
  return parsed;
12231
12458
  } catch (error) {
12232
12459
  if (error instanceof SyntaxError)
12233
- throw new WorkerNetworkError(method, path19, "response JSON is invalid");
12460
+ throw new WorkerNetworkError(method, path20, "response JSON is invalid");
12234
12461
  throw error;
12235
12462
  }
12236
12463
  }
12237
- function pathOnly(path19) {
12238
- return path19.split("?", 1)[0] || path19;
12464
+ function pathOnly(path20) {
12465
+ return path20.split("?", 1)[0] || path20;
12239
12466
  }
12240
12467
 
12241
- // .dist-releases/1784862378006-1249051/src/worker/stream-upload.js
12468
+ // .dist-releases/1784888160293-2271563/src/worker/stream-upload.js
12242
12469
  async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId) {
12243
12470
  if (!leaseToken.trim())
12244
12471
  throw new Error("lease_token is required");
@@ -12248,7 +12475,7 @@ async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId
12248
12475
  await session.http.postBytes(`/api/v1/jobs/${encodeURIComponent(jobId)}/stream-upload?${query.toString()}`, chunks, { "X-Lease-Token": leaseToken }, UPLOAD_TIMEOUTS);
12249
12476
  }
12250
12477
 
12251
- // .dist-releases/1784862378006-1249051/src/worker/executor.js
12478
+ // .dist-releases/1784888160293-2271563/src/worker/executor.js
12252
12479
  class StreamingProcessFailure extends Error {
12253
12480
  exitCode;
12254
12481
  stdout;
@@ -12320,8 +12547,8 @@ function sandboxPath(workdir, requested) {
12320
12547
  if (requested.startsWith("/") || parts.length === 0 || parts.includes("..")) {
12321
12548
  throw new Error("path must be a relative path inside the worker sandbox");
12322
12549
  }
12323
- const root = realpathSync(path19.resolve(workdir));
12324
- const target = path19.resolve(root, ...parts);
12550
+ const root = realpathSync(path20.resolve(workdir));
12551
+ const target = path20.resolve(root, ...parts);
12325
12552
  if (!isPathInside(root, target)) {
12326
12553
  throw new Error("path escapes worker sandbox");
12327
12554
  }
@@ -12461,7 +12688,7 @@ async function collectAndUpload(session, jobId, stream, pipe, leaseToken, worker
12461
12688
  return collected.join("");
12462
12689
  }
12463
12690
  function spawnProcess(argv, cwd) {
12464
- return spawn5(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
12691
+ return spawn6(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
12465
12692
  }
12466
12693
  async function terminateProcess(proc) {
12467
12694
  if (proc.pid === undefined)
@@ -12497,9 +12724,9 @@ function writeFileJob(workdir, spec, startedAt, started) {
12497
12724
  if (typeof spec.content !== "string")
12498
12725
  throw new Error("write_file spec.content must be a string");
12499
12726
  const target = sandboxPath(workdir, String(spec.path ?? ""));
12500
- mkdirSync4(path19.dirname(target), { recursive: true });
12727
+ mkdirSync4(path20.dirname(target), { recursive: true });
12501
12728
  writeFileSync3(target, spec.content, "utf8");
12502
- return output(0, path19.relative(realpathSync(path19.resolve(workdir)), target), "", startedAt, started);
12729
+ return output(0, path20.relative(realpathSync(path20.resolve(workdir)), target), "", startedAt, started);
12503
12730
  }
12504
12731
  function readUtf8File(target) {
12505
12732
  return decodeUtf8(readFileSync4(target));
@@ -12507,8 +12734,8 @@ function readUtf8File(target) {
12507
12734
  async function applyDiff(workdir, spec, signal) {
12508
12735
  if (typeof spec.diff_unified_text !== "string")
12509
12736
  throw new Error("apply_diff spec.diff_unified_text must be a string");
12510
- const dir = mkdtempSync2(path19.join(tmpdir2(), "sailorbridge-patch-"));
12511
- const patch = path19.join(dir, "change.patch");
12737
+ const dir = mkdtempSync2(path20.join(tmpdir2(), "sailorbridge-patch-"));
12738
+ const patch = path20.join(dir, "change.patch");
12512
12739
  writeFileSync3(patch, spec.diff_unified_text, "utf8");
12513
12740
  try {
12514
12741
  return await runArgv(["patch", "-p1", "-i", patch], workdir, coerceJobTimeoutSeconds(spec) * 1000, signal);
@@ -12748,24 +12975,24 @@ function resolveExistingTarget(target) {
12748
12975
  let current = target;
12749
12976
  while (true) {
12750
12977
  try {
12751
- return path19.resolve(realpathSync(current), ...missingParts.reverse());
12978
+ return path20.resolve(realpathSync(current), ...missingParts.reverse());
12752
12979
  } catch (error) {
12753
12980
  if (error.code !== "ENOENT")
12754
12981
  throw error;
12755
- const parent = path19.dirname(current);
12982
+ const parent = path20.dirname(current);
12756
12983
  if (parent === current)
12757
12984
  throw error;
12758
- missingParts.push(path19.basename(current));
12985
+ missingParts.push(path20.basename(current));
12759
12986
  current = parent;
12760
12987
  }
12761
12988
  }
12762
12989
  }
12763
12990
  function isPathInside(root, target) {
12764
- const relative = path19.relative(root, target);
12765
- return relative === "" || !relative.startsWith("..") && !path19.isAbsolute(relative);
12991
+ const relative = path20.relative(root, target);
12992
+ return relative === "" || !relative.startsWith("..") && !path20.isAbsolute(relative);
12766
12993
  }
12767
12994
  function prepareWorkdir(workdir) {
12768
- const resolved = path19.resolve(workdir);
12995
+ const resolved = path20.resolve(workdir);
12769
12996
  mkdirSync4(resolved, { recursive: true });
12770
12997
  return resolved;
12771
12998
  }
@@ -12780,7 +13007,7 @@ function errorMessage6(error) {
12780
13007
  return error instanceof Error ? error.message : String(error);
12781
13008
  }
12782
13009
 
12783
- // .dist-releases/1784862378006-1249051/src/worker/client.js
13010
+ // .dist-releases/1784888160293-2271563/src/worker/client.js
12784
13011
  var TOKEN_FILES = [
12785
13012
  "~/.config/sailorbridge/token",
12786
13013
  "/etc/sailorbridge/token"
@@ -13119,7 +13346,7 @@ ${JSON.stringify(result)}\r
13119
13346
  `);
13120
13347
  for (const file of attachments) {
13121
13348
  yield Buffer.from(`--${multipartBoundary}\r
13122
- Content-Disposition: form-data; name="files"; filename="${path20.basename(file)}"\r
13349
+ Content-Disposition: form-data; name="files"; filename="${path21.basename(file)}"\r
13123
13350
  \r
13124
13351
  `);
13125
13352
  yield readFileSync5(file);
@@ -13153,7 +13380,7 @@ function parseFlags2(argv) {
13153
13380
  return values;
13154
13381
  }
13155
13382
  function expandHome2(value) {
13156
- return value === "~" || value.startsWith("~/") ? path20.join(os3.homedir(), value.slice(2)) : value;
13383
+ return value === "~" || value.startsWith("~/") ? path21.join(os3.homedir(), value.slice(2)) : value;
13157
13384
  }
13158
13385
  function _sleep(ms, signal) {
13159
13386
  if (signal?.aborted)
@@ -13177,7 +13404,7 @@ function errorName2(error) {
13177
13404
  function errorMessage7(error) {
13178
13405
  return error instanceof Error ? error.message : String(error);
13179
13406
  }
13180
- // .dist-releases/1784862378006-1249051/src/host/worker-launcher.js
13407
+ // .dist-releases/1784888160293-2271563/src/host/worker-launcher.js
13181
13408
  var DEFAULT_WORKER_READY_TIMEOUT_MS = 60000;
13182
13409
  async function launchSupervisedWorker(input, deps = {}) {
13183
13410
  const logger = input.logger ?? (() => {});
@@ -13260,7 +13487,7 @@ function _requireWorkerName(descriptor) {
13260
13487
  return worker;
13261
13488
  }
13262
13489
  function _resolveWorkdir(workdir, homeDir) {
13263
- return workdir === "~" || workdir.startsWith("~/") ? path21.join(homeDir, workdir.slice(2)) : workdir;
13490
+ return workdir === "~" || workdir.startsWith("~/") ? path22.join(homeDir, workdir.slice(2)) : workdir;
13264
13491
  }
13265
13492
  function _assertWorkdir(workdir) {
13266
13493
  let stat4;
@@ -13274,7 +13501,7 @@ function _assertWorkdir(workdir) {
13274
13501
  return workdir;
13275
13502
  }
13276
13503
 
13277
- // .dist-releases/1784862378006-1249051/src/host/daemon.js
13504
+ // .dist-releases/1784888160293-2271563/src/host/daemon.js
13278
13505
  async function runHostDaemon(options) {
13279
13506
  const logger = options.logger ?? (() => {});
13280
13507
  const sleep3 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
@@ -13302,6 +13529,13 @@ async function runHostDaemon(options) {
13302
13529
  let lastRepoTipReportAt = now() - repoTipReportIntervalMs;
13303
13530
  const reconcileIntervalMs = options.reconcileIntervalMs ?? 5 * 60 * 1000;
13304
13531
  let lastReconcileAt = now() - reconcileIntervalMs;
13532
+ const autoUpdater = createAutoUpdater({
13533
+ enabled: options.autoUpdate !== false,
13534
+ isActive: () => runtimes.size > 0 || workerRuntimes.size > 0 || startLocks.size > 0 || cleanupLocks.size > 0,
13535
+ isMidWork: () => startLocks.size > 0 || cleanupLocks.size > 0,
13536
+ logger,
13537
+ deps: { now, ...options.autoUpdateDeps }
13538
+ });
13305
13539
  const _startWorker = async (request, recoveredDescriptor) => {
13306
13540
  const spec = parseWorkerStartSpec(request.spec);
13307
13541
  const descriptor = recoveredDescriptor ?? await options.resolveCredential(request);
@@ -13452,7 +13686,7 @@ async function runHostDaemon(options) {
13452
13686
  try {
13453
13687
  await _recoverDurableRuntimes();
13454
13688
  while (!options.signal?.aborted) {
13455
- const drained = await _drainOnce(options, identity, starter, logger, redactSecrets);
13689
+ const drained = await _drainOnce(options, identity, starter, logger, redactSecrets, autoUpdater);
13456
13690
  if (drained === "retired") {
13457
13691
  logger("host_daemon_retired", { host_id: options.hostId, status: 401 });
13458
13692
  break;
@@ -13588,7 +13822,7 @@ async function _loadHostConfigBestEffort(options, logger) {
13588
13822
  return { kind: "sailorbridge-host-config", workspaces: [] };
13589
13823
  }
13590
13824
  }
13591
- async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13825
+ async function _drainOnce(options, identity, starter, logger, redactSecrets, autoUpdater) {
13592
13826
  try {
13593
13827
  const hostConfig = await _loadHostConfigBestEffort(options, logger);
13594
13828
  const workspaceInventory = await discoverWorkspaceInventoryBestEffort(hostWorkspacePaths(hostConfig, options.hostEnv ?? process.env), {
@@ -13597,17 +13831,20 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13597
13831
  error: error instanceof Error ? error.message : String(error)
13598
13832
  })
13599
13833
  });
13834
+ const updateError = autoUpdater.pendingUpdateError();
13600
13835
  const result = await runHostRequestOnce({
13601
13836
  relay: options.relay,
13602
13837
  ...identity,
13603
13838
  starter,
13604
13839
  redactSecrets,
13605
13840
  ...workspaceInventory.length ? { workspaceInventory } : {},
13841
+ ...updateError === undefined ? {} : { updateError },
13606
13842
  onWorkspaceInventoryError: (error) => logger("host_workspace_inventory_error", {
13607
13843
  error: error instanceof Error ? error.message : String(error)
13608
13844
  }),
13609
13845
  ...options.signal ? { signal: options.signal } : {}
13610
13846
  });
13847
+ await autoUpdater.onHostReport(result.host);
13611
13848
  if (result.status === "idle")
13612
13849
  return false;
13613
13850
  logger(`host_request_${result.status}`, {
@@ -13619,11 +13856,16 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13619
13856
  } catch (error) {
13620
13857
  if (error instanceof RelayHttpError && error.status === 401)
13621
13858
  return "retired";
13859
+ if (error instanceof RelayHttpError && error.status === 426) {
13860
+ logger("host_daemon_upgrade_required", { error: error.message });
13861
+ await autoUpdater.onUpgradeRequired(error.body);
13862
+ return false;
13863
+ }
13622
13864
  logger("host_daemon_poll_error", { error: error instanceof Error ? error.message : String(error) });
13623
13865
  return false;
13624
13866
  }
13625
13867
  }
13626
- // .dist-releases/1784862378006-1249051/src/host/enrollment-redeem.js
13868
+ // .dist-releases/1784888160293-2271563/src/host/enrollment-redeem.js
13627
13869
  var DEFAULT_NOT_FOUND_RETRIES = 3;
13628
13870
  var NOT_FOUND_RETRY_DELAY_MS = 500;
13629
13871
  function createEnrollmentRedeemer(options) {
@@ -13760,7 +14002,7 @@ function _role2(enrollment) {
13760
14002
  }
13761
14003
  return _string4(enrollment.roles[0], "roles[0]").trim();
13762
14004
  }
13763
- // .dist-releases/1784862378006-1249051/src/host/tutorial.js
14005
+ // .dist-releases/1784888160293-2271563/src/host/tutorial.js
13764
14006
  function buildTutorialSteps(state) {
13765
14007
  const paired = state.hasCredential;
13766
14008
  return [
@@ -13831,7 +14073,7 @@ async function runTutorial(options) {
13831
14073
  }
13832
14074
  return steps;
13833
14075
  }
13834
- // .dist-releases/1784862378006-1249051/src/events.js
14076
+ // .dist-releases/1784888160293-2271563/src/events.js
13835
14077
  var EVENT_COMMANDS = new Set(["add", "list", "enable", "disable", "rm"]);
13836
14078
  var EVENT_FLAGS = new Set(["at", "days", "message", "run-date", "session", "tag", "tz"]);
13837
14079
  var PRODUCT_DAYS_MODES = new Set(["once", "daily", "weekday"]);
@@ -13921,9 +14163,9 @@ function _assertListArgs(args) {
13921
14163
  }
13922
14164
  }
13923
14165
 
13924
- // .dist-releases/1784862378006-1249051/src/text-input.js
13925
- import { readFile as readFile8 } from "node:fs/promises";
13926
- var _readUtf8File = async (file) => await readFile8(file, "utf8");
14166
+ // .dist-releases/1784888160293-2271563/src/text-input.js
14167
+ import { readFile as readFile9 } from "node:fs/promises";
14168
+ var _readUtf8File = async (file) => await readFile9(file, "utf8");
13927
14169
  async function readTextInput(input) {
13928
14170
  let content = "";
13929
14171
  for await (const chunk of input)
@@ -13955,17 +14197,17 @@ async function rejectUnusedTextInput(input, message) {
13955
14197
  input.unref?.();
13956
14198
  }
13957
14199
  }
13958
- async function readTextFile(path22, readUtf8File2 = _readUtf8File) {
13959
- return await readUtf8File2(path22);
14200
+ async function readTextFile(path23, readUtf8File2 = _readUtf8File) {
14201
+ return await readUtf8File2(path23);
13960
14202
  }
13961
- async function readTextFileOrStdin(path22, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
13962
- if (path22 === "-")
14203
+ async function readTextFileOrStdin(path23, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
14204
+ if (path23 === "-")
13963
14205
  return await readTextInput(input);
13964
14206
  await rejectUnusedTextInput(input, unusedInputMessage);
13965
- return await readTextFile(path22, readUtf8File2);
14207
+ return await readTextFile(path23, readUtf8File2);
13966
14208
  }
13967
14209
 
13968
- // .dist-releases/1784862378006-1249051/src/ask.js
14210
+ // .dist-releases/1784888160293-2271563/src/ask.js
13969
14211
  var ASK_FLAGS = new Set(["body-file", "option", "reason", "recommend", "session", "title"]);
13970
14212
  async function runAskFromCli(client2, argv, config, input) {
13971
14213
  const args = _parseAskArgs(argv);
@@ -14080,8 +14322,8 @@ function _singleAskValue(values, name) {
14080
14322
  return entries[0];
14081
14323
  }
14082
14324
 
14083
- // .dist-releases/1784862378006-1249051/src/task.js
14084
- import { readFile as readFile9 } from "node:fs/promises";
14325
+ // .dist-releases/1784888160293-2271563/src/task.js
14326
+ import { readFile as readFile10 } from "node:fs/promises";
14085
14327
  import { basename } from "node:path";
14086
14328
  var TASK_FLAGS = new Set(["after", "all", "artifact-id", "before", "body-file", "commit", "depends", "description", "description-file", "draft", "findings", "image", "level", "limit", "owner-of-resume", "pair", "priority", "query", "reason", "resume-condition", "resume-dependency", "session", "status", "type", "version"]);
14087
14329
  var TASK_BOOLEAN_FLAGS = new Set(["all", "draft"]);
@@ -14312,7 +14554,7 @@ async function _createTaskRequest(args, input) {
14312
14554
  async function _uploadTaskImages(client2, captainId, session, imagePaths) {
14313
14555
  const artifactIds = [];
14314
14556
  for (const imagePath of imagePaths) {
14315
- const content = await readFile9(imagePath);
14557
+ const content = await readFile10(imagePath);
14316
14558
  const artifact = await client2.uploadSessionArtifact(captainId, session, {
14317
14559
  name: basename(imagePath),
14318
14560
  content_bytes_base64: bytesToBase64(content)
@@ -14570,7 +14812,7 @@ function _taskListFooter(shown, returned, total, hasMore, cursor) {
14570
14812
  return `Shown ${shown} of ${total}${local}${next}`;
14571
14813
  }
14572
14814
 
14573
- // .dist-releases/1784862378006-1249051/src/team.js
14815
+ // .dist-releases/1784888160293-2271563/src/team.js
14574
14816
  var TEAM_BOOTSTRAP_ENGINES = new Set(["claude", "codex"]);
14575
14817
  var SLOT_SPEC_RE = /^([^=\s]+)=([^:@\s]+):([^:@\s]+)$/;
14576
14818
  var TEAM_PLAIN_LIMIT = 20;
@@ -14725,7 +14967,7 @@ function _slotTable(slots) {
14725
14967
  Shown ${shown.length} of ${slots.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`;
14726
14968
  }
14727
14969
 
14728
- // .dist-releases/1784862378006-1249051/src/memory.js
14970
+ // .dist-releases/1784888160293-2271563/src/memory.js
14729
14971
  var VALUE_FLAGS = new Set(["file", "source"]);
14730
14972
  var MEMORY_SEARCH_PLAIN_LIMIT = 20;
14731
14973
  async function runMemoryFromCli(client2, argv, config, input, format) {
@@ -14860,7 +15102,7 @@ function _summary(value) {
14860
15102
  return value.replace(/\s+/gu, " ").trim().slice(0, 180);
14861
15103
  }
14862
15104
 
14863
- // .dist-releases/1784862378006-1249051/src/mailbox-send.js
15105
+ // .dist-releases/1784888160293-2271563/src/mailbox-send.js
14864
15106
  var SEND_FLAGS = new Set(["body-file", "commit", "session", "tag", "task-id", "task-version", "to-agents", "to-roles", "to-host"]);
14865
15107
  function parseMailboxSendArgs(argv) {
14866
15108
  const parsed = argsFromValues(parseFlagValues(argv));
@@ -14959,7 +15201,7 @@ function splitList(value) {
14959
15201
  return list?.length ? list : undefined;
14960
15202
  }
14961
15203
 
14962
- // .dist-releases/1784862378006-1249051/src/reply.js
15204
+ // .dist-releases/1784888160293-2271563/src/reply.js
14963
15205
  var REPLY_FLAGS = new Set(["agent", "body-file", "in-reply-to", "session"]);
14964
15206
  async function sendReplyFromCli(client2, argv, config, io) {
14965
15207
  const args = _parseReplyArgs(argv);
@@ -15016,7 +15258,7 @@ function _requiredReplyValue(values, name) {
15016
15258
  return value;
15017
15259
  }
15018
15260
 
15019
- // .dist-releases/1784862378006-1249051/src/worker-command.js
15261
+ // .dist-releases/1784888160293-2271563/src/worker-command.js
15020
15262
  async function runWorkerCommandFromCli(client2, argv, config, io, format = "plain") {
15021
15263
  const captainId = config.captainId;
15022
15264
  if (!captainId)
@@ -15197,7 +15439,7 @@ function _writeSignalStatus(remoteCode, localStatus, remoteStderr, io) {
15197
15439
  `);
15198
15440
  }
15199
15441
 
15200
- // .dist-releases/1784862378006-1249051/src/create.js
15442
+ // .dist-releases/1784888160293-2271563/src/create.js
15201
15443
  var CREATE_ENGINES = new Set(["claude", "codex"]);
15202
15444
  var DEFAULT_ENGINE = "claude";
15203
15445
  var WORKSPACE_PLAIN_LIMIT = 20;
@@ -15251,9 +15493,9 @@ async function _startTeamSlots(deps, ctx, team, hostId, workspaceRoot, engine, s
15251
15493
  }
15252
15494
  return requests;
15253
15495
  }
15254
- async function _postConsoleJson(deps, ctx, path22, body) {
15496
+ async function _postConsoleJson(deps, ctx, path23, body) {
15255
15497
  const fetchImpl = deps.fetchImpl ?? fetch;
15256
- const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
15498
+ const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path23}`, {
15257
15499
  method: "POST",
15258
15500
  headers: { Authorization: `Bearer ${ctx.token}`, "Content-Type": "application/json" },
15259
15501
  body: JSON.stringify(body)
@@ -15266,9 +15508,9 @@ async function _postConsoleJson(deps, ctx, path22, body) {
15266
15508
  throw new Error("SailorBridge API returned a malformed object");
15267
15509
  return parsed;
15268
15510
  }
15269
- async function _getConsoleJson(deps, ctx, path22) {
15511
+ async function _getConsoleJson(deps, ctx, path23) {
15270
15512
  const fetchImpl = deps.fetchImpl ?? fetch;
15271
- const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
15513
+ const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path23}`, {
15272
15514
  headers: { Authorization: `Bearer ${ctx.token}` }
15273
15515
  });
15274
15516
  const text = await response.text();
@@ -15343,15 +15585,15 @@ function _rejectMemberFlags(role, flags) {
15343
15585
  throw new Error(`create ${role} does not accept ${found[0]}`);
15344
15586
  }
15345
15587
  async function _memberSlotForCreate(deps, ctx, session, role) {
15346
- const path22 = `/api/console/teams/${encodeURIComponent(session)}/agent-slots`;
15347
- const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx, path22));
15588
+ const path23 = `/api/console/teams/${encodeURIComponent(session)}/agent-slots`;
15589
+ const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx, path23));
15348
15590
  const retryable = slots.find((slot) => _isRetryableMemberSlot(slot, role));
15349
15591
  if (retryable)
15350
15592
  return retryable;
15351
15593
  if (role === "planner" && slots.some((slot) => slot.role === role)) {
15352
15594
  throw new Error("duplicate-singleton-role: team already has a planner slot");
15353
15595
  }
15354
- const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path22, { role }));
15596
+ const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path23, { role }));
15355
15597
  if (created.role !== role)
15356
15598
  throw new Error("SailorBridge API returned a member slot with the wrong role");
15357
15599
  return created;
@@ -15587,7 +15829,7 @@ function _workerPlain(hostId, workdir, result) {
15587
15829
  `);
15588
15830
  }
15589
15831
 
15590
- // .dist-releases/1784862378006-1249051/src/cli.js
15832
+ // .dist-releases/1784888160293-2271563/src/cli.js
15591
15833
  var FORMATTED_COMMANDS = new Set([
15592
15834
  "ask",
15593
15835
  "create",
@@ -15647,6 +15889,10 @@ async function main(argv) {
15647
15889
  });
15648
15890
  return;
15649
15891
  }
15892
+ if (command === "upgrade") {
15893
+ await runUpgradeFromCli(argv.slice(1), { stdout: process.stdout });
15894
+ return;
15895
+ }
15650
15896
  if (command === "host" && argv[1] === "start-request") {
15651
15897
  await runHostStartRequestCommandFromCli(argv.slice(2), { stdout: process.stdout, stderr: process.stderr, format: output2.format });
15652
15898
  return;
@@ -15753,6 +15999,7 @@ var KNOWN_COMMANDS = new Set([
15753
15999
  "task",
15754
16000
  "team",
15755
16001
  "tutorial",
16002
+ "upgrade",
15756
16003
  "workflow",
15757
16004
  "worker"
15758
16005
  ]);
@@ -15780,6 +16027,7 @@ var SUBCOMMANDS = {
15780
16027
  "unpark"
15781
16028
  ]),
15782
16029
  team: new Set(["bootstrap"]),
16030
+ upgrade: new Set,
15783
16031
  worker: new Set(["job", "list", "run", "serve"])
15784
16032
  };
15785
16033
  function _assertKnownCommand(command, subcommand) {
@@ -15981,8 +16229,9 @@ function _resolveOptionalApiUrl(flag, env) {
15981
16229
  }
15982
16230
  return env?.trim() || undefined;
15983
16231
  }
16232
+ var AUTO_UPDATE_SKIP_FLAG = "--disable-auto-update";
15984
16233
  async function hostRun(argv) {
15985
- const input = parseFlags3(argv);
16234
+ const input = parseFlags3(argv.filter((arg) => arg !== AUTO_UPDATE_SKIP_FLAG));
15986
16235
  const credentialsPath = input["credentials-path"] ?? process.env.SAILORBRIDGE_CREDENTIALS_FILE ?? defaultHostCredentialsPath();
15987
16236
  const credential = await loadHostCredential(credentialsPath);
15988
16237
  const relayUrl = credential.relay_url ?? process.env.SAILORBRIDGE_RELAY_URL;
@@ -16000,6 +16249,7 @@ async function hostRun(argv) {
16000
16249
  process.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, host_id: credential.host_id, ...fields })}
16001
16250
  `);
16002
16251
  };
16252
+ const autoUpdate = !argv.includes(AUTO_UPDATE_SKIP_FLAG) && await _hostConfigAllowsAutoUpdate(emit);
16003
16253
  await runHostDaemon({
16004
16254
  relay,
16005
16255
  resolveCredential,
@@ -16010,9 +16260,19 @@ async function hostRun(argv) {
16010
16260
  signal: controller.signal,
16011
16261
  redactSecrets: [credential.token],
16012
16262
  ...input["workspace-root"] ? { workspaceRoot: input["workspace-root"] } : {},
16263
+ autoUpdate,
16013
16264
  logger: emit
16014
16265
  });
16015
16266
  }
16267
+ async function _hostConfigAllowsAutoUpdate(emit) {
16268
+ try {
16269
+ const config = await loadHostConfig(process.env.SAILORBRIDGE_HOST_CONFIG_FILE ?? defaultHostConfigPath());
16270
+ return config.auto_update !== false;
16271
+ } catch (error) {
16272
+ emit("host_config_load_error", { error: error instanceof Error ? error.message : String(error) });
16273
+ return true;
16274
+ }
16275
+ }
16016
16276
  async function supervise(client2, argv, config) {
16017
16277
  const parsed = parseSuperviseArgs(argv);
16018
16278
  assertSingleOwnerConfirmed(parsed);
@@ -16234,6 +16494,8 @@ function _usageText() {
16234
16494
  sailorbridge workflow
16235
16495
  sailorbridge refresh
16236
16496
  sailorbridge --version
16497
+ sailorbridge upgrade
16498
+ installs @sailorbridge/client@latest and restarts a running SailorBridge-managed host service.
16237
16499
  sailorbridge tutorial [--check] [--api-url URL] [--credentials-path PATH] (default API: ${DEFAULT_PRODUCT_API_URL})
16238
16500
  sailorbridge login [--api-url URL] [--host-id H] [--display-name N] [--credentials-path PATH] [--binary PATH] [${SERVICE_SKIP_FLAG}] [--service-dir DIR] [--service-name NAME] (default API: ${DEFAULT_PRODUCT_API_URL})
16239
16501
  pairs this host, then installs and starts the host service so the console can start workers and agents here.
@@ -16283,7 +16545,7 @@ function _usageText() {
16283
16545
  sailorbridge host consume --once --dry-run-start [--host-id H] [--display-name N]
16284
16546
  sailorbridge host service install|uninstall|status [--binary PATH] [--credentials-path PATH] [--workspace-root DIR] [--service-dir DIR] [--service-name NAME]
16285
16547
  sailorbridge host start-request [--credentials-path PATH] [--api-url URL] [--access-token TOKEN] --team T [--host-id H] --agent A --engine codex --roles operator --workspace-mode fresh|git [--git-url URL]
16286
- sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR]
16548
+ sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR] [--disable-auto-update]
16287
16549
  sailorbridge worker list [--format plain|json]
16288
16550
  sailorbridge worker run [--detach] --worker NAME [--timeout SEC] [--format plain|json] -- ARGV...
16289
16551
  sailorbridge worker job ID [--format plain|json]
@@ -16337,5 +16599,5 @@ function _looksLikeNetworkFailure(message) {
16337
16599
  return /fetch failed|network|ECONN(?:REFUSED|RESET)|ENOTFOUND|ETIMEDOUT/iu.test(message);
16338
16600
  }
16339
16601
 
16340
- //# debugId=3B810968682A4A6F64756E2164756E21
16602
+ //# debugId=56B72B0801B5CDF264756E2164756E21
16341
16603
  //# sourceMappingURL=cli.js.map