@sailorbridge/client 0.2.2 → 0.2.4
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/README.md +37 -5
- package/dist/src/cli.js +543 -253
- package/dist/src/cli.js.map +16 -14
- package/package.json +1 -1
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/
|
|
5
|
+
// .dist-releases/1784894032002-2676534/src/cli.js
|
|
6
6
|
import { hostname as hostname3 } from "node:os";
|
|
7
7
|
import { writeSync } from "node:fs";
|
|
8
|
-
// .dist-releases/
|
|
8
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
223
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
405
|
+
// .dist-releases/1784894032002-2676534/src/host/request-consumer.js
|
|
406
406
|
import { hostname as hostname2 } from "node:os";
|
|
407
407
|
|
|
408
|
-
// .dist-releases/
|
|
408
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
428
|
-
var CLIENT_VERSION = "0.2.
|
|
429
|
-
var CLIENT_GIT_SHA = "
|
|
430
|
-
var CLIENT_BUILD_DIRTY =
|
|
427
|
+
// .dist-releases/1784894032002-2676534/src/version.js
|
|
428
|
+
var CLIENT_VERSION = "0.2.4";
|
|
429
|
+
var CLIENT_GIT_SHA = "22d3b8376a97";
|
|
430
|
+
var CLIENT_BUILD_DIRTY = false;
|
|
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/
|
|
436
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
492
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
552
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
629
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
693
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
708
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
747
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
762
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
772
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
948
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1129
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1156
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1182
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1334
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1339
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1362
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1386
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1406
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1531
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1926
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1931
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1968
|
+
// .dist-releases/1784894032002-2676534/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/
|
|
1998
|
+
// .dist-releases/1784894032002-2676534/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
|
|
2181
|
-
if (
|
|
2182
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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/
|
|
4064
|
+
// .dist-releases/1784894032002-2676534/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";
|
|
@@ -4065,6 +4079,7 @@ function loadRelayConfig(env = process.env) {
|
|
|
4065
4079
|
const captainId = optionalCaptainId(env.SAILORBRIDGE_CAPTAIN_ID) ?? stored?.captain_id;
|
|
4066
4080
|
const hostId = nonEmpty(env.SAILORBRIDGE_HOST_ID) ?? stored?.host_id;
|
|
4067
4081
|
const hostDisplayName = nonEmpty(env.SAILORBRIDGE_HOST_DISPLAY_NAME);
|
|
4082
|
+
const keyScope = !envBearerToken && stored ? "host" : undefined;
|
|
4068
4083
|
return {
|
|
4069
4084
|
relayUrl,
|
|
4070
4085
|
bearerToken,
|
|
@@ -4072,7 +4087,8 @@ function loadRelayConfig(env = process.env) {
|
|
|
4072
4087
|
...session ? { session } : {},
|
|
4073
4088
|
...captainId ? { captainId } : {},
|
|
4074
4089
|
...hostId ? { hostId } : {},
|
|
4075
|
-
...hostDisplayName ? { hostDisplayName } : {}
|
|
4090
|
+
...hostDisplayName ? { hostDisplayName } : {},
|
|
4091
|
+
...keyScope ? { keyScope } : {}
|
|
4076
4092
|
};
|
|
4077
4093
|
}
|
|
4078
4094
|
function loadStoredHostCredential(env, options = {}) {
|
|
@@ -4122,7 +4138,7 @@ function storedCaptainId(value) {
|
|
|
4122
4138
|
}
|
|
4123
4139
|
return;
|
|
4124
4140
|
}
|
|
4125
|
-
// .dist-releases/
|
|
4141
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/retirement.js
|
|
4126
4142
|
var DEFAULT_THRESHOLD = 3;
|
|
4127
4143
|
var DEFAULT_WINDOW_MS = 30000;
|
|
4128
4144
|
var DEFAULT_COOLDOWN_MS = 1000;
|
|
@@ -4197,7 +4213,7 @@ function isAuthoritativeRetirementReason(reason) {
|
|
|
4197
4213
|
return reason === "monitor_fetch" || reason === "agent_session_patch";
|
|
4198
4214
|
}
|
|
4199
4215
|
|
|
4200
|
-
// .dist-releases/
|
|
4216
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/wait.js
|
|
4201
4217
|
async function waitForWakeableTimeout(ms, setWake, sleep3) {
|
|
4202
4218
|
if (sleep3) {
|
|
4203
4219
|
await Promise.race([
|
|
@@ -4228,7 +4244,7 @@ async function waitForWakeableTimeout(ms, setWake, sleep3) {
|
|
|
4228
4244
|
setWake(null);
|
|
4229
4245
|
}
|
|
4230
4246
|
|
|
4231
|
-
// .dist-releases/
|
|
4247
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/mailbox.js
|
|
4232
4248
|
var CLEAR_TAG = "CLEAR";
|
|
4233
4249
|
var REFRESH_TAG = "WORKFLOW-REFRESH";
|
|
4234
4250
|
var SYNC_TAG = "SYNC";
|
|
@@ -4299,7 +4315,9 @@ class MailboxSupervisor {
|
|
|
4299
4315
|
let handled = await this.dispatchBatch(controls, true);
|
|
4300
4316
|
if (this.stopped)
|
|
4301
4317
|
return handled;
|
|
4302
|
-
const
|
|
4318
|
+
const preview = await this.claimOwnedMessages(true);
|
|
4319
|
+
handled += await this.dispatchBatch(preview.filter(isControl));
|
|
4320
|
+
const messages = preview.filter((message) => !isControl(message));
|
|
4303
4321
|
this.restoreDoorbellAttempts(messages);
|
|
4304
4322
|
this.pruneDoorbellAttempts(messages);
|
|
4305
4323
|
if (messages.length === 0)
|
|
@@ -4409,7 +4427,7 @@ class MailboxSupervisor {
|
|
|
4409
4427
|
if (this.stopped && !deliver)
|
|
4410
4428
|
return 0;
|
|
4411
4429
|
try {
|
|
4412
|
-
await this.deliverRefreshCue();
|
|
4430
|
+
await this.deliverRefreshCue(refreshNotices(pending));
|
|
4413
4431
|
} catch (error) {
|
|
4414
4432
|
await this.recordSurfaceBatch(pending, { status: "failed", error: this.redact(errorMessage2(error)) });
|
|
4415
4433
|
return 0;
|
|
@@ -4421,8 +4439,8 @@ class MailboxSupervisor {
|
|
|
4421
4439
|
await this.claimDeliveredControls(messages);
|
|
4422
4440
|
return pending.length;
|
|
4423
4441
|
}
|
|
4424
|
-
async deliverRefreshCue() {
|
|
4425
|
-
const text = refreshCue(this.options.cli);
|
|
4442
|
+
async deliverRefreshCue(notices = []) {
|
|
4443
|
+
const text = refreshCue(this.options.cli, notices);
|
|
4426
4444
|
await this.options.driver.send(text, { verifyText: REFRESH_TAG });
|
|
4427
4445
|
}
|
|
4428
4446
|
async handleSyncMessages(messages, deliver = false) {
|
|
@@ -4706,7 +4724,7 @@ function formatWorkDoorbell(input, toDisplay) {
|
|
|
4706
4724
|
throw new Error("cannot format an empty work doorbell");
|
|
4707
4725
|
const sender = message.sender_agent_name ?? message.sender_role ?? "unknown";
|
|
4708
4726
|
const tag = canonicalDoorbellTag(tagOf(message));
|
|
4709
|
-
return `[${sender}->${toDisplay}] ${tag} ${doorbellToken(message.id)} \uD83D\uDCEC ${messages.length} new message(s). Read
|
|
4727
|
+
return `[${sender}->${toDisplay}] ${tag} ${doorbellToken(message.id)} \uD83D\uDCEC ${messages.length} new message(s). Read: sail mailbox claim. Conversational tags: answer with one sail reply — no ack owed. Obligation tags: complete them; their claim output prints the ack line.`;
|
|
4710
4728
|
}
|
|
4711
4729
|
function canonicalDoorbellTag(tag) {
|
|
4712
4730
|
const printable = Array.from(tag, (char) => char >= " " && char <= "~" ? char : " ").join("");
|
|
@@ -4719,10 +4737,27 @@ function unsurfaced(messages) {
|
|
|
4719
4737
|
function doorbellToken(messageId) {
|
|
4720
4738
|
return `#${messageId}#`;
|
|
4721
4739
|
}
|
|
4722
|
-
function refreshCue(_cli) {
|
|
4723
|
-
return
|
|
4740
|
+
function refreshCue(_cli, notices = []) {
|
|
4741
|
+
return [
|
|
4742
|
+
REFRESH_TAG,
|
|
4743
|
+
...notices,
|
|
4744
|
+
"Run `sail refresh` now to reload your role and instructions, then continue."
|
|
4745
|
+
].join(`
|
|
4724
4746
|
|
|
4725
|
-
|
|
4747
|
+
`);
|
|
4748
|
+
}
|
|
4749
|
+
function refreshNotices(messages) {
|
|
4750
|
+
const notices = [];
|
|
4751
|
+
for (const message of messages) {
|
|
4752
|
+
const payload = message.payload_json;
|
|
4753
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4754
|
+
continue;
|
|
4755
|
+
const notice = payload.notice;
|
|
4756
|
+
if (typeof notice === "string" && notice.trim() && !notices.includes(notice.trim())) {
|
|
4757
|
+
notices.push(notice.trim());
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
return notices;
|
|
4726
4761
|
}
|
|
4727
4762
|
function saveContextCue(agentName, checkpointId) {
|
|
4728
4763
|
return [
|
|
@@ -4764,19 +4799,19 @@ function ordinaryDeliveryAttempt(error) {
|
|
|
4764
4799
|
function _isMissingModerator(error) {
|
|
4765
4800
|
return error instanceof RelayHttpError && error.status === 400 && error.body.includes("no agents match");
|
|
4766
4801
|
}
|
|
4767
|
-
// .dist-releases/
|
|
4802
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/runtime.js
|
|
4768
4803
|
import path9 from "node:path";
|
|
4769
4804
|
import { execFile } from "node:child_process";
|
|
4770
4805
|
import { promisify } from "node:util";
|
|
4771
4806
|
|
|
4772
|
-
// .dist-releases/
|
|
4807
|
+
// .dist-releases/1784894032002-2676534/src/host/managed-materialization.js
|
|
4773
4808
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
4774
4809
|
import { constants as fsConstants } from "node:fs";
|
|
4775
4810
|
import { lstat, mkdir as mkdir3, open, readdir, rename, rm as rm2 } from "node:fs/promises";
|
|
4776
4811
|
import { homedir as homedir5 } from "node:os";
|
|
4777
4812
|
import path5 from "node:path";
|
|
4778
4813
|
|
|
4779
|
-
// .dist-releases/
|
|
4814
|
+
// .dist-releases/1784894032002-2676534/src/host/memory-stub.js
|
|
4780
4815
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
|
|
4781
4816
|
import { homedir as homedir4 } from "node:os";
|
|
4782
4817
|
import path4 from "node:path";
|
|
@@ -4923,7 +4958,7 @@ function _safePathLabel(value, field) {
|
|
|
4923
4958
|
return trimmed;
|
|
4924
4959
|
}
|
|
4925
4960
|
|
|
4926
|
-
// .dist-releases/
|
|
4961
|
+
// .dist-releases/1784894032002-2676534/src/host/managed-materialization.js
|
|
4927
4962
|
var MANIFEST_FILE = ".sailorbridge-managed-manifest.json";
|
|
4928
4963
|
var BANNER_PREFIX = "# SailorBridge managed file";
|
|
4929
4964
|
var HASH_RE = /^sha256:[a-f0-9]{64}$/u;
|
|
@@ -5449,7 +5484,7 @@ function _expandHome2(value) {
|
|
|
5449
5484
|
return value;
|
|
5450
5485
|
}
|
|
5451
5486
|
|
|
5452
|
-
// .dist-releases/
|
|
5487
|
+
// .dist-releases/1784894032002-2676534/src/host/credential-guardian.js
|
|
5453
5488
|
import { lstat as lstat2, readFile as readFile3, rename as rename2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
|
|
5454
5489
|
import path6 from "node:path";
|
|
5455
5490
|
var DEFAULT_CREDENTIAL_SWEEP_INTERVAL_MS = 60000;
|
|
@@ -5595,7 +5630,7 @@ function _tmpSibling(filePath) {
|
|
|
5595
5630
|
return path6.join(path6.dirname(filePath), `.${path6.basename(filePath)}.sb-guardian.${process.pid}.${tmpSequence}.tmp`);
|
|
5596
5631
|
}
|
|
5597
5632
|
|
|
5598
|
-
// .dist-releases/
|
|
5633
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/resolve.js
|
|
5599
5634
|
var DEFAULT_CAPTURE_LINES = 200;
|
|
5600
5635
|
var MAX_CAPTURE_LINES = 1000;
|
|
5601
5636
|
|
|
@@ -5801,7 +5836,7 @@ function defaultSleep(ms) {
|
|
|
5801
5836
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5802
5837
|
}
|
|
5803
5838
|
|
|
5804
|
-
// .dist-releases/
|
|
5839
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/transcript-backfill.js
|
|
5805
5840
|
import { createHash as createHash3 } from "node:crypto";
|
|
5806
5841
|
import {
|
|
5807
5842
|
closeSync,
|
|
@@ -6220,6 +6255,17 @@ class TranscriptBackfillWorker {
|
|
|
6220
6255
|
store.rememberPostedEvent(source, event.eventId, item.nextOffset, fingerprint);
|
|
6221
6256
|
this.clearPendingIfUnchanged(pendingMessageId);
|
|
6222
6257
|
this.resetRetry();
|
|
6258
|
+
if (result.skippedReason !== undefined) {
|
|
6259
|
+
this.log("transcript_backfill_relay_skipped", {
|
|
6260
|
+
agent: this.options.agentName,
|
|
6261
|
+
engine: this.options.engine,
|
|
6262
|
+
source,
|
|
6263
|
+
offset: item.startOffset,
|
|
6264
|
+
eventId: event.eventId,
|
|
6265
|
+
reason: result.skippedReason
|
|
6266
|
+
});
|
|
6267
|
+
break;
|
|
6268
|
+
}
|
|
6223
6269
|
count += 1;
|
|
6224
6270
|
this.log("transcript_backfill_posted", {
|
|
6225
6271
|
agent: this.options.agentName,
|
|
@@ -6248,7 +6294,8 @@ class TranscriptBackfillWorker {
|
|
|
6248
6294
|
throw new Error("transcript backfill requires session");
|
|
6249
6295
|
const target = this.pending?.replyTarget ?? { kind: "captain" };
|
|
6250
6296
|
const inReplyToMessageId = target.kind === "captain" ? this.pending?.messageId : undefined;
|
|
6251
|
-
|
|
6297
|
+
const origin = target.kind === "captain" ? "transcript-backfill" : undefined;
|
|
6298
|
+
return this.options.relay.postSessionReply(this.options.session, event.text, target, inReplyToMessageId, "REPLY", origin);
|
|
6252
6299
|
}
|
|
6253
6300
|
clearPendingIfUnchanged(pendingMessageId) {
|
|
6254
6301
|
if (this.pending && this.pending.messageId === pendingMessageId)
|
|
@@ -6458,7 +6505,7 @@ function errorMessage4(error) {
|
|
|
6458
6505
|
return error instanceof Error ? error.message : String(error);
|
|
6459
6506
|
}
|
|
6460
6507
|
|
|
6461
|
-
// .dist-releases/
|
|
6508
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/usage-collector.js
|
|
6462
6509
|
import { statSync as statSync2 } from "node:fs";
|
|
6463
6510
|
import path8 from "node:path";
|
|
6464
6511
|
var SUPPORTED_USAGE_ENGINES = new Set(["claude", "codex"]);
|
|
@@ -6867,7 +6914,7 @@ function sourceUnreadableReason(source) {
|
|
|
6867
6914
|
}
|
|
6868
6915
|
}
|
|
6869
6916
|
|
|
6870
|
-
// .dist-releases/
|
|
6917
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/runtime.js
|
|
6871
6918
|
var RESOLVE_CAPTURE_LINES = 200;
|
|
6872
6919
|
var SHORT_LIVED_EXIT_MS = 5000;
|
|
6873
6920
|
var STARTUP_OBSERVATION_MS = 1e4;
|
|
@@ -7537,7 +7584,7 @@ function errorMessage5(error) {
|
|
|
7537
7584
|
function formatRestartCommand(command) {
|
|
7538
7585
|
return command.map((part) => /^[A-Za-z0-9_./:=@%+-]+$/.test(part) ? part : `'${part.replaceAll("'", "'\\''")}'`).join(" ");
|
|
7539
7586
|
}
|
|
7540
|
-
// .dist-releases/
|
|
7587
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/transport.js
|
|
7541
7588
|
var SUPPORTED_PHASE1_CLI_KINDS = ["claude", "codex"];
|
|
7542
7589
|
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
7590
|
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 +7792,7 @@ function hasCodexSafetyDriverOptions(options) {
|
|
|
7745
7792
|
return options?.codex?.sandboxMode !== undefined || options?.codex?.approvalPolicy !== undefined || options?.codex?.yolo !== undefined;
|
|
7746
7793
|
}
|
|
7747
7794
|
|
|
7748
|
-
// .dist-releases/
|
|
7795
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/cli-options.js
|
|
7749
7796
|
function parseSuperviseArgs(argv) {
|
|
7750
7797
|
const split = argv.indexOf("--");
|
|
7751
7798
|
const flagArgs = split >= 0 ? argv.slice(0, split) : argv;
|
|
@@ -7897,7 +7944,7 @@ function optionalBoolean(value, label) {
|
|
|
7897
7944
|
return false;
|
|
7898
7945
|
throw new Error(`${label} must be true or false`);
|
|
7899
7946
|
}
|
|
7900
|
-
// .dist-releases/
|
|
7947
|
+
// .dist-releases/1784894032002-2676534/src/supervisor/tmux-availability.js
|
|
7901
7948
|
import { spawn as spawn3 } from "node:child_process";
|
|
7902
7949
|
import { createInterface } from "node:readline";
|
|
7903
7950
|
function isTmuxAvailable(spawnFn = spawn3) {
|
|
@@ -8014,7 +8061,7 @@ function _tmuxMissingMessage(platform2) {
|
|
|
8014
8061
|
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
8062
|
return `tmux is required in this release but was not found. Install tmux (${how}) and re-run.`;
|
|
8016
8063
|
}
|
|
8017
|
-
// .dist-releases/
|
|
8064
|
+
// .dist-releases/1784894032002-2676534/src/output-format.js
|
|
8018
8065
|
function parseOutputArgs(argv) {
|
|
8019
8066
|
const separator = argv.indexOf("--");
|
|
8020
8067
|
const localArgv = separator < 0 ? argv : argv.slice(0, separator);
|
|
@@ -8107,7 +8154,7 @@ ${normalized.split(`
|
|
|
8107
8154
|
`)}`;
|
|
8108
8155
|
}
|
|
8109
8156
|
|
|
8110
|
-
// .dist-releases/
|
|
8157
|
+
// .dist-releases/1784894032002-2676534/src/plain-output-fields.js
|
|
8111
8158
|
var PREVIEW_LIMIT = 20;
|
|
8112
8159
|
var UNSAFE_DISPLAY_CODE_POINT = /[\u007f-\u009f\u2028-\u202e\u2066-\u2069]/gu;
|
|
8113
8160
|
function renderTaskPlain(command, value) {
|
|
@@ -8515,7 +8562,7 @@ function _omissionLine(total) {
|
|
|
8515
8562
|
return total > PREVIEW_LIMIT ? `Omitted ${total - PREVIEW_LIMIT}; rerun with --format json for the complete response.` : undefined;
|
|
8516
8563
|
}
|
|
8517
8564
|
|
|
8518
|
-
// .dist-releases/
|
|
8565
|
+
// .dist-releases/1784894032002-2676534/src/host/cli.js
|
|
8519
8566
|
var HOST_RELAY_SUBCOMMANDS = new Set(["consume", "heartbeat", "list", "register"]);
|
|
8520
8567
|
async function runHostCommandFromCli(client, argv, config, io) {
|
|
8521
8568
|
const sub = argv[0] ?? "list";
|
|
@@ -8753,7 +8800,7 @@ function optionalDisplayName(value) {
|
|
|
8753
8800
|
const trimmed = value?.trim();
|
|
8754
8801
|
return trimmed || undefined;
|
|
8755
8802
|
}
|
|
8756
|
-
// .dist-releases/
|
|
8803
|
+
// .dist-releases/1784894032002-2676534/src/host/service.js
|
|
8757
8804
|
import { execFile as execFileCb } from "node:child_process";
|
|
8758
8805
|
import { createHash as createHash4 } from "node:crypto";
|
|
8759
8806
|
import { constants as fsConstants2 } from "node:fs";
|
|
@@ -8779,6 +8826,18 @@ async function installHostService(options, deps = {}) {
|
|
|
8779
8826
|
return installSystemd(flags, runtime, paths, command);
|
|
8780
8827
|
return installLaunchd(flags, runtime, paths, command);
|
|
8781
8828
|
}
|
|
8829
|
+
async function restartRunningHostService(deps = {}) {
|
|
8830
|
+
const runtime = serviceRuntime(deps);
|
|
8831
|
+
if (runtime.platform !== "linux" && runtime.platform !== "darwin") {
|
|
8832
|
+
return { restarted: false, reason: "unsupported-platform" };
|
|
8833
|
+
}
|
|
8834
|
+
const paths = servicePaths(runtime, { dryRun: false });
|
|
8835
|
+
if (!await _isManagedHostService(runtime, paths))
|
|
8836
|
+
return { restarted: false, reason: "not-managed" };
|
|
8837
|
+
if (runtime.platform === "linux")
|
|
8838
|
+
return _restartRunningSystemd(runtime, paths);
|
|
8839
|
+
return _restartRunningLaunchd(runtime, paths);
|
|
8840
|
+
}
|
|
8782
8841
|
var UNSUPPORTED_SERVICE_PLATFORM = "host service supports Linux systemd and macOS launchd only";
|
|
8783
8842
|
async function runHostServiceCommandFromCli(argv, io, deps = {}) {
|
|
8784
8843
|
const action = argv[0] ?? "status";
|
|
@@ -8968,6 +9027,32 @@ async function statusLaunchd(runtime, paths, io) {
|
|
|
8968
9027
|
const installed = await fileExists(runtime, paths.file);
|
|
8969
9028
|
_writeServiceOutput(io, { status: active ? "active" : "inactive", installed, platform: "launchd", file: paths.file, linger: "not-applicable" });
|
|
8970
9029
|
}
|
|
9030
|
+
async function _restartRunningSystemd(runtime, paths) {
|
|
9031
|
+
const active = await optionalExec(runtime, "systemctl", ["--user", "is-active", paths.serviceName]);
|
|
9032
|
+
if (active?.stdout.trim() !== "active")
|
|
9033
|
+
return { restarted: false, reason: "not-running" };
|
|
9034
|
+
await runtime.execFile("systemctl", ["--user", "restart", paths.serviceName]);
|
|
9035
|
+
return { restarted: true, platform: "systemd", target: paths.serviceName };
|
|
9036
|
+
}
|
|
9037
|
+
async function _restartRunningLaunchd(runtime, paths) {
|
|
9038
|
+
const target = launchdServiceTarget(runtime.uid, paths.launchdLabel);
|
|
9039
|
+
if (!await optionalExec(runtime, "launchctl", ["print", target])) {
|
|
9040
|
+
return { restarted: false, reason: "not-running" };
|
|
9041
|
+
}
|
|
9042
|
+
await runtime.execFile("launchctl", ["kickstart", "-k", target]);
|
|
9043
|
+
return { restarted: true, platform: "launchd", target: paths.launchdLabel };
|
|
9044
|
+
}
|
|
9045
|
+
async function _isManagedHostService(runtime, paths) {
|
|
9046
|
+
try {
|
|
9047
|
+
const definition = await _readDefinition(runtime, paths.file);
|
|
9048
|
+
if (!definition)
|
|
9049
|
+
return false;
|
|
9050
|
+
const manager = runtime.platform === "linux" ? "systemd" : "launchd";
|
|
9051
|
+
return _isLegacyHostDefinition(definition, manager);
|
|
9052
|
+
} catch {
|
|
9053
|
+
return false;
|
|
9054
|
+
}
|
|
9055
|
+
}
|
|
8971
9056
|
function serviceRuntime(deps) {
|
|
8972
9057
|
const info = userInfo();
|
|
8973
9058
|
return {
|
|
@@ -9105,8 +9190,8 @@ function _isLegacyHostDefinition(text, manager) {
|
|
|
9105
9190
|
function _isSailorBridgeEntry(value) {
|
|
9106
9191
|
if (!value)
|
|
9107
9192
|
return false;
|
|
9108
|
-
const
|
|
9109
|
-
return
|
|
9193
|
+
const normalized = `/${value.replace(/\\/gu, "/").split("/").filter(Boolean).join("/")}`;
|
|
9194
|
+
return normalized.endsWith("/@sailorbridge/client/dist/cli.js") || normalized.endsWith("/@sailorbridge/client/dist/src/cli.js");
|
|
9110
9195
|
}
|
|
9111
9196
|
function _systemdExecStart(text) {
|
|
9112
9197
|
const values = [...text.matchAll(/^\s*ExecStart\s*=(.*)$/gmu)].map((match) => match[1]?.trim() ?? "").filter(Boolean);
|
|
@@ -9236,7 +9321,7 @@ function writeServiceLog(io, event, fields) {
|
|
|
9236
9321
|
io.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...fields ?? {} })}
|
|
9237
9322
|
`);
|
|
9238
9323
|
}
|
|
9239
|
-
// .dist-releases/
|
|
9324
|
+
// .dist-releases/1784894032002-2676534/src/host/git-remote.js
|
|
9240
9325
|
var GIT_SECRET_PATH_MARKERS = [
|
|
9241
9326
|
"token",
|
|
9242
9327
|
"credential",
|
|
@@ -9328,7 +9413,7 @@ function looksLikeCredential(segment2) {
|
|
|
9328
9413
|
return value.length >= 32 && /^[A-Za-z0-9_-]+$/u.test(value);
|
|
9329
9414
|
}
|
|
9330
9415
|
|
|
9331
|
-
// .dist-releases/
|
|
9416
|
+
// .dist-releases/1784894032002-2676534/src/host/start-spec.js
|
|
9332
9417
|
var TRANSPORTS = ["auto", "sdk", "pty", "tmux", "headless"];
|
|
9333
9418
|
var SUPPORTED_PHASE1_ENGINES = ["claude", "codex"];
|
|
9334
9419
|
var ENGINE_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/u;
|
|
@@ -9578,7 +9663,7 @@ function _trimmed(value, label) {
|
|
|
9578
9663
|
throw new Error(`${label} must not be empty`);
|
|
9579
9664
|
return trimmed;
|
|
9580
9665
|
}
|
|
9581
|
-
// .dist-releases/
|
|
9666
|
+
// .dist-releases/1784894032002-2676534/src/host/workspace.js
|
|
9582
9667
|
import { spawn as spawn4 } from "node:child_process";
|
|
9583
9668
|
import { existsSync as existsSync4 } from "node:fs";
|
|
9584
9669
|
import { mkdir as mkdir5, readdir as readdir2, rename as rename4, stat } from "node:fs/promises";
|
|
@@ -9809,7 +9894,7 @@ function _safeSegment(value) {
|
|
|
9809
9894
|
throw new Error("workspace path segment is empty after sanitizing");
|
|
9810
9895
|
return segment2;
|
|
9811
9896
|
}
|
|
9812
|
-
// .dist-releases/
|
|
9897
|
+
// .dist-releases/1784894032002-2676534/src/host/workspace-inventory.js
|
|
9813
9898
|
import { access as access2 } from "node:fs/promises";
|
|
9814
9899
|
import { constants } from "node:fs";
|
|
9815
9900
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -9901,25 +9986,179 @@ async function canAccess(path12, mode, deps) {
|
|
|
9901
9986
|
return false;
|
|
9902
9987
|
}
|
|
9903
9988
|
}
|
|
9904
|
-
// .dist-releases/
|
|
9905
|
-
import {
|
|
9906
|
-
import {
|
|
9989
|
+
// .dist-releases/1784894032002-2676534/src/upgrade.js
|
|
9990
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
9991
|
+
import { execFile as execFileCb2 } from "node:child_process";
|
|
9992
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
9907
9993
|
import path12 from "node:path";
|
|
9994
|
+
import { promisify as promisify4 } from "node:util";
|
|
9995
|
+
var execFileDefault2 = promisify4(execFileCb2);
|
|
9996
|
+
var CLIENT_PACKAGE = "@sailorbridge/client";
|
|
9997
|
+
var LATEST_PACKAGE = `${CLIENT_PACKAGE}@latest`;
|
|
9998
|
+
async function runUpgradeFromCli(argv, io, deps = {}) {
|
|
9999
|
+
if (argv.length)
|
|
10000
|
+
throw new Error(`upgrade takes no arguments: ${argv.join(" ")}`);
|
|
10001
|
+
const before = deps.currentVersion ?? CLIENT_VERSION;
|
|
10002
|
+
await (deps.installLatest ?? installLatestClientPackage)();
|
|
10003
|
+
const after = await (deps.readInstalledVersion ?? readInstalledClientVersion)();
|
|
10004
|
+
if (before === after) {
|
|
10005
|
+
io.stdout.write(`SailorBridge client is already up to date (${after}).
|
|
10006
|
+
`);
|
|
10007
|
+
return;
|
|
10008
|
+
}
|
|
10009
|
+
const service = await (deps.restartHostService ?? restartRunningHostService)();
|
|
10010
|
+
_writeUpgradeResult(io.stdout, before, after, service);
|
|
10011
|
+
}
|
|
10012
|
+
function installLatestClientPackage() {
|
|
10013
|
+
return new Promise((resolve2, reject) => {
|
|
10014
|
+
const child = spawn5("npm", ["i", "-g", LATEST_PACKAGE], { stdio: "inherit" });
|
|
10015
|
+
child.once("error", reject);
|
|
10016
|
+
child.once("close", (code, signal) => {
|
|
10017
|
+
if (code === 0)
|
|
10018
|
+
resolve2();
|
|
10019
|
+
else
|
|
10020
|
+
reject(new Error(`npm i -g ${LATEST_PACKAGE} failed (${signal ?? `exit ${code ?? "unknown"}`})`));
|
|
10021
|
+
});
|
|
10022
|
+
});
|
|
10023
|
+
}
|
|
10024
|
+
async function readInstalledClientVersion() {
|
|
10025
|
+
const { stdout } = await execFileDefault2("npm", ["root", "-g"]);
|
|
10026
|
+
const root = String(stdout).trim();
|
|
10027
|
+
if (!root)
|
|
10028
|
+
throw new Error("npm root -g returned an empty path after upgrade");
|
|
10029
|
+
const packageJson = path12.join(root, "@sailorbridge", "client", "package.json");
|
|
10030
|
+
const parsed = JSON.parse(await readFile5(packageJson, "utf8"));
|
|
10031
|
+
const version = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : undefined;
|
|
10032
|
+
if (typeof version !== "string" || !version.trim()) {
|
|
10033
|
+
throw new Error(`${packageJson} has no valid version after upgrade`);
|
|
10034
|
+
}
|
|
10035
|
+
return version.trim();
|
|
10036
|
+
}
|
|
10037
|
+
function _writeUpgradeResult(output, before, after, service) {
|
|
10038
|
+
if (service.restarted) {
|
|
10039
|
+
output.write(`Updated SailorBridge client from ${before} to ${after} and restarted ${service.target} (${service.platform}).
|
|
10040
|
+
`);
|
|
10041
|
+
return;
|
|
10042
|
+
}
|
|
10043
|
+
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}.
|
|
10044
|
+
`);
|
|
10045
|
+
}
|
|
10046
|
+
|
|
10047
|
+
// .dist-releases/1784894032002-2676534/src/host/auto-update.js
|
|
10048
|
+
var AUTO_UPDATE_HARD_DEADLINE_MS = 24 * 60 * 60 * 1000;
|
|
10049
|
+
var AUTO_UPDATE_ATTEMPT_COOLDOWN_MS = 60 * 1000;
|
|
10050
|
+
var MAX_UPDATE_ERROR_LENGTH = 500;
|
|
10051
|
+
function createAutoUpdater(options) {
|
|
10052
|
+
const logger = options.logger ?? (() => {});
|
|
10053
|
+
const deps = options.deps ?? {};
|
|
10054
|
+
const now = deps.now ?? (() => Date.now());
|
|
10055
|
+
const currentVersion = deps.currentVersion ?? CLIENT_VERSION;
|
|
10056
|
+
const installLatest = deps.installLatest ?? installLatestClientPackage;
|
|
10057
|
+
const readInstalledVersion = deps.readInstalledVersion ?? readInstalledClientVersion;
|
|
10058
|
+
const restartHostService = deps.restartHostService ?? restartRunningHostService;
|
|
10059
|
+
let pendingSince;
|
|
10060
|
+
let lastAttemptAt;
|
|
10061
|
+
let updateError;
|
|
10062
|
+
let installedVersion;
|
|
10063
|
+
const _attempt = async (reason, target) => {
|
|
10064
|
+
if (lastAttemptAt !== undefined && now() - lastAttemptAt < AUTO_UPDATE_ATTEMPT_COOLDOWN_MS)
|
|
10065
|
+
return;
|
|
10066
|
+
lastAttemptAt = now();
|
|
10067
|
+
logger("host_auto_update_start", { reason, target, current: currentVersion });
|
|
10068
|
+
try {
|
|
10069
|
+
await installLatest();
|
|
10070
|
+
const after = await readInstalledVersion();
|
|
10071
|
+
updateError = undefined;
|
|
10072
|
+
installedVersion = after;
|
|
10073
|
+
if (after === currentVersion) {
|
|
10074
|
+
logger("host_auto_update_noop", { reason, target, installed: after });
|
|
10075
|
+
return;
|
|
10076
|
+
}
|
|
10077
|
+
const report = await restartHostService();
|
|
10078
|
+
if (report.restarted) {
|
|
10079
|
+
logger("host_auto_update_restarting", { from: currentVersion, to: after, platform: report.platform, target: report.target });
|
|
10080
|
+
} else {
|
|
10081
|
+
logger("host_auto_update_installed_pending_restart", { from: currentVersion, to: after, reason: report.reason });
|
|
10082
|
+
}
|
|
10083
|
+
} catch (error) {
|
|
10084
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10085
|
+
updateError = message.length <= MAX_UPDATE_ERROR_LENGTH ? message : `${message.slice(0, MAX_UPDATE_ERROR_LENGTH - 3)}...`;
|
|
10086
|
+
logger("host_auto_update_error", { reason, target, error: updateError });
|
|
10087
|
+
}
|
|
10088
|
+
};
|
|
10089
|
+
return {
|
|
10090
|
+
async onHostReport(host) {
|
|
10091
|
+
if (!options.enabled)
|
|
10092
|
+
return;
|
|
10093
|
+
const latest = host.latest_client_version;
|
|
10094
|
+
if (!latest || compareReleaseSemver(latest, currentVersion) !== 1) {
|
|
10095
|
+
pendingSince = undefined;
|
|
10096
|
+
return;
|
|
10097
|
+
}
|
|
10098
|
+
const installedCmp = installedVersion === undefined ? null : compareReleaseSemver(installedVersion, latest);
|
|
10099
|
+
if (installedCmp === 0 || installedCmp === 1)
|
|
10100
|
+
return;
|
|
10101
|
+
if (pendingSince === undefined) {
|
|
10102
|
+
pendingSince = now();
|
|
10103
|
+
logger("host_auto_update_pending", { latest, current: currentVersion });
|
|
10104
|
+
}
|
|
10105
|
+
const forced = now() - pendingSince >= AUTO_UPDATE_HARD_DEADLINE_MS;
|
|
10106
|
+
if (options.isActive() && !forced)
|
|
10107
|
+
return;
|
|
10108
|
+
if (options.isMidWork())
|
|
10109
|
+
return;
|
|
10110
|
+
await _attempt(forced ? "deadline-forced" : "scheduled", latest);
|
|
10111
|
+
},
|
|
10112
|
+
async onUpgradeRequired(detail) {
|
|
10113
|
+
if (!options.enabled) {
|
|
10114
|
+
logger("host_auto_update_disabled_upgrade_required", { detail });
|
|
10115
|
+
return;
|
|
10116
|
+
}
|
|
10117
|
+
if (options.isMidWork())
|
|
10118
|
+
return;
|
|
10119
|
+
await _attempt("relay-rejected", null);
|
|
10120
|
+
},
|
|
10121
|
+
pendingUpdateError() {
|
|
10122
|
+
return updateError;
|
|
10123
|
+
}
|
|
10124
|
+
};
|
|
10125
|
+
}
|
|
10126
|
+
function compareReleaseSemver(a, b) {
|
|
10127
|
+
const left = _releaseTuple(a);
|
|
10128
|
+
const right = _releaseTuple(b);
|
|
10129
|
+
if (left === null || right === null)
|
|
10130
|
+
return null;
|
|
10131
|
+
for (let i = 0;i < 3; i += 1) {
|
|
10132
|
+
if (left[i] !== right[i])
|
|
10133
|
+
return left[i] < right[i] ? -1 : 1;
|
|
10134
|
+
}
|
|
10135
|
+
return 0;
|
|
10136
|
+
}
|
|
10137
|
+
function _releaseTuple(value) {
|
|
10138
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(value);
|
|
10139
|
+
if (!match)
|
|
10140
|
+
return null;
|
|
10141
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
10142
|
+
}
|
|
10143
|
+
// .dist-releases/1784894032002-2676534/src/host/host-config.js
|
|
10144
|
+
import { chmod as chmod3, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
|
|
10145
|
+
import { homedir as homedir10 } from "node:os";
|
|
10146
|
+
import path13 from "node:path";
|
|
9908
10147
|
var HOST_CONFIG_KIND = "sailorbridge-host-config";
|
|
9909
10148
|
function defaultHostConfigPath() {
|
|
9910
|
-
return
|
|
10149
|
+
return path13.join(homedir10(), ".sailorbridge", "host-config.json");
|
|
9911
10150
|
}
|
|
9912
10151
|
function normalizeWorkspacePath(entry) {
|
|
9913
10152
|
const trimmed = entry.trim();
|
|
9914
10153
|
if (!trimmed)
|
|
9915
10154
|
throw new Error("workspace path must not be empty");
|
|
9916
10155
|
const expanded = trimmed === "~" ? homedir10() : trimmed.startsWith("~/") ? `${homedir10()}${trimmed.slice(1)}` : trimmed;
|
|
9917
|
-
return
|
|
10156
|
+
return path13.resolve(expanded);
|
|
9918
10157
|
}
|
|
9919
10158
|
async function loadHostConfig(configPath = defaultHostConfigPath()) {
|
|
9920
10159
|
let text;
|
|
9921
10160
|
try {
|
|
9922
|
-
text = await
|
|
10161
|
+
text = await readFile6(configPath, "utf8");
|
|
9923
10162
|
} catch (error) {
|
|
9924
10163
|
if (error.code === "ENOENT")
|
|
9925
10164
|
return { kind: HOST_CONFIG_KIND, workspaces: [] };
|
|
@@ -9928,8 +10167,8 @@ async function loadHostConfig(configPath = defaultHostConfigPath()) {
|
|
|
9928
10167
|
return _parseHostConfig(text, configPath);
|
|
9929
10168
|
}
|
|
9930
10169
|
async function saveHostConfig(configPath, config) {
|
|
9931
|
-
await mkdir6(
|
|
9932
|
-
await chmod3(
|
|
10170
|
+
await mkdir6(path13.dirname(configPath), { recursive: true, mode: 448 });
|
|
10171
|
+
await chmod3(path13.dirname(configPath), 448);
|
|
9933
10172
|
await writeFile6(configPath, `${JSON.stringify(config, null, 2)}
|
|
9934
10173
|
`, { mode: 384 });
|
|
9935
10174
|
await chmod3(configPath, 384);
|
|
@@ -9939,7 +10178,7 @@ async function addHostWorkspace(configPath, workspacePath) {
|
|
|
9939
10178
|
const config = await loadHostConfig(configPath);
|
|
9940
10179
|
if (config.workspaces.includes(normalized))
|
|
9941
10180
|
return { config, path: normalized, added: false };
|
|
9942
|
-
const next = {
|
|
10181
|
+
const next = { ...config, workspaces: [...config.workspaces, normalized] };
|
|
9943
10182
|
await saveHostConfig(configPath, next);
|
|
9944
10183
|
return { config: next, path: normalized, added: true };
|
|
9945
10184
|
}
|
|
@@ -9960,7 +10199,7 @@ async function removeHostWorkspace(configPath, workspacePath) {
|
|
|
9960
10199
|
const config = await loadHostConfig(configPath);
|
|
9961
10200
|
if (!config.workspaces.includes(normalized))
|
|
9962
10201
|
return { config, path: normalized, removed: false };
|
|
9963
|
-
const next = {
|
|
10202
|
+
const next = { ...config, workspaces: config.workspaces.filter((entry) => entry !== normalized) };
|
|
9964
10203
|
await saveHostConfig(configPath, next);
|
|
9965
10204
|
return { config: next, path: normalized, removed: true };
|
|
9966
10205
|
}
|
|
@@ -9984,9 +10223,16 @@ function _parseHostConfig(text, configPath) {
|
|
|
9984
10223
|
throw new Error(`host config workspaces[${index}] must be a non-empty string`);
|
|
9985
10224
|
return value.trim();
|
|
9986
10225
|
});
|
|
9987
|
-
|
|
10226
|
+
if (record.auto_update !== undefined && typeof record.auto_update !== "boolean") {
|
|
10227
|
+
throw new Error(`host config at ${configPath} auto_update must be a boolean`);
|
|
10228
|
+
}
|
|
10229
|
+
return {
|
|
10230
|
+
kind: HOST_CONFIG_KIND,
|
|
10231
|
+
workspaces,
|
|
10232
|
+
...record.auto_update === undefined ? {} : { auto_update: record.auto_update }
|
|
10233
|
+
};
|
|
9988
10234
|
}
|
|
9989
|
-
// .dist-releases/
|
|
10235
|
+
// .dist-releases/1784894032002-2676534/src/host/host-workspace-cli.js
|
|
9990
10236
|
async function runHostWorkspaceCommandFromCli(argv, io, deps = {}) {
|
|
9991
10237
|
const configPath = deps.configPath ?? defaultHostConfigPath();
|
|
9992
10238
|
const sub = argv[0] ?? "list";
|
|
@@ -10030,7 +10276,7 @@ function _renderAddPlain(result) {
|
|
|
10030
10276
|
return result.note ? `${status} ${result.path}
|
|
10031
10277
|
${result.note}` : `${status} ${result.path}`;
|
|
10032
10278
|
}
|
|
10033
|
-
// .dist-releases/
|
|
10279
|
+
// .dist-releases/1784894032002-2676534/src/host/agent-env.js
|
|
10034
10280
|
var RUNTIME_NATIVE_HOME_ENV = new Set(["CLAUDE_CONFIG_DIR", "CODEX_HOME"]);
|
|
10035
10281
|
function buildAgentEnv(options) {
|
|
10036
10282
|
const env = {};
|
|
@@ -10059,25 +10305,25 @@ function buildAgentEnv(options) {
|
|
|
10059
10305
|
}
|
|
10060
10306
|
return env;
|
|
10061
10307
|
}
|
|
10062
|
-
// .dist-releases/
|
|
10063
|
-
import
|
|
10308
|
+
// .dist-releases/1784894032002-2676534/src/host/runtime-env.js
|
|
10309
|
+
import path17 from "node:path";
|
|
10064
10310
|
|
|
10065
|
-
// .dist-releases/
|
|
10066
|
-
import { chmod as chmod4, mkdir as mkdir7, readFile as
|
|
10311
|
+
// .dist-releases/1784894032002-2676534/src/host/agent-defaults.js
|
|
10312
|
+
import { chmod as chmod4, mkdir as mkdir7, readFile as readFile8, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
|
|
10067
10313
|
import { homedir as homedir11 } from "node:os";
|
|
10068
|
-
import
|
|
10314
|
+
import path15 from "node:path";
|
|
10069
10315
|
|
|
10070
|
-
// .dist-releases/
|
|
10071
|
-
import { readFile as
|
|
10072
|
-
import
|
|
10316
|
+
// .dist-releases/1784894032002-2676534/src/host/claude-settings.js
|
|
10317
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
10318
|
+
import path14 from "node:path";
|
|
10073
10319
|
async function readHostClaudeSettings(claudeConfigDir) {
|
|
10074
|
-
const settings = await _readSettingsObject(
|
|
10320
|
+
const settings = await _readSettingsObject(path14.join(claudeConfigDir, "settings.json"));
|
|
10075
10321
|
return { env: _stringValues(settings.env), apiKeyHelper: _nonEmptyString(settings.apiKeyHelper) };
|
|
10076
10322
|
}
|
|
10077
10323
|
async function _readSettingsObject(filePath) {
|
|
10078
10324
|
let text;
|
|
10079
10325
|
try {
|
|
10080
|
-
text = await
|
|
10326
|
+
text = await readFile7(filePath, "utf8");
|
|
10081
10327
|
} catch (error) {
|
|
10082
10328
|
if (error.code === "ENOENT")
|
|
10083
10329
|
return {};
|
|
@@ -10108,7 +10354,7 @@ function _nonEmptyString(value) {
|
|
|
10108
10354
|
return typeof value === "string" && value.trim() ? value : null;
|
|
10109
10355
|
}
|
|
10110
10356
|
|
|
10111
|
-
// .dist-releases/
|
|
10357
|
+
// .dist-releases/1784894032002-2676534/src/host/agent-defaults.js
|
|
10112
10358
|
var CODEX_BLOCK_BEGIN = "# sailorbridge-terse-defaults:begin";
|
|
10113
10359
|
var CODEX_BLOCK_END = "# sailorbridge-terse-defaults:end";
|
|
10114
10360
|
var CODEX_MODEL_BLOCK_BEGIN = "# sailorbridge-host-model-settings:begin";
|
|
@@ -10760,23 +11006,23 @@ function _warnStatusLine(message) {
|
|
|
10760
11006
|
`);
|
|
10761
11007
|
}
|
|
10762
11008
|
function resolveClaudeStatusLineCommand(runtime = { execPath: process.execPath, argv1: process.argv[1] }) {
|
|
10763
|
-
const name =
|
|
10764
|
-
const prefix = name === "node" || name === "bun" ? [
|
|
11009
|
+
const name = path15.basename(runtime.execPath);
|
|
11010
|
+
const prefix = name === "node" || name === "bun" ? [path15.resolve(runtime.execPath), _requireEntry(runtime.argv1)] : [path15.resolve(runtime.execPath)];
|
|
10765
11011
|
return [...prefix.map(_shellSingleQuote), "host", "statusline"].join(" ");
|
|
10766
11012
|
}
|
|
10767
11013
|
function _requireEntry(argv1) {
|
|
10768
11014
|
const entry = argv1?.trim();
|
|
10769
11015
|
if (!entry)
|
|
10770
11016
|
throw new Error("cannot resolve claude statusline command: client entry point is unknown");
|
|
10771
|
-
return
|
|
11017
|
+
return path15.resolve(entry);
|
|
10772
11018
|
}
|
|
10773
11019
|
async function _materializeClaudeDefaults(homeDir, workspaceDir, statusLineCommand, warn = _warnStatusLine, sourceHomeDir) {
|
|
10774
|
-
const settingsPath =
|
|
10775
|
-
const stylePath =
|
|
10776
|
-
const hooksDir =
|
|
10777
|
-
const hygienePath =
|
|
10778
|
-
const refsPath =
|
|
10779
|
-
await mkdir7(
|
|
11020
|
+
const settingsPath = path15.join(homeDir, "settings.json");
|
|
11021
|
+
const stylePath = path15.join(homeDir, "output-styles", "sailorbridge-terse.md");
|
|
11022
|
+
const hooksDir = path15.join(homeDir, "hooks");
|
|
11023
|
+
const hygienePath = path15.join(hooksDir, "tool-output-hygiene.py");
|
|
11024
|
+
const refsPath = path15.join(hooksDir, "stale-refs-guard.py");
|
|
11025
|
+
await mkdir7(path15.dirname(stylePath), { recursive: true });
|
|
10780
11026
|
await _writeManagedFile(stylePath, CLAUDE_TERSE_STYLE, false);
|
|
10781
11027
|
await mkdir7(hooksDir, { recursive: true });
|
|
10782
11028
|
await _writeManagedFile(hygienePath, TOOL_OUTPUT_HYGIENE, true);
|
|
@@ -10791,7 +11037,7 @@ async function _materializeClaudeDefaults(homeDir, workspaceDir, statusLineComma
|
|
|
10791
11037
|
{ matcher: "Bash", command: `python3 ${_shellSingleQuote(refsPath)}` }
|
|
10792
11038
|
]);
|
|
10793
11039
|
_ensureClaudeStatusLine(settings, statusLineCommand ?? resolveClaudeStatusLineCommand(), warn);
|
|
10794
|
-
await mkdir7(
|
|
11040
|
+
await mkdir7(path15.dirname(settingsPath), { recursive: true });
|
|
10795
11041
|
await writeFile7(settingsPath, JSON.stringify(settings, null, 2) + `
|
|
10796
11042
|
`, { mode: 384 });
|
|
10797
11043
|
if (workspaceDir)
|
|
@@ -10806,10 +11052,10 @@ async function _mergeHostClaudeEnv(settings, sourceHomeDir) {
|
|
|
10806
11052
|
settings.env = { ..._jsonObject(settings.env, "Claude settings env"), ...hostEnv };
|
|
10807
11053
|
}
|
|
10808
11054
|
async function _trustClaudeWorkspace(homeDir, workspaceDir) {
|
|
10809
|
-
const configPath =
|
|
11055
|
+
const configPath = path15.join(homeDir, ".claude.json");
|
|
10810
11056
|
const config = await _readJsonObject(configPath);
|
|
10811
11057
|
const projects = _jsonObject(config.projects, "Claude config projects");
|
|
10812
|
-
const projectPath =
|
|
11058
|
+
const projectPath = path15.resolve(_expandHome3(workspaceDir));
|
|
10813
11059
|
for (const trustedPath of await _claudeTrustRoots(projectPath)) {
|
|
10814
11060
|
const project = _jsonObject(projects[trustedPath], `Claude config project ${trustedPath}`);
|
|
10815
11061
|
project.hasTrustDialogAccepted = true;
|
|
@@ -10817,12 +11063,12 @@ async function _trustClaudeWorkspace(homeDir, workspaceDir) {
|
|
|
10817
11063
|
}
|
|
10818
11064
|
config.projects = projects;
|
|
10819
11065
|
config.hasCompletedOnboarding = true;
|
|
10820
|
-
await mkdir7(
|
|
11066
|
+
await mkdir7(path15.dirname(configPath), { recursive: true });
|
|
10821
11067
|
await writeFile7(configPath, JSON.stringify(config, null, 2) + `
|
|
10822
11068
|
`, { mode: 384 });
|
|
10823
11069
|
}
|
|
10824
11070
|
async function _claudeTrustRoots(projectPath) {
|
|
10825
|
-
const pointer =
|
|
11071
|
+
const pointer = path15.join(projectPath, ".git");
|
|
10826
11072
|
let stats;
|
|
10827
11073
|
try {
|
|
10828
11074
|
stats = await stat2(pointer);
|
|
@@ -10831,12 +11077,12 @@ async function _claudeTrustRoots(projectPath) {
|
|
|
10831
11077
|
}
|
|
10832
11078
|
if (!stats.isFile())
|
|
10833
11079
|
return [projectPath];
|
|
10834
|
-
const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await
|
|
11080
|
+
const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile8(pointer, "utf8"))?.[1];
|
|
10835
11081
|
if (!gitdir) {
|
|
10836
11082
|
throw new Error(`workspace ${projectPath} has an unreadable .git pointer — cannot pre-trust its main repository for claude`);
|
|
10837
11083
|
}
|
|
10838
|
-
const resolved =
|
|
10839
|
-
const marker = `${
|
|
11084
|
+
const resolved = path15.resolve(projectPath, gitdir);
|
|
11085
|
+
const marker = `${path15.sep}.git${path15.sep}worktrees${path15.sep}`;
|
|
10840
11086
|
const markerIndex = resolved.indexOf(marker);
|
|
10841
11087
|
if (markerIndex === -1)
|
|
10842
11088
|
return [projectPath];
|
|
@@ -10865,26 +11111,26 @@ function _claudeStatusLineShouldBeReplaced(command) {
|
|
|
10865
11111
|
if (CLAUDE_STATUSLINE_SUBCOMMAND_RE.test(trimmed))
|
|
10866
11112
|
return true;
|
|
10867
11113
|
const first = trimmed.split(/\s+/u)[0] ?? "";
|
|
10868
|
-
return SAIL_CLAUDE_STATUSLINE_BASENAMES.has(
|
|
11114
|
+
return SAIL_CLAUDE_STATUSLINE_BASENAMES.has(path15.basename(first));
|
|
10869
11115
|
}
|
|
10870
11116
|
function _claudeStatusLineConfig(command) {
|
|
10871
11117
|
return { type: "command", command, refreshInterval: 20 };
|
|
10872
11118
|
}
|
|
10873
11119
|
async function _materializeCodexDefaults(homeDir, workspaceDir, sourceHomeDir, warn = _warnStatusLine) {
|
|
10874
|
-
const configPath =
|
|
11120
|
+
const configPath = path15.join(homeDir, "config.toml");
|
|
10875
11121
|
let current = await _readTextIfExists2(configPath);
|
|
10876
11122
|
current = current.replace(new RegExp(`${_escapeRegExp(CODEX_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_BLOCK_END)}\\n?`, "u"), "");
|
|
10877
11123
|
current = current.replace(new RegExp(`${_escapeRegExp(CODEX_TRUST_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_TRUST_BLOCK_END)}\\n?`, "u"), "");
|
|
10878
11124
|
current = await _refreshCodexModelSettings(current, sourceHomeDir);
|
|
10879
11125
|
current = _removeTopLevelTomlKeys(current, CODEX_UPDATE_CHECK_KEYS);
|
|
10880
|
-
const trustedWorkspace = workspaceDir ?
|
|
11126
|
+
const trustedWorkspace = workspaceDir ? path15.resolve(_expandHome3(workspaceDir)) : undefined;
|
|
10881
11127
|
const trust = trustedWorkspace ? _setExistingCodexProjectTrust(current, trustedWorkspace) : { text: current, found: false };
|
|
10882
11128
|
current = trust.text;
|
|
10883
11129
|
const missing = Object.entries(CODEX_DEFAULTS).filter(([key]) => !_hasTopLevelTomlKey(current, key));
|
|
10884
11130
|
const defaultsBlock = _codexManagedBlock(missing);
|
|
10885
11131
|
const trustBlock = trustedWorkspace && !trust.found ? _codexTrustBlock(trustedWorkspace) : "";
|
|
10886
11132
|
const next = _ensureCodexStatusLine(_appendTomlBlock(_insertBeforeFirstTomlTable(current, defaultsBlock), trustBlock), warn);
|
|
10887
|
-
await mkdir7(
|
|
11133
|
+
await mkdir7(path15.dirname(configPath), { recursive: true });
|
|
10888
11134
|
await writeFile7(configPath, next, { mode: 384 });
|
|
10889
11135
|
}
|
|
10890
11136
|
function _ensureCodexStatusLine(text, warn) {
|
|
@@ -10934,7 +11180,7 @@ ${CODEX_CONTEXT_STATUS_LINE}`);
|
|
|
10934
11180
|
async function _refreshCodexModelSettings(current, sourceHomeDir) {
|
|
10935
11181
|
if (!sourceHomeDir)
|
|
10936
11182
|
return current;
|
|
10937
|
-
const source = await _readTextIfExists2(
|
|
11183
|
+
const source = await _readTextIfExists2(path15.join(sourceHomeDir, "config.toml"));
|
|
10938
11184
|
const entries = _codexModelEntries(source);
|
|
10939
11185
|
const withoutManaged = current.replace(new RegExp(`${_escapeRegExp(CODEX_MODEL_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_MODEL_BLOCK_END)}\\n?`, "u"), "");
|
|
10940
11186
|
const keys = new Set(entries.map(({ key }) => key));
|
|
@@ -11201,7 +11447,7 @@ async function _writeManagedFile(filePath, content, executable) {
|
|
|
11201
11447
|
if (existing && existing !== content && !existing.includes(MANAGED_MARKER2) && !_matchesMarkerlessManagedContent(existing, content)) {
|
|
11202
11448
|
throw new Error(`refusing to overwrite unmanaged file: ${filePath}`);
|
|
11203
11449
|
}
|
|
11204
|
-
await mkdir7(
|
|
11450
|
+
await mkdir7(path15.dirname(filePath), { recursive: true });
|
|
11205
11451
|
await writeFile7(filePath, content, { mode: executable ? 493 : 384 });
|
|
11206
11452
|
if (executable)
|
|
11207
11453
|
await chmod4(filePath, 493);
|
|
@@ -11217,13 +11463,13 @@ async function _readTextIfExists2(filePath) {
|
|
|
11217
11463
|
return "";
|
|
11218
11464
|
throw error;
|
|
11219
11465
|
}
|
|
11220
|
-
return
|
|
11466
|
+
return readFile8(filePath, "utf8");
|
|
11221
11467
|
}
|
|
11222
11468
|
function _expandHome3(value) {
|
|
11223
11469
|
if (value === "~")
|
|
11224
11470
|
return homedir11();
|
|
11225
11471
|
if (value.startsWith("~/"))
|
|
11226
|
-
return
|
|
11472
|
+
return path15.join(homedir11(), value.slice(2));
|
|
11227
11473
|
return value;
|
|
11228
11474
|
}
|
|
11229
11475
|
function _shellSingleQuote(value) {
|
|
@@ -11238,10 +11484,10 @@ function _escapeRegExp(value) {
|
|
|
11238
11484
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
11239
11485
|
}
|
|
11240
11486
|
|
|
11241
|
-
// .dist-releases/
|
|
11487
|
+
// .dist-releases/1784894032002-2676534/src/host/engine-auth.js
|
|
11242
11488
|
import { lstat as lstat4, mkdir as mkdir8, readlink, realpath, stat as stat3, symlink as symlink2, unlink as unlink2 } from "node:fs/promises";
|
|
11243
11489
|
import { homedir as homedir12 } from "node:os";
|
|
11244
|
-
import
|
|
11490
|
+
import path16 from "node:path";
|
|
11245
11491
|
class EngineNotLoggedInError extends Error {
|
|
11246
11492
|
constructor(engine, authPath) {
|
|
11247
11493
|
super(`engine-auth-missing[${engine}]: ${_notLoggedInMessage(engine, authPath)}`);
|
|
@@ -11256,9 +11502,9 @@ async function provisionEngineAuth(options) {
|
|
|
11256
11502
|
return null;
|
|
11257
11503
|
throw new EngineNotLoggedInError(options.engine, source);
|
|
11258
11504
|
}
|
|
11259
|
-
const target =
|
|
11505
|
+
const target = path16.join(options.homeDir, engineAuthFileName(options.engine));
|
|
11260
11506
|
const sharedTargetPath = await realpath(source);
|
|
11261
|
-
await mkdir8(
|
|
11507
|
+
await mkdir8(path16.dirname(target), { recursive: true });
|
|
11262
11508
|
await _ensureSymlink(target, source, sharedTargetPath);
|
|
11263
11509
|
return { linkPath: target, sharedTargetPath };
|
|
11264
11510
|
}
|
|
@@ -11267,7 +11513,7 @@ async function _hasEnvBasedAuth(engine, env) {
|
|
|
11267
11513
|
return _nonEmpty(env.OPENAI_API_KEY);
|
|
11268
11514
|
if (_nonEmpty(env.ANTHROPIC_AUTH_TOKEN) || _nonEmpty(env.ANTHROPIC_API_KEY))
|
|
11269
11515
|
return true;
|
|
11270
|
-
const settings = await readHostClaudeSettings(
|
|
11516
|
+
const settings = await readHostClaudeSettings(path16.dirname(engineAuthSourcePath("claude", env)));
|
|
11271
11517
|
return _nonEmpty(settings.env.ANTHROPIC_AUTH_TOKEN) || _nonEmpty(settings.env.ANTHROPIC_API_KEY) || settings.apiKeyHelper !== null;
|
|
11272
11518
|
}
|
|
11273
11519
|
function _nonEmpty(value) {
|
|
@@ -11277,12 +11523,12 @@ function _notLoggedInMessage(engine, authPath) {
|
|
|
11277
11523
|
if (engine === "codex") {
|
|
11278
11524
|
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
11525
|
}
|
|
11280
|
-
const settingsPath =
|
|
11526
|
+
const settingsPath = path16.join(path16.dirname(authPath), "settings.json");
|
|
11281
11527
|
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
11528
|
}
|
|
11283
11529
|
function engineAuthSourcePath(engine, env = process.env) {
|
|
11284
11530
|
const spec = _engineAuthSpec(engine, env);
|
|
11285
|
-
return
|
|
11531
|
+
return path16.join(spec.sourceDir, spec.fileName);
|
|
11286
11532
|
}
|
|
11287
11533
|
function engineAuthFileName(engine) {
|
|
11288
11534
|
return _engineAuthSpec(engine, {}).fileName;
|
|
@@ -11290,9 +11536,9 @@ function engineAuthFileName(engine) {
|
|
|
11290
11536
|
function _engineAuthSpec(engine, env) {
|
|
11291
11537
|
const home = env.HOME?.trim() || homedir12();
|
|
11292
11538
|
if (engine === "codex") {
|
|
11293
|
-
return { sourceDir: env.CODEX_HOME?.trim() ||
|
|
11539
|
+
return { sourceDir: env.CODEX_HOME?.trim() || path16.join(home, ".codex"), fileName: "auth.json" };
|
|
11294
11540
|
}
|
|
11295
|
-
return { sourceDir: env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
11541
|
+
return { sourceDir: env.CLAUDE_CONFIG_DIR?.trim() || path16.join(home, ".claude"), fileName: ".credentials.json" };
|
|
11296
11542
|
}
|
|
11297
11543
|
async function _isReadableAuthFile(filePath) {
|
|
11298
11544
|
try {
|
|
@@ -11321,14 +11567,14 @@ async function _ensureSymlink(linkPath, targetPath, sharedTargetPath) {
|
|
|
11321
11567
|
return;
|
|
11322
11568
|
}
|
|
11323
11569
|
const current = await readlink(linkPath);
|
|
11324
|
-
const resolved =
|
|
11325
|
-
if (resolved ===
|
|
11570
|
+
const resolved = path16.resolve(path16.dirname(linkPath), current);
|
|
11571
|
+
if (resolved === path16.resolve(targetPath))
|
|
11326
11572
|
return;
|
|
11327
11573
|
await unlink2(linkPath);
|
|
11328
11574
|
await symlink2(targetPath, linkPath);
|
|
11329
11575
|
}
|
|
11330
11576
|
|
|
11331
|
-
// .dist-releases/
|
|
11577
|
+
// .dist-releases/1784894032002-2676534/src/host/runtime-env.js
|
|
11332
11578
|
async function materializeAgentRuntimeEnv(options) {
|
|
11333
11579
|
const env = buildAgentEnv({
|
|
11334
11580
|
hostEnv: options.hostEnv,
|
|
@@ -11347,7 +11593,7 @@ async function materializeAgentRuntimeEnv(options) {
|
|
|
11347
11593
|
agentName: options.agentName,
|
|
11348
11594
|
engine: memoryStub.engine,
|
|
11349
11595
|
homeDir: memoryStub.homeDir,
|
|
11350
|
-
sourceHomeDir:
|
|
11596
|
+
sourceHomeDir: path17.dirname(engineAuthSourcePath(memoryStub.engine, options.hostEnv))
|
|
11351
11597
|
});
|
|
11352
11598
|
const credentialGuard = await provisionEngineAuth({
|
|
11353
11599
|
engine: memoryStub.engine,
|
|
@@ -11368,7 +11614,7 @@ function _ensureRootSandboxEscape(env, engine, uid) {
|
|
|
11368
11614
|
function _currentUid() {
|
|
11369
11615
|
return typeof process.getuid === "function" ? process.getuid() : null;
|
|
11370
11616
|
}
|
|
11371
|
-
// .dist-releases/
|
|
11617
|
+
// .dist-releases/1784894032002-2676534/src/host/context-statusline.js
|
|
11372
11618
|
function renderClaudeContextStatusLine(payload) {
|
|
11373
11619
|
const percent = _usedPercentage(payload);
|
|
11374
11620
|
if (percent === null)
|
|
@@ -11406,12 +11652,12 @@ function _bar(percent) {
|
|
|
11406
11652
|
const filled = Math.min(10, Math.max(0, Math.trunc(percent / 10)));
|
|
11407
11653
|
return "█".repeat(filled) + "░".repeat(10 - filled);
|
|
11408
11654
|
}
|
|
11409
|
-
// .dist-releases/
|
|
11655
|
+
// .dist-releases/1784894032002-2676534/src/host/scratch-sweeper.js
|
|
11410
11656
|
import { execFile as execFile3 } from "node:child_process";
|
|
11411
11657
|
import { lstat as lstat5, readdir as readdir3, realpath as realpath2, unlink as unlink3 } from "node:fs/promises";
|
|
11412
|
-
import
|
|
11413
|
-
import { promisify as
|
|
11414
|
-
var execFileAsync3 =
|
|
11658
|
+
import path18 from "node:path";
|
|
11659
|
+
import { promisify as promisify5 } from "node:util";
|
|
11660
|
+
var execFileAsync3 = promisify5(execFile3);
|
|
11415
11661
|
var SCRATCH_SUBDIR = ".agent/tmp";
|
|
11416
11662
|
var SCRATCH_SWEEP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
11417
11663
|
var SCRATCH_SWEEP_MAX_BYTES = 512 * 1024 * 1024;
|
|
@@ -11423,8 +11669,8 @@ function scratchSweepEnabled(env) {
|
|
|
11423
11669
|
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
11424
11670
|
}
|
|
11425
11671
|
async function sweepWorkspaceScratch(options) {
|
|
11426
|
-
const root =
|
|
11427
|
-
const tmpDir =
|
|
11672
|
+
const root = path18.resolve(options.workspaceRoot);
|
|
11673
|
+
const tmpDir = path18.join(root, SCRATCH_SUBDIR);
|
|
11428
11674
|
const state = await _tmpDirState(root, tmpDir);
|
|
11429
11675
|
if (state === "missing")
|
|
11430
11676
|
return EMPTY;
|
|
@@ -11462,7 +11708,7 @@ async function _trackedScratchFiles(root, options) {
|
|
|
11462
11708
|
const tracked = new Set;
|
|
11463
11709
|
for (const rel of stdout.split("\x00"))
|
|
11464
11710
|
if (rel)
|
|
11465
|
-
tracked.add(
|
|
11711
|
+
tracked.add(path18.join(root, rel));
|
|
11466
11712
|
return tracked;
|
|
11467
11713
|
} catch (error) {
|
|
11468
11714
|
options.logger?.("host_scratch_sweep_git_failed", { workspace: root, error: _errno(error) });
|
|
@@ -11472,7 +11718,7 @@ async function _trackedScratchFiles(root, options) {
|
|
|
11472
11718
|
async function _collectScratchFiles(dir, root, logger) {
|
|
11473
11719
|
const out = [];
|
|
11474
11720
|
for (const entry of await readdir3(dir, { withFileTypes: true })) {
|
|
11475
|
-
const abs =
|
|
11721
|
+
const abs = path18.join(dir, entry.name);
|
|
11476
11722
|
if (!_within(root, abs)) {
|
|
11477
11723
|
logger?.("host_scratch_sweep_out_of_bounds", { workspace: root, path: abs });
|
|
11478
11724
|
continue;
|
|
@@ -11538,8 +11784,8 @@ async function _runGit2(root, args) {
|
|
|
11538
11784
|
return String(stdout);
|
|
11539
11785
|
}
|
|
11540
11786
|
function _within(root, target) {
|
|
11541
|
-
const rel =
|
|
11542
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11787
|
+
const rel = path18.relative(root, target);
|
|
11788
|
+
return rel === "" || !rel.startsWith("..") && !path18.isAbsolute(rel);
|
|
11543
11789
|
}
|
|
11544
11790
|
function _isEnoent(error) {
|
|
11545
11791
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
@@ -11549,7 +11795,7 @@ function _errno(error) {
|
|
|
11549
11795
|
return String(error.code);
|
|
11550
11796
|
return error instanceof Error ? error.message : String(error);
|
|
11551
11797
|
}
|
|
11552
|
-
// .dist-releases/
|
|
11798
|
+
// .dist-releases/1784894032002-2676534/src/host/launcher.js
|
|
11553
11799
|
var MAX_TMUX_SESSION_SUFFIX = 100;
|
|
11554
11800
|
async function launchSupervisedAgent(input, deps = {}) {
|
|
11555
11801
|
const spec = parseAgentStartSpec(input.spec);
|
|
@@ -11700,10 +11946,10 @@ async function _tmuxSessionExists(session) {
|
|
|
11700
11946
|
throw error;
|
|
11701
11947
|
}
|
|
11702
11948
|
}
|
|
11703
|
-
// .dist-releases/
|
|
11949
|
+
// .dist-releases/1784894032002-2676534/src/worker/capability.js
|
|
11704
11950
|
import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
11705
11951
|
import os from "node:os";
|
|
11706
|
-
import
|
|
11952
|
+
import path19 from "node:path";
|
|
11707
11953
|
import { spawnSync } from "node:child_process";
|
|
11708
11954
|
var TOOLS = ["podman", "git", "kubectl", "perf", "bpftrace"];
|
|
11709
11955
|
var GPU_TAGS = new Set(["intel-b60", "nvidia-h100", "none"]);
|
|
@@ -11803,10 +12049,10 @@ function parseOsReleaseLine(line) {
|
|
|
11803
12049
|
}
|
|
11804
12050
|
function isReadonly(dir) {
|
|
11805
12051
|
try {
|
|
11806
|
-
const root =
|
|
12052
|
+
const root = path19.resolve(dir);
|
|
11807
12053
|
mkdirSync3(root, { recursive: true });
|
|
11808
|
-
const temp = mkdtempSync(
|
|
11809
|
-
writeFileSync2(
|
|
12054
|
+
const temp = mkdtempSync(path19.join(root, ".sailorbridge-write-test-"));
|
|
12055
|
+
writeFileSync2(path19.join(temp, "probe"), "");
|
|
11810
12056
|
rmSync(temp, { recursive: true, force: true });
|
|
11811
12057
|
return false;
|
|
11812
12058
|
} catch {
|
|
@@ -11831,10 +12077,10 @@ function commandExists(command) {
|
|
|
11831
12077
|
return commandPath(command) !== null;
|
|
11832
12078
|
}
|
|
11833
12079
|
function commandPath(command) {
|
|
11834
|
-
if (command.includes(
|
|
12080
|
+
if (command.includes(path19.sep))
|
|
11835
12081
|
return command;
|
|
11836
|
-
for (const entry of (process.env.PATH ?? "").split(
|
|
11837
|
-
const candidate =
|
|
12082
|
+
for (const entry of (process.env.PATH ?? "").split(path19.delimiter)) {
|
|
12083
|
+
const candidate = path19.join(entry, command);
|
|
11838
12084
|
try {
|
|
11839
12085
|
accessSync(candidate, constants2.X_OK);
|
|
11840
12086
|
return candidate;
|
|
@@ -11843,7 +12089,7 @@ function commandPath(command) {
|
|
|
11843
12089
|
return null;
|
|
11844
12090
|
}
|
|
11845
12091
|
|
|
11846
|
-
// .dist-releases/
|
|
12092
|
+
// .dist-releases/1784894032002-2676534/src/host/worker-start-spec.js
|
|
11847
12093
|
var MAX_TAGS = 32;
|
|
11848
12094
|
var TAG_PATTERN = /^[a-z0-9][a-z0-9=._:/-]{0,63}$/iu;
|
|
11849
12095
|
var RESERVED_TAG_KEYS = new Set(["os", "kernel", "arch", "gpu", "mode", "role", "tool"]);
|
|
@@ -11903,16 +12149,16 @@ function _trimmed2(value, label) {
|
|
|
11903
12149
|
return trimmed;
|
|
11904
12150
|
}
|
|
11905
12151
|
|
|
11906
|
-
// .dist-releases/
|
|
12152
|
+
// .dist-releases/1784894032002-2676534/src/host/worker-launcher.js
|
|
11907
12153
|
import { statSync as statSync3 } from "node:fs";
|
|
11908
12154
|
import os4 from "node:os";
|
|
11909
|
-
import
|
|
11910
|
-
// .dist-releases/
|
|
12155
|
+
import path22 from "node:path";
|
|
12156
|
+
// .dist-releases/1784894032002-2676534/src/worker/client.js
|
|
11911
12157
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
11912
12158
|
import os3 from "node:os";
|
|
11913
|
-
import
|
|
12159
|
+
import path21 from "node:path";
|
|
11914
12160
|
|
|
11915
|
-
// .dist-releases/
|
|
12161
|
+
// .dist-releases/1784894032002-2676534/src/worker/errors.js
|
|
11916
12162
|
class LeaseFencingRejected extends Error {
|
|
11917
12163
|
status;
|
|
11918
12164
|
constructor(status, message) {
|
|
@@ -11926,10 +12172,10 @@ class WorkerHttpError extends Error {
|
|
|
11926
12172
|
method;
|
|
11927
12173
|
path;
|
|
11928
12174
|
status;
|
|
11929
|
-
constructor(method,
|
|
12175
|
+
constructor(method, path20, status, message) {
|
|
11930
12176
|
super(message || `HTTP ${status}`);
|
|
11931
12177
|
this.method = method;
|
|
11932
|
-
this.path =
|
|
12178
|
+
this.path = path20;
|
|
11933
12179
|
this.status = status;
|
|
11934
12180
|
this.name = "WorkerHttpError";
|
|
11935
12181
|
}
|
|
@@ -11938,10 +12184,10 @@ class WorkerHttpError extends Error {
|
|
|
11938
12184
|
class WorkerNetworkError extends Error {
|
|
11939
12185
|
method;
|
|
11940
12186
|
path;
|
|
11941
|
-
constructor(method,
|
|
12187
|
+
constructor(method, path20, message) {
|
|
11942
12188
|
super(message);
|
|
11943
12189
|
this.method = method;
|
|
11944
|
-
this.path =
|
|
12190
|
+
this.path = path20;
|
|
11945
12191
|
this.name = "WorkerNetworkError";
|
|
11946
12192
|
}
|
|
11947
12193
|
}
|
|
@@ -11949,14 +12195,14 @@ var LEASE_FENCING_BAD_REQUEST_MESSAGES = [
|
|
|
11949
12195
|
"lease_token is required",
|
|
11950
12196
|
"lease_token must be a non-empty string"
|
|
11951
12197
|
];
|
|
11952
|
-
function raiseForLeaseFencingOrStatus(method,
|
|
12198
|
+
function raiseForLeaseFencingOrStatus(method, path20, status, body) {
|
|
11953
12199
|
if (status === 409)
|
|
11954
12200
|
throw new LeaseFencingRejected(status, body);
|
|
11955
12201
|
if (status === 400 && LEASE_FENCING_BAD_REQUEST_MESSAGES.some((message) => body.includes(message))) {
|
|
11956
12202
|
throw new LeaseFencingRejected(status, body);
|
|
11957
12203
|
}
|
|
11958
12204
|
if (status >= 300)
|
|
11959
|
-
throw new WorkerHttpError(method,
|
|
12205
|
+
throw new WorkerHttpError(method, path20, status, body);
|
|
11960
12206
|
}
|
|
11961
12207
|
function isNetworkError(error) {
|
|
11962
12208
|
if (error instanceof WorkerNetworkError)
|
|
@@ -11968,23 +12214,23 @@ function isNetworkError(error) {
|
|
|
11968
12214
|
const code = error.code;
|
|
11969
12215
|
return code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EPIPE" || error.name === "AbortError";
|
|
11970
12216
|
}
|
|
11971
|
-
function countsTowardWorkerSessionRebuild(
|
|
11972
|
-
if (
|
|
12217
|
+
function countsTowardWorkerSessionRebuild(path20) {
|
|
12218
|
+
if (path20 === "/api/v1/workers/register")
|
|
11973
12219
|
return false;
|
|
11974
|
-
if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(
|
|
12220
|
+
if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path20))
|
|
11975
12221
|
return false;
|
|
11976
12222
|
return true;
|
|
11977
12223
|
}
|
|
11978
12224
|
|
|
11979
|
-
// .dist-releases/
|
|
11980
|
-
import { spawn as
|
|
12225
|
+
// .dist-releases/1784894032002-2676534/src/worker/executor.js
|
|
12226
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
11981
12227
|
import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, realpathSync, rmSync as rmSync2, statfsSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
11982
12228
|
import { tmpdir as tmpdir2, loadavg } from "node:os";
|
|
11983
12229
|
import os2 from "node:os";
|
|
11984
|
-
import
|
|
12230
|
+
import path20 from "node:path";
|
|
11985
12231
|
import { TextDecoder } from "node:util";
|
|
11986
12232
|
|
|
11987
|
-
// .dist-releases/
|
|
12233
|
+
// .dist-releases/1784894032002-2676534/src/worker/async-queue.js
|
|
11988
12234
|
class AsyncByteQueue {
|
|
11989
12235
|
maxSize;
|
|
11990
12236
|
values = [];
|
|
@@ -12044,7 +12290,7 @@ class AsyncByteQueue {
|
|
|
12044
12290
|
}
|
|
12045
12291
|
}
|
|
12046
12292
|
|
|
12047
|
-
// .dist-releases/
|
|
12293
|
+
// .dist-releases/1784894032002-2676534/src/worker/http.js
|
|
12048
12294
|
import { request as httpRequest } from "node:http";
|
|
12049
12295
|
import { request as httpsRequest } from "node:https";
|
|
12050
12296
|
import { URLSearchParams as URLSearchParams2 } from "node:url";
|
|
@@ -12074,27 +12320,27 @@ class WorkerHttpClient {
|
|
|
12074
12320
|
throw new Error("token is required");
|
|
12075
12321
|
this.relayUrl = relayUrl.replace(/\/+$/u, "");
|
|
12076
12322
|
}
|
|
12077
|
-
async getJson(
|
|
12323
|
+
async getJson(path20, query, timeouts, options = {}) {
|
|
12078
12324
|
const suffix = new URLSearchParams2(query).toString();
|
|
12079
|
-
return this.requestJson("GET", suffix ? `${
|
|
12325
|
+
return this.requestJson("GET", suffix ? `${path20}?${suffix}` : path20, undefined, timeouts, options);
|
|
12080
12326
|
}
|
|
12081
|
-
async postJson(
|
|
12082
|
-
return this.requestJson("POST",
|
|
12327
|
+
async postJson(path20, value, timeouts) {
|
|
12328
|
+
return this.requestJson("POST", path20, { type: "json", value }, timeouts, {});
|
|
12083
12329
|
}
|
|
12084
|
-
async postBytes(
|
|
12085
|
-
const response = await this.request("POST",
|
|
12086
|
-
raiseForLeaseFencingOrStatus("POST",
|
|
12330
|
+
async postBytes(path20, body, headers, timeouts) {
|
|
12331
|
+
const response = await this.request("POST", path20, { type: "bytes", value: body }, timeouts, headers);
|
|
12332
|
+
raiseForLeaseFencingOrStatus("POST", path20, response.status, response.text);
|
|
12087
12333
|
}
|
|
12088
|
-
async requestJson(method,
|
|
12089
|
-
const response = await this.request(method,
|
|
12090
|
-
const errorPath = pathOnly(
|
|
12334
|
+
async requestJson(method, path20, body, timeouts, options) {
|
|
12335
|
+
const response = await this.request(method, path20, body, timeouts, {});
|
|
12336
|
+
const errorPath = pathOnly(path20);
|
|
12091
12337
|
if (options.leaseFencing === false && response.status >= 300)
|
|
12092
12338
|
throw new WorkerHttpError(method, errorPath, response.status, response.text);
|
|
12093
12339
|
raiseForLeaseFencingOrStatus(method, errorPath, response.status, response.text);
|
|
12094
12340
|
return parseJson(method, errorPath, response.text);
|
|
12095
12341
|
}
|
|
12096
|
-
async request(method,
|
|
12097
|
-
const url = new URL(`${this.relayUrl}${
|
|
12342
|
+
async request(method, path20, body, timeouts, extraHeaders) {
|
|
12343
|
+
const url = new URL(`${this.relayUrl}${path20}`);
|
|
12098
12344
|
const headers = this.headers(body, extraHeaders);
|
|
12099
12345
|
return sendRequest(method, url, body, headers, timeouts, this.signal);
|
|
12100
12346
|
}
|
|
@@ -12221,7 +12467,7 @@ function waitForRequestDrain(req) {
|
|
|
12221
12467
|
req.once("close", onClose);
|
|
12222
12468
|
});
|
|
12223
12469
|
}
|
|
12224
|
-
function parseJson(method,
|
|
12470
|
+
function parseJson(method, path20, text) {
|
|
12225
12471
|
try {
|
|
12226
12472
|
const parsed = JSON.parse(text);
|
|
12227
12473
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -12230,15 +12476,15 @@ function parseJson(method, path19, text) {
|
|
|
12230
12476
|
return parsed;
|
|
12231
12477
|
} catch (error) {
|
|
12232
12478
|
if (error instanceof SyntaxError)
|
|
12233
|
-
throw new WorkerNetworkError(method,
|
|
12479
|
+
throw new WorkerNetworkError(method, path20, "response JSON is invalid");
|
|
12234
12480
|
throw error;
|
|
12235
12481
|
}
|
|
12236
12482
|
}
|
|
12237
|
-
function pathOnly(
|
|
12238
|
-
return
|
|
12483
|
+
function pathOnly(path20) {
|
|
12484
|
+
return path20.split("?", 1)[0] || path20;
|
|
12239
12485
|
}
|
|
12240
12486
|
|
|
12241
|
-
// .dist-releases/
|
|
12487
|
+
// .dist-releases/1784894032002-2676534/src/worker/stream-upload.js
|
|
12242
12488
|
async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId) {
|
|
12243
12489
|
if (!leaseToken.trim())
|
|
12244
12490
|
throw new Error("lease_token is required");
|
|
@@ -12248,7 +12494,7 @@ async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId
|
|
|
12248
12494
|
await session.http.postBytes(`/api/v1/jobs/${encodeURIComponent(jobId)}/stream-upload?${query.toString()}`, chunks, { "X-Lease-Token": leaseToken }, UPLOAD_TIMEOUTS);
|
|
12249
12495
|
}
|
|
12250
12496
|
|
|
12251
|
-
// .dist-releases/
|
|
12497
|
+
// .dist-releases/1784894032002-2676534/src/worker/executor.js
|
|
12252
12498
|
class StreamingProcessFailure extends Error {
|
|
12253
12499
|
exitCode;
|
|
12254
12500
|
stdout;
|
|
@@ -12320,8 +12566,8 @@ function sandboxPath(workdir, requested) {
|
|
|
12320
12566
|
if (requested.startsWith("/") || parts.length === 0 || parts.includes("..")) {
|
|
12321
12567
|
throw new Error("path must be a relative path inside the worker sandbox");
|
|
12322
12568
|
}
|
|
12323
|
-
const root = realpathSync(
|
|
12324
|
-
const target =
|
|
12569
|
+
const root = realpathSync(path20.resolve(workdir));
|
|
12570
|
+
const target = path20.resolve(root, ...parts);
|
|
12325
12571
|
if (!isPathInside(root, target)) {
|
|
12326
12572
|
throw new Error("path escapes worker sandbox");
|
|
12327
12573
|
}
|
|
@@ -12461,7 +12707,7 @@ async function collectAndUpload(session, jobId, stream, pipe, leaseToken, worker
|
|
|
12461
12707
|
return collected.join("");
|
|
12462
12708
|
}
|
|
12463
12709
|
function spawnProcess(argv, cwd) {
|
|
12464
|
-
return
|
|
12710
|
+
return spawn6(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
|
|
12465
12711
|
}
|
|
12466
12712
|
async function terminateProcess(proc) {
|
|
12467
12713
|
if (proc.pid === undefined)
|
|
@@ -12497,9 +12743,9 @@ function writeFileJob(workdir, spec, startedAt, started) {
|
|
|
12497
12743
|
if (typeof spec.content !== "string")
|
|
12498
12744
|
throw new Error("write_file spec.content must be a string");
|
|
12499
12745
|
const target = sandboxPath(workdir, String(spec.path ?? ""));
|
|
12500
|
-
mkdirSync4(
|
|
12746
|
+
mkdirSync4(path20.dirname(target), { recursive: true });
|
|
12501
12747
|
writeFileSync3(target, spec.content, "utf8");
|
|
12502
|
-
return output(0,
|
|
12748
|
+
return output(0, path20.relative(realpathSync(path20.resolve(workdir)), target), "", startedAt, started);
|
|
12503
12749
|
}
|
|
12504
12750
|
function readUtf8File(target) {
|
|
12505
12751
|
return decodeUtf8(readFileSync4(target));
|
|
@@ -12507,8 +12753,8 @@ function readUtf8File(target) {
|
|
|
12507
12753
|
async function applyDiff(workdir, spec, signal) {
|
|
12508
12754
|
if (typeof spec.diff_unified_text !== "string")
|
|
12509
12755
|
throw new Error("apply_diff spec.diff_unified_text must be a string");
|
|
12510
|
-
const dir = mkdtempSync2(
|
|
12511
|
-
const patch =
|
|
12756
|
+
const dir = mkdtempSync2(path20.join(tmpdir2(), "sailorbridge-patch-"));
|
|
12757
|
+
const patch = path20.join(dir, "change.patch");
|
|
12512
12758
|
writeFileSync3(patch, spec.diff_unified_text, "utf8");
|
|
12513
12759
|
try {
|
|
12514
12760
|
return await runArgv(["patch", "-p1", "-i", patch], workdir, coerceJobTimeoutSeconds(spec) * 1000, signal);
|
|
@@ -12748,24 +12994,24 @@ function resolveExistingTarget(target) {
|
|
|
12748
12994
|
let current = target;
|
|
12749
12995
|
while (true) {
|
|
12750
12996
|
try {
|
|
12751
|
-
return
|
|
12997
|
+
return path20.resolve(realpathSync(current), ...missingParts.reverse());
|
|
12752
12998
|
} catch (error) {
|
|
12753
12999
|
if (error.code !== "ENOENT")
|
|
12754
13000
|
throw error;
|
|
12755
|
-
const parent =
|
|
13001
|
+
const parent = path20.dirname(current);
|
|
12756
13002
|
if (parent === current)
|
|
12757
13003
|
throw error;
|
|
12758
|
-
missingParts.push(
|
|
13004
|
+
missingParts.push(path20.basename(current));
|
|
12759
13005
|
current = parent;
|
|
12760
13006
|
}
|
|
12761
13007
|
}
|
|
12762
13008
|
}
|
|
12763
13009
|
function isPathInside(root, target) {
|
|
12764
|
-
const relative =
|
|
12765
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
13010
|
+
const relative = path20.relative(root, target);
|
|
13011
|
+
return relative === "" || !relative.startsWith("..") && !path20.isAbsolute(relative);
|
|
12766
13012
|
}
|
|
12767
13013
|
function prepareWorkdir(workdir) {
|
|
12768
|
-
const resolved =
|
|
13014
|
+
const resolved = path20.resolve(workdir);
|
|
12769
13015
|
mkdirSync4(resolved, { recursive: true });
|
|
12770
13016
|
return resolved;
|
|
12771
13017
|
}
|
|
@@ -12780,7 +13026,7 @@ function errorMessage6(error) {
|
|
|
12780
13026
|
return error instanceof Error ? error.message : String(error);
|
|
12781
13027
|
}
|
|
12782
13028
|
|
|
12783
|
-
// .dist-releases/
|
|
13029
|
+
// .dist-releases/1784894032002-2676534/src/worker/client.js
|
|
12784
13030
|
var TOKEN_FILES = [
|
|
12785
13031
|
"~/.config/sailorbridge/token",
|
|
12786
13032
|
"/etc/sailorbridge/token"
|
|
@@ -13119,7 +13365,7 @@ ${JSON.stringify(result)}\r
|
|
|
13119
13365
|
`);
|
|
13120
13366
|
for (const file of attachments) {
|
|
13121
13367
|
yield Buffer.from(`--${multipartBoundary}\r
|
|
13122
|
-
Content-Disposition: form-data; name="files"; filename="${
|
|
13368
|
+
Content-Disposition: form-data; name="files"; filename="${path21.basename(file)}"\r
|
|
13123
13369
|
\r
|
|
13124
13370
|
`);
|
|
13125
13371
|
yield readFileSync5(file);
|
|
@@ -13153,7 +13399,7 @@ function parseFlags2(argv) {
|
|
|
13153
13399
|
return values;
|
|
13154
13400
|
}
|
|
13155
13401
|
function expandHome2(value) {
|
|
13156
|
-
return value === "~" || value.startsWith("~/") ?
|
|
13402
|
+
return value === "~" || value.startsWith("~/") ? path21.join(os3.homedir(), value.slice(2)) : value;
|
|
13157
13403
|
}
|
|
13158
13404
|
function _sleep(ms, signal) {
|
|
13159
13405
|
if (signal?.aborted)
|
|
@@ -13177,7 +13423,7 @@ function errorName2(error) {
|
|
|
13177
13423
|
function errorMessage7(error) {
|
|
13178
13424
|
return error instanceof Error ? error.message : String(error);
|
|
13179
13425
|
}
|
|
13180
|
-
// .dist-releases/
|
|
13426
|
+
// .dist-releases/1784894032002-2676534/src/host/worker-launcher.js
|
|
13181
13427
|
var DEFAULT_WORKER_READY_TIMEOUT_MS = 60000;
|
|
13182
13428
|
async function launchSupervisedWorker(input, deps = {}) {
|
|
13183
13429
|
const logger = input.logger ?? (() => {});
|
|
@@ -13260,7 +13506,7 @@ function _requireWorkerName(descriptor) {
|
|
|
13260
13506
|
return worker;
|
|
13261
13507
|
}
|
|
13262
13508
|
function _resolveWorkdir(workdir, homeDir) {
|
|
13263
|
-
return workdir === "~" || workdir.startsWith("~/") ?
|
|
13509
|
+
return workdir === "~" || workdir.startsWith("~/") ? path22.join(homeDir, workdir.slice(2)) : workdir;
|
|
13264
13510
|
}
|
|
13265
13511
|
function _assertWorkdir(workdir) {
|
|
13266
13512
|
let stat4;
|
|
@@ -13274,7 +13520,7 @@ function _assertWorkdir(workdir) {
|
|
|
13274
13520
|
return workdir;
|
|
13275
13521
|
}
|
|
13276
13522
|
|
|
13277
|
-
// .dist-releases/
|
|
13523
|
+
// .dist-releases/1784894032002-2676534/src/host/daemon.js
|
|
13278
13524
|
async function runHostDaemon(options) {
|
|
13279
13525
|
const logger = options.logger ?? (() => {});
|
|
13280
13526
|
const sleep3 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
@@ -13302,6 +13548,13 @@ async function runHostDaemon(options) {
|
|
|
13302
13548
|
let lastRepoTipReportAt = now() - repoTipReportIntervalMs;
|
|
13303
13549
|
const reconcileIntervalMs = options.reconcileIntervalMs ?? 5 * 60 * 1000;
|
|
13304
13550
|
let lastReconcileAt = now() - reconcileIntervalMs;
|
|
13551
|
+
const autoUpdater = createAutoUpdater({
|
|
13552
|
+
enabled: options.autoUpdate !== false,
|
|
13553
|
+
isActive: () => runtimes.size > 0 || workerRuntimes.size > 0 || startLocks.size > 0 || cleanupLocks.size > 0,
|
|
13554
|
+
isMidWork: () => startLocks.size > 0 || cleanupLocks.size > 0,
|
|
13555
|
+
logger,
|
|
13556
|
+
deps: { now, ...options.autoUpdateDeps }
|
|
13557
|
+
});
|
|
13305
13558
|
const _startWorker = async (request, recoveredDescriptor) => {
|
|
13306
13559
|
const spec = parseWorkerStartSpec(request.spec);
|
|
13307
13560
|
const descriptor = recoveredDescriptor ?? await options.resolveCredential(request);
|
|
@@ -13452,7 +13705,7 @@ async function runHostDaemon(options) {
|
|
|
13452
13705
|
try {
|
|
13453
13706
|
await _recoverDurableRuntimes();
|
|
13454
13707
|
while (!options.signal?.aborted) {
|
|
13455
|
-
const drained = await _drainOnce(options, identity, starter, logger, redactSecrets);
|
|
13708
|
+
const drained = await _drainOnce(options, identity, starter, logger, redactSecrets, autoUpdater);
|
|
13456
13709
|
if (drained === "retired") {
|
|
13457
13710
|
logger("host_daemon_retired", { host_id: options.hostId, status: 401 });
|
|
13458
13711
|
break;
|
|
@@ -13588,7 +13841,7 @@ async function _loadHostConfigBestEffort(options, logger) {
|
|
|
13588
13841
|
return { kind: "sailorbridge-host-config", workspaces: [] };
|
|
13589
13842
|
}
|
|
13590
13843
|
}
|
|
13591
|
-
async function _drainOnce(options, identity, starter, logger, redactSecrets) {
|
|
13844
|
+
async function _drainOnce(options, identity, starter, logger, redactSecrets, autoUpdater) {
|
|
13592
13845
|
try {
|
|
13593
13846
|
const hostConfig = await _loadHostConfigBestEffort(options, logger);
|
|
13594
13847
|
const workspaceInventory = await discoverWorkspaceInventoryBestEffort(hostWorkspacePaths(hostConfig, options.hostEnv ?? process.env), {
|
|
@@ -13597,17 +13850,20 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
|
|
|
13597
13850
|
error: error instanceof Error ? error.message : String(error)
|
|
13598
13851
|
})
|
|
13599
13852
|
});
|
|
13853
|
+
const updateError = autoUpdater.pendingUpdateError();
|
|
13600
13854
|
const result = await runHostRequestOnce({
|
|
13601
13855
|
relay: options.relay,
|
|
13602
13856
|
...identity,
|
|
13603
13857
|
starter,
|
|
13604
13858
|
redactSecrets,
|
|
13605
13859
|
...workspaceInventory.length ? { workspaceInventory } : {},
|
|
13860
|
+
...updateError === undefined ? {} : { updateError },
|
|
13606
13861
|
onWorkspaceInventoryError: (error) => logger("host_workspace_inventory_error", {
|
|
13607
13862
|
error: error instanceof Error ? error.message : String(error)
|
|
13608
13863
|
}),
|
|
13609
13864
|
...options.signal ? { signal: options.signal } : {}
|
|
13610
13865
|
});
|
|
13866
|
+
await autoUpdater.onHostReport(result.host);
|
|
13611
13867
|
if (result.status === "idle")
|
|
13612
13868
|
return false;
|
|
13613
13869
|
logger(`host_request_${result.status}`, {
|
|
@@ -13619,11 +13875,16 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
|
|
|
13619
13875
|
} catch (error) {
|
|
13620
13876
|
if (error instanceof RelayHttpError && error.status === 401)
|
|
13621
13877
|
return "retired";
|
|
13878
|
+
if (error instanceof RelayHttpError && error.status === 426) {
|
|
13879
|
+
logger("host_daemon_upgrade_required", { error: error.message });
|
|
13880
|
+
await autoUpdater.onUpgradeRequired(error.body);
|
|
13881
|
+
return false;
|
|
13882
|
+
}
|
|
13622
13883
|
logger("host_daemon_poll_error", { error: error instanceof Error ? error.message : String(error) });
|
|
13623
13884
|
return false;
|
|
13624
13885
|
}
|
|
13625
13886
|
}
|
|
13626
|
-
// .dist-releases/
|
|
13887
|
+
// .dist-releases/1784894032002-2676534/src/host/enrollment-redeem.js
|
|
13627
13888
|
var DEFAULT_NOT_FOUND_RETRIES = 3;
|
|
13628
13889
|
var NOT_FOUND_RETRY_DELAY_MS = 500;
|
|
13629
13890
|
function createEnrollmentRedeemer(options) {
|
|
@@ -13760,7 +14021,7 @@ function _role2(enrollment) {
|
|
|
13760
14021
|
}
|
|
13761
14022
|
return _string4(enrollment.roles[0], "roles[0]").trim();
|
|
13762
14023
|
}
|
|
13763
|
-
// .dist-releases/
|
|
14024
|
+
// .dist-releases/1784894032002-2676534/src/host/tutorial.js
|
|
13764
14025
|
function buildTutorialSteps(state) {
|
|
13765
14026
|
const paired = state.hasCredential;
|
|
13766
14027
|
return [
|
|
@@ -13831,7 +14092,7 @@ async function runTutorial(options) {
|
|
|
13831
14092
|
}
|
|
13832
14093
|
return steps;
|
|
13833
14094
|
}
|
|
13834
|
-
// .dist-releases/
|
|
14095
|
+
// .dist-releases/1784894032002-2676534/src/events.js
|
|
13835
14096
|
var EVENT_COMMANDS = new Set(["add", "list", "enable", "disable", "rm"]);
|
|
13836
14097
|
var EVENT_FLAGS = new Set(["at", "days", "message", "run-date", "session", "tag", "tz"]);
|
|
13837
14098
|
var PRODUCT_DAYS_MODES = new Set(["once", "daily", "weekday"]);
|
|
@@ -13921,9 +14182,9 @@ function _assertListArgs(args) {
|
|
|
13921
14182
|
}
|
|
13922
14183
|
}
|
|
13923
14184
|
|
|
13924
|
-
// .dist-releases/
|
|
13925
|
-
import { readFile as
|
|
13926
|
-
var _readUtf8File = async (file) => await
|
|
14185
|
+
// .dist-releases/1784894032002-2676534/src/text-input.js
|
|
14186
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
14187
|
+
var _readUtf8File = async (file) => await readFile9(file, "utf8");
|
|
13927
14188
|
async function readTextInput(input) {
|
|
13928
14189
|
let content = "";
|
|
13929
14190
|
for await (const chunk of input)
|
|
@@ -13955,17 +14216,17 @@ async function rejectUnusedTextInput(input, message) {
|
|
|
13955
14216
|
input.unref?.();
|
|
13956
14217
|
}
|
|
13957
14218
|
}
|
|
13958
|
-
async function readTextFile(
|
|
13959
|
-
return await readUtf8File2(
|
|
14219
|
+
async function readTextFile(path23, readUtf8File2 = _readUtf8File) {
|
|
14220
|
+
return await readUtf8File2(path23);
|
|
13960
14221
|
}
|
|
13961
|
-
async function readTextFileOrStdin(
|
|
13962
|
-
if (
|
|
14222
|
+
async function readTextFileOrStdin(path23, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
|
|
14223
|
+
if (path23 === "-")
|
|
13963
14224
|
return await readTextInput(input);
|
|
13964
14225
|
await rejectUnusedTextInput(input, unusedInputMessage);
|
|
13965
|
-
return await readTextFile(
|
|
14226
|
+
return await readTextFile(path23, readUtf8File2);
|
|
13966
14227
|
}
|
|
13967
14228
|
|
|
13968
|
-
// .dist-releases/
|
|
14229
|
+
// .dist-releases/1784894032002-2676534/src/ask.js
|
|
13969
14230
|
var ASK_FLAGS = new Set(["body-file", "option", "reason", "recommend", "session", "title"]);
|
|
13970
14231
|
async function runAskFromCli(client2, argv, config, input) {
|
|
13971
14232
|
const args = _parseAskArgs(argv);
|
|
@@ -14080,8 +14341,8 @@ function _singleAskValue(values, name) {
|
|
|
14080
14341
|
return entries[0];
|
|
14081
14342
|
}
|
|
14082
14343
|
|
|
14083
|
-
// .dist-releases/
|
|
14084
|
-
import { readFile as
|
|
14344
|
+
// .dist-releases/1784894032002-2676534/src/task.js
|
|
14345
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
14085
14346
|
import { basename } from "node:path";
|
|
14086
14347
|
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
14348
|
var TASK_BOOLEAN_FLAGS = new Set(["all", "draft"]);
|
|
@@ -14312,7 +14573,7 @@ async function _createTaskRequest(args, input) {
|
|
|
14312
14573
|
async function _uploadTaskImages(client2, captainId, session, imagePaths) {
|
|
14313
14574
|
const artifactIds = [];
|
|
14314
14575
|
for (const imagePath of imagePaths) {
|
|
14315
|
-
const content = await
|
|
14576
|
+
const content = await readFile10(imagePath);
|
|
14316
14577
|
const artifact = await client2.uploadSessionArtifact(captainId, session, {
|
|
14317
14578
|
name: basename(imagePath),
|
|
14318
14579
|
content_bytes_base64: bytesToBase64(content)
|
|
@@ -14570,7 +14831,7 @@ function _taskListFooter(shown, returned, total, hasMore, cursor) {
|
|
|
14570
14831
|
return `Shown ${shown} of ${total}${local}${next}`;
|
|
14571
14832
|
}
|
|
14572
14833
|
|
|
14573
|
-
// .dist-releases/
|
|
14834
|
+
// .dist-releases/1784894032002-2676534/src/team.js
|
|
14574
14835
|
var TEAM_BOOTSTRAP_ENGINES = new Set(["claude", "codex"]);
|
|
14575
14836
|
var SLOT_SPEC_RE = /^([^=\s]+)=([^:@\s]+):([^:@\s]+)$/;
|
|
14576
14837
|
var TEAM_PLAIN_LIMIT = 20;
|
|
@@ -14725,7 +14986,7 @@ function _slotTable(slots) {
|
|
|
14725
14986
|
Shown ${shown.length} of ${slots.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`;
|
|
14726
14987
|
}
|
|
14727
14988
|
|
|
14728
|
-
// .dist-releases/
|
|
14989
|
+
// .dist-releases/1784894032002-2676534/src/memory.js
|
|
14729
14990
|
var VALUE_FLAGS = new Set(["file", "source"]);
|
|
14730
14991
|
var MEMORY_SEARCH_PLAIN_LIMIT = 20;
|
|
14731
14992
|
async function runMemoryFromCli(client2, argv, config, input, format) {
|
|
@@ -14860,7 +15121,7 @@ function _summary(value) {
|
|
|
14860
15121
|
return value.replace(/\s+/gu, " ").trim().slice(0, 180);
|
|
14861
15122
|
}
|
|
14862
15123
|
|
|
14863
|
-
// .dist-releases/
|
|
15124
|
+
// .dist-releases/1784894032002-2676534/src/mailbox-send.js
|
|
14864
15125
|
var SEND_FLAGS = new Set(["body-file", "commit", "session", "tag", "task-id", "task-version", "to-agents", "to-roles", "to-host"]);
|
|
14865
15126
|
function parseMailboxSendArgs(argv) {
|
|
14866
15127
|
const parsed = argsFromValues(parseFlagValues(argv));
|
|
@@ -14959,7 +15220,7 @@ function splitList(value) {
|
|
|
14959
15220
|
return list?.length ? list : undefined;
|
|
14960
15221
|
}
|
|
14961
15222
|
|
|
14962
|
-
// .dist-releases/
|
|
15223
|
+
// .dist-releases/1784894032002-2676534/src/reply.js
|
|
14963
15224
|
var REPLY_FLAGS = new Set(["agent", "body-file", "in-reply-to", "session"]);
|
|
14964
15225
|
async function sendReplyFromCli(client2, argv, config, io) {
|
|
14965
15226
|
const args = _parseReplyArgs(argv);
|
|
@@ -15016,12 +15277,13 @@ function _requiredReplyValue(values, name) {
|
|
|
15016
15277
|
return value;
|
|
15017
15278
|
}
|
|
15018
15279
|
|
|
15019
|
-
// .dist-releases/
|
|
15280
|
+
// .dist-releases/1784894032002-2676534/src/worker-command.js
|
|
15020
15281
|
async function runWorkerCommandFromCli(client2, argv, config, io, format = "plain") {
|
|
15282
|
+
const command = argv[0];
|
|
15283
|
+
_requireWorkerCredential(config.keyScope, command);
|
|
15021
15284
|
const captainId = config.captainId;
|
|
15022
15285
|
if (!captainId)
|
|
15023
15286
|
throw new Error("SAILORBRIDGE_CAPTAIN_ID is required for worker commands");
|
|
15024
|
-
const command = argv[0];
|
|
15025
15287
|
switch (command) {
|
|
15026
15288
|
case "list":
|
|
15027
15289
|
return _listWorkers(client2, captainId, _requireSession(config.session), argv.slice(1), io, format);
|
|
@@ -15033,6 +15295,14 @@ async function runWorkerCommandFromCli(client2, argv, config, io, format = "plai
|
|
|
15033
15295
|
throw new Error("worker command must be job, list, run, or serve");
|
|
15034
15296
|
}
|
|
15035
15297
|
}
|
|
15298
|
+
function _requireWorkerCredential(keyScope, command) {
|
|
15299
|
+
if (keyScope !== "host")
|
|
15300
|
+
return;
|
|
15301
|
+
if (command === "list") {
|
|
15302
|
+
throw new Error("worker list cannot use a paired host credential. " + "Open this team's Workers page in the SailorBridge console.");
|
|
15303
|
+
}
|
|
15304
|
+
throw new Error("worker run/job requires a member-runtime credential; this command loaded a paired host credential. " + "Retry from a member workspace where SAILORBRIDGE_TOKEN is already set.");
|
|
15305
|
+
}
|
|
15036
15306
|
function _requireSession(session) {
|
|
15037
15307
|
if (!session)
|
|
15038
15308
|
throw new Error("SAILORBRIDGE_SESSION is required for worker list and run");
|
|
@@ -15197,7 +15467,7 @@ function _writeSignalStatus(remoteCode, localStatus, remoteStderr, io) {
|
|
|
15197
15467
|
`);
|
|
15198
15468
|
}
|
|
15199
15469
|
|
|
15200
|
-
// .dist-releases/
|
|
15470
|
+
// .dist-releases/1784894032002-2676534/src/create.js
|
|
15201
15471
|
var CREATE_ENGINES = new Set(["claude", "codex"]);
|
|
15202
15472
|
var DEFAULT_ENGINE = "claude";
|
|
15203
15473
|
var WORKSPACE_PLAIN_LIMIT = 20;
|
|
@@ -15251,9 +15521,9 @@ async function _startTeamSlots(deps, ctx, team, hostId, workspaceRoot, engine, s
|
|
|
15251
15521
|
}
|
|
15252
15522
|
return requests;
|
|
15253
15523
|
}
|
|
15254
|
-
async function _postConsoleJson(deps, ctx,
|
|
15524
|
+
async function _postConsoleJson(deps, ctx, path23, body) {
|
|
15255
15525
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
15256
|
-
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${
|
|
15526
|
+
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path23}`, {
|
|
15257
15527
|
method: "POST",
|
|
15258
15528
|
headers: { Authorization: `Bearer ${ctx.token}`, "Content-Type": "application/json" },
|
|
15259
15529
|
body: JSON.stringify(body)
|
|
@@ -15266,9 +15536,9 @@ async function _postConsoleJson(deps, ctx, path22, body) {
|
|
|
15266
15536
|
throw new Error("SailorBridge API returned a malformed object");
|
|
15267
15537
|
return parsed;
|
|
15268
15538
|
}
|
|
15269
|
-
async function _getConsoleJson(deps, ctx,
|
|
15539
|
+
async function _getConsoleJson(deps, ctx, path23) {
|
|
15270
15540
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
15271
|
-
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${
|
|
15541
|
+
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path23}`, {
|
|
15272
15542
|
headers: { Authorization: `Bearer ${ctx.token}` }
|
|
15273
15543
|
});
|
|
15274
15544
|
const text = await response.text();
|
|
@@ -15343,15 +15613,15 @@ function _rejectMemberFlags(role, flags) {
|
|
|
15343
15613
|
throw new Error(`create ${role} does not accept ${found[0]}`);
|
|
15344
15614
|
}
|
|
15345
15615
|
async function _memberSlotForCreate(deps, ctx, session, role) {
|
|
15346
|
-
const
|
|
15347
|
-
const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx,
|
|
15616
|
+
const path23 = `/api/console/teams/${encodeURIComponent(session)}/agent-slots`;
|
|
15617
|
+
const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx, path23));
|
|
15348
15618
|
const retryable = slots.find((slot) => _isRetryableMemberSlot(slot, role));
|
|
15349
15619
|
if (retryable)
|
|
15350
15620
|
return retryable;
|
|
15351
15621
|
if (role === "planner" && slots.some((slot) => slot.role === role)) {
|
|
15352
15622
|
throw new Error("duplicate-singleton-role: team already has a planner slot");
|
|
15353
15623
|
}
|
|
15354
|
-
const created = _responseMemberSlot(await _postConsoleJson(deps, ctx,
|
|
15624
|
+
const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path23, { role }));
|
|
15355
15625
|
if (created.role !== role)
|
|
15356
15626
|
throw new Error("SailorBridge API returned a member slot with the wrong role");
|
|
15357
15627
|
return created;
|
|
@@ -15587,7 +15857,7 @@ function _workerPlain(hostId, workdir, result) {
|
|
|
15587
15857
|
`);
|
|
15588
15858
|
}
|
|
15589
15859
|
|
|
15590
|
-
// .dist-releases/
|
|
15860
|
+
// .dist-releases/1784894032002-2676534/src/cli.js
|
|
15591
15861
|
var FORMATTED_COMMANDS = new Set([
|
|
15592
15862
|
"ask",
|
|
15593
15863
|
"create",
|
|
@@ -15647,6 +15917,10 @@ async function main(argv) {
|
|
|
15647
15917
|
});
|
|
15648
15918
|
return;
|
|
15649
15919
|
}
|
|
15920
|
+
if (command === "upgrade") {
|
|
15921
|
+
await runUpgradeFromCli(argv.slice(1), { stdout: process.stdout });
|
|
15922
|
+
return;
|
|
15923
|
+
}
|
|
15650
15924
|
if (command === "host" && argv[1] === "start-request") {
|
|
15651
15925
|
await runHostStartRequestCommandFromCli(argv.slice(2), { stdout: process.stdout, stderr: process.stderr, format: output2.format });
|
|
15652
15926
|
return;
|
|
@@ -15753,6 +16027,7 @@ var KNOWN_COMMANDS = new Set([
|
|
|
15753
16027
|
"task",
|
|
15754
16028
|
"team",
|
|
15755
16029
|
"tutorial",
|
|
16030
|
+
"upgrade",
|
|
15756
16031
|
"workflow",
|
|
15757
16032
|
"worker"
|
|
15758
16033
|
]);
|
|
@@ -15780,6 +16055,7 @@ var SUBCOMMANDS = {
|
|
|
15780
16055
|
"unpark"
|
|
15781
16056
|
]),
|
|
15782
16057
|
team: new Set(["bootstrap"]),
|
|
16058
|
+
upgrade: new Set,
|
|
15783
16059
|
worker: new Set(["job", "list", "run", "serve"])
|
|
15784
16060
|
};
|
|
15785
16061
|
function _assertKnownCommand(command, subcommand) {
|
|
@@ -15981,8 +16257,9 @@ function _resolveOptionalApiUrl(flag, env) {
|
|
|
15981
16257
|
}
|
|
15982
16258
|
return env?.trim() || undefined;
|
|
15983
16259
|
}
|
|
16260
|
+
var AUTO_UPDATE_SKIP_FLAG = "--disable-auto-update";
|
|
15984
16261
|
async function hostRun(argv) {
|
|
15985
|
-
const input = parseFlags3(argv);
|
|
16262
|
+
const input = parseFlags3(argv.filter((arg) => arg !== AUTO_UPDATE_SKIP_FLAG));
|
|
15986
16263
|
const credentialsPath = input["credentials-path"] ?? process.env.SAILORBRIDGE_CREDENTIALS_FILE ?? defaultHostCredentialsPath();
|
|
15987
16264
|
const credential = await loadHostCredential(credentialsPath);
|
|
15988
16265
|
const relayUrl = credential.relay_url ?? process.env.SAILORBRIDGE_RELAY_URL;
|
|
@@ -16000,6 +16277,7 @@ async function hostRun(argv) {
|
|
|
16000
16277
|
process.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, host_id: credential.host_id, ...fields })}
|
|
16001
16278
|
`);
|
|
16002
16279
|
};
|
|
16280
|
+
const autoUpdate = !argv.includes(AUTO_UPDATE_SKIP_FLAG) && await _hostConfigAllowsAutoUpdate(emit);
|
|
16003
16281
|
await runHostDaemon({
|
|
16004
16282
|
relay,
|
|
16005
16283
|
resolveCredential,
|
|
@@ -16010,9 +16288,19 @@ async function hostRun(argv) {
|
|
|
16010
16288
|
signal: controller.signal,
|
|
16011
16289
|
redactSecrets: [credential.token],
|
|
16012
16290
|
...input["workspace-root"] ? { workspaceRoot: input["workspace-root"] } : {},
|
|
16291
|
+
autoUpdate,
|
|
16013
16292
|
logger: emit
|
|
16014
16293
|
});
|
|
16015
16294
|
}
|
|
16295
|
+
async function _hostConfigAllowsAutoUpdate(emit) {
|
|
16296
|
+
try {
|
|
16297
|
+
const config = await loadHostConfig(process.env.SAILORBRIDGE_HOST_CONFIG_FILE ?? defaultHostConfigPath());
|
|
16298
|
+
return config.auto_update !== false;
|
|
16299
|
+
} catch (error) {
|
|
16300
|
+
emit("host_config_load_error", { error: error instanceof Error ? error.message : String(error) });
|
|
16301
|
+
return true;
|
|
16302
|
+
}
|
|
16303
|
+
}
|
|
16016
16304
|
async function supervise(client2, argv, config) {
|
|
16017
16305
|
const parsed = parseSuperviseArgs(argv);
|
|
16018
16306
|
assertSingleOwnerConfirmed(parsed);
|
|
@@ -16234,6 +16522,8 @@ function _usageText() {
|
|
|
16234
16522
|
sailorbridge workflow
|
|
16235
16523
|
sailorbridge refresh
|
|
16236
16524
|
sailorbridge --version
|
|
16525
|
+
sailorbridge upgrade
|
|
16526
|
+
installs @sailorbridge/client@latest and restarts a running SailorBridge-managed host service.
|
|
16237
16527
|
sailorbridge tutorial [--check] [--api-url URL] [--credentials-path PATH] (default API: ${DEFAULT_PRODUCT_API_URL})
|
|
16238
16528
|
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
16529
|
pairs this host, then installs and starts the host service so the console can start workers and agents here.
|
|
@@ -16247,7 +16537,7 @@ function _usageText() {
|
|
|
16247
16537
|
sailorbridge event add --at HH:MM --tz IANA --days once|daily|weekday --message TEXT [--run-date YYYY-MM-DD] [--tag T] [--disabled] [--session S]
|
|
16248
16538
|
sailorbridge event list [--session S]
|
|
16249
16539
|
sailorbridge event enable|disable|rm EVENT_ID [--session S]
|
|
16250
|
-
sailorbridge ask --title TITLE --option LABEL::DESCRIPTION --option LABEL::DESCRIPTION --recommend A --reason TEXT [--reason TEXT]
|
|
16540
|
+
sailorbridge ask --title TITLE --option LABEL::DESCRIPTION --option LABEL::DESCRIPTION --recommend A --reason TEXT [--reason TEXT] --body-file - [--session S]
|
|
16251
16541
|
sailorbridge ask list [--session S]
|
|
16252
16542
|
sailorbridge ask get|status DECISION_ID [--session S]
|
|
16253
16543
|
sailorbridge task create [--description TEXT|-|--body-file PATH|-|--description-file PATH|-] [--pair PAIR] [--draft|--status draft|new] [--type feature|bug|docs|chore] [--priority high|normal|low] [--level d1|d2|d3|d4] [--depends TASK[,TASK]] [--artifact-id ID] [--image PATH] [--session S]
|
|
@@ -16283,7 +16573,7 @@ function _usageText() {
|
|
|
16283
16573
|
sailorbridge host consume --once --dry-run-start [--host-id H] [--display-name N]
|
|
16284
16574
|
sailorbridge host service install|uninstall|status [--binary PATH] [--credentials-path PATH] [--workspace-root DIR] [--service-dir DIR] [--service-name NAME]
|
|
16285
16575
|
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]
|
|
16576
|
+
sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR] [--disable-auto-update]
|
|
16287
16577
|
sailorbridge worker list [--format plain|json]
|
|
16288
16578
|
sailorbridge worker run [--detach] --worker NAME [--timeout SEC] [--format plain|json] -- ARGV...
|
|
16289
16579
|
sailorbridge worker job ID [--format plain|json]
|
|
@@ -16337,5 +16627,5 @@ function _looksLikeNetworkFailure(message) {
|
|
|
16337
16627
|
return /fetch failed|network|ECONN(?:REFUSED|RESET)|ENOTFOUND|ETIMEDOUT/iu.test(message);
|
|
16338
16628
|
}
|
|
16339
16629
|
|
|
16340
|
-
//# debugId=
|
|
16630
|
+
//# debugId=F9747B92FC45FFDB64756E2164756E21
|
|
16341
16631
|
//# sourceMappingURL=cli.js.map
|