@openscout/scout 0.2.58 → 0.2.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{arc.es-Bkj9Bkvx.js → arc.es-DgeMPL-I.js} +1 -1
- package/dist/client/assets/index-25TJx0iN.js +140 -0
- package/dist/client/assets/index-B1kglsyi.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/main.mjs +1275 -94
- package/dist/pair-supervisor.mjs +1079 -73
- package/dist/scout-control-plane-web.mjs +629 -461
- package/package.json +2 -2
- package/dist/client/assets/index-BDyu05Hi.js +0 -140
- package/dist/client/assets/index-pbcZpgCR.css +0 -1
package/dist/main.mjs
CHANGED
|
@@ -464,6 +464,7 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
|
|
|
464
464
|
let agentName;
|
|
465
465
|
let displayName;
|
|
466
466
|
let harness;
|
|
467
|
+
let model;
|
|
467
468
|
let requesterId = null;
|
|
468
469
|
let noInput = false;
|
|
469
470
|
for (let index = 0;index < parsed.args.length; index += 1) {
|
|
@@ -486,6 +487,12 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
|
|
|
486
487
|
index = value.nextIndex;
|
|
487
488
|
continue;
|
|
488
489
|
}
|
|
490
|
+
if (current === "--model" || current.startsWith("--model=")) {
|
|
491
|
+
const value = parseFlagValue(parsed.args, index, "--model");
|
|
492
|
+
model = value.value;
|
|
493
|
+
index = value.nextIndex;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
489
496
|
if (current === "--as" || current.startsWith("--as=")) {
|
|
490
497
|
const value = parseFlagValue(parsed.args, index, "--as");
|
|
491
498
|
requesterId = value.value;
|
|
@@ -520,6 +527,7 @@ function parseCardCreateCommandOptions(args, defaultCurrentDirectory) {
|
|
|
520
527
|
agentName,
|
|
521
528
|
displayName,
|
|
522
529
|
harness,
|
|
530
|
+
model,
|
|
523
531
|
requesterId,
|
|
524
532
|
noInput
|
|
525
533
|
};
|
|
@@ -9446,8 +9454,107 @@ var init_src = __esm(() => {
|
|
|
9446
9454
|
|
|
9447
9455
|
// ../runtime/src/codex-app-server.ts
|
|
9448
9456
|
import { spawn as spawn3 } from "child_process";
|
|
9449
|
-
import {
|
|
9457
|
+
import { constants as constants3 } from "fs";
|
|
9458
|
+
import { access as access3, appendFile as appendFile3, mkdir as mkdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
9450
9459
|
import { delimiter as delimiter3, join as join9 } from "path";
|
|
9460
|
+
function normalizeCodexModelValue(value) {
|
|
9461
|
+
const trimmed = value?.trim();
|
|
9462
|
+
return trimmed ? trimmed : null;
|
|
9463
|
+
}
|
|
9464
|
+
function encodeCodexModelConfig(model) {
|
|
9465
|
+
return `model=${JSON.stringify(model)}`;
|
|
9466
|
+
}
|
|
9467
|
+
function parseCodexModelConfig(value) {
|
|
9468
|
+
const trimmed = value?.trim();
|
|
9469
|
+
if (!trimmed) {
|
|
9470
|
+
return null;
|
|
9471
|
+
}
|
|
9472
|
+
const separatorIndex = trimmed.indexOf("=");
|
|
9473
|
+
if (separatorIndex <= 0) {
|
|
9474
|
+
return null;
|
|
9475
|
+
}
|
|
9476
|
+
const key = trimmed.slice(0, separatorIndex).trim();
|
|
9477
|
+
if (key !== "model") {
|
|
9478
|
+
return null;
|
|
9479
|
+
}
|
|
9480
|
+
const rawValue = trimmed.slice(separatorIndex + 1).trim();
|
|
9481
|
+
if (!rawValue) {
|
|
9482
|
+
return null;
|
|
9483
|
+
}
|
|
9484
|
+
if (rawValue.startsWith('"') && rawValue.endsWith('"') || rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
9485
|
+
return rawValue.slice(1, -1) || null;
|
|
9486
|
+
}
|
|
9487
|
+
return rawValue;
|
|
9488
|
+
}
|
|
9489
|
+
function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
9490
|
+
const args = Array.isArray(launchArgs) ? launchArgs.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
9491
|
+
const normalized = [];
|
|
9492
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
9493
|
+
const current = args[index] ?? "";
|
|
9494
|
+
if (current === "--model" || current === "-m") {
|
|
9495
|
+
const model = normalizeCodexModelValue(args[index + 1]);
|
|
9496
|
+
if (model) {
|
|
9497
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
9498
|
+
index += 1;
|
|
9499
|
+
continue;
|
|
9500
|
+
}
|
|
9501
|
+
normalized.push(current);
|
|
9502
|
+
continue;
|
|
9503
|
+
}
|
|
9504
|
+
if (current.startsWith("--model=")) {
|
|
9505
|
+
const model = normalizeCodexModelValue(current.slice("--model=".length));
|
|
9506
|
+
if (model) {
|
|
9507
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
9508
|
+
continue;
|
|
9509
|
+
}
|
|
9510
|
+
}
|
|
9511
|
+
if (current.startsWith("-m=")) {
|
|
9512
|
+
const model = normalizeCodexModelValue(current.slice(3));
|
|
9513
|
+
if (model) {
|
|
9514
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
9515
|
+
continue;
|
|
9516
|
+
}
|
|
9517
|
+
}
|
|
9518
|
+
if (current === "-c" || current === "--config") {
|
|
9519
|
+
const next = args[index + 1];
|
|
9520
|
+
if (typeof next === "string") {
|
|
9521
|
+
const model = parseCodexModelConfig(next);
|
|
9522
|
+
normalized.push(current === "--config" ? "--config" : "-c", model ? encodeCodexModelConfig(model) : next);
|
|
9523
|
+
index += 1;
|
|
9524
|
+
continue;
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
if (current.startsWith("--config=")) {
|
|
9528
|
+
const value = current.slice("--config=".length);
|
|
9529
|
+
const model = parseCodexModelConfig(value);
|
|
9530
|
+
normalized.push(model ? `--config=${encodeCodexModelConfig(model)}` : current);
|
|
9531
|
+
continue;
|
|
9532
|
+
}
|
|
9533
|
+
normalized.push(current);
|
|
9534
|
+
}
|
|
9535
|
+
return normalized;
|
|
9536
|
+
}
|
|
9537
|
+
function readCodexAppServerModelFromLaunchArgs(launchArgs) {
|
|
9538
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
9539
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
9540
|
+
const current = normalized[index] ?? "";
|
|
9541
|
+
if (current === "-c" || current === "--config") {
|
|
9542
|
+
const model = parseCodexModelConfig(normalized[index + 1]);
|
|
9543
|
+
if (model) {
|
|
9544
|
+
return model;
|
|
9545
|
+
}
|
|
9546
|
+
index += 1;
|
|
9547
|
+
continue;
|
|
9548
|
+
}
|
|
9549
|
+
if (current.startsWith("--config=")) {
|
|
9550
|
+
const model = parseCodexModelConfig(current.slice("--config=".length));
|
|
9551
|
+
if (model) {
|
|
9552
|
+
return model;
|
|
9553
|
+
}
|
|
9554
|
+
}
|
|
9555
|
+
}
|
|
9556
|
+
return null;
|
|
9557
|
+
}
|
|
9451
9558
|
function sessionKey2(options) {
|
|
9452
9559
|
return `${options.agentName}:${options.sessionId}`;
|
|
9453
9560
|
}
|
|
@@ -9556,6 +9663,13 @@ function isMissingCodexRolloutError2(error) {
|
|
|
9556
9663
|
const message = errorMessage3(error).toLowerCase();
|
|
9557
9664
|
return message.includes("no rollout found for thread id");
|
|
9558
9665
|
}
|
|
9666
|
+
function resolveCodexCompletionGraceMs() {
|
|
9667
|
+
const parsed = Number.parseInt(process.env.OPENSCOUT_CODEX_COMPLETION_GRACE_MS ?? "", 10);
|
|
9668
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
9669
|
+
return parsed;
|
|
9670
|
+
}
|
|
9671
|
+
return 60000;
|
|
9672
|
+
}
|
|
9559
9673
|
|
|
9560
9674
|
class CodexAppServerSession {
|
|
9561
9675
|
options;
|
|
@@ -9745,7 +9859,7 @@ class CodexAppServerSession {
|
|
|
9745
9859
|
systemPrompt: options.systemPrompt,
|
|
9746
9860
|
threadId: options.threadId ?? null,
|
|
9747
9861
|
requireExistingThread: options.requireExistingThread === true,
|
|
9748
|
-
launchArgs:
|
|
9862
|
+
launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
|
|
9749
9863
|
});
|
|
9750
9864
|
}
|
|
9751
9865
|
async ensureStarted() {
|
|
@@ -9767,6 +9881,7 @@ class CodexAppServerSession {
|
|
|
9767
9881
|
await mkdir5(this.options.logsDirectory, { recursive: true });
|
|
9768
9882
|
await writeFile5(join9(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
|
|
9769
9883
|
const codexExecutable = await resolveCodexExecutable2();
|
|
9884
|
+
const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
|
|
9770
9885
|
const env = buildManagedAgentEnvironment({
|
|
9771
9886
|
agentName: this.options.agentName,
|
|
9772
9887
|
currentDirectory: this.options.cwd,
|
|
@@ -9777,7 +9892,8 @@ class CodexAppServerSession {
|
|
|
9777
9892
|
...buildScoutMcpCodexLaunchArgs({
|
|
9778
9893
|
currentDirectory: this.options.cwd,
|
|
9779
9894
|
env
|
|
9780
|
-
})
|
|
9895
|
+
}),
|
|
9896
|
+
...launchArgs
|
|
9781
9897
|
], {
|
|
9782
9898
|
cwd: this.options.cwd,
|
|
9783
9899
|
env,
|
|
@@ -9875,6 +9991,8 @@ class CodexAppServerSession {
|
|
|
9875
9991
|
turnId: "",
|
|
9876
9992
|
startedAt: Date.now(),
|
|
9877
9993
|
timer: null,
|
|
9994
|
+
graceTimer: null,
|
|
9995
|
+
timedOutAt: null,
|
|
9878
9996
|
messageOrder: [],
|
|
9879
9997
|
messageByItemId: new Map,
|
|
9880
9998
|
resolve: resolve6,
|
|
@@ -9882,20 +10000,43 @@ class CodexAppServerSession {
|
|
|
9882
10000
|
watchers: []
|
|
9883
10001
|
};
|
|
9884
10002
|
turn.timer = setTimeout(() => {
|
|
9885
|
-
this.
|
|
9886
|
-
return;
|
|
9887
|
-
});
|
|
9888
|
-
if (this.activeTurn === turn) {
|
|
9889
|
-
this.activeTurn = null;
|
|
9890
|
-
}
|
|
9891
|
-
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
9892
|
-
watcher.reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
9893
|
-
}
|
|
9894
|
-
reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
10003
|
+
this.scheduleTurnTimeout(turn, timeoutMs);
|
|
9895
10004
|
}, timeoutMs);
|
|
9896
10005
|
this.activeTurn = turn;
|
|
9897
10006
|
return turn;
|
|
9898
10007
|
}
|
|
10008
|
+
buildTurnTimeoutError(timeoutMs) {
|
|
10009
|
+
return new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`);
|
|
10010
|
+
}
|
|
10011
|
+
scheduleTurnTimeout(turn, timeoutMs) {
|
|
10012
|
+
if (turn.timedOutAt !== null) {
|
|
10013
|
+
return;
|
|
10014
|
+
}
|
|
10015
|
+
turn.timedOutAt = Date.now();
|
|
10016
|
+
this.interrupt().catch(() => {
|
|
10017
|
+
return;
|
|
10018
|
+
});
|
|
10019
|
+
const graceMs = resolveCodexCompletionGraceMs();
|
|
10020
|
+
if (graceMs <= 0) {
|
|
10021
|
+
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
10022
|
+
return;
|
|
10023
|
+
}
|
|
10024
|
+
turn.graceTimer = setTimeout(() => {
|
|
10025
|
+
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
10026
|
+
}, graceMs);
|
|
10027
|
+
}
|
|
10028
|
+
rejectTimedOutTurn(turn, timeoutMs) {
|
|
10029
|
+
if (this.activeTurn !== turn) {
|
|
10030
|
+
this.clearActiveTurn(turn);
|
|
10031
|
+
return;
|
|
10032
|
+
}
|
|
10033
|
+
this.clearActiveTurn(turn);
|
|
10034
|
+
const error = this.buildTurnTimeoutError(timeoutMs);
|
|
10035
|
+
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
10036
|
+
watcher.reject(error);
|
|
10037
|
+
}
|
|
10038
|
+
turn.reject(error);
|
|
10039
|
+
}
|
|
9899
10040
|
addTurnWatcher(turn, resolve6, reject, timeoutMs) {
|
|
9900
10041
|
const watcher = {
|
|
9901
10042
|
resolve: resolve6,
|
|
@@ -9929,6 +10070,9 @@ class CodexAppServerSession {
|
|
|
9929
10070
|
if (turn.timer) {
|
|
9930
10071
|
clearTimeout(turn.timer);
|
|
9931
10072
|
}
|
|
10073
|
+
if (turn.graceTimer) {
|
|
10074
|
+
clearTimeout(turn.graceTimer);
|
|
10075
|
+
}
|
|
9932
10076
|
if (this.activeTurn === turn) {
|
|
9933
10077
|
this.activeTurn = null;
|
|
9934
10078
|
}
|
|
@@ -11134,6 +11278,128 @@ function normalizeLocalAgentCapabilities(value) {
|
|
|
11134
11278
|
function normalizeLocalAgentLaunchArgs(value) {
|
|
11135
11279
|
return Array.isArray(value) ? value.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
11136
11280
|
}
|
|
11281
|
+
function normalizeRequestedModel(value) {
|
|
11282
|
+
const trimmed = value?.trim();
|
|
11283
|
+
return trimmed ? trimmed : undefined;
|
|
11284
|
+
}
|
|
11285
|
+
function readClaudeLaunchModel(launchArgs) {
|
|
11286
|
+
for (let index = 0;index < launchArgs.length; index += 1) {
|
|
11287
|
+
const current = launchArgs[index] ?? "";
|
|
11288
|
+
if (current === "--model") {
|
|
11289
|
+
const next = launchArgs[index + 1]?.trim();
|
|
11290
|
+
return next || undefined;
|
|
11291
|
+
}
|
|
11292
|
+
if (current.startsWith("--model=")) {
|
|
11293
|
+
const next = current.slice("--model=".length).trim();
|
|
11294
|
+
return next || undefined;
|
|
11295
|
+
}
|
|
11296
|
+
}
|
|
11297
|
+
return;
|
|
11298
|
+
}
|
|
11299
|
+
function normalizeLaunchArgsForHarness(harness, value) {
|
|
11300
|
+
const normalized = normalizeLocalAgentLaunchArgs(value);
|
|
11301
|
+
if (harness === "codex") {
|
|
11302
|
+
return normalizeCodexAppServerLaunchArgs(normalized);
|
|
11303
|
+
}
|
|
11304
|
+
return normalized;
|
|
11305
|
+
}
|
|
11306
|
+
function readLaunchModelForHarness(harness, launchArgs) {
|
|
11307
|
+
if (harness === "codex") {
|
|
11308
|
+
return readCodexAppServerModelFromLaunchArgs(launchArgs) ?? undefined;
|
|
11309
|
+
}
|
|
11310
|
+
if (harness === "claude") {
|
|
11311
|
+
return readClaudeLaunchModel(launchArgs ?? []);
|
|
11312
|
+
}
|
|
11313
|
+
return;
|
|
11314
|
+
}
|
|
11315
|
+
function stripLaunchModelForHarness(harness, launchArgs) {
|
|
11316
|
+
if (harness === "codex") {
|
|
11317
|
+
const next = [];
|
|
11318
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
11319
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
11320
|
+
const current = normalized[index] ?? "";
|
|
11321
|
+
if (current === "-c" || current === "--config") {
|
|
11322
|
+
const value = normalized[index + 1];
|
|
11323
|
+
if (readCodexAppServerModelFromLaunchArgs(value ? [current, value] : [current])) {
|
|
11324
|
+
index += 1;
|
|
11325
|
+
continue;
|
|
11326
|
+
}
|
|
11327
|
+
next.push(current);
|
|
11328
|
+
if (value) {
|
|
11329
|
+
next.push(value);
|
|
11330
|
+
index += 1;
|
|
11331
|
+
}
|
|
11332
|
+
continue;
|
|
11333
|
+
}
|
|
11334
|
+
if (current.startsWith("--config=")) {
|
|
11335
|
+
if (readCodexAppServerModelFromLaunchArgs([current])) {
|
|
11336
|
+
continue;
|
|
11337
|
+
}
|
|
11338
|
+
}
|
|
11339
|
+
next.push(current);
|
|
11340
|
+
}
|
|
11341
|
+
return next;
|
|
11342
|
+
}
|
|
11343
|
+
if (harness === "claude") {
|
|
11344
|
+
const next = [];
|
|
11345
|
+
const normalized = normalizeLocalAgentLaunchArgs(launchArgs);
|
|
11346
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
11347
|
+
const current = normalized[index] ?? "";
|
|
11348
|
+
if (current === "--model") {
|
|
11349
|
+
index += 1;
|
|
11350
|
+
continue;
|
|
11351
|
+
}
|
|
11352
|
+
if (current.startsWith("--model=")) {
|
|
11353
|
+
continue;
|
|
11354
|
+
}
|
|
11355
|
+
next.push(current);
|
|
11356
|
+
}
|
|
11357
|
+
return next;
|
|
11358
|
+
}
|
|
11359
|
+
return normalizeLocalAgentLaunchArgs(launchArgs);
|
|
11360
|
+
}
|
|
11361
|
+
function buildLaunchArgsForRequestedModel(harness, model) {
|
|
11362
|
+
if (harness === "codex") {
|
|
11363
|
+
return normalizeCodexAppServerLaunchArgs(["--model", model]);
|
|
11364
|
+
}
|
|
11365
|
+
if (harness === "claude") {
|
|
11366
|
+
return ["--model", model];
|
|
11367
|
+
}
|
|
11368
|
+
return [];
|
|
11369
|
+
}
|
|
11370
|
+
function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
11371
|
+
const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
|
|
11372
|
+
const requestedModel = normalizeRequestedModel(model);
|
|
11373
|
+
if (!requestedModel) {
|
|
11374
|
+
return normalized;
|
|
11375
|
+
}
|
|
11376
|
+
return [
|
|
11377
|
+
...stripLaunchModelForHarness(harness, normalized),
|
|
11378
|
+
...buildLaunchArgsForRequestedModel(harness, requestedModel)
|
|
11379
|
+
];
|
|
11380
|
+
}
|
|
11381
|
+
function defaultHarnessForOverride(override, fallback = "claude") {
|
|
11382
|
+
return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
|
|
11383
|
+
}
|
|
11384
|
+
function launchArgsForOverrideHarness(override, harness) {
|
|
11385
|
+
const profileLaunchArgs = override.harnessProfiles?.[harness]?.launchArgs;
|
|
11386
|
+
if (profileLaunchArgs) {
|
|
11387
|
+
return normalizeLaunchArgsForHarness(harness, profileLaunchArgs);
|
|
11388
|
+
}
|
|
11389
|
+
if (harness === defaultHarnessForOverride(override, harness)) {
|
|
11390
|
+
return normalizeLaunchArgsForHarness(harness, override.launchArgs);
|
|
11391
|
+
}
|
|
11392
|
+
return [];
|
|
11393
|
+
}
|
|
11394
|
+
function overrideHarnessProfile(override, harness) {
|
|
11395
|
+
const profile = override.harnessProfiles?.[harness];
|
|
11396
|
+
return {
|
|
11397
|
+
cwd: normalizeProjectPath(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
|
|
11398
|
+
transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
|
|
11399
|
+
sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
|
|
11400
|
+
launchArgs: launchArgsForOverrideHarness(override, harness)
|
|
11401
|
+
};
|
|
11402
|
+
}
|
|
11137
11403
|
function normalizeManagedHarness2(value, fallback) {
|
|
11138
11404
|
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
11139
11405
|
}
|
|
@@ -11149,7 +11415,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11149
11415
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
11150
11416
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
11151
11417
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
11152
|
-
launchArgs:
|
|
11418
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
11153
11419
|
};
|
|
11154
11420
|
}
|
|
11155
11421
|
const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
|
|
@@ -11158,7 +11424,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11158
11424
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
11159
11425
|
transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
|
|
11160
11426
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
|
|
11161
|
-
launchArgs:
|
|
11427
|
+
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
|
|
11162
11428
|
};
|
|
11163
11429
|
}
|
|
11164
11430
|
if (!nextProfiles[defaultHarness]) {
|
|
@@ -11166,7 +11432,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11166
11432
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
11167
11433
|
transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
|
|
11168
11434
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
|
|
11169
|
-
launchArgs:
|
|
11435
|
+
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
|
|
11170
11436
|
};
|
|
11171
11437
|
}
|
|
11172
11438
|
for (const harness of ["claude", "codex"]) {
|
|
@@ -11178,7 +11444,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11178
11444
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
11179
11445
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
11180
11446
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
11181
|
-
launchArgs:
|
|
11447
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
11182
11448
|
};
|
|
11183
11449
|
}
|
|
11184
11450
|
return nextProfiles;
|
|
@@ -11202,14 +11468,14 @@ function recordForHarness(record, harnessOverride) {
|
|
|
11202
11468
|
tmuxSession: fallbackSessionId,
|
|
11203
11469
|
cwd: fallbackCwd,
|
|
11204
11470
|
transport: fallbackTransport,
|
|
11205
|
-
launchArgs:
|
|
11471
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
11206
11472
|
harnessProfiles: {
|
|
11207
11473
|
...normalized.harnessProfiles,
|
|
11208
11474
|
[selectedHarness]: {
|
|
11209
11475
|
cwd: fallbackCwd,
|
|
11210
11476
|
transport: fallbackTransport,
|
|
11211
11477
|
sessionId: fallbackSessionId,
|
|
11212
|
-
launchArgs:
|
|
11478
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
|
|
11213
11479
|
}
|
|
11214
11480
|
}
|
|
11215
11481
|
};
|
|
@@ -11251,7 +11517,7 @@ function normalizeLocalAgentRecord(agentId, record) {
|
|
|
11251
11517
|
harnessProfiles,
|
|
11252
11518
|
transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
|
|
11253
11519
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
11254
|
-
launchArgs: activeProfile?.launchArgs ??
|
|
11520
|
+
launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs)
|
|
11255
11521
|
};
|
|
11256
11522
|
}
|
|
11257
11523
|
function localAgentStatusFromRecord(agentId, record, source) {
|
|
@@ -11320,7 +11586,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
|
|
|
11320
11586
|
source: existing?.source && existing.source !== "inferred" ? existing.source : "manual",
|
|
11321
11587
|
startedAt: normalizedRecord.startedAt,
|
|
11322
11588
|
systemPrompt: normalizedRecord.systemPrompt,
|
|
11323
|
-
launchArgs:
|
|
11589
|
+
launchArgs: normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs),
|
|
11324
11590
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
11325
11591
|
defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
|
|
11326
11592
|
harnessProfiles: normalizedRecord.harnessProfiles,
|
|
@@ -11341,7 +11607,7 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
11341
11607
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
|
|
11342
11608
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
11343
11609
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
11344
|
-
launchArgs:
|
|
11610
|
+
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
|
|
11345
11611
|
};
|
|
11346
11612
|
}
|
|
11347
11613
|
function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
@@ -11352,7 +11618,7 @@ function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
11352
11618
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "claude_stream_json" }),
|
|
11353
11619
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
11354
11620
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
11355
|
-
launchArgs:
|
|
11621
|
+
launchArgs: normalizeLaunchArgsForHarness("claude", record.launchArgs)
|
|
11356
11622
|
};
|
|
11357
11623
|
}
|
|
11358
11624
|
function isLocalAgentRecordOnline(agentName, record) {
|
|
@@ -11386,7 +11652,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
|
|
|
11386
11652
|
return initialMessage;
|
|
11387
11653
|
}
|
|
11388
11654
|
function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
|
|
11389
|
-
const extraArgs = shellQuoteArguments(
|
|
11655
|
+
const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
|
|
11390
11656
|
if (normalizeLocalAgentHarness(record.harness) === "codex") {
|
|
11391
11657
|
return `exec bash ${JSON.stringify(workerScript ?? join11(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
|
|
11392
11658
|
}
|
|
@@ -11758,6 +12024,7 @@ async function startLocalAgent(input) {
|
|
|
11758
12024
|
const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
|
|
11759
12025
|
const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
|
|
11760
12026
|
const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
|
|
12027
|
+
const launchArgs = applyRequestedModelToLaunchArgs(effectiveHarness, [], input.model);
|
|
11761
12028
|
const existingForInstance = overrides[instance.id];
|
|
11762
12029
|
if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
|
|
11763
12030
|
throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
|
|
@@ -11771,13 +12038,14 @@ async function startLocalAgent(input) {
|
|
|
11771
12038
|
projectConfigPath: coldProjectConfigPath,
|
|
11772
12039
|
source: "manual",
|
|
11773
12040
|
startedAt: nowSeconds2(),
|
|
12041
|
+
launchArgs,
|
|
11774
12042
|
defaultHarness: effectiveHarness,
|
|
11775
12043
|
harnessProfiles: {
|
|
11776
12044
|
[effectiveHarness]: {
|
|
11777
12045
|
cwd: effectiveCwd ?? projectRoot,
|
|
11778
12046
|
transport,
|
|
11779
12047
|
sessionId,
|
|
11780
|
-
launchArgs
|
|
12048
|
+
launchArgs
|
|
11781
12049
|
}
|
|
11782
12050
|
},
|
|
11783
12051
|
runtime: {
|
|
@@ -11798,6 +12066,9 @@ async function startLocalAgent(input) {
|
|
|
11798
12066
|
throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
|
|
11799
12067
|
}
|
|
11800
12068
|
const resolvedHarness = preferredHarness ?? matchingOverride.runtime?.harness;
|
|
12069
|
+
const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
|
|
12070
|
+
const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
|
|
12071
|
+
const nextLaunchArgs = applyRequestedModelToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), input.model);
|
|
11801
12072
|
overrides[instance.id] = {
|
|
11802
12073
|
agentId: instance.id,
|
|
11803
12074
|
definitionId: requestedDefinitionId,
|
|
@@ -11808,10 +12079,16 @@ async function startLocalAgent(input) {
|
|
|
11808
12079
|
source: "manual",
|
|
11809
12080
|
startedAt: matchingOverride.startedAt ?? nowSeconds2(),
|
|
11810
12081
|
systemPrompt: matchingOverride.systemPrompt,
|
|
11811
|
-
launchArgs: matchingOverride.launchArgs,
|
|
12082
|
+
launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
11812
12083
|
capabilities: matchingOverride.capabilities,
|
|
11813
|
-
defaultHarness:
|
|
11814
|
-
harnessProfiles:
|
|
12084
|
+
defaultHarness: nextDefaultHarness,
|
|
12085
|
+
harnessProfiles: {
|
|
12086
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
12087
|
+
[nextHarness]: {
|
|
12088
|
+
...overrideHarnessProfile(matchingOverride, nextHarness),
|
|
12089
|
+
launchArgs: nextLaunchArgs
|
|
12090
|
+
}
|
|
12091
|
+
},
|
|
11815
12092
|
runtime: {
|
|
11816
12093
|
cwd: effectiveCwd ?? matchingOverride.runtime?.cwd ?? matchingProjectRoot,
|
|
11817
12094
|
harness: resolvedHarness,
|
|
@@ -11823,6 +12100,22 @@ async function startLocalAgent(input) {
|
|
|
11823
12100
|
await writeRelayAgentOverrides(overrides);
|
|
11824
12101
|
targetAgentId = instance.id;
|
|
11825
12102
|
} else {
|
|
12103
|
+
if (input.model) {
|
|
12104
|
+
const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
|
|
12105
|
+
const nextLaunchArgs = applyRequestedModelToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), input.model);
|
|
12106
|
+
overrides[matchingAgentId] = {
|
|
12107
|
+
...matchingOverride,
|
|
12108
|
+
launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
12109
|
+
harnessProfiles: {
|
|
12110
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
12111
|
+
[existingHarness]: {
|
|
12112
|
+
...overrideHarnessProfile(matchingOverride, existingHarness),
|
|
12113
|
+
launchArgs: nextLaunchArgs
|
|
12114
|
+
}
|
|
12115
|
+
}
|
|
12116
|
+
};
|
|
12117
|
+
await writeRelayAgentOverrides(overrides);
|
|
12118
|
+
}
|
|
11826
12119
|
targetAgentId = matchingAgentId;
|
|
11827
12120
|
}
|
|
11828
12121
|
await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
|
|
@@ -11935,6 +12228,7 @@ function readPersistedClaudeSessionId(agentName) {
|
|
|
11935
12228
|
}
|
|
11936
12229
|
function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
11937
12230
|
const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
|
|
12231
|
+
const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
|
|
11938
12232
|
const definitionId = normalizedRecord.definitionId ?? agentId;
|
|
11939
12233
|
const displayName = titleCaseLocalAgentName(definitionId);
|
|
11940
12234
|
const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
|
|
@@ -11959,7 +12253,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
11959
12253
|
defaultSelector: instance.defaultSelector,
|
|
11960
12254
|
nodeQualifier: instance.nodeQualifier,
|
|
11961
12255
|
workspaceQualifier: instance.workspaceQualifier,
|
|
11962
|
-
branch: instance.branch
|
|
12256
|
+
branch: instance.branch,
|
|
12257
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
11963
12258
|
}
|
|
11964
12259
|
},
|
|
11965
12260
|
agent: {
|
|
@@ -11986,7 +12281,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
11986
12281
|
defaultSelector: instance.defaultSelector,
|
|
11987
12282
|
nodeQualifier: instance.nodeQualifier,
|
|
11988
12283
|
workspaceQualifier: instance.workspaceQualifier,
|
|
11989
|
-
branch: instance.branch
|
|
12284
|
+
branch: instance.branch,
|
|
12285
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
11990
12286
|
},
|
|
11991
12287
|
agentClass: "general",
|
|
11992
12288
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
@@ -12019,6 +12315,7 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
12019
12315
|
nodeQualifier: instance.nodeQualifier,
|
|
12020
12316
|
workspaceQualifier: instance.workspaceQualifier,
|
|
12021
12317
|
branch: instance.branch,
|
|
12318
|
+
...configuredModel ? { model: configuredModel } : {},
|
|
12022
12319
|
...externalSessionId ? { externalSessionId } : {}
|
|
12023
12320
|
}
|
|
12024
12321
|
}
|
|
@@ -13130,8 +13427,73 @@ async function sendScoutMessage(input) {
|
|
|
13130
13427
|
}
|
|
13131
13428
|
const currentDirectory = input.currentDirectory ?? process.cwd();
|
|
13132
13429
|
const createdAtMs = input.createdAtMs ?? Date.now();
|
|
13133
|
-
const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
|
|
13134
13430
|
const senderId = await resolveConversationActorId(broker.baseUrl, broker.snapshot, broker.node.id, input.senderId, currentDirectory);
|
|
13431
|
+
const requestedTargetLabel = input.targetLabel?.trim();
|
|
13432
|
+
if (requestedTargetLabel) {
|
|
13433
|
+
const targetResolution = await resolveSingleBrokerTarget(broker.snapshot, requestedTargetLabel, currentDirectory);
|
|
13434
|
+
if (targetResolution.kind !== "resolved") {
|
|
13435
|
+
return {
|
|
13436
|
+
usedBroker: true,
|
|
13437
|
+
invokedTargets: [],
|
|
13438
|
+
unresolvedTargets: [requestedTargetLabel]
|
|
13439
|
+
};
|
|
13440
|
+
}
|
|
13441
|
+
const target = targetResolution.target;
|
|
13442
|
+
const targetReady = await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory);
|
|
13443
|
+
if (!targetReady || target.agentId === senderId) {
|
|
13444
|
+
return {
|
|
13445
|
+
usedBroker: true,
|
|
13446
|
+
invokedTargets: [],
|
|
13447
|
+
unresolvedTargets: [requestedTargetLabel]
|
|
13448
|
+
};
|
|
13449
|
+
}
|
|
13450
|
+
let conversation2;
|
|
13451
|
+
if (!input.channel) {
|
|
13452
|
+
const dm = await ensureBrokerDirectConversationBetween(broker.baseUrl, broker.snapshot, broker.node.id, senderId, target.agentId);
|
|
13453
|
+
conversation2 = dm.conversation;
|
|
13454
|
+
} else {
|
|
13455
|
+
conversation2 = await ensureBrokerConversation(broker.baseUrl, broker.snapshot, broker.node.id, input.channel, senderId, [target.agentId]);
|
|
13456
|
+
}
|
|
13457
|
+
const messageId2 = `m-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
13458
|
+
const speechText2 = input.shouldSpeak ? stripScoutAgentSelectorLabels(input.body) : "";
|
|
13459
|
+
const returnAddress2 = buildScoutReturnAddress2(broker.snapshot, senderId, {
|
|
13460
|
+
conversationId: conversation2.id,
|
|
13461
|
+
replyToMessageId: messageId2
|
|
13462
|
+
});
|
|
13463
|
+
await brokerPostJson(broker.baseUrl, scoutBrokerPaths.v1.messages, {
|
|
13464
|
+
id: messageId2,
|
|
13465
|
+
conversationId: conversation2.id,
|
|
13466
|
+
actorId: senderId,
|
|
13467
|
+
originNodeId: broker.node.id,
|
|
13468
|
+
class: conversation2.kind === "system" ? "system" : "agent",
|
|
13469
|
+
body: input.body,
|
|
13470
|
+
mentions: [{ actorId: target.agentId, label: target.label }],
|
|
13471
|
+
speech: speechText2 ? { text: speechText2 } : undefined,
|
|
13472
|
+
audience: {
|
|
13473
|
+
notify: [target.agentId],
|
|
13474
|
+
reason: relayAudienceReason(conversation2)
|
|
13475
|
+
},
|
|
13476
|
+
visibility: conversation2.visibility,
|
|
13477
|
+
policy: "durable",
|
|
13478
|
+
createdAt: createdAtMs,
|
|
13479
|
+
metadata: {
|
|
13480
|
+
source: "scout-cli",
|
|
13481
|
+
relayChannel: relayChannelMetadata(conversation2, input.channel),
|
|
13482
|
+
relayTargetIds: [target.agentId],
|
|
13483
|
+
relayMessageId: messageId2,
|
|
13484
|
+
returnAddress: returnAddress2
|
|
13485
|
+
}
|
|
13486
|
+
});
|
|
13487
|
+
return {
|
|
13488
|
+
usedBroker: true,
|
|
13489
|
+
conversationId: conversation2.id,
|
|
13490
|
+
messageId: messageId2,
|
|
13491
|
+
invokedTargets: [target.agentId],
|
|
13492
|
+
unresolvedTargets: [],
|
|
13493
|
+
routeKind: relayRouteKind(conversation2)
|
|
13494
|
+
};
|
|
13495
|
+
}
|
|
13496
|
+
const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
|
|
13135
13497
|
const availableTargets = (await Promise.all(mentionResolution.resolved.map(async (target) => await ensureTargetRelayAgentRegistered(broker.baseUrl, broker.snapshot, broker.node.id, target.agentId, currentDirectory) ? target : null))).filter((target) => Boolean(target));
|
|
13136
13498
|
const validTargets = [
|
|
13137
13499
|
...new Set(availableTargets.map((target) => target.agentId).filter((target) => target !== senderId && Boolean(broker.snapshot.agents[target])))
|
|
@@ -14413,6 +14775,7 @@ function buildScoutAgentCard(binding, options = {}) {
|
|
|
14413
14775
|
const selector = binding.agent.selector?.trim() || metadataString3(binding.agent.metadata, "selector");
|
|
14414
14776
|
const defaultSelector = binding.agent.defaultSelector?.trim() || metadataString3(binding.agent.metadata, "defaultSelector");
|
|
14415
14777
|
const branch = metadataString3(binding.agent.metadata, "branch") || metadataString3(binding.endpoint.metadata, "branch");
|
|
14778
|
+
const model = metadataString3(binding.endpoint.metadata, "model") || metadataString3(binding.agent.metadata, "model");
|
|
14416
14779
|
const description = metadataString3(binding.agent.metadata, "description");
|
|
14417
14780
|
const version = metadataString3(binding.agent.metadata, "version");
|
|
14418
14781
|
const documentationUrl = metadataString3(binding.agent.metadata, "documentationUrl") || metadataString3(binding.agent.metadata, "docsUrl");
|
|
@@ -14470,7 +14833,8 @@ function buildScoutAgentCard(binding, options = {}) {
|
|
|
14470
14833
|
metadata: {
|
|
14471
14834
|
actorId: binding.actor.id,
|
|
14472
14835
|
endpointId: binding.endpoint.id,
|
|
14473
|
-
wakePolicy: binding.agent.wakePolicy
|
|
14836
|
+
wakePolicy: binding.agent.wakePolicy,
|
|
14837
|
+
...model ? { model } : {}
|
|
14474
14838
|
}
|
|
14475
14839
|
};
|
|
14476
14840
|
}
|
|
@@ -14504,6 +14868,7 @@ async function createScoutAgentCard(input) {
|
|
|
14504
14868
|
agentName: input.agentName,
|
|
14505
14869
|
displayName: input.displayName,
|
|
14506
14870
|
harness: input.harness,
|
|
14871
|
+
model: input.model,
|
|
14507
14872
|
currentDirectory: input.currentDirectory
|
|
14508
14873
|
});
|
|
14509
14874
|
const currentDirectory = input.currentDirectory ?? input.projectPath;
|
|
@@ -14588,7 +14953,7 @@ __export(exports_card, {
|
|
|
14588
14953
|
import { createInterface } from "readline";
|
|
14589
14954
|
function renderCardCommandHelp() {
|
|
14590
14955
|
return [
|
|
14591
|
-
"Usage: scout card create [path] [--name <alias>] [--display-name <name>] [--harness <claude|codex>] [--as <requester>] [--no-input] [--path <path>]",
|
|
14956
|
+
"Usage: scout card create [path] [--name <alias>] [--display-name <name>] [--harness <claude|codex>] [--model <model>] [--as <requester>] [--no-input] [--path <path>]",
|
|
14592
14957
|
"",
|
|
14593
14958
|
"Create a dedicated Scout agent card with a reply-ready return address.",
|
|
14594
14959
|
"",
|
|
@@ -14597,7 +14962,7 @@ function renderCardCommandHelp() {
|
|
|
14597
14962
|
"",
|
|
14598
14963
|
"Examples:",
|
|
14599
14964
|
" scout card create",
|
|
14600
|
-
" scout card create ~/dev/openscout-worktrees/shell-fix --name shellfix --harness claude"
|
|
14965
|
+
" scout card create ~/dev/openscout-worktrees/shell-fix --name shellfix --harness claude --model claude-sonnet-4-6"
|
|
14601
14966
|
].join(`
|
|
14602
14967
|
`);
|
|
14603
14968
|
}
|
|
@@ -14661,6 +15026,7 @@ async function runCardCommand(context, args) {
|
|
|
14661
15026
|
agentName,
|
|
14662
15027
|
displayName,
|
|
14663
15028
|
harness: parseScoutHarness(options.harness),
|
|
15029
|
+
model: options.model,
|
|
14664
15030
|
currentDirectory: options.currentDirectory,
|
|
14665
15031
|
createdById: resolveScoutAgentName(options.requesterId)
|
|
14666
15032
|
});
|
|
@@ -45391,6 +45757,7 @@ function defaultScoutMcpDependencies(env) {
|
|
|
45391
45757
|
agentName,
|
|
45392
45758
|
displayName,
|
|
45393
45759
|
harness,
|
|
45760
|
+
model,
|
|
45394
45761
|
currentDirectory,
|
|
45395
45762
|
createdById
|
|
45396
45763
|
}) => createScoutAgentCard({
|
|
@@ -45398,12 +45765,21 @@ function defaultScoutMcpDependencies(env) {
|
|
|
45398
45765
|
agentName,
|
|
45399
45766
|
displayName,
|
|
45400
45767
|
harness,
|
|
45768
|
+
model,
|
|
45401
45769
|
currentDirectory,
|
|
45402
45770
|
createdById
|
|
45403
45771
|
}),
|
|
45404
|
-
sendMessage: ({
|
|
45772
|
+
sendMessage: ({
|
|
45405
45773
|
senderId,
|
|
45406
45774
|
body,
|
|
45775
|
+
targetLabel,
|
|
45776
|
+
channel,
|
|
45777
|
+
shouldSpeak,
|
|
45778
|
+
currentDirectory
|
|
45779
|
+
}) => sendScoutMessage({
|
|
45780
|
+
senderId,
|
|
45781
|
+
body,
|
|
45782
|
+
targetLabel,
|
|
45407
45783
|
channel,
|
|
45408
45784
|
shouldSpeak,
|
|
45409
45785
|
currentDirectory
|
|
@@ -45515,7 +45891,8 @@ function createScoutMcpServer(options) {
|
|
|
45515
45891
|
senderId: string2().optional(),
|
|
45516
45892
|
agentName: string2().optional(),
|
|
45517
45893
|
displayName: string2().optional(),
|
|
45518
|
-
harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional()
|
|
45894
|
+
harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional(),
|
|
45895
|
+
model: string2().optional()
|
|
45519
45896
|
}),
|
|
45520
45897
|
outputSchema: cardCreateResultSchema,
|
|
45521
45898
|
annotations: {
|
|
@@ -45530,7 +45907,8 @@ function createScoutMcpServer(options) {
|
|
|
45530
45907
|
senderId,
|
|
45531
45908
|
agentName,
|
|
45532
45909
|
displayName,
|
|
45533
|
-
harness
|
|
45910
|
+
harness,
|
|
45911
|
+
model
|
|
45534
45912
|
}) => {
|
|
45535
45913
|
const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
|
|
45536
45914
|
const resolvedSenderId = await deps.resolveSenderId(senderId, resolvedCurrentDirectory, env);
|
|
@@ -45539,6 +45917,7 @@ function createScoutMcpServer(options) {
|
|
|
45539
45917
|
agentName: agentName?.trim() || undefined,
|
|
45540
45918
|
displayName: displayName?.trim() || undefined,
|
|
45541
45919
|
harness,
|
|
45920
|
+
model: model?.trim() || undefined,
|
|
45542
45921
|
currentDirectory: resolvedCurrentDirectory,
|
|
45543
45922
|
createdById: resolvedSenderId
|
|
45544
45923
|
});
|
|
@@ -45618,11 +45997,12 @@ function createScoutMcpServer(options) {
|
|
|
45618
45997
|
});
|
|
45619
45998
|
server.registerTool("messages_send", {
|
|
45620
45999
|
title: "Send Scout Message",
|
|
45621
|
-
description: "Post a broker-backed Scout tell/update. Use this for heads-up, replies, and status. One explicit target without a channel becomes a DM. Group delivery requires an explicit channel. Use broadcast or channel='shared' for shared updates. For owned work or a reply lifecycle, use invocations_ask instead. Prefer mentionAgentIds for first-class targeting.",
|
|
46000
|
+
description: "Post a broker-backed Scout tell/update. Use this for heads-up, replies, and status. One explicit target without a channel becomes a DM. Group delivery requires an explicit channel. Use broadcast or channel='shared' for shared updates. Pass targetLabel for the single-call broker-resolved path when you know who to contact but do not have an exact id. For owned work or a reply lifecycle, use invocations_ask instead. Prefer mentionAgentIds for first-class targeting when you already have exact ids.",
|
|
45622
46001
|
inputSchema: object2({
|
|
45623
46002
|
body: string2().min(1),
|
|
45624
46003
|
currentDirectory: string2().optional(),
|
|
45625
46004
|
senderId: string2().optional(),
|
|
46005
|
+
targetLabel: string2().optional(),
|
|
45626
46006
|
channel: string2().optional(),
|
|
45627
46007
|
shouldSpeak: boolean2().optional(),
|
|
45628
46008
|
mentionAgentIds: array(string2()).optional()
|
|
@@ -45638,6 +46018,7 @@ function createScoutMcpServer(options) {
|
|
|
45638
46018
|
body,
|
|
45639
46019
|
currentDirectory,
|
|
45640
46020
|
senderId,
|
|
46021
|
+
targetLabel,
|
|
45641
46022
|
channel,
|
|
45642
46023
|
shouldSpeak,
|
|
45643
46024
|
mentionAgentIds
|
|
@@ -45674,6 +46055,32 @@ function createScoutMcpServer(options) {
|
|
|
45674
46055
|
structuredContent: structuredContent2
|
|
45675
46056
|
};
|
|
45676
46057
|
}
|
|
46058
|
+
if (targetLabel?.trim()) {
|
|
46059
|
+
const result2 = await deps.sendMessage({
|
|
46060
|
+
senderId: resolvedSenderId,
|
|
46061
|
+
body,
|
|
46062
|
+
targetLabel: targetLabel.trim(),
|
|
46063
|
+
channel,
|
|
46064
|
+
shouldSpeak,
|
|
46065
|
+
currentDirectory: resolvedCurrentDirectory
|
|
46066
|
+
});
|
|
46067
|
+
const structuredContent2 = {
|
|
46068
|
+
currentDirectory: resolvedCurrentDirectory,
|
|
46069
|
+
senderId: resolvedSenderId,
|
|
46070
|
+
mode: "target_label",
|
|
46071
|
+
usedBroker: result2.usedBroker,
|
|
46072
|
+
conversationId: result2.conversationId ?? null,
|
|
46073
|
+
messageId: result2.messageId ?? null,
|
|
46074
|
+
invokedTargetIds: result2.invokedTargets,
|
|
46075
|
+
unresolvedTargetIds: result2.unresolvedTargets,
|
|
46076
|
+
routeKind: result2.routeKind ?? null,
|
|
46077
|
+
routingError: result2.routingError ?? null
|
|
46078
|
+
};
|
|
46079
|
+
return {
|
|
46080
|
+
content: createTextContent(structuredContent2),
|
|
46081
|
+
structuredContent: structuredContent2
|
|
46082
|
+
};
|
|
46083
|
+
}
|
|
45677
46084
|
const result = await deps.sendMessage({
|
|
45678
46085
|
senderId: resolvedSenderId,
|
|
45679
46086
|
body,
|
|
@@ -46025,7 +46432,7 @@ var init_scout_mcp = __esm(async () => {
|
|
|
46025
46432
|
sendResultSchema = object2({
|
|
46026
46433
|
currentDirectory: string2(),
|
|
46027
46434
|
senderId: string2(),
|
|
46028
|
-
mode: _enum2(["body_mentions", "explicit_targets"]),
|
|
46435
|
+
mode: _enum2(["body_mentions", "explicit_targets", "target_label"]),
|
|
46029
46436
|
usedBroker: boolean2(),
|
|
46030
46437
|
conversationId: string2().nullable(),
|
|
46031
46438
|
messageId: string2().nullable(),
|
|
@@ -49402,9 +49809,74 @@ var init_config3 = __esm(() => {
|
|
|
49402
49809
|
CONFIG_FILE = join22(CONFIG_DIR, "config.json");
|
|
49403
49810
|
});
|
|
49404
49811
|
|
|
49812
|
+
// ../../apps/desktop/src/core/pairing/runtime/bridge/web-handoff.ts
|
|
49813
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
49814
|
+
function pruneExpiredWebHandoffs(now = Date.now()) {
|
|
49815
|
+
for (const [token, record3] of activeWebHandoffs) {
|
|
49816
|
+
if (record3.expiresAt <= now) {
|
|
49817
|
+
activeWebHandoffs.delete(token);
|
|
49818
|
+
}
|
|
49819
|
+
}
|
|
49820
|
+
}
|
|
49821
|
+
function scopesMatch(left, right) {
|
|
49822
|
+
if (left.kind !== right.kind) {
|
|
49823
|
+
return false;
|
|
49824
|
+
}
|
|
49825
|
+
if (left.sessionId !== right.sessionId) {
|
|
49826
|
+
return false;
|
|
49827
|
+
}
|
|
49828
|
+
if (left.kind === "session") {
|
|
49829
|
+
return true;
|
|
49830
|
+
}
|
|
49831
|
+
return left.turnId === right.turnId && left.blockId === right.blockId;
|
|
49832
|
+
}
|
|
49833
|
+
function pathForWebHandoffScope(scope) {
|
|
49834
|
+
switch (scope.kind) {
|
|
49835
|
+
case "session":
|
|
49836
|
+
return `/handoff/session/${encodeURIComponent(scope.sessionId)}`;
|
|
49837
|
+
case "file_change":
|
|
49838
|
+
return `/handoff/file-change/${encodeURIComponent(scope.sessionId)}/${encodeURIComponent(scope.turnId)}/${encodeURIComponent(scope.blockId)}`;
|
|
49839
|
+
}
|
|
49840
|
+
}
|
|
49841
|
+
function issueWebHandoff(scope, deviceId) {
|
|
49842
|
+
pruneExpiredWebHandoffs();
|
|
49843
|
+
const token = randomBytes2(24).toString("base64url");
|
|
49844
|
+
const expiresAt = Date.now() + WEB_HANDOFF_TTL_MS;
|
|
49845
|
+
activeWebHandoffs.set(token, {
|
|
49846
|
+
token,
|
|
49847
|
+
deviceId: deviceId?.trim() || null,
|
|
49848
|
+
expiresAt,
|
|
49849
|
+
scope
|
|
49850
|
+
});
|
|
49851
|
+
return { token, expiresAt, scope };
|
|
49852
|
+
}
|
|
49853
|
+
function readAuthorizedWebHandoff(token, scope) {
|
|
49854
|
+
pruneExpiredWebHandoffs();
|
|
49855
|
+
if (!token) {
|
|
49856
|
+
return null;
|
|
49857
|
+
}
|
|
49858
|
+
const record3 = activeWebHandoffs.get(token);
|
|
49859
|
+
if (!record3) {
|
|
49860
|
+
return null;
|
|
49861
|
+
}
|
|
49862
|
+
if (!scopesMatch(record3.scope, scope)) {
|
|
49863
|
+
return null;
|
|
49864
|
+
}
|
|
49865
|
+
return record3;
|
|
49866
|
+
}
|
|
49867
|
+
var SCOUT_WEB_HANDOFF_COOKIE = "scout_handoff", WEB_HANDOFF_TTL_MS, activeWebHandoffs;
|
|
49868
|
+
var init_web_handoff = __esm(() => {
|
|
49869
|
+
WEB_HANDOFF_TTL_MS = 5 * 60 * 1000;
|
|
49870
|
+
activeWebHandoffs = new Map;
|
|
49871
|
+
});
|
|
49872
|
+
|
|
49405
49873
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
|
|
49406
49874
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
49407
49875
|
import { homedir as homedir16 } from "os";
|
|
49876
|
+
function isTrustedLoopbackHostname(hostname5) {
|
|
49877
|
+
const normalized = hostname5.trim().toLowerCase();
|
|
49878
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "::1" || LOOPBACK_IPV4_HOST_PATTERN.test(normalized);
|
|
49879
|
+
}
|
|
49408
49880
|
function isAllowedPath(filePath) {
|
|
49409
49881
|
if (!isAbsolute2(filePath))
|
|
49410
49882
|
return false;
|
|
@@ -49415,7 +49887,7 @@ function isAllowedPath(filePath) {
|
|
|
49415
49887
|
return ALLOWED_ROOTS.some((root) => filePath.startsWith(root));
|
|
49416
49888
|
}
|
|
49417
49889
|
function startFileServer(options) {
|
|
49418
|
-
const { port } = options;
|
|
49890
|
+
const { port, bridge } = options;
|
|
49419
49891
|
let server = null;
|
|
49420
49892
|
function start() {
|
|
49421
49893
|
try {
|
|
@@ -49423,7 +49895,7 @@ function startFileServer(options) {
|
|
|
49423
49895
|
port,
|
|
49424
49896
|
fetch(req) {
|
|
49425
49897
|
try {
|
|
49426
|
-
return route(new URL(req.url));
|
|
49898
|
+
return route(req, new URL(req.url), bridge);
|
|
49427
49899
|
} catch (err) {
|
|
49428
49900
|
console.error(`[fileserver] ${req.url} \u2192`, err.message);
|
|
49429
49901
|
return new Response("Internal error", { status: 500 });
|
|
@@ -49446,12 +49918,19 @@ function startFileServer(options) {
|
|
|
49446
49918
|
start();
|
|
49447
49919
|
return { port, stop, restart };
|
|
49448
49920
|
}
|
|
49449
|
-
function route(url2) {
|
|
49921
|
+
function route(req, url2, bridge) {
|
|
49450
49922
|
const path2 = url2.pathname;
|
|
49451
|
-
if (path2 === "/file")
|
|
49923
|
+
if (path2 === "/file") {
|
|
49924
|
+
if (!isTrustedLoopbackHostname(url2.hostname)) {
|
|
49925
|
+
return new Response("Forbidden", { status: 403 });
|
|
49926
|
+
}
|
|
49452
49927
|
return serveFile(url2);
|
|
49928
|
+
}
|
|
49453
49929
|
if (path2 === "/health")
|
|
49454
49930
|
return Response.json({ ok: true });
|
|
49931
|
+
if (path2.startsWith("/handoff/")) {
|
|
49932
|
+
return serveHandoffPage(req, url2, bridge);
|
|
49933
|
+
}
|
|
49455
49934
|
return new Response("Pairing file server", { status: 200 });
|
|
49456
49935
|
}
|
|
49457
49936
|
function serveFile(url2) {
|
|
@@ -49465,9 +49944,525 @@ function serveFile(url2) {
|
|
|
49465
49944
|
return new Response("Not found", { status: 404 });
|
|
49466
49945
|
return new Response(file2);
|
|
49467
49946
|
}
|
|
49468
|
-
|
|
49947
|
+
function serveHandoffPage(req, url2, bridge) {
|
|
49948
|
+
if (!bridge) {
|
|
49949
|
+
return new Response("Secure handoff unavailable", { status: 503 });
|
|
49950
|
+
}
|
|
49951
|
+
const scope = parseHandoffScope(url2);
|
|
49952
|
+
if (!scope) {
|
|
49953
|
+
return new Response("Not found", { status: 404 });
|
|
49954
|
+
}
|
|
49955
|
+
const token = readHandoffToken(req);
|
|
49956
|
+
const authorized = readAuthorizedWebHandoff(token, scope);
|
|
49957
|
+
if (!authorized) {
|
|
49958
|
+
return unauthorizedHandoffResponse();
|
|
49959
|
+
}
|
|
49960
|
+
let html;
|
|
49961
|
+
switch (scope.kind) {
|
|
49962
|
+
case "session": {
|
|
49963
|
+
const snapshot = bridge.getSessionSnapshot(scope.sessionId);
|
|
49964
|
+
if (!snapshot) {
|
|
49965
|
+
return new Response("Session handoff expired", { status: 404 });
|
|
49966
|
+
}
|
|
49967
|
+
html = renderSessionHandoffPage(snapshot);
|
|
49968
|
+
break;
|
|
49969
|
+
}
|
|
49970
|
+
case "file_change": {
|
|
49971
|
+
const snapshot = bridge.getSessionSnapshot(scope.sessionId);
|
|
49972
|
+
if (!snapshot) {
|
|
49973
|
+
return new Response("Session handoff expired", { status: 404 });
|
|
49974
|
+
}
|
|
49975
|
+
const target = findFileChangeBlock(snapshot, scope.turnId, scope.blockId);
|
|
49976
|
+
if (!target) {
|
|
49977
|
+
return new Response("File change handoff expired", { status: 404 });
|
|
49978
|
+
}
|
|
49979
|
+
html = renderFileChangeHandoffPage(snapshot, target.turn, target.block);
|
|
49980
|
+
break;
|
|
49981
|
+
}
|
|
49982
|
+
}
|
|
49983
|
+
const headers = new Headers({
|
|
49984
|
+
"content-type": "text/html; charset=utf-8",
|
|
49985
|
+
"cache-control": "no-store",
|
|
49986
|
+
"x-content-type-options": "nosniff"
|
|
49987
|
+
});
|
|
49988
|
+
headers.set("set-cookie", `${SCOUT_WEB_HANDOFF_COOKIE}=${authorized.token}; Max-Age=300; Path=/handoff; HttpOnly; SameSite=Strict`);
|
|
49989
|
+
return new Response(html, { status: 200, headers });
|
|
49990
|
+
}
|
|
49991
|
+
function unauthorizedHandoffResponse() {
|
|
49992
|
+
return new Response("Secure handoff required", {
|
|
49993
|
+
status: 401,
|
|
49994
|
+
headers: {
|
|
49995
|
+
"cache-control": "no-store",
|
|
49996
|
+
"set-cookie": `${SCOUT_WEB_HANDOFF_COOKIE}=; Max-Age=0; Path=/handoff; HttpOnly; SameSite=Strict`
|
|
49997
|
+
}
|
|
49998
|
+
});
|
|
49999
|
+
}
|
|
50000
|
+
function parseHandoffScope(url2) {
|
|
50001
|
+
const parts = url2.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part));
|
|
50002
|
+
if (parts[0] !== "handoff") {
|
|
50003
|
+
return null;
|
|
50004
|
+
}
|
|
50005
|
+
if (parts[1] === "session" && parts[2]) {
|
|
50006
|
+
return { kind: "session", sessionId: parts[2] };
|
|
50007
|
+
}
|
|
50008
|
+
if (parts[1] === "file-change" && parts[2] && parts[3] && parts[4]) {
|
|
50009
|
+
return {
|
|
50010
|
+
kind: "file_change",
|
|
50011
|
+
sessionId: parts[2],
|
|
50012
|
+
turnId: parts[3],
|
|
50013
|
+
blockId: parts[4]
|
|
50014
|
+
};
|
|
50015
|
+
}
|
|
50016
|
+
return null;
|
|
50017
|
+
}
|
|
50018
|
+
function readHandoffToken(req) {
|
|
50019
|
+
const headerToken = req.headers.get("x-scout-handoff-token")?.trim();
|
|
50020
|
+
if (headerToken) {
|
|
50021
|
+
return headerToken;
|
|
50022
|
+
}
|
|
50023
|
+
const cookieHeader = req.headers.get("cookie");
|
|
50024
|
+
if (!cookieHeader) {
|
|
50025
|
+
return null;
|
|
50026
|
+
}
|
|
50027
|
+
for (const part of cookieHeader.split(";")) {
|
|
50028
|
+
const [name, ...rest] = part.trim().split("=");
|
|
50029
|
+
if (name === SCOUT_WEB_HANDOFF_COOKIE) {
|
|
50030
|
+
const value = rest.join("=").trim();
|
|
50031
|
+
return value || null;
|
|
50032
|
+
}
|
|
50033
|
+
}
|
|
50034
|
+
return null;
|
|
50035
|
+
}
|
|
50036
|
+
function findFileChangeBlock(snapshot, turnId, blockId) {
|
|
50037
|
+
const turn = snapshot.turns.find((candidate) => candidate.id === turnId);
|
|
50038
|
+
if (!turn) {
|
|
50039
|
+
return null;
|
|
50040
|
+
}
|
|
50041
|
+
const blockState = turn.blocks.find((candidate) => candidate.block.id === blockId);
|
|
50042
|
+
const block = blockState?.block;
|
|
50043
|
+
if (!block || block.type !== "action" || block.action.kind !== "file_change") {
|
|
50044
|
+
return null;
|
|
50045
|
+
}
|
|
50046
|
+
return { turn, block };
|
|
50047
|
+
}
|
|
50048
|
+
function renderPage(title, body) {
|
|
50049
|
+
return `<!doctype html>
|
|
50050
|
+
<html lang="en">
|
|
50051
|
+
<head>
|
|
50052
|
+
<meta charset="utf-8" />
|
|
50053
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
50054
|
+
<title>${escapeHtml(title)}</title>
|
|
50055
|
+
<style>
|
|
50056
|
+
:root {
|
|
50057
|
+
color-scheme: dark;
|
|
50058
|
+
--bg: #0b0f14;
|
|
50059
|
+
--panel: rgba(19, 27, 39, 0.92);
|
|
50060
|
+
--panel-strong: rgba(12, 18, 28, 0.96);
|
|
50061
|
+
--line: rgba(148, 163, 184, 0.16);
|
|
50062
|
+
--line-strong: rgba(148, 163, 184, 0.24);
|
|
50063
|
+
--text: #e5ecf5;
|
|
50064
|
+
--muted: #90a0b4;
|
|
50065
|
+
--accent: #63d0ff;
|
|
50066
|
+
--green: #54d38a;
|
|
50067
|
+
--amber: #f8c36a;
|
|
50068
|
+
--red: #ff7b72;
|
|
50069
|
+
--surface-add: rgba(84, 211, 138, 0.08);
|
|
50070
|
+
--surface-del: rgba(255, 123, 114, 0.08);
|
|
50071
|
+
}
|
|
50072
|
+
* { box-sizing: border-box; }
|
|
50073
|
+
body {
|
|
50074
|
+
margin: 0;
|
|
50075
|
+
background:
|
|
50076
|
+
radial-gradient(circle at top left, rgba(99, 208, 255, 0.12), transparent 32%),
|
|
50077
|
+
linear-gradient(180deg, #090d12 0%, #0b0f14 55%, #111723 100%);
|
|
50078
|
+
color: var(--text);
|
|
50079
|
+
font: 14px/1.5 ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
|
|
50080
|
+
}
|
|
50081
|
+
.wrap {
|
|
50082
|
+
width: min(980px, calc(100vw - 24px));
|
|
50083
|
+
margin: 0 auto;
|
|
50084
|
+
padding: 20px 0 40px;
|
|
50085
|
+
}
|
|
50086
|
+
.hero, .panel {
|
|
50087
|
+
background: var(--panel);
|
|
50088
|
+
border: 1px solid var(--line);
|
|
50089
|
+
border-radius: 18px;
|
|
50090
|
+
backdrop-filter: blur(18px);
|
|
50091
|
+
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28);
|
|
50092
|
+
}
|
|
50093
|
+
.hero {
|
|
50094
|
+
padding: 18px 18px 16px;
|
|
50095
|
+
margin-bottom: 14px;
|
|
50096
|
+
}
|
|
50097
|
+
.eyebrow {
|
|
50098
|
+
display: inline-flex;
|
|
50099
|
+
align-items: center;
|
|
50100
|
+
gap: 8px;
|
|
50101
|
+
color: var(--muted);
|
|
50102
|
+
font-size: 11px;
|
|
50103
|
+
letter-spacing: 0.14em;
|
|
50104
|
+
text-transform: uppercase;
|
|
50105
|
+
}
|
|
50106
|
+
.dot {
|
|
50107
|
+
width: 8px;
|
|
50108
|
+
height: 8px;
|
|
50109
|
+
border-radius: 999px;
|
|
50110
|
+
background: var(--accent);
|
|
50111
|
+
box-shadow: 0 0 14px rgba(99, 208, 255, 0.65);
|
|
50112
|
+
}
|
|
50113
|
+
h1 {
|
|
50114
|
+
margin: 12px 0 10px;
|
|
50115
|
+
font-size: 24px;
|
|
50116
|
+
line-height: 1.15;
|
|
50117
|
+
}
|
|
50118
|
+
.meta, .stack {
|
|
50119
|
+
display: flex;
|
|
50120
|
+
flex-wrap: wrap;
|
|
50121
|
+
gap: 8px;
|
|
50122
|
+
}
|
|
50123
|
+
.pill {
|
|
50124
|
+
display: inline-flex;
|
|
50125
|
+
align-items: center;
|
|
50126
|
+
gap: 6px;
|
|
50127
|
+
padding: 5px 10px;
|
|
50128
|
+
border-radius: 999px;
|
|
50129
|
+
border: 1px solid var(--line-strong);
|
|
50130
|
+
background: rgba(255, 255, 255, 0.03);
|
|
50131
|
+
color: var(--muted);
|
|
50132
|
+
font-size: 12px;
|
|
50133
|
+
}
|
|
50134
|
+
.pill strong {
|
|
50135
|
+
color: var(--text);
|
|
50136
|
+
font-weight: 600;
|
|
50137
|
+
}
|
|
50138
|
+
.stack { flex-direction: column; }
|
|
50139
|
+
.panel {
|
|
50140
|
+
margin-top: 12px;
|
|
50141
|
+
overflow: hidden;
|
|
50142
|
+
}
|
|
50143
|
+
.panel-inner {
|
|
50144
|
+
padding: 16px;
|
|
50145
|
+
}
|
|
50146
|
+
.turn {
|
|
50147
|
+
border-top: 1px solid var(--line);
|
|
50148
|
+
}
|
|
50149
|
+
.turn:first-child {
|
|
50150
|
+
border-top: none;
|
|
50151
|
+
}
|
|
50152
|
+
.turn-head {
|
|
50153
|
+
padding: 14px 16px;
|
|
50154
|
+
display: flex;
|
|
50155
|
+
justify-content: space-between;
|
|
50156
|
+
gap: 12px;
|
|
50157
|
+
align-items: center;
|
|
50158
|
+
background: rgba(255, 255, 255, 0.02);
|
|
50159
|
+
}
|
|
50160
|
+
.turn-title {
|
|
50161
|
+
font-size: 13px;
|
|
50162
|
+
font-weight: 600;
|
|
50163
|
+
letter-spacing: 0.02em;
|
|
50164
|
+
}
|
|
50165
|
+
.turn-subtitle {
|
|
50166
|
+
color: var(--muted);
|
|
50167
|
+
font-size: 12px;
|
|
50168
|
+
margin-top: 2px;
|
|
50169
|
+
}
|
|
50170
|
+
.blocks {
|
|
50171
|
+
padding: 8px;
|
|
50172
|
+
}
|
|
50173
|
+
.block {
|
|
50174
|
+
padding: 12px;
|
|
50175
|
+
border-radius: 14px;
|
|
50176
|
+
border: 1px solid var(--line);
|
|
50177
|
+
background: var(--panel-strong);
|
|
50178
|
+
margin: 8px;
|
|
50179
|
+
}
|
|
50180
|
+
.block-head {
|
|
50181
|
+
display: flex;
|
|
50182
|
+
justify-content: space-between;
|
|
50183
|
+
align-items: center;
|
|
50184
|
+
gap: 12px;
|
|
50185
|
+
margin-bottom: 10px;
|
|
50186
|
+
}
|
|
50187
|
+
.block-type {
|
|
50188
|
+
color: var(--muted);
|
|
50189
|
+
text-transform: uppercase;
|
|
50190
|
+
letter-spacing: 0.12em;
|
|
50191
|
+
font-size: 11px;
|
|
50192
|
+
}
|
|
50193
|
+
.label {
|
|
50194
|
+
color: var(--muted);
|
|
50195
|
+
font-size: 12px;
|
|
50196
|
+
}
|
|
50197
|
+
.label strong {
|
|
50198
|
+
color: var(--text);
|
|
50199
|
+
}
|
|
50200
|
+
pre {
|
|
50201
|
+
margin: 0;
|
|
50202
|
+
white-space: pre-wrap;
|
|
50203
|
+
word-break: break-word;
|
|
50204
|
+
overflow-wrap: anywhere;
|
|
50205
|
+
font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
50206
|
+
}
|
|
50207
|
+
.code {
|
|
50208
|
+
padding: 12px;
|
|
50209
|
+
border-radius: 12px;
|
|
50210
|
+
border: 1px solid var(--line);
|
|
50211
|
+
background: rgba(255, 255, 255, 0.02);
|
|
50212
|
+
}
|
|
50213
|
+
.diff-line-add { background: var(--surface-add); color: #8ef3b7; }
|
|
50214
|
+
.diff-line-del { background: var(--surface-del); color: #ff9d96; }
|
|
50215
|
+
.details {
|
|
50216
|
+
margin-top: 10px;
|
|
50217
|
+
border: 1px solid var(--line);
|
|
50218
|
+
border-radius: 12px;
|
|
50219
|
+
overflow: hidden;
|
|
50220
|
+
}
|
|
50221
|
+
details > summary {
|
|
50222
|
+
list-style: none;
|
|
50223
|
+
cursor: pointer;
|
|
50224
|
+
padding: 10px 12px;
|
|
50225
|
+
background: rgba(255, 255, 255, 0.03);
|
|
50226
|
+
color: var(--muted);
|
|
50227
|
+
font-size: 12px;
|
|
50228
|
+
text-transform: uppercase;
|
|
50229
|
+
letter-spacing: 0.1em;
|
|
50230
|
+
}
|
|
50231
|
+
details[open] > summary {
|
|
50232
|
+
border-bottom: 1px solid var(--line);
|
|
50233
|
+
}
|
|
50234
|
+
.empty {
|
|
50235
|
+
padding: 28px 18px;
|
|
50236
|
+
color: var(--muted);
|
|
50237
|
+
}
|
|
50238
|
+
.grid {
|
|
50239
|
+
display: grid;
|
|
50240
|
+
gap: 10px;
|
|
50241
|
+
}
|
|
50242
|
+
.grid.two {
|
|
50243
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
50244
|
+
}
|
|
50245
|
+
a.inline {
|
|
50246
|
+
color: var(--accent);
|
|
50247
|
+
text-decoration: none;
|
|
50248
|
+
}
|
|
50249
|
+
.status-streaming { color: var(--amber); }
|
|
50250
|
+
.status-completed { color: var(--green); }
|
|
50251
|
+
.status-failed, .status-error { color: var(--red); }
|
|
50252
|
+
</style>
|
|
50253
|
+
</head>
|
|
50254
|
+
<body>
|
|
50255
|
+
<main class="wrap">
|
|
50256
|
+
${body}
|
|
50257
|
+
</main>
|
|
50258
|
+
</body>
|
|
50259
|
+
</html>`;
|
|
50260
|
+
}
|
|
50261
|
+
function renderSessionHandoffPage(snapshot) {
|
|
50262
|
+
const title = snapshot.session.name || snapshot.session.id;
|
|
50263
|
+
const hero = `
|
|
50264
|
+
<section class="hero">
|
|
50265
|
+
<div class="eyebrow"><span class="dot"></span> Secure Proxy Session Handoff</div>
|
|
50266
|
+
<h1>${escapeHtml(title)}</h1>
|
|
50267
|
+
<div class="meta">
|
|
50268
|
+
<span class="pill"><strong>ID</strong> ${escapeHtml(snapshot.session.id)}</span>
|
|
50269
|
+
<span class="pill"><strong>Adapter</strong> ${escapeHtml(snapshot.session.adapterType)}</span>
|
|
50270
|
+
<span class="pill"><strong>Status</strong> ${escapeHtml(snapshot.session.status)}</span>
|
|
50271
|
+
${snapshot.session.model ? `<span class="pill"><strong>Model</strong> ${escapeHtml(snapshot.session.model)}</span>` : ""}
|
|
50272
|
+
${snapshot.session.cwd ? `<span class="pill"><strong>Workspace</strong> ${escapeHtml(snapshot.session.cwd)}</span>` : ""}
|
|
50273
|
+
</div>
|
|
50274
|
+
</section>
|
|
50275
|
+
`;
|
|
50276
|
+
const turns = snapshot.turns.length > 0 ? snapshot.turns.map((turn) => {
|
|
50277
|
+
const turnBody = turn.blocks.length > 0 ? turn.blocks.map(({ block }) => renderBlock(block)).join("") : `<div class="empty">No blocks were captured for this turn.</div>`;
|
|
50278
|
+
return `
|
|
50279
|
+
<section class="turn">
|
|
50280
|
+
<div class="turn-head">
|
|
50281
|
+
<div>
|
|
50282
|
+
<div class="turn-title">${escapeHtml(turn.id)}</div>
|
|
50283
|
+
<div class="turn-subtitle">${formatTimestamp(turn.startedAt)}${turn.endedAt ? ` to ${formatTimestamp(turn.endedAt)}` : ""}</div>
|
|
50284
|
+
</div>
|
|
50285
|
+
<span class="pill"><strong>Turn</strong> <span class="status-${escapeHtml(turn.status)}">${escapeHtml(turn.status)}</span></span>
|
|
50286
|
+
</div>
|
|
50287
|
+
<div class="blocks">${turnBody}</div>
|
|
50288
|
+
</section>
|
|
50289
|
+
`;
|
|
50290
|
+
}).join("") : `<div class="empty">This session has not produced any turns yet.</div>`;
|
|
50291
|
+
return renderPage(title, `${hero}<section class="panel">${turns}</section>`);
|
|
50292
|
+
}
|
|
50293
|
+
function renderFileChangeHandoffPage(snapshot, turn, block) {
|
|
50294
|
+
const action = block.action;
|
|
50295
|
+
const title = action.path || snapshot.session.name || snapshot.session.id;
|
|
50296
|
+
const hero = `
|
|
50297
|
+
<section class="hero">
|
|
50298
|
+
<div class="eyebrow"><span class="dot"></span> Secure Proxy File Change</div>
|
|
50299
|
+
<h1>${escapeHtml(title)}</h1>
|
|
50300
|
+
<div class="meta">
|
|
50301
|
+
<span class="pill"><strong>Session</strong> ${escapeHtml(snapshot.session.name || snapshot.session.id)}</span>
|
|
50302
|
+
<span class="pill"><strong>Turn</strong> ${escapeHtml(turn.id)}</span>
|
|
50303
|
+
<span class="pill"><strong>Status</strong> ${escapeHtml(action.status)}</span>
|
|
50304
|
+
</div>
|
|
50305
|
+
</section>
|
|
50306
|
+
`;
|
|
50307
|
+
const body = `
|
|
50308
|
+
<section class="panel">
|
|
50309
|
+
<div class="panel-inner stack">
|
|
50310
|
+
<div class="grid two">
|
|
50311
|
+
<div class="pill"><strong>Path</strong> ${escapeHtml(action.path)}</div>
|
|
50312
|
+
<div class="pill"><strong>Block</strong> ${escapeHtml(block.id)}</div>
|
|
50313
|
+
</div>
|
|
50314
|
+
${action.diff ? renderDetailsSection("Diff", renderDiff(action.diff), true) : `<div class="empty">No diff was recorded for this action.</div>`}
|
|
50315
|
+
${action.output ? renderDetailsSection("Output", `<div class="code"><pre>${escapeHtml(action.output)}</pre></div>`) : ""}
|
|
50316
|
+
</div>
|
|
50317
|
+
</section>
|
|
50318
|
+
`;
|
|
50319
|
+
return renderPage(title, `${hero}${body}`);
|
|
50320
|
+
}
|
|
50321
|
+
function renderBlock(block) {
|
|
50322
|
+
switch (block.type) {
|
|
50323
|
+
case "text":
|
|
50324
|
+
return renderTextLikeBlock("Text", block.text);
|
|
50325
|
+
case "reasoning":
|
|
50326
|
+
return renderTextLikeBlock("Reasoning", block.text);
|
|
50327
|
+
case "error":
|
|
50328
|
+
return `
|
|
50329
|
+
<article class="block">
|
|
50330
|
+
<div class="block-head">
|
|
50331
|
+
<div class="block-type">Error</div>
|
|
50332
|
+
<span class="pill status-error">${escapeHtml(block.status)}</span>
|
|
50333
|
+
</div>
|
|
50334
|
+
<div class="code"><pre>${escapeHtml(block.message)}</pre></div>
|
|
50335
|
+
</article>
|
|
50336
|
+
`;
|
|
50337
|
+
case "file":
|
|
50338
|
+
return `
|
|
50339
|
+
<article class="block">
|
|
50340
|
+
<div class="block-head">
|
|
50341
|
+
<div class="block-type">File</div>
|
|
50342
|
+
<span class="pill">${escapeHtml(block.status)}</span>
|
|
50343
|
+
</div>
|
|
50344
|
+
<div class="grid">
|
|
50345
|
+
${block.name ? `<div class="label"><strong>Name</strong> ${escapeHtml(block.name)}</div>` : ""}
|
|
50346
|
+
<div class="label"><strong>MIME</strong> ${escapeHtml(block.mimeType)}</div>
|
|
50347
|
+
</div>
|
|
50348
|
+
</article>
|
|
50349
|
+
`;
|
|
50350
|
+
case "question":
|
|
50351
|
+
return renderQuestionBlock(block);
|
|
50352
|
+
case "action":
|
|
50353
|
+
return renderActionBlock(block);
|
|
50354
|
+
}
|
|
50355
|
+
}
|
|
50356
|
+
function renderTextLikeBlock(label, text) {
|
|
50357
|
+
return `
|
|
50358
|
+
<article class="block">
|
|
50359
|
+
<div class="block-head">
|
|
50360
|
+
<div class="block-type">${escapeHtml(label)}</div>
|
|
50361
|
+
</div>
|
|
50362
|
+
<div class="code"><pre>${escapeHtml(text)}</pre></div>
|
|
50363
|
+
</article>
|
|
50364
|
+
`;
|
|
50365
|
+
}
|
|
50366
|
+
function renderQuestionBlock(block) {
|
|
50367
|
+
const answer = block.answer?.length ? block.answer.join(", ") : "Awaiting answer";
|
|
50368
|
+
const options = block.options?.length ? `<div class="label"><strong>Options</strong> ${escapeHtml(block.options.map((option) => option.label).join(", "))}</div>` : "";
|
|
50369
|
+
return `
|
|
50370
|
+
<article class="block">
|
|
50371
|
+
<div class="block-head">
|
|
50372
|
+
<div class="block-type">Question</div>
|
|
50373
|
+
<span class="pill">${escapeHtml(block.questionStatus)}</span>
|
|
50374
|
+
</div>
|
|
50375
|
+
${block.header ? `<div class="label"><strong>${escapeHtml(block.header)}</strong></div>` : ""}
|
|
50376
|
+
<div class="code"><pre>${escapeHtml(block.question)}</pre></div>
|
|
50377
|
+
<div class="stack" style="margin-top: 10px;">
|
|
50378
|
+
${options}
|
|
50379
|
+
<div class="label"><strong>Answer</strong> ${escapeHtml(answer)}</div>
|
|
50380
|
+
</div>
|
|
50381
|
+
</article>
|
|
50382
|
+
`;
|
|
50383
|
+
}
|
|
50384
|
+
function renderActionBlock(block) {
|
|
50385
|
+
const action = block.action;
|
|
50386
|
+
const parts = [];
|
|
50387
|
+
switch (action.kind) {
|
|
50388
|
+
case "file_change":
|
|
50389
|
+
parts.push(`<div class="label"><strong>Path</strong> ${escapeHtml(action.path)}</div>`);
|
|
50390
|
+
if (action.diff) {
|
|
50391
|
+
parts.push(renderDetailsSection("Diff", renderDiff(action.diff)));
|
|
50392
|
+
}
|
|
50393
|
+
break;
|
|
50394
|
+
case "command":
|
|
50395
|
+
parts.push(`<div class="label"><strong>Command</strong></div><div class="code"><pre>${escapeHtml(action.command)}</pre></div>`);
|
|
50396
|
+
if (typeof action.exitCode === "number") {
|
|
50397
|
+
parts.push(`<div class="label"><strong>Exit Code</strong> ${escapeHtml(String(action.exitCode))}</div>`);
|
|
50398
|
+
}
|
|
50399
|
+
break;
|
|
50400
|
+
case "tool_call":
|
|
50401
|
+
parts.push(`<div class="label"><strong>Tool</strong> ${escapeHtml(action.toolName)}</div>`);
|
|
50402
|
+
if (action.toolCallId) {
|
|
50403
|
+
parts.push(`<div class="label"><strong>Call ID</strong> ${escapeHtml(action.toolCallId)}</div>`);
|
|
50404
|
+
}
|
|
50405
|
+
break;
|
|
50406
|
+
case "subagent":
|
|
50407
|
+
parts.push(`<div class="label"><strong>Agent</strong> ${escapeHtml(action.agentName || action.agentId)}</div>`);
|
|
50408
|
+
if (action.prompt) {
|
|
50409
|
+
parts.push(`<div class="code"><pre>${escapeHtml(action.prompt)}</pre></div>`);
|
|
50410
|
+
}
|
|
50411
|
+
break;
|
|
50412
|
+
}
|
|
50413
|
+
if (action.approval) {
|
|
50414
|
+
parts.push(`<div class="label"><strong>Approval</strong> ${escapeHtml(action.approval.risk || "medium")} risk${action.approval.description ? ` - ${escapeHtml(action.approval.description)}` : ""}</div>`);
|
|
50415
|
+
}
|
|
50416
|
+
if (action.output) {
|
|
50417
|
+
parts.push(renderDetailsSection("Output", `<div class="code"><pre>${escapeHtml(action.output)}</pre></div>`));
|
|
50418
|
+
}
|
|
50419
|
+
return `
|
|
50420
|
+
<article class="block">
|
|
50421
|
+
<div class="block-head">
|
|
50422
|
+
<div class="block-type">${escapeHtml(action.kind.replace("_", " "))}</div>
|
|
50423
|
+
<span class="pill">${escapeHtml(action.status)}</span>
|
|
50424
|
+
</div>
|
|
50425
|
+
<div class="stack">${parts.join("")}</div>
|
|
50426
|
+
</article>
|
|
50427
|
+
`;
|
|
50428
|
+
}
|
|
50429
|
+
function renderDetailsSection(title, innerHtml, open2 = false) {
|
|
50430
|
+
return `
|
|
50431
|
+
<details class="details"${open2 ? " open" : ""}>
|
|
50432
|
+
<summary>${escapeHtml(title)}</summary>
|
|
50433
|
+
<div class="panel-inner">${innerHtml}</div>
|
|
50434
|
+
</details>
|
|
50435
|
+
`;
|
|
50436
|
+
}
|
|
50437
|
+
function renderDiff(diff) {
|
|
50438
|
+
const lines = diff.split(`
|
|
50439
|
+
`).map((line) => {
|
|
50440
|
+
const className = line.startsWith("+") ? "diff-line-add" : line.startsWith("-") ? "diff-line-del" : "";
|
|
50441
|
+
return `<div class="${className}"><pre>${escapeHtml(line || " ")}</pre></div>`;
|
|
50442
|
+
}).join("");
|
|
50443
|
+
return `<div class="code">${lines}</div>`;
|
|
50444
|
+
}
|
|
50445
|
+
function escapeHtml(value) {
|
|
50446
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
50447
|
+
}
|
|
50448
|
+
function formatTimestamp(value) {
|
|
50449
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
50450
|
+
return "Unknown time";
|
|
50451
|
+
}
|
|
50452
|
+
try {
|
|
50453
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
50454
|
+
dateStyle: "medium",
|
|
50455
|
+
timeStyle: "short"
|
|
50456
|
+
}).format(value);
|
|
50457
|
+
} catch {
|
|
50458
|
+
return new Date(value).toISOString();
|
|
50459
|
+
}
|
|
50460
|
+
}
|
|
50461
|
+
var ALLOWED_ROOTS, LOOPBACK_IPV4_HOST_PATTERN;
|
|
49469
50462
|
var init_fileserver = __esm(() => {
|
|
50463
|
+
init_web_handoff();
|
|
49470
50464
|
ALLOWED_ROOTS = [homedir16(), "/tmp"];
|
|
50465
|
+
LOOPBACK_IPV4_HOST_PATTERN = /^127(?:\.\d{1,3}){3}$/;
|
|
49471
50466
|
});
|
|
49472
50467
|
|
|
49473
50468
|
// ../../apps/desktop/src/server/db-queries.ts
|
|
@@ -50319,6 +51314,20 @@ function readSyncStatus(bridge, sessionId) {
|
|
|
50319
51314
|
oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
|
|
50320
51315
|
};
|
|
50321
51316
|
}
|
|
51317
|
+
function resolveSyncSessionId(bridge, preferredSessionId) {
|
|
51318
|
+
if (preferredSessionId) {
|
|
51319
|
+
return preferredSessionId;
|
|
51320
|
+
}
|
|
51321
|
+
let latestSessionId = null;
|
|
51322
|
+
let latestActivityAt = Number.NEGATIVE_INFINITY;
|
|
51323
|
+
for (const session of bridge.getSessionSummaries()) {
|
|
51324
|
+
if (session.lastActivityAt > latestActivityAt) {
|
|
51325
|
+
latestActivityAt = session.lastActivityAt;
|
|
51326
|
+
latestSessionId = session.sessionId;
|
|
51327
|
+
}
|
|
51328
|
+
}
|
|
51329
|
+
return latestSessionId;
|
|
51330
|
+
}
|
|
50322
51331
|
function sessionRegistryErrorResponse(reqId, error48) {
|
|
50323
51332
|
if (!isSessionRegistryError(error48)) {
|
|
50324
51333
|
return null;
|
|
@@ -50388,23 +51397,36 @@ async function handleRPCInner(bridge, req, deviceId) {
|
|
|
50388
51397
|
return { id: req.id, result: { ok: true } };
|
|
50389
51398
|
}
|
|
50390
51399
|
case "sync/replay": {
|
|
50391
|
-
const p = req.params;
|
|
50392
|
-
if (
|
|
50393
|
-
return { id: req.id, error: { code: -32602, message: "
|
|
51400
|
+
const p = req.params ?? {};
|
|
51401
|
+
if (typeof p.lastSeq !== "number") {
|
|
51402
|
+
return { id: req.id, error: { code: -32602, message: "lastSeq is required" } };
|
|
51403
|
+
}
|
|
51404
|
+
const sessionId = resolveSyncSessionId(bridge, p.sessionId);
|
|
51405
|
+
if (!sessionId) {
|
|
51406
|
+
return { id: req.id, result: { events: [] } };
|
|
50394
51407
|
}
|
|
50395
|
-
const events2 = replaySyncEvents(bridge,
|
|
51408
|
+
const events2 = replaySyncEvents(bridge, sessionId, p.lastSeq);
|
|
50396
51409
|
return { id: req.id, result: { events: events2 } };
|
|
50397
51410
|
}
|
|
50398
51411
|
case "sync/status": {
|
|
50399
51412
|
const p = req.params ?? {};
|
|
50400
|
-
|
|
50401
|
-
|
|
51413
|
+
const sessionCount = bridge.listSessions().length;
|
|
51414
|
+
const sessionId = resolveSyncSessionId(bridge, p.sessionId);
|
|
51415
|
+
if (!sessionId) {
|
|
51416
|
+
return {
|
|
51417
|
+
id: req.id,
|
|
51418
|
+
result: {
|
|
51419
|
+
currentSeq: 0,
|
|
51420
|
+
oldestBufferedSeq: 0,
|
|
51421
|
+
sessionCount
|
|
51422
|
+
}
|
|
51423
|
+
};
|
|
50402
51424
|
}
|
|
50403
51425
|
return {
|
|
50404
51426
|
id: req.id,
|
|
50405
51427
|
result: {
|
|
50406
|
-
...readSyncStatus(bridge,
|
|
50407
|
-
sessionCount
|
|
51428
|
+
...readSyncStatus(bridge, sessionId),
|
|
51429
|
+
sessionCount
|
|
50408
51430
|
}
|
|
50409
51431
|
};
|
|
50410
51432
|
}
|
|
@@ -55584,6 +56606,20 @@ function readSyncStatus2(bridge, sessionId) {
|
|
|
55584
56606
|
oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
|
|
55585
56607
|
};
|
|
55586
56608
|
}
|
|
56609
|
+
function resolveSyncSessionId2(bridge, preferredSessionId) {
|
|
56610
|
+
if (preferredSessionId) {
|
|
56611
|
+
return preferredSessionId;
|
|
56612
|
+
}
|
|
56613
|
+
let latestSessionId = null;
|
|
56614
|
+
let latestActivityAt = Number.NEGATIVE_INFINITY;
|
|
56615
|
+
for (const session2 of bridge.getSessionSummaries()) {
|
|
56616
|
+
if (session2.lastActivityAt > latestActivityAt) {
|
|
56617
|
+
latestActivityAt = session2.lastActivityAt;
|
|
56618
|
+
latestSessionId = session2.sessionId;
|
|
56619
|
+
}
|
|
56620
|
+
}
|
|
56621
|
+
return latestSessionId;
|
|
56622
|
+
}
|
|
55587
56623
|
var t, logged, procedure, MARKER_FILES2, sessionRouter, mobileRouter, workspaceRouter, historyRouter, promptRouter, syncRouter, bridgeRouter;
|
|
55588
56624
|
var init_router = __esm(async () => {
|
|
55589
56625
|
init_dist3();
|
|
@@ -55592,6 +56628,7 @@ var init_router = __esm(async () => {
|
|
|
55592
56628
|
init_log();
|
|
55593
56629
|
init_config3();
|
|
55594
56630
|
init_db_queries();
|
|
56631
|
+
init_web_handoff();
|
|
55595
56632
|
await __promiseAll([
|
|
55596
56633
|
init_service5(),
|
|
55597
56634
|
init_src2(),
|
|
@@ -55799,6 +56836,64 @@ var init_router = __esm(async () => {
|
|
|
55799
56836
|
limit: typeof input.limit === "number" ? input.limit : null
|
|
55800
56837
|
}, resolveMobileCurrentDirectory2());
|
|
55801
56838
|
}),
|
|
56839
|
+
webHandoff: procedure.input(exports_external.object({
|
|
56840
|
+
kind: exports_external.enum(["session", "file_change"]),
|
|
56841
|
+
sessionId: exports_external.string(),
|
|
56842
|
+
turnId: exports_external.string().optional(),
|
|
56843
|
+
blockId: exports_external.string().optional()
|
|
56844
|
+
})).mutation(({ input, ctx }) => {
|
|
56845
|
+
if (!ctx.deviceId) {
|
|
56846
|
+
throw new TRPCError({
|
|
56847
|
+
code: "UNAUTHORIZED",
|
|
56848
|
+
message: "Secure web handoff requires a paired mobile device"
|
|
56849
|
+
});
|
|
56850
|
+
}
|
|
56851
|
+
const snapshot = ctx.bridge.getSessionSnapshot(input.sessionId);
|
|
56852
|
+
if (!snapshot) {
|
|
56853
|
+
throw new TRPCError({
|
|
56854
|
+
code: "NOT_FOUND",
|
|
56855
|
+
message: `No session: ${input.sessionId}`
|
|
56856
|
+
});
|
|
56857
|
+
}
|
|
56858
|
+
let scope;
|
|
56859
|
+
let title = snapshot.session.name || snapshot.session.id;
|
|
56860
|
+
if (input.kind === "file_change") {
|
|
56861
|
+
if (!input.turnId || !input.blockId) {
|
|
56862
|
+
throw new TRPCError({
|
|
56863
|
+
code: "BAD_REQUEST",
|
|
56864
|
+
message: "turnId and blockId are required for file_change handoffs"
|
|
56865
|
+
});
|
|
56866
|
+
}
|
|
56867
|
+
const turn = snapshot.turns.find((candidate) => candidate.id === input.turnId);
|
|
56868
|
+
const block = turn?.blocks.find((candidate) => candidate.block.id === input.blockId)?.block;
|
|
56869
|
+
if (!turn || !block || block.type !== "action" || block.action.kind !== "file_change") {
|
|
56870
|
+
throw new TRPCError({
|
|
56871
|
+
code: "NOT_FOUND",
|
|
56872
|
+
message: "File change block not found"
|
|
56873
|
+
});
|
|
56874
|
+
}
|
|
56875
|
+
scope = {
|
|
56876
|
+
kind: "file_change",
|
|
56877
|
+
sessionId: input.sessionId,
|
|
56878
|
+
turnId: input.turnId,
|
|
56879
|
+
blockId: input.blockId
|
|
56880
|
+
};
|
|
56881
|
+
title = block.action.path || title;
|
|
56882
|
+
} else {
|
|
56883
|
+
scope = {
|
|
56884
|
+
kind: "session",
|
|
56885
|
+
sessionId: input.sessionId
|
|
56886
|
+
};
|
|
56887
|
+
}
|
|
56888
|
+
const issued = issueWebHandoff(scope, ctx.deviceId);
|
|
56889
|
+
return {
|
|
56890
|
+
kind: input.kind,
|
|
56891
|
+
path: pathForWebHandoffScope(scope),
|
|
56892
|
+
token: issued.token,
|
|
56893
|
+
expiresAt: issued.expiresAt,
|
|
56894
|
+
title
|
|
56895
|
+
};
|
|
56896
|
+
}),
|
|
55802
56897
|
createSession: procedure.input(exports_external.object({
|
|
55803
56898
|
workspaceId: exports_external.string(),
|
|
55804
56899
|
harness: exports_external.string().optional(),
|
|
@@ -56004,14 +57099,27 @@ var init_router = __esm(async () => {
|
|
|
56004
57099
|
})
|
|
56005
57100
|
});
|
|
56006
57101
|
syncRouter = t.router({
|
|
56007
|
-
replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string() })).query(({ input, ctx }) => {
|
|
56008
|
-
const
|
|
57102
|
+
replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string().optional() })).query(({ input, ctx }) => {
|
|
57103
|
+
const sessionId = resolveSyncSessionId2(ctx.bridge, input.sessionId);
|
|
57104
|
+
if (!sessionId) {
|
|
57105
|
+
return { events: [] };
|
|
57106
|
+
}
|
|
57107
|
+
const events2 = replaySyncEvents2(ctx.bridge, sessionId, input.lastSeq);
|
|
56009
57108
|
return { events: events2 };
|
|
56010
57109
|
}),
|
|
56011
|
-
status: procedure.input(exports_external.object({ sessionId: exports_external.string() })).query(({ input, ctx }) => {
|
|
57110
|
+
status: procedure.input(exports_external.object({ sessionId: exports_external.string().optional() }).optional()).query(({ input, ctx }) => {
|
|
57111
|
+
const sessionCount = ctx.bridge.listSessions().length;
|
|
57112
|
+
const sessionId = resolveSyncSessionId2(ctx.bridge, input?.sessionId);
|
|
57113
|
+
if (!sessionId) {
|
|
57114
|
+
return {
|
|
57115
|
+
currentSeq: 0,
|
|
57116
|
+
oldestBufferedSeq: 0,
|
|
57117
|
+
sessionCount
|
|
57118
|
+
};
|
|
57119
|
+
}
|
|
56012
57120
|
return {
|
|
56013
|
-
...readSyncStatus2(ctx.bridge,
|
|
56014
|
-
sessionCount
|
|
57121
|
+
...readSyncStatus2(ctx.bridge, sessionId),
|
|
57122
|
+
sessionCount
|
|
56015
57123
|
};
|
|
56016
57124
|
})
|
|
56017
57125
|
});
|
|
@@ -60620,7 +61728,7 @@ async function startPairingRuntime(options) {
|
|
|
60620
61728
|
secure: config2.secure,
|
|
60621
61729
|
identity: config2.secure ? identity2 : undefined
|
|
60622
61730
|
});
|
|
60623
|
-
const fileServer = startFileServer({ port: config2.port + 2 });
|
|
61731
|
+
const fileServer = startFileServer({ port: config2.port + 2, bridge });
|
|
60624
61732
|
const relayUrl = options?.relayUrl?.trim() || config2.relay || null;
|
|
60625
61733
|
if (!relayUrl) {
|
|
60626
61734
|
fileServer.stop();
|
|
@@ -60952,19 +62060,58 @@ function findStoredCerts() {
|
|
|
60952
62060
|
return null;
|
|
60953
62061
|
}
|
|
60954
62062
|
}
|
|
60955
|
-
function
|
|
62063
|
+
function readTailscaleStatus2() {
|
|
60956
62064
|
try {
|
|
60957
62065
|
const output = execSync4("tailscale status --self=true --peers=false --json", {
|
|
60958
62066
|
stdio: ["pipe", "pipe", "pipe"],
|
|
60959
62067
|
timeout: 5000
|
|
60960
62068
|
}).toString();
|
|
60961
62069
|
const data = JSON.parse(output);
|
|
60962
|
-
const dnsName = data
|
|
60963
|
-
return
|
|
62070
|
+
const dnsName = typeof data.Self?.DNSName === "string" ? data.Self.DNSName.replace(/\.$/, "") : "";
|
|
62071
|
+
return {
|
|
62072
|
+
backendState: typeof data.BackendState === "string" ? data.BackendState : null,
|
|
62073
|
+
dnsName: dnsName || null,
|
|
62074
|
+
online: data.Self?.Online !== false,
|
|
62075
|
+
health: Array.isArray(data.Health) ? data.Health.filter((entry) => typeof entry === "string") : []
|
|
62076
|
+
};
|
|
60964
62077
|
} catch {
|
|
60965
62078
|
return null;
|
|
60966
62079
|
}
|
|
60967
62080
|
}
|
|
62081
|
+
function resolveRelayEndpointForTailscaleStatus(port, tailscale2) {
|
|
62082
|
+
const backendState = tailscale2?.backendState?.trim().toLowerCase() ?? "";
|
|
62083
|
+
const hostname5 = tailscale2?.dnsName ?? null;
|
|
62084
|
+
const tailscaleRunning = backendState === "running" && tailscale2?.online !== false && Boolean(hostname5);
|
|
62085
|
+
const tls = resolveTls(tailscaleRunning ? hostname5 : null);
|
|
62086
|
+
if (tailscaleRunning && hostname5 && tls) {
|
|
62087
|
+
return {
|
|
62088
|
+
relayUrl: `wss://${hostname5}:${port}`,
|
|
62089
|
+
options: { tls }
|
|
62090
|
+
};
|
|
62091
|
+
}
|
|
62092
|
+
if (tailscaleRunning && hostname5) {
|
|
62093
|
+
pairingLog.warn("relay", "tailscale is running without TLS; falling back to insecure websocket relay", {
|
|
62094
|
+
hostname: hostname5,
|
|
62095
|
+
port
|
|
62096
|
+
});
|
|
62097
|
+
return {
|
|
62098
|
+
relayUrl: `ws://${hostname5}:${port}`,
|
|
62099
|
+
options: {}
|
|
62100
|
+
};
|
|
62101
|
+
}
|
|
62102
|
+
if (hostname5 && backendState && backendState !== "running") {
|
|
62103
|
+
pairingLog.warn("relay", "tailscale hostname detected but tailscale is not running; using local-only relay endpoint", {
|
|
62104
|
+
hostname: hostname5,
|
|
62105
|
+
backendState,
|
|
62106
|
+
health: tailscale2?.health ?? [],
|
|
62107
|
+
port
|
|
62108
|
+
});
|
|
62109
|
+
}
|
|
62110
|
+
return {
|
|
62111
|
+
relayUrl: `ws://127.0.0.1:${port}`,
|
|
62112
|
+
options: {}
|
|
62113
|
+
};
|
|
62114
|
+
}
|
|
60968
62115
|
function generateTailscaleCerts(hostname5) {
|
|
60969
62116
|
mkdirSync11(PAIRING_DIR2, { recursive: true });
|
|
60970
62117
|
const certPath = join27(PAIRING_DIR2, `${hostname5}.crt`);
|
|
@@ -61010,28 +62157,7 @@ function resolveTls(hostname5) {
|
|
|
61010
62157
|
return generateTailscaleCerts(hostname5);
|
|
61011
62158
|
}
|
|
61012
62159
|
function resolveRelayEndpoint(port) {
|
|
61013
|
-
|
|
61014
|
-
const tls = resolveTls(hostname5);
|
|
61015
|
-
if (hostname5 && tls) {
|
|
61016
|
-
return {
|
|
61017
|
-
relayUrl: `wss://${hostname5}:${port}`,
|
|
61018
|
-
options: { tls }
|
|
61019
|
-
};
|
|
61020
|
-
}
|
|
61021
|
-
if (hostname5) {
|
|
61022
|
-
pairingLog.warn("relay", "tailscale hostname detected without TLS; falling back to insecure websocket relay", {
|
|
61023
|
-
hostname: hostname5,
|
|
61024
|
-
port
|
|
61025
|
-
});
|
|
61026
|
-
return {
|
|
61027
|
-
relayUrl: `ws://${hostname5}:${port}`,
|
|
61028
|
-
options: {}
|
|
61029
|
-
};
|
|
61030
|
-
}
|
|
61031
|
-
return {
|
|
61032
|
-
relayUrl: `ws://127.0.0.1:${port}`,
|
|
61033
|
-
options: {}
|
|
61034
|
-
};
|
|
62160
|
+
return resolveRelayEndpointForTailscaleStatus(port, readTailscaleStatus2());
|
|
61035
62161
|
}
|
|
61036
62162
|
function startManagedRelay(port = 7889) {
|
|
61037
62163
|
const endpoint = resolveRelayEndpoint(port);
|
|
@@ -61983,11 +63109,13 @@ __export(exports_server, {
|
|
|
61983
63109
|
runServerCommand: () => runServerCommand,
|
|
61984
63110
|
resolveScoutWebServerEntry: () => resolveScoutWebServerEntry,
|
|
61985
63111
|
resolveScoutControlPlaneWebServerEntry: () => resolveScoutControlPlaneWebServerEntry,
|
|
63112
|
+
resolveBunExecutable: () => resolveBunExecutable3,
|
|
61986
63113
|
renderServerCommandHelp: () => renderServerCommandHelp,
|
|
61987
63114
|
normalizeServerOpenPath: () => normalizeServerOpenPath
|
|
61988
63115
|
});
|
|
61989
63116
|
import { spawn as spawn5 } from "child_process";
|
|
61990
|
-
import { existsSync as existsSync20 } from "fs";
|
|
63117
|
+
import { accessSync as accessSync2, constants as constants4, existsSync as existsSync20 } from "fs";
|
|
63118
|
+
import { homedir as homedir23 } from "os";
|
|
61991
63119
|
import { dirname as dirname14, join as join28, resolve as resolve14 } from "path";
|
|
61992
63120
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
61993
63121
|
function renderServerCommandHelp() {
|
|
@@ -62108,6 +63236,42 @@ function resolveBundledStaticClientRoot(entry, _mode) {
|
|
|
62108
63236
|
const indexPath = join28(clientDirectory, "index.html");
|
|
62109
63237
|
return existsSync20(indexPath) ? clientDirectory : null;
|
|
62110
63238
|
}
|
|
63239
|
+
function isExecutable4(filePath) {
|
|
63240
|
+
if (!filePath) {
|
|
63241
|
+
return false;
|
|
63242
|
+
}
|
|
63243
|
+
try {
|
|
63244
|
+
accessSync2(filePath, constants4.X_OK);
|
|
63245
|
+
return true;
|
|
63246
|
+
} catch {
|
|
63247
|
+
return false;
|
|
63248
|
+
}
|
|
63249
|
+
}
|
|
63250
|
+
function resolveBunExecutable3(env) {
|
|
63251
|
+
const explicitPaths = [
|
|
63252
|
+
env.SCOUT_BUN_BIN,
|
|
63253
|
+
env.OPENSCOUT_BUN_BIN,
|
|
63254
|
+
env.BUN_BIN
|
|
63255
|
+
].filter((candidate) => Boolean(candidate?.trim()));
|
|
63256
|
+
for (const candidate of explicitPaths) {
|
|
63257
|
+
if (isExecutable4(candidate)) {
|
|
63258
|
+
return candidate;
|
|
63259
|
+
}
|
|
63260
|
+
}
|
|
63261
|
+
const pathEntries = (env.PATH ?? "").split(":").filter(Boolean);
|
|
63262
|
+
const commonDirectories = [
|
|
63263
|
+
join28(homedir23(), ".bun", "bin"),
|
|
63264
|
+
"/opt/homebrew/bin",
|
|
63265
|
+
"/usr/local/bin"
|
|
63266
|
+
];
|
|
63267
|
+
for (const directory of [...pathEntries, ...commonDirectories]) {
|
|
63268
|
+
const candidate = join28(directory.replace(/^~(?=$|\/)/, homedir23()), "bun");
|
|
63269
|
+
if (isExecutable4(candidate)) {
|
|
63270
|
+
return candidate;
|
|
63271
|
+
}
|
|
63272
|
+
}
|
|
63273
|
+
return "bun";
|
|
63274
|
+
}
|
|
62111
63275
|
function buildMergedServerEnv(entry, mode, flagEnv) {
|
|
62112
63276
|
const bundledStaticClientRoot = resolveBundledStaticClientRoot(entry, mode);
|
|
62113
63277
|
const autoEnv = {};
|
|
@@ -62280,8 +63444,9 @@ async function openBrowser(url2) {
|
|
|
62280
63444
|
});
|
|
62281
63445
|
}
|
|
62282
63446
|
async function spawnDetachedServer(entry, env) {
|
|
63447
|
+
const bunExecutable = resolveBunExecutable3(env);
|
|
62283
63448
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
62284
|
-
const child = spawn5(
|
|
63449
|
+
const child = spawn5(bunExecutable, ["run", entry], {
|
|
62285
63450
|
detached: true,
|
|
62286
63451
|
stdio: "ignore",
|
|
62287
63452
|
env,
|
|
@@ -62373,8 +63538,9 @@ async function runServerCommand(context, args) {
|
|
|
62373
63538
|
context.output.writeValue(result, renderServerOpenResult);
|
|
62374
63539
|
return;
|
|
62375
63540
|
}
|
|
63541
|
+
const bunExecutable = resolveBunExecutable3(mergedEnv);
|
|
62376
63542
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
62377
|
-
const child = spawn5(
|
|
63543
|
+
const child = spawn5(bunExecutable, ["run", selection.entry], {
|
|
62378
63544
|
stdio: "inherit",
|
|
62379
63545
|
env: mergedEnv
|
|
62380
63546
|
});
|
|
@@ -71572,7 +72738,7 @@ var __defProp3, __returnValue2 = (v) => v, __export2 = (target, all) => {
|
|
|
71572
72738
|
configurable: true,
|
|
71573
72739
|
set: __exportSetter2.bind(all, name)
|
|
71574
72740
|
});
|
|
71575
|
-
}, exports_src, loadYoga, yoga_wasm_base64_esm_default, Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap,
|
|
72741
|
+
}, exports_src, loadYoga, yoga_wasm_base64_esm_default, Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap, constants5, YGEnums_default, Yoga, src_default, VALID_BORDER_STYLES, BorderChars, BorderCharArrays, KeyHandler, InternalKeyHandler, CSS_COLOR_NAMES, block_default, shade_default, slick_default, tiny_default, huge_default, grid_default, pallet_default, fonts, parsedFonts, TextAttributes, ATTRIBUTE_BASE_BITS = 8, ATTRIBUTE_BASE_MASK = 255, DebugOverlayCorner, TargetChannel, ATTRIBUTE_BASE_MASK2 = 255, LINK_ID_SHIFT = 8, LINK_ID_PAYLOAD_MASK = 16777215, BrandedStyledText, StyledText, black = (input) => applyStyle(input, { fg: "black" }), red = (input) => applyStyle(input, { fg: "red" }), green = (input) => applyStyle(input, { fg: "green" }), yellow = (input) => applyStyle(input, { fg: "yellow" }), blue = (input) => applyStyle(input, { fg: "blue" }), magenta = (input) => applyStyle(input, { fg: "magenta" }), cyan = (input) => applyStyle(input, { fg: "cyan" }), white = (input) => applyStyle(input, { fg: "white" }), brightBlack = (input) => applyStyle(input, { fg: "brightBlack" }), brightRed = (input) => applyStyle(input, { fg: "brightRed" }), brightGreen = (input) => applyStyle(input, { fg: "brightGreen" }), brightYellow = (input) => applyStyle(input, { fg: "brightYellow" }), brightBlue = (input) => applyStyle(input, { fg: "brightBlue" }), brightMagenta = (input) => applyStyle(input, { fg: "brightMagenta" }), brightCyan = (input) => applyStyle(input, { fg: "brightCyan" }), brightWhite = (input) => applyStyle(input, { fg: "brightWhite" }), bgBlack = (input) => applyStyle(input, { bg: "black" }), bgRed = (input) => applyStyle(input, { bg: "red" }), bgGreen = (input) => applyStyle(input, { bg: "green" }), bgYellow = (input) => applyStyle(input, { bg: "yellow" }), bgBlue = (input) => applyStyle(input, { bg: "blue" }), bgMagenta = (input) => applyStyle(input, { bg: "magenta" }), bgCyan = (input) => applyStyle(input, { bg: "cyan" }), bgWhite = (input) => applyStyle(input, { bg: "white" }), bold = (input) => applyStyle(input, { bold: true }), italic = (input) => applyStyle(input, { italic: true }), underline = (input) => applyStyle(input, { underline: true }), strikethrough = (input) => applyStyle(input, { strikethrough: true }), dim = (input) => applyStyle(input, { dim: true }), reverse = (input) => applyStyle(input, { reverse: true }), blink = (input) => applyStyle(input, { blink: true }), fg = (color) => (input) => applyStyle(input, { fg: color }), bg = (color) => (input) => applyStyle(input, { bg: color }), link = (url2) => (input) => {
|
|
71576
72742
|
const chunk = typeof input === "object" && "__isChunk" in input ? input : {
|
|
71577
72743
|
__isChunk: true,
|
|
71578
72744
|
text: String(input)
|
|
@@ -73232,7 +74398,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
|
|
|
73232
74398
|
Wrap2[Wrap2["WrapReverse"] = 2] = "WrapReverse";
|
|
73233
74399
|
return Wrap2;
|
|
73234
74400
|
}({});
|
|
73235
|
-
|
|
74401
|
+
constants5 = {
|
|
73236
74402
|
ALIGN_AUTO: Align.Auto,
|
|
73237
74403
|
ALIGN_FLEX_START: Align.FlexStart,
|
|
73238
74404
|
ALIGN_CENTER: Align.Center,
|
|
@@ -73306,7 +74472,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
|
|
|
73306
74472
|
WRAP_WRAP: Wrap.Wrap,
|
|
73307
74473
|
WRAP_WRAP_REVERSE: Wrap.WrapReverse
|
|
73308
74474
|
};
|
|
73309
|
-
YGEnums_default =
|
|
74475
|
+
YGEnums_default = constants5;
|
|
73310
74476
|
Yoga = wrapAssembly(await yoga_wasm_base64_esm_default());
|
|
73311
74477
|
src_default = Yoga;
|
|
73312
74478
|
VALID_BORDER_STYLES = ["single", "double", "rounded", "heavy"];
|
|
@@ -123823,6 +124989,7 @@ async function runUpCommand(context, args) {
|
|
|
123823
124989
|
let target = null;
|
|
123824
124990
|
let agentName;
|
|
123825
124991
|
let harness;
|
|
124992
|
+
let model;
|
|
123826
124993
|
for (let index2 = 0;index2 < args.length; index2 += 1) {
|
|
123827
124994
|
const current = args[index2] ?? "";
|
|
123828
124995
|
if (current === "--name") {
|
|
@@ -123851,6 +125018,19 @@ async function runUpCommand(context, args) {
|
|
|
123851
125018
|
harness = current.slice("--harness=".length);
|
|
123852
125019
|
continue;
|
|
123853
125020
|
}
|
|
125021
|
+
if (current === "--model") {
|
|
125022
|
+
const value = args[index2 + 1];
|
|
125023
|
+
if (!value) {
|
|
125024
|
+
throw new ScoutCliError("missing value for --model");
|
|
125025
|
+
}
|
|
125026
|
+
model = value;
|
|
125027
|
+
index2 += 1;
|
|
125028
|
+
continue;
|
|
125029
|
+
}
|
|
125030
|
+
if (current.startsWith("--model=")) {
|
|
125031
|
+
model = current.slice("--model=".length);
|
|
125032
|
+
continue;
|
|
125033
|
+
}
|
|
123854
125034
|
if (current.startsWith("--")) {
|
|
123855
125035
|
throw new ScoutCliError(`unexpected argument for up: ${current}`);
|
|
123856
125036
|
}
|
|
@@ -123860,7 +125040,7 @@ async function runUpCommand(context, args) {
|
|
|
123860
125040
|
target = current;
|
|
123861
125041
|
}
|
|
123862
125042
|
if (!target) {
|
|
123863
|
-
throw new ScoutCliError("usage: scout up <name|path> [--name <alias>] [--harness <claude|codex>]");
|
|
125043
|
+
throw new ScoutCliError("usage: scout up <name|path> [--name <alias>] [--harness <claude|codex>] [--model <model>]");
|
|
123864
125044
|
}
|
|
123865
125045
|
let projectPath;
|
|
123866
125046
|
if (looksLikePath(target) || existsSync23(resolve17(target))) {
|
|
@@ -123876,6 +125056,7 @@ async function runUpCommand(context, args) {
|
|
|
123876
125056
|
projectPath,
|
|
123877
125057
|
agentName,
|
|
123878
125058
|
harness: parseScoutHarness(harness),
|
|
125059
|
+
model,
|
|
123879
125060
|
currentDirectory: defaultScoutContextDirectory(context)
|
|
123880
125061
|
});
|
|
123881
125062
|
context.output.writeValue(agent, renderScoutUpResult);
|
|
@@ -123986,7 +125167,7 @@ var init_whoami = __esm(async () => {
|
|
|
123986
125167
|
import { existsSync as existsSync25, readFileSync as readFileSync15, writeFileSync as writeFileSync9, statSync as statSync5, mkdirSync as mkdirSync12 } from "fs";
|
|
123987
125168
|
import { join as join32 } from "path";
|
|
123988
125169
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
123989
|
-
import { homedir as
|
|
125170
|
+
import { homedir as homedir24 } from "os";
|
|
123990
125171
|
|
|
123991
125172
|
// ../../apps/desktop/src/cli/argv.ts
|
|
123992
125173
|
function parseScoutArgv(argv) {
|
|
@@ -124246,7 +125427,7 @@ var _scoutBinPath = null;
|
|
|
124246
125427
|
function getScoutBinPath() {
|
|
124247
125428
|
if (_scoutBinPath)
|
|
124248
125429
|
return _scoutBinPath;
|
|
124249
|
-
_scoutBinPath = join32(
|
|
125430
|
+
_scoutBinPath = join32(homedir24(), ".bun", "bin", "scout");
|
|
124250
125431
|
if (!existsSync25(_scoutBinPath)) {
|
|
124251
125432
|
_scoutBinPath = spawnSync3("which", ["scout"], { encoding: "utf8" }).stdout.trim();
|
|
124252
125433
|
}
|
|
@@ -124255,12 +125436,12 @@ function getScoutBinPath() {
|
|
|
124255
125436
|
async function ensureBrokerUptodate() {
|
|
124256
125437
|
try {
|
|
124257
125438
|
const mtime = statSync5(getScoutBinPath()).mtimeMs;
|
|
124258
|
-
const checkpointDir = join32(
|
|
125439
|
+
const checkpointDir = join32(homedir24(), ".scout");
|
|
124259
125440
|
const mtimePath = join32(checkpointDir, "cli-mtime");
|
|
124260
125441
|
const lastMtime = existsSync25(mtimePath) ? Number(readFileSync15(mtimePath, "utf8").trim()) : 0;
|
|
124261
125442
|
if (mtime > lastMtime) {
|
|
124262
125443
|
const uid = process.getuid();
|
|
124263
|
-
const plistPath = join32(
|
|
125444
|
+
const plistPath = join32(homedir24(), "Library", "LaunchAgents", "dev.openscout.broker.plist");
|
|
124264
125445
|
spawnSync3("launchctl", ["bootout", `gui/${uid}/dev.openscout.broker`], { stdio: "ignore" });
|
|
124265
125446
|
await new Promise((resolve18) => setTimeout(resolve18, 1500));
|
|
124266
125447
|
spawnSync3("launchctl", ["bootstrap", `gui/${uid}`, plistPath], { stdio: "ignore" });
|