@sailorbridge/client 0.1.1 → 0.2.1
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 +30 -7
- package/dist/src/cli.js +904 -839
- package/dist/src/cli.js.map +18 -19
- 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/1784860966076-1188772/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/1784860966076-1188772/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,37 +220,82 @@ 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/1784860966076-1188772/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";
|
|
227
227
|
import path2 from "node:path";
|
|
228
228
|
import { spawn } from "node:child_process";
|
|
229
229
|
var COMMAND_TIMEOUT_MS = 120000;
|
|
230
|
+
var REPO_BACKOFF_INITIAL_MS = 60000;
|
|
231
|
+
var REPO_BACKOFF_MAX_MS = 60 * 60000;
|
|
230
232
|
var SHA_RE = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
|
|
231
233
|
function defaultRepoMirrorRoot() {
|
|
232
234
|
return path2.join(homedir2(), ".sailorbridge", "repo-mirrors");
|
|
233
235
|
}
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
+
function createRepoTipReporter(deps = {}) {
|
|
237
|
+
const backoffs = new Map;
|
|
238
|
+
return async (options) => _reportRepoTipsOnce(options, deps, backoffs);
|
|
239
|
+
}
|
|
240
|
+
async function _reportRepoTipsOnce(options, deps, backoffs) {
|
|
236
241
|
const wanted = await options.relay.getTaskRepoTipWanted(options.captainId);
|
|
237
242
|
const mirrorRoot = options.mirrorRoot ?? defaultRepoMirrorRoot();
|
|
238
243
|
let reported = 0;
|
|
239
244
|
for (const entry of wanted.wanted) {
|
|
240
|
-
|
|
241
|
-
const report = await observeRepoTips(entry, mirrorRoot, deps);
|
|
242
|
-
await options.relay.putTaskRepoTips(options.captainId, entry.session, report);
|
|
245
|
+
if (await _reportRepoTip(entry, mirrorRoot, options, deps, backoffs))
|
|
243
246
|
reported += 1;
|
|
244
|
-
} catch (error) {
|
|
245
|
-
logger("host_repo_tip_report_failed", {
|
|
246
|
-
session: entry.session,
|
|
247
|
-
repo: entry.repo_path,
|
|
248
|
-
error: error instanceof Error ? error.message : String(error)
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
247
|
}
|
|
248
|
+
_forgetUnwantedBackoffs(backoffs, wanted.wanted);
|
|
252
249
|
return { observed: wanted.wanted.length, reported };
|
|
253
250
|
}
|
|
251
|
+
async function _reportRepoTip(entry, mirrorRoot, options, deps, backoffs) {
|
|
252
|
+
const backoff = backoffs.get(entry.repo_path);
|
|
253
|
+
if (backoff && _now(deps) < backoff.retryAt)
|
|
254
|
+
return false;
|
|
255
|
+
try {
|
|
256
|
+
const report = await observeRepoTips(entry, mirrorRoot, deps);
|
|
257
|
+
await options.relay.putTaskRepoTips(options.captainId, entry.session, report);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
_backOffRepo(entry, error, options.logger, deps, backoffs);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
if (backoff)
|
|
263
|
+
_recoverRepo(entry, backoff, options.logger, backoffs);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
function _backOffRepo(entry, error, logger, deps, backoffs) {
|
|
267
|
+
const previous = backoffs.get(entry.repo_path);
|
|
268
|
+
const retryMs = previous ? Math.min(previous.retryMs * 2, REPO_BACKOFF_MAX_MS) : REPO_BACKOFF_INITIAL_MS;
|
|
269
|
+
const failures = (previous?.failures ?? 0) + 1;
|
|
270
|
+
backoffs.set(entry.repo_path, { failures, retryAt: _now(deps) + retryMs, retryMs });
|
|
271
|
+
if (previous)
|
|
272
|
+
return;
|
|
273
|
+
logger?.("host_repo_tip_backoff_started", {
|
|
274
|
+
session: entry.session,
|
|
275
|
+
repo: entry.repo_path,
|
|
276
|
+
error: error instanceof Error ? error.message : String(error),
|
|
277
|
+
failures,
|
|
278
|
+
retry_ms: retryMs
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function _recoverRepo(entry, backoff, logger, backoffs) {
|
|
282
|
+
backoffs.delete(entry.repo_path);
|
|
283
|
+
logger?.("host_repo_tip_backoff_recovered", {
|
|
284
|
+
session: entry.session,
|
|
285
|
+
repo: entry.repo_path,
|
|
286
|
+
failures: backoff.failures
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function _forgetUnwantedBackoffs(backoffs, wanted) {
|
|
290
|
+
const activeRepos = new Set(wanted.map((entry) => entry.repo_path));
|
|
291
|
+
for (const repo of backoffs.keys()) {
|
|
292
|
+
if (!activeRepos.has(repo))
|
|
293
|
+
backoffs.delete(repo);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function _now(deps) {
|
|
297
|
+
return deps.now?.() ?? Date.now();
|
|
298
|
+
}
|
|
254
299
|
async function observeRepoTips(entry, mirrorRoot, deps) {
|
|
255
300
|
const exec = deps.execCommand ?? execArgv;
|
|
256
301
|
const mirror = mirrorPathFor(mirrorRoot, entry.repo_path);
|
|
@@ -357,10 +402,10 @@ ${error.message}`));
|
|
|
357
402
|
proc.on("close", (code) => finish(code ?? 1));
|
|
358
403
|
});
|
|
359
404
|
}
|
|
360
|
-
// .dist-releases/
|
|
405
|
+
// .dist-releases/1784860966076-1188772/src/host/request-consumer.js
|
|
361
406
|
import { hostname as hostname2 } from "node:os";
|
|
362
407
|
|
|
363
|
-
// .dist-releases/
|
|
408
|
+
// .dist-releases/1784860966076-1188772/src/driver/secrets.js
|
|
364
409
|
function redactSensitiveText(text, env = {}, explicitSecrets = []) {
|
|
365
410
|
let redacted = text;
|
|
366
411
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -379,16 +424,16 @@ function isSensitiveEnvKey(key) {
|
|
|
379
424
|
return /(TOKEN|KEY|SECRET|AUTHORIZATION|PASSWORD)/i.test(key);
|
|
380
425
|
}
|
|
381
426
|
|
|
382
|
-
// .dist-releases/
|
|
383
|
-
var CLIENT_VERSION = "0.
|
|
384
|
-
var CLIENT_GIT_SHA = "
|
|
385
|
-
var CLIENT_BUILD_DIRTY =
|
|
427
|
+
// .dist-releases/1784860966076-1188772/src/version.js
|
|
428
|
+
var CLIENT_VERSION = "0.2.1";
|
|
429
|
+
var CLIENT_GIT_SHA = "89604f060de5-dirty";
|
|
430
|
+
var CLIENT_BUILD_DIRTY = true;
|
|
386
431
|
var CLIENT_VERSION_LABEL = formatClientVersion(CLIENT_VERSION, CLIENT_GIT_SHA);
|
|
387
432
|
function formatClientVersion(version, gitSha) {
|
|
388
433
|
return `${version}+git.${gitSha}`;
|
|
389
434
|
}
|
|
390
435
|
|
|
391
|
-
// .dist-releases/
|
|
436
|
+
// .dist-releases/1784860966076-1188772/src/host/request-consumer.js
|
|
392
437
|
var MAX_ERROR_LENGTH = 500;
|
|
393
438
|
function defaultHostIdentity(config) {
|
|
394
439
|
const captainId = config.captainId?.trim();
|
|
@@ -443,7 +488,7 @@ function limitErrorMessage(error, explicitSecrets) {
|
|
|
443
488
|
const trimmed = redactSensitiveText(message, process.env, explicitSecrets).trim() || "host request starter failed";
|
|
444
489
|
return trimmed.length <= MAX_ERROR_LENGTH ? trimmed : `${trimmed.slice(0, MAX_ERROR_LENGTH - 3)}...`;
|
|
445
490
|
}
|
|
446
|
-
// .dist-releases/
|
|
491
|
+
// .dist-releases/1784860966076-1188772/src/product/start-request.js
|
|
447
492
|
async function createAgentStartRequest(options) {
|
|
448
493
|
const apiUrl = _required(options.apiUrl, "product API URL").replace(/\/+$/u, "");
|
|
449
494
|
const accessToken = _required(options.accessToken, "product API bearer token");
|
|
@@ -503,7 +548,7 @@ function _string(value, label) {
|
|
|
503
548
|
throw new Error(`${label} must be a non-empty string`);
|
|
504
549
|
return value;
|
|
505
550
|
}
|
|
506
|
-
// .dist-releases/
|
|
551
|
+
// .dist-releases/1784860966076-1188772/src/product/pair.js
|
|
507
552
|
async function createPair(options) {
|
|
508
553
|
const apiUrl = _required2(options.apiUrl, "product API URL").replace(/\/+$/u, "");
|
|
509
554
|
const accessToken = _required2(options.accessToken, "product API bearer token");
|
|
@@ -580,7 +625,7 @@ function _string2(value, label) {
|
|
|
580
625
|
throw new Error(`${label} must be a non-empty string`);
|
|
581
626
|
return value;
|
|
582
627
|
}
|
|
583
|
-
// .dist-releases/
|
|
628
|
+
// .dist-releases/1784860966076-1188772/src/product/worker.js
|
|
584
629
|
async function createWorker(options) {
|
|
585
630
|
const apiUrl = _required3(options.apiUrl, "product API URL").replace(/\/+$/u, "");
|
|
586
631
|
const accessToken = _required3(options.accessToken, "product API bearer token");
|
|
@@ -644,7 +689,7 @@ function _string3(value, label) {
|
|
|
644
689
|
throw new Error(`${label} must be a non-empty string`);
|
|
645
690
|
return value;
|
|
646
691
|
}
|
|
647
|
-
// .dist-releases/
|
|
692
|
+
// .dist-releases/1784860966076-1188772/src/driver/events.js
|
|
648
693
|
class DriverEventEmitter {
|
|
649
694
|
callbacks = new Map;
|
|
650
695
|
on(type, callback) {
|
|
@@ -659,7 +704,7 @@ class DriverEventEmitter {
|
|
|
659
704
|
}
|
|
660
705
|
}
|
|
661
706
|
}
|
|
662
|
-
// .dist-releases/
|
|
707
|
+
// .dist-releases/1784860966076-1188772/src/driver/async-queue.js
|
|
663
708
|
class AsyncQueue {
|
|
664
709
|
values = [];
|
|
665
710
|
waiters = [];
|
|
@@ -698,7 +743,7 @@ class AsyncQueue {
|
|
|
698
743
|
}
|
|
699
744
|
}
|
|
700
745
|
|
|
701
|
-
// .dist-releases/
|
|
746
|
+
// .dist-releases/1784860966076-1188772/src/driver/types.js
|
|
702
747
|
class DriverUnsupportedError extends Error {
|
|
703
748
|
constructor(message) {
|
|
704
749
|
super(message);
|
|
@@ -713,7 +758,7 @@ class DriverTargetNotReadyError extends Error {
|
|
|
713
758
|
}
|
|
714
759
|
}
|
|
715
760
|
|
|
716
|
-
// .dist-releases/
|
|
761
|
+
// .dist-releases/1784860966076-1188772/src/driver/sdk-loader.js
|
|
717
762
|
async function loadSdk(loader, packageName, env = {}, installHint = `Install the optional SDK dependency ${packageName} and its peers for this transport.`) {
|
|
718
763
|
try {
|
|
719
764
|
return await loader();
|
|
@@ -723,7 +768,7 @@ async function loadSdk(loader, packageName, env = {}, installHint = `Install the
|
|
|
723
768
|
}
|
|
724
769
|
}
|
|
725
770
|
|
|
726
|
-
// .dist-releases/
|
|
771
|
+
// .dist-releases/1784860966076-1188772/src/driver/claude/driver.js
|
|
727
772
|
class ClaudeAgentSdkDriver {
|
|
728
773
|
options;
|
|
729
774
|
capabilities = { structured: true, resume: true, attach: false };
|
|
@@ -899,7 +944,7 @@ function asRecord(value) {
|
|
|
899
944
|
function stringValue(value) {
|
|
900
945
|
return typeof value === "string" ? value : undefined;
|
|
901
946
|
}
|
|
902
|
-
// .dist-releases/
|
|
947
|
+
// .dist-releases/1784860966076-1188772/src/driver/codex/driver.js
|
|
903
948
|
class CodexSdkDriver {
|
|
904
949
|
options;
|
|
905
950
|
capabilities = { structured: true, resume: true, attach: false };
|
|
@@ -1080,7 +1125,7 @@ function asRecord2(value) {
|
|
|
1080
1125
|
function stringValue2(value) {
|
|
1081
1126
|
return typeof value === "string" ? value : undefined;
|
|
1082
1127
|
}
|
|
1083
|
-
// .dist-releases/
|
|
1128
|
+
// .dist-releases/1784860966076-1188772/src/driver/liveness-heuristics.js
|
|
1084
1129
|
var BUSY_MARKERS = ["Working (", "Waiting for background", "Pasted Content", "tab to queue"];
|
|
1085
1130
|
var ANSI_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\)/g;
|
|
1086
1131
|
function stripAnsi(text) {
|
|
@@ -1107,7 +1152,7 @@ function classifyInteractiveOutput(input) {
|
|
|
1107
1152
|
function isOutputStable(now, lastOutputAt, idleStableMs) {
|
|
1108
1153
|
return Boolean(lastOutputAt && now.getTime() - lastOutputAt.getTime() >= idleStableMs);
|
|
1109
1154
|
}
|
|
1110
|
-
// .dist-releases/
|
|
1155
|
+
// .dist-releases/1784860966076-1188772/src/driver/pty/loader.js
|
|
1111
1156
|
async function loadNodePty(loader = defaultNodePtyLoader) {
|
|
1112
1157
|
try {
|
|
1113
1158
|
return await loader();
|
|
@@ -1133,7 +1178,7 @@ async function defaultNodePtyLoader() {
|
|
|
1133
1178
|
return loaded;
|
|
1134
1179
|
}
|
|
1135
1180
|
|
|
1136
|
-
// .dist-releases/
|
|
1181
|
+
// .dist-releases/1784860966076-1188772/src/driver/pty/driver.js
|
|
1137
1182
|
class PtyDriver {
|
|
1138
1183
|
options;
|
|
1139
1184
|
capabilities = { structured: false, resume: false, attach: false };
|
|
@@ -1285,12 +1330,12 @@ function tail(text, lines) {
|
|
|
1285
1330
|
function sleep(ms) {
|
|
1286
1331
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1287
1332
|
}
|
|
1288
|
-
// .dist-releases/
|
|
1333
|
+
// .dist-releases/1784860966076-1188772/src/driver/tmux/driver.js
|
|
1289
1334
|
import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
1290
1335
|
import { tmpdir } from "node:os";
|
|
1291
1336
|
import { join } from "node:path";
|
|
1292
1337
|
|
|
1293
|
-
// .dist-releases/
|
|
1338
|
+
// .dist-releases/1784860966076-1188772/src/driver/tmux/launch.js
|
|
1294
1339
|
var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1295
1340
|
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 "$@"';
|
|
1296
1341
|
function buildTmuxNewSessionArgs(input) {
|
|
@@ -1313,7 +1358,7 @@ function validateEnv(env) {
|
|
|
1313
1358
|
}
|
|
1314
1359
|
}
|
|
1315
1360
|
|
|
1316
|
-
// .dist-releases/
|
|
1361
|
+
// .dist-releases/1784860966076-1188772/src/driver/tmux/paste.js
|
|
1317
1362
|
function buildDispatchPlan(text, useTyping) {
|
|
1318
1363
|
return {
|
|
1319
1364
|
mode: useTyping ? "type-literal" : "paste-buffer",
|
|
@@ -1337,7 +1382,7 @@ function arraysEqual(left, right) {
|
|
|
1337
1382
|
return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
1338
1383
|
}
|
|
1339
1384
|
|
|
1340
|
-
// .dist-releases/
|
|
1385
|
+
// .dist-releases/1784860966076-1188772/src/driver/tmux/liveness.js
|
|
1341
1386
|
var SHELL_COMMANDS = new Set(["", "bash", "zsh", "sh", "fish", "pwsh", "powershell"]);
|
|
1342
1387
|
function classifyTmuxLiveness(input) {
|
|
1343
1388
|
if (!input.hasSession) {
|
|
@@ -1357,7 +1402,7 @@ function isTmuxProcessAlive(command, runningCommands) {
|
|
|
1357
1402
|
return !SHELL_COMMANDS.has(command);
|
|
1358
1403
|
}
|
|
1359
1404
|
|
|
1360
|
-
// .dist-releases/
|
|
1405
|
+
// .dist-releases/1784860966076-1188772/src/driver/tmux/runner.js
|
|
1361
1406
|
import { spawn as spawn2 } from "node:child_process";
|
|
1362
1407
|
var DEFAULT_TIMEOUT_MS = 15000;
|
|
1363
1408
|
|
|
@@ -1482,7 +1527,7 @@ class TmuxClient {
|
|
|
1482
1527
|
}
|
|
1483
1528
|
}
|
|
1484
1529
|
|
|
1485
|
-
// .dist-releases/
|
|
1530
|
+
// .dist-releases/1784860966076-1188772/src/driver/tmux/driver.js
|
|
1486
1531
|
var ATTACHED_STOP_GRACE_MS = 1e4;
|
|
1487
1532
|
var DEFAULT_STOP_TIMEOUT_MS = 15000;
|
|
1488
1533
|
var DEFAULT_READY_TIMEOUT_MS = 90000;
|
|
@@ -1877,12 +1922,12 @@ function isMissingTmuxSessionError(error) {
|
|
|
1877
1922
|
${error.result.stderr}`.toLowerCase();
|
|
1878
1923
|
return text.includes("can't find session") || text.includes("cannot find session") || text.includes("no such session") || text.includes("session not found");
|
|
1879
1924
|
}
|
|
1880
|
-
// .dist-releases/
|
|
1925
|
+
// .dist-releases/1784860966076-1188772/src/relay/client.js
|
|
1881
1926
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
1882
1927
|
import http from "node:http";
|
|
1883
1928
|
import https from "node:https";
|
|
1884
1929
|
|
|
1885
|
-
// .dist-releases/
|
|
1930
|
+
// .dist-releases/1784860966076-1188772/src/relay/errors.js
|
|
1886
1931
|
class RelayError extends Error {
|
|
1887
1932
|
method;
|
|
1888
1933
|
path;
|
|
@@ -1919,7 +1964,37 @@ class RelayProtocolError extends RelayError {
|
|
|
1919
1964
|
}
|
|
1920
1965
|
}
|
|
1921
1966
|
|
|
1922
|
-
// .dist-releases/
|
|
1967
|
+
// .dist-releases/1784860966076-1188772/src/worker/protocol.js
|
|
1968
|
+
var DEFAULT_RELAY_URL = DEFAULT_PRODUCT_API_URL;
|
|
1969
|
+
var DEFAULT_WORKER = "host";
|
|
1970
|
+
var DEFAULT_WORKDIR = "~/sailorbridge-workdir";
|
|
1971
|
+
var DEFAULT_JOB_TIMEOUT_SECONDS = 300;
|
|
1972
|
+
var MAX_JOB_TIMEOUT_SECONDS = 24 * 60 * 60;
|
|
1973
|
+
var MIN_JOB_TIMEOUT_SECONDS = 1;
|
|
1974
|
+
var WORKER_JOB_RESULT_GRACE_SECONDS = 30;
|
|
1975
|
+
var ACTIVE_JOB_KINDS = new Set(["apply_diff", "bash", "read_file", "write_file", "compose", "stream", "status"]);
|
|
1976
|
+
var READONLY_ALLOWED_KINDS = new Set(["read_file", "status"]);
|
|
1977
|
+
var WORKER_SOCK_CONNECT_MS = 1e4;
|
|
1978
|
+
var WORKER_PULL_READ_IDLE_MS = 45000;
|
|
1979
|
+
var WORKER_QUICK_READ_IDLE_MS = 15000;
|
|
1980
|
+
var WORKER_UPLOAD_READ_IDLE_MS = (MAX_JOB_TIMEOUT_SECONDS + 300) * 1000;
|
|
1981
|
+
var DRAIN_AFTER_KILL_MS = 5000;
|
|
1982
|
+
var WORKER_RETRY_INITIAL_MS = 1000;
|
|
1983
|
+
var WORKER_RETRY_MAX_MS = 30000;
|
|
1984
|
+
var WORKER_SESSION_REBUILD_FAILURES = 10;
|
|
1985
|
+
var MAX_WORKER_RESULT_OUTPUT_BYTES = 256 * 1024;
|
|
1986
|
+
function coerceJobTimeoutSeconds(spec) {
|
|
1987
|
+
const value = spec.timeout_sec === undefined ? DEFAULT_JOB_TIMEOUT_SECONDS : spec.timeout_sec;
|
|
1988
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
1989
|
+
throw new Error("spec.timeout_sec must be a finite positive number");
|
|
1990
|
+
}
|
|
1991
|
+
return Math.max(MIN_JOB_TIMEOUT_SECONDS, Math.min(value, MAX_JOB_TIMEOUT_SECONDS));
|
|
1992
|
+
}
|
|
1993
|
+
function effectiveJobKind(kind, spec) {
|
|
1994
|
+
return kind === "stream" || spec.stream === true ? "stream" : kind;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// .dist-releases/1784860966076-1188772/src/relay/client.js
|
|
1923
1998
|
var JSON_CONTENT_TYPE = "application/json";
|
|
1924
1999
|
var MAX_MAILBOX_LIMIT = 16;
|
|
1925
2000
|
var MAX_WAIT_TIMEOUT_SEC = 120;
|
|
@@ -1954,6 +2029,44 @@ class RelayClient {
|
|
|
1954
2029
|
close() {
|
|
1955
2030
|
this.httpClient?.close();
|
|
1956
2031
|
}
|
|
2032
|
+
async listWorkers(captainId, session) {
|
|
2033
|
+
requireCaptainId(captainId);
|
|
2034
|
+
if (session !== undefined)
|
|
2035
|
+
requireSegment(session, "session");
|
|
2036
|
+
const suffix = session === undefined ? "" : `?session=${segment(session)}`;
|
|
2037
|
+
const path3 = `/api/v1/captains/${segment(captainId)}/workers${suffix}`;
|
|
2038
|
+
const response = await this.requestJson("GET", path3);
|
|
2039
|
+
if (!Array.isArray(response.workers)) {
|
|
2040
|
+
throw new RelayProtocolError("GET", path3, "workers must be an array");
|
|
2041
|
+
}
|
|
2042
|
+
return { workers: response.workers.map((worker) => _parseWorkerRecord(worker, path3)) };
|
|
2043
|
+
}
|
|
2044
|
+
async submitJob(captainId, body) {
|
|
2045
|
+
requireCaptainId(captainId);
|
|
2046
|
+
_validateWorkerJobSubmit(body);
|
|
2047
|
+
const path3 = `/api/v1/captains/${segment(captainId)}/task`;
|
|
2048
|
+
const spec = {
|
|
2049
|
+
argv: [...body.argv],
|
|
2050
|
+
...body.timeoutSec === undefined ? {} : { timeout_sec: body.timeoutSec }
|
|
2051
|
+
};
|
|
2052
|
+
const response = await this.requestJson("POST", path3, { kind: "bash", worker: body.worker, spec });
|
|
2053
|
+
return _parseWorkerJobSubmitResponse(response, path3);
|
|
2054
|
+
}
|
|
2055
|
+
async getJobStatus(captainId, jobId) {
|
|
2056
|
+
return this._getJob(captainId, jobId);
|
|
2057
|
+
}
|
|
2058
|
+
async getJobResult(captainId, jobId, timeoutSec) {
|
|
2059
|
+
_validateWorkerJobWaitTimeout(timeoutSec);
|
|
2060
|
+
return this._getJob(captainId, jobId, timeoutSec);
|
|
2061
|
+
}
|
|
2062
|
+
async _getJob(captainId, jobId, timeoutSec) {
|
|
2063
|
+
requireCaptainId(captainId);
|
|
2064
|
+
requireSegment(jobId, "jobId");
|
|
2065
|
+
const base = `/api/v1/captains/${segment(captainId)}/task/${segment(jobId)}`;
|
|
2066
|
+
const path3 = timeoutSec === undefined ? base : `${base}?wait=true&timeout_sec=${timeoutSec}`;
|
|
2067
|
+
const timeoutMs = timeoutSec === undefined ? this.defaultTimeoutMs : (timeoutSec + 5) * 1000;
|
|
2068
|
+
return _parseWorkerJobResultResponse(await this.requestJson("GET", path3, undefined, timeoutMs), path3);
|
|
2069
|
+
}
|
|
1957
2070
|
async claimMailbox(agentName, options = {}) {
|
|
1958
2071
|
requireSegment(agentName, "agentName");
|
|
1959
2072
|
if (options.messageId)
|
|
@@ -2206,29 +2319,6 @@ class RelayClient {
|
|
|
2206
2319
|
const response = await this.requestJson("POST", path3, body);
|
|
2207
2320
|
return _parseTeamAgentSlot(response.agent_slot, "POST", path3);
|
|
2208
2321
|
}
|
|
2209
|
-
async getTeamRepository(captainId, session) {
|
|
2210
|
-
const path3 = `/api/v1/captains/${segment(captainId)}/sessions`;
|
|
2211
|
-
const response = await this.requestJson("GET", path3);
|
|
2212
|
-
if (!Array.isArray(response.sessions))
|
|
2213
|
-
throw new RelayProtocolError("GET", path3, "sessions must be an array");
|
|
2214
|
-
const rows = response.sessions.map((row) => _parseTeamRepository(row, "GET", path3));
|
|
2215
|
-
const repository = rows.find((row) => row.team_id === session);
|
|
2216
|
-
if (!repository)
|
|
2217
|
-
throw new RelayProtocolError("GET", path3, `team ${session} is missing`);
|
|
2218
|
-
return repository;
|
|
2219
|
-
}
|
|
2220
|
-
async setTeamRepositoryState(captainId, session, body) {
|
|
2221
|
-
const path3 = `${_captainSessionPath(captainId, session)}/repo-verification-state`;
|
|
2222
|
-
const response = await this.requestJson("PATCH", path3, body);
|
|
2223
|
-
return _parseTeamRepository(response.session, "PATCH", path3);
|
|
2224
|
-
}
|
|
2225
|
-
async createRepoVerificationRequest(captainId, session, hostId, gitRepoUrl) {
|
|
2226
|
-
const path3 = `${_captainSessionPath(captainId, session)}/repo-verification-requests`;
|
|
2227
|
-
const response = await this.requestJson("POST", path3, { host_id: hostId, git_repo_url: gitRepoUrl });
|
|
2228
|
-
const request = parseAgentStartRequest(response.request, "POST", path3);
|
|
2229
|
-
_requireRepoVerificationRequest(request, "POST", path3, session, hostId);
|
|
2230
|
-
return request;
|
|
2231
|
-
}
|
|
2232
2322
|
async getAgentStartRequest(captainId, session, requestId, timeoutMs) {
|
|
2233
2323
|
const path3 = `${_captainSessionPath(captainId, session)}/agent-start-requests/${segment(requestId)}`;
|
|
2234
2324
|
const response = await this.requestJson("GET", path3, undefined, timeoutMs);
|
|
@@ -2403,6 +2493,12 @@ class RelayClient {
|
|
|
2403
2493
|
const path3 = `/api/v1/rag/captains/${segment(captainId)}/teams/${segment(teamId)}/index`;
|
|
2404
2494
|
return parseRagMemoryIndex(await this.requestJson("GET", path3), "GET", path3);
|
|
2405
2495
|
}
|
|
2496
|
+
async getRagEntitlement(captainId, teamId) {
|
|
2497
|
+
requireCaptainId(captainId);
|
|
2498
|
+
requireSegment(teamId, "teamId");
|
|
2499
|
+
const path3 = `/api/v1/rag/captains/${segment(captainId)}/teams/${segment(teamId)}/entitlement`;
|
|
2500
|
+
return parseRagEntitlement(await this.requestJson("GET", path3), "GET", path3);
|
|
2501
|
+
}
|
|
2406
2502
|
async setRagCardPin(captainId, teamId, key, pinned) {
|
|
2407
2503
|
requireCaptainId(captainId);
|
|
2408
2504
|
requireSegment(teamId, "teamId");
|
|
@@ -2889,14 +2985,139 @@ function parseAgentMonitor(value) {
|
|
|
2889
2985
|
if (typeof value.context_checkpoint_percent !== "number" || !Number.isFinite(value.context_checkpoint_percent)) {
|
|
2890
2986
|
throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "context_checkpoint_percent must be a finite number");
|
|
2891
2987
|
}
|
|
2988
|
+
if (value.idle_presave_min !== null && (typeof value.idle_presave_min !== "number" || !Number.isFinite(value.idle_presave_min))) {
|
|
2989
|
+
throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "idle_presave_min must be a finite number or null");
|
|
2990
|
+
}
|
|
2991
|
+
const latestInbound = value.latest_message_inbound_at;
|
|
2992
|
+
const latestOutbound = value.latest_message_outbound_at;
|
|
2993
|
+
if (latestInbound !== null && typeof latestInbound !== "string") {
|
|
2994
|
+
throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "latest_message_inbound_at must be string or null");
|
|
2995
|
+
}
|
|
2996
|
+
if (latestOutbound !== null && typeof latestOutbound !== "string") {
|
|
2997
|
+
throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "latest_message_outbound_at must be string or null");
|
|
2998
|
+
}
|
|
2892
2999
|
return {
|
|
2893
3000
|
name: value.name,
|
|
2894
3001
|
engine: value.engine,
|
|
2895
3002
|
should_resume: value.should_resume,
|
|
2896
3003
|
session_handle: value.session_handle,
|
|
2897
|
-
context_checkpoint_percent: value.context_checkpoint_percent
|
|
3004
|
+
context_checkpoint_percent: value.context_checkpoint_percent,
|
|
3005
|
+
idle_presave_min: value.idle_presave_min,
|
|
3006
|
+
latest_message_inbound_at: latestInbound,
|
|
3007
|
+
latest_message_outbound_at: latestOutbound
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
function _parseWorkerRecord(value, path3) {
|
|
3011
|
+
const worker = _requireObject(value, "GET", path3, "worker");
|
|
3012
|
+
for (const field of ["id", "session", "name", "host", "consumer_status", "last_seen"]) {
|
|
3013
|
+
_requireStringField(worker, field, "GET", path3, "worker");
|
|
3014
|
+
}
|
|
3015
|
+
if (!isStringArray(worker.capabilities) || !isStringArray(worker.caps)) {
|
|
3016
|
+
throw new RelayProtocolError("GET", path3, "worker capabilities and caps must be string arrays");
|
|
3017
|
+
}
|
|
3018
|
+
if (!Number.isSafeInteger(worker.max_concurrency) || worker.max_concurrency < 1) {
|
|
3019
|
+
throw new RelayProtocolError("GET", path3, "worker.max_concurrency must be a positive integer");
|
|
3020
|
+
}
|
|
3021
|
+
if (typeof worker.online !== "boolean") {
|
|
3022
|
+
throw new RelayProtocolError("GET", path3, "worker.online must be boolean");
|
|
3023
|
+
}
|
|
3024
|
+
return worker;
|
|
3025
|
+
}
|
|
3026
|
+
function _validateWorkerJobSubmit(body) {
|
|
3027
|
+
requireSegment(body.worker, "worker");
|
|
3028
|
+
if (body.argv.length === 0 || !body.argv[0]?.trim())
|
|
3029
|
+
throw new Error("job argv requires a command");
|
|
3030
|
+
if (body.argv.some((arg) => typeof arg !== "string"))
|
|
3031
|
+
throw new Error("job argv must contain strings");
|
|
3032
|
+
if (body.timeoutSec !== undefined)
|
|
3033
|
+
_validateWorkerJobTimeout(body.timeoutSec);
|
|
3034
|
+
}
|
|
3035
|
+
function _validateWorkerJobTimeout(value) {
|
|
3036
|
+
if (!Number.isFinite(value) || value < 1 || value > MAX_JOB_TIMEOUT_SECONDS) {
|
|
3037
|
+
throw new Error(`timeoutSec must be a number from 1 to ${MAX_JOB_TIMEOUT_SECONDS}`);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
function _validateWorkerJobWaitTimeout(value) {
|
|
3041
|
+
const maximum = MAX_JOB_TIMEOUT_SECONDS + WORKER_JOB_RESULT_GRACE_SECONDS;
|
|
3042
|
+
if (!Number.isFinite(value) || value < 1 || value > maximum) {
|
|
3043
|
+
throw new Error(`result wait timeoutSec must be a number from 1 to ${maximum}`);
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
function _parseWorkerJobSubmitResponse(value, path3) {
|
|
3047
|
+
if (typeof value.id !== "string" || !value.id || value.status !== "queued") {
|
|
3048
|
+
throw new RelayProtocolError("POST", path3, "job submit response requires id and queued status");
|
|
3049
|
+
}
|
|
3050
|
+
if (value.warning !== undefined && typeof value.warning !== "string") {
|
|
3051
|
+
throw new RelayProtocolError("POST", path3, "job submit warning must be a string");
|
|
3052
|
+
}
|
|
3053
|
+
return {
|
|
3054
|
+
id: value.id,
|
|
3055
|
+
status: "queued",
|
|
3056
|
+
...value.warning === undefined ? {} : { warning: value.warning }
|
|
2898
3057
|
};
|
|
2899
3058
|
}
|
|
3059
|
+
function _parseWorkerJobResultResponse(value, path3) {
|
|
3060
|
+
const status = value.status;
|
|
3061
|
+
if (!_isWorkerJobStatus(status))
|
|
3062
|
+
throw new RelayProtocolError("GET", path3, "job status is invalid");
|
|
3063
|
+
for (const field of ["id", "created_at"])
|
|
3064
|
+
_requireStringField(value, field, "GET", path3, "job");
|
|
3065
|
+
_requireNullableStringFields(value, ["started_at", "finished_at", "submitter_agent_name"], path3);
|
|
3066
|
+
_requireNullableNumberFields(value, ["duration_sec"], path3);
|
|
3067
|
+
if (value.exit_code !== null && !Number.isSafeInteger(value.exit_code)) {
|
|
3068
|
+
throw new RelayProtocolError("GET", path3, "job.exit_code must be an integer or null");
|
|
3069
|
+
}
|
|
3070
|
+
const result = value.result === undefined ? undefined : _parseWorkerJobResult(value.result, path3);
|
|
3071
|
+
if (_isTerminalJobStatus(status) && (result === undefined || value.exit_code !== result.exit_code)) {
|
|
3072
|
+
throw new RelayProtocolError("GET", path3, "terminal job response requires a matching result");
|
|
3073
|
+
}
|
|
3074
|
+
return { ...value, status, ...result ? { result } : {} };
|
|
3075
|
+
}
|
|
3076
|
+
function _parseWorkerJobResult(value, path3) {
|
|
3077
|
+
const result = _requireObject(value, "GET", path3, "job result");
|
|
3078
|
+
for (const field of ["stdout", "stderr"])
|
|
3079
|
+
_requireStringField(result, field, "GET", path3, "job result", true);
|
|
3080
|
+
_requireNullableStringFields(result, ["started_at", "ended_at"], path3, "job result");
|
|
3081
|
+
if (!Number.isSafeInteger(result.exit_code)) {
|
|
3082
|
+
throw new RelayProtocolError("GET", path3, "job result.exit_code must be an integer");
|
|
3083
|
+
}
|
|
3084
|
+
_requireNullableNumberFields(result, ["duration_sec"], path3, "job result");
|
|
3085
|
+
if (!isStringArray(result.attached_file_paths)) {
|
|
3086
|
+
throw new RelayProtocolError("GET", path3, "job result.attached_file_paths must be a string array");
|
|
3087
|
+
}
|
|
3088
|
+
return result;
|
|
3089
|
+
}
|
|
3090
|
+
function _isWorkerJobStatus(value) {
|
|
3091
|
+
return value === "queued" || value === "running" || value === "done" || value === "failed" || value === "cancelled";
|
|
3092
|
+
}
|
|
3093
|
+
function _isTerminalJobStatus(value) {
|
|
3094
|
+
return value === "done" || value === "failed" || value === "cancelled";
|
|
3095
|
+
}
|
|
3096
|
+
function _requireObject(value, method, path3, label) {
|
|
3097
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3098
|
+
throw new RelayProtocolError(method, path3, `${label} must be an object`);
|
|
3099
|
+
}
|
|
3100
|
+
return value;
|
|
3101
|
+
}
|
|
3102
|
+
function _requireStringField(value, field, method, path3, label, allowEmpty = false) {
|
|
3103
|
+
if (typeof value[field] !== "string" || !allowEmpty && !value[field]) {
|
|
3104
|
+
throw new RelayProtocolError(method, path3, `${label}.${field} must be a string`);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
function _requireNullableStringFields(value, fields, path3, label = "job") {
|
|
3108
|
+
for (const field of fields) {
|
|
3109
|
+
if (value[field] !== null && typeof value[field] !== "string") {
|
|
3110
|
+
throw new RelayProtocolError("GET", path3, `${label}.${field} must be a string or null`);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
function _requireNullableNumberFields(value, fields, path3, label = "job") {
|
|
3115
|
+
for (const field of fields) {
|
|
3116
|
+
if (value[field] !== null && (typeof value[field] !== "number" || !Number.isFinite(value[field]))) {
|
|
3117
|
+
throw new RelayProtocolError("GET", path3, `${label}.${field} must be a finite number or null`);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
2900
3121
|
function parseScheduledEventResponse(value, method, path3) {
|
|
2901
3122
|
return { event: parseScheduledEvent(value.event, method, path3) };
|
|
2902
3123
|
}
|
|
@@ -2966,34 +3187,6 @@ function _parseTeamAgentSlot(value, method, path3) {
|
|
|
2966
3187
|
template_id: slot.template_id ?? null
|
|
2967
3188
|
};
|
|
2968
3189
|
}
|
|
2969
|
-
function _parseTeamRepository(value, method, path3) {
|
|
2970
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2971
|
-
throw new RelayProtocolError(method, path3, "session must be an object");
|
|
2972
|
-
const session = value;
|
|
2973
|
-
if (typeof session.id !== "string" || !session.id)
|
|
2974
|
-
throw new RelayProtocolError(method, path3, "session.id must be a non-empty string");
|
|
2975
|
-
if (session.git_repo_url !== null && typeof session.git_repo_url !== "string")
|
|
2976
|
-
throw new RelayProtocolError(method, path3, "session.git_repo_url must be a string or null");
|
|
2977
|
-
if (!_isTeamRepositoryStatus(session.git_repo_verification_status))
|
|
2978
|
-
throw new RelayProtocolError(method, path3, "session.git_repo_verification_status is invalid");
|
|
2979
|
-
if (session.git_repo_verification_reason !== null && typeof session.git_repo_verification_reason !== "string")
|
|
2980
|
-
throw new RelayProtocolError(method, path3, "session.git_repo_verification_reason must be a string or null");
|
|
2981
|
-
return {
|
|
2982
|
-
team_id: session.id,
|
|
2983
|
-
git_repo_url: session.git_repo_url,
|
|
2984
|
-
git_repo_verification_status: session.git_repo_verification_status,
|
|
2985
|
-
git_repo_verification_reason: session.git_repo_verification_reason
|
|
2986
|
-
};
|
|
2987
|
-
}
|
|
2988
|
-
function _isTeamRepositoryStatus(value) {
|
|
2989
|
-
return value === "unverified" || value === "pending" || value === "verified" || value === "failed";
|
|
2990
|
-
}
|
|
2991
|
-
function _requireRepoVerificationRequest(request, method, path3, session, hostId) {
|
|
2992
|
-
if (request.request_kind !== "repo_verification")
|
|
2993
|
-
throw new RelayProtocolError(method, path3, "request_kind must be repo_verification");
|
|
2994
|
-
if (request.session !== session || request.host_id !== hostId)
|
|
2995
|
-
throw new RelayProtocolError(method, path3, "request identity does not match repository verification target");
|
|
2996
|
-
}
|
|
2997
3190
|
function _parseSessionArtifact(value, method, path3) {
|
|
2998
3191
|
for (const field of ["id", "name", "sha256", "content_url"]) {
|
|
2999
3192
|
if (typeof value[field] !== "string" || !value[field])
|
|
@@ -3357,7 +3550,7 @@ function parseAgentStartRequest(value, method, path3) {
|
|
|
3357
3550
|
}
|
|
3358
3551
|
if (request.request_kind === undefined)
|
|
3359
3552
|
request.request_kind = "agent_start";
|
|
3360
|
-
if (request.request_kind !== "agent_start" && request.request_kind !== "
|
|
3553
|
+
if (request.request_kind !== "agent_start" && request.request_kind !== "worker_start") {
|
|
3361
3554
|
throw new RelayProtocolError(method, path3, "request.request_kind is invalid");
|
|
3362
3555
|
}
|
|
3363
3556
|
if (request.result !== undefined && (!request.result || typeof request.result !== "object" || Array.isArray(request.result))) {
|
|
@@ -3520,6 +3713,11 @@ function parseRagMemoryIndex(value, method, path3) {
|
|
|
3520
3713
|
}));
|
|
3521
3714
|
return { enabled: value.enabled, degraded: value.degraded, text: value.text, budget_tokens: value.budget_tokens, total_cards: value.total_cards, omitted_cards: value.omitted_cards, sections };
|
|
3522
3715
|
}
|
|
3716
|
+
function parseRagEntitlement(value, method, path3) {
|
|
3717
|
+
if (typeof value.enabled !== "boolean")
|
|
3718
|
+
throw new RelayProtocolError(method, path3, "entitlement.enabled must be a boolean");
|
|
3719
|
+
return { enabled: value.enabled };
|
|
3720
|
+
}
|
|
3523
3721
|
function parseManagedMaterializationResponse(value, method, path3, cursor) {
|
|
3524
3722
|
if (value === "not_modified_304") {
|
|
3525
3723
|
if (!cursor?.trim())
|
|
@@ -3849,41 +4047,10 @@ function redact(value, token) {
|
|
|
3849
4047
|
function bytesToBase64(bytes) {
|
|
3850
4048
|
return Buffer2.from(bytes).toString("base64");
|
|
3851
4049
|
}
|
|
3852
|
-
// .dist-releases/
|
|
4050
|
+
// .dist-releases/1784860966076-1188772/src/relay/config.js
|
|
3853
4051
|
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
3854
4052
|
import { homedir as homedir3 } from "node:os";
|
|
3855
4053
|
import path3 from "node:path";
|
|
3856
|
-
|
|
3857
|
-
// .dist-releases/1784772051951-3107696/src/worker/protocol.js
|
|
3858
|
-
var DEFAULT_RELAY_URL = DEFAULT_PRODUCT_API_URL;
|
|
3859
|
-
var DEFAULT_WORKER = "host";
|
|
3860
|
-
var DEFAULT_WORKDIR = "~/sailorbridge-workdir";
|
|
3861
|
-
var DEFAULT_JOB_TIMEOUT_SECONDS = 300;
|
|
3862
|
-
var MAX_JOB_TIMEOUT_SECONDS = 24 * 60 * 60;
|
|
3863
|
-
var MIN_JOB_TIMEOUT_SECONDS = 1;
|
|
3864
|
-
var ACTIVE_JOB_KINDS = new Set(["apply_diff", "bash", "read_file", "write_file", "compose", "stream", "status"]);
|
|
3865
|
-
var READONLY_ALLOWED_KINDS = new Set(["read_file", "status"]);
|
|
3866
|
-
var WORKER_SOCK_CONNECT_MS = 1e4;
|
|
3867
|
-
var WORKER_PULL_READ_IDLE_MS = 45000;
|
|
3868
|
-
var WORKER_QUICK_READ_IDLE_MS = 15000;
|
|
3869
|
-
var WORKER_UPLOAD_READ_IDLE_MS = (MAX_JOB_TIMEOUT_SECONDS + 300) * 1000;
|
|
3870
|
-
var DRAIN_AFTER_KILL_MS = 5000;
|
|
3871
|
-
var WORKER_RETRY_INITIAL_MS = 1000;
|
|
3872
|
-
var WORKER_RETRY_MAX_MS = 30000;
|
|
3873
|
-
var WORKER_SESSION_REBUILD_FAILURES = 10;
|
|
3874
|
-
var MAX_WORKER_RESULT_OUTPUT_BYTES = 256 * 1024;
|
|
3875
|
-
function coerceJobTimeoutSeconds(spec) {
|
|
3876
|
-
const value = spec.timeout_sec === undefined ? DEFAULT_JOB_TIMEOUT_SECONDS : spec.timeout_sec;
|
|
3877
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
3878
|
-
throw new Error("spec.timeout_sec must be a finite positive number");
|
|
3879
|
-
}
|
|
3880
|
-
return Math.max(MIN_JOB_TIMEOUT_SECONDS, Math.min(value, MAX_JOB_TIMEOUT_SECONDS));
|
|
3881
|
-
}
|
|
3882
|
-
function effectiveJobKind(kind, spec) {
|
|
3883
|
-
return kind === "stream" || spec.stream === true ? "stream" : kind;
|
|
3884
|
-
}
|
|
3885
|
-
|
|
3886
|
-
// .dist-releases/1784772051951-3107696/src/relay/config.js
|
|
3887
4054
|
function loadRelayConfig(env = process.env) {
|
|
3888
4055
|
const envRelayUrl = nonEmpty(env.SAILORBRIDGE_RELAY_URL);
|
|
3889
4056
|
const envBearerToken = nonEmpty(env.SAILORBRIDGE_TOKEN);
|
|
@@ -3955,7 +4122,7 @@ function storedCaptainId(value) {
|
|
|
3955
4122
|
}
|
|
3956
4123
|
return;
|
|
3957
4124
|
}
|
|
3958
|
-
// .dist-releases/
|
|
4125
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/retirement.js
|
|
3959
4126
|
var DEFAULT_THRESHOLD = 3;
|
|
3960
4127
|
var DEFAULT_WINDOW_MS = 30000;
|
|
3961
4128
|
var DEFAULT_COOLDOWN_MS = 1000;
|
|
@@ -4030,7 +4197,7 @@ function isAuthoritativeRetirementReason(reason) {
|
|
|
4030
4197
|
return reason === "monitor_fetch" || reason === "agent_session_patch";
|
|
4031
4198
|
}
|
|
4032
4199
|
|
|
4033
|
-
// .dist-releases/
|
|
4200
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/wait.js
|
|
4034
4201
|
async function waitForWakeableTimeout(ms, setWake, sleep3) {
|
|
4035
4202
|
if (sleep3) {
|
|
4036
4203
|
await Promise.race([
|
|
@@ -4061,7 +4228,7 @@ async function waitForWakeableTimeout(ms, setWake, sleep3) {
|
|
|
4061
4228
|
setWake(null);
|
|
4062
4229
|
}
|
|
4063
4230
|
|
|
4064
|
-
// .dist-releases/
|
|
4231
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/mailbox.js
|
|
4065
4232
|
var CLEAR_TAG = "CLEAR";
|
|
4066
4233
|
var REFRESH_TAG = "WORKFLOW-REFRESH";
|
|
4067
4234
|
var SYNC_TAG = "SYNC";
|
|
@@ -4597,38 +4764,79 @@ function ordinaryDeliveryAttempt(error) {
|
|
|
4597
4764
|
function _isMissingModerator(error) {
|
|
4598
4765
|
return error instanceof RelayHttpError && error.status === 400 && error.body.includes("no agents match");
|
|
4599
4766
|
}
|
|
4600
|
-
// .dist-releases/
|
|
4767
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/runtime.js
|
|
4601
4768
|
import path9 from "node:path";
|
|
4602
4769
|
import { execFile } from "node:child_process";
|
|
4603
4770
|
import { promisify } from "node:util";
|
|
4604
4771
|
|
|
4605
|
-
// .dist-releases/
|
|
4772
|
+
// .dist-releases/1784860966076-1188772/src/host/managed-materialization.js
|
|
4606
4773
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
4607
4774
|
import { constants as fsConstants } from "node:fs";
|
|
4608
4775
|
import { lstat, mkdir as mkdir3, open, readdir, rename, rm as rm2 } from "node:fs/promises";
|
|
4609
4776
|
import { homedir as homedir5 } from "node:os";
|
|
4610
4777
|
import path5 from "node:path";
|
|
4611
4778
|
|
|
4612
|
-
// .dist-releases/
|
|
4613
|
-
import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
|
|
4779
|
+
// .dist-releases/1784860966076-1188772/src/host/memory-stub.js
|
|
4780
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
|
|
4614
4781
|
import { homedir as homedir4 } from "node:os";
|
|
4615
4782
|
import path4 from "node:path";
|
|
4616
|
-
var
|
|
4617
|
-
var
|
|
4618
|
-
var
|
|
4619
|
-
|
|
4783
|
+
var TEAM_MEMORY_STUB_BEGIN = "<!-- sailorbridge-team-memory-stub:begin -->";
|
|
4784
|
+
var TEAM_MEMORY_STUB_END = "<!-- sailorbridge-team-memory-stub:end -->";
|
|
4785
|
+
var LEGACY_STUB_BEGIN = "<!-- sailorbridge-rag-memory-stub:begin -->";
|
|
4786
|
+
var LEGACY_STUB_END = "<!-- sailorbridge-rag-memory-stub:end -->";
|
|
4787
|
+
var STUB_LOCAL_MEMORY = [
|
|
4788
|
+
"- Your local memory files are yours: the product never collects, rewrites, or deletes them.",
|
|
4789
|
+
" Keep working notes and everything tied to your own role in local memory.",
|
|
4790
|
+
"- Role-bound and personal content must stay local. Never publish it to shared team stores:",
|
|
4791
|
+
" another member recalling your role's private memory is an identity mix-up."
|
|
4792
|
+
];
|
|
4793
|
+
var TEAM_MEMORY_STUB_ENTITLED = [
|
|
4794
|
+
"# SailorBridge Team Memory Protocol",
|
|
4620
4795
|
"",
|
|
4621
|
-
|
|
4622
|
-
"-
|
|
4623
|
-
"
|
|
4624
|
-
"-
|
|
4625
|
-
"
|
|
4626
|
-
"-
|
|
4627
|
-
"
|
|
4796
|
+
...STUB_LOCAL_MEMORY,
|
|
4797
|
+
"- Team memory is the team's shared knowledge and experience. Before a non-trivial",
|
|
4798
|
+
" question, run `sail memory search <query>` — someone may already know.",
|
|
4799
|
+
"- When you learn something the whole team benefits from, save it with",
|
|
4800
|
+
" `sail memory set <key> --stdin --source <source>` (or `--file <path>`).",
|
|
4801
|
+
"- Prefer team memory over rules when saving: rules are behavior norms; knowledge",
|
|
4802
|
+
" and experience belong in team memory.",
|
|
4803
|
+
"- If the memory service is unavailable, fail fast and say so; never present stale",
|
|
4804
|
+
" local content as the team's shared truth.",
|
|
4628
4805
|
""
|
|
4629
4806
|
].join(`
|
|
4630
4807
|
`);
|
|
4631
|
-
var
|
|
4808
|
+
var TEAM_MEMORY_STUB_UNENTITLED = [
|
|
4809
|
+
"# SailorBridge Team Memory Protocol",
|
|
4810
|
+
"",
|
|
4811
|
+
...STUB_LOCAL_MEMORY,
|
|
4812
|
+
"- Share lessons and working norms through team rules: propose a rule to your",
|
|
4813
|
+
" captain or moderator so the whole team inherits it.",
|
|
4814
|
+
"- The Team Memory add-on adds shared cross-member memory with corrections",
|
|
4815
|
+
" (`sail memory` commands); the captain can enable it from the console.",
|
|
4816
|
+
""
|
|
4817
|
+
].join(`
|
|
4818
|
+
`);
|
|
4819
|
+
function teamMemoryStub(entitled) {
|
|
4820
|
+
return entitled ? TEAM_MEMORY_STUB_ENTITLED : TEAM_MEMORY_STUB_UNENTITLED;
|
|
4821
|
+
}
|
|
4822
|
+
function teamMemoryStubMarked(entitled) {
|
|
4823
|
+
return `${TEAM_MEMORY_STUB_BEGIN}
|
|
4824
|
+
${teamMemoryStub(entitled)}${TEAM_MEMORY_STUB_END}
|
|
4825
|
+
`;
|
|
4826
|
+
}
|
|
4827
|
+
async function resolveTeamMemoryEntitlement(relay, captainId, teamId) {
|
|
4828
|
+
const captain = captainId?.trim();
|
|
4829
|
+
const team = teamId?.trim();
|
|
4830
|
+
if (!captain || !team)
|
|
4831
|
+
throw new Error("team memory entitlement requires captain_id and session");
|
|
4832
|
+
try {
|
|
4833
|
+
return (await relay.getRagEntitlement(captain, team)).enabled;
|
|
4834
|
+
} catch (error) {
|
|
4835
|
+
if (error instanceof RelayHttpError && error.status === 402)
|
|
4836
|
+
return false;
|
|
4837
|
+
throw error;
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4632
4840
|
async function materializeAgentMemoryStub(options) {
|
|
4633
4841
|
const engine = _memoryStubEngine(options.engine);
|
|
4634
4842
|
const homeDir = options.homeDir ? path4.resolve(_expandHome(options.homeDir)) : agentSandboxHome(options.agentName, engine, options.env ?? process.env);
|
|
@@ -4637,7 +4845,10 @@ async function materializeAgentMemoryStub(options) {
|
|
|
4637
4845
|
const mkdirFn = options.mkdir ?? mkdir2;
|
|
4638
4846
|
const writeFileFn = options.writeFile ?? writeFile3;
|
|
4639
4847
|
await mkdirFn(homeDir, { recursive: true });
|
|
4640
|
-
await
|
|
4848
|
+
const existing = await _readIfPresent(filePath, options.readFile ?? readFile2);
|
|
4849
|
+
const next = _spliceStubBlock(existing, teamMemoryStubMarked(options.teamMemoryEntitled));
|
|
4850
|
+
if (next !== existing)
|
|
4851
|
+
await writeFileFn(filePath, next, { mode: 384 });
|
|
4641
4852
|
return {
|
|
4642
4853
|
engine,
|
|
4643
4854
|
homeDir,
|
|
@@ -4652,6 +4863,38 @@ function agentSandboxHome(agentName, engine, env = process.env) {
|
|
|
4652
4863
|
function memoryStubFileName(engine) {
|
|
4653
4864
|
return engine === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
4654
4865
|
}
|
|
4866
|
+
function _spliceStubBlock(existing, block) {
|
|
4867
|
+
if (existing === null || !existing.trim())
|
|
4868
|
+
return block;
|
|
4869
|
+
const current = _replaceMarkedBlock(existing, TEAM_MEMORY_STUB_BEGIN, TEAM_MEMORY_STUB_END, block) ?? _replaceMarkedBlock(existing, LEGACY_STUB_BEGIN, LEGACY_STUB_END, block);
|
|
4870
|
+
if (current !== undefined)
|
|
4871
|
+
return current;
|
|
4872
|
+
const cleaned = _dropStrayMarkers(existing);
|
|
4873
|
+
if (!cleaned.trim())
|
|
4874
|
+
return block;
|
|
4875
|
+
return `${block}
|
|
4876
|
+
${cleaned.replace(/^\n+/u, "")}`;
|
|
4877
|
+
}
|
|
4878
|
+
function _replaceMarkedBlock(content, begin, end, block) {
|
|
4879
|
+
const start = content.indexOf(begin);
|
|
4880
|
+
const stop = content.indexOf(end);
|
|
4881
|
+
if (start < 0 || stop < start)
|
|
4882
|
+
return;
|
|
4883
|
+
const after = content.slice(stop + end.length).replace(/^\n/u, "");
|
|
4884
|
+
return `${content.slice(0, start)}${block}${after}`;
|
|
4885
|
+
}
|
|
4886
|
+
function _dropStrayMarkers(content) {
|
|
4887
|
+
return [TEAM_MEMORY_STUB_BEGIN, TEAM_MEMORY_STUB_END, LEGACY_STUB_BEGIN, LEGACY_STUB_END].reduce((text, marker) => text.split(marker).join(""), content);
|
|
4888
|
+
}
|
|
4889
|
+
async function _readIfPresent(filePath, readFileFn) {
|
|
4890
|
+
try {
|
|
4891
|
+
return await readFileFn(filePath, "utf8");
|
|
4892
|
+
} catch (error) {
|
|
4893
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
|
|
4894
|
+
return null;
|
|
4895
|
+
throw error;
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4655
4898
|
function _memoryStubEngine(value) {
|
|
4656
4899
|
if (value !== "claude" && value !== "codex") {
|
|
4657
4900
|
throw new Error("memory stub engine must be claude or codex");
|
|
@@ -4679,13 +4922,8 @@ function _safePathLabel(value, field) {
|
|
|
4679
4922
|
}
|
|
4680
4923
|
return trimmed;
|
|
4681
4924
|
}
|
|
4682
|
-
function _markedStub(body) {
|
|
4683
|
-
return `${RAG_MEMORY_STUB_BEGIN}
|
|
4684
|
-
${body}${RAG_MEMORY_STUB_END}
|
|
4685
|
-
`;
|
|
4686
|
-
}
|
|
4687
4925
|
|
|
4688
|
-
// .dist-releases/
|
|
4926
|
+
// .dist-releases/1784860966076-1188772/src/host/managed-materialization.js
|
|
4689
4927
|
var MANIFEST_FILE = ".sailorbridge-managed-manifest.json";
|
|
4690
4928
|
var BANNER_PREFIX = "# SailorBridge managed file";
|
|
4691
4929
|
var HASH_RE = /^sha256:[a-f0-9]{64}$/u;
|
|
@@ -5211,14 +5449,14 @@ function _expandHome2(value) {
|
|
|
5211
5449
|
return value;
|
|
5212
5450
|
}
|
|
5213
5451
|
|
|
5214
|
-
// .dist-releases/
|
|
5215
|
-
import { lstat as lstat2, readFile as
|
|
5452
|
+
// .dist-releases/1784860966076-1188772/src/host/credential-guardian.js
|
|
5453
|
+
import { lstat as lstat2, readFile as readFile3, rename as rename2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
|
|
5216
5454
|
import path6 from "node:path";
|
|
5217
5455
|
var DEFAULT_CREDENTIAL_SWEEP_INTERVAL_MS = 60000;
|
|
5218
5456
|
var DEFAULT_FAILURE_ALERT_THRESHOLD = 3;
|
|
5219
5457
|
var defaultFs = {
|
|
5220
5458
|
lstat: lstat2,
|
|
5221
|
-
readFile: (filePath) =>
|
|
5459
|
+
readFile: (filePath) => readFile3(filePath),
|
|
5222
5460
|
writeFile: (filePath, data, options) => writeFile4(filePath, data, options),
|
|
5223
5461
|
rename: rename2,
|
|
5224
5462
|
symlink: (target, linkPath) => symlink(target, linkPath),
|
|
@@ -5357,7 +5595,7 @@ function _tmpSibling(filePath) {
|
|
|
5357
5595
|
return path6.join(path6.dirname(filePath), `.${path6.basename(filePath)}.sb-guardian.${process.pid}.${tmpSequence}.tmp`);
|
|
5358
5596
|
}
|
|
5359
5597
|
|
|
5360
|
-
// .dist-releases/
|
|
5598
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/resolve.js
|
|
5361
5599
|
var DEFAULT_CAPTURE_LINES = 200;
|
|
5362
5600
|
var MAX_CAPTURE_LINES = 1000;
|
|
5363
5601
|
|
|
@@ -5563,7 +5801,7 @@ function defaultSleep(ms) {
|
|
|
5563
5801
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5564
5802
|
}
|
|
5565
5803
|
|
|
5566
|
-
// .dist-releases/
|
|
5804
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/transcript-backfill.js
|
|
5567
5805
|
import { createHash as createHash3 } from "node:crypto";
|
|
5568
5806
|
import {
|
|
5569
5807
|
closeSync,
|
|
@@ -6220,7 +6458,7 @@ function errorMessage4(error) {
|
|
|
6220
6458
|
return error instanceof Error ? error.message : String(error);
|
|
6221
6459
|
}
|
|
6222
6460
|
|
|
6223
|
-
// .dist-releases/
|
|
6461
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/usage-collector.js
|
|
6224
6462
|
import { statSync as statSync2 } from "node:fs";
|
|
6225
6463
|
import path8 from "node:path";
|
|
6226
6464
|
var SUPPORTED_USAGE_ENGINES = new Set(["claude", "codex"]);
|
|
@@ -6629,7 +6867,7 @@ function sourceUnreadableReason(source) {
|
|
|
6629
6867
|
}
|
|
6630
6868
|
}
|
|
6631
6869
|
|
|
6632
|
-
// .dist-releases/
|
|
6870
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/runtime.js
|
|
6633
6871
|
var RESOLVE_CAPTURE_LINES = 200;
|
|
6634
6872
|
var SHORT_LIVED_EXIT_MS = 5000;
|
|
6635
6873
|
var STARTUP_OBSERVATION_MS = 1e4;
|
|
@@ -7299,7 +7537,7 @@ function errorMessage5(error) {
|
|
|
7299
7537
|
function formatRestartCommand(command) {
|
|
7300
7538
|
return command.map((part) => /^[A-Za-z0-9_./:=@%+-]+$/.test(part) ? part : `'${part.replaceAll("'", "'\\''")}'`).join(" ");
|
|
7301
7539
|
}
|
|
7302
|
-
// .dist-releases/
|
|
7540
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/transport.js
|
|
7303
7541
|
var SUPPORTED_PHASE1_CLI_KINDS = ["claude", "codex"];
|
|
7304
7542
|
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.";
|
|
7305
7543
|
var PHASE1_TRANSPORT_GATE_MESSAGE = "Only tmux transport is available in this release; sdk, headless, and pty transports will open in the next release.";
|
|
@@ -7507,7 +7745,7 @@ function hasCodexSafetyDriverOptions(options) {
|
|
|
7507
7745
|
return options?.codex?.sandboxMode !== undefined || options?.codex?.approvalPolicy !== undefined || options?.codex?.yolo !== undefined;
|
|
7508
7746
|
}
|
|
7509
7747
|
|
|
7510
|
-
// .dist-releases/
|
|
7748
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/cli-options.js
|
|
7511
7749
|
function parseSuperviseArgs(argv) {
|
|
7512
7750
|
const split = argv.indexOf("--");
|
|
7513
7751
|
const flagArgs = split >= 0 ? argv.slice(0, split) : argv;
|
|
@@ -7659,7 +7897,7 @@ function optionalBoolean(value, label) {
|
|
|
7659
7897
|
return false;
|
|
7660
7898
|
throw new Error(`${label} must be true or false`);
|
|
7661
7899
|
}
|
|
7662
|
-
// .dist-releases/
|
|
7900
|
+
// .dist-releases/1784860966076-1188772/src/supervisor/tmux-availability.js
|
|
7663
7901
|
import { spawn as spawn3 } from "node:child_process";
|
|
7664
7902
|
import { createInterface } from "node:readline";
|
|
7665
7903
|
function isTmuxAvailable(spawnFn = spawn3) {
|
|
@@ -7776,26 +8014,29 @@ function _tmuxMissingMessage(platform2) {
|
|
|
7776
8014
|
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";
|
|
7777
8015
|
return `tmux is required in this release but was not found. Install tmux (${how}) and re-run.`;
|
|
7778
8016
|
}
|
|
7779
|
-
// .dist-releases/
|
|
8017
|
+
// .dist-releases/1784860966076-1188772/src/output-format.js
|
|
7780
8018
|
function parseOutputArgs(argv) {
|
|
8019
|
+
const separator = argv.indexOf("--");
|
|
8020
|
+
const localArgv = separator < 0 ? argv : argv.slice(0, separator);
|
|
8021
|
+
const remoteArgv = separator < 0 ? [] : argv.slice(separator);
|
|
7781
8022
|
const cleaned = [];
|
|
7782
8023
|
let format = "plain";
|
|
7783
8024
|
let seen = false;
|
|
7784
|
-
for (let index = 0;index <
|
|
7785
|
-
const token =
|
|
8025
|
+
for (let index = 0;index < localArgv.length; index += 1) {
|
|
8026
|
+
const token = localArgv[index];
|
|
7786
8027
|
if (token !== "--format" && !token.startsWith("--format=")) {
|
|
7787
8028
|
cleaned.push(token);
|
|
7788
8029
|
continue;
|
|
7789
8030
|
}
|
|
7790
8031
|
if (seen)
|
|
7791
8032
|
throw new Error("--format may only be provided once");
|
|
7792
|
-
const value = token === "--format" ?
|
|
8033
|
+
const value = token === "--format" ? localArgv[++index] : token.slice("--format=".length);
|
|
7793
8034
|
if (value !== "plain" && value !== "json")
|
|
7794
8035
|
throw new Error("--format must be plain or json");
|
|
7795
8036
|
format = value;
|
|
7796
8037
|
seen = true;
|
|
7797
8038
|
}
|
|
7798
|
-
return { argv: cleaned, format, provided: seen };
|
|
8039
|
+
return { argv: [...cleaned, ...remoteArgv], format, provided: seen };
|
|
7799
8040
|
}
|
|
7800
8041
|
function _renderOutput(value, format, plain) {
|
|
7801
8042
|
if (format === "json") {
|
|
@@ -7866,7 +8107,7 @@ ${normalized.split(`
|
|
|
7866
8107
|
`)}`;
|
|
7867
8108
|
}
|
|
7868
8109
|
|
|
7869
|
-
// .dist-releases/
|
|
8110
|
+
// .dist-releases/1784860966076-1188772/src/plain-output-fields.js
|
|
7870
8111
|
var PREVIEW_LIMIT = 20;
|
|
7871
8112
|
var UNSAFE_DISPLAY_CODE_POINT = /[\u007f-\u009f\u2028-\u202e\u2066-\u2069]/gu;
|
|
7872
8113
|
function renderTaskPlain(command, value) {
|
|
@@ -8274,7 +8515,7 @@ function _omissionLine(total) {
|
|
|
8274
8515
|
return total > PREVIEW_LIMIT ? `Omitted ${total - PREVIEW_LIMIT}; rerun with --format json for the complete response.` : undefined;
|
|
8275
8516
|
}
|
|
8276
8517
|
|
|
8277
|
-
// .dist-releases/
|
|
8518
|
+
// .dist-releases/1784860966076-1188772/src/host/cli.js
|
|
8278
8519
|
var HOST_RELAY_SUBCOMMANDS = new Set(["consume", "heartbeat", "list", "register"]);
|
|
8279
8520
|
async function runHostCommandFromCli(client, argv, config, io) {
|
|
8280
8521
|
const sub = argv[0] ?? "list";
|
|
@@ -8512,11 +8753,11 @@ function optionalDisplayName(value) {
|
|
|
8512
8753
|
const trimmed = value?.trim();
|
|
8513
8754
|
return trimmed || undefined;
|
|
8514
8755
|
}
|
|
8515
|
-
// .dist-releases/
|
|
8756
|
+
// .dist-releases/1784860966076-1188772/src/host/service.js
|
|
8516
8757
|
import { execFile as execFileCb } from "node:child_process";
|
|
8517
8758
|
import { createHash as createHash4 } from "node:crypto";
|
|
8518
8759
|
import { constants as fsConstants2 } from "node:fs";
|
|
8519
|
-
import { access, chmod as chmod2, lstat as lstat3, mkdir as mkdir4, readFile as
|
|
8760
|
+
import { access, chmod as chmod2, lstat as lstat3, mkdir as mkdir4, readFile as readFile4, rename as rename3, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
|
|
8520
8761
|
import { homedir as homedir7, platform as platform2, userInfo } from "node:os";
|
|
8521
8762
|
import path10 from "node:path";
|
|
8522
8763
|
import { promisify as promisify2 } from "node:util";
|
|
@@ -8736,7 +8977,7 @@ function serviceRuntime(deps) {
|
|
|
8736
8977
|
username: deps.username ?? info.username,
|
|
8737
8978
|
execFile: deps.execFile ?? execFileDefault,
|
|
8738
8979
|
writeFile: deps.writeFile ?? writeFile5,
|
|
8739
|
-
readFile: deps.readFile ??
|
|
8980
|
+
readFile: deps.readFile ?? readFile4,
|
|
8740
8981
|
mkdir: deps.mkdir ?? mkdir4,
|
|
8741
8982
|
rm: deps.rm ?? rm3,
|
|
8742
8983
|
access: deps.access ?? access,
|
|
@@ -8995,7 +9236,7 @@ function writeServiceLog(io, event, fields) {
|
|
|
8995
9236
|
io.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...fields ?? {} })}
|
|
8996
9237
|
`);
|
|
8997
9238
|
}
|
|
8998
|
-
// .dist-releases/
|
|
9239
|
+
// .dist-releases/1784860966076-1188772/src/host/git-remote.js
|
|
8999
9240
|
var GIT_SECRET_PATH_MARKERS = [
|
|
9000
9241
|
"token",
|
|
9001
9242
|
"credential",
|
|
@@ -9047,16 +9288,6 @@ function sanitizeGitOrigin(value) {
|
|
|
9047
9288
|
}
|
|
9048
9289
|
return hasCredentialPath(origin) ? null : origin;
|
|
9049
9290
|
}
|
|
9050
|
-
function gitRemoteRequiresGitHubAuth(value) {
|
|
9051
|
-
const remote = value.trim();
|
|
9052
|
-
if (remote.startsWith("git@"))
|
|
9053
|
-
return /^git@github\.com:/iu.test(remote);
|
|
9054
|
-
try {
|
|
9055
|
-
return new URL(remote).hostname.toLowerCase() === "github.com";
|
|
9056
|
-
} catch {
|
|
9057
|
-
return false;
|
|
9058
|
-
}
|
|
9059
|
-
}
|
|
9060
9291
|
function isAcceptedRemote(value, allowLocalPath) {
|
|
9061
9292
|
const remote = value.trim();
|
|
9062
9293
|
if (!remote || remote.length > 2048 || GIT_REMOTE_UNSAFE_RE.test(remote))
|
|
@@ -9097,7 +9328,7 @@ function looksLikeCredential(segment2) {
|
|
|
9097
9328
|
return value.length >= 32 && /^[A-Za-z0-9_-]+$/u.test(value);
|
|
9098
9329
|
}
|
|
9099
9330
|
|
|
9100
|
-
// .dist-releases/
|
|
9331
|
+
// .dist-releases/1784860966076-1188772/src/host/start-spec.js
|
|
9101
9332
|
var TRANSPORTS = ["auto", "sdk", "pty", "tmux", "headless"];
|
|
9102
9333
|
var SUPPORTED_PHASE1_ENGINES = ["claude", "codex"];
|
|
9103
9334
|
var ENGINE_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/u;
|
|
@@ -9347,7 +9578,7 @@ function _trimmed(value, label) {
|
|
|
9347
9578
|
throw new Error(`${label} must not be empty`);
|
|
9348
9579
|
return trimmed;
|
|
9349
9580
|
}
|
|
9350
|
-
// .dist-releases/
|
|
9581
|
+
// .dist-releases/1784860966076-1188772/src/host/workspace.js
|
|
9351
9582
|
import { spawn as spawn4 } from "node:child_process";
|
|
9352
9583
|
import { existsSync as existsSync4 } from "node:fs";
|
|
9353
9584
|
import { mkdir as mkdir5, readdir as readdir2, rename as rename4, stat } from "node:fs/promises";
|
|
@@ -9578,7 +9809,7 @@ function _safeSegment(value) {
|
|
|
9578
9809
|
throw new Error("workspace path segment is empty after sanitizing");
|
|
9579
9810
|
return segment2;
|
|
9580
9811
|
}
|
|
9581
|
-
// .dist-releases/
|
|
9812
|
+
// .dist-releases/1784860966076-1188772/src/host/workspace-inventory.js
|
|
9582
9813
|
import { access as access2 } from "node:fs/promises";
|
|
9583
9814
|
import { constants } from "node:fs";
|
|
9584
9815
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -9670,8 +9901,8 @@ async function canAccess(path12, mode, deps) {
|
|
|
9670
9901
|
return false;
|
|
9671
9902
|
}
|
|
9672
9903
|
}
|
|
9673
|
-
// .dist-releases/
|
|
9674
|
-
import { chmod as chmod3, mkdir as mkdir6, readFile as
|
|
9904
|
+
// .dist-releases/1784860966076-1188772/src/host/host-config.js
|
|
9905
|
+
import { chmod as chmod3, mkdir as mkdir6, readFile as readFile5, writeFile as writeFile6 } from "node:fs/promises";
|
|
9675
9906
|
import { homedir as homedir10 } from "node:os";
|
|
9676
9907
|
import path12 from "node:path";
|
|
9677
9908
|
var HOST_CONFIG_KIND = "sailorbridge-host-config";
|
|
@@ -9688,7 +9919,7 @@ function normalizeWorkspacePath(entry) {
|
|
|
9688
9919
|
async function loadHostConfig(configPath = defaultHostConfigPath()) {
|
|
9689
9920
|
let text;
|
|
9690
9921
|
try {
|
|
9691
|
-
text = await
|
|
9922
|
+
text = await readFile5(configPath, "utf8");
|
|
9692
9923
|
} catch (error) {
|
|
9693
9924
|
if (error.code === "ENOENT")
|
|
9694
9925
|
return { kind: HOST_CONFIG_KIND, workspaces: [] };
|
|
@@ -9755,7 +9986,7 @@ function _parseHostConfig(text, configPath) {
|
|
|
9755
9986
|
});
|
|
9756
9987
|
return { kind: HOST_CONFIG_KIND, workspaces };
|
|
9757
9988
|
}
|
|
9758
|
-
// .dist-releases/
|
|
9989
|
+
// .dist-releases/1784860966076-1188772/src/host/host-workspace-cli.js
|
|
9759
9990
|
async function runHostWorkspaceCommandFromCli(argv, io, deps = {}) {
|
|
9760
9991
|
const configPath = deps.configPath ?? defaultHostConfigPath();
|
|
9761
9992
|
const sub = argv[0] ?? "list";
|
|
@@ -9799,7 +10030,7 @@ function _renderAddPlain(result) {
|
|
|
9799
10030
|
return result.note ? `${status} ${result.path}
|
|
9800
10031
|
${result.note}` : `${status} ${result.path}`;
|
|
9801
10032
|
}
|
|
9802
|
-
// .dist-releases/
|
|
10033
|
+
// .dist-releases/1784860966076-1188772/src/host/agent-env.js
|
|
9803
10034
|
var RUNTIME_NATIVE_HOME_ENV = new Set(["CLAUDE_CONFIG_DIR", "CODEX_HOME"]);
|
|
9804
10035
|
function buildAgentEnv(options) {
|
|
9805
10036
|
const env = {};
|
|
@@ -9828,16 +10059,16 @@ function buildAgentEnv(options) {
|
|
|
9828
10059
|
}
|
|
9829
10060
|
return env;
|
|
9830
10061
|
}
|
|
9831
|
-
// .dist-releases/
|
|
10062
|
+
// .dist-releases/1784860966076-1188772/src/host/runtime-env.js
|
|
9832
10063
|
import path16 from "node:path";
|
|
9833
10064
|
|
|
9834
|
-
// .dist-releases/
|
|
9835
|
-
import { chmod as chmod4, mkdir as mkdir7, readFile as
|
|
10065
|
+
// .dist-releases/1784860966076-1188772/src/host/agent-defaults.js
|
|
10066
|
+
import { chmod as chmod4, mkdir as mkdir7, readFile as readFile7, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
|
|
9836
10067
|
import { homedir as homedir11 } from "node:os";
|
|
9837
10068
|
import path14 from "node:path";
|
|
9838
10069
|
|
|
9839
|
-
// .dist-releases/
|
|
9840
|
-
import { readFile as
|
|
10070
|
+
// .dist-releases/1784860966076-1188772/src/host/claude-settings.js
|
|
10071
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
9841
10072
|
import path13 from "node:path";
|
|
9842
10073
|
async function readHostClaudeSettings(claudeConfigDir) {
|
|
9843
10074
|
const settings = await _readSettingsObject(path13.join(claudeConfigDir, "settings.json"));
|
|
@@ -9846,7 +10077,7 @@ async function readHostClaudeSettings(claudeConfigDir) {
|
|
|
9846
10077
|
async function _readSettingsObject(filePath) {
|
|
9847
10078
|
let text;
|
|
9848
10079
|
try {
|
|
9849
|
-
text = await
|
|
10080
|
+
text = await readFile6(filePath, "utf8");
|
|
9850
10081
|
} catch (error) {
|
|
9851
10082
|
if (error.code === "ENOENT")
|
|
9852
10083
|
return {};
|
|
@@ -9877,7 +10108,7 @@ function _nonEmptyString(value) {
|
|
|
9877
10108
|
return typeof value === "string" && value.trim() ? value : null;
|
|
9878
10109
|
}
|
|
9879
10110
|
|
|
9880
|
-
// .dist-releases/
|
|
10111
|
+
// .dist-releases/1784860966076-1188772/src/host/agent-defaults.js
|
|
9881
10112
|
var CODEX_BLOCK_BEGIN = "# sailorbridge-terse-defaults:begin";
|
|
9882
10113
|
var CODEX_BLOCK_END = "# sailorbridge-terse-defaults:end";
|
|
9883
10114
|
var CODEX_MODEL_BLOCK_BEGIN = "# sailorbridge-host-model-settings:begin";
|
|
@@ -10600,7 +10831,7 @@ async function _claudeTrustRoots(projectPath) {
|
|
|
10600
10831
|
}
|
|
10601
10832
|
if (!stats.isFile())
|
|
10602
10833
|
return [projectPath];
|
|
10603
|
-
const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await
|
|
10834
|
+
const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile7(pointer, "utf8"))?.[1];
|
|
10604
10835
|
if (!gitdir) {
|
|
10605
10836
|
throw new Error(`workspace ${projectPath} has an unreadable .git pointer — cannot pre-trust its main repository for claude`);
|
|
10606
10837
|
}
|
|
@@ -10986,7 +11217,7 @@ async function _readTextIfExists2(filePath) {
|
|
|
10986
11217
|
return "";
|
|
10987
11218
|
throw error;
|
|
10988
11219
|
}
|
|
10989
|
-
return
|
|
11220
|
+
return readFile7(filePath, "utf8");
|
|
10990
11221
|
}
|
|
10991
11222
|
function _expandHome3(value) {
|
|
10992
11223
|
if (value === "~")
|
|
@@ -11007,7 +11238,7 @@ function _escapeRegExp(value) {
|
|
|
11007
11238
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
11008
11239
|
}
|
|
11009
11240
|
|
|
11010
|
-
// .dist-releases/
|
|
11241
|
+
// .dist-releases/1784860966076-1188772/src/host/engine-auth.js
|
|
11011
11242
|
import { lstat as lstat4, mkdir as mkdir8, readlink, realpath, stat as stat3, symlink as symlink2, unlink as unlink2 } from "node:fs/promises";
|
|
11012
11243
|
import { homedir as homedir12 } from "node:os";
|
|
11013
11244
|
import path15 from "node:path";
|
|
@@ -11097,7 +11328,7 @@ async function _ensureSymlink(linkPath, targetPath, sharedTargetPath) {
|
|
|
11097
11328
|
await symlink2(targetPath, linkPath);
|
|
11098
11329
|
}
|
|
11099
11330
|
|
|
11100
|
-
// .dist-releases/
|
|
11331
|
+
// .dist-releases/1784860966076-1188772/src/host/runtime-env.js
|
|
11101
11332
|
async function materializeAgentRuntimeEnv(options) {
|
|
11102
11333
|
const env = buildAgentEnv({
|
|
11103
11334
|
hostEnv: options.hostEnv,
|
|
@@ -11108,6 +11339,7 @@ async function materializeAgentRuntimeEnv(options) {
|
|
|
11108
11339
|
const memoryStub = await materializeAgentMemoryStub({
|
|
11109
11340
|
agentName: options.agentName,
|
|
11110
11341
|
engine: options.engine,
|
|
11342
|
+
teamMemoryEntitled: options.teamMemoryEntitled,
|
|
11111
11343
|
env: options.hostEnv,
|
|
11112
11344
|
homeDir
|
|
11113
11345
|
});
|
|
@@ -11136,7 +11368,7 @@ function _ensureRootSandboxEscape(env, engine, uid) {
|
|
|
11136
11368
|
function _currentUid() {
|
|
11137
11369
|
return typeof process.getuid === "function" ? process.getuid() : null;
|
|
11138
11370
|
}
|
|
11139
|
-
// .dist-releases/
|
|
11371
|
+
// .dist-releases/1784860966076-1188772/src/host/context-statusline.js
|
|
11140
11372
|
function renderClaudeContextStatusLine(payload) {
|
|
11141
11373
|
const percent = _usedPercentage(payload);
|
|
11142
11374
|
if (percent === null)
|
|
@@ -11174,179 +11406,10 @@ function _bar(percent) {
|
|
|
11174
11406
|
const filled = Math.min(10, Math.max(0, Math.trunc(percent / 10)));
|
|
11175
11407
|
return "█".repeat(filled) + "░".repeat(10 - filled);
|
|
11176
11408
|
}
|
|
11177
|
-
// .dist-releases/
|
|
11178
|
-
import { createHash as createHash5 } from "node:crypto";
|
|
11179
|
-
import { readdir as readdir3, readFile as readFile7, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
11180
|
-
import path17 from "node:path";
|
|
11181
|
-
function createMemorySweepState() {
|
|
11182
|
-
return { completedFingerprints: new Map };
|
|
11183
|
-
}
|
|
11184
|
-
async function sweepAgentMemory(options) {
|
|
11185
|
-
const captainId = _captainId2(options.captainId);
|
|
11186
|
-
const engine = _memoryEngine(options.engine);
|
|
11187
|
-
const sandbox = agentSandboxHome(options.agentName, engine, options.env ?? process.env);
|
|
11188
|
-
const candidates = await _memoryCandidates(sandbox, engine, options);
|
|
11189
|
-
let ingested = 0;
|
|
11190
|
-
let skipped = 0;
|
|
11191
|
-
for (const candidate of candidates) {
|
|
11192
|
-
const changed = await _sweepCandidate(candidate, { ...options, captainId, engine });
|
|
11193
|
-
if (changed)
|
|
11194
|
-
ingested += 1;
|
|
11195
|
-
else
|
|
11196
|
-
skipped += 1;
|
|
11197
|
-
}
|
|
11198
|
-
return { scanned: candidates.length, ingested, skipped };
|
|
11199
|
-
}
|
|
11200
|
-
async function _sweepCandidate(candidate, options) {
|
|
11201
|
-
const content = await (options.readFile ?? readFile7)(candidate.filePath, "utf8");
|
|
11202
|
-
const spill = _spillContent(content);
|
|
11203
|
-
if (spill.malformedStub) {
|
|
11204
|
-
options.logger?.("host_memory_spill_malformed_marker", {
|
|
11205
|
-
agent_name: options.agentName,
|
|
11206
|
-
engine: options.engine,
|
|
11207
|
-
mode: options.mode ?? "runtime",
|
|
11208
|
-
path: candidate.relativePath
|
|
11209
|
-
});
|
|
11210
|
-
return false;
|
|
11211
|
-
}
|
|
11212
|
-
if (!spill.text.trim())
|
|
11213
|
-
return false;
|
|
11214
|
-
const fingerprint = _fingerprint(candidate.filePath, spill.text, await (options.stat ?? stat4)(candidate.filePath));
|
|
11215
|
-
if (options.state?.completedFingerprints.get(candidate.filePath) === fingerprint)
|
|
11216
|
-
return false;
|
|
11217
|
-
const response = await options.relay.ingestRagDocument(options.captainId, options.teamId, _ingestBody(candidate, options, spill.text));
|
|
11218
|
-
await _verifyCards(options.relay, options.captainId, options.teamId, response);
|
|
11219
|
-
const latestContent = await (options.readFile ?? readFile7)(candidate.filePath, "utf8");
|
|
11220
|
-
if (latestContent !== content) {
|
|
11221
|
-
options.logger?.("host_memory_spill_changed_before_slim", {
|
|
11222
|
-
agent_name: options.agentName,
|
|
11223
|
-
engine: options.engine,
|
|
11224
|
-
mode: options.mode ?? "runtime",
|
|
11225
|
-
path: candidate.relativePath,
|
|
11226
|
-
cards: response.cards
|
|
11227
|
-
});
|
|
11228
|
-
return true;
|
|
11229
|
-
}
|
|
11230
|
-
await (options.writeFile ?? writeFile8)(candidate.filePath, RAG_MEMORY_STUB_MARKED, { mode: 384 });
|
|
11231
|
-
options.state?.completedFingerprints.set(candidate.filePath, fingerprint);
|
|
11232
|
-
options.logger?.("host_memory_spill_ingested", {
|
|
11233
|
-
agent_name: options.agentName,
|
|
11234
|
-
engine: options.engine,
|
|
11235
|
-
mode: options.mode ?? "runtime",
|
|
11236
|
-
path: candidate.relativePath,
|
|
11237
|
-
cards: response.cards
|
|
11238
|
-
});
|
|
11239
|
-
return true;
|
|
11240
|
-
}
|
|
11241
|
-
async function _memoryCandidates(sandbox, engine, deps) {
|
|
11242
|
-
const canonical = path17.join(sandbox, memoryStubFileName(engine));
|
|
11243
|
-
const candidates = await _fileExists(canonical, deps) ? [_candidate(sandbox, canonical)] : [];
|
|
11244
|
-
const projectRoot = path17.join(sandbox, "projects");
|
|
11245
|
-
for (const filePath of await _walkMemoryFiles(projectRoot, deps)) {
|
|
11246
|
-
if (filePath !== canonical)
|
|
11247
|
-
candidates.push(_candidate(sandbox, filePath));
|
|
11248
|
-
}
|
|
11249
|
-
return candidates;
|
|
11250
|
-
}
|
|
11251
|
-
async function _walkMemoryFiles(root, deps) {
|
|
11252
|
-
if (!await _dirExists(root, deps))
|
|
11253
|
-
return [];
|
|
11254
|
-
const out = [];
|
|
11255
|
-
for (const entry of await (deps.readdir ?? readdir3)(root, { withFileTypes: true })) {
|
|
11256
|
-
const filePath = path17.join(root, entry.name);
|
|
11257
|
-
if (entry.isDirectory())
|
|
11258
|
-
out.push(...await _walkMemoryFiles(filePath, deps));
|
|
11259
|
-
if (entry.isFile() && path17.basename(filePath) === "MEMORY.md" && path17.basename(path17.dirname(filePath)) === "memory") {
|
|
11260
|
-
out.push(filePath);
|
|
11261
|
-
}
|
|
11262
|
-
}
|
|
11263
|
-
return out;
|
|
11264
|
-
}
|
|
11265
|
-
function _spillContent(content) {
|
|
11266
|
-
const start = content.indexOf(RAG_MEMORY_STUB_BEGIN);
|
|
11267
|
-
const stop = content.indexOf(RAG_MEMORY_STUB_END);
|
|
11268
|
-
if (start < 0 && stop < 0)
|
|
11269
|
-
return { text: content, malformedStub: false };
|
|
11270
|
-
if (start < 0 || stop < start)
|
|
11271
|
-
return { text: "", malformedStub: true };
|
|
11272
|
-
return {
|
|
11273
|
-
text: _joinSpillParts(content.slice(0, start), content.slice(stop + RAG_MEMORY_STUB_END.length)),
|
|
11274
|
-
malformedStub: false
|
|
11275
|
-
};
|
|
11276
|
-
}
|
|
11277
|
-
function _joinSpillParts(beforeStub, afterStub) {
|
|
11278
|
-
const before = beforeStub.trim();
|
|
11279
|
-
const after = afterStub.trim();
|
|
11280
|
-
if (!before)
|
|
11281
|
-
return after ? `${after}
|
|
11282
|
-
` : "";
|
|
11283
|
-
if (!after)
|
|
11284
|
-
return `${before}
|
|
11285
|
-
`;
|
|
11286
|
-
return `${before}
|
|
11287
|
-
${after}
|
|
11288
|
-
`;
|
|
11289
|
-
}
|
|
11290
|
-
function _ingestBody(candidate, options, text) {
|
|
11291
|
-
return {
|
|
11292
|
-
name: `agent-memory/${options.agentName}/${options.engine}/${candidate.relativePath}`,
|
|
11293
|
-
text,
|
|
11294
|
-
type: "memory",
|
|
11295
|
-
max_chars: 800,
|
|
11296
|
-
citation: { agent: options.agentName, engine: options.engine, file: candidate.relativePath }
|
|
11297
|
-
};
|
|
11298
|
-
}
|
|
11299
|
-
async function _verifyCards(relay, captainId, teamId, response) {
|
|
11300
|
-
if (response.cards < 1)
|
|
11301
|
-
throw new Error("RAG ingest returned no cards for non-empty memory spill");
|
|
11302
|
-
const cards = [];
|
|
11303
|
-
for (let index = 0;index < response.cards; index += 1) {
|
|
11304
|
-
cards.push(await relay.getRagCurrentCard(captainId, teamId, `${response.doc_id}#${String(index).padStart(2, "0")}`));
|
|
11305
|
-
}
|
|
11306
|
-
return cards;
|
|
11307
|
-
}
|
|
11308
|
-
function _candidate(sandbox, filePath) {
|
|
11309
|
-
return { filePath, relativePath: path17.relative(sandbox, filePath).split(path17.sep).join("/") };
|
|
11310
|
-
}
|
|
11311
|
-
function _fingerprint(filePath, content, stats) {
|
|
11312
|
-
const digest = createHash5("sha256").update(content).digest("hex");
|
|
11313
|
-
return `${filePath}:${stats.size}:${stats.mtimeMs}:${digest}`;
|
|
11314
|
-
}
|
|
11315
|
-
async function _fileExists(filePath, deps) {
|
|
11316
|
-
try {
|
|
11317
|
-
return (await (deps.stat ?? stat4)(filePath)).isFile();
|
|
11318
|
-
} catch (error) {
|
|
11319
|
-
if (_notFound(error))
|
|
11320
|
-
return false;
|
|
11321
|
-
throw error;
|
|
11322
|
-
}
|
|
11323
|
-
}
|
|
11324
|
-
async function _dirExists(filePath, deps) {
|
|
11325
|
-
try {
|
|
11326
|
-
return (await (deps.stat ?? stat4)(filePath)).isDirectory();
|
|
11327
|
-
} catch (error) {
|
|
11328
|
-
if (_notFound(error))
|
|
11329
|
-
return false;
|
|
11330
|
-
throw error;
|
|
11331
|
-
}
|
|
11332
|
-
}
|
|
11333
|
-
function _captainId2(value) {
|
|
11334
|
-
if (value?.trim())
|
|
11335
|
-
return value.trim();
|
|
11336
|
-
throw new Error("memory sweep requires descriptor captain_id");
|
|
11337
|
-
}
|
|
11338
|
-
function _memoryEngine(value) {
|
|
11339
|
-
if (value === "claude" || value === "codex")
|
|
11340
|
-
return value;
|
|
11341
|
-
throw new Error("memory sweep engine must be claude or codex");
|
|
11342
|
-
}
|
|
11343
|
-
function _notFound(error) {
|
|
11344
|
-
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
11345
|
-
}
|
|
11346
|
-
// .dist-releases/1784772051951-3107696/src/host/scratch-sweeper.js
|
|
11409
|
+
// .dist-releases/1784860966076-1188772/src/host/scratch-sweeper.js
|
|
11347
11410
|
import { execFile as execFile3 } from "node:child_process";
|
|
11348
|
-
import { lstat as lstat5, readdir as
|
|
11349
|
-
import
|
|
11411
|
+
import { lstat as lstat5, readdir as readdir3, realpath as realpath2, unlink as unlink3 } from "node:fs/promises";
|
|
11412
|
+
import path17 from "node:path";
|
|
11350
11413
|
import { promisify as promisify4 } from "node:util";
|
|
11351
11414
|
var execFileAsync3 = promisify4(execFile3);
|
|
11352
11415
|
var SCRATCH_SUBDIR = ".agent/tmp";
|
|
@@ -11360,8 +11423,8 @@ function scratchSweepEnabled(env) {
|
|
|
11360
11423
|
return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
|
|
11361
11424
|
}
|
|
11362
11425
|
async function sweepWorkspaceScratch(options) {
|
|
11363
|
-
const root =
|
|
11364
|
-
const tmpDir =
|
|
11426
|
+
const root = path17.resolve(options.workspaceRoot);
|
|
11427
|
+
const tmpDir = path17.join(root, SCRATCH_SUBDIR);
|
|
11365
11428
|
const state = await _tmpDirState(root, tmpDir);
|
|
11366
11429
|
if (state === "missing")
|
|
11367
11430
|
return EMPTY;
|
|
@@ -11399,7 +11462,7 @@ async function _trackedScratchFiles(root, options) {
|
|
|
11399
11462
|
const tracked = new Set;
|
|
11400
11463
|
for (const rel of stdout.split("\x00"))
|
|
11401
11464
|
if (rel)
|
|
11402
|
-
tracked.add(
|
|
11465
|
+
tracked.add(path17.join(root, rel));
|
|
11403
11466
|
return tracked;
|
|
11404
11467
|
} catch (error) {
|
|
11405
11468
|
options.logger?.("host_scratch_sweep_git_failed", { workspace: root, error: _errno(error) });
|
|
@@ -11408,8 +11471,8 @@ async function _trackedScratchFiles(root, options) {
|
|
|
11408
11471
|
}
|
|
11409
11472
|
async function _collectScratchFiles(dir, root, logger) {
|
|
11410
11473
|
const out = [];
|
|
11411
|
-
for (const entry of await
|
|
11412
|
-
const abs =
|
|
11474
|
+
for (const entry of await readdir3(dir, { withFileTypes: true })) {
|
|
11475
|
+
const abs = path17.join(dir, entry.name);
|
|
11413
11476
|
if (!_within(root, abs)) {
|
|
11414
11477
|
logger?.("host_scratch_sweep_out_of_bounds", { workspace: root, path: abs });
|
|
11415
11478
|
continue;
|
|
@@ -11475,8 +11538,8 @@ async function _runGit2(root, args) {
|
|
|
11475
11538
|
return String(stdout);
|
|
11476
11539
|
}
|
|
11477
11540
|
function _within(root, target) {
|
|
11478
|
-
const rel =
|
|
11479
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11541
|
+
const rel = path17.relative(root, target);
|
|
11542
|
+
return rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel);
|
|
11480
11543
|
}
|
|
11481
11544
|
function _isEnoent(error) {
|
|
11482
11545
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
@@ -11486,16 +11549,22 @@ function _errno(error) {
|
|
|
11486
11549
|
return String(error.code);
|
|
11487
11550
|
return error instanceof Error ? error.message : String(error);
|
|
11488
11551
|
}
|
|
11489
|
-
// .dist-releases/
|
|
11552
|
+
// .dist-releases/1784860966076-1188772/src/host/launcher.js
|
|
11490
11553
|
var MAX_TMUX_SESSION_SUFFIX = 100;
|
|
11491
11554
|
async function launchSupervisedAgent(input, deps = {}) {
|
|
11492
11555
|
const spec = parseAgentStartSpec(input.spec);
|
|
11493
11556
|
const logger = input.logger ?? (() => {});
|
|
11557
|
+
const relay = new RelayClient({
|
|
11558
|
+
relayUrl: input.descriptor.relay_url,
|
|
11559
|
+
bearerToken: input.descriptor.token,
|
|
11560
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
11561
|
+
});
|
|
11494
11562
|
const { env, credentialGuard } = await materializeAgentRuntimeEnv({
|
|
11495
11563
|
agentName: input.descriptor.agent_name,
|
|
11496
11564
|
engine: spec.engine,
|
|
11497
11565
|
hostEnv: input.hostEnv ?? process.env,
|
|
11498
11566
|
descriptor: input.descriptor,
|
|
11567
|
+
teamMemoryEntitled: await (deps.resolveTeamMemoryEntitlementFn ?? resolveTeamMemoryEntitlement)(relay, input.captainId, input.descriptor.session),
|
|
11499
11568
|
specEnv: spec.env
|
|
11500
11569
|
});
|
|
11501
11570
|
const cwd = await (deps.prepareWorkspaceFn ?? prepareWorkspace)({
|
|
@@ -11508,11 +11577,6 @@ async function launchSupervisedAgent(input, deps = {}) {
|
|
|
11508
11577
|
});
|
|
11509
11578
|
await _trustPreparedWorkspace(input.descriptor.agent_name, spec.engine, env, cwd);
|
|
11510
11579
|
const resolved = await _resolveTransport(spec, input.descriptor, cwd, logger, deps.ensureTmuxTransportFn, deps.tmuxSessionExistsFn, input.recoveryTmuxSession);
|
|
11511
|
-
const relay = new RelayClient({
|
|
11512
|
-
relayUrl: input.descriptor.relay_url,
|
|
11513
|
-
bearerToken: input.descriptor.token,
|
|
11514
|
-
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
11515
|
-
});
|
|
11516
11580
|
const runtime = (deps.createRuntime ?? ((options) => new AgentSupervisorRuntime(options)))({
|
|
11517
11581
|
agentName: input.descriptor.agent_name,
|
|
11518
11582
|
displayName: input.descriptor.agent_name,
|
|
@@ -11636,90 +11700,10 @@ async function _tmuxSessionExists(session) {
|
|
|
11636
11700
|
throw error;
|
|
11637
11701
|
}
|
|
11638
11702
|
}
|
|
11639
|
-
// .dist-releases/
|
|
11640
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
11641
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
11642
|
-
import path19 from "node:path";
|
|
11643
|
-
import { spawn as spawn5 } from "node:child_process";
|
|
11644
|
-
var COMMAND_TIMEOUT_MS2 = 30000;
|
|
11645
|
-
async function verifyTeamRepository(gitRepoUrl, hostId, deps = {}) {
|
|
11646
|
-
const command = deps.runCommand ?? runCommand;
|
|
11647
|
-
const commandOutput = deps.runCommandOutput ?? runCommandOutput;
|
|
11648
|
-
const trimmed = gitRepoUrl.trim();
|
|
11649
|
-
if (!isAcceptedTeamGitRemote(trimmed))
|
|
11650
|
-
throw new Error("git repository URL is not accepted by SailorBridge hardening rules");
|
|
11651
|
-
if (gitRemoteRequiresGitHubAuth(trimmed)) {
|
|
11652
|
-
await command(["gh", "auth", "status"], process.cwd(), "gh auth status failed; install and authenticate GitHub CLI on the Moderator host");
|
|
11653
|
-
}
|
|
11654
|
-
const readProbe = await commandOutput(["git", "ls-remote", "--symref", trimmed, "HEAD"], process.cwd(), "git read probe failed");
|
|
11655
|
-
const mainRef = _defaultBranchFromSymrefProbe(readProbe);
|
|
11656
|
-
const temp = mkdtempSync(path19.join(tmpdir2(), "sailorbridge-repo-verify-"));
|
|
11657
|
-
try {
|
|
11658
|
-
await command(["git", "init"], temp, "git init failed for write probe");
|
|
11659
|
-
await command(["git", "config", "user.email", "sailorbridge-verify@example.invalid"], temp, "git config failed for write probe");
|
|
11660
|
-
await command(["git", "config", "user.name", "SailorBridge Verify"], temp, "git config failed for write probe");
|
|
11661
|
-
await command(["git", "commit", "--allow-empty", "-m", "sailorbridge verify"], temp, "git commit failed for write probe");
|
|
11662
|
-
await command(["git", "push", "--dry-run", trimmed, "HEAD:refs/heads/sailorbridge-verify"], temp, "git write probe failed");
|
|
11663
|
-
} finally {
|
|
11664
|
-
rmSync(temp, { recursive: true, force: true });
|
|
11665
|
-
}
|
|
11666
|
-
return {
|
|
11667
|
-
verified: true,
|
|
11668
|
-
reason: "git read and dry-run write probes succeeded",
|
|
11669
|
-
verified_at: (deps.now ?? (() => new Date))().toISOString(),
|
|
11670
|
-
verification_host_id: hostId,
|
|
11671
|
-
...mainRef ? { main_ref: mainRef } : {}
|
|
11672
|
-
};
|
|
11673
|
-
}
|
|
11674
|
-
function _defaultBranchFromSymrefProbe(output) {
|
|
11675
|
-
const match = /^ref:\s+refs\/heads\/(\S+)\s+HEAD$/mu.exec(output);
|
|
11676
|
-
return match ? match[1] : null;
|
|
11677
|
-
}
|
|
11678
|
-
async function runCommand(argv, cwd, failurePrefix) {
|
|
11679
|
-
await runCommandOutput(argv, cwd, failurePrefix);
|
|
11680
|
-
}
|
|
11681
|
-
async function runCommandOutput(argv, cwd, failurePrefix) {
|
|
11682
|
-
const result = await execArgv2(argv, cwd);
|
|
11683
|
-
if (result.exitCode === 0)
|
|
11684
|
-
return result.stdout;
|
|
11685
|
-
const detail = [result.stderr.trim(), result.stdout.trim()].filter(Boolean).join(`
|
|
11686
|
-
`).slice(0, 500);
|
|
11687
|
-
throw new Error(detail ? `${failurePrefix}: ${detail}` : failurePrefix);
|
|
11688
|
-
}
|
|
11689
|
-
function execArgv2(argv, cwd) {
|
|
11690
|
-
return new Promise((resolve2) => {
|
|
11691
|
-
const proc = spawn5(argv[0], argv.slice(1), { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
11692
|
-
const stdout = [];
|
|
11693
|
-
const stderr = [];
|
|
11694
|
-
let settled = false;
|
|
11695
|
-
const finish = (exitCode, extraStderr = "") => {
|
|
11696
|
-
if (settled)
|
|
11697
|
-
return;
|
|
11698
|
-
settled = true;
|
|
11699
|
-
clearTimeout(timer);
|
|
11700
|
-
resolve2({
|
|
11701
|
-
exitCode,
|
|
11702
|
-
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
11703
|
-
stderr: `${Buffer.concat(stderr).toString("utf8")}${extraStderr}`
|
|
11704
|
-
});
|
|
11705
|
-
};
|
|
11706
|
-
const timer = setTimeout(() => {
|
|
11707
|
-
proc.kill("SIGTERM");
|
|
11708
|
-
finish(124, `
|
|
11709
|
-
timeout expired`);
|
|
11710
|
-
}, COMMAND_TIMEOUT_MS2);
|
|
11711
|
-
proc.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
11712
|
-
proc.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
11713
|
-
proc.on("error", (error) => finish(127, `
|
|
11714
|
-
${error.message}`));
|
|
11715
|
-
proc.on("close", (code) => finish(code ?? 1));
|
|
11716
|
-
});
|
|
11717
|
-
}
|
|
11718
|
-
|
|
11719
|
-
// .dist-releases/1784772051951-3107696/src/worker/capability.js
|
|
11720
|
-
import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
11703
|
+
// .dist-releases/1784860966076-1188772/src/worker/capability.js
|
|
11704
|
+
import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
11721
11705
|
import os from "node:os";
|
|
11722
|
-
import
|
|
11706
|
+
import path18 from "node:path";
|
|
11723
11707
|
import { spawnSync } from "node:child_process";
|
|
11724
11708
|
var TOOLS = ["podman", "git", "kubectl", "perf", "bpftrace"];
|
|
11725
11709
|
var GPU_TAGS = new Set(["intel-b60", "nvidia-h100", "none"]);
|
|
@@ -11819,11 +11803,11 @@ function parseOsReleaseLine(line) {
|
|
|
11819
11803
|
}
|
|
11820
11804
|
function isReadonly(dir) {
|
|
11821
11805
|
try {
|
|
11822
|
-
const root =
|
|
11806
|
+
const root = path18.resolve(dir);
|
|
11823
11807
|
mkdirSync3(root, { recursive: true });
|
|
11824
|
-
const temp =
|
|
11825
|
-
writeFileSync2(
|
|
11826
|
-
|
|
11808
|
+
const temp = mkdtempSync(path18.join(root, ".sailorbridge-write-test-"));
|
|
11809
|
+
writeFileSync2(path18.join(temp, "probe"), "");
|
|
11810
|
+
rmSync(temp, { recursive: true, force: true });
|
|
11827
11811
|
return false;
|
|
11828
11812
|
} catch {
|
|
11829
11813
|
return true;
|
|
@@ -11847,10 +11831,10 @@ function commandExists(command) {
|
|
|
11847
11831
|
return commandPath(command) !== null;
|
|
11848
11832
|
}
|
|
11849
11833
|
function commandPath(command) {
|
|
11850
|
-
if (command.includes(
|
|
11834
|
+
if (command.includes(path18.sep))
|
|
11851
11835
|
return command;
|
|
11852
|
-
for (const entry of (process.env.PATH ?? "").split(
|
|
11853
|
-
const candidate =
|
|
11836
|
+
for (const entry of (process.env.PATH ?? "").split(path18.delimiter)) {
|
|
11837
|
+
const candidate = path18.join(entry, command);
|
|
11854
11838
|
try {
|
|
11855
11839
|
accessSync(candidate, constants2.X_OK);
|
|
11856
11840
|
return candidate;
|
|
@@ -11859,7 +11843,7 @@ function commandPath(command) {
|
|
|
11859
11843
|
return null;
|
|
11860
11844
|
}
|
|
11861
11845
|
|
|
11862
|
-
// .dist-releases/
|
|
11846
|
+
// .dist-releases/1784860966076-1188772/src/host/worker-start-spec.js
|
|
11863
11847
|
var MAX_TAGS = 32;
|
|
11864
11848
|
var TAG_PATTERN = /^[a-z0-9][a-z0-9=._:/-]{0,63}$/iu;
|
|
11865
11849
|
var RESERVED_TAG_KEYS = new Set(["os", "kernel", "arch", "gpu", "mode", "role", "tool"]);
|
|
@@ -11919,16 +11903,16 @@ function _trimmed2(value, label) {
|
|
|
11919
11903
|
return trimmed;
|
|
11920
11904
|
}
|
|
11921
11905
|
|
|
11922
|
-
// .dist-releases/
|
|
11906
|
+
// .dist-releases/1784860966076-1188772/src/host/worker-launcher.js
|
|
11923
11907
|
import { statSync as statSync3 } from "node:fs";
|
|
11924
11908
|
import os4 from "node:os";
|
|
11925
|
-
import
|
|
11926
|
-
// .dist-releases/
|
|
11909
|
+
import path21 from "node:path";
|
|
11910
|
+
// .dist-releases/1784860966076-1188772/src/worker/client.js
|
|
11927
11911
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
11928
11912
|
import os3 from "node:os";
|
|
11929
|
-
import
|
|
11913
|
+
import path20 from "node:path";
|
|
11930
11914
|
|
|
11931
|
-
// .dist-releases/
|
|
11915
|
+
// .dist-releases/1784860966076-1188772/src/worker/errors.js
|
|
11932
11916
|
class LeaseFencingRejected extends Error {
|
|
11933
11917
|
status;
|
|
11934
11918
|
constructor(status, message) {
|
|
@@ -11942,10 +11926,10 @@ class WorkerHttpError extends Error {
|
|
|
11942
11926
|
method;
|
|
11943
11927
|
path;
|
|
11944
11928
|
status;
|
|
11945
|
-
constructor(method,
|
|
11929
|
+
constructor(method, path19, status, message) {
|
|
11946
11930
|
super(message || `HTTP ${status}`);
|
|
11947
11931
|
this.method = method;
|
|
11948
|
-
this.path =
|
|
11932
|
+
this.path = path19;
|
|
11949
11933
|
this.status = status;
|
|
11950
11934
|
this.name = "WorkerHttpError";
|
|
11951
11935
|
}
|
|
@@ -11954,10 +11938,10 @@ class WorkerHttpError extends Error {
|
|
|
11954
11938
|
class WorkerNetworkError extends Error {
|
|
11955
11939
|
method;
|
|
11956
11940
|
path;
|
|
11957
|
-
constructor(method,
|
|
11941
|
+
constructor(method, path19, message) {
|
|
11958
11942
|
super(message);
|
|
11959
11943
|
this.method = method;
|
|
11960
|
-
this.path =
|
|
11944
|
+
this.path = path19;
|
|
11961
11945
|
this.name = "WorkerNetworkError";
|
|
11962
11946
|
}
|
|
11963
11947
|
}
|
|
@@ -11965,14 +11949,14 @@ var LEASE_FENCING_BAD_REQUEST_MESSAGES = [
|
|
|
11965
11949
|
"lease_token is required",
|
|
11966
11950
|
"lease_token must be a non-empty string"
|
|
11967
11951
|
];
|
|
11968
|
-
function raiseForLeaseFencingOrStatus(method,
|
|
11952
|
+
function raiseForLeaseFencingOrStatus(method, path19, status, body) {
|
|
11969
11953
|
if (status === 409)
|
|
11970
11954
|
throw new LeaseFencingRejected(status, body);
|
|
11971
11955
|
if (status === 400 && LEASE_FENCING_BAD_REQUEST_MESSAGES.some((message) => body.includes(message))) {
|
|
11972
11956
|
throw new LeaseFencingRejected(status, body);
|
|
11973
11957
|
}
|
|
11974
11958
|
if (status >= 300)
|
|
11975
|
-
throw new WorkerHttpError(method,
|
|
11959
|
+
throw new WorkerHttpError(method, path19, status, body);
|
|
11976
11960
|
}
|
|
11977
11961
|
function isNetworkError(error) {
|
|
11978
11962
|
if (error instanceof WorkerNetworkError)
|
|
@@ -11984,23 +11968,23 @@ function isNetworkError(error) {
|
|
|
11984
11968
|
const code = error.code;
|
|
11985
11969
|
return code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EPIPE" || error.name === "AbortError";
|
|
11986
11970
|
}
|
|
11987
|
-
function countsTowardWorkerSessionRebuild(
|
|
11988
|
-
if (
|
|
11971
|
+
function countsTowardWorkerSessionRebuild(path19) {
|
|
11972
|
+
if (path19 === "/api/v1/workers/register")
|
|
11989
11973
|
return false;
|
|
11990
|
-
if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(
|
|
11974
|
+
if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path19))
|
|
11991
11975
|
return false;
|
|
11992
11976
|
return true;
|
|
11993
11977
|
}
|
|
11994
11978
|
|
|
11995
|
-
// .dist-releases/
|
|
11996
|
-
import { spawn as
|
|
11997
|
-
import { mkdirSync as mkdirSync4, mkdtempSync as
|
|
11998
|
-
import { tmpdir as
|
|
11979
|
+
// .dist-releases/1784860966076-1188772/src/worker/executor.js
|
|
11980
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
11981
|
+
import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, realpathSync, rmSync as rmSync2, statfsSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
11982
|
+
import { tmpdir as tmpdir2, loadavg } from "node:os";
|
|
11999
11983
|
import os2 from "node:os";
|
|
12000
|
-
import
|
|
11984
|
+
import path19 from "node:path";
|
|
12001
11985
|
import { TextDecoder } from "node:util";
|
|
12002
11986
|
|
|
12003
|
-
// .dist-releases/
|
|
11987
|
+
// .dist-releases/1784860966076-1188772/src/worker/async-queue.js
|
|
12004
11988
|
class AsyncByteQueue {
|
|
12005
11989
|
maxSize;
|
|
12006
11990
|
values = [];
|
|
@@ -12060,7 +12044,7 @@ class AsyncByteQueue {
|
|
|
12060
12044
|
}
|
|
12061
12045
|
}
|
|
12062
12046
|
|
|
12063
|
-
// .dist-releases/
|
|
12047
|
+
// .dist-releases/1784860966076-1188772/src/worker/http.js
|
|
12064
12048
|
import { request as httpRequest } from "node:http";
|
|
12065
12049
|
import { request as httpsRequest } from "node:https";
|
|
12066
12050
|
import { URLSearchParams as URLSearchParams2 } from "node:url";
|
|
@@ -12090,27 +12074,27 @@ class WorkerHttpClient {
|
|
|
12090
12074
|
throw new Error("token is required");
|
|
12091
12075
|
this.relayUrl = relayUrl.replace(/\/+$/u, "");
|
|
12092
12076
|
}
|
|
12093
|
-
async getJson(
|
|
12077
|
+
async getJson(path19, query, timeouts, options = {}) {
|
|
12094
12078
|
const suffix = new URLSearchParams2(query).toString();
|
|
12095
|
-
return this.requestJson("GET", suffix ? `${
|
|
12079
|
+
return this.requestJson("GET", suffix ? `${path19}?${suffix}` : path19, undefined, timeouts, options);
|
|
12096
12080
|
}
|
|
12097
|
-
async postJson(
|
|
12098
|
-
return this.requestJson("POST",
|
|
12081
|
+
async postJson(path19, value, timeouts) {
|
|
12082
|
+
return this.requestJson("POST", path19, { type: "json", value }, timeouts, {});
|
|
12099
12083
|
}
|
|
12100
|
-
async postBytes(
|
|
12101
|
-
const response = await this.request("POST",
|
|
12102
|
-
raiseForLeaseFencingOrStatus("POST",
|
|
12084
|
+
async postBytes(path19, body, headers, timeouts) {
|
|
12085
|
+
const response = await this.request("POST", path19, { type: "bytes", value: body }, timeouts, headers);
|
|
12086
|
+
raiseForLeaseFencingOrStatus("POST", path19, response.status, response.text);
|
|
12103
12087
|
}
|
|
12104
|
-
async requestJson(method,
|
|
12105
|
-
const response = await this.request(method,
|
|
12106
|
-
const errorPath = pathOnly(
|
|
12088
|
+
async requestJson(method, path19, body, timeouts, options) {
|
|
12089
|
+
const response = await this.request(method, path19, body, timeouts, {});
|
|
12090
|
+
const errorPath = pathOnly(path19);
|
|
12107
12091
|
if (options.leaseFencing === false && response.status >= 300)
|
|
12108
12092
|
throw new WorkerHttpError(method, errorPath, response.status, response.text);
|
|
12109
12093
|
raiseForLeaseFencingOrStatus(method, errorPath, response.status, response.text);
|
|
12110
12094
|
return parseJson(method, errorPath, response.text);
|
|
12111
12095
|
}
|
|
12112
|
-
async request(method,
|
|
12113
|
-
const url = new URL(`${this.relayUrl}${
|
|
12096
|
+
async request(method, path19, body, timeouts, extraHeaders) {
|
|
12097
|
+
const url = new URL(`${this.relayUrl}${path19}`);
|
|
12114
12098
|
const headers = this.headers(body, extraHeaders);
|
|
12115
12099
|
return sendRequest(method, url, body, headers, timeouts, this.signal);
|
|
12116
12100
|
}
|
|
@@ -12237,7 +12221,7 @@ function waitForRequestDrain(req) {
|
|
|
12237
12221
|
req.once("close", onClose);
|
|
12238
12222
|
});
|
|
12239
12223
|
}
|
|
12240
|
-
function parseJson(method,
|
|
12224
|
+
function parseJson(method, path19, text) {
|
|
12241
12225
|
try {
|
|
12242
12226
|
const parsed = JSON.parse(text);
|
|
12243
12227
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
@@ -12246,15 +12230,15 @@ function parseJson(method, path21, text) {
|
|
|
12246
12230
|
return parsed;
|
|
12247
12231
|
} catch (error) {
|
|
12248
12232
|
if (error instanceof SyntaxError)
|
|
12249
|
-
throw new WorkerNetworkError(method,
|
|
12233
|
+
throw new WorkerNetworkError(method, path19, "response JSON is invalid");
|
|
12250
12234
|
throw error;
|
|
12251
12235
|
}
|
|
12252
12236
|
}
|
|
12253
|
-
function pathOnly(
|
|
12254
|
-
return
|
|
12237
|
+
function pathOnly(path19) {
|
|
12238
|
+
return path19.split("?", 1)[0] || path19;
|
|
12255
12239
|
}
|
|
12256
12240
|
|
|
12257
|
-
// .dist-releases/
|
|
12241
|
+
// .dist-releases/1784860966076-1188772/src/worker/stream-upload.js
|
|
12258
12242
|
async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId) {
|
|
12259
12243
|
if (!leaseToken.trim())
|
|
12260
12244
|
throw new Error("lease_token is required");
|
|
@@ -12264,7 +12248,7 @@ async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId
|
|
|
12264
12248
|
await session.http.postBytes(`/api/v1/jobs/${encodeURIComponent(jobId)}/stream-upload?${query.toString()}`, chunks, { "X-Lease-Token": leaseToken }, UPLOAD_TIMEOUTS);
|
|
12265
12249
|
}
|
|
12266
12250
|
|
|
12267
|
-
// .dist-releases/
|
|
12251
|
+
// .dist-releases/1784860966076-1188772/src/worker/executor.js
|
|
12268
12252
|
class StreamingProcessFailure extends Error {
|
|
12269
12253
|
exitCode;
|
|
12270
12254
|
stdout;
|
|
@@ -12336,8 +12320,8 @@ function sandboxPath(workdir, requested) {
|
|
|
12336
12320
|
if (requested.startsWith("/") || parts.length === 0 || parts.includes("..")) {
|
|
12337
12321
|
throw new Error("path must be a relative path inside the worker sandbox");
|
|
12338
12322
|
}
|
|
12339
|
-
const root = realpathSync(
|
|
12340
|
-
const target =
|
|
12323
|
+
const root = realpathSync(path19.resolve(workdir));
|
|
12324
|
+
const target = path19.resolve(root, ...parts);
|
|
12341
12325
|
if (!isPathInside(root, target)) {
|
|
12342
12326
|
throw new Error("path escapes worker sandbox");
|
|
12343
12327
|
}
|
|
@@ -12477,7 +12461,7 @@ async function collectAndUpload(session, jobId, stream, pipe, leaseToken, worker
|
|
|
12477
12461
|
return collected.join("");
|
|
12478
12462
|
}
|
|
12479
12463
|
function spawnProcess(argv, cwd) {
|
|
12480
|
-
return
|
|
12464
|
+
return spawn5(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
|
|
12481
12465
|
}
|
|
12482
12466
|
async function terminateProcess(proc) {
|
|
12483
12467
|
if (proc.pid === undefined)
|
|
@@ -12513,9 +12497,9 @@ function writeFileJob(workdir, spec, startedAt, started) {
|
|
|
12513
12497
|
if (typeof spec.content !== "string")
|
|
12514
12498
|
throw new Error("write_file spec.content must be a string");
|
|
12515
12499
|
const target = sandboxPath(workdir, String(spec.path ?? ""));
|
|
12516
|
-
mkdirSync4(
|
|
12500
|
+
mkdirSync4(path19.dirname(target), { recursive: true });
|
|
12517
12501
|
writeFileSync3(target, spec.content, "utf8");
|
|
12518
|
-
return output(0,
|
|
12502
|
+
return output(0, path19.relative(realpathSync(path19.resolve(workdir)), target), "", startedAt, started);
|
|
12519
12503
|
}
|
|
12520
12504
|
function readUtf8File(target) {
|
|
12521
12505
|
return decodeUtf8(readFileSync4(target));
|
|
@@ -12523,13 +12507,13 @@ function readUtf8File(target) {
|
|
|
12523
12507
|
async function applyDiff(workdir, spec, signal) {
|
|
12524
12508
|
if (typeof spec.diff_unified_text !== "string")
|
|
12525
12509
|
throw new Error("apply_diff spec.diff_unified_text must be a string");
|
|
12526
|
-
const dir =
|
|
12527
|
-
const patch =
|
|
12510
|
+
const dir = mkdtempSync2(path19.join(tmpdir2(), "sailorbridge-patch-"));
|
|
12511
|
+
const patch = path19.join(dir, "change.patch");
|
|
12528
12512
|
writeFileSync3(patch, spec.diff_unified_text, "utf8");
|
|
12529
12513
|
try {
|
|
12530
12514
|
return await runArgv(["patch", "-p1", "-i", patch], workdir, coerceJobTimeoutSeconds(spec) * 1000, signal);
|
|
12531
12515
|
} finally {
|
|
12532
|
-
|
|
12516
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
12533
12517
|
}
|
|
12534
12518
|
}
|
|
12535
12519
|
async function executeCompose(spec, workdir, startedAt, started, signal) {
|
|
@@ -12764,24 +12748,24 @@ function resolveExistingTarget(target) {
|
|
|
12764
12748
|
let current = target;
|
|
12765
12749
|
while (true) {
|
|
12766
12750
|
try {
|
|
12767
|
-
return
|
|
12751
|
+
return path19.resolve(realpathSync(current), ...missingParts.reverse());
|
|
12768
12752
|
} catch (error) {
|
|
12769
12753
|
if (error.code !== "ENOENT")
|
|
12770
12754
|
throw error;
|
|
12771
|
-
const parent =
|
|
12755
|
+
const parent = path19.dirname(current);
|
|
12772
12756
|
if (parent === current)
|
|
12773
12757
|
throw error;
|
|
12774
|
-
missingParts.push(
|
|
12758
|
+
missingParts.push(path19.basename(current));
|
|
12775
12759
|
current = parent;
|
|
12776
12760
|
}
|
|
12777
12761
|
}
|
|
12778
12762
|
}
|
|
12779
12763
|
function isPathInside(root, target) {
|
|
12780
|
-
const relative =
|
|
12781
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
12764
|
+
const relative = path19.relative(root, target);
|
|
12765
|
+
return relative === "" || !relative.startsWith("..") && !path19.isAbsolute(relative);
|
|
12782
12766
|
}
|
|
12783
12767
|
function prepareWorkdir(workdir) {
|
|
12784
|
-
const resolved =
|
|
12768
|
+
const resolved = path19.resolve(workdir);
|
|
12785
12769
|
mkdirSync4(resolved, { recursive: true });
|
|
12786
12770
|
return resolved;
|
|
12787
12771
|
}
|
|
@@ -12796,7 +12780,7 @@ function errorMessage6(error) {
|
|
|
12796
12780
|
return error instanceof Error ? error.message : String(error);
|
|
12797
12781
|
}
|
|
12798
12782
|
|
|
12799
|
-
// .dist-releases/
|
|
12783
|
+
// .dist-releases/1784860966076-1188772/src/worker/client.js
|
|
12800
12784
|
var TOKEN_FILES = [
|
|
12801
12785
|
"~/.config/sailorbridge/token",
|
|
12802
12786
|
"/etc/sailorbridge/token"
|
|
@@ -13135,7 +13119,7 @@ ${JSON.stringify(result)}\r
|
|
|
13135
13119
|
`);
|
|
13136
13120
|
for (const file of attachments) {
|
|
13137
13121
|
yield Buffer.from(`--${multipartBoundary}\r
|
|
13138
|
-
Content-Disposition: form-data; name="files"; filename="${
|
|
13122
|
+
Content-Disposition: form-data; name="files"; filename="${path20.basename(file)}"\r
|
|
13139
13123
|
\r
|
|
13140
13124
|
`);
|
|
13141
13125
|
yield readFileSync5(file);
|
|
@@ -13169,7 +13153,7 @@ function parseFlags2(argv) {
|
|
|
13169
13153
|
return values;
|
|
13170
13154
|
}
|
|
13171
13155
|
function expandHome2(value) {
|
|
13172
|
-
return value === "~" || value.startsWith("~/") ?
|
|
13156
|
+
return value === "~" || value.startsWith("~/") ? path20.join(os3.homedir(), value.slice(2)) : value;
|
|
13173
13157
|
}
|
|
13174
13158
|
function _sleep(ms, signal) {
|
|
13175
13159
|
if (signal?.aborted)
|
|
@@ -13193,7 +13177,7 @@ function errorName2(error) {
|
|
|
13193
13177
|
function errorMessage7(error) {
|
|
13194
13178
|
return error instanceof Error ? error.message : String(error);
|
|
13195
13179
|
}
|
|
13196
|
-
// .dist-releases/
|
|
13180
|
+
// .dist-releases/1784860966076-1188772/src/host/worker-launcher.js
|
|
13197
13181
|
var DEFAULT_WORKER_READY_TIMEOUT_MS = 60000;
|
|
13198
13182
|
async function launchSupervisedWorker(input, deps = {}) {
|
|
13199
13183
|
const logger = input.logger ?? (() => {});
|
|
@@ -13276,21 +13260,21 @@ function _requireWorkerName(descriptor) {
|
|
|
13276
13260
|
return worker;
|
|
13277
13261
|
}
|
|
13278
13262
|
function _resolveWorkdir(workdir, homeDir) {
|
|
13279
|
-
return workdir === "~" || workdir.startsWith("~/") ?
|
|
13263
|
+
return workdir === "~" || workdir.startsWith("~/") ? path21.join(homeDir, workdir.slice(2)) : workdir;
|
|
13280
13264
|
}
|
|
13281
13265
|
function _assertWorkdir(workdir) {
|
|
13282
|
-
let
|
|
13266
|
+
let stat4;
|
|
13283
13267
|
try {
|
|
13284
|
-
|
|
13268
|
+
stat4 = statSync3(workdir);
|
|
13285
13269
|
} catch {
|
|
13286
13270
|
throw new Error(`worker workdir does not exist: ${workdir}`);
|
|
13287
13271
|
}
|
|
13288
|
-
if (!
|
|
13272
|
+
if (!stat4.isDirectory())
|
|
13289
13273
|
throw new Error(`worker workdir is not a directory: ${workdir}`);
|
|
13290
13274
|
return workdir;
|
|
13291
13275
|
}
|
|
13292
13276
|
|
|
13293
|
-
// .dist-releases/
|
|
13277
|
+
// .dist-releases/1784860966076-1188772/src/host/daemon.js
|
|
13294
13278
|
async function runHostDaemon(options) {
|
|
13295
13279
|
const logger = options.logger ?? (() => {});
|
|
13296
13280
|
const sleep3 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
@@ -13299,7 +13283,6 @@ async function runHostDaemon(options) {
|
|
|
13299
13283
|
const workerRuntimes = new Map;
|
|
13300
13284
|
const cleanupLocks = new Map;
|
|
13301
13285
|
const startLocks = new Map;
|
|
13302
|
-
const memorySweepTargets = new Map;
|
|
13303
13286
|
const managedTargets = new Map;
|
|
13304
13287
|
const launch = options.launch ?? launchSupervisedAgent;
|
|
13305
13288
|
const launchWorker = options.launchWorker ?? launchSupervisedWorker;
|
|
@@ -13307,15 +13290,14 @@ async function runHostDaemon(options) {
|
|
|
13307
13290
|
const reclaim = options.reclaimOrphanWorkspaces ?? reclaimOrphanWorkspaces;
|
|
13308
13291
|
const identity = { captainId: options.captainId, hostId: options.hostId, displayName: options.displayName };
|
|
13309
13292
|
const redactSecrets = [...options.redactSecrets ?? []];
|
|
13310
|
-
const sweep = options.sweepAgentMemory ?? sweepAgentMemory;
|
|
13311
13293
|
const materializeManaged = options.materializeManagedFiles ?? materializeManagedFiles;
|
|
13312
13294
|
const now = options.now ?? (() => Date.now());
|
|
13313
|
-
const
|
|
13314
|
-
let
|
|
13295
|
+
const managedSyncIntervalMs = options.managedSyncIntervalMs ?? 6 * 60 * 60 * 1000;
|
|
13296
|
+
let lastManagedSyncAt = now();
|
|
13315
13297
|
const scratchSweep = options.sweepWorkspaceScratch ?? sweepWorkspaceScratch;
|
|
13316
13298
|
const scratchSweepIntervalMs = options.scratchSweepIntervalMs ?? 6 * 60 * 60 * 1000;
|
|
13317
13299
|
let lastScratchSweepAt = now();
|
|
13318
|
-
const reportRepoTips = options.reportRepoTips ??
|
|
13300
|
+
const reportRepoTips = options.reportRepoTips ?? createRepoTipReporter();
|
|
13319
13301
|
const repoTipReportIntervalMs = options.repoTipReportIntervalMs ?? 60 * 1000;
|
|
13320
13302
|
let lastRepoTipReportAt = now() - repoTipReportIntervalMs;
|
|
13321
13303
|
const reconcileIntervalMs = options.reconcileIntervalMs ?? 5 * 60 * 1000;
|
|
@@ -13360,9 +13342,7 @@ async function runHostDaemon(options) {
|
|
|
13360
13342
|
await waitForAgentCleanup(cleanupLocks, request.agent_name);
|
|
13361
13343
|
const spec = parseAgentStartSpec(request.spec);
|
|
13362
13344
|
const agentRelay = agentRelayClient(descriptor, options);
|
|
13363
|
-
const memoryTarget = memorySweepTarget(descriptor, spec.engine, options, agentRelay);
|
|
13364
13345
|
const managedTarget = managedMaterializationTarget(descriptor, spec.engine, options, agentRelay);
|
|
13365
|
-
await runMemorySweep(sweep, memoryTarget, "pre-start", logger, true);
|
|
13366
13346
|
await runManagedMaterialization(materializeManaged, managedTarget, "pre-start", logger, true);
|
|
13367
13347
|
await waitForAgentCleanup(cleanupLocks, request.agent_name);
|
|
13368
13348
|
let runtime = null;
|
|
@@ -13379,10 +13359,8 @@ async function runHostDaemon(options) {
|
|
|
13379
13359
|
return;
|
|
13380
13360
|
}
|
|
13381
13361
|
const cleanupTask = Promise.resolve().then(async () => {
|
|
13382
|
-
await runMemorySweep(sweep, memoryTarget, "runtime", logger, false);
|
|
13383
13362
|
await runManagedMaterialization(materializeManaged, managedTarget, "runtime", logger, false);
|
|
13384
13363
|
runtimes.delete(request.agent_name);
|
|
13385
|
-
memorySweepTargets.delete(request.agent_name);
|
|
13386
13364
|
managedTargets.delete(request.agent_name);
|
|
13387
13365
|
try {
|
|
13388
13366
|
await cleanup({
|
|
@@ -13426,7 +13404,6 @@ async function runHostDaemon(options) {
|
|
|
13426
13404
|
}, options.launchDeps);
|
|
13427
13405
|
previous = runtimes.get(request.agent_name);
|
|
13428
13406
|
runtimes.set(request.agent_name, runtime);
|
|
13429
|
-
memorySweepTargets.set(request.agent_name, memoryTarget);
|
|
13430
13407
|
managedTargets.set(request.agent_name, managedTarget);
|
|
13431
13408
|
} finally {
|
|
13432
13409
|
finishStart();
|
|
@@ -13438,14 +13415,6 @@ async function runHostDaemon(options) {
|
|
|
13438
13415
|
await previous.stop().catch(() => {});
|
|
13439
13416
|
};
|
|
13440
13417
|
const starter = async (request) => {
|
|
13441
|
-
if (request.request_kind === "repo_verification") {
|
|
13442
|
-
const gitRepoUrl = request.spec.git_repo_url;
|
|
13443
|
-
if (typeof gitRepoUrl !== "string" || !gitRepoUrl.trim()) {
|
|
13444
|
-
throw new Error("repo verification request requires spec.git_repo_url");
|
|
13445
|
-
}
|
|
13446
|
-
const verifyRepository = options.verifyRepository ?? verifyTeamRepository;
|
|
13447
|
-
return { ...await verifyRepository(gitRepoUrl, options.hostId) };
|
|
13448
|
-
}
|
|
13449
13418
|
if (request.request_kind === "worker_start")
|
|
13450
13419
|
return _startWorker(request);
|
|
13451
13420
|
await startAgent(request, await options.resolveCredential(request));
|
|
@@ -13462,7 +13431,9 @@ async function runHostDaemon(options) {
|
|
|
13462
13431
|
const _recoverDurableRuntimes = async () => {
|
|
13463
13432
|
if (!options.recoverRuntimes)
|
|
13464
13433
|
return;
|
|
13465
|
-
|
|
13434
|
+
const recoveries = await options.recoverRuntimes();
|
|
13435
|
+
const workers = recoveries.filter(({ request }) => request.request_kind === "worker_start").length;
|
|
13436
|
+
for (const recovery of recoveries) {
|
|
13466
13437
|
if (options.signal?.aborted)
|
|
13467
13438
|
return;
|
|
13468
13439
|
try {
|
|
@@ -13475,6 +13446,7 @@ async function runHostDaemon(options) {
|
|
|
13475
13446
|
});
|
|
13476
13447
|
}
|
|
13477
13448
|
}
|
|
13449
|
+
logger("host_runtime_recovery_completed", { agents: recoveries.length - workers, workers });
|
|
13478
13450
|
};
|
|
13479
13451
|
logger("host_daemon_start", { host_id: options.hostId, captain_id: options.captainId });
|
|
13480
13452
|
try {
|
|
@@ -13500,9 +13472,8 @@ async function runHostDaemon(options) {
|
|
|
13500
13472
|
logger("host_workspace_reconcile_failed", { error: errorMessage8(error) });
|
|
13501
13473
|
}
|
|
13502
13474
|
}
|
|
13503
|
-
if (now() -
|
|
13504
|
-
|
|
13505
|
-
await sweepRuntimeMemory(sweep, memorySweepTargets, logger);
|
|
13475
|
+
if (now() - lastManagedSyncAt >= managedSyncIntervalMs) {
|
|
13476
|
+
lastManagedSyncAt = now();
|
|
13506
13477
|
await syncRuntimeManagedMaterialization(materializeManaged, managedTargets, logger);
|
|
13507
13478
|
}
|
|
13508
13479
|
if (now() - lastScratchSweepAt >= scratchSweepIntervalMs) {
|
|
@@ -13530,7 +13501,6 @@ async function runHostDaemon(options) {
|
|
|
13530
13501
|
} finally {
|
|
13531
13502
|
logger("host_daemon_stopping", { agents: runtimes.size, workers: workerRuntimes.size });
|
|
13532
13503
|
try {
|
|
13533
|
-
await sweepRuntimeMemory(sweep, memorySweepTargets, logger);
|
|
13534
13504
|
await syncRuntimeManagedMaterialization(materializeManaged, managedTargets, logger);
|
|
13535
13505
|
} finally {
|
|
13536
13506
|
await Promise.allSettled([
|
|
@@ -13540,17 +13510,6 @@ async function runHostDaemon(options) {
|
|
|
13540
13510
|
}
|
|
13541
13511
|
}
|
|
13542
13512
|
}
|
|
13543
|
-
function memorySweepTarget(descriptor, engine, options, relay) {
|
|
13544
|
-
return {
|
|
13545
|
-
agentName: descriptor.agent_name,
|
|
13546
|
-
engine,
|
|
13547
|
-
captainId: descriptor.captain_id,
|
|
13548
|
-
teamId: descriptor.session,
|
|
13549
|
-
env: options.hostEnv ?? process.env,
|
|
13550
|
-
relay,
|
|
13551
|
-
state: createMemorySweepState()
|
|
13552
|
-
};
|
|
13553
|
-
}
|
|
13554
13513
|
function managedMaterializationTarget(descriptor, engine, options, relay) {
|
|
13555
13514
|
return {
|
|
13556
13515
|
agentName: descriptor.agent_name,
|
|
@@ -13569,11 +13528,6 @@ function agentRelayClient(descriptor, options) {
|
|
|
13569
13528
|
...options.launchDeps?.fetchImpl ? { fetchImpl: options.launchDeps.fetchImpl } : {}
|
|
13570
13529
|
});
|
|
13571
13530
|
}
|
|
13572
|
-
async function sweepRuntimeMemory(sweep, targets, logger) {
|
|
13573
|
-
for (const target of targets.values()) {
|
|
13574
|
-
await runMemorySweep(sweep, target, "runtime", logger, false);
|
|
13575
|
-
}
|
|
13576
|
-
}
|
|
13577
13531
|
async function sweepWorkspaceScratchRoots(scratchSweep, env, logger) {
|
|
13578
13532
|
if (!scratchSweepEnabled(env))
|
|
13579
13533
|
return;
|
|
@@ -13590,15 +13544,6 @@ async function syncRuntimeManagedMaterialization(materialize, targets, logger) {
|
|
|
13590
13544
|
await runManagedMaterialization(materialize, target, "runtime", logger, false);
|
|
13591
13545
|
}
|
|
13592
13546
|
}
|
|
13593
|
-
async function runMemorySweep(sweep, target, mode, logger, failFast) {
|
|
13594
|
-
try {
|
|
13595
|
-
await sweep({ ...target, mode, logger });
|
|
13596
|
-
} catch (error) {
|
|
13597
|
-
logger("host_memory_sweep_failed", { agent_name: target.agentName, mode, error: errorMessage8(error) });
|
|
13598
|
-
if (failFast)
|
|
13599
|
-
throw error;
|
|
13600
|
-
}
|
|
13601
|
-
}
|
|
13602
13547
|
async function runManagedMaterialization(materialize, target, mode, logger, failFast) {
|
|
13603
13548
|
try {
|
|
13604
13549
|
await materialize({ ...target, logger });
|
|
@@ -13678,7 +13623,7 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
|
|
|
13678
13623
|
return false;
|
|
13679
13624
|
}
|
|
13680
13625
|
}
|
|
13681
|
-
// .dist-releases/
|
|
13626
|
+
// .dist-releases/1784860966076-1188772/src/host/enrollment-redeem.js
|
|
13682
13627
|
var DEFAULT_NOT_FOUND_RETRIES = 3;
|
|
13683
13628
|
var NOT_FOUND_RETRY_DELAY_MS = 500;
|
|
13684
13629
|
function createEnrollmentRedeemer(options) {
|
|
@@ -13815,7 +13760,7 @@ function _role2(enrollment) {
|
|
|
13815
13760
|
}
|
|
13816
13761
|
return _string4(enrollment.roles[0], "roles[0]").trim();
|
|
13817
13762
|
}
|
|
13818
|
-
// .dist-releases/
|
|
13763
|
+
// .dist-releases/1784860966076-1188772/src/host/tutorial.js
|
|
13819
13764
|
function buildTutorialSteps(state) {
|
|
13820
13765
|
const paired = state.hasCredential;
|
|
13821
13766
|
return [
|
|
@@ -13886,7 +13831,7 @@ async function runTutorial(options) {
|
|
|
13886
13831
|
}
|
|
13887
13832
|
return steps;
|
|
13888
13833
|
}
|
|
13889
|
-
// .dist-releases/
|
|
13834
|
+
// .dist-releases/1784860966076-1188772/src/events.js
|
|
13890
13835
|
var EVENT_COMMANDS = new Set(["add", "list", "enable", "disable", "rm"]);
|
|
13891
13836
|
var EVENT_FLAGS = new Set(["at", "days", "message", "run-date", "session", "tag", "tz"]);
|
|
13892
13837
|
var PRODUCT_DAYS_MODES = new Set(["once", "daily", "weekday"]);
|
|
@@ -13976,7 +13921,7 @@ function _assertListArgs(args) {
|
|
|
13976
13921
|
}
|
|
13977
13922
|
}
|
|
13978
13923
|
|
|
13979
|
-
// .dist-releases/
|
|
13924
|
+
// .dist-releases/1784860966076-1188772/src/text-input.js
|
|
13980
13925
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
13981
13926
|
var _readUtf8File = async (file) => await readFile8(file, "utf8");
|
|
13982
13927
|
async function readTextInput(input) {
|
|
@@ -14010,17 +13955,17 @@ async function rejectUnusedTextInput(input, message) {
|
|
|
14010
13955
|
input.unref?.();
|
|
14011
13956
|
}
|
|
14012
13957
|
}
|
|
14013
|
-
async function readTextFile(
|
|
14014
|
-
return await readUtf8File2(
|
|
13958
|
+
async function readTextFile(path22, readUtf8File2 = _readUtf8File) {
|
|
13959
|
+
return await readUtf8File2(path22);
|
|
14015
13960
|
}
|
|
14016
|
-
async function readTextFileOrStdin(
|
|
14017
|
-
if (
|
|
13961
|
+
async function readTextFileOrStdin(path22, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
|
|
13962
|
+
if (path22 === "-")
|
|
14018
13963
|
return await readTextInput(input);
|
|
14019
13964
|
await rejectUnusedTextInput(input, unusedInputMessage);
|
|
14020
|
-
return await readTextFile(
|
|
13965
|
+
return await readTextFile(path22, readUtf8File2);
|
|
14021
13966
|
}
|
|
14022
13967
|
|
|
14023
|
-
// .dist-releases/
|
|
13968
|
+
// .dist-releases/1784860966076-1188772/src/ask.js
|
|
14024
13969
|
var ASK_FLAGS = new Set(["body-file", "option", "reason", "recommend", "session", "title"]);
|
|
14025
13970
|
async function runAskFromCli(client2, argv, config, input) {
|
|
14026
13971
|
const args = _parseAskArgs(argv);
|
|
@@ -14135,7 +14080,7 @@ function _singleAskValue(values, name) {
|
|
|
14135
14080
|
return entries[0];
|
|
14136
14081
|
}
|
|
14137
14082
|
|
|
14138
|
-
// .dist-releases/
|
|
14083
|
+
// .dist-releases/1784860966076-1188772/src/task.js
|
|
14139
14084
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
14140
14085
|
import { basename } from "node:path";
|
|
14141
14086
|
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"]);
|
|
@@ -14625,13 +14570,9 @@ function _taskListFooter(shown, returned, total, hasMore, cursor) {
|
|
|
14625
14570
|
return `Shown ${shown} of ${total}${local}${next}`;
|
|
14626
14571
|
}
|
|
14627
14572
|
|
|
14628
|
-
// .dist-releases/
|
|
14629
|
-
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
14573
|
+
// .dist-releases/1784860966076-1188772/src/team.js
|
|
14630
14574
|
var TEAM_BOOTSTRAP_ENGINES = new Set(["claude", "codex"]);
|
|
14631
14575
|
var SLOT_SPEC_RE = /^([^=\s]+)=([^:@\s]+):([^:@\s]+)$/;
|
|
14632
|
-
var REPO_WAIT_DEFAULT_SECONDS = 60;
|
|
14633
|
-
var REPO_WAIT_MAX_SECONDS = 300;
|
|
14634
|
-
var REPO_POLL_INTERVAL_MS = 250;
|
|
14635
14576
|
var TEAM_PLAIN_LIMIT = 20;
|
|
14636
14577
|
|
|
14637
14578
|
class TeamBootstrapError extends Error {
|
|
@@ -14642,20 +14583,9 @@ class TeamBootstrapError extends Error {
|
|
|
14642
14583
|
this.kind = kind;
|
|
14643
14584
|
}
|
|
14644
14585
|
}
|
|
14645
|
-
|
|
14646
|
-
class TeamRepoError extends Error {
|
|
14647
|
-
kind;
|
|
14648
|
-
constructor(kind, message) {
|
|
14649
|
-
super(message);
|
|
14650
|
-
this.name = "TeamRepoError";
|
|
14651
|
-
this.kind = kind;
|
|
14652
|
-
}
|
|
14653
|
-
}
|
|
14654
14586
|
async function runTeamFromCli(client2, argv, config, format) {
|
|
14655
|
-
if (argv[0] === "repo")
|
|
14656
|
-
return await _runTeamRepoFromCli(client2, argv.slice(1), config, format);
|
|
14657
14587
|
if (argv[0] !== "bootstrap")
|
|
14658
|
-
throw new TeamBootstrapError("usage", "team command must be bootstrap
|
|
14588
|
+
throw new TeamBootstrapError("usage", "team command must be bootstrap");
|
|
14659
14589
|
const captainId = config.captainId;
|
|
14660
14590
|
if (!captainId)
|
|
14661
14591
|
throw new TeamBootstrapError("usage", "team bootstrap requires SAILORBRIDGE_CAPTAIN_ID");
|
|
@@ -14668,173 +14598,6 @@ async function runTeamFromCli(client2, argv, config, format) {
|
|
|
14668
14598
|
const finalSlots = (await client2.getTeamAgentSlots(captainId, args.session)).agent_slots;
|
|
14669
14599
|
return format === "json" ? { agent_slots: finalSlots } : _auditFinalState(finalSlots);
|
|
14670
14600
|
}
|
|
14671
|
-
async function _runTeamRepoFromCli(client2, argv, config, format) {
|
|
14672
|
-
const captainId = config.captainId;
|
|
14673
|
-
if (!captainId)
|
|
14674
|
-
throw new TeamRepoError("usage", "team repo requires SAILORBRIDGE_CAPTAIN_ID");
|
|
14675
|
-
if (argv[0] === "status") {
|
|
14676
|
-
const repository = await client2.getTeamRepository(captainId, _parseRepoStatusArgs(argv.slice(1), config));
|
|
14677
|
-
return format === "json" ? repository : _formatRepository(repository);
|
|
14678
|
-
}
|
|
14679
|
-
if (argv[0] !== "set")
|
|
14680
|
-
throw new TeamRepoError("usage", "team repo command must be set or status");
|
|
14681
|
-
return await _setTeamRepository(client2, captainId, _parseRepoSetArgs(argv.slice(1), config), format);
|
|
14682
|
-
}
|
|
14683
|
-
function _parseRepoStatusArgs(tokens, config) {
|
|
14684
|
-
if (!tokens.length)
|
|
14685
|
-
return _requiredRepoSession(undefined, config);
|
|
14686
|
-
if (tokens.length !== 2 || tokens[0] !== "--session")
|
|
14687
|
-
throw new TeamRepoError("usage", "team repo status accepts only --session TEAM");
|
|
14688
|
-
return _requiredRepoSession(tokens[1], config);
|
|
14689
|
-
}
|
|
14690
|
-
function _parseRepoSetArgs(tokens, config) {
|
|
14691
|
-
const gitRepoUrl = tokens[0]?.trim();
|
|
14692
|
-
if (!gitRepoUrl || gitRepoUrl.startsWith("--"))
|
|
14693
|
-
throw new TeamRepoError("usage", "team repo set requires GIT_URL");
|
|
14694
|
-
if (!isAcceptedTeamGitRemote(gitRepoUrl))
|
|
14695
|
-
throw new TeamRepoError("usage", "GIT_URL must be an accepted git remote without embedded credentials");
|
|
14696
|
-
const flags = _parseRepoSetFlags(tokens.slice(1));
|
|
14697
|
-
return {
|
|
14698
|
-
gitRepoUrl,
|
|
14699
|
-
session: _requiredRepoSession(flags.session, config),
|
|
14700
|
-
wait: flags.wait,
|
|
14701
|
-
timeoutSeconds: flags.timeoutSeconds
|
|
14702
|
-
};
|
|
14703
|
-
}
|
|
14704
|
-
function _parseRepoSetFlags(tokens) {
|
|
14705
|
-
const flags = { wait: false, timeoutSet: false, timeoutSeconds: REPO_WAIT_DEFAULT_SECONDS };
|
|
14706
|
-
for (let index = 0;index < tokens.length; index += 1) {
|
|
14707
|
-
index += _applyRepoSetFlag(tokens, index, flags);
|
|
14708
|
-
}
|
|
14709
|
-
if (flags.timeoutSet && !flags.wait)
|
|
14710
|
-
throw new TeamRepoError("usage", "--timeout-sec requires --wait");
|
|
14711
|
-
return flags;
|
|
14712
|
-
}
|
|
14713
|
-
function _applyRepoSetFlag(tokens, index, flags) {
|
|
14714
|
-
const token = tokens[index];
|
|
14715
|
-
if (token === "--wait") {
|
|
14716
|
-
if (flags.wait)
|
|
14717
|
-
throw new TeamRepoError("usage", "--wait may only be provided once");
|
|
14718
|
-
flags.wait = true;
|
|
14719
|
-
return 0;
|
|
14720
|
-
}
|
|
14721
|
-
if (token === "--session") {
|
|
14722
|
-
flags.session = _singleRepoFlagValue(tokens, index + 1, token, flags.session);
|
|
14723
|
-
return 1;
|
|
14724
|
-
}
|
|
14725
|
-
if (token !== "--timeout-sec")
|
|
14726
|
-
throw new TeamRepoError("usage", `unknown team repo set flag: ${token}`);
|
|
14727
|
-
if (flags.timeoutSet)
|
|
14728
|
-
throw new TeamRepoError("usage", "--timeout-sec may only be provided once");
|
|
14729
|
-
flags.timeoutSet = true;
|
|
14730
|
-
flags.timeoutSeconds = _repoTimeoutSeconds(_singleRepoFlagValue(tokens, index + 1, token));
|
|
14731
|
-
return 1;
|
|
14732
|
-
}
|
|
14733
|
-
function _singleRepoFlagValue(tokens, index, flag, previous) {
|
|
14734
|
-
if (previous !== undefined)
|
|
14735
|
-
throw new TeamRepoError("usage", `${flag} may only be provided once`);
|
|
14736
|
-
const value = tokens[index]?.trim();
|
|
14737
|
-
if (!value || value.startsWith("--"))
|
|
14738
|
-
throw new TeamRepoError("usage", `${flag} requires a value`);
|
|
14739
|
-
return value;
|
|
14740
|
-
}
|
|
14741
|
-
function _repoTimeoutSeconds(value) {
|
|
14742
|
-
const seconds = Number(value);
|
|
14743
|
-
if (!Number.isInteger(seconds) || seconds < 1 || seconds > REPO_WAIT_MAX_SECONDS) {
|
|
14744
|
-
throw new TeamRepoError("usage", `--timeout-sec must be an integer from 1 to ${REPO_WAIT_MAX_SECONDS}`);
|
|
14745
|
-
}
|
|
14746
|
-
return seconds;
|
|
14747
|
-
}
|
|
14748
|
-
function _requiredRepoSession(value, config) {
|
|
14749
|
-
const session = (value ?? config.session)?.trim();
|
|
14750
|
-
if (!session)
|
|
14751
|
-
throw new TeamRepoError("usage", "team repo requires --session or SAILORBRIDGE_SESSION");
|
|
14752
|
-
return session;
|
|
14753
|
-
}
|
|
14754
|
-
async function _setTeamRepository(client2, captainId, args, format) {
|
|
14755
|
-
const hostId = _moderatorHostId((await client2.getTeamAgentSlots(captainId, args.session)).agent_slots);
|
|
14756
|
-
const pending = await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "pending", null));
|
|
14757
|
-
const request = await _queueRepoVerification(client2, captainId, args, hostId);
|
|
14758
|
-
if (!args.wait) {
|
|
14759
|
-
if (format === "json")
|
|
14760
|
-
return { repository: pending, verification_request: request };
|
|
14761
|
-
return `${_formatRepository(pending)}
|
|
14762
|
-
Verification request: ${request.id}`;
|
|
14763
|
-
}
|
|
14764
|
-
const terminal = await _waitForRepoVerification(client2, captainId, args, request.id);
|
|
14765
|
-
const repository = await _persistRepoTerminal(client2, captainId, args, terminal);
|
|
14766
|
-
return format === "json" ? { repository, verification_request: terminal } : _formatRepository(repository);
|
|
14767
|
-
}
|
|
14768
|
-
function _moderatorHostId(slots) {
|
|
14769
|
-
const moderator = slots.find((slot) => slot.slot_id === "moderator" && slot.status === "materialized");
|
|
14770
|
-
if (!moderator?.host_id) {
|
|
14771
|
-
throw new TeamRepoError("moderator-unavailable", "team repo verification requires a materialized Moderator slot with a host");
|
|
14772
|
-
}
|
|
14773
|
-
return moderator.host_id;
|
|
14774
|
-
}
|
|
14775
|
-
async function _queueRepoVerification(client2, captainId, args, hostId) {
|
|
14776
|
-
try {
|
|
14777
|
-
return await client2.createRepoVerificationRequest(captainId, args.session, hostId, args.gitRepoUrl);
|
|
14778
|
-
} catch (error) {
|
|
14779
|
-
await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "failed", "Repository verification could not be queued on the Moderator host"));
|
|
14780
|
-
throw error;
|
|
14781
|
-
}
|
|
14782
|
-
}
|
|
14783
|
-
async function _waitForRepoVerification(client2, captainId, args, requestId) {
|
|
14784
|
-
const deadline = Date.now() + args.timeoutSeconds * 1000;
|
|
14785
|
-
while (Date.now() < deadline) {
|
|
14786
|
-
let request;
|
|
14787
|
-
try {
|
|
14788
|
-
request = await client2.getAgentStartRequest(captainId, args.session, requestId, Math.max(1, deadline - Date.now()));
|
|
14789
|
-
} catch (error) {
|
|
14790
|
-
if (Date.now() < deadline)
|
|
14791
|
-
throw error;
|
|
14792
|
-
break;
|
|
14793
|
-
}
|
|
14794
|
-
if (request.status !== "queued" && request.status !== "claimed")
|
|
14795
|
-
return request;
|
|
14796
|
-
await sleep3(Math.min(REPO_POLL_INTERVAL_MS, Math.max(1, deadline - Date.now())));
|
|
14797
|
-
}
|
|
14798
|
-
throw new TeamRepoError("verification-timeout", `repository verification timed out after ${args.timeoutSeconds} second${args.timeoutSeconds === 1 ? "" : "s"}`);
|
|
14799
|
-
}
|
|
14800
|
-
async function _persistRepoTerminal(client2, captainId, args, request) {
|
|
14801
|
-
if (request.status === "started") {
|
|
14802
|
-
try {
|
|
14803
|
-
const reason2 = _verifiedRepoReason(request);
|
|
14804
|
-
return await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "verified", reason2));
|
|
14805
|
-
} catch (error) {
|
|
14806
|
-
const reason2 = error instanceof Error ? error.message : "Moderator host returned an invalid repository verification result";
|
|
14807
|
-
await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "failed", reason2));
|
|
14808
|
-
throw error;
|
|
14809
|
-
}
|
|
14810
|
-
}
|
|
14811
|
-
const reason = request.error?.trim() || `Repository verification request ended as ${request.status}`;
|
|
14812
|
-
await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "failed", reason));
|
|
14813
|
-
throw new TeamRepoError("verification-failed", reason);
|
|
14814
|
-
}
|
|
14815
|
-
function _verifiedRepoReason(request) {
|
|
14816
|
-
const result = request.result;
|
|
14817
|
-
if (result?.verified !== true || typeof result.reason !== "string" || !result.reason.trim()) {
|
|
14818
|
-
throw new TeamRepoError("verification-failed", "Moderator host returned an invalid repository verification result");
|
|
14819
|
-
}
|
|
14820
|
-
if (typeof result.verified_at !== "string" || !result.verified_at || typeof result.verification_host_id !== "string" || !result.verification_host_id) {
|
|
14821
|
-
throw new TeamRepoError("verification-failed", "Moderator host returned an incomplete repository verification result");
|
|
14822
|
-
}
|
|
14823
|
-
if (!Number.isFinite(Date.parse(result.verified_at)) || result.verification_host_id !== request.host_id) {
|
|
14824
|
-
throw new TeamRepoError("verification-failed", "Moderator host returned an inconsistent repository verification result");
|
|
14825
|
-
}
|
|
14826
|
-
return result.reason.trim();
|
|
14827
|
-
}
|
|
14828
|
-
function _repoState(gitRepoUrl, status, reason) {
|
|
14829
|
-
return { git_repo_url: gitRepoUrl, git_repo_verification_status: status, git_repo_verification_reason: reason };
|
|
14830
|
-
}
|
|
14831
|
-
function _formatRepository(repository) {
|
|
14832
|
-
const url = repository.git_repo_url ?? "not configured";
|
|
14833
|
-
const reason = repository.git_repo_verification_reason ?? "-";
|
|
14834
|
-
return `Repository: ${url}
|
|
14835
|
-
Status: ${repository.git_repo_verification_status}
|
|
14836
|
-
Reason: ${reason}`;
|
|
14837
|
-
}
|
|
14838
14601
|
function _parseBootstrapArgs(tokens, config) {
|
|
14839
14602
|
const values = {};
|
|
14840
14603
|
for (let index = 0;index < tokens.length; index += 1) {
|
|
@@ -14962,11 +14725,11 @@ function _slotTable(slots) {
|
|
|
14962
14725
|
Shown ${shown.length} of ${slots.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`;
|
|
14963
14726
|
}
|
|
14964
14727
|
|
|
14965
|
-
// .dist-releases/
|
|
14728
|
+
// .dist-releases/1784860966076-1188772/src/memory.js
|
|
14966
14729
|
var VALUE_FLAGS = new Set(["file", "source"]);
|
|
14967
|
-
var
|
|
14968
|
-
async function
|
|
14969
|
-
const args =
|
|
14730
|
+
var MEMORY_SEARCH_PLAIN_LIMIT = 20;
|
|
14731
|
+
async function runMemoryFromCli(client2, argv, config, input, format) {
|
|
14732
|
+
const args = _parseMemoryCliArgs(argv);
|
|
14970
14733
|
const captainId = _requireConfig(config.captainId, "SAILORBRIDGE_CAPTAIN_ID");
|
|
14971
14734
|
const teamId = _requireConfig(config.session, "SAILORBRIDGE_SESSION");
|
|
14972
14735
|
if (args.command === "set")
|
|
@@ -14977,14 +14740,14 @@ async function runRagFromCli(client2, argv, config, input, format) {
|
|
|
14977
14740
|
return await _currentMemory(client2, captainId, teamId, args, format);
|
|
14978
14741
|
if (args.command === "pin" || args.command === "unpin")
|
|
14979
14742
|
return await _pinMemory(client2, captainId, teamId, args, format);
|
|
14980
|
-
|
|
14743
|
+
_assertMemoryShape(args, "memory index", 0, []);
|
|
14981
14744
|
const result = await client2.getRagMemoryIndex(captainId, teamId);
|
|
14982
14745
|
return format === "json" ? result : result.text;
|
|
14983
14746
|
}
|
|
14984
|
-
function
|
|
14747
|
+
function _parseMemoryCliArgs(argv) {
|
|
14985
14748
|
const command = argv[0];
|
|
14986
14749
|
if (command !== "set" && command !== "search" && command !== "current" && command !== "index" && command !== "pin" && command !== "unpin") {
|
|
14987
|
-
throw new Error("
|
|
14750
|
+
throw new Error("memory command must be set, search, current, index, pin, or unpin");
|
|
14988
14751
|
}
|
|
14989
14752
|
const positionals = [];
|
|
14990
14753
|
const flags = {};
|
|
@@ -14993,14 +14756,14 @@ function _parseRagArgs(argv) {
|
|
|
14993
14756
|
if (!token.startsWith("--"))
|
|
14994
14757
|
positionals.push(token);
|
|
14995
14758
|
else
|
|
14996
|
-
|
|
14759
|
+
_addMemoryFlag(flags, token, VALUE_FLAGS.has(token.slice(2)) ? argv[++index] : undefined);
|
|
14997
14760
|
}
|
|
14998
14761
|
return { command, positionals, flags };
|
|
14999
14762
|
}
|
|
15000
|
-
function
|
|
14763
|
+
function _addMemoryFlag(flags, token, value) {
|
|
15001
14764
|
const name = token.slice(2);
|
|
15002
14765
|
if (name !== "stdin" && !VALUE_FLAGS.has(name))
|
|
15003
|
-
throw new Error(`unknown
|
|
14766
|
+
throw new Error(`unknown memory flag: ${token}`);
|
|
15004
14767
|
if (flags[name] !== undefined)
|
|
15005
14768
|
throw new Error(`${token} may only be provided once`);
|
|
15006
14769
|
if (name === "stdin")
|
|
@@ -15011,7 +14774,7 @@ function _addRagFlag(flags, token, value) {
|
|
|
15011
14774
|
flags[name] = value;
|
|
15012
14775
|
}
|
|
15013
14776
|
async function _setMemory(client2, captainId, teamId, args, input, format) {
|
|
15014
|
-
|
|
14777
|
+
_assertMemoryShape(args, "memory set", 1, ["stdin", "file", "source"]);
|
|
15015
14778
|
const key = args.positionals[0];
|
|
15016
14779
|
const source = _requiredFlag(args, "source");
|
|
15017
14780
|
const value = await _payload(args, input);
|
|
@@ -15024,26 +14787,26 @@ async function _setMemory(client2, captainId, teamId, args, input, format) {
|
|
|
15024
14787
|
return `${result.card.key} v${result.card.version ?? 1} ${result.card.status ?? "active"}${suffix}`;
|
|
15025
14788
|
}
|
|
15026
14789
|
async function _searchMemory(client2, captainId, teamId, args, input, format) {
|
|
15027
|
-
|
|
14790
|
+
_assertMemoryShape(args, "memory search", 1, []);
|
|
15028
14791
|
if (args.positionals[0] !== undefined)
|
|
15029
|
-
await rejectUnusedTextInput(input, "
|
|
14792
|
+
await rejectUnusedTextInput(input, "memory search received stdin that its query argument does not consume; omit the query argument to read stdin");
|
|
15030
14793
|
const text = args.positionals[0] ?? await readTextInput(input);
|
|
15031
14794
|
if (!text.trim())
|
|
15032
|
-
throw new Error("
|
|
14795
|
+
throw new Error("memory search requires a query argument or stdin");
|
|
15033
14796
|
const result = await client2.searchRag(captainId, teamId, text);
|
|
15034
14797
|
if (format === "json")
|
|
15035
14798
|
return result;
|
|
15036
14799
|
const { cards } = result;
|
|
15037
14800
|
if (!cards.length)
|
|
15038
14801
|
return "No matching memory cards.";
|
|
15039
|
-
const shown = cards.slice(0,
|
|
14802
|
+
const shown = cards.slice(0, MEMORY_SEARCH_PLAIN_LIMIT);
|
|
15040
14803
|
const lines = shown.map((card) => `${card.key} ${card.score} ${_summary(card.value)}`);
|
|
15041
14804
|
const omitted = cards.length - shown.length;
|
|
15042
14805
|
return [...lines, `Shown ${shown.length} of ${cards.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`].join(`
|
|
15043
14806
|
`);
|
|
15044
14807
|
}
|
|
15045
14808
|
async function _currentMemory(client2, captainId, teamId, args, format) {
|
|
15046
|
-
|
|
14809
|
+
_assertMemoryShape(args, "memory current", 1, []);
|
|
15047
14810
|
const card = await client2.getRagCurrentCard(captainId, teamId, args.positionals[0]);
|
|
15048
14811
|
if (format === "json")
|
|
15049
14812
|
return card;
|
|
@@ -15053,14 +14816,14 @@ ${card.value}`;
|
|
|
15053
14816
|
}
|
|
15054
14817
|
async function _pinMemory(client2, captainId, teamId, args, format) {
|
|
15055
14818
|
const pinned = args.command === "pin";
|
|
15056
|
-
|
|
14819
|
+
_assertMemoryShape(args, `memory ${args.command}`, 1, []);
|
|
15057
14820
|
const card = await client2.setRagCardPin(captainId, teamId, args.positionals[0], pinned);
|
|
15058
14821
|
if (format === "json")
|
|
15059
14822
|
return card;
|
|
15060
14823
|
return `${card.key} v${card.version ?? 1} ${pinned ? "pinned" : "unpinned"}`;
|
|
15061
14824
|
}
|
|
15062
|
-
function
|
|
15063
|
-
const requiresKey = name !== "
|
|
14825
|
+
function _assertMemoryShape(args, name, maxPositionals, allowedFlags) {
|
|
14826
|
+
const requiresKey = name !== "memory search" && name !== "memory index";
|
|
15064
14827
|
if (args.positionals.length > maxPositionals) {
|
|
15065
14828
|
const requirement = maxPositionals ? requiresKey ? "requires one key" : "accepts at most one positional argument" : "requires no positional arguments";
|
|
15066
14829
|
throw new Error(`${name} ${requirement}`);
|
|
@@ -15074,12 +14837,12 @@ async function _payload(args, input) {
|
|
|
15074
14837
|
const fromStdin = args.flags.stdin === true;
|
|
15075
14838
|
const file = args.flags.file;
|
|
15076
14839
|
if (fromStdin === (typeof file === "string"))
|
|
15077
|
-
throw new Error("
|
|
14840
|
+
throw new Error("memory set requires exactly one of --stdin or --file");
|
|
15078
14841
|
if (!fromStdin)
|
|
15079
|
-
await rejectUnusedTextInput(input, "
|
|
14842
|
+
await rejectUnusedTextInput(input, "memory set received stdin that --file does not consume; use --stdin");
|
|
15080
14843
|
const value = fromStdin ? await readTextInput(input) : await readTextFile(file);
|
|
15081
14844
|
if (!value.trim())
|
|
15082
|
-
throw new Error("
|
|
14845
|
+
throw new Error("memory set payload must not be empty");
|
|
15083
14846
|
return value;
|
|
15084
14847
|
}
|
|
15085
14848
|
function _requiredFlag(args, name) {
|
|
@@ -15090,14 +14853,14 @@ function _requiredFlag(args, name) {
|
|
|
15090
14853
|
}
|
|
15091
14854
|
function _requireConfig(value, name) {
|
|
15092
14855
|
if (!value?.trim())
|
|
15093
|
-
throw new Error(`
|
|
14856
|
+
throw new Error(`memory requires ${name}`);
|
|
15094
14857
|
return value;
|
|
15095
14858
|
}
|
|
15096
14859
|
function _summary(value) {
|
|
15097
14860
|
return value.replace(/\s+/gu, " ").trim().slice(0, 180);
|
|
15098
14861
|
}
|
|
15099
14862
|
|
|
15100
|
-
// .dist-releases/
|
|
14863
|
+
// .dist-releases/1784860966076-1188772/src/mailbox-send.js
|
|
15101
14864
|
var SEND_FLAGS = new Set(["body-file", "commit", "session", "tag", "task-id", "task-version", "to-agents", "to-roles", "to-host"]);
|
|
15102
14865
|
function parseMailboxSendArgs(argv) {
|
|
15103
14866
|
const parsed = argsFromValues(parseFlagValues(argv));
|
|
@@ -15196,7 +14959,7 @@ function splitList(value) {
|
|
|
15196
14959
|
return list?.length ? list : undefined;
|
|
15197
14960
|
}
|
|
15198
14961
|
|
|
15199
|
-
// .dist-releases/
|
|
14962
|
+
// .dist-releases/1784860966076-1188772/src/reply.js
|
|
15200
14963
|
var REPLY_FLAGS = new Set(["agent", "body-file", "in-reply-to", "session"]);
|
|
15201
14964
|
async function sendReplyFromCli(client2, argv, config, io) {
|
|
15202
14965
|
const args = _parseReplyArgs(argv);
|
|
@@ -15253,11 +15016,192 @@ function _requiredReplyValue(values, name) {
|
|
|
15253
15016
|
return value;
|
|
15254
15017
|
}
|
|
15255
15018
|
|
|
15256
|
-
// .dist-releases/
|
|
15019
|
+
// .dist-releases/1784860966076-1188772/src/worker-command.js
|
|
15020
|
+
async function runWorkerCommandFromCli(client2, argv, config, io, format = "plain") {
|
|
15021
|
+
const captainId = config.captainId;
|
|
15022
|
+
if (!captainId)
|
|
15023
|
+
throw new Error("SAILORBRIDGE_CAPTAIN_ID is required for worker commands");
|
|
15024
|
+
const command = argv[0];
|
|
15025
|
+
switch (command) {
|
|
15026
|
+
case "list":
|
|
15027
|
+
return _listWorkers(client2, captainId, _requireSession(config.session), argv.slice(1), io, format);
|
|
15028
|
+
case "run":
|
|
15029
|
+
return _runWorker(client2, captainId, _requireSession(config.session), argv.slice(1), io, format);
|
|
15030
|
+
case "job":
|
|
15031
|
+
return _showJob(client2, captainId, argv.slice(1), io, format);
|
|
15032
|
+
default:
|
|
15033
|
+
throw new Error("worker command must be job, list, run, or serve");
|
|
15034
|
+
}
|
|
15035
|
+
}
|
|
15036
|
+
function _requireSession(session) {
|
|
15037
|
+
if (!session)
|
|
15038
|
+
throw new Error("SAILORBRIDGE_SESSION is required for worker list and run");
|
|
15039
|
+
return session;
|
|
15040
|
+
}
|
|
15041
|
+
async function _listWorkers(client2, captainId, session, argv, io, format) {
|
|
15042
|
+
if (argv.length > 0)
|
|
15043
|
+
throw new Error(`unexpected argument: ${argv[0]}`);
|
|
15044
|
+
const response = await client2.listWorkers(captainId, session);
|
|
15045
|
+
if (format === "json") {
|
|
15046
|
+
writeOutput(io.stdout, response, format);
|
|
15047
|
+
return;
|
|
15048
|
+
}
|
|
15049
|
+
const { workers } = response;
|
|
15050
|
+
if (workers.length === 0) {
|
|
15051
|
+
io.stdout.write(`No workers registered.
|
|
15052
|
+
`);
|
|
15053
|
+
return;
|
|
15054
|
+
}
|
|
15055
|
+
for (const worker of workers.slice(0, 20)) {
|
|
15056
|
+
io.stdout.write(`${worker.name} ${worker.online ? "online" : "offline"} ${worker.host}
|
|
15057
|
+
`);
|
|
15058
|
+
}
|
|
15059
|
+
const omitted = workers.length - 20;
|
|
15060
|
+
if (omitted > 0)
|
|
15061
|
+
io.stdout.write(`${omitted} omitted; run sail worker list --format json for all.
|
|
15062
|
+
`);
|
|
15063
|
+
}
|
|
15064
|
+
async function _runWorker(client2, captainId, session, argv, io, format) {
|
|
15065
|
+
const input = _parseRunArgs(argv);
|
|
15066
|
+
const workers = (await client2.listWorkers(captainId, session)).workers;
|
|
15067
|
+
const worker = _requireRunnableWorker(workers, input.worker);
|
|
15068
|
+
const submitted = await _submitWorkerJob(client2, captainId, input);
|
|
15069
|
+
if (!input.detach || format === "plain" || !submitted.warning) {
|
|
15070
|
+
_writeWarning(submitted.warning, worker, io);
|
|
15071
|
+
}
|
|
15072
|
+
if (input.detach) {
|
|
15073
|
+
if (format === "json")
|
|
15074
|
+
writeOutput(io.stdout, submitted, format);
|
|
15075
|
+
else
|
|
15076
|
+
io.stdout.write(`${submitted.id}
|
|
15077
|
+
`);
|
|
15078
|
+
return;
|
|
15079
|
+
}
|
|
15080
|
+
const remoteBudget = input.timeoutSec ?? DEFAULT_JOB_TIMEOUT_SECONDS;
|
|
15081
|
+
const result = await client2.getJobResult(captainId, submitted.id, remoteBudget + WORKER_JOB_RESULT_GRACE_SECONDS);
|
|
15082
|
+
_writeResult(result, io, format);
|
|
15083
|
+
}
|
|
15084
|
+
function _requireRunnableWorker(workers, name) {
|
|
15085
|
+
const worker = workers.find((item) => item.name === name);
|
|
15086
|
+
if (!worker)
|
|
15087
|
+
throw new Error(`worker '${name}' is not registered in this team`);
|
|
15088
|
+
if (worker.capabilities.includes("mode=readonly")) {
|
|
15089
|
+
throw new Error(`worker '${name}' does not support bash jobs in readonly mode`);
|
|
15090
|
+
}
|
|
15091
|
+
return worker;
|
|
15092
|
+
}
|
|
15093
|
+
function _submitWorkerJob(client2, captainId, input) {
|
|
15094
|
+
return client2.submitJob(captainId, {
|
|
15095
|
+
worker: input.worker,
|
|
15096
|
+
argv: input.argv,
|
|
15097
|
+
...input.timeoutSec === undefined ? {} : { timeoutSec: input.timeoutSec }
|
|
15098
|
+
});
|
|
15099
|
+
}
|
|
15100
|
+
async function _showJob(client2, captainId, argv, io, format) {
|
|
15101
|
+
if (argv.length !== 1 || !argv[0])
|
|
15102
|
+
throw new Error("worker job requires exactly one job id");
|
|
15103
|
+
const result = await client2.getJobStatus(captainId, argv[0]);
|
|
15104
|
+
if (format === "json") {
|
|
15105
|
+
writeOutput(io.stdout, result, format);
|
|
15106
|
+
if (result.exit_code !== null)
|
|
15107
|
+
io.setExitCode(_localExitStatus(result.exit_code));
|
|
15108
|
+
return;
|
|
15109
|
+
}
|
|
15110
|
+
if (result.status === "queued" || result.status === "running") {
|
|
15111
|
+
io.stdout.write(`${result.id} ${result.status}
|
|
15112
|
+
`);
|
|
15113
|
+
return;
|
|
15114
|
+
}
|
|
15115
|
+
_writeResult(result, io, format);
|
|
15116
|
+
}
|
|
15117
|
+
function _parseRunArgs(argv) {
|
|
15118
|
+
const separator = argv.indexOf("--");
|
|
15119
|
+
if (separator < 0)
|
|
15120
|
+
throw new Error("worker run requires '--' before argv");
|
|
15121
|
+
const remoteArgv = argv.slice(separator + 1);
|
|
15122
|
+
if (remoteArgv.length === 0 || !remoteArgv[0]?.trim())
|
|
15123
|
+
throw new Error("worker run requires a command after '--'");
|
|
15124
|
+
const { worker, timeoutSec, detach } = _parseRunFlags(argv.slice(0, separator));
|
|
15125
|
+
if (!worker)
|
|
15126
|
+
throw new Error("--worker is required");
|
|
15127
|
+
return { worker, argv: remoteArgv, detach, ...timeoutSec === undefined ? {} : { timeoutSec } };
|
|
15128
|
+
}
|
|
15129
|
+
function _parseRunFlags(flags) {
|
|
15130
|
+
let worker = "";
|
|
15131
|
+
let timeoutSec;
|
|
15132
|
+
let detach = false;
|
|
15133
|
+
for (let index = 0;index < flags.length; index += 1) {
|
|
15134
|
+
const flag = flags[index];
|
|
15135
|
+
if (flag === "--detach") {
|
|
15136
|
+
detach = true;
|
|
15137
|
+
continue;
|
|
15138
|
+
}
|
|
15139
|
+
const value = flags[index + 1];
|
|
15140
|
+
if (!value)
|
|
15141
|
+
throw new Error(`${flag} requires a value`);
|
|
15142
|
+
if (flag === "--worker")
|
|
15143
|
+
worker = value.trim();
|
|
15144
|
+
else if (flag === "--timeout")
|
|
15145
|
+
timeoutSec = _parseTimeout(value);
|
|
15146
|
+
else
|
|
15147
|
+
throw new Error(`unexpected argument: ${flag}`);
|
|
15148
|
+
index += 1;
|
|
15149
|
+
}
|
|
15150
|
+
return { worker, detach, ...timeoutSec === undefined ? {} : { timeoutSec } };
|
|
15151
|
+
}
|
|
15152
|
+
function _parseTimeout(value) {
|
|
15153
|
+
if (!/^(?:0|[1-9]\d*)(?:\.\d+)?$/u.test(value)) {
|
|
15154
|
+
throw new Error(`timeout must be a number from 1 to ${MAX_JOB_TIMEOUT_SECONDS} seconds`);
|
|
15155
|
+
}
|
|
15156
|
+
const timeout = Number(value);
|
|
15157
|
+
if (!Number.isFinite(timeout) || timeout < 1 || timeout > MAX_JOB_TIMEOUT_SECONDS) {
|
|
15158
|
+
throw new Error(`timeout must be a number from 1 to ${MAX_JOB_TIMEOUT_SECONDS} seconds`);
|
|
15159
|
+
}
|
|
15160
|
+
return timeout;
|
|
15161
|
+
}
|
|
15162
|
+
function _writeWarning(warning, worker, io) {
|
|
15163
|
+
if (warning) {
|
|
15164
|
+
io.stderr.write(`Warning: ${warning}
|
|
15165
|
+
`);
|
|
15166
|
+
return;
|
|
15167
|
+
}
|
|
15168
|
+
if (!worker.online)
|
|
15169
|
+
io.stderr.write(`Warning: worker '${worker.name}' is registered but offline.
|
|
15170
|
+
`);
|
|
15171
|
+
}
|
|
15172
|
+
function _writeResult(response, io, format) {
|
|
15173
|
+
if (!response.result || response.exit_code === null) {
|
|
15174
|
+
throw new Error(`job ${response.id} did not finish before the timeout (status=${response.status})`);
|
|
15175
|
+
}
|
|
15176
|
+
const localStatus = _localExitStatus(response.exit_code);
|
|
15177
|
+
if (format === "json") {
|
|
15178
|
+
writeOutput(io.stdout, response, format);
|
|
15179
|
+
io.setExitCode(localStatus);
|
|
15180
|
+
return;
|
|
15181
|
+
}
|
|
15182
|
+
io.stdout.write(response.result.stdout);
|
|
15183
|
+
io.stderr.write(response.result.stderr);
|
|
15184
|
+
if (response.exit_code < 0)
|
|
15185
|
+
_writeSignalStatus(response.exit_code, localStatus, response.result.stderr, io);
|
|
15186
|
+
io.setExitCode(localStatus);
|
|
15187
|
+
}
|
|
15188
|
+
function _localExitStatus(remoteCode) {
|
|
15189
|
+
return remoteCode < 0 ? 128 + Math.abs(remoteCode) : remoteCode;
|
|
15190
|
+
}
|
|
15191
|
+
function _writeSignalStatus(remoteCode, localStatus, remoteStderr, io) {
|
|
15192
|
+
if (remoteStderr && !remoteStderr.endsWith(`
|
|
15193
|
+
`))
|
|
15194
|
+
io.stderr.write(`
|
|
15195
|
+
`);
|
|
15196
|
+
io.stderr.write(`Remote exit code ${remoteCode}; local exit status ${localStatus}.
|
|
15197
|
+
`);
|
|
15198
|
+
}
|
|
15199
|
+
|
|
15200
|
+
// .dist-releases/1784860966076-1188772/src/create.js
|
|
15257
15201
|
var CREATE_ENGINES = new Set(["claude", "codex"]);
|
|
15258
15202
|
var DEFAULT_ENGINE = "claude";
|
|
15259
15203
|
var WORKSPACE_PLAIN_LIMIT = 20;
|
|
15260
|
-
var CREATE_SUBCOMMANDS = new Set(["team", "pair", "worker"]);
|
|
15204
|
+
var CREATE_SUBCOMMANDS = new Set(["team", "pair", "planner", "tester", "worker"]);
|
|
15261
15205
|
async function runCreateFromCli(argv, io, deps = {}) {
|
|
15262
15206
|
const sub = argv[0];
|
|
15263
15207
|
if (!sub || sub.startsWith("--"))
|
|
@@ -15269,6 +15213,8 @@ async function runCreateFromCli(argv, io, deps = {}) {
|
|
|
15269
15213
|
return await _createTeam(flags, io, deps);
|
|
15270
15214
|
if (sub === "pair")
|
|
15271
15215
|
return await _createPair(flags, io, deps);
|
|
15216
|
+
if (sub === "planner" || sub === "tester")
|
|
15217
|
+
return await _createMember(sub, flags, io, deps);
|
|
15272
15218
|
return await _createWorker(flags, io, deps);
|
|
15273
15219
|
}
|
|
15274
15220
|
async function _createTeam(flags, io, deps) {
|
|
@@ -15277,7 +15223,7 @@ async function _createTeam(flags, io, deps) {
|
|
|
15277
15223
|
const displayName = _required4(flags.name, "--name");
|
|
15278
15224
|
const template = _required4(flags.template, "--template");
|
|
15279
15225
|
const engine = _engine3(flags.engine);
|
|
15280
|
-
const ctx = await _teamConsoleContext(flags, deps);
|
|
15226
|
+
const ctx = await _teamConsoleContext("team", flags, deps);
|
|
15281
15227
|
const host = await _resolveOnlineHost(ctx.relay, ctx.captainId, flags.host);
|
|
15282
15228
|
const workspace = await _resolveHostWorkspace(ctx.relay, ctx.captainId, host.host_id, flags.workspace);
|
|
15283
15229
|
const { team, slots } = await _createAndApplyTeam(deps, ctx, displayName, template);
|
|
@@ -15305,9 +15251,9 @@ async function _startTeamSlots(deps, ctx, team, hostId, workspaceRoot, engine, s
|
|
|
15305
15251
|
}
|
|
15306
15252
|
return requests;
|
|
15307
15253
|
}
|
|
15308
|
-
async function _postConsoleJson(deps, ctx,
|
|
15254
|
+
async function _postConsoleJson(deps, ctx, path22, body) {
|
|
15309
15255
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
15310
|
-
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${
|
|
15256
|
+
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
|
|
15311
15257
|
method: "POST",
|
|
15312
15258
|
headers: { Authorization: `Bearer ${ctx.token}`, "Content-Type": "application/json" },
|
|
15313
15259
|
body: JSON.stringify(body)
|
|
@@ -15320,6 +15266,19 @@ async function _postConsoleJson(deps, ctx, path24, body) {
|
|
|
15320
15266
|
throw new Error("SailorBridge API returned a malformed object");
|
|
15321
15267
|
return parsed;
|
|
15322
15268
|
}
|
|
15269
|
+
async function _getConsoleJson(deps, ctx, path22) {
|
|
15270
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
15271
|
+
const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
|
|
15272
|
+
headers: { Authorization: `Bearer ${ctx.token}` }
|
|
15273
|
+
});
|
|
15274
|
+
const text = await response.text();
|
|
15275
|
+
if (!response.ok)
|
|
15276
|
+
throw new Error(`SailorBridge API ${response.status}: ${text}`);
|
|
15277
|
+
const parsed = JSON.parse(text);
|
|
15278
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
15279
|
+
throw new Error("SailorBridge API returned a malformed object");
|
|
15280
|
+
return parsed;
|
|
15281
|
+
}
|
|
15323
15282
|
function _responseString(body, field) {
|
|
15324
15283
|
const value = body[field];
|
|
15325
15284
|
if (typeof value !== "string" || !value)
|
|
@@ -15361,6 +15320,77 @@ async function _createPair(flags, io, deps) {
|
|
|
15361
15320
|
});
|
|
15362
15321
|
writeOutput(io.stdout, result, io.format, () => _pairPlain(host.host_id, workspace, engine, result));
|
|
15363
15322
|
}
|
|
15323
|
+
async function _createMember(role, flags, io, deps) {
|
|
15324
|
+
_rejectMemberFlags(role, flags);
|
|
15325
|
+
const engine = _engine3(flags.engine);
|
|
15326
|
+
const session = _requiredSession(flags.session, _envSession(deps));
|
|
15327
|
+
const ctx = await _teamConsoleContext(role, flags, deps);
|
|
15328
|
+
const host = await _resolveOnlineHost(ctx.relay, ctx.captainId, flags.host);
|
|
15329
|
+
const workspace = await _resolveHostWorkspace(ctx.relay, ctx.captainId, host.host_id, flags.workspace);
|
|
15330
|
+
const slot = await _memberSlotForCreate(deps, ctx, session, role);
|
|
15331
|
+
const request = await _startMember(deps, ctx, session, host.host_id, workspace.path, engine, role, slot);
|
|
15332
|
+
const result = { role, host: host.host_id, workspace: workspace.path, engine, slot, request };
|
|
15333
|
+
writeOutput(io.stdout, result, io.format, () => _memberPlain(result));
|
|
15334
|
+
}
|
|
15335
|
+
function _rejectMemberFlags(role, flags) {
|
|
15336
|
+
const unsupported = [
|
|
15337
|
+
["--name", flags.name],
|
|
15338
|
+
["--template", flags.template],
|
|
15339
|
+
["--concurrency", flags.concurrency]
|
|
15340
|
+
];
|
|
15341
|
+
const found = unsupported.find(([, value]) => value !== undefined);
|
|
15342
|
+
if (found)
|
|
15343
|
+
throw new Error(`create ${role} does not accept ${found[0]}`);
|
|
15344
|
+
}
|
|
15345
|
+
async function _memberSlotForCreate(deps, ctx, session, role) {
|
|
15346
|
+
const path22 = `/api/console/teams/${encodeURIComponent(session)}/agent-slots`;
|
|
15347
|
+
const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx, path22));
|
|
15348
|
+
const retryable = slots.find((slot) => _isRetryableMemberSlot(slot, role));
|
|
15349
|
+
if (retryable)
|
|
15350
|
+
return retryable;
|
|
15351
|
+
if (role === "planner" && slots.some((slot) => slot.role === role)) {
|
|
15352
|
+
throw new Error("duplicate-singleton-role: team already has a planner slot");
|
|
15353
|
+
}
|
|
15354
|
+
const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path22, { role }));
|
|
15355
|
+
if (created.role !== role)
|
|
15356
|
+
throw new Error("SailorBridge API returned a member slot with the wrong role");
|
|
15357
|
+
return created;
|
|
15358
|
+
}
|
|
15359
|
+
function _isRetryableMemberSlot(slot, role) {
|
|
15360
|
+
return slot.role === role && slot.status === "planned" && slot.template_origin === "single-add";
|
|
15361
|
+
}
|
|
15362
|
+
async function _startMember(deps, ctx, session, hostId, workspaceRoot, engine, role, slot) {
|
|
15363
|
+
const response = await _postConsoleJson(deps, ctx, `/api/console/teams/${encodeURIComponent(session)}/agent-start-requests`, { host_id: hostId, slot_id: slot.slot_id, spec: { engine, role, workspace: { mode: "worktree", root: workspaceRoot } } });
|
|
15364
|
+
const request = _responseStartRequest(response);
|
|
15365
|
+
if (request.host_id !== hostId || request.agent_name !== slot.agent_name) {
|
|
15366
|
+
throw new Error("SailorBridge API returned a start request with mismatched identity");
|
|
15367
|
+
}
|
|
15368
|
+
return request;
|
|
15369
|
+
}
|
|
15370
|
+
function _responseMemberSlots(body) {
|
|
15371
|
+
if (!Array.isArray(body.agent_slots))
|
|
15372
|
+
throw new Error("SailorBridge API response is missing agent_slots");
|
|
15373
|
+
return body.agent_slots.map(_responseMemberSlotValue);
|
|
15374
|
+
}
|
|
15375
|
+
function _responseMemberSlot(body) {
|
|
15376
|
+
if (!body.agent_slot || typeof body.agent_slot !== "object" || Array.isArray(body.agent_slot)) {
|
|
15377
|
+
throw new Error("SailorBridge API response is missing agent_slot");
|
|
15378
|
+
}
|
|
15379
|
+
return _responseMemberSlotValue(body.agent_slot);
|
|
15380
|
+
}
|
|
15381
|
+
function _responseMemberSlotValue(raw) {
|
|
15382
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15383
|
+
throw new Error("SailorBridge API returned a malformed agent slot");
|
|
15384
|
+
}
|
|
15385
|
+
const slot = raw;
|
|
15386
|
+
return {
|
|
15387
|
+
slot_id: _responseString(slot, "slot_id"),
|
|
15388
|
+
agent_name: _responseString(slot, "agent_name"),
|
|
15389
|
+
role: _responseString(slot, "role"),
|
|
15390
|
+
status: _responseString(slot, "status"),
|
|
15391
|
+
template_origin: _responseString(slot, "template_origin")
|
|
15392
|
+
};
|
|
15393
|
+
}
|
|
15364
15394
|
async function _createWorker(flags, io, deps) {
|
|
15365
15395
|
const workerName = _required4(flags.name, "--name");
|
|
15366
15396
|
const maxConcurrency = _concurrency(flags.concurrency);
|
|
@@ -15397,11 +15427,11 @@ async function _consoleContext(flags, deps) {
|
|
|
15397
15427
|
token: credential.token
|
|
15398
15428
|
};
|
|
15399
15429
|
}
|
|
15400
|
-
async function _teamConsoleContext(flags, deps) {
|
|
15430
|
+
async function _teamConsoleContext(command, flags, deps) {
|
|
15401
15431
|
const env = deps.env ?? process.env;
|
|
15402
15432
|
const accessToken = env.SAILORBRIDGE_CAPTAIN_ACCESS_TOKEN?.trim();
|
|
15403
15433
|
if (!accessToken) {
|
|
15404
|
-
throw new Error(
|
|
15434
|
+
throw new Error(`create ${command} requires SAILORBRIDGE_CAPTAIN_ACCESS_TOKEN for captain-authorized console operations`);
|
|
15405
15435
|
}
|
|
15406
15436
|
return { ...await _consoleContext(flags, deps), token: accessToken };
|
|
15407
15437
|
}
|
|
@@ -15534,6 +15564,18 @@ function _pairPlain(hostId, workspace, engine, result) {
|
|
|
15534
15564
|
].join(`
|
|
15535
15565
|
`);
|
|
15536
15566
|
}
|
|
15567
|
+
function _memberPlain(result) {
|
|
15568
|
+
return [
|
|
15569
|
+
`${result.role}: ${result.slot.agent_name}`,
|
|
15570
|
+
`slot: ${result.slot.slot_id}`,
|
|
15571
|
+
`host: ${result.host}`,
|
|
15572
|
+
`workspace: ${result.workspace}`,
|
|
15573
|
+
`engine: ${result.engine}`,
|
|
15574
|
+
`request: ${result.request.id}`,
|
|
15575
|
+
`status: ${result.request.status}`
|
|
15576
|
+
].join(`
|
|
15577
|
+
`);
|
|
15578
|
+
}
|
|
15537
15579
|
function _workerPlain(hostId, workdir, result) {
|
|
15538
15580
|
return [
|
|
15539
15581
|
`worker: ${result.worker.name}`,
|
|
@@ -15545,7 +15587,7 @@ function _workerPlain(hostId, workdir, result) {
|
|
|
15545
15587
|
`);
|
|
15546
15588
|
}
|
|
15547
15589
|
|
|
15548
|
-
// .dist-releases/
|
|
15590
|
+
// .dist-releases/1784860966076-1188772/src/cli.js
|
|
15549
15591
|
var FORMATTED_COMMANDS = new Set([
|
|
15550
15592
|
"ask",
|
|
15551
15593
|
"create",
|
|
@@ -15554,11 +15596,12 @@ var FORMATTED_COMMANDS = new Set([
|
|
|
15554
15596
|
"host",
|
|
15555
15597
|
"login",
|
|
15556
15598
|
"mailbox",
|
|
15557
|
-
"
|
|
15599
|
+
"memory",
|
|
15558
15600
|
"reply",
|
|
15559
15601
|
"task",
|
|
15560
15602
|
"team",
|
|
15561
|
-
"tutorial"
|
|
15603
|
+
"tutorial",
|
|
15604
|
+
"worker"
|
|
15562
15605
|
]);
|
|
15563
15606
|
var SERVICE_INSTALL_COMMAND = "sailorbridge host service install";
|
|
15564
15607
|
var SERVICE_UNINSTALL_COMMAND = "sailorbridge host service uninstall";
|
|
@@ -15585,6 +15628,9 @@ async function main(argv) {
|
|
|
15585
15628
|
if (command === "host" && argv[1] === "run" && output2.provided) {
|
|
15586
15629
|
throw new Error("host run does not produce a finite result; remove --format");
|
|
15587
15630
|
}
|
|
15631
|
+
if (command === "worker" && argv[1] === "serve" && output2.provided) {
|
|
15632
|
+
throw new Error("worker serve does not produce a finite result; remove --format");
|
|
15633
|
+
}
|
|
15588
15634
|
await _rejectUnsupportedCliInput(command, argv.slice(1), process.stdin);
|
|
15589
15635
|
if (command === "login") {
|
|
15590
15636
|
await _hostLogin(argv.slice(1), output2.format);
|
|
@@ -15621,7 +15667,7 @@ async function main(argv) {
|
|
|
15621
15667
|
await runHostWorkspaceCommandFromCli(argv.slice(2), { stdout: process.stdout, format: output2.format });
|
|
15622
15668
|
return;
|
|
15623
15669
|
}
|
|
15624
|
-
if (command === "worker") {
|
|
15670
|
+
if (command === "worker" && argv[1] === "serve") {
|
|
15625
15671
|
await runWorkerFromCli(argv.slice(2));
|
|
15626
15672
|
return;
|
|
15627
15673
|
}
|
|
@@ -15664,8 +15710,18 @@ async function main(argv) {
|
|
|
15664
15710
|
writeOutput(process.stdout, await runTeamFromCli(client2, argv.slice(1), config, output2.format), output2.format);
|
|
15665
15711
|
return;
|
|
15666
15712
|
}
|
|
15667
|
-
if (command === "
|
|
15668
|
-
writeOutput(process.stdout, await
|
|
15713
|
+
if (command === "memory") {
|
|
15714
|
+
writeOutput(process.stdout, await runMemoryFromCli(client2, argv.slice(1), config, process.stdin, output2.format), output2.format);
|
|
15715
|
+
return;
|
|
15716
|
+
}
|
|
15717
|
+
if (command === "worker") {
|
|
15718
|
+
await runWorkerCommandFromCli(client2, argv.slice(1), config, {
|
|
15719
|
+
stdout: process.stdout,
|
|
15720
|
+
stderr: process.stderr,
|
|
15721
|
+
setExitCode: (code) => {
|
|
15722
|
+
process.exitCode = code;
|
|
15723
|
+
}
|
|
15724
|
+
}, output2.format);
|
|
15669
15725
|
return;
|
|
15670
15726
|
}
|
|
15671
15727
|
if (command === "enrollment") {
|
|
@@ -15690,7 +15746,7 @@ var KNOWN_COMMANDS = new Set([
|
|
|
15690
15746
|
"host",
|
|
15691
15747
|
"login",
|
|
15692
15748
|
"mailbox",
|
|
15693
|
-
"
|
|
15749
|
+
"memory",
|
|
15694
15750
|
"refresh",
|
|
15695
15751
|
"reply",
|
|
15696
15752
|
"supervise",
|
|
@@ -15707,7 +15763,7 @@ var SUBCOMMANDS = {
|
|
|
15707
15763
|
event: new Set(["add", "disable", "enable", "list", "rm"]),
|
|
15708
15764
|
host: new Set([...HOST_RELAY_SUBCOMMANDS, "run", "service", "start-request", "statusline", "workspace"]),
|
|
15709
15765
|
mailbox: new Set(["ack", "claim", "peek", "send"]),
|
|
15710
|
-
|
|
15766
|
+
memory: new Set(["current", "index", "pin", "search", "set", "unpin"]),
|
|
15711
15767
|
supervise: new Set,
|
|
15712
15768
|
task: new Set([
|
|
15713
15769
|
"activate",
|
|
@@ -15723,8 +15779,8 @@ var SUBCOMMANDS = {
|
|
|
15723
15779
|
"ready",
|
|
15724
15780
|
"unpark"
|
|
15725
15781
|
]),
|
|
15726
|
-
team: new Set(["bootstrap"
|
|
15727
|
-
worker: new Set(["serve"])
|
|
15782
|
+
team: new Set(["bootstrap"]),
|
|
15783
|
+
worker: new Set(["job", "list", "run", "serve"])
|
|
15728
15784
|
};
|
|
15729
15785
|
function _assertKnownCommand(command, subcommand) {
|
|
15730
15786
|
if (!KNOWN_COMMANDS.has(command))
|
|
@@ -15740,7 +15796,7 @@ function _assertKnownCommand(command, subcommand) {
|
|
|
15740
15796
|
}
|
|
15741
15797
|
async function _rejectUnsupportedCliInput(command, args, input) {
|
|
15742
15798
|
const subcommand = args[0];
|
|
15743
|
-
const acceptsInput = command === "reply" || command === "host" && subcommand === "statusline" || command === "mailbox" && subcommand === "send" || command === "task" && subcommand === "create" || command === "ask" && subcommand !== "list" && subcommand !== "get" && subcommand !== "status" || command === "
|
|
15799
|
+
const acceptsInput = command === "reply" || command === "host" && subcommand === "statusline" || command === "mailbox" && subcommand === "send" || command === "task" && subcommand === "create" || command === "ask" && subcommand !== "list" && subcommand !== "get" && subcommand !== "status" || command === "memory" && (subcommand === "set" || subcommand === "search");
|
|
15744
15800
|
if (!acceptsInput)
|
|
15745
15801
|
await rejectUnusedTextInput(input, `${command}${subcommand ? ` ${subcommand}` : ""} does not accept stdin`);
|
|
15746
15802
|
}
|
|
@@ -15986,6 +16042,7 @@ async function supervise(client2, argv, config) {
|
|
|
15986
16042
|
agentName: parsed.agentName,
|
|
15987
16043
|
engine: parsed.cli,
|
|
15988
16044
|
hostEnv: process.env,
|
|
16045
|
+
teamMemoryEntitled: await resolveTeamMemoryEntitlement(client2, config.captainId, config.session),
|
|
15989
16046
|
descriptor: {
|
|
15990
16047
|
key_id: "direct-supervise",
|
|
15991
16048
|
relay_url: config.relayUrl,
|
|
@@ -16208,30 +16265,38 @@ function _usageText() {
|
|
|
16208
16265
|
creates an auto-numbered team and starts every template slot on one paired online host.
|
|
16209
16266
|
sailorbridge create pair [--host H] [--workspace PATH|GIT_URL] [--engine claude|codex] [--name N] [--session S]
|
|
16210
16267
|
builds a writer+reviewer dev pair on a paired online host; workspace defaults to the host's reported one.
|
|
16268
|
+
sailorbridge create planner [--host H] [--workspace PATH] [--engine claude|codex] [--session S]
|
|
16269
|
+
adds the team's single planner through the console slot + start flow.
|
|
16270
|
+
sailorbridge create tester [--host H] [--workspace PATH] [--engine claude|codex] [--session S]
|
|
16271
|
+
adds one tester through the console slot + start flow.
|
|
16211
16272
|
sailorbridge create worker --name N --concurrency N [--host H] [--workspace PATH] [--session S]
|
|
16212
16273
|
starts a worker on a paired online host. All create commands: [--api-url URL] [--credentials-path PATH] [--format plain|json]
|
|
16213
16274
|
sailorbridge team bootstrap --template classic3 --slot SLOT=HOST:claude|codex [--slot ...] [--session S]
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
sail
|
|
16217
|
-
sail
|
|
16218
|
-
sail
|
|
16219
|
-
sail
|
|
16220
|
-
sail rag unpin KEY (moderator/captain only)
|
|
16221
|
-
sail rag index
|
|
16275
|
+
sail memory set KEY (--stdin | --file PATH) --source SOURCE
|
|
16276
|
+
sail memory search [QUERY]
|
|
16277
|
+
sail memory current KEY
|
|
16278
|
+
sail memory pin KEY (moderator/captain only)
|
|
16279
|
+
sail memory unpin KEY (moderator/captain only)
|
|
16280
|
+
sail memory index
|
|
16222
16281
|
sailorbridge enrollment --session S --agent A --roles operator [--host H] [--name N]
|
|
16223
16282
|
sailorbridge host list|register|heartbeat [--host-id H] [--display-name N]
|
|
16224
16283
|
sailorbridge host consume --once --dry-run-start [--host-id H] [--display-name N]
|
|
16225
16284
|
sailorbridge host service install|uninstall|status [--binary PATH] [--credentials-path PATH] [--workspace-root DIR] [--service-dir DIR] [--service-name NAME]
|
|
16226
16285
|
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]
|
|
16227
16286
|
sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR]
|
|
16287
|
+
sailorbridge worker list [--format plain|json]
|
|
16288
|
+
sailorbridge worker run [--detach] --worker NAME [--timeout SEC] [--format plain|json] -- ARGV...
|
|
16289
|
+
sailorbridge worker job ID [--format plain|json]
|
|
16290
|
+
list/run/job require SAILORBRIDGE_CAPTAIN_ID; list/run also require SAILORBRIDGE_SESSION.
|
|
16228
16291
|
sailorbridge worker serve [--relay-url URL] [--worker NAME] [--workdir DIR] [--max-concurrency N]
|
|
16229
16292
|
sailorbridge supervise --agent A --cli claude|codex --cwd DIR --i-own-this-agent [--transport tmux] [--tmux-session S] [-- COMMAND...]
|
|
16230
16293
|
|
|
16231
16294
|
Environment:
|
|
16232
16295
|
SAILORBRIDGE_RELAY_URL / SAILORBRIDGE_TOKEN
|
|
16233
16296
|
SAILORBRIDGE_API_URL overrides the default product API for login/tutorial (${DEFAULT_PRODUCT_API_URL}); --api-url wins
|
|
16234
|
-
SAILORBRIDGE_AGENT_NAME
|
|
16297
|
+
SAILORBRIDGE_AGENT_NAME
|
|
16298
|
+
SAILORBRIDGE_CAPTAIN_ID is required for worker list/run/job.
|
|
16299
|
+
SAILORBRIDGE_SESSION is also required for worker list/run.
|
|
16235
16300
|
SAILORBRIDGE_HOST_ID / SAILORBRIDGE_HOST_DISPLAY_NAME
|
|
16236
16301
|
SAILORBRIDGE_NO_SERVICE=1 makes login skip the host service install
|
|
16237
16302
|
SAILORBRIDGE_DEBUG=1 prints the internal pairing id during login (diagnostics only)
|
|
@@ -16272,5 +16337,5 @@ function _looksLikeNetworkFailure(message) {
|
|
|
16272
16337
|
return /fetch failed|network|ECONN(?:REFUSED|RESET)|ENOTFOUND|ETIMEDOUT/iu.test(message);
|
|
16273
16338
|
}
|
|
16274
16339
|
|
|
16275
|
-
//# debugId=
|
|
16340
|
+
//# debugId=9889EE8093CEB27064756E2164756E21
|
|
16276
16341
|
//# sourceMappingURL=cli.js.map
|