@openscout/scout 0.2.57 → 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/README.md +2 -2
- package/dist/client/assets/{arc.es-Ccqmb9Pq.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 +1560 -223
- package/dist/pair-supervisor.mjs +1230 -115
- package/dist/scout-control-plane-web.mjs +935 -571
- package/package.json +2 -2
- package/dist/client/assets/index-CN8PCsrs.css +0 -1
- package/dist/client/assets/index-X8QwrbtL.js +0 -140
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
|
};
|
|
@@ -2277,6 +2285,7 @@ function createBuiltInHarnessCatalog() {
|
|
|
2277
2285
|
anyOf: entry.readiness.anyOf ? [...entry.readiness.anyOf] : undefined
|
|
2278
2286
|
} : undefined,
|
|
2279
2287
|
launch: entry.launch ? { ...entry.launch, args: [...entry.launch.args] } : undefined,
|
|
2288
|
+
resume: entry.resume ? { ...entry.resume } : undefined,
|
|
2280
2289
|
resolveEnv: entry.resolveEnv ? [...entry.resolveEnv] : undefined,
|
|
2281
2290
|
capabilities: [...entry.capabilities],
|
|
2282
2291
|
metadata: entry.metadata ? { ...entry.metadata } : undefined
|
|
@@ -2294,6 +2303,7 @@ function mergeHarnessCatalogEntries(baseEntries, overrides = {}) {
|
|
|
2294
2303
|
install: mergeInstall(entry.install, override.install),
|
|
2295
2304
|
readiness: mergeReadiness(entry.readiness, override.readiness),
|
|
2296
2305
|
launch: mergeLaunch(entry.launch, override.launch),
|
|
2306
|
+
resume: override.resume && entry.resume ? { ...entry.resume, ...override.resume } : entry.resume,
|
|
2297
2307
|
tags: override.tags ? [...override.tags] : entry.tags,
|
|
2298
2308
|
capabilities: override.capabilities ? [...override.capabilities] : entry.capabilities,
|
|
2299
2309
|
resolveEnv: override.resolveEnv ? [...override.resolveEnv] : entry.resolveEnv,
|
|
@@ -2322,6 +2332,7 @@ function mergeHarnessCatalogEntries(baseEntries, overrides = {}) {
|
|
|
2322
2332
|
install: mergeInstall(undefined, override.install),
|
|
2323
2333
|
readiness: mergeReadiness(undefined, override.readiness),
|
|
2324
2334
|
launch: mergeLaunch(undefined, override.launch),
|
|
2335
|
+
resume: override.resume?.command && override.resume?.sessionFlag ? { command: override.resume.command, sessionFlag: override.resume.sessionFlag, cwdFlag: override.resume.cwdFlag } : undefined,
|
|
2325
2336
|
resolveEnv: override.resolveEnv ? [...override.resolveEnv] : undefined,
|
|
2326
2337
|
capabilities: [...override.capabilities],
|
|
2327
2338
|
metadata: override.metadata ? { ...override.metadata } : undefined
|
|
@@ -2474,6 +2485,11 @@ var init_harness_catalog = __esm(() => {
|
|
|
2474
2485
|
loginCommand: "claude login",
|
|
2475
2486
|
notReadyMessage: "Claude is installed but not authenticated yet."
|
|
2476
2487
|
},
|
|
2488
|
+
resume: {
|
|
2489
|
+
command: "claude",
|
|
2490
|
+
sessionFlag: "--resume",
|
|
2491
|
+
cwdFlag: "--cwd"
|
|
2492
|
+
},
|
|
2477
2493
|
capabilities: ["chat", "invoke", "deliver", "summarize", "review"]
|
|
2478
2494
|
},
|
|
2479
2495
|
{
|
|
@@ -2505,6 +2521,11 @@ var init_harness_catalog = __esm(() => {
|
|
|
2505
2521
|
loginCommand: "codex login",
|
|
2506
2522
|
notReadyMessage: "Codex is installed but not authenticated yet."
|
|
2507
2523
|
},
|
|
2524
|
+
resume: {
|
|
2525
|
+
command: "codex",
|
|
2526
|
+
sessionFlag: "--thread",
|
|
2527
|
+
cwdFlag: "--cwd"
|
|
2528
|
+
},
|
|
2508
2529
|
capabilities: ["chat", "invoke", "deliver", "review", "execute"]
|
|
2509
2530
|
}
|
|
2510
2531
|
];
|
|
@@ -4684,7 +4705,8 @@ function buildManagedAgentShellExports(options) {
|
|
|
4684
4705
|
// ../runtime/src/claude-stream-json.ts
|
|
4685
4706
|
import { randomUUID } from "crypto";
|
|
4686
4707
|
import { spawn } from "child_process";
|
|
4687
|
-
import { appendFile, mkdir as mkdir3, readFile as readFile4,
|
|
4708
|
+
import { appendFile, mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
4709
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
4688
4710
|
import { join as join5 } from "path";
|
|
4689
4711
|
function resolveClaudeStreamJsonOutput(result, fallbackParts) {
|
|
4690
4712
|
const trimmedResult = result?.trim();
|
|
@@ -4699,19 +4721,57 @@ function sessionKey(options) {
|
|
|
4699
4721
|
function errorMessage(error) {
|
|
4700
4722
|
return error instanceof Error ? error.message : String(error);
|
|
4701
4723
|
}
|
|
4702
|
-
|
|
4724
|
+
function readSessionCatalogSync(runtimeDirectory) {
|
|
4725
|
+
const catalogPath = join5(runtimeDirectory, SESSION_CATALOG_FILENAME);
|
|
4703
4726
|
try {
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4727
|
+
if (!existsSync4(catalogPath)) {
|
|
4728
|
+
const legacyPath = join5(runtimeDirectory, "claude-session-id.txt");
|
|
4729
|
+
if (existsSync4(legacyPath)) {
|
|
4730
|
+
const legacyId = readFileSync2(legacyPath, "utf8").trim();
|
|
4731
|
+
if (legacyId) {
|
|
4732
|
+
return {
|
|
4733
|
+
activeSessionId: legacyId,
|
|
4734
|
+
sessions: [{ id: legacyId, startedAt: Date.now(), cwd: "" }]
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
return { activeSessionId: null, sessions: [] };
|
|
4739
|
+
}
|
|
4740
|
+
const raw = readFileSync2(catalogPath, "utf8").trim();
|
|
4741
|
+
if (!raw)
|
|
4742
|
+
return { activeSessionId: null, sessions: [] };
|
|
4743
|
+
const parsed = JSON.parse(raw);
|
|
4744
|
+
return {
|
|
4745
|
+
activeSessionId: parsed.activeSessionId ?? null,
|
|
4746
|
+
sessions: Array.isArray(parsed.sessions) ? parsed.sessions : []
|
|
4747
|
+
};
|
|
4707
4748
|
} catch {
|
|
4708
|
-
return null;
|
|
4749
|
+
return { activeSessionId: null, sessions: [] };
|
|
4709
4750
|
}
|
|
4710
4751
|
}
|
|
4752
|
+
async function readSessionCatalog(runtimeDirectory) {
|
|
4753
|
+
return readSessionCatalogSync(runtimeDirectory);
|
|
4754
|
+
}
|
|
4755
|
+
async function writeSessionCatalog(runtimeDirectory, catalog) {
|
|
4756
|
+
const catalogPath = join5(runtimeDirectory, SESSION_CATALOG_FILENAME);
|
|
4757
|
+
await writeFile3(catalogPath, JSON.stringify(catalog, null, 2) + `
|
|
4758
|
+
`);
|
|
4759
|
+
}
|
|
4760
|
+
function catalogRecordSession(catalog, sessionId, cwd) {
|
|
4761
|
+
const now = Date.now();
|
|
4762
|
+
const sessions = catalog.sessions.map((s) => s.id === catalog.activeSessionId && !s.endedAt ? { ...s, endedAt: now } : s);
|
|
4763
|
+
if (!sessions.some((s) => s.id === sessionId)) {
|
|
4764
|
+
sessions.push({ id: sessionId, startedAt: now, cwd });
|
|
4765
|
+
}
|
|
4766
|
+
while (sessions.length > SESSION_CATALOG_MAX_ENTRIES) {
|
|
4767
|
+
sessions.shift();
|
|
4768
|
+
}
|
|
4769
|
+
return { activeSessionId: sessionId, sessions };
|
|
4770
|
+
}
|
|
4711
4771
|
|
|
4712
4772
|
class ClaudeStreamJsonSession {
|
|
4713
4773
|
options;
|
|
4714
|
-
|
|
4774
|
+
catalogDirectory;
|
|
4715
4775
|
stdoutLogPath;
|
|
4716
4776
|
stderrLogPath;
|
|
4717
4777
|
process = null;
|
|
@@ -4722,7 +4782,7 @@ class ClaudeStreamJsonSession {
|
|
|
4722
4782
|
lastConfigSignature;
|
|
4723
4783
|
constructor(options) {
|
|
4724
4784
|
this.options = options;
|
|
4725
|
-
this.
|
|
4785
|
+
this.catalogDirectory = options.runtimeDirectory;
|
|
4726
4786
|
this.stdoutLogPath = join5(options.logsDirectory, "stdout.log");
|
|
4727
4787
|
this.stderrLogPath = join5(options.logsDirectory, "stderr.log");
|
|
4728
4788
|
this.lastConfigSignature = this.configSignature(options);
|
|
@@ -4828,7 +4888,9 @@ class ClaudeStreamJsonSession {
|
|
|
4828
4888
|
}
|
|
4829
4889
|
if (options.resetSession) {
|
|
4830
4890
|
this.claudeSessionId = null;
|
|
4831
|
-
await
|
|
4891
|
+
const catalog = await readSessionCatalog(this.catalogDirectory);
|
|
4892
|
+
catalog.activeSessionId = null;
|
|
4893
|
+
await writeSessionCatalog(this.catalogDirectory, catalog);
|
|
4832
4894
|
}
|
|
4833
4895
|
}
|
|
4834
4896
|
configSignature(options) {
|
|
@@ -4857,7 +4919,8 @@ class ClaudeStreamJsonSession {
|
|
|
4857
4919
|
await mkdir3(this.options.runtimeDirectory, { recursive: true });
|
|
4858
4920
|
await mkdir3(this.options.logsDirectory, { recursive: true });
|
|
4859
4921
|
await writeFile3(join5(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
|
|
4860
|
-
|
|
4922
|
+
const catalog = await readSessionCatalog(this.catalogDirectory);
|
|
4923
|
+
this.claudeSessionId = catalog.activeSessionId;
|
|
4861
4924
|
const args = [
|
|
4862
4925
|
"--verbose",
|
|
4863
4926
|
"--print",
|
|
@@ -4936,8 +4999,11 @@ class ClaudeStreamJsonSession {
|
|
|
4936
4999
|
const nextSessionId = event.session_id ?? event.sessionId ?? null;
|
|
4937
5000
|
if (nextSessionId && nextSessionId !== this.claudeSessionId) {
|
|
4938
5001
|
this.claudeSessionId = nextSessionId;
|
|
4939
|
-
|
|
4940
|
-
|
|
5002
|
+
(async () => {
|
|
5003
|
+
const catalog = await readSessionCatalog(this.catalogDirectory);
|
|
5004
|
+
const updated = catalogRecordSession(catalog, nextSessionId, this.options.cwd);
|
|
5005
|
+
await writeSessionCatalog(this.catalogDirectory, updated);
|
|
5006
|
+
})();
|
|
4941
5007
|
}
|
|
4942
5008
|
return;
|
|
4943
5009
|
}
|
|
@@ -5000,14 +5066,16 @@ async function shutdownClaudeStreamJsonAgent(options, shutdownOptions = {}) {
|
|
|
5000
5066
|
const session = sessions.get(key);
|
|
5001
5067
|
if (!session) {
|
|
5002
5068
|
if (shutdownOptions.resetSession) {
|
|
5003
|
-
await
|
|
5069
|
+
const catalog = await readSessionCatalog(options.runtimeDirectory);
|
|
5070
|
+
catalog.activeSessionId = null;
|
|
5071
|
+
await writeSessionCatalog(options.runtimeDirectory, catalog);
|
|
5004
5072
|
}
|
|
5005
5073
|
return;
|
|
5006
5074
|
}
|
|
5007
5075
|
sessions.delete(key);
|
|
5008
5076
|
await session.shutdown(shutdownOptions);
|
|
5009
5077
|
}
|
|
5010
|
-
var sessions;
|
|
5078
|
+
var SESSION_CATALOG_FILENAME = "session-catalog.json", SESSION_CATALOG_MAX_ENTRIES = 64, sessions;
|
|
5011
5079
|
var init_claude_stream_json = __esm(() => {
|
|
5012
5080
|
sessions = new Map;
|
|
5013
5081
|
});
|
|
@@ -6184,7 +6252,7 @@ class ClaudeCodeHistoryParser {
|
|
|
6184
6252
|
var init_history = () => {};
|
|
6185
6253
|
|
|
6186
6254
|
// ../agent-sessions/src/adapters/claude-code.ts
|
|
6187
|
-
import { existsSync as
|
|
6255
|
+
import { existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
|
|
6188
6256
|
import { homedir as homedir4 } from "os";
|
|
6189
6257
|
import { join as join6 } from "path";
|
|
6190
6258
|
function resolveClaudeResumeContext(config) {
|
|
@@ -6194,7 +6262,7 @@ function resolveClaudeResumeContext(config) {
|
|
|
6194
6262
|
return null;
|
|
6195
6263
|
}
|
|
6196
6264
|
const projectsRoot = join6(homedir4(), ".claude", "projects");
|
|
6197
|
-
if (!
|
|
6265
|
+
if (!existsSync5(projectsRoot)) {
|
|
6198
6266
|
return null;
|
|
6199
6267
|
}
|
|
6200
6268
|
let projectSlugs;
|
|
@@ -6205,7 +6273,7 @@ function resolveClaudeResumeContext(config) {
|
|
|
6205
6273
|
}
|
|
6206
6274
|
for (const slug of projectSlugs) {
|
|
6207
6275
|
const sessionPath = join6(projectsRoot, slug, `${resumeId}.jsonl`);
|
|
6208
|
-
if (!
|
|
6276
|
+
if (!existsSync5(sessionPath)) {
|
|
6209
6277
|
continue;
|
|
6210
6278
|
}
|
|
6211
6279
|
const cwd = decodeClaudeProjectsSlug2(slug);
|
|
@@ -6746,7 +6814,7 @@ Referenced files: ${prompt.files.join(", ")}` });
|
|
|
6746
6814
|
});
|
|
6747
6815
|
|
|
6748
6816
|
// ../agent-sessions/src/codex-launch-config.ts
|
|
6749
|
-
import { accessSync, constants, existsSync as
|
|
6817
|
+
import { accessSync, constants, existsSync as existsSync6 } from "fs";
|
|
6750
6818
|
import { homedir as homedir5 } from "os";
|
|
6751
6819
|
import { basename as basename2, delimiter, dirname as dirname4, join as join7, resolve as resolve5 } from "path";
|
|
6752
6820
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -6830,7 +6898,7 @@ function resolveRepoScoutScript(currentDirectory) {
|
|
|
6830
6898
|
for (const start of starts) {
|
|
6831
6899
|
for (const candidate of ancestorChain(start)) {
|
|
6832
6900
|
const scriptPath = join7(candidate, "apps", "desktop", "bin", "scout.ts");
|
|
6833
|
-
if (
|
|
6901
|
+
if (existsSync6(scriptPath)) {
|
|
6834
6902
|
return scriptPath;
|
|
6835
6903
|
}
|
|
6836
6904
|
}
|
|
@@ -6920,7 +6988,7 @@ function errorMessage2(error) {
|
|
|
6920
6988
|
}
|
|
6921
6989
|
return String(error);
|
|
6922
6990
|
}
|
|
6923
|
-
async function
|
|
6991
|
+
async function readOptionalFile(filePath) {
|
|
6924
6992
|
try {
|
|
6925
6993
|
const raw = await readFile5(filePath, "utf8");
|
|
6926
6994
|
const trimmed = raw.trim();
|
|
@@ -7267,7 +7335,7 @@ var init_codex = __esm(() => {
|
|
|
7267
7335
|
async resumeOrStartThread() {
|
|
7268
7336
|
const options = this.codexOptions;
|
|
7269
7337
|
const requestedThreadId = options.threadId?.trim() || null;
|
|
7270
|
-
const storedThreadId = requestedThreadId ?? await
|
|
7338
|
+
const storedThreadId = requestedThreadId ?? await readOptionalFile(this.threadIdPath);
|
|
7271
7339
|
if (storedThreadId) {
|
|
7272
7340
|
try {
|
|
7273
7341
|
const resumed = await this.request("thread/resume", {
|
|
@@ -9386,8 +9454,107 @@ var init_src = __esm(() => {
|
|
|
9386
9454
|
|
|
9387
9455
|
// ../runtime/src/codex-app-server.ts
|
|
9388
9456
|
import { spawn as spawn3 } from "child_process";
|
|
9389
|
-
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";
|
|
9390
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
|
+
}
|
|
9391
9558
|
function sessionKey2(options) {
|
|
9392
9559
|
return `${options.agentName}:${options.sessionId}`;
|
|
9393
9560
|
}
|
|
@@ -9483,7 +9650,7 @@ function isServerRequest2(message) {
|
|
|
9483
9650
|
function isNotification2(message) {
|
|
9484
9651
|
return Boolean(message && typeof message === "object" && "method" in message && !("id" in message));
|
|
9485
9652
|
}
|
|
9486
|
-
async function
|
|
9653
|
+
async function readOptionalFile2(filePath) {
|
|
9487
9654
|
try {
|
|
9488
9655
|
const raw = await readFile6(filePath, "utf8");
|
|
9489
9656
|
const trimmed = raw.trim();
|
|
@@ -9496,6 +9663,13 @@ function isMissingCodexRolloutError2(error) {
|
|
|
9496
9663
|
const message = errorMessage3(error).toLowerCase();
|
|
9497
9664
|
return message.includes("no rollout found for thread id");
|
|
9498
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
|
+
}
|
|
9499
9673
|
|
|
9500
9674
|
class CodexAppServerSession {
|
|
9501
9675
|
options;
|
|
@@ -9685,7 +9859,7 @@ class CodexAppServerSession {
|
|
|
9685
9859
|
systemPrompt: options.systemPrompt,
|
|
9686
9860
|
threadId: options.threadId ?? null,
|
|
9687
9861
|
requireExistingThread: options.requireExistingThread === true,
|
|
9688
|
-
launchArgs:
|
|
9862
|
+
launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
|
|
9689
9863
|
});
|
|
9690
9864
|
}
|
|
9691
9865
|
async ensureStarted() {
|
|
@@ -9707,6 +9881,7 @@ class CodexAppServerSession {
|
|
|
9707
9881
|
await mkdir5(this.options.logsDirectory, { recursive: true });
|
|
9708
9882
|
await writeFile5(join9(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
|
|
9709
9883
|
const codexExecutable = await resolveCodexExecutable2();
|
|
9884
|
+
const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
|
|
9710
9885
|
const env = buildManagedAgentEnvironment({
|
|
9711
9886
|
agentName: this.options.agentName,
|
|
9712
9887
|
currentDirectory: this.options.cwd,
|
|
@@ -9717,7 +9892,8 @@ class CodexAppServerSession {
|
|
|
9717
9892
|
...buildScoutMcpCodexLaunchArgs({
|
|
9718
9893
|
currentDirectory: this.options.cwd,
|
|
9719
9894
|
env
|
|
9720
|
-
})
|
|
9895
|
+
}),
|
|
9896
|
+
...launchArgs
|
|
9721
9897
|
], {
|
|
9722
9898
|
cwd: this.options.cwd,
|
|
9723
9899
|
env,
|
|
@@ -9760,7 +9936,7 @@ class CodexAppServerSession {
|
|
|
9760
9936
|
}
|
|
9761
9937
|
async resumeOrStartThread() {
|
|
9762
9938
|
const requestedThreadId = this.options.threadId?.trim() || null;
|
|
9763
|
-
const storedThreadId = requestedThreadId ?? await
|
|
9939
|
+
const storedThreadId = requestedThreadId ?? await readOptionalFile2(this.threadIdPath);
|
|
9764
9940
|
if (storedThreadId) {
|
|
9765
9941
|
try {
|
|
9766
9942
|
const resumed = await this.request("thread/resume", {
|
|
@@ -9815,6 +9991,8 @@ class CodexAppServerSession {
|
|
|
9815
9991
|
turnId: "",
|
|
9816
9992
|
startedAt: Date.now(),
|
|
9817
9993
|
timer: null,
|
|
9994
|
+
graceTimer: null,
|
|
9995
|
+
timedOutAt: null,
|
|
9818
9996
|
messageOrder: [],
|
|
9819
9997
|
messageByItemId: new Map,
|
|
9820
9998
|
resolve: resolve6,
|
|
@@ -9822,20 +10000,43 @@ class CodexAppServerSession {
|
|
|
9822
10000
|
watchers: []
|
|
9823
10001
|
};
|
|
9824
10002
|
turn.timer = setTimeout(() => {
|
|
9825
|
-
this.
|
|
9826
|
-
return;
|
|
9827
|
-
});
|
|
9828
|
-
if (this.activeTurn === turn) {
|
|
9829
|
-
this.activeTurn = null;
|
|
9830
|
-
}
|
|
9831
|
-
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
9832
|
-
watcher.reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
9833
|
-
}
|
|
9834
|
-
reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
10003
|
+
this.scheduleTurnTimeout(turn, timeoutMs);
|
|
9835
10004
|
}, timeoutMs);
|
|
9836
10005
|
this.activeTurn = turn;
|
|
9837
10006
|
return turn;
|
|
9838
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
|
+
}
|
|
9839
10040
|
addTurnWatcher(turn, resolve6, reject, timeoutMs) {
|
|
9840
10041
|
const watcher = {
|
|
9841
10042
|
resolve: resolve6,
|
|
@@ -9869,6 +10070,9 @@ class CodexAppServerSession {
|
|
|
9869
10070
|
if (turn.timer) {
|
|
9870
10071
|
clearTimeout(turn.timer);
|
|
9871
10072
|
}
|
|
10073
|
+
if (turn.graceTimer) {
|
|
10074
|
+
clearTimeout(turn.graceTimer);
|
|
10075
|
+
}
|
|
9872
10076
|
if (this.activeTurn === turn) {
|
|
9873
10077
|
this.activeTurn = null;
|
|
9874
10078
|
}
|
|
@@ -10191,7 +10395,7 @@ function buildCollaborationContractPrompt(agentId) {
|
|
|
10191
10395
|
|
|
10192
10396
|
// ../runtime/src/broker-service.ts
|
|
10193
10397
|
import { spawnSync } from "child_process";
|
|
10194
|
-
import { existsSync as
|
|
10398
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10195
10399
|
import { homedir as homedir7 } from "os";
|
|
10196
10400
|
import { basename as basename3, dirname as dirname5, join as join10, resolve as resolve6 } from "path";
|
|
10197
10401
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -10230,7 +10434,7 @@ function runtimePackageDir() {
|
|
|
10230
10434
|
return resolve6(moduleDir, "..");
|
|
10231
10435
|
}
|
|
10232
10436
|
function isInstalledRuntimePackageDir(candidate) {
|
|
10233
|
-
return
|
|
10437
|
+
return existsSync7(join10(candidate, "package.json")) && existsSync7(join10(candidate, "bin", "openscout-runtime.mjs"));
|
|
10234
10438
|
}
|
|
10235
10439
|
function findGlobalRuntimeDir() {
|
|
10236
10440
|
const candidates = [
|
|
@@ -10261,7 +10465,7 @@ function findWorkspaceRuntimeDir(startDir) {
|
|
|
10261
10465
|
let current = resolve6(startDir);
|
|
10262
10466
|
while (true) {
|
|
10263
10467
|
const candidate = join10(current, "packages", "runtime");
|
|
10264
|
-
if (
|
|
10468
|
+
if (existsSync7(join10(candidate, "package.json")) && existsSync7(join10(candidate, "src"))) {
|
|
10265
10469
|
return candidate;
|
|
10266
10470
|
}
|
|
10267
10471
|
const parent = dirname5(current);
|
|
@@ -10275,18 +10479,18 @@ function resolveBunExecutable2() {
|
|
|
10275
10479
|
if (explicit && explicit.trim().length > 0) {
|
|
10276
10480
|
return explicit;
|
|
10277
10481
|
}
|
|
10278
|
-
if (basename3(process.execPath).startsWith("bun") &&
|
|
10482
|
+
if (basename3(process.execPath).startsWith("bun") && existsSync7(process.execPath)) {
|
|
10279
10483
|
return process.execPath;
|
|
10280
10484
|
}
|
|
10281
10485
|
const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
10282
10486
|
for (const entry of pathEntries) {
|
|
10283
10487
|
const candidate = join10(entry, "bun");
|
|
10284
|
-
if (
|
|
10488
|
+
if (existsSync7(candidate)) {
|
|
10285
10489
|
return candidate;
|
|
10286
10490
|
}
|
|
10287
10491
|
}
|
|
10288
10492
|
const homeBun = join10(homedir7(), ".bun", "bin", "bun");
|
|
10289
|
-
if (
|
|
10493
|
+
if (existsSync7(homeBun)) {
|
|
10290
10494
|
return homeBun;
|
|
10291
10495
|
}
|
|
10292
10496
|
return "bun";
|
|
@@ -10476,10 +10680,10 @@ function launchctlPath() {
|
|
|
10476
10680
|
return "/bin/launchctl";
|
|
10477
10681
|
}
|
|
10478
10682
|
function readLogLines(path) {
|
|
10479
|
-
if (!
|
|
10683
|
+
if (!existsSync7(path)) {
|
|
10480
10684
|
return [];
|
|
10481
10685
|
}
|
|
10482
|
-
return
|
|
10686
|
+
return readFileSync3(path, "utf8").split(`
|
|
10483
10687
|
`).map((line) => line.trim()).filter(Boolean);
|
|
10484
10688
|
}
|
|
10485
10689
|
function isPackageScriptBanner(line) {
|
|
@@ -10576,7 +10780,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
|
|
|
10576
10780
|
ensureServiceDirectories(config);
|
|
10577
10781
|
const launchctl = inspectLaunchctl(config);
|
|
10578
10782
|
const health = await fetchHealthSnapshot(config);
|
|
10579
|
-
const installed =
|
|
10783
|
+
const installed = existsSync7(config.launchAgentPath);
|
|
10580
10784
|
const lastLogLine = health.reachable ? readLastLogLine([config.stdoutLogPath, config.stderrLogPath]) : readLastLogLine([config.stderrLogPath, config.stdoutLogPath]);
|
|
10581
10785
|
return {
|
|
10582
10786
|
label: config.label,
|
|
@@ -10638,7 +10842,7 @@ async function restartBrokerService(config = resolveBrokerServiceConfig()) {
|
|
|
10638
10842
|
}
|
|
10639
10843
|
async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
|
|
10640
10844
|
await stopBrokerService(config);
|
|
10641
|
-
if (
|
|
10845
|
+
if (existsSync7(config.launchAgentPath)) {
|
|
10642
10846
|
rmSync2(config.launchAgentPath, { force: true });
|
|
10643
10847
|
}
|
|
10644
10848
|
return brokerServiceStatus(config);
|
|
@@ -10717,7 +10921,7 @@ var init_local_agent_template = __esm(() => {
|
|
|
10717
10921
|
|
|
10718
10922
|
// ../runtime/src/local-agents.ts
|
|
10719
10923
|
import { execFileSync as execFileSync3, execSync } from "child_process";
|
|
10720
|
-
import { existsSync as
|
|
10924
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
|
|
10721
10925
|
import { mkdir as mkdir6, rm as rm5, stat as stat3, writeFile as writeFile6 } from "fs/promises";
|
|
10722
10926
|
import { basename as basename4, dirname as dirname6, join as join11, resolve as resolve7 } from "path";
|
|
10723
10927
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
@@ -10730,10 +10934,10 @@ function resolveProjectsRoot(projectPath) {
|
|
|
10730
10934
|
}
|
|
10731
10935
|
try {
|
|
10732
10936
|
const supportPaths = resolveOpenScoutSupportPaths();
|
|
10733
|
-
if (!
|
|
10937
|
+
if (!existsSync8(supportPaths.settingsPath)) {
|
|
10734
10938
|
return dirname6(projectPath);
|
|
10735
10939
|
}
|
|
10736
|
-
const raw = JSON.parse(
|
|
10940
|
+
const raw = JSON.parse(readFileSync4(supportPaths.settingsPath, "utf8"));
|
|
10737
10941
|
const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
10738
10942
|
return workspaceRoot ? resolve7(workspaceRoot) : dirname6(projectPath);
|
|
10739
10943
|
} catch {
|
|
@@ -10746,8 +10950,14 @@ function resolveBrokerUrl() {
|
|
|
10746
10950
|
function nowSeconds2() {
|
|
10747
10951
|
return Math.floor(Date.now() / 1000);
|
|
10748
10952
|
}
|
|
10953
|
+
function scoutCliPath() {
|
|
10954
|
+
return join11(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
|
|
10955
|
+
}
|
|
10956
|
+
function legacyNodeBrokerRelayCommand() {
|
|
10957
|
+
return `node ${JSON.stringify(scoutCliPath())}`;
|
|
10958
|
+
}
|
|
10749
10959
|
function brokerRelayCommand() {
|
|
10750
|
-
return `
|
|
10960
|
+
return `bun ${JSON.stringify(scoutCliPath())}`;
|
|
10751
10961
|
}
|
|
10752
10962
|
function titleCaseLocalAgentName(value) {
|
|
10753
10963
|
return value.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
|
|
@@ -10760,7 +10970,7 @@ function resolveScoutSkillPath() {
|
|
|
10760
10970
|
join11(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
|
|
10761
10971
|
];
|
|
10762
10972
|
for (const path of candidatePaths) {
|
|
10763
|
-
if (
|
|
10973
|
+
if (existsSync8(path)) {
|
|
10764
10974
|
return path;
|
|
10765
10975
|
}
|
|
10766
10976
|
}
|
|
@@ -10956,7 +11166,7 @@ function buildLegacyBrokerBackedRelayPrompt(agentId, projectName, projectPath, r
|
|
|
10956
11166
|
function legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath) {
|
|
10957
11167
|
const relayHub = resolveRelayHub();
|
|
10958
11168
|
const brokerUrl = resolveBrokerUrl();
|
|
10959
|
-
const relayCommandBases = ["openscout relay", brokerRelayCommand()];
|
|
11169
|
+
const relayCommandBases = ["openscout relay", brokerRelayCommand(), legacyNodeBrokerRelayCommand()];
|
|
10960
11170
|
const projectPathCandidates = projectPath.endsWith("/") ? [projectPath, projectPath.slice(0, -1)] : [projectPath, `${projectPath}/`];
|
|
10961
11171
|
const candidates = new Set;
|
|
10962
11172
|
for (const pathCandidate of projectPathCandidates) {
|
|
@@ -10967,12 +11177,28 @@ function legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPat
|
|
|
10967
11177
|
}
|
|
10968
11178
|
return Array.from(candidates);
|
|
10969
11179
|
}
|
|
11180
|
+
function generatedLocalAgentSystemPromptCandidates(agentId, projectName, projectPath) {
|
|
11181
|
+
const baseContext = buildLocalAgentTemplateContext(agentId, projectName, projectPath);
|
|
11182
|
+
const relayCommands = [brokerRelayCommand(), legacyNodeBrokerRelayCommand()];
|
|
11183
|
+
const transportModes = [undefined, "codex_app_server", "claude_stream_json"];
|
|
11184
|
+
const candidates = new Set;
|
|
11185
|
+
for (const relayCommand of relayCommands) {
|
|
11186
|
+
const context = {
|
|
11187
|
+
...baseContext,
|
|
11188
|
+
relayCommand
|
|
11189
|
+
};
|
|
11190
|
+
for (const transport of transportModes) {
|
|
11191
|
+
candidates.add(renderLocalAgentSystemPromptTemplate(buildLocalAgentSystemPromptTemplate(), context, transport ? { transport } : {}));
|
|
11192
|
+
}
|
|
11193
|
+
}
|
|
11194
|
+
return Array.from(candidates);
|
|
11195
|
+
}
|
|
10970
11196
|
function normalizeLocalAgentSystemPrompt(agentId, projectName, projectPath, systemPrompt) {
|
|
10971
11197
|
const trimmed = systemPrompt?.trim();
|
|
10972
11198
|
if (!trimmed) {
|
|
10973
11199
|
return;
|
|
10974
11200
|
}
|
|
10975
|
-
if (legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed)) {
|
|
11201
|
+
if (legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed) || generatedLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed)) {
|
|
10976
11202
|
return;
|
|
10977
11203
|
}
|
|
10978
11204
|
return trimmed;
|
|
@@ -11052,6 +11278,128 @@ function normalizeLocalAgentCapabilities(value) {
|
|
|
11052
11278
|
function normalizeLocalAgentLaunchArgs(value) {
|
|
11053
11279
|
return Array.isArray(value) ? value.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
11054
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
|
+
}
|
|
11055
11403
|
function normalizeManagedHarness2(value, fallback) {
|
|
11056
11404
|
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
11057
11405
|
}
|
|
@@ -11067,7 +11415,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11067
11415
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
11068
11416
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
11069
11417
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
11070
|
-
launchArgs:
|
|
11418
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
11071
11419
|
};
|
|
11072
11420
|
}
|
|
11073
11421
|
const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
|
|
@@ -11076,7 +11424,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11076
11424
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
11077
11425
|
transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
|
|
11078
11426
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
|
|
11079
|
-
launchArgs:
|
|
11427
|
+
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
|
|
11080
11428
|
};
|
|
11081
11429
|
}
|
|
11082
11430
|
if (!nextProfiles[defaultHarness]) {
|
|
@@ -11084,7 +11432,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11084
11432
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
11085
11433
|
transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
|
|
11086
11434
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
|
|
11087
|
-
launchArgs:
|
|
11435
|
+
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
|
|
11088
11436
|
};
|
|
11089
11437
|
}
|
|
11090
11438
|
for (const harness of ["claude", "codex"]) {
|
|
@@ -11096,7 +11444,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
11096
11444
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
11097
11445
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
11098
11446
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
11099
|
-
launchArgs:
|
|
11447
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
11100
11448
|
};
|
|
11101
11449
|
}
|
|
11102
11450
|
return nextProfiles;
|
|
@@ -11120,14 +11468,14 @@ function recordForHarness(record, harnessOverride) {
|
|
|
11120
11468
|
tmuxSession: fallbackSessionId,
|
|
11121
11469
|
cwd: fallbackCwd,
|
|
11122
11470
|
transport: fallbackTransport,
|
|
11123
|
-
launchArgs:
|
|
11471
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
11124
11472
|
harnessProfiles: {
|
|
11125
11473
|
...normalized.harnessProfiles,
|
|
11126
11474
|
[selectedHarness]: {
|
|
11127
11475
|
cwd: fallbackCwd,
|
|
11128
11476
|
transport: fallbackTransport,
|
|
11129
11477
|
sessionId: fallbackSessionId,
|
|
11130
|
-
launchArgs:
|
|
11478
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
|
|
11131
11479
|
}
|
|
11132
11480
|
}
|
|
11133
11481
|
};
|
|
@@ -11169,7 +11517,7 @@ function normalizeLocalAgentRecord(agentId, record) {
|
|
|
11169
11517
|
harnessProfiles,
|
|
11170
11518
|
transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
|
|
11171
11519
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
11172
|
-
launchArgs: activeProfile?.launchArgs ??
|
|
11520
|
+
launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs)
|
|
11173
11521
|
};
|
|
11174
11522
|
}
|
|
11175
11523
|
function localAgentStatusFromRecord(agentId, record, source) {
|
|
@@ -11238,7 +11586,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
|
|
|
11238
11586
|
source: existing?.source && existing.source !== "inferred" ? existing.source : "manual",
|
|
11239
11587
|
startedAt: normalizedRecord.startedAt,
|
|
11240
11588
|
systemPrompt: normalizedRecord.systemPrompt,
|
|
11241
|
-
launchArgs:
|
|
11589
|
+
launchArgs: normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs),
|
|
11242
11590
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
11243
11591
|
defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
|
|
11244
11592
|
harnessProfiles: normalizedRecord.harnessProfiles,
|
|
@@ -11259,7 +11607,7 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
11259
11607
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
|
|
11260
11608
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
11261
11609
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
11262
|
-
launchArgs:
|
|
11610
|
+
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
|
|
11263
11611
|
};
|
|
11264
11612
|
}
|
|
11265
11613
|
function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
@@ -11270,7 +11618,7 @@ function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
11270
11618
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "claude_stream_json" }),
|
|
11271
11619
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
11272
11620
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
11273
|
-
launchArgs:
|
|
11621
|
+
launchArgs: normalizeLaunchArgsForHarness("claude", record.launchArgs)
|
|
11274
11622
|
};
|
|
11275
11623
|
}
|
|
11276
11624
|
function isLocalAgentRecordOnline(agentName, record) {
|
|
@@ -11304,7 +11652,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
|
|
|
11304
11652
|
return initialMessage;
|
|
11305
11653
|
}
|
|
11306
11654
|
function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
|
|
11307
|
-
const extraArgs = shellQuoteArguments(
|
|
11655
|
+
const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
|
|
11308
11656
|
if (normalizeLocalAgentHarness(record.harness) === "codex") {
|
|
11309
11657
|
return `exec bash ${JSON.stringify(workerScript ?? join11(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
|
|
11310
11658
|
}
|
|
@@ -11460,7 +11808,7 @@ async function ensureLocalAgentOnline(agentName, record) {
|
|
|
11460
11808
|
await new Promise((resolve8) => setTimeout(resolve8, 100));
|
|
11461
11809
|
}
|
|
11462
11810
|
if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
|
|
11463
|
-
const stderrTail =
|
|
11811
|
+
const stderrTail = existsSync8(stderrLogFile) ? readFileSync4(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
|
|
11464
11812
|
`).trim() : "";
|
|
11465
11813
|
throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
|
|
11466
11814
|
${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
|
|
@@ -11676,6 +12024,7 @@ async function startLocalAgent(input) {
|
|
|
11676
12024
|
const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
|
|
11677
12025
|
const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
|
|
11678
12026
|
const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
|
|
12027
|
+
const launchArgs = applyRequestedModelToLaunchArgs(effectiveHarness, [], input.model);
|
|
11679
12028
|
const existingForInstance = overrides[instance.id];
|
|
11680
12029
|
if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
|
|
11681
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.`);
|
|
@@ -11689,13 +12038,14 @@ async function startLocalAgent(input) {
|
|
|
11689
12038
|
projectConfigPath: coldProjectConfigPath,
|
|
11690
12039
|
source: "manual",
|
|
11691
12040
|
startedAt: nowSeconds2(),
|
|
12041
|
+
launchArgs,
|
|
11692
12042
|
defaultHarness: effectiveHarness,
|
|
11693
12043
|
harnessProfiles: {
|
|
11694
12044
|
[effectiveHarness]: {
|
|
11695
12045
|
cwd: effectiveCwd ?? projectRoot,
|
|
11696
12046
|
transport,
|
|
11697
12047
|
sessionId,
|
|
11698
|
-
launchArgs
|
|
12048
|
+
launchArgs
|
|
11699
12049
|
}
|
|
11700
12050
|
},
|
|
11701
12051
|
runtime: {
|
|
@@ -11716,6 +12066,9 @@ async function startLocalAgent(input) {
|
|
|
11716
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.`);
|
|
11717
12067
|
}
|
|
11718
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);
|
|
11719
12072
|
overrides[instance.id] = {
|
|
11720
12073
|
agentId: instance.id,
|
|
11721
12074
|
definitionId: requestedDefinitionId,
|
|
@@ -11726,10 +12079,16 @@ async function startLocalAgent(input) {
|
|
|
11726
12079
|
source: "manual",
|
|
11727
12080
|
startedAt: matchingOverride.startedAt ?? nowSeconds2(),
|
|
11728
12081
|
systemPrompt: matchingOverride.systemPrompt,
|
|
11729
|
-
launchArgs: matchingOverride.launchArgs,
|
|
12082
|
+
launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
11730
12083
|
capabilities: matchingOverride.capabilities,
|
|
11731
|
-
defaultHarness:
|
|
11732
|
-
harnessProfiles:
|
|
12084
|
+
defaultHarness: nextDefaultHarness,
|
|
12085
|
+
harnessProfiles: {
|
|
12086
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
12087
|
+
[nextHarness]: {
|
|
12088
|
+
...overrideHarnessProfile(matchingOverride, nextHarness),
|
|
12089
|
+
launchArgs: nextLaunchArgs
|
|
12090
|
+
}
|
|
12091
|
+
},
|
|
11733
12092
|
runtime: {
|
|
11734
12093
|
cwd: effectiveCwd ?? matchingOverride.runtime?.cwd ?? matchingProjectRoot,
|
|
11735
12094
|
harness: resolvedHarness,
|
|
@@ -11741,6 +12100,22 @@ async function startLocalAgent(input) {
|
|
|
11741
12100
|
await writeRelayAgentOverrides(overrides);
|
|
11742
12101
|
targetAgentId = instance.id;
|
|
11743
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
|
+
}
|
|
11744
12119
|
targetAgentId = matchingAgentId;
|
|
11745
12120
|
}
|
|
11746
12121
|
await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
|
|
@@ -11828,13 +12203,38 @@ async function restartAllLocalAgents(options = {}) {
|
|
|
11828
12203
|
}));
|
|
11829
12204
|
return restarted.filter((agent) => Boolean(agent));
|
|
11830
12205
|
}
|
|
12206
|
+
function readPersistedClaudeSessionId(agentName) {
|
|
12207
|
+
try {
|
|
12208
|
+
const runtimeDir = relayAgentRuntimeDirectory(agentName);
|
|
12209
|
+
const catalogPath = join11(runtimeDir, "session-catalog.json");
|
|
12210
|
+
if (existsSync8(catalogPath)) {
|
|
12211
|
+
const raw = readFileSync4(catalogPath, "utf8").trim();
|
|
12212
|
+
if (raw) {
|
|
12213
|
+
const catalog = JSON.parse(raw);
|
|
12214
|
+
if (typeof catalog.activeSessionId === "string" && catalog.activeSessionId.trim()) {
|
|
12215
|
+
return catalog.activeSessionId.trim();
|
|
12216
|
+
}
|
|
12217
|
+
}
|
|
12218
|
+
}
|
|
12219
|
+
const legacyPath = join11(runtimeDir, "claude-session-id.txt");
|
|
12220
|
+
if (existsSync8(legacyPath)) {
|
|
12221
|
+
const value = readFileSync4(legacyPath, "utf8").trim();
|
|
12222
|
+
return value || null;
|
|
12223
|
+
}
|
|
12224
|
+
return null;
|
|
12225
|
+
} catch {
|
|
12226
|
+
return null;
|
|
12227
|
+
}
|
|
12228
|
+
}
|
|
11831
12229
|
function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
11832
12230
|
const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
|
|
12231
|
+
const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
|
|
11833
12232
|
const definitionId = normalizedRecord.definitionId ?? agentId;
|
|
11834
12233
|
const displayName = titleCaseLocalAgentName(definitionId);
|
|
11835
12234
|
const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
|
|
11836
12235
|
const instance = buildRelayAgentInstance(definitionId, projectRoot);
|
|
11837
12236
|
const actorId = instance.id;
|
|
12237
|
+
const externalSessionId = normalizedRecord.transport === "claude_stream_json" ? readPersistedClaudeSessionId(definitionId) : null;
|
|
11838
12238
|
return {
|
|
11839
12239
|
actor: {
|
|
11840
12240
|
id: actorId,
|
|
@@ -11853,7 +12253,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
11853
12253
|
defaultSelector: instance.defaultSelector,
|
|
11854
12254
|
nodeQualifier: instance.nodeQualifier,
|
|
11855
12255
|
workspaceQualifier: instance.workspaceQualifier,
|
|
11856
|
-
branch: instance.branch
|
|
12256
|
+
branch: instance.branch,
|
|
12257
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
11857
12258
|
}
|
|
11858
12259
|
},
|
|
11859
12260
|
agent: {
|
|
@@ -11880,7 +12281,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
11880
12281
|
defaultSelector: instance.defaultSelector,
|
|
11881
12282
|
nodeQualifier: instance.nodeQualifier,
|
|
11882
12283
|
workspaceQualifier: instance.workspaceQualifier,
|
|
11883
|
-
branch: instance.branch
|
|
12284
|
+
branch: instance.branch,
|
|
12285
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
11884
12286
|
},
|
|
11885
12287
|
agentClass: "general",
|
|
11886
12288
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
@@ -11912,7 +12314,9 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
11912
12314
|
selector: instance.selector,
|
|
11913
12315
|
nodeQualifier: instance.nodeQualifier,
|
|
11914
12316
|
workspaceQualifier: instance.workspaceQualifier,
|
|
11915
|
-
branch: instance.branch
|
|
12317
|
+
branch: instance.branch,
|
|
12318
|
+
...configuredModel ? { model: configuredModel } : {},
|
|
12319
|
+
...externalSessionId ? { externalSessionId } : {}
|
|
11916
12320
|
}
|
|
11917
12321
|
}
|
|
11918
12322
|
};
|
|
@@ -12008,7 +12412,7 @@ var init_local_agents = __esm(async () => {
|
|
|
12008
12412
|
});
|
|
12009
12413
|
|
|
12010
12414
|
// ../runtime/src/user-config.ts
|
|
12011
|
-
import { existsSync as
|
|
12415
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
12012
12416
|
import { homedir as homedir8 } from "os";
|
|
12013
12417
|
import { dirname as dirname7, join as join12 } from "path";
|
|
12014
12418
|
function userConfigPath() {
|
|
@@ -12016,10 +12420,10 @@ function userConfigPath() {
|
|
|
12016
12420
|
}
|
|
12017
12421
|
function loadUserConfig() {
|
|
12018
12422
|
const configPath = userConfigPath();
|
|
12019
|
-
if (!
|
|
12423
|
+
if (!existsSync9(configPath))
|
|
12020
12424
|
return {};
|
|
12021
12425
|
try {
|
|
12022
|
-
return JSON.parse(
|
|
12426
|
+
return JSON.parse(readFileSync5(configPath, "utf8"));
|
|
12023
12427
|
} catch {
|
|
12024
12428
|
return {};
|
|
12025
12429
|
}
|
|
@@ -13023,8 +13427,73 @@ async function sendScoutMessage(input) {
|
|
|
13023
13427
|
}
|
|
13024
13428
|
const currentDirectory = input.currentDirectory ?? process.cwd();
|
|
13025
13429
|
const createdAtMs = input.createdAtMs ?? Date.now();
|
|
13026
|
-
const mentionResolution = await resolveMentionTargets(broker.snapshot, input.body, currentDirectory);
|
|
13027
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);
|
|
13028
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));
|
|
13029
13498
|
const validTargets = [
|
|
13030
13499
|
...new Set(availableTargets.map((target) => target.agentId).filter((target) => target !== senderId && Boolean(broker.snapshot.agents[target])))
|
|
@@ -14306,6 +14775,7 @@ function buildScoutAgentCard(binding, options = {}) {
|
|
|
14306
14775
|
const selector = binding.agent.selector?.trim() || metadataString3(binding.agent.metadata, "selector");
|
|
14307
14776
|
const defaultSelector = binding.agent.defaultSelector?.trim() || metadataString3(binding.agent.metadata, "defaultSelector");
|
|
14308
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");
|
|
14309
14779
|
const description = metadataString3(binding.agent.metadata, "description");
|
|
14310
14780
|
const version = metadataString3(binding.agent.metadata, "version");
|
|
14311
14781
|
const documentationUrl = metadataString3(binding.agent.metadata, "documentationUrl") || metadataString3(binding.agent.metadata, "docsUrl");
|
|
@@ -14363,7 +14833,8 @@ function buildScoutAgentCard(binding, options = {}) {
|
|
|
14363
14833
|
metadata: {
|
|
14364
14834
|
actorId: binding.actor.id,
|
|
14365
14835
|
endpointId: binding.endpoint.id,
|
|
14366
|
-
wakePolicy: binding.agent.wakePolicy
|
|
14836
|
+
wakePolicy: binding.agent.wakePolicy,
|
|
14837
|
+
...model ? { model } : {}
|
|
14367
14838
|
}
|
|
14368
14839
|
};
|
|
14369
14840
|
}
|
|
@@ -14397,6 +14868,7 @@ async function createScoutAgentCard(input) {
|
|
|
14397
14868
|
agentName: input.agentName,
|
|
14398
14869
|
displayName: input.displayName,
|
|
14399
14870
|
harness: input.harness,
|
|
14871
|
+
model: input.model,
|
|
14400
14872
|
currentDirectory: input.currentDirectory
|
|
14401
14873
|
});
|
|
14402
14874
|
const currentDirectory = input.currentDirectory ?? input.projectPath;
|
|
@@ -14481,7 +14953,7 @@ __export(exports_card, {
|
|
|
14481
14953
|
import { createInterface } from "readline";
|
|
14482
14954
|
function renderCardCommandHelp() {
|
|
14483
14955
|
return [
|
|
14484
|
-
"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>]",
|
|
14485
14957
|
"",
|
|
14486
14958
|
"Create a dedicated Scout agent card with a reply-ready return address.",
|
|
14487
14959
|
"",
|
|
@@ -14490,7 +14962,7 @@ function renderCardCommandHelp() {
|
|
|
14490
14962
|
"",
|
|
14491
14963
|
"Examples:",
|
|
14492
14964
|
" scout card create",
|
|
14493
|
-
" 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"
|
|
14494
14966
|
].join(`
|
|
14495
14967
|
`);
|
|
14496
14968
|
}
|
|
@@ -14554,6 +15026,7 @@ async function runCardCommand(context, args) {
|
|
|
14554
15026
|
agentName,
|
|
14555
15027
|
displayName,
|
|
14556
15028
|
harness: parseScoutHarness(options.harness),
|
|
15029
|
+
model: options.model,
|
|
14557
15030
|
currentDirectory: options.currentDirectory,
|
|
14558
15031
|
createdById: resolveScoutAgentName(options.requesterId)
|
|
14559
15032
|
});
|
|
@@ -43090,7 +43563,7 @@ var init_down = __esm(async () => {
|
|
|
43090
43563
|
// ../../apps/desktop/src/app/host/runtime-service-client.ts
|
|
43091
43564
|
import { spawn as spawn4 } from "child_process";
|
|
43092
43565
|
import { basename as basename6, dirname as dirname8, join as join14 } from "path";
|
|
43093
|
-
import { existsSync as
|
|
43566
|
+
import { existsSync as existsSync10 } from "fs";
|
|
43094
43567
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
43095
43568
|
import { homedir as homedir9 } from "os";
|
|
43096
43569
|
function tryWhich(executableName) {
|
|
@@ -43100,13 +43573,13 @@ function tryWhich(executableName) {
|
|
|
43100
43573
|
if (!dir)
|
|
43101
43574
|
continue;
|
|
43102
43575
|
const candidate = join14(dir, executableName);
|
|
43103
|
-
if (
|
|
43576
|
+
if (existsSync10(candidate)) {
|
|
43104
43577
|
return candidate;
|
|
43105
43578
|
}
|
|
43106
43579
|
if (process.platform === "win32") {
|
|
43107
43580
|
for (const ext of [".cmd", ".exe", ".bat"]) {
|
|
43108
43581
|
const withExt = candidate + ext;
|
|
43109
|
-
if (
|
|
43582
|
+
if (existsSync10(withExt))
|
|
43110
43583
|
return withExt;
|
|
43111
43584
|
}
|
|
43112
43585
|
}
|
|
@@ -43118,7 +43591,7 @@ function findNodeModulesRuntimeBin() {
|
|
|
43118
43591
|
let dir = dirname8(fileURLToPath6(import.meta.url));
|
|
43119
43592
|
for (let i = 0;i < 24; i++) {
|
|
43120
43593
|
const candidate = join14(dir, runtimeBinRel);
|
|
43121
|
-
if (
|
|
43594
|
+
if (existsSync10(candidate))
|
|
43122
43595
|
return candidate;
|
|
43123
43596
|
const parent = dirname8(dir);
|
|
43124
43597
|
if (parent === dir)
|
|
@@ -43126,7 +43599,7 @@ function findNodeModulesRuntimeBin() {
|
|
|
43126
43599
|
dir = parent;
|
|
43127
43600
|
}
|
|
43128
43601
|
const bunGlobal = join14(homedir9(), ".bun", "install", "global", "node_modules", "@openscout", "runtime", "bin", "openscout-runtime.mjs");
|
|
43129
|
-
if (
|
|
43602
|
+
if (existsSync10(bunGlobal))
|
|
43130
43603
|
return bunGlobal;
|
|
43131
43604
|
return null;
|
|
43132
43605
|
}
|
|
@@ -43134,7 +43607,7 @@ function findMonorepoOpenscoutRuntimeBin() {
|
|
|
43134
43607
|
let dir = process.cwd();
|
|
43135
43608
|
for (let i = 0;i < 24; i++) {
|
|
43136
43609
|
const candidate = join14(dir, "packages", "runtime", "bin", "openscout-runtime.mjs");
|
|
43137
|
-
if (
|
|
43610
|
+
if (existsSync10(candidate)) {
|
|
43138
43611
|
return candidate;
|
|
43139
43612
|
}
|
|
43140
43613
|
const parent = dirname8(dir);
|
|
@@ -43147,7 +43620,7 @@ function findMonorepoOpenscoutRuntimeBin() {
|
|
|
43147
43620
|
function resolveJavaScriptRuntimeExecutable() {
|
|
43148
43621
|
const explicit = process.env.OPENSCOUT_RUNTIME_NODE_BIN?.trim();
|
|
43149
43622
|
if (explicit) {
|
|
43150
|
-
if (
|
|
43623
|
+
if (existsSync10(explicit)) {
|
|
43151
43624
|
return explicit;
|
|
43152
43625
|
}
|
|
43153
43626
|
const found = tryWhich(explicit);
|
|
@@ -43173,7 +43646,7 @@ function resolveJavaScriptRuntimeExecutable() {
|
|
|
43173
43646
|
function resolveRuntimeServiceEntrypoint() {
|
|
43174
43647
|
const explicit = process.env.OPENSCOUT_RUNTIME_BIN?.trim();
|
|
43175
43648
|
if (explicit) {
|
|
43176
|
-
if (
|
|
43649
|
+
if (existsSync10(explicit)) {
|
|
43177
43650
|
return explicit;
|
|
43178
43651
|
}
|
|
43179
43652
|
const found = tryWhich(explicit);
|
|
@@ -43450,16 +43923,16 @@ var init_service3 = __esm(() => {
|
|
|
43450
43923
|
});
|
|
43451
43924
|
|
|
43452
43925
|
// ../../apps/desktop/src/shared/paths.ts
|
|
43453
|
-
import { existsSync as
|
|
43926
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
|
|
43454
43927
|
import { dirname as dirname10, join as join16, resolve as resolve10 } from "path";
|
|
43455
43928
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
43456
43929
|
function looksLikeWorkspaceRoot(candidate) {
|
|
43457
43930
|
const packageJsonPath = join16(candidate, "package.json");
|
|
43458
|
-
if (!
|
|
43931
|
+
if (!existsSync11(packageJsonPath)) {
|
|
43459
43932
|
return false;
|
|
43460
43933
|
}
|
|
43461
43934
|
try {
|
|
43462
|
-
const parsed = JSON.parse(
|
|
43935
|
+
const parsed = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
|
|
43463
43936
|
return Array.isArray(parsed.workspaces);
|
|
43464
43937
|
} catch {
|
|
43465
43938
|
return false;
|
|
@@ -43467,18 +43940,18 @@ function looksLikeWorkspaceRoot(candidate) {
|
|
|
43467
43940
|
}
|
|
43468
43941
|
function looksLikePackagedAppRoot(candidate) {
|
|
43469
43942
|
const packageJsonPath = join16(candidate, "package.json");
|
|
43470
|
-
if (!
|
|
43943
|
+
if (!existsSync11(packageJsonPath)) {
|
|
43471
43944
|
return false;
|
|
43472
43945
|
}
|
|
43473
43946
|
try {
|
|
43474
|
-
const parsed = JSON.parse(
|
|
43947
|
+
const parsed = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
|
|
43475
43948
|
return parsed.name === "@openscout/scout" || parsed.name === "@openscout/cli";
|
|
43476
43949
|
} catch {
|
|
43477
43950
|
return false;
|
|
43478
43951
|
}
|
|
43479
43952
|
}
|
|
43480
43953
|
function looksLikeSourceAppRoot(candidate) {
|
|
43481
|
-
return
|
|
43954
|
+
return existsSync11(join16(candidate, "bin", "scout.ts"));
|
|
43482
43955
|
}
|
|
43483
43956
|
function findMatchingAncestor(startDirectory, predicate) {
|
|
43484
43957
|
let current = resolve10(startDirectory);
|
|
@@ -43798,7 +44271,7 @@ var exports_env = {};
|
|
|
43798
44271
|
__export(exports_env, {
|
|
43799
44272
|
runEnvCommand: () => runEnvCommand
|
|
43800
44273
|
});
|
|
43801
|
-
import { existsSync as
|
|
44274
|
+
import { existsSync as existsSync12 } from "fs";
|
|
43802
44275
|
import { join as join17 } from "path";
|
|
43803
44276
|
function resolveCommandOnPath(command, env) {
|
|
43804
44277
|
const pathValue = env.PATH ?? "";
|
|
@@ -43808,7 +44281,7 @@ function resolveCommandOnPath(command, env) {
|
|
|
43808
44281
|
continue;
|
|
43809
44282
|
}
|
|
43810
44283
|
const candidate = join17(trimmed, command);
|
|
43811
|
-
if (
|
|
44284
|
+
if (existsSync12(candidate)) {
|
|
43812
44285
|
return candidate;
|
|
43813
44286
|
}
|
|
43814
44287
|
}
|
|
@@ -43820,7 +44293,7 @@ function resolveScoutBinPath(appRoot) {
|
|
|
43820
44293
|
join17(appRoot, "bin", "scout.mjs")
|
|
43821
44294
|
];
|
|
43822
44295
|
for (const candidate of candidates) {
|
|
43823
|
-
if (
|
|
44296
|
+
if (existsSync12(candidate)) {
|
|
43824
44297
|
return candidate;
|
|
43825
44298
|
}
|
|
43826
44299
|
}
|
|
@@ -43895,9 +44368,9 @@ var init_env = __esm(async () => {
|
|
|
43895
44368
|
|
|
43896
44369
|
// ../runtime/src/local-config.ts
|
|
43897
44370
|
import {
|
|
43898
|
-
existsSync as
|
|
44371
|
+
existsSync as existsSync13,
|
|
43899
44372
|
mkdirSync as mkdirSync4,
|
|
43900
|
-
readFileSync as
|
|
44373
|
+
readFileSync as readFileSync7,
|
|
43901
44374
|
renameSync,
|
|
43902
44375
|
writeFileSync as writeFileSync4
|
|
43903
44376
|
} from "fs";
|
|
@@ -43911,10 +44384,10 @@ function localConfigPath() {
|
|
|
43911
44384
|
}
|
|
43912
44385
|
function loadLocalConfig() {
|
|
43913
44386
|
const configPath = localConfigPath();
|
|
43914
|
-
if (!
|
|
44387
|
+
if (!existsSync13(configPath))
|
|
43915
44388
|
return { version: LOCAL_CONFIG_VERSION };
|
|
43916
44389
|
try {
|
|
43917
|
-
return validateLocalConfig(JSON.parse(
|
|
44390
|
+
return validateLocalConfig(JSON.parse(readFileSync7(configPath, "utf8")));
|
|
43918
44391
|
} catch {
|
|
43919
44392
|
return { version: LOCAL_CONFIG_VERSION };
|
|
43920
44393
|
}
|
|
@@ -43954,7 +44427,7 @@ function writeLocalConfig(config2) {
|
|
|
43954
44427
|
renameSync(tmp, configPath);
|
|
43955
44428
|
}
|
|
43956
44429
|
function localConfigExists() {
|
|
43957
|
-
return
|
|
44430
|
+
return existsSync13(localConfigPath());
|
|
43958
44431
|
}
|
|
43959
44432
|
var LOCAL_CONFIG_VERSION = 1, DEFAULT_LOCAL_CONFIG;
|
|
43960
44433
|
var init_local_config = __esm(() => {
|
|
@@ -45284,6 +45757,7 @@ function defaultScoutMcpDependencies(env) {
|
|
|
45284
45757
|
agentName,
|
|
45285
45758
|
displayName,
|
|
45286
45759
|
harness,
|
|
45760
|
+
model,
|
|
45287
45761
|
currentDirectory,
|
|
45288
45762
|
createdById
|
|
45289
45763
|
}) => createScoutAgentCard({
|
|
@@ -45291,12 +45765,21 @@ function defaultScoutMcpDependencies(env) {
|
|
|
45291
45765
|
agentName,
|
|
45292
45766
|
displayName,
|
|
45293
45767
|
harness,
|
|
45768
|
+
model,
|
|
45294
45769
|
currentDirectory,
|
|
45295
45770
|
createdById
|
|
45296
45771
|
}),
|
|
45297
|
-
sendMessage: ({
|
|
45772
|
+
sendMessage: ({
|
|
45298
45773
|
senderId,
|
|
45299
45774
|
body,
|
|
45775
|
+
targetLabel,
|
|
45776
|
+
channel,
|
|
45777
|
+
shouldSpeak,
|
|
45778
|
+
currentDirectory
|
|
45779
|
+
}) => sendScoutMessage({
|
|
45780
|
+
senderId,
|
|
45781
|
+
body,
|
|
45782
|
+
targetLabel,
|
|
45300
45783
|
channel,
|
|
45301
45784
|
shouldSpeak,
|
|
45302
45785
|
currentDirectory
|
|
@@ -45408,7 +45891,8 @@ function createScoutMcpServer(options) {
|
|
|
45408
45891
|
senderId: string2().optional(),
|
|
45409
45892
|
agentName: string2().optional(),
|
|
45410
45893
|
displayName: string2().optional(),
|
|
45411
|
-
harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional()
|
|
45894
|
+
harness: _enum2(LOCAL_AGENT_HARNESS_VALUES).optional(),
|
|
45895
|
+
model: string2().optional()
|
|
45412
45896
|
}),
|
|
45413
45897
|
outputSchema: cardCreateResultSchema,
|
|
45414
45898
|
annotations: {
|
|
@@ -45423,7 +45907,8 @@ function createScoutMcpServer(options) {
|
|
|
45423
45907
|
senderId,
|
|
45424
45908
|
agentName,
|
|
45425
45909
|
displayName,
|
|
45426
|
-
harness
|
|
45910
|
+
harness,
|
|
45911
|
+
model
|
|
45427
45912
|
}) => {
|
|
45428
45913
|
const resolvedCurrentDirectory = resolveToolCurrentDirectory(currentDirectory, options.defaultCurrentDirectory);
|
|
45429
45914
|
const resolvedSenderId = await deps.resolveSenderId(senderId, resolvedCurrentDirectory, env);
|
|
@@ -45432,6 +45917,7 @@ function createScoutMcpServer(options) {
|
|
|
45432
45917
|
agentName: agentName?.trim() || undefined,
|
|
45433
45918
|
displayName: displayName?.trim() || undefined,
|
|
45434
45919
|
harness,
|
|
45920
|
+
model: model?.trim() || undefined,
|
|
45435
45921
|
currentDirectory: resolvedCurrentDirectory,
|
|
45436
45922
|
createdById: resolvedSenderId
|
|
45437
45923
|
});
|
|
@@ -45511,11 +45997,12 @@ function createScoutMcpServer(options) {
|
|
|
45511
45997
|
});
|
|
45512
45998
|
server.registerTool("messages_send", {
|
|
45513
45999
|
title: "Send Scout Message",
|
|
45514
|
-
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.",
|
|
45515
46001
|
inputSchema: object2({
|
|
45516
46002
|
body: string2().min(1),
|
|
45517
46003
|
currentDirectory: string2().optional(),
|
|
45518
46004
|
senderId: string2().optional(),
|
|
46005
|
+
targetLabel: string2().optional(),
|
|
45519
46006
|
channel: string2().optional(),
|
|
45520
46007
|
shouldSpeak: boolean2().optional(),
|
|
45521
46008
|
mentionAgentIds: array(string2()).optional()
|
|
@@ -45531,6 +46018,7 @@ function createScoutMcpServer(options) {
|
|
|
45531
46018
|
body,
|
|
45532
46019
|
currentDirectory,
|
|
45533
46020
|
senderId,
|
|
46021
|
+
targetLabel,
|
|
45534
46022
|
channel,
|
|
45535
46023
|
shouldSpeak,
|
|
45536
46024
|
mentionAgentIds
|
|
@@ -45567,6 +46055,32 @@ function createScoutMcpServer(options) {
|
|
|
45567
46055
|
structuredContent: structuredContent2
|
|
45568
46056
|
};
|
|
45569
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
|
+
}
|
|
45570
46084
|
const result = await deps.sendMessage({
|
|
45571
46085
|
senderId: resolvedSenderId,
|
|
45572
46086
|
body,
|
|
@@ -45918,7 +46432,7 @@ var init_scout_mcp = __esm(async () => {
|
|
|
45918
46432
|
sendResultSchema = object2({
|
|
45919
46433
|
currentDirectory: string2(),
|
|
45920
46434
|
senderId: string2(),
|
|
45921
|
-
mode: _enum2(["body_mentions", "explicit_targets"]),
|
|
46435
|
+
mode: _enum2(["body_mentions", "explicit_targets", "target_label"]),
|
|
45922
46436
|
usedBroker: boolean2(),
|
|
45923
46437
|
conversationId: string2().nullable(),
|
|
45924
46438
|
messageId: string2().nullable(),
|
|
@@ -46011,7 +46525,7 @@ __export(exports_menu, {
|
|
|
46011
46525
|
parseMenuCommand: () => parseMenuCommand
|
|
46012
46526
|
});
|
|
46013
46527
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
46014
|
-
import { existsSync as
|
|
46528
|
+
import { existsSync as existsSync14 } from "fs";
|
|
46015
46529
|
import { homedir as homedir11 } from "os";
|
|
46016
46530
|
import { dirname as dirname12, join as join19, resolve as resolve12 } from "path";
|
|
46017
46531
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
@@ -46100,7 +46614,7 @@ function findRepoMenuHelper(startDirectory) {
|
|
|
46100
46614
|
let current = resolve12(startDirectory);
|
|
46101
46615
|
while (true) {
|
|
46102
46616
|
const candidate = join19(current, "apps", "macos", "bin", "openscout-menu.ts");
|
|
46103
|
-
if (
|
|
46617
|
+
if (existsSync14(candidate)) {
|
|
46104
46618
|
return candidate;
|
|
46105
46619
|
}
|
|
46106
46620
|
const parent = dirname12(current);
|
|
@@ -46110,7 +46624,7 @@ function findRepoMenuHelper(startDirectory) {
|
|
|
46110
46624
|
current = parent;
|
|
46111
46625
|
}
|
|
46112
46626
|
const sourceRelativeCandidate = fileURLToPath8(new URL("../../../../macos/bin/openscout-menu.ts", import.meta.url));
|
|
46113
|
-
return
|
|
46627
|
+
return existsSync14(sourceRelativeCandidate) ? sourceRelativeCandidate : null;
|
|
46114
46628
|
}
|
|
46115
46629
|
function resolveRepoBundlePath(helperPath) {
|
|
46116
46630
|
return resolve12(dirname12(helperPath), "..", "dist", MENU_BUNDLE_NAME);
|
|
@@ -46129,7 +46643,7 @@ function resolveInstalledMenuBundlePath(env) {
|
|
|
46129
46643
|
return indexedPath;
|
|
46130
46644
|
}
|
|
46131
46645
|
for (const candidate of COMMON_MENU_BUNDLE_PATHS) {
|
|
46132
|
-
if (
|
|
46646
|
+
if (existsSync14(candidate)) {
|
|
46133
46647
|
return candidate;
|
|
46134
46648
|
}
|
|
46135
46649
|
}
|
|
@@ -46198,7 +46712,7 @@ function runWithRepoHelper(context, helperPath, command) {
|
|
|
46198
46712
|
});
|
|
46199
46713
|
const bundlePath = resolveRepoBundlePath(helperPath);
|
|
46200
46714
|
const running = command.action === "quit" ? false : isMenuRunning(context.env);
|
|
46201
|
-
const installed =
|
|
46715
|
+
const installed = existsSync14(bundlePath) || running;
|
|
46202
46716
|
return {
|
|
46203
46717
|
action: command.action,
|
|
46204
46718
|
mode: "repo-helper",
|
|
@@ -46290,6 +46804,26 @@ function parsePeers(status) {
|
|
|
46290
46804
|
tags: peer.Tags ?? []
|
|
46291
46805
|
}));
|
|
46292
46806
|
}
|
|
46807
|
+
function parseSelf(status) {
|
|
46808
|
+
const self2 = status.Self;
|
|
46809
|
+
if (!self2) {
|
|
46810
|
+
return null;
|
|
46811
|
+
}
|
|
46812
|
+
return {
|
|
46813
|
+
id: self2.ID ?? self2.DNSName ?? self2.HostName ?? "self",
|
|
46814
|
+
name: self2.HostName ?? self2.DNSName ?? "self",
|
|
46815
|
+
dnsName: self2.DNSName,
|
|
46816
|
+
addresses: self2.TailscaleIPs ?? [],
|
|
46817
|
+
online: self2.Online ?? true,
|
|
46818
|
+
hostName: self2.HostName,
|
|
46819
|
+
os: self2.OS,
|
|
46820
|
+
tailnetName: status.CurrentTailnet?.Name,
|
|
46821
|
+
magicDnsSuffix: status.CurrentTailnet?.MagicDNSSuffix
|
|
46822
|
+
};
|
|
46823
|
+
}
|
|
46824
|
+
function isBackendRunning(status) {
|
|
46825
|
+
return (status.BackendState ?? "").trim().toLowerCase() === "running";
|
|
46826
|
+
}
|
|
46293
46827
|
async function readStatusJsonFromFile(filePath) {
|
|
46294
46828
|
const raw = await readFile9(filePath, "utf8");
|
|
46295
46829
|
return parseStatusJson(raw);
|
|
@@ -46308,11 +46842,24 @@ async function readStatusJson() {
|
|
|
46308
46842
|
}
|
|
46309
46843
|
}
|
|
46310
46844
|
async function readTailscalePeers() {
|
|
46845
|
+
const summary = await readTailscaleStatusSummary();
|
|
46846
|
+
if (!summary) {
|
|
46847
|
+
return [];
|
|
46848
|
+
}
|
|
46849
|
+
return summary.peers;
|
|
46850
|
+
}
|
|
46851
|
+
async function readTailscaleStatusSummary() {
|
|
46311
46852
|
const status = await readStatusJson();
|
|
46312
46853
|
if (!status) {
|
|
46313
|
-
return
|
|
46854
|
+
return null;
|
|
46314
46855
|
}
|
|
46315
|
-
return
|
|
46856
|
+
return {
|
|
46857
|
+
backendState: status.BackendState ?? null,
|
|
46858
|
+
running: isBackendRunning(status),
|
|
46859
|
+
health: status.Health ?? [],
|
|
46860
|
+
peers: parsePeers(status),
|
|
46861
|
+
self: parseSelf(status)
|
|
46862
|
+
};
|
|
46316
46863
|
}
|
|
46317
46864
|
var execFileAsync;
|
|
46318
46865
|
var init_tailscale = __esm(() => {
|
|
@@ -46342,9 +46889,13 @@ function readMeshEnvVars() {
|
|
|
46342
46889
|
};
|
|
46343
46890
|
}
|
|
46344
46891
|
async function readTailscaleStatus() {
|
|
46345
|
-
const
|
|
46892
|
+
const summary = await readTailscaleStatusSummary();
|
|
46893
|
+
const peers = summary?.peers ?? [];
|
|
46346
46894
|
return {
|
|
46347
|
-
available:
|
|
46895
|
+
available: summary !== null,
|
|
46896
|
+
running: summary?.running ?? false,
|
|
46897
|
+
backendState: summary?.backendState ?? null,
|
|
46898
|
+
health: summary?.health ?? [],
|
|
46348
46899
|
peers,
|
|
46349
46900
|
onlineCount: peers.filter((p) => p.online).length
|
|
46350
46901
|
};
|
|
@@ -46360,6 +46911,9 @@ function computeWarnings(health, localNode, nodes, tailscale) {
|
|
|
46360
46911
|
} else if (localNode?.advertiseScope === "mesh" && localNode.brokerUrl && isLoopbackBrokerUrl(localNode.brokerUrl)) {
|
|
46361
46912
|
warnings.push("Broker advertises mesh scope but is bound to loopback \u2014 peers cannot reach it. " + "Unset OPENSCOUT_BROKER_HOST (mesh default is 0.0.0.0) or use your Tailscale IP.");
|
|
46362
46913
|
}
|
|
46914
|
+
if (tailscale.available && !tailscale.running) {
|
|
46915
|
+
warnings.push("Tailscale is installed but currently stopped on this machine. " + "Cached peers may appear, but the broker cannot reach them until Tailscale is running.");
|
|
46916
|
+
}
|
|
46363
46917
|
const remoteNodes = Object.values(nodes).filter((n) => n.id !== localNode?.id);
|
|
46364
46918
|
if (!tailscale.available && remoteNodes.length === 0) {
|
|
46365
46919
|
warnings.push("No Tailscale peers found and no mesh seeds configured. " + "Install Tailscale or set OPENSCOUT_MESH_SEEDS.");
|
|
@@ -46525,7 +47079,7 @@ function renderMeshStatus(report) {
|
|
|
46525
47079
|
}
|
|
46526
47080
|
lines.push(` Broker: ${report.brokerUrl}`);
|
|
46527
47081
|
lines.push(` Scope: ${report.localNode?.advertiseScope === "mesh" ? "mesh-reachable" : "local-only"}`);
|
|
46528
|
-
const tsLabel = report.tailscale.available ?
|
|
47082
|
+
const tsLabel = !report.tailscale.available ? "not available" : report.tailscale.running ? `running (${report.tailscale.onlineCount} peer${report.tailscale.onlineCount === 1 ? "" : "s"} online)` : `not running (${report.tailscale.backendState ?? "unknown"})`;
|
|
46529
47083
|
lines.push(` Tailscale: ${tsLabel}`);
|
|
46530
47084
|
const allNodes = Object.values(report.nodes);
|
|
46531
47085
|
const remoteNodes = allNodes.filter((n) => n.id !== report.localNode?.id);
|
|
@@ -46559,7 +47113,8 @@ function renderMeshDoctor(report) {
|
|
|
46559
47113
|
lines.push(` ID: ${report.localNode.id}`);
|
|
46560
47114
|
lines.push(` Name: ${report.localNode.name}`);
|
|
46561
47115
|
lines.push(` Mesh: ${report.localNode.meshId}`);
|
|
46562
|
-
lines.push(`
|
|
47116
|
+
lines.push(` Control URL: ${report.brokerUrl}`);
|
|
47117
|
+
lines.push(` Announced URL: ${report.localNode.brokerUrl ?? report.brokerUrl}`);
|
|
46563
47118
|
lines.push(` Advertise scope: ${report.localNode.advertiseScope}`);
|
|
46564
47119
|
if (report.localNode.hostName) {
|
|
46565
47120
|
lines.push(` Hostname: ${report.localNode.hostName}`);
|
|
@@ -46570,7 +47125,11 @@ function renderMeshDoctor(report) {
|
|
|
46570
47125
|
lines.push("");
|
|
46571
47126
|
lines.push("Tailscale:");
|
|
46572
47127
|
if (report.tailscale.available) {
|
|
46573
|
-
|
|
47128
|
+
const state = report.tailscale.backendState ?? "unknown";
|
|
47129
|
+
lines.push(` Status: ${report.tailscale.running ? `running (${state})` : `not running (${state})`} (${report.tailscale.peers.length} peer${report.tailscale.peers.length === 1 ? "" : "s"})`);
|
|
47130
|
+
for (const detail of report.tailscale.health) {
|
|
47131
|
+
lines.push(` ! ${detail}`);
|
|
47132
|
+
}
|
|
46574
47133
|
for (const peer of report.tailscale.peers) {
|
|
46575
47134
|
const ips = peer.addresses.join(", ");
|
|
46576
47135
|
const status = peer.online ? "online" : "offline";
|
|
@@ -46598,7 +47157,7 @@ function renderMeshDoctor(report) {
|
|
|
46598
47157
|
}
|
|
46599
47158
|
}
|
|
46600
47159
|
lines.push("");
|
|
46601
|
-
lines.push("
|
|
47160
|
+
lines.push("Client environment (this shell):");
|
|
46602
47161
|
const env = report.envVars;
|
|
46603
47162
|
lines.push(` OPENSCOUT_MESH_ID: ${env.meshId ?? "(default: openscout)"}`);
|
|
46604
47163
|
lines.push(` OPENSCOUT_MESH_SEEDS: ${env.meshSeeds || "(not set)"}`);
|
|
@@ -46741,7 +47300,7 @@ var init_mesh2 = __esm(async () => {
|
|
|
46741
47300
|
});
|
|
46742
47301
|
|
|
46743
47302
|
// ../../apps/desktop/src/core/pairing/runtime/config.ts
|
|
46744
|
-
import { existsSync as
|
|
47303
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
46745
47304
|
import { homedir as homedir12 } from "os";
|
|
46746
47305
|
import path from "path";
|
|
46747
47306
|
function pairingPaths() {
|
|
@@ -46758,11 +47317,11 @@ function pairingPaths() {
|
|
|
46758
47317
|
}
|
|
46759
47318
|
function loadPairingConfig() {
|
|
46760
47319
|
const { configPath } = pairingPaths();
|
|
46761
|
-
if (!
|
|
47320
|
+
if (!existsSync15(configPath)) {
|
|
46762
47321
|
return {};
|
|
46763
47322
|
}
|
|
46764
47323
|
try {
|
|
46765
|
-
const payload = JSON.parse(
|
|
47324
|
+
const payload = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
46766
47325
|
return typeof payload === "object" && payload ? payload : {};
|
|
46767
47326
|
} catch {
|
|
46768
47327
|
return {};
|
|
@@ -48845,17 +49404,17 @@ var init_noise = __esm(() => {
|
|
|
48845
49404
|
});
|
|
48846
49405
|
|
|
48847
49406
|
// ../../apps/desktop/src/core/pairing/runtime/security/identity.ts
|
|
48848
|
-
import { existsSync as
|
|
49407
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
48849
49408
|
import { join as join20 } from "path";
|
|
48850
49409
|
import { homedir as homedir13 } from "os";
|
|
48851
49410
|
function loadOrCreateIdentity() {
|
|
48852
|
-
if (
|
|
49411
|
+
if (existsSync16(IDENTITY_FILE)) {
|
|
48853
49412
|
return loadIdentity();
|
|
48854
49413
|
}
|
|
48855
49414
|
return createAndSaveIdentity();
|
|
48856
49415
|
}
|
|
48857
49416
|
function loadIdentity() {
|
|
48858
|
-
const data = JSON.parse(
|
|
49417
|
+
const data = JSON.parse(readFileSync9(IDENTITY_FILE, "utf8"));
|
|
48859
49418
|
return {
|
|
48860
49419
|
publicKey: hexToBytes2(data.publicKey),
|
|
48861
49420
|
privateKey: hexToBytes2(data.privateKey)
|
|
@@ -48873,9 +49432,9 @@ function createAndSaveIdentity() {
|
|
|
48873
49432
|
return keyPair;
|
|
48874
49433
|
}
|
|
48875
49434
|
function loadTrustedPeers() {
|
|
48876
|
-
if (!
|
|
49435
|
+
if (!existsSync16(TRUSTED_PEERS_FILE))
|
|
48877
49436
|
return new Map;
|
|
48878
|
-
const data = JSON.parse(
|
|
49437
|
+
const data = JSON.parse(readFileSync9(TRUSTED_PEERS_FILE, "utf8"));
|
|
48879
49438
|
return new Map(data.map((p) => [p.publicKey, p]));
|
|
48880
49439
|
}
|
|
48881
49440
|
function saveTrustedPeer(peer) {
|
|
@@ -49056,14 +49615,14 @@ var init_security2 = __esm(() => {
|
|
|
49056
49615
|
});
|
|
49057
49616
|
|
|
49058
49617
|
// ../../apps/desktop/src/core/pairing/runtime/runtime-state.ts
|
|
49059
|
-
import { existsSync as
|
|
49618
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync7 } from "fs";
|
|
49060
49619
|
function readPairingRuntimeSnapshot() {
|
|
49061
49620
|
const { runtimeStatePath } = pairingPaths();
|
|
49062
|
-
if (!
|
|
49621
|
+
if (!existsSync17(runtimeStatePath)) {
|
|
49063
49622
|
return null;
|
|
49064
49623
|
}
|
|
49065
49624
|
try {
|
|
49066
|
-
const parsed = JSON.parse(
|
|
49625
|
+
const parsed = JSON.parse(readFileSync10(runtimeStatePath, "utf8"));
|
|
49067
49626
|
return parsed?.version === 1 ? parsed : null;
|
|
49068
49627
|
} catch {
|
|
49069
49628
|
return null;
|
|
@@ -49181,15 +49740,15 @@ var init_bridge = __esm(() => {
|
|
|
49181
49740
|
});
|
|
49182
49741
|
|
|
49183
49742
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/config.ts
|
|
49184
|
-
import { existsSync as
|
|
49743
|
+
import { existsSync as existsSync18, readFileSync as readFileSync11 } from "fs";
|
|
49185
49744
|
import { join as join22 } from "path";
|
|
49186
49745
|
import { homedir as homedir15 } from "os";
|
|
49187
49746
|
function loadConfigFile() {
|
|
49188
|
-
if (!
|
|
49747
|
+
if (!existsSync18(CONFIG_FILE)) {
|
|
49189
49748
|
return {};
|
|
49190
49749
|
}
|
|
49191
49750
|
try {
|
|
49192
|
-
const raw =
|
|
49751
|
+
const raw = readFileSync11(CONFIG_FILE, "utf8");
|
|
49193
49752
|
const parsed = JSON.parse(raw);
|
|
49194
49753
|
return parsed;
|
|
49195
49754
|
} catch (err) {
|
|
@@ -49250,9 +49809,74 @@ var init_config3 = __esm(() => {
|
|
|
49250
49809
|
CONFIG_FILE = join22(CONFIG_DIR, "config.json");
|
|
49251
49810
|
});
|
|
49252
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
|
+
|
|
49253
49873
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
|
|
49254
49874
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
49255
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
|
+
}
|
|
49256
49880
|
function isAllowedPath(filePath) {
|
|
49257
49881
|
if (!isAbsolute2(filePath))
|
|
49258
49882
|
return false;
|
|
@@ -49263,7 +49887,7 @@ function isAllowedPath(filePath) {
|
|
|
49263
49887
|
return ALLOWED_ROOTS.some((root) => filePath.startsWith(root));
|
|
49264
49888
|
}
|
|
49265
49889
|
function startFileServer(options) {
|
|
49266
|
-
const { port } = options;
|
|
49890
|
+
const { port, bridge } = options;
|
|
49267
49891
|
let server = null;
|
|
49268
49892
|
function start() {
|
|
49269
49893
|
try {
|
|
@@ -49271,7 +49895,7 @@ function startFileServer(options) {
|
|
|
49271
49895
|
port,
|
|
49272
49896
|
fetch(req) {
|
|
49273
49897
|
try {
|
|
49274
|
-
return route(new URL(req.url));
|
|
49898
|
+
return route(req, new URL(req.url), bridge);
|
|
49275
49899
|
} catch (err) {
|
|
49276
49900
|
console.error(`[fileserver] ${req.url} \u2192`, err.message);
|
|
49277
49901
|
return new Response("Internal error", { status: 500 });
|
|
@@ -49294,12 +49918,19 @@ function startFileServer(options) {
|
|
|
49294
49918
|
start();
|
|
49295
49919
|
return { port, stop, restart };
|
|
49296
49920
|
}
|
|
49297
|
-
function route(url2) {
|
|
49921
|
+
function route(req, url2, bridge) {
|
|
49298
49922
|
const path2 = url2.pathname;
|
|
49299
|
-
if (path2 === "/file")
|
|
49923
|
+
if (path2 === "/file") {
|
|
49924
|
+
if (!isTrustedLoopbackHostname(url2.hostname)) {
|
|
49925
|
+
return new Response("Forbidden", { status: 403 });
|
|
49926
|
+
}
|
|
49300
49927
|
return serveFile(url2);
|
|
49928
|
+
}
|
|
49301
49929
|
if (path2 === "/health")
|
|
49302
49930
|
return Response.json({ ok: true });
|
|
49931
|
+
if (path2.startsWith("/handoff/")) {
|
|
49932
|
+
return serveHandoffPage(req, url2, bridge);
|
|
49933
|
+
}
|
|
49303
49934
|
return new Response("Pairing file server", { status: 200 });
|
|
49304
49935
|
}
|
|
49305
49936
|
function serveFile(url2) {
|
|
@@ -49313,9 +49944,525 @@ function serveFile(url2) {
|
|
|
49313
49944
|
return new Response("Not found", { status: 404 });
|
|
49314
49945
|
return new Response(file2);
|
|
49315
49946
|
}
|
|
49316
|
-
|
|
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;
|
|
49317
50462
|
var init_fileserver = __esm(() => {
|
|
50463
|
+
init_web_handoff();
|
|
49318
50464
|
ALLOWED_ROOTS = [homedir16(), "/tmp"];
|
|
50465
|
+
LOOPBACK_IPV4_HOST_PATTERN = /^127(?:\.\d{1,3}){3}$/;
|
|
49319
50466
|
});
|
|
49320
50467
|
|
|
49321
50468
|
// ../../apps/desktop/src/server/db-queries.ts
|
|
@@ -50102,7 +51249,7 @@ async function deriveNewAgentName(projectName, branch, harness) {
|
|
|
50102
51249
|
async function createGitWorktree(projectRoot, agentName) {
|
|
50103
51250
|
const { execSync: execSync2 } = await import("child_process");
|
|
50104
51251
|
const { join: join24 } = await import("path");
|
|
50105
|
-
const { mkdirSync: mkdirSync9, existsSync:
|
|
51252
|
+
const { mkdirSync: mkdirSync9, existsSync: existsSync19 } = await import("fs");
|
|
50106
51253
|
try {
|
|
50107
51254
|
execSync2("git rev-parse --git-dir", { cwd: projectRoot, stdio: "pipe" });
|
|
50108
51255
|
} catch {
|
|
@@ -50111,7 +51258,7 @@ async function createGitWorktree(projectRoot, agentName) {
|
|
|
50111
51258
|
const branchName = `scout/${agentName}`;
|
|
50112
51259
|
const worktreeDir = join24(projectRoot, ".scout-worktrees");
|
|
50113
51260
|
const worktreePath = join24(worktreeDir, agentName);
|
|
50114
|
-
if (
|
|
51261
|
+
if (existsSync19(worktreePath)) {
|
|
50115
51262
|
return { path: worktreePath, branch: branchName };
|
|
50116
51263
|
}
|
|
50117
51264
|
mkdirSync9(worktreeDir, { recursive: true });
|
|
@@ -50154,7 +51301,7 @@ var init_service5 = __esm(async () => {
|
|
|
50154
51301
|
});
|
|
50155
51302
|
|
|
50156
51303
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/server.ts
|
|
50157
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
51304
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync12, realpathSync, statSync as statSync3 } from "fs";
|
|
50158
51305
|
import { execSync as execSync2 } from "child_process";
|
|
50159
51306
|
import { basename as basename9, isAbsolute as isAbsolute3, join as join24, relative as relative2 } from "path";
|
|
50160
51307
|
import { homedir as homedir18 } from "os";
|
|
@@ -50167,6 +51314,20 @@ function readSyncStatus(bridge, sessionId) {
|
|
|
50167
51314
|
oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
|
|
50168
51315
|
};
|
|
50169
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
|
+
}
|
|
50170
51331
|
function sessionRegistryErrorResponse(reqId, error48) {
|
|
50171
51332
|
if (!isSessionRegistryError(error48)) {
|
|
50172
51333
|
return null;
|
|
@@ -50236,23 +51397,36 @@ async function handleRPCInner(bridge, req, deviceId) {
|
|
|
50236
51397
|
return { id: req.id, result: { ok: true } };
|
|
50237
51398
|
}
|
|
50238
51399
|
case "sync/replay": {
|
|
50239
|
-
const p = req.params;
|
|
50240
|
-
if (
|
|
50241
|
-
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: [] } };
|
|
50242
51407
|
}
|
|
50243
|
-
const events2 = replaySyncEvents(bridge,
|
|
51408
|
+
const events2 = replaySyncEvents(bridge, sessionId, p.lastSeq);
|
|
50244
51409
|
return { id: req.id, result: { events: events2 } };
|
|
50245
51410
|
}
|
|
50246
51411
|
case "sync/status": {
|
|
50247
51412
|
const p = req.params ?? {};
|
|
50248
|
-
|
|
50249
|
-
|
|
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
|
+
};
|
|
50250
51424
|
}
|
|
50251
51425
|
return {
|
|
50252
51426
|
id: req.id,
|
|
50253
51427
|
result: {
|
|
50254
|
-
...readSyncStatus(bridge,
|
|
50255
|
-
sessionCount
|
|
51428
|
+
...readSyncStatus(bridge, sessionId),
|
|
51429
|
+
sessionCount
|
|
50256
51430
|
}
|
|
50257
51431
|
};
|
|
50258
51432
|
}
|
|
@@ -50493,7 +51667,7 @@ async function handleRPCInner(bridge, req, deviceId) {
|
|
|
50493
51667
|
return { id: req.id, error: { code: -32000, message: "Only .jsonl files can be read" } };
|
|
50494
51668
|
}
|
|
50495
51669
|
try {
|
|
50496
|
-
const content =
|
|
51670
|
+
const content = readFileSync12(p.path, "utf-8");
|
|
50497
51671
|
const lines = content.split(`
|
|
50498
51672
|
`).filter((l) => l.trim().length > 0);
|
|
50499
51673
|
const trimmed = lines.length > 500 ? lines.slice(-500) : lines;
|
|
@@ -54747,7 +55921,7 @@ var init_thread_events2 = __esm(() => {
|
|
|
54747
55921
|
|
|
54748
55922
|
// ../runtime/src/mobile-push.ts
|
|
54749
55923
|
import { Database as Database2 } from "bun:sqlite";
|
|
54750
|
-
import { mkdirSync as mkdirSync9, readFileSync as
|
|
55924
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync13 } from "fs";
|
|
54751
55925
|
import { connect as connectHttp2 } from "http2";
|
|
54752
55926
|
import { createPrivateKey, sign as signWithKey } from "crypto";
|
|
54753
55927
|
import { dirname as dirname13, join as join25 } from "path";
|
|
@@ -54918,7 +56092,7 @@ function loadApnsCredentials() {
|
|
|
54918
56092
|
privateKeyPem = Buffer.from(inlineBase64, "base64").toString("utf8");
|
|
54919
56093
|
}
|
|
54920
56094
|
if (!privateKeyPem && path2) {
|
|
54921
|
-
privateKeyPem =
|
|
56095
|
+
privateKeyPem = readFileSync13(path2, "utf8");
|
|
54922
56096
|
}
|
|
54923
56097
|
if (!teamId || !keyId || !privateKeyPem) {
|
|
54924
56098
|
return null;
|
|
@@ -55142,7 +56316,7 @@ var init_src2 = __esm(async () => {
|
|
|
55142
56316
|
});
|
|
55143
56317
|
|
|
55144
56318
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/router.ts
|
|
55145
|
-
import { readFileSync as
|
|
56319
|
+
import { readFileSync as readFileSync14, readdirSync as readdirSync3, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
55146
56320
|
import { execSync as execSync3 } from "child_process";
|
|
55147
56321
|
import { basename as basename10, isAbsolute as isAbsolute4, join as join26, relative as relative3 } from "path";
|
|
55148
56322
|
import { homedir as homedir19 } from "os";
|
|
@@ -55432,6 +56606,20 @@ function readSyncStatus2(bridge, sessionId) {
|
|
|
55432
56606
|
oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
|
|
55433
56607
|
};
|
|
55434
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
|
+
}
|
|
55435
56623
|
var t, logged, procedure, MARKER_FILES2, sessionRouter, mobileRouter, workspaceRouter, historyRouter, promptRouter, syncRouter, bridgeRouter;
|
|
55436
56624
|
var init_router = __esm(async () => {
|
|
55437
56625
|
init_dist3();
|
|
@@ -55440,6 +56628,7 @@ var init_router = __esm(async () => {
|
|
|
55440
56628
|
init_log();
|
|
55441
56629
|
init_config3();
|
|
55442
56630
|
init_db_queries();
|
|
56631
|
+
init_web_handoff();
|
|
55443
56632
|
await __promiseAll([
|
|
55444
56633
|
init_service5(),
|
|
55445
56634
|
init_src2(),
|
|
@@ -55647,6 +56836,64 @@ var init_router = __esm(async () => {
|
|
|
55647
56836
|
limit: typeof input.limit === "number" ? input.limit : null
|
|
55648
56837
|
}, resolveMobileCurrentDirectory2());
|
|
55649
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
|
+
}),
|
|
55650
56897
|
createSession: procedure.input(exports_external.object({
|
|
55651
56898
|
workspaceId: exports_external.string(),
|
|
55652
56899
|
harness: exports_external.string().optional(),
|
|
@@ -55822,7 +57069,7 @@ var init_router = __esm(async () => {
|
|
|
55822
57069
|
});
|
|
55823
57070
|
}
|
|
55824
57071
|
try {
|
|
55825
|
-
const content =
|
|
57072
|
+
const content = readFileSync14(input.path, "utf-8");
|
|
55826
57073
|
const lines = content.split(`
|
|
55827
57074
|
`).filter((l) => l.trim().length > 0);
|
|
55828
57075
|
const trimmed = lines.length > 500 ? lines.slice(-500) : lines;
|
|
@@ -55852,14 +57099,27 @@ var init_router = __esm(async () => {
|
|
|
55852
57099
|
})
|
|
55853
57100
|
});
|
|
55854
57101
|
syncRouter = t.router({
|
|
55855
|
-
replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string() })).query(({ input, ctx }) => {
|
|
55856
|
-
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);
|
|
55857
57108
|
return { events: events2 };
|
|
55858
57109
|
}),
|
|
55859
|
-
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
|
+
}
|
|
55860
57120
|
return {
|
|
55861
|
-
...readSyncStatus2(ctx.bridge,
|
|
55862
|
-
sessionCount
|
|
57121
|
+
...readSyncStatus2(ctx.bridge, sessionId),
|
|
57122
|
+
sessionCount
|
|
55863
57123
|
};
|
|
55864
57124
|
})
|
|
55865
57125
|
});
|
|
@@ -60468,7 +61728,7 @@ async function startPairingRuntime(options) {
|
|
|
60468
61728
|
secure: config2.secure,
|
|
60469
61729
|
identity: config2.secure ? identity2 : undefined
|
|
60470
61730
|
});
|
|
60471
|
-
const fileServer = startFileServer({ port: config2.port + 2 });
|
|
61731
|
+
const fileServer = startFileServer({ port: config2.port + 2, bridge });
|
|
60472
61732
|
const relayUrl = options?.relayUrl?.trim() || config2.relay || null;
|
|
60473
61733
|
if (!relayUrl) {
|
|
60474
61734
|
fileServer.stop();
|
|
@@ -60775,11 +62035,11 @@ var BRIDGE_ABSENCE_GRACE_MS = 30000, ROOM_IDLE_TIMEOUT_MS = 60000;
|
|
|
60775
62035
|
|
|
60776
62036
|
// ../../apps/desktop/src/core/pairing/runtime/relay-runtime.ts
|
|
60777
62037
|
import { execSync as execSync4 } from "child_process";
|
|
60778
|
-
import { existsSync as
|
|
62038
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync11, readdirSync as readdirSync4 } from "fs";
|
|
60779
62039
|
import { homedir as homedir22 } from "os";
|
|
60780
62040
|
import { join as join27 } from "path";
|
|
60781
62041
|
function findStoredCerts() {
|
|
60782
|
-
if (!
|
|
62042
|
+
if (!existsSync19(PAIRING_DIR2)) {
|
|
60783
62043
|
return null;
|
|
60784
62044
|
}
|
|
60785
62045
|
try {
|
|
@@ -60800,19 +62060,58 @@ function findStoredCerts() {
|
|
|
60800
62060
|
return null;
|
|
60801
62061
|
}
|
|
60802
62062
|
}
|
|
60803
|
-
function
|
|
62063
|
+
function readTailscaleStatus2() {
|
|
60804
62064
|
try {
|
|
60805
62065
|
const output = execSync4("tailscale status --self=true --peers=false --json", {
|
|
60806
62066
|
stdio: ["pipe", "pipe", "pipe"],
|
|
60807
62067
|
timeout: 5000
|
|
60808
62068
|
}).toString();
|
|
60809
62069
|
const data = JSON.parse(output);
|
|
60810
|
-
const dnsName = data
|
|
60811
|
-
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
|
+
};
|
|
60812
62077
|
} catch {
|
|
60813
62078
|
return null;
|
|
60814
62079
|
}
|
|
60815
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
|
+
}
|
|
60816
62115
|
function generateTailscaleCerts(hostname5) {
|
|
60817
62116
|
mkdirSync11(PAIRING_DIR2, { recursive: true });
|
|
60818
62117
|
const certPath = join27(PAIRING_DIR2, `${hostname5}.crt`);
|
|
@@ -60858,28 +62157,7 @@ function resolveTls(hostname5) {
|
|
|
60858
62157
|
return generateTailscaleCerts(hostname5);
|
|
60859
62158
|
}
|
|
60860
62159
|
function resolveRelayEndpoint(port) {
|
|
60861
|
-
|
|
60862
|
-
const tls = resolveTls(hostname5);
|
|
60863
|
-
if (hostname5 && tls) {
|
|
60864
|
-
return {
|
|
60865
|
-
relayUrl: `wss://${hostname5}:${port}`,
|
|
60866
|
-
options: { tls }
|
|
60867
|
-
};
|
|
60868
|
-
}
|
|
60869
|
-
if (hostname5) {
|
|
60870
|
-
pairingLog.warn("relay", "tailscale hostname detected without TLS; falling back to insecure websocket relay", {
|
|
60871
|
-
hostname: hostname5,
|
|
60872
|
-
port
|
|
60873
|
-
});
|
|
60874
|
-
return {
|
|
60875
|
-
relayUrl: `ws://${hostname5}:${port}`,
|
|
60876
|
-
options: {}
|
|
60877
|
-
};
|
|
60878
|
-
}
|
|
60879
|
-
return {
|
|
60880
|
-
relayUrl: `ws://127.0.0.1:${port}`,
|
|
60881
|
-
options: {}
|
|
60882
|
-
};
|
|
62160
|
+
return resolveRelayEndpointForTailscaleStatus(port, readTailscaleStatus2());
|
|
60883
62161
|
}
|
|
60884
62162
|
function startManagedRelay(port = 7889) {
|
|
60885
62163
|
const endpoint = resolveRelayEndpoint(port);
|
|
@@ -61831,11 +63109,13 @@ __export(exports_server, {
|
|
|
61831
63109
|
runServerCommand: () => runServerCommand,
|
|
61832
63110
|
resolveScoutWebServerEntry: () => resolveScoutWebServerEntry,
|
|
61833
63111
|
resolveScoutControlPlaneWebServerEntry: () => resolveScoutControlPlaneWebServerEntry,
|
|
63112
|
+
resolveBunExecutable: () => resolveBunExecutable3,
|
|
61834
63113
|
renderServerCommandHelp: () => renderServerCommandHelp,
|
|
61835
63114
|
normalizeServerOpenPath: () => normalizeServerOpenPath
|
|
61836
63115
|
});
|
|
61837
63116
|
import { spawn as spawn5 } from "child_process";
|
|
61838
|
-
import { existsSync as
|
|
63117
|
+
import { accessSync as accessSync2, constants as constants4, existsSync as existsSync20 } from "fs";
|
|
63118
|
+
import { homedir as homedir23 } from "os";
|
|
61839
63119
|
import { dirname as dirname14, join as join28, resolve as resolve14 } from "path";
|
|
61840
63120
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
61841
63121
|
function renderServerCommandHelp() {
|
|
@@ -61845,6 +63125,7 @@ function renderServerCommandHelp() {
|
|
|
61845
63125
|
"Usage:",
|
|
61846
63126
|
" scout server start [options]",
|
|
61847
63127
|
" scout server open [options]",
|
|
63128
|
+
" scout server control-plane open [options] # legacy alias",
|
|
61848
63129
|
"",
|
|
61849
63130
|
"Subcommands:",
|
|
61850
63131
|
" start Start the Scout web UI server.",
|
|
@@ -61868,14 +63149,18 @@ function resolveScoutWebServerEntry() {
|
|
|
61868
63149
|
function resolveScoutControlPlaneWebServerEntry() {
|
|
61869
63150
|
const mainDir = dirname14(fileURLToPath9(import.meta.url));
|
|
61870
63151
|
const bundled = join28(mainDir, "scout-control-plane-web.mjs");
|
|
61871
|
-
if (
|
|
63152
|
+
if (existsSync20(bundled)) {
|
|
61872
63153
|
return bundled;
|
|
61873
63154
|
}
|
|
61874
|
-
const source = fileURLToPath9(new URL("
|
|
61875
|
-
if (
|
|
63155
|
+
const source = fileURLToPath9(new URL("../../../../../packages/web/server/index.ts", import.meta.url).href);
|
|
63156
|
+
if (existsSync20(source)) {
|
|
61876
63157
|
return source;
|
|
61877
63158
|
}
|
|
61878
|
-
|
|
63159
|
+
const legacySource = fileURLToPath9(new URL("../../server/control-plane-index.ts", import.meta.url).href);
|
|
63160
|
+
if (existsSync20(legacySource)) {
|
|
63161
|
+
return legacySource;
|
|
63162
|
+
}
|
|
63163
|
+
throw new ScoutCliError("Could not find Scout web server entry. Rebuild @openscout/scout or run from the OpenScout repository.");
|
|
61879
63164
|
}
|
|
61880
63165
|
function parseServerFlags(args) {
|
|
61881
63166
|
const env = {};
|
|
@@ -61949,7 +63234,43 @@ function resolveBundledStaticClientRoot(entry, _mode) {
|
|
|
61949
63234
|
const entryDir = dirname14(entry);
|
|
61950
63235
|
const clientDirectory = join28(entryDir, "client");
|
|
61951
63236
|
const indexPath = join28(clientDirectory, "index.html");
|
|
61952
|
-
return
|
|
63237
|
+
return existsSync20(indexPath) ? clientDirectory : null;
|
|
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";
|
|
61953
63274
|
}
|
|
61954
63275
|
function buildMergedServerEnv(entry, mode, flagEnv) {
|
|
61955
63276
|
const bundledStaticClientRoot = resolveBundledStaticClientRoot(entry, mode);
|
|
@@ -61975,7 +63296,7 @@ function parseServerSelection(args) {
|
|
|
61975
63296
|
action: "start",
|
|
61976
63297
|
flagArgs: args.slice(1),
|
|
61977
63298
|
entry: resolveScoutControlPlaneWebServerEntry(),
|
|
61978
|
-
mode: "
|
|
63299
|
+
mode: "openscout-web"
|
|
61979
63300
|
};
|
|
61980
63301
|
}
|
|
61981
63302
|
if (args[0] === "open") {
|
|
@@ -61983,7 +63304,7 @@ function parseServerSelection(args) {
|
|
|
61983
63304
|
action: "open",
|
|
61984
63305
|
flagArgs: args.slice(1),
|
|
61985
63306
|
entry: resolveScoutControlPlaneWebServerEntry(),
|
|
61986
|
-
mode: "
|
|
63307
|
+
mode: "openscout-web"
|
|
61987
63308
|
};
|
|
61988
63309
|
}
|
|
61989
63310
|
if (args[0] === "control-plane") {
|
|
@@ -61992,7 +63313,7 @@ function parseServerSelection(args) {
|
|
|
61992
63313
|
action: sub,
|
|
61993
63314
|
flagArgs: args.slice(2),
|
|
61994
63315
|
entry: resolveScoutControlPlaneWebServerEntry(),
|
|
61995
|
-
mode: "
|
|
63316
|
+
mode: "openscout-web"
|
|
61996
63317
|
};
|
|
61997
63318
|
}
|
|
61998
63319
|
throw new ScoutCliError(`unknown subcommand: ${args[0]} (try: scout server open)`);
|
|
@@ -62028,7 +63349,7 @@ function resolveExpectedCurrentDirectory(env) {
|
|
|
62028
63349
|
return resolve14(env.OPENSCOUT_SETUP_CWD?.trim() || process.cwd());
|
|
62029
63350
|
}
|
|
62030
63351
|
function renderModeLabel(mode) {
|
|
62031
|
-
return
|
|
63352
|
+
return "Scout web";
|
|
62032
63353
|
}
|
|
62033
63354
|
function renderSurfaceLabel(surface) {
|
|
62034
63355
|
switch (surface) {
|
|
@@ -62036,14 +63357,13 @@ function renderSurfaceLabel(surface) {
|
|
|
62036
63357
|
return "control-plane";
|
|
62037
63358
|
case "openscout-web":
|
|
62038
63359
|
return "@openscout/web";
|
|
62039
|
-
case "full":
|
|
62040
|
-
default:
|
|
62041
|
-
return "full";
|
|
62042
63360
|
}
|
|
62043
63361
|
}
|
|
62044
63362
|
function renderServerOpenResult(result) {
|
|
62045
|
-
|
|
62046
|
-
|
|
63363
|
+
return `Opened Scout web at ${result.url}${result.reusedExistingServer ? "" : " (started server)"}`;
|
|
63364
|
+
}
|
|
63365
|
+
function isCurrentScoutWebSurface(surface) {
|
|
63366
|
+
return surface === "openscout-web" || surface === "control-plane";
|
|
62047
63367
|
}
|
|
62048
63368
|
function healthUrlForPort(port) {
|
|
62049
63369
|
return new URL(`/api/health`, `http://127.0.0.1:${port}`);
|
|
@@ -62071,7 +63391,7 @@ async function probeScoutServer(port) {
|
|
|
62071
63391
|
statusCode: response.status
|
|
62072
63392
|
};
|
|
62073
63393
|
}
|
|
62074
|
-
if (body.ok === true && (body.surface === "
|
|
63394
|
+
if (body.ok === true && (body.surface === "control-plane" || body.surface === "openscout-web") && typeof body.currentDirectory === "string") {
|
|
62075
63395
|
return {
|
|
62076
63396
|
status: "healthy",
|
|
62077
63397
|
health: {
|
|
@@ -62124,8 +63444,9 @@ async function openBrowser(url2) {
|
|
|
62124
63444
|
});
|
|
62125
63445
|
}
|
|
62126
63446
|
async function spawnDetachedServer(entry, env) {
|
|
63447
|
+
const bunExecutable = resolveBunExecutable3(env);
|
|
62127
63448
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
62128
|
-
const child = spawn5(
|
|
63449
|
+
const child = spawn5(bunExecutable, ["run", entry], {
|
|
62129
63450
|
detached: true,
|
|
62130
63451
|
stdio: "ignore",
|
|
62131
63452
|
env,
|
|
@@ -62150,7 +63471,7 @@ async function waitForScoutServer(port, mode, expectedCurrentDirectory) {
|
|
|
62150
63471
|
const probe = await probeScoutServer(port);
|
|
62151
63472
|
if (probe.status === "healthy") {
|
|
62152
63473
|
const actualCurrentDirectory = resolve14(probe.health.currentDirectory);
|
|
62153
|
-
if (probe.health.surface
|
|
63474
|
+
if (!isCurrentScoutWebSurface(probe.health.surface)) {
|
|
62154
63475
|
throw new ScoutCliError(`port ${port} is serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(mode)}.`);
|
|
62155
63476
|
}
|
|
62156
63477
|
if (actualCurrentDirectory !== expectedCurrentDirectory) {
|
|
@@ -62172,7 +63493,7 @@ async function openScoutServer(options) {
|
|
|
62172
63493
|
const probe = await probeScoutServer(port);
|
|
62173
63494
|
if (probe.status === "healthy") {
|
|
62174
63495
|
const actualCurrentDirectory = resolve14(probe.health.currentDirectory);
|
|
62175
|
-
if (probe.health.surface
|
|
63496
|
+
if (!isCurrentScoutWebSurface(probe.health.surface)) {
|
|
62176
63497
|
throw new ScoutCliError(`port ${port} is already serving Scout ${renderSurfaceLabel(probe.health.surface)}, not ${renderModeLabel(options.mode)}.`);
|
|
62177
63498
|
}
|
|
62178
63499
|
if (actualCurrentDirectory !== expectedCurrentDirectory) {
|
|
@@ -62217,8 +63538,9 @@ async function runServerCommand(context, args) {
|
|
|
62217
63538
|
context.output.writeValue(result, renderServerOpenResult);
|
|
62218
63539
|
return;
|
|
62219
63540
|
}
|
|
63541
|
+
const bunExecutable = resolveBunExecutable3(mergedEnv);
|
|
62220
63542
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
62221
|
-
const child = spawn5(
|
|
63543
|
+
const child = spawn5(bunExecutable, ["run", selection.entry], {
|
|
62222
63544
|
stdio: "inherit",
|
|
62223
63545
|
env: mergedEnv
|
|
62224
63546
|
});
|
|
@@ -62370,7 +63692,7 @@ import { EventEmitter as EventEmitter2 } from "events";
|
|
|
62370
63692
|
import { resolve as resolve15, dirname as dirname15 } from "path";
|
|
62371
63693
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
62372
63694
|
import { resolve as resolve22, isAbsolute as isAbsolute5, parse as parse7 } from "path";
|
|
62373
|
-
import { existsSync as
|
|
63695
|
+
import { existsSync as existsSync21 } from "fs";
|
|
62374
63696
|
import { basename as basename11, join as join29 } from "path";
|
|
62375
63697
|
import os from "os";
|
|
62376
63698
|
import path2 from "path";
|
|
@@ -71416,7 +72738,7 @@ var __defProp3, __returnValue2 = (v) => v, __export2 = (target, all) => {
|
|
|
71416
72738
|
configurable: true,
|
|
71417
72739
|
set: __exportSetter2.bind(all, name)
|
|
71418
72740
|
});
|
|
71419
|
-
}, 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) => {
|
|
71420
72742
|
const chunk = typeof input === "object" && "__isChunk" in input ? input : {
|
|
71421
72743
|
__isChunk: true,
|
|
71422
72744
|
text: String(input)
|
|
@@ -73076,7 +74398,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
|
|
|
73076
74398
|
Wrap2[Wrap2["WrapReverse"] = 2] = "WrapReverse";
|
|
73077
74399
|
return Wrap2;
|
|
73078
74400
|
}({});
|
|
73079
|
-
|
|
74401
|
+
constants5 = {
|
|
73080
74402
|
ALIGN_AUTO: Align.Auto,
|
|
73081
74403
|
ALIGN_FLEX_START: Align.FlexStart,
|
|
73082
74404
|
ALIGN_CENTER: Align.Center,
|
|
@@ -73150,7 +74472,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
|
|
|
73150
74472
|
WRAP_WRAP: Wrap.Wrap,
|
|
73151
74473
|
WRAP_WRAP_REVERSE: Wrap.WrapReverse
|
|
73152
74474
|
};
|
|
73153
|
-
YGEnums_default =
|
|
74475
|
+
YGEnums_default = constants5;
|
|
73154
74476
|
Yoga = wrapAssembly(await yoga_wasm_base64_esm_default());
|
|
73155
74477
|
src_default = Yoga;
|
|
73156
74478
|
VALID_BORDER_STYLES = ["single", "double", "rounded", "heavy"];
|
|
@@ -76584,7 +77906,7 @@ var init_index_vy1rm1x3 = __esm(async () => {
|
|
|
76584
77906
|
worker_path = this.options.workerPath;
|
|
76585
77907
|
} else {
|
|
76586
77908
|
worker_path = new URL("./parser.worker.js", import.meta.url).href;
|
|
76587
|
-
if (!
|
|
77909
|
+
if (!existsSync21(resolve22(import.meta.dirname, "parser.worker.js"))) {
|
|
76588
77910
|
worker_path = new URL("./parser.worker.ts", import.meta.url).href;
|
|
76589
77911
|
}
|
|
76590
77912
|
}
|
|
@@ -123658,7 +124980,7 @@ var exports_up = {};
|
|
|
123658
124980
|
__export(exports_up, {
|
|
123659
124981
|
runUpCommand: () => runUpCommand
|
|
123660
124982
|
});
|
|
123661
|
-
import { existsSync as
|
|
124983
|
+
import { existsSync as existsSync23 } from "fs";
|
|
123662
124984
|
import { resolve as resolve17 } from "path";
|
|
123663
124985
|
function looksLikePath(value) {
|
|
123664
124986
|
return value.includes("/") || value.startsWith(".") || value.startsWith("~");
|
|
@@ -123667,6 +124989,7 @@ async function runUpCommand(context, args) {
|
|
|
123667
124989
|
let target = null;
|
|
123668
124990
|
let agentName;
|
|
123669
124991
|
let harness;
|
|
124992
|
+
let model;
|
|
123670
124993
|
for (let index2 = 0;index2 < args.length; index2 += 1) {
|
|
123671
124994
|
const current = args[index2] ?? "";
|
|
123672
124995
|
if (current === "--name") {
|
|
@@ -123695,6 +125018,19 @@ async function runUpCommand(context, args) {
|
|
|
123695
125018
|
harness = current.slice("--harness=".length);
|
|
123696
125019
|
continue;
|
|
123697
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
|
+
}
|
|
123698
125034
|
if (current.startsWith("--")) {
|
|
123699
125035
|
throw new ScoutCliError(`unexpected argument for up: ${current}`);
|
|
123700
125036
|
}
|
|
@@ -123704,10 +125040,10 @@ async function runUpCommand(context, args) {
|
|
|
123704
125040
|
target = current;
|
|
123705
125041
|
}
|
|
123706
125042
|
if (!target) {
|
|
123707
|
-
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>]");
|
|
123708
125044
|
}
|
|
123709
125045
|
let projectPath;
|
|
123710
|
-
if (looksLikePath(target) ||
|
|
125046
|
+
if (looksLikePath(target) || existsSync23(resolve17(target))) {
|
|
123711
125047
|
projectPath = resolve17(target);
|
|
123712
125048
|
} else {
|
|
123713
125049
|
const resolved = await resolveLocalAgentByName(target);
|
|
@@ -123720,6 +125056,7 @@ async function runUpCommand(context, args) {
|
|
|
123720
125056
|
projectPath,
|
|
123721
125057
|
agentName,
|
|
123722
125058
|
harness: parseScoutHarness(harness),
|
|
125059
|
+
model,
|
|
123723
125060
|
currentDirectory: defaultScoutContextDirectory(context)
|
|
123724
125061
|
});
|
|
123725
125062
|
context.output.writeValue(agent, renderScoutUpResult);
|
|
@@ -123827,10 +125164,10 @@ var init_whoami = __esm(async () => {
|
|
|
123827
125164
|
});
|
|
123828
125165
|
|
|
123829
125166
|
// ../../apps/desktop/src/cli/main.ts
|
|
123830
|
-
import { existsSync as
|
|
125167
|
+
import { existsSync as existsSync25, readFileSync as readFileSync15, writeFileSync as writeFileSync9, statSync as statSync5, mkdirSync as mkdirSync12 } from "fs";
|
|
123831
125168
|
import { join as join32 } from "path";
|
|
123832
125169
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
123833
|
-
import { homedir as
|
|
125170
|
+
import { homedir as homedir24 } from "os";
|
|
123834
125171
|
|
|
123835
125172
|
// ../../apps/desktop/src/cli/argv.ts
|
|
123836
125173
|
function parseScoutArgv(argv) {
|
|
@@ -124090,8 +125427,8 @@ var _scoutBinPath = null;
|
|
|
124090
125427
|
function getScoutBinPath() {
|
|
124091
125428
|
if (_scoutBinPath)
|
|
124092
125429
|
return _scoutBinPath;
|
|
124093
|
-
_scoutBinPath = join32(
|
|
124094
|
-
if (!
|
|
125430
|
+
_scoutBinPath = join32(homedir24(), ".bun", "bin", "scout");
|
|
125431
|
+
if (!existsSync25(_scoutBinPath)) {
|
|
124095
125432
|
_scoutBinPath = spawnSync3("which", ["scout"], { encoding: "utf8" }).stdout.trim();
|
|
124096
125433
|
}
|
|
124097
125434
|
return _scoutBinPath;
|
|
@@ -124099,18 +125436,18 @@ function getScoutBinPath() {
|
|
|
124099
125436
|
async function ensureBrokerUptodate() {
|
|
124100
125437
|
try {
|
|
124101
125438
|
const mtime = statSync5(getScoutBinPath()).mtimeMs;
|
|
124102
|
-
const checkpointDir = join32(
|
|
125439
|
+
const checkpointDir = join32(homedir24(), ".scout");
|
|
124103
125440
|
const mtimePath = join32(checkpointDir, "cli-mtime");
|
|
124104
|
-
const lastMtime =
|
|
125441
|
+
const lastMtime = existsSync25(mtimePath) ? Number(readFileSync15(mtimePath, "utf8").trim()) : 0;
|
|
124105
125442
|
if (mtime > lastMtime) {
|
|
124106
125443
|
const uid = process.getuid();
|
|
124107
|
-
const plistPath = join32(
|
|
125444
|
+
const plistPath = join32(homedir24(), "Library", "LaunchAgents", "dev.openscout.broker.plist");
|
|
124108
125445
|
spawnSync3("launchctl", ["bootout", `gui/${uid}/dev.openscout.broker`], { stdio: "ignore" });
|
|
124109
125446
|
await new Promise((resolve18) => setTimeout(resolve18, 1500));
|
|
124110
125447
|
spawnSync3("launchctl", ["bootstrap", `gui/${uid}`, plistPath], { stdio: "ignore" });
|
|
124111
125448
|
await new Promise((resolve18) => setTimeout(resolve18, 2000));
|
|
124112
125449
|
}
|
|
124113
|
-
if (!
|
|
125450
|
+
if (!existsSync25(checkpointDir)) {
|
|
124114
125451
|
mkdirSync12(checkpointDir, { recursive: true });
|
|
124115
125452
|
}
|
|
124116
125453
|
writeFileSync9(mtimePath, String(Math.floor(mtime)), { encoding: "utf8", flag: "w" });
|