@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/pair-supervisor.mjs
CHANGED
|
@@ -956,19 +956,58 @@ function findStoredCerts() {
|
|
|
956
956
|
return null;
|
|
957
957
|
}
|
|
958
958
|
}
|
|
959
|
-
function
|
|
959
|
+
function readTailscaleStatus() {
|
|
960
960
|
try {
|
|
961
961
|
const output = execSync("tailscale status --self=true --peers=false --json", {
|
|
962
962
|
stdio: ["pipe", "pipe", "pipe"],
|
|
963
963
|
timeout: 5000
|
|
964
964
|
}).toString();
|
|
965
965
|
const data = JSON.parse(output);
|
|
966
|
-
const dnsName = data
|
|
967
|
-
return
|
|
966
|
+
const dnsName = typeof data.Self?.DNSName === "string" ? data.Self.DNSName.replace(/\.$/, "") : "";
|
|
967
|
+
return {
|
|
968
|
+
backendState: typeof data.BackendState === "string" ? data.BackendState : null,
|
|
969
|
+
dnsName: dnsName || null,
|
|
970
|
+
online: data.Self?.Online !== false,
|
|
971
|
+
health: Array.isArray(data.Health) ? data.Health.filter((entry) => typeof entry === "string") : []
|
|
972
|
+
};
|
|
968
973
|
} catch {
|
|
969
974
|
return null;
|
|
970
975
|
}
|
|
971
976
|
}
|
|
977
|
+
function resolveRelayEndpointForTailscaleStatus(port, tailscale) {
|
|
978
|
+
const backendState = tailscale?.backendState?.trim().toLowerCase() ?? "";
|
|
979
|
+
const hostname = tailscale?.dnsName ?? null;
|
|
980
|
+
const tailscaleRunning = backendState === "running" && tailscale?.online !== false && Boolean(hostname);
|
|
981
|
+
const tls = resolveTls(tailscaleRunning ? hostname : null);
|
|
982
|
+
if (tailscaleRunning && hostname && tls) {
|
|
983
|
+
return {
|
|
984
|
+
relayUrl: `wss://${hostname}:${port}`,
|
|
985
|
+
options: { tls }
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
if (tailscaleRunning && hostname) {
|
|
989
|
+
pairingLog.warn("relay", "tailscale is running without TLS; falling back to insecure websocket relay", {
|
|
990
|
+
hostname,
|
|
991
|
+
port
|
|
992
|
+
});
|
|
993
|
+
return {
|
|
994
|
+
relayUrl: `ws://${hostname}:${port}`,
|
|
995
|
+
options: {}
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
if (hostname && backendState && backendState !== "running") {
|
|
999
|
+
pairingLog.warn("relay", "tailscale hostname detected but tailscale is not running; using local-only relay endpoint", {
|
|
1000
|
+
hostname,
|
|
1001
|
+
backendState,
|
|
1002
|
+
health: tailscale?.health ?? [],
|
|
1003
|
+
port
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
return {
|
|
1007
|
+
relayUrl: `ws://127.0.0.1:${port}`,
|
|
1008
|
+
options: {}
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
972
1011
|
function generateTailscaleCerts(hostname) {
|
|
973
1012
|
mkdirSync3(PAIRING_DIR, { recursive: true });
|
|
974
1013
|
const certPath = join(PAIRING_DIR, `${hostname}.crt`);
|
|
@@ -1014,28 +1053,7 @@ function resolveTls(hostname) {
|
|
|
1014
1053
|
return generateTailscaleCerts(hostname);
|
|
1015
1054
|
}
|
|
1016
1055
|
function resolveRelayEndpoint(port) {
|
|
1017
|
-
|
|
1018
|
-
const tls = resolveTls(hostname);
|
|
1019
|
-
if (hostname && tls) {
|
|
1020
|
-
return {
|
|
1021
|
-
relayUrl: `wss://${hostname}:${port}`,
|
|
1022
|
-
options: { tls }
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
if (hostname) {
|
|
1026
|
-
pairingLog.warn("relay", "tailscale hostname detected without TLS; falling back to insecure websocket relay", {
|
|
1027
|
-
hostname,
|
|
1028
|
-
port
|
|
1029
|
-
});
|
|
1030
|
-
return {
|
|
1031
|
-
relayUrl: `ws://${hostname}:${port}`,
|
|
1032
|
-
options: {}
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
return {
|
|
1036
|
-
relayUrl: `ws://127.0.0.1:${port}`,
|
|
1037
|
-
options: {}
|
|
1038
|
-
};
|
|
1056
|
+
return resolveRelayEndpointForTailscaleStatus(port, readTailscaleStatus());
|
|
1039
1057
|
}
|
|
1040
1058
|
function startManagedRelay(port = 7889) {
|
|
1041
1059
|
const endpoint = resolveRelayEndpoint(port);
|
|
@@ -7869,7 +7887,73 @@ function resolveConfig() {
|
|
|
7869
7887
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
|
|
7870
7888
|
import { isAbsolute } from "path";
|
|
7871
7889
|
import { homedir as homedir9 } from "os";
|
|
7890
|
+
|
|
7891
|
+
// ../../apps/desktop/src/core/pairing/runtime/bridge/web-handoff.ts
|
|
7892
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
7893
|
+
var SCOUT_WEB_HANDOFF_COOKIE = "scout_handoff";
|
|
7894
|
+
var WEB_HANDOFF_TTL_MS = 5 * 60 * 1000;
|
|
7895
|
+
var activeWebHandoffs = new Map;
|
|
7896
|
+
function pruneExpiredWebHandoffs(now = Date.now()) {
|
|
7897
|
+
for (const [token, record] of activeWebHandoffs) {
|
|
7898
|
+
if (record.expiresAt <= now) {
|
|
7899
|
+
activeWebHandoffs.delete(token);
|
|
7900
|
+
}
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
function scopesMatch(left, right) {
|
|
7904
|
+
if (left.kind !== right.kind) {
|
|
7905
|
+
return false;
|
|
7906
|
+
}
|
|
7907
|
+
if (left.sessionId !== right.sessionId) {
|
|
7908
|
+
return false;
|
|
7909
|
+
}
|
|
7910
|
+
if (left.kind === "session") {
|
|
7911
|
+
return true;
|
|
7912
|
+
}
|
|
7913
|
+
return left.turnId === right.turnId && left.blockId === right.blockId;
|
|
7914
|
+
}
|
|
7915
|
+
function pathForWebHandoffScope(scope) {
|
|
7916
|
+
switch (scope.kind) {
|
|
7917
|
+
case "session":
|
|
7918
|
+
return `/handoff/session/${encodeURIComponent(scope.sessionId)}`;
|
|
7919
|
+
case "file_change":
|
|
7920
|
+
return `/handoff/file-change/${encodeURIComponent(scope.sessionId)}/${encodeURIComponent(scope.turnId)}/${encodeURIComponent(scope.blockId)}`;
|
|
7921
|
+
}
|
|
7922
|
+
}
|
|
7923
|
+
function issueWebHandoff(scope, deviceId) {
|
|
7924
|
+
pruneExpiredWebHandoffs();
|
|
7925
|
+
const token = randomBytes2(24).toString("base64url");
|
|
7926
|
+
const expiresAt = Date.now() + WEB_HANDOFF_TTL_MS;
|
|
7927
|
+
activeWebHandoffs.set(token, {
|
|
7928
|
+
token,
|
|
7929
|
+
deviceId: deviceId?.trim() || null,
|
|
7930
|
+
expiresAt,
|
|
7931
|
+
scope
|
|
7932
|
+
});
|
|
7933
|
+
return { token, expiresAt, scope };
|
|
7934
|
+
}
|
|
7935
|
+
function readAuthorizedWebHandoff(token, scope) {
|
|
7936
|
+
pruneExpiredWebHandoffs();
|
|
7937
|
+
if (!token) {
|
|
7938
|
+
return null;
|
|
7939
|
+
}
|
|
7940
|
+
const record = activeWebHandoffs.get(token);
|
|
7941
|
+
if (!record) {
|
|
7942
|
+
return null;
|
|
7943
|
+
}
|
|
7944
|
+
if (!scopesMatch(record.scope, scope)) {
|
|
7945
|
+
return null;
|
|
7946
|
+
}
|
|
7947
|
+
return record;
|
|
7948
|
+
}
|
|
7949
|
+
|
|
7950
|
+
// ../../apps/desktop/src/core/pairing/runtime/bridge/fileserver.ts
|
|
7872
7951
|
var ALLOWED_ROOTS = [homedir9(), "/tmp"];
|
|
7952
|
+
var LOOPBACK_IPV4_HOST_PATTERN = /^127(?:\.\d{1,3}){3}$/;
|
|
7953
|
+
function isTrustedLoopbackHostname(hostname) {
|
|
7954
|
+
const normalized = hostname.trim().toLowerCase();
|
|
7955
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "::1" || LOOPBACK_IPV4_HOST_PATTERN.test(normalized);
|
|
7956
|
+
}
|
|
7873
7957
|
function isAllowedPath(filePath) {
|
|
7874
7958
|
if (!isAbsolute(filePath))
|
|
7875
7959
|
return false;
|
|
@@ -7880,7 +7964,7 @@ function isAllowedPath(filePath) {
|
|
|
7880
7964
|
return ALLOWED_ROOTS.some((root) => filePath.startsWith(root));
|
|
7881
7965
|
}
|
|
7882
7966
|
function startFileServer(options) {
|
|
7883
|
-
const { port } = options;
|
|
7967
|
+
const { port, bridge } = options;
|
|
7884
7968
|
let server = null;
|
|
7885
7969
|
function start() {
|
|
7886
7970
|
try {
|
|
@@ -7888,7 +7972,7 @@ function startFileServer(options) {
|
|
|
7888
7972
|
port,
|
|
7889
7973
|
fetch(req) {
|
|
7890
7974
|
try {
|
|
7891
|
-
return route(new URL(req.url));
|
|
7975
|
+
return route(req, new URL(req.url), bridge);
|
|
7892
7976
|
} catch (err) {
|
|
7893
7977
|
console.error(`[fileserver] ${req.url} \u2192`, err.message);
|
|
7894
7978
|
return new Response("Internal error", { status: 500 });
|
|
@@ -7911,12 +7995,19 @@ function startFileServer(options) {
|
|
|
7911
7995
|
start();
|
|
7912
7996
|
return { port, stop, restart };
|
|
7913
7997
|
}
|
|
7914
|
-
function route(url) {
|
|
7998
|
+
function route(req, url, bridge) {
|
|
7915
7999
|
const path2 = url.pathname;
|
|
7916
|
-
if (path2 === "/file")
|
|
8000
|
+
if (path2 === "/file") {
|
|
8001
|
+
if (!isTrustedLoopbackHostname(url.hostname)) {
|
|
8002
|
+
return new Response("Forbidden", { status: 403 });
|
|
8003
|
+
}
|
|
7917
8004
|
return serveFile(url);
|
|
8005
|
+
}
|
|
7918
8006
|
if (path2 === "/health")
|
|
7919
8007
|
return Response.json({ ok: true });
|
|
8008
|
+
if (path2.startsWith("/handoff/")) {
|
|
8009
|
+
return serveHandoffPage(req, url, bridge);
|
|
8010
|
+
}
|
|
7920
8011
|
return new Response("Pairing file server", { status: 200 });
|
|
7921
8012
|
}
|
|
7922
8013
|
function serveFile(url) {
|
|
@@ -7930,9 +8021,523 @@ function serveFile(url) {
|
|
|
7930
8021
|
return new Response("Not found", { status: 404 });
|
|
7931
8022
|
return new Response(file);
|
|
7932
8023
|
}
|
|
8024
|
+
function serveHandoffPage(req, url, bridge) {
|
|
8025
|
+
if (!bridge) {
|
|
8026
|
+
return new Response("Secure handoff unavailable", { status: 503 });
|
|
8027
|
+
}
|
|
8028
|
+
const scope = parseHandoffScope(url);
|
|
8029
|
+
if (!scope) {
|
|
8030
|
+
return new Response("Not found", { status: 404 });
|
|
8031
|
+
}
|
|
8032
|
+
const token = readHandoffToken(req);
|
|
8033
|
+
const authorized = readAuthorizedWebHandoff(token, scope);
|
|
8034
|
+
if (!authorized) {
|
|
8035
|
+
return unauthorizedHandoffResponse();
|
|
8036
|
+
}
|
|
8037
|
+
let html;
|
|
8038
|
+
switch (scope.kind) {
|
|
8039
|
+
case "session": {
|
|
8040
|
+
const snapshot = bridge.getSessionSnapshot(scope.sessionId);
|
|
8041
|
+
if (!snapshot) {
|
|
8042
|
+
return new Response("Session handoff expired", { status: 404 });
|
|
8043
|
+
}
|
|
8044
|
+
html = renderSessionHandoffPage(snapshot);
|
|
8045
|
+
break;
|
|
8046
|
+
}
|
|
8047
|
+
case "file_change": {
|
|
8048
|
+
const snapshot = bridge.getSessionSnapshot(scope.sessionId);
|
|
8049
|
+
if (!snapshot) {
|
|
8050
|
+
return new Response("Session handoff expired", { status: 404 });
|
|
8051
|
+
}
|
|
8052
|
+
const target = findFileChangeBlock(snapshot, scope.turnId, scope.blockId);
|
|
8053
|
+
if (!target) {
|
|
8054
|
+
return new Response("File change handoff expired", { status: 404 });
|
|
8055
|
+
}
|
|
8056
|
+
html = renderFileChangeHandoffPage(snapshot, target.turn, target.block);
|
|
8057
|
+
break;
|
|
8058
|
+
}
|
|
8059
|
+
}
|
|
8060
|
+
const headers = new Headers({
|
|
8061
|
+
"content-type": "text/html; charset=utf-8",
|
|
8062
|
+
"cache-control": "no-store",
|
|
8063
|
+
"x-content-type-options": "nosniff"
|
|
8064
|
+
});
|
|
8065
|
+
headers.set("set-cookie", `${SCOUT_WEB_HANDOFF_COOKIE}=${authorized.token}; Max-Age=300; Path=/handoff; HttpOnly; SameSite=Strict`);
|
|
8066
|
+
return new Response(html, { status: 200, headers });
|
|
8067
|
+
}
|
|
8068
|
+
function unauthorizedHandoffResponse() {
|
|
8069
|
+
return new Response("Secure handoff required", {
|
|
8070
|
+
status: 401,
|
|
8071
|
+
headers: {
|
|
8072
|
+
"cache-control": "no-store",
|
|
8073
|
+
"set-cookie": `${SCOUT_WEB_HANDOFF_COOKIE}=; Max-Age=0; Path=/handoff; HttpOnly; SameSite=Strict`
|
|
8074
|
+
}
|
|
8075
|
+
});
|
|
8076
|
+
}
|
|
8077
|
+
function parseHandoffScope(url) {
|
|
8078
|
+
const parts = url.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part));
|
|
8079
|
+
if (parts[0] !== "handoff") {
|
|
8080
|
+
return null;
|
|
8081
|
+
}
|
|
8082
|
+
if (parts[1] === "session" && parts[2]) {
|
|
8083
|
+
return { kind: "session", sessionId: parts[2] };
|
|
8084
|
+
}
|
|
8085
|
+
if (parts[1] === "file-change" && parts[2] && parts[3] && parts[4]) {
|
|
8086
|
+
return {
|
|
8087
|
+
kind: "file_change",
|
|
8088
|
+
sessionId: parts[2],
|
|
8089
|
+
turnId: parts[3],
|
|
8090
|
+
blockId: parts[4]
|
|
8091
|
+
};
|
|
8092
|
+
}
|
|
8093
|
+
return null;
|
|
8094
|
+
}
|
|
8095
|
+
function readHandoffToken(req) {
|
|
8096
|
+
const headerToken = req.headers.get("x-scout-handoff-token")?.trim();
|
|
8097
|
+
if (headerToken) {
|
|
8098
|
+
return headerToken;
|
|
8099
|
+
}
|
|
8100
|
+
const cookieHeader = req.headers.get("cookie");
|
|
8101
|
+
if (!cookieHeader) {
|
|
8102
|
+
return null;
|
|
8103
|
+
}
|
|
8104
|
+
for (const part of cookieHeader.split(";")) {
|
|
8105
|
+
const [name, ...rest] = part.trim().split("=");
|
|
8106
|
+
if (name === SCOUT_WEB_HANDOFF_COOKIE) {
|
|
8107
|
+
const value = rest.join("=").trim();
|
|
8108
|
+
return value || null;
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
return null;
|
|
8112
|
+
}
|
|
8113
|
+
function findFileChangeBlock(snapshot, turnId, blockId) {
|
|
8114
|
+
const turn = snapshot.turns.find((candidate) => candidate.id === turnId);
|
|
8115
|
+
if (!turn) {
|
|
8116
|
+
return null;
|
|
8117
|
+
}
|
|
8118
|
+
const blockState = turn.blocks.find((candidate) => candidate.block.id === blockId);
|
|
8119
|
+
const block = blockState?.block;
|
|
8120
|
+
if (!block || block.type !== "action" || block.action.kind !== "file_change") {
|
|
8121
|
+
return null;
|
|
8122
|
+
}
|
|
8123
|
+
return { turn, block };
|
|
8124
|
+
}
|
|
8125
|
+
function renderPage(title, body) {
|
|
8126
|
+
return `<!doctype html>
|
|
8127
|
+
<html lang="en">
|
|
8128
|
+
<head>
|
|
8129
|
+
<meta charset="utf-8" />
|
|
8130
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8131
|
+
<title>${escapeHtml(title)}</title>
|
|
8132
|
+
<style>
|
|
8133
|
+
:root {
|
|
8134
|
+
color-scheme: dark;
|
|
8135
|
+
--bg: #0b0f14;
|
|
8136
|
+
--panel: rgba(19, 27, 39, 0.92);
|
|
8137
|
+
--panel-strong: rgba(12, 18, 28, 0.96);
|
|
8138
|
+
--line: rgba(148, 163, 184, 0.16);
|
|
8139
|
+
--line-strong: rgba(148, 163, 184, 0.24);
|
|
8140
|
+
--text: #e5ecf5;
|
|
8141
|
+
--muted: #90a0b4;
|
|
8142
|
+
--accent: #63d0ff;
|
|
8143
|
+
--green: #54d38a;
|
|
8144
|
+
--amber: #f8c36a;
|
|
8145
|
+
--red: #ff7b72;
|
|
8146
|
+
--surface-add: rgba(84, 211, 138, 0.08);
|
|
8147
|
+
--surface-del: rgba(255, 123, 114, 0.08);
|
|
8148
|
+
}
|
|
8149
|
+
* { box-sizing: border-box; }
|
|
8150
|
+
body {
|
|
8151
|
+
margin: 0;
|
|
8152
|
+
background:
|
|
8153
|
+
radial-gradient(circle at top left, rgba(99, 208, 255, 0.12), transparent 32%),
|
|
8154
|
+
linear-gradient(180deg, #090d12 0%, #0b0f14 55%, #111723 100%);
|
|
8155
|
+
color: var(--text);
|
|
8156
|
+
font: 14px/1.5 ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
|
|
8157
|
+
}
|
|
8158
|
+
.wrap {
|
|
8159
|
+
width: min(980px, calc(100vw - 24px));
|
|
8160
|
+
margin: 0 auto;
|
|
8161
|
+
padding: 20px 0 40px;
|
|
8162
|
+
}
|
|
8163
|
+
.hero, .panel {
|
|
8164
|
+
background: var(--panel);
|
|
8165
|
+
border: 1px solid var(--line);
|
|
8166
|
+
border-radius: 18px;
|
|
8167
|
+
backdrop-filter: blur(18px);
|
|
8168
|
+
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.28);
|
|
8169
|
+
}
|
|
8170
|
+
.hero {
|
|
8171
|
+
padding: 18px 18px 16px;
|
|
8172
|
+
margin-bottom: 14px;
|
|
8173
|
+
}
|
|
8174
|
+
.eyebrow {
|
|
8175
|
+
display: inline-flex;
|
|
8176
|
+
align-items: center;
|
|
8177
|
+
gap: 8px;
|
|
8178
|
+
color: var(--muted);
|
|
8179
|
+
font-size: 11px;
|
|
8180
|
+
letter-spacing: 0.14em;
|
|
8181
|
+
text-transform: uppercase;
|
|
8182
|
+
}
|
|
8183
|
+
.dot {
|
|
8184
|
+
width: 8px;
|
|
8185
|
+
height: 8px;
|
|
8186
|
+
border-radius: 999px;
|
|
8187
|
+
background: var(--accent);
|
|
8188
|
+
box-shadow: 0 0 14px rgba(99, 208, 255, 0.65);
|
|
8189
|
+
}
|
|
8190
|
+
h1 {
|
|
8191
|
+
margin: 12px 0 10px;
|
|
8192
|
+
font-size: 24px;
|
|
8193
|
+
line-height: 1.15;
|
|
8194
|
+
}
|
|
8195
|
+
.meta, .stack {
|
|
8196
|
+
display: flex;
|
|
8197
|
+
flex-wrap: wrap;
|
|
8198
|
+
gap: 8px;
|
|
8199
|
+
}
|
|
8200
|
+
.pill {
|
|
8201
|
+
display: inline-flex;
|
|
8202
|
+
align-items: center;
|
|
8203
|
+
gap: 6px;
|
|
8204
|
+
padding: 5px 10px;
|
|
8205
|
+
border-radius: 999px;
|
|
8206
|
+
border: 1px solid var(--line-strong);
|
|
8207
|
+
background: rgba(255, 255, 255, 0.03);
|
|
8208
|
+
color: var(--muted);
|
|
8209
|
+
font-size: 12px;
|
|
8210
|
+
}
|
|
8211
|
+
.pill strong {
|
|
8212
|
+
color: var(--text);
|
|
8213
|
+
font-weight: 600;
|
|
8214
|
+
}
|
|
8215
|
+
.stack { flex-direction: column; }
|
|
8216
|
+
.panel {
|
|
8217
|
+
margin-top: 12px;
|
|
8218
|
+
overflow: hidden;
|
|
8219
|
+
}
|
|
8220
|
+
.panel-inner {
|
|
8221
|
+
padding: 16px;
|
|
8222
|
+
}
|
|
8223
|
+
.turn {
|
|
8224
|
+
border-top: 1px solid var(--line);
|
|
8225
|
+
}
|
|
8226
|
+
.turn:first-child {
|
|
8227
|
+
border-top: none;
|
|
8228
|
+
}
|
|
8229
|
+
.turn-head {
|
|
8230
|
+
padding: 14px 16px;
|
|
8231
|
+
display: flex;
|
|
8232
|
+
justify-content: space-between;
|
|
8233
|
+
gap: 12px;
|
|
8234
|
+
align-items: center;
|
|
8235
|
+
background: rgba(255, 255, 255, 0.02);
|
|
8236
|
+
}
|
|
8237
|
+
.turn-title {
|
|
8238
|
+
font-size: 13px;
|
|
8239
|
+
font-weight: 600;
|
|
8240
|
+
letter-spacing: 0.02em;
|
|
8241
|
+
}
|
|
8242
|
+
.turn-subtitle {
|
|
8243
|
+
color: var(--muted);
|
|
8244
|
+
font-size: 12px;
|
|
8245
|
+
margin-top: 2px;
|
|
8246
|
+
}
|
|
8247
|
+
.blocks {
|
|
8248
|
+
padding: 8px;
|
|
8249
|
+
}
|
|
8250
|
+
.block {
|
|
8251
|
+
padding: 12px;
|
|
8252
|
+
border-radius: 14px;
|
|
8253
|
+
border: 1px solid var(--line);
|
|
8254
|
+
background: var(--panel-strong);
|
|
8255
|
+
margin: 8px;
|
|
8256
|
+
}
|
|
8257
|
+
.block-head {
|
|
8258
|
+
display: flex;
|
|
8259
|
+
justify-content: space-between;
|
|
8260
|
+
align-items: center;
|
|
8261
|
+
gap: 12px;
|
|
8262
|
+
margin-bottom: 10px;
|
|
8263
|
+
}
|
|
8264
|
+
.block-type {
|
|
8265
|
+
color: var(--muted);
|
|
8266
|
+
text-transform: uppercase;
|
|
8267
|
+
letter-spacing: 0.12em;
|
|
8268
|
+
font-size: 11px;
|
|
8269
|
+
}
|
|
8270
|
+
.label {
|
|
8271
|
+
color: var(--muted);
|
|
8272
|
+
font-size: 12px;
|
|
8273
|
+
}
|
|
8274
|
+
.label strong {
|
|
8275
|
+
color: var(--text);
|
|
8276
|
+
}
|
|
8277
|
+
pre {
|
|
8278
|
+
margin: 0;
|
|
8279
|
+
white-space: pre-wrap;
|
|
8280
|
+
word-break: break-word;
|
|
8281
|
+
overflow-wrap: anywhere;
|
|
8282
|
+
font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
8283
|
+
}
|
|
8284
|
+
.code {
|
|
8285
|
+
padding: 12px;
|
|
8286
|
+
border-radius: 12px;
|
|
8287
|
+
border: 1px solid var(--line);
|
|
8288
|
+
background: rgba(255, 255, 255, 0.02);
|
|
8289
|
+
}
|
|
8290
|
+
.diff-line-add { background: var(--surface-add); color: #8ef3b7; }
|
|
8291
|
+
.diff-line-del { background: var(--surface-del); color: #ff9d96; }
|
|
8292
|
+
.details {
|
|
8293
|
+
margin-top: 10px;
|
|
8294
|
+
border: 1px solid var(--line);
|
|
8295
|
+
border-radius: 12px;
|
|
8296
|
+
overflow: hidden;
|
|
8297
|
+
}
|
|
8298
|
+
details > summary {
|
|
8299
|
+
list-style: none;
|
|
8300
|
+
cursor: pointer;
|
|
8301
|
+
padding: 10px 12px;
|
|
8302
|
+
background: rgba(255, 255, 255, 0.03);
|
|
8303
|
+
color: var(--muted);
|
|
8304
|
+
font-size: 12px;
|
|
8305
|
+
text-transform: uppercase;
|
|
8306
|
+
letter-spacing: 0.1em;
|
|
8307
|
+
}
|
|
8308
|
+
details[open] > summary {
|
|
8309
|
+
border-bottom: 1px solid var(--line);
|
|
8310
|
+
}
|
|
8311
|
+
.empty {
|
|
8312
|
+
padding: 28px 18px;
|
|
8313
|
+
color: var(--muted);
|
|
8314
|
+
}
|
|
8315
|
+
.grid {
|
|
8316
|
+
display: grid;
|
|
8317
|
+
gap: 10px;
|
|
8318
|
+
}
|
|
8319
|
+
.grid.two {
|
|
8320
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
8321
|
+
}
|
|
8322
|
+
a.inline {
|
|
8323
|
+
color: var(--accent);
|
|
8324
|
+
text-decoration: none;
|
|
8325
|
+
}
|
|
8326
|
+
.status-streaming { color: var(--amber); }
|
|
8327
|
+
.status-completed { color: var(--green); }
|
|
8328
|
+
.status-failed, .status-error { color: var(--red); }
|
|
8329
|
+
</style>
|
|
8330
|
+
</head>
|
|
8331
|
+
<body>
|
|
8332
|
+
<main class="wrap">
|
|
8333
|
+
${body}
|
|
8334
|
+
</main>
|
|
8335
|
+
</body>
|
|
8336
|
+
</html>`;
|
|
8337
|
+
}
|
|
8338
|
+
function renderSessionHandoffPage(snapshot) {
|
|
8339
|
+
const title = snapshot.session.name || snapshot.session.id;
|
|
8340
|
+
const hero = `
|
|
8341
|
+
<section class="hero">
|
|
8342
|
+
<div class="eyebrow"><span class="dot"></span> Secure Proxy Session Handoff</div>
|
|
8343
|
+
<h1>${escapeHtml(title)}</h1>
|
|
8344
|
+
<div class="meta">
|
|
8345
|
+
<span class="pill"><strong>ID</strong> ${escapeHtml(snapshot.session.id)}</span>
|
|
8346
|
+
<span class="pill"><strong>Adapter</strong> ${escapeHtml(snapshot.session.adapterType)}</span>
|
|
8347
|
+
<span class="pill"><strong>Status</strong> ${escapeHtml(snapshot.session.status)}</span>
|
|
8348
|
+
${snapshot.session.model ? `<span class="pill"><strong>Model</strong> ${escapeHtml(snapshot.session.model)}</span>` : ""}
|
|
8349
|
+
${snapshot.session.cwd ? `<span class="pill"><strong>Workspace</strong> ${escapeHtml(snapshot.session.cwd)}</span>` : ""}
|
|
8350
|
+
</div>
|
|
8351
|
+
</section>
|
|
8352
|
+
`;
|
|
8353
|
+
const turns = snapshot.turns.length > 0 ? snapshot.turns.map((turn) => {
|
|
8354
|
+
const turnBody = turn.blocks.length > 0 ? turn.blocks.map(({ block }) => renderBlock(block)).join("") : `<div class="empty">No blocks were captured for this turn.</div>`;
|
|
8355
|
+
return `
|
|
8356
|
+
<section class="turn">
|
|
8357
|
+
<div class="turn-head">
|
|
8358
|
+
<div>
|
|
8359
|
+
<div class="turn-title">${escapeHtml(turn.id)}</div>
|
|
8360
|
+
<div class="turn-subtitle">${formatTimestamp(turn.startedAt)}${turn.endedAt ? ` to ${formatTimestamp(turn.endedAt)}` : ""}</div>
|
|
8361
|
+
</div>
|
|
8362
|
+
<span class="pill"><strong>Turn</strong> <span class="status-${escapeHtml(turn.status)}">${escapeHtml(turn.status)}</span></span>
|
|
8363
|
+
</div>
|
|
8364
|
+
<div class="blocks">${turnBody}</div>
|
|
8365
|
+
</section>
|
|
8366
|
+
`;
|
|
8367
|
+
}).join("") : `<div class="empty">This session has not produced any turns yet.</div>`;
|
|
8368
|
+
return renderPage(title, `${hero}<section class="panel">${turns}</section>`);
|
|
8369
|
+
}
|
|
8370
|
+
function renderFileChangeHandoffPage(snapshot, turn, block) {
|
|
8371
|
+
const action = block.action;
|
|
8372
|
+
const title = action.path || snapshot.session.name || snapshot.session.id;
|
|
8373
|
+
const hero = `
|
|
8374
|
+
<section class="hero">
|
|
8375
|
+
<div class="eyebrow"><span class="dot"></span> Secure Proxy File Change</div>
|
|
8376
|
+
<h1>${escapeHtml(title)}</h1>
|
|
8377
|
+
<div class="meta">
|
|
8378
|
+
<span class="pill"><strong>Session</strong> ${escapeHtml(snapshot.session.name || snapshot.session.id)}</span>
|
|
8379
|
+
<span class="pill"><strong>Turn</strong> ${escapeHtml(turn.id)}</span>
|
|
8380
|
+
<span class="pill"><strong>Status</strong> ${escapeHtml(action.status)}</span>
|
|
8381
|
+
</div>
|
|
8382
|
+
</section>
|
|
8383
|
+
`;
|
|
8384
|
+
const body = `
|
|
8385
|
+
<section class="panel">
|
|
8386
|
+
<div class="panel-inner stack">
|
|
8387
|
+
<div class="grid two">
|
|
8388
|
+
<div class="pill"><strong>Path</strong> ${escapeHtml(action.path)}</div>
|
|
8389
|
+
<div class="pill"><strong>Block</strong> ${escapeHtml(block.id)}</div>
|
|
8390
|
+
</div>
|
|
8391
|
+
${action.diff ? renderDetailsSection("Diff", renderDiff(action.diff), true) : `<div class="empty">No diff was recorded for this action.</div>`}
|
|
8392
|
+
${action.output ? renderDetailsSection("Output", `<div class="code"><pre>${escapeHtml(action.output)}</pre></div>`) : ""}
|
|
8393
|
+
</div>
|
|
8394
|
+
</section>
|
|
8395
|
+
`;
|
|
8396
|
+
return renderPage(title, `${hero}${body}`);
|
|
8397
|
+
}
|
|
8398
|
+
function renderBlock(block) {
|
|
8399
|
+
switch (block.type) {
|
|
8400
|
+
case "text":
|
|
8401
|
+
return renderTextLikeBlock("Text", block.text);
|
|
8402
|
+
case "reasoning":
|
|
8403
|
+
return renderTextLikeBlock("Reasoning", block.text);
|
|
8404
|
+
case "error":
|
|
8405
|
+
return `
|
|
8406
|
+
<article class="block">
|
|
8407
|
+
<div class="block-head">
|
|
8408
|
+
<div class="block-type">Error</div>
|
|
8409
|
+
<span class="pill status-error">${escapeHtml(block.status)}</span>
|
|
8410
|
+
</div>
|
|
8411
|
+
<div class="code"><pre>${escapeHtml(block.message)}</pre></div>
|
|
8412
|
+
</article>
|
|
8413
|
+
`;
|
|
8414
|
+
case "file":
|
|
8415
|
+
return `
|
|
8416
|
+
<article class="block">
|
|
8417
|
+
<div class="block-head">
|
|
8418
|
+
<div class="block-type">File</div>
|
|
8419
|
+
<span class="pill">${escapeHtml(block.status)}</span>
|
|
8420
|
+
</div>
|
|
8421
|
+
<div class="grid">
|
|
8422
|
+
${block.name ? `<div class="label"><strong>Name</strong> ${escapeHtml(block.name)}</div>` : ""}
|
|
8423
|
+
<div class="label"><strong>MIME</strong> ${escapeHtml(block.mimeType)}</div>
|
|
8424
|
+
</div>
|
|
8425
|
+
</article>
|
|
8426
|
+
`;
|
|
8427
|
+
case "question":
|
|
8428
|
+
return renderQuestionBlock(block);
|
|
8429
|
+
case "action":
|
|
8430
|
+
return renderActionBlock(block);
|
|
8431
|
+
}
|
|
8432
|
+
}
|
|
8433
|
+
function renderTextLikeBlock(label, text) {
|
|
8434
|
+
return `
|
|
8435
|
+
<article class="block">
|
|
8436
|
+
<div class="block-head">
|
|
8437
|
+
<div class="block-type">${escapeHtml(label)}</div>
|
|
8438
|
+
</div>
|
|
8439
|
+
<div class="code"><pre>${escapeHtml(text)}</pre></div>
|
|
8440
|
+
</article>
|
|
8441
|
+
`;
|
|
8442
|
+
}
|
|
8443
|
+
function renderQuestionBlock(block) {
|
|
8444
|
+
const answer = block.answer?.length ? block.answer.join(", ") : "Awaiting answer";
|
|
8445
|
+
const options = block.options?.length ? `<div class="label"><strong>Options</strong> ${escapeHtml(block.options.map((option) => option.label).join(", "))}</div>` : "";
|
|
8446
|
+
return `
|
|
8447
|
+
<article class="block">
|
|
8448
|
+
<div class="block-head">
|
|
8449
|
+
<div class="block-type">Question</div>
|
|
8450
|
+
<span class="pill">${escapeHtml(block.questionStatus)}</span>
|
|
8451
|
+
</div>
|
|
8452
|
+
${block.header ? `<div class="label"><strong>${escapeHtml(block.header)}</strong></div>` : ""}
|
|
8453
|
+
<div class="code"><pre>${escapeHtml(block.question)}</pre></div>
|
|
8454
|
+
<div class="stack" style="margin-top: 10px;">
|
|
8455
|
+
${options}
|
|
8456
|
+
<div class="label"><strong>Answer</strong> ${escapeHtml(answer)}</div>
|
|
8457
|
+
</div>
|
|
8458
|
+
</article>
|
|
8459
|
+
`;
|
|
8460
|
+
}
|
|
8461
|
+
function renderActionBlock(block) {
|
|
8462
|
+
const action = block.action;
|
|
8463
|
+
const parts = [];
|
|
8464
|
+
switch (action.kind) {
|
|
8465
|
+
case "file_change":
|
|
8466
|
+
parts.push(`<div class="label"><strong>Path</strong> ${escapeHtml(action.path)}</div>`);
|
|
8467
|
+
if (action.diff) {
|
|
8468
|
+
parts.push(renderDetailsSection("Diff", renderDiff(action.diff)));
|
|
8469
|
+
}
|
|
8470
|
+
break;
|
|
8471
|
+
case "command":
|
|
8472
|
+
parts.push(`<div class="label"><strong>Command</strong></div><div class="code"><pre>${escapeHtml(action.command)}</pre></div>`);
|
|
8473
|
+
if (typeof action.exitCode === "number") {
|
|
8474
|
+
parts.push(`<div class="label"><strong>Exit Code</strong> ${escapeHtml(String(action.exitCode))}</div>`);
|
|
8475
|
+
}
|
|
8476
|
+
break;
|
|
8477
|
+
case "tool_call":
|
|
8478
|
+
parts.push(`<div class="label"><strong>Tool</strong> ${escapeHtml(action.toolName)}</div>`);
|
|
8479
|
+
if (action.toolCallId) {
|
|
8480
|
+
parts.push(`<div class="label"><strong>Call ID</strong> ${escapeHtml(action.toolCallId)}</div>`);
|
|
8481
|
+
}
|
|
8482
|
+
break;
|
|
8483
|
+
case "subagent":
|
|
8484
|
+
parts.push(`<div class="label"><strong>Agent</strong> ${escapeHtml(action.agentName || action.agentId)}</div>`);
|
|
8485
|
+
if (action.prompt) {
|
|
8486
|
+
parts.push(`<div class="code"><pre>${escapeHtml(action.prompt)}</pre></div>`);
|
|
8487
|
+
}
|
|
8488
|
+
break;
|
|
8489
|
+
}
|
|
8490
|
+
if (action.approval) {
|
|
8491
|
+
parts.push(`<div class="label"><strong>Approval</strong> ${escapeHtml(action.approval.risk || "medium")} risk${action.approval.description ? ` - ${escapeHtml(action.approval.description)}` : ""}</div>`);
|
|
8492
|
+
}
|
|
8493
|
+
if (action.output) {
|
|
8494
|
+
parts.push(renderDetailsSection("Output", `<div class="code"><pre>${escapeHtml(action.output)}</pre></div>`));
|
|
8495
|
+
}
|
|
8496
|
+
return `
|
|
8497
|
+
<article class="block">
|
|
8498
|
+
<div class="block-head">
|
|
8499
|
+
<div class="block-type">${escapeHtml(action.kind.replace("_", " "))}</div>
|
|
8500
|
+
<span class="pill">${escapeHtml(action.status)}</span>
|
|
8501
|
+
</div>
|
|
8502
|
+
<div class="stack">${parts.join("")}</div>
|
|
8503
|
+
</article>
|
|
8504
|
+
`;
|
|
8505
|
+
}
|
|
8506
|
+
function renderDetailsSection(title, innerHtml, open = false) {
|
|
8507
|
+
return `
|
|
8508
|
+
<details class="details"${open ? " open" : ""}>
|
|
8509
|
+
<summary>${escapeHtml(title)}</summary>
|
|
8510
|
+
<div class="panel-inner">${innerHtml}</div>
|
|
8511
|
+
</details>
|
|
8512
|
+
`;
|
|
8513
|
+
}
|
|
8514
|
+
function renderDiff(diff) {
|
|
8515
|
+
const lines = diff.split(`
|
|
8516
|
+
`).map((line) => {
|
|
8517
|
+
const className = line.startsWith("+") ? "diff-line-add" : line.startsWith("-") ? "diff-line-del" : "";
|
|
8518
|
+
return `<div class="${className}"><pre>${escapeHtml(line || " ")}</pre></div>`;
|
|
8519
|
+
}).join("");
|
|
8520
|
+
return `<div class="code">${lines}</div>`;
|
|
8521
|
+
}
|
|
8522
|
+
function escapeHtml(value) {
|
|
8523
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
8524
|
+
}
|
|
8525
|
+
function formatTimestamp(value) {
|
|
8526
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
8527
|
+
return "Unknown time";
|
|
8528
|
+
}
|
|
8529
|
+
try {
|
|
8530
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
8531
|
+
dateStyle: "medium",
|
|
8532
|
+
timeStyle: "short"
|
|
8533
|
+
}).format(value);
|
|
8534
|
+
} catch {
|
|
8535
|
+
return new Date(value).toISOString();
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
7933
8538
|
|
|
7934
8539
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/server.ts
|
|
7935
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
8540
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync9, realpathSync, statSync as statSync3 } from "fs";
|
|
7936
8541
|
import { execSync as execSync3 } from "child_process";
|
|
7937
8542
|
import { basename as basename6, isAbsolute as isAbsolute3, join as join17, relative as relative2 } from "path";
|
|
7938
8543
|
import { homedir as homedir15 } from "os";
|
|
@@ -8036,6 +8641,11 @@ var BUILT_IN_HARNESS_CATALOG = [
|
|
|
8036
8641
|
loginCommand: "claude login",
|
|
8037
8642
|
notReadyMessage: "Claude is installed but not authenticated yet."
|
|
8038
8643
|
},
|
|
8644
|
+
resume: {
|
|
8645
|
+
command: "claude",
|
|
8646
|
+
sessionFlag: "--resume",
|
|
8647
|
+
cwdFlag: "--cwd"
|
|
8648
|
+
},
|
|
8039
8649
|
capabilities: ["chat", "invoke", "deliver", "summarize", "review"]
|
|
8040
8650
|
},
|
|
8041
8651
|
{
|
|
@@ -8067,6 +8677,11 @@ var BUILT_IN_HARNESS_CATALOG = [
|
|
|
8067
8677
|
loginCommand: "codex login",
|
|
8068
8678
|
notReadyMessage: "Codex is installed but not authenticated yet."
|
|
8069
8679
|
},
|
|
8680
|
+
resume: {
|
|
8681
|
+
command: "codex",
|
|
8682
|
+
sessionFlag: "--thread",
|
|
8683
|
+
cwdFlag: "--cwd"
|
|
8684
|
+
},
|
|
8070
8685
|
capabilities: ["chat", "invoke", "deliver", "review", "execute"]
|
|
8071
8686
|
}
|
|
8072
8687
|
];
|
|
@@ -8179,6 +8794,7 @@ function createBuiltInHarnessCatalog() {
|
|
|
8179
8794
|
anyOf: entry.readiness.anyOf ? [...entry.readiness.anyOf] : undefined
|
|
8180
8795
|
} : undefined,
|
|
8181
8796
|
launch: entry.launch ? { ...entry.launch, args: [...entry.launch.args] } : undefined,
|
|
8797
|
+
resume: entry.resume ? { ...entry.resume } : undefined,
|
|
8182
8798
|
resolveEnv: entry.resolveEnv ? [...entry.resolveEnv] : undefined,
|
|
8183
8799
|
capabilities: [...entry.capabilities],
|
|
8184
8800
|
metadata: entry.metadata ? { ...entry.metadata } : undefined
|
|
@@ -8196,6 +8812,7 @@ function mergeHarnessCatalogEntries(baseEntries, overrides = {}) {
|
|
|
8196
8812
|
install: mergeInstall(entry.install, override.install),
|
|
8197
8813
|
readiness: mergeReadiness(entry.readiness, override.readiness),
|
|
8198
8814
|
launch: mergeLaunch(entry.launch, override.launch),
|
|
8815
|
+
resume: override.resume && entry.resume ? { ...entry.resume, ...override.resume } : entry.resume,
|
|
8199
8816
|
tags: override.tags ? [...override.tags] : entry.tags,
|
|
8200
8817
|
capabilities: override.capabilities ? [...override.capabilities] : entry.capabilities,
|
|
8201
8818
|
resolveEnv: override.resolveEnv ? [...override.resolveEnv] : entry.resolveEnv,
|
|
@@ -8224,6 +8841,7 @@ function mergeHarnessCatalogEntries(baseEntries, overrides = {}) {
|
|
|
8224
8841
|
install: mergeInstall(undefined, override.install),
|
|
8225
8842
|
readiness: mergeReadiness(undefined, override.readiness),
|
|
8226
8843
|
launch: mergeLaunch(undefined, override.launch),
|
|
8844
|
+
resume: override.resume?.command && override.resume?.sessionFlag ? { command: override.resume.command, sessionFlag: override.resume.sessionFlag, cwdFlag: override.resume.cwdFlag } : undefined,
|
|
8227
8845
|
resolveEnv: override.resolveEnv ? [...override.resolveEnv] : undefined,
|
|
8228
8846
|
capabilities: [...override.capabilities],
|
|
8229
8847
|
metadata: override.metadata ? { ...override.metadata } : undefined
|
|
@@ -11729,7 +12347,7 @@ async function ensureRelayAgentConfigured(value, options = {}) {
|
|
|
11729
12347
|
|
|
11730
12348
|
// ../runtime/src/local-agents.ts
|
|
11731
12349
|
import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
|
|
11732
|
-
import { existsSync as
|
|
12350
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
11733
12351
|
import { mkdir as mkdir6, rm as rm5, stat as stat3, writeFile as writeFile6 } from "fs/promises";
|
|
11734
12352
|
import { basename as basename4, dirname as dirname6, join as join15, resolve as resolve5 } from "path";
|
|
11735
12353
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
@@ -11737,7 +12355,8 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
11737
12355
|
// ../runtime/src/claude-stream-json.ts
|
|
11738
12356
|
import { randomUUID } from "crypto";
|
|
11739
12357
|
import { spawn as spawn2 } from "child_process";
|
|
11740
|
-
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile5,
|
|
12358
|
+
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
12359
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
|
|
11741
12360
|
import { join as join12 } from "path";
|
|
11742
12361
|
|
|
11743
12362
|
// ../runtime/src/managed-agent-environment.ts
|
|
@@ -11778,18 +12397,58 @@ function sessionKey(options) {
|
|
|
11778
12397
|
function errorMessage2(error) {
|
|
11779
12398
|
return error instanceof Error ? error.message : String(error);
|
|
11780
12399
|
}
|
|
11781
|
-
|
|
12400
|
+
var SESSION_CATALOG_FILENAME = "session-catalog.json";
|
|
12401
|
+
var SESSION_CATALOG_MAX_ENTRIES = 64;
|
|
12402
|
+
function readSessionCatalogSync(runtimeDirectory) {
|
|
12403
|
+
const catalogPath = join12(runtimeDirectory, SESSION_CATALOG_FILENAME);
|
|
11782
12404
|
try {
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
12405
|
+
if (!existsSync11(catalogPath)) {
|
|
12406
|
+
const legacyPath = join12(runtimeDirectory, "claude-session-id.txt");
|
|
12407
|
+
if (existsSync11(legacyPath)) {
|
|
12408
|
+
const legacyId = readFileSync6(legacyPath, "utf8").trim();
|
|
12409
|
+
if (legacyId) {
|
|
12410
|
+
return {
|
|
12411
|
+
activeSessionId: legacyId,
|
|
12412
|
+
sessions: [{ id: legacyId, startedAt: Date.now(), cwd: "" }]
|
|
12413
|
+
};
|
|
12414
|
+
}
|
|
12415
|
+
}
|
|
12416
|
+
return { activeSessionId: null, sessions: [] };
|
|
12417
|
+
}
|
|
12418
|
+
const raw = readFileSync6(catalogPath, "utf8").trim();
|
|
12419
|
+
if (!raw)
|
|
12420
|
+
return { activeSessionId: null, sessions: [] };
|
|
12421
|
+
const parsed = JSON.parse(raw);
|
|
12422
|
+
return {
|
|
12423
|
+
activeSessionId: parsed.activeSessionId ?? null,
|
|
12424
|
+
sessions: Array.isArray(parsed.sessions) ? parsed.sessions : []
|
|
12425
|
+
};
|
|
11786
12426
|
} catch {
|
|
11787
|
-
return null;
|
|
12427
|
+
return { activeSessionId: null, sessions: [] };
|
|
12428
|
+
}
|
|
12429
|
+
}
|
|
12430
|
+
async function readSessionCatalog(runtimeDirectory) {
|
|
12431
|
+
return readSessionCatalogSync(runtimeDirectory);
|
|
12432
|
+
}
|
|
12433
|
+
async function writeSessionCatalog(runtimeDirectory, catalog) {
|
|
12434
|
+
const catalogPath = join12(runtimeDirectory, SESSION_CATALOG_FILENAME);
|
|
12435
|
+
await writeFile4(catalogPath, JSON.stringify(catalog, null, 2) + `
|
|
12436
|
+
`);
|
|
12437
|
+
}
|
|
12438
|
+
function catalogRecordSession(catalog, sessionId, cwd) {
|
|
12439
|
+
const now = Date.now();
|
|
12440
|
+
const sessions = catalog.sessions.map((s) => s.id === catalog.activeSessionId && !s.endedAt ? { ...s, endedAt: now } : s);
|
|
12441
|
+
if (!sessions.some((s) => s.id === sessionId)) {
|
|
12442
|
+
sessions.push({ id: sessionId, startedAt: now, cwd });
|
|
12443
|
+
}
|
|
12444
|
+
while (sessions.length > SESSION_CATALOG_MAX_ENTRIES) {
|
|
12445
|
+
sessions.shift();
|
|
11788
12446
|
}
|
|
12447
|
+
return { activeSessionId: sessionId, sessions };
|
|
11789
12448
|
}
|
|
11790
12449
|
class ClaudeStreamJsonSession {
|
|
11791
12450
|
options;
|
|
11792
|
-
|
|
12451
|
+
catalogDirectory;
|
|
11793
12452
|
stdoutLogPath;
|
|
11794
12453
|
stderrLogPath;
|
|
11795
12454
|
process = null;
|
|
@@ -11800,7 +12459,7 @@ class ClaudeStreamJsonSession {
|
|
|
11800
12459
|
lastConfigSignature;
|
|
11801
12460
|
constructor(options) {
|
|
11802
12461
|
this.options = options;
|
|
11803
|
-
this.
|
|
12462
|
+
this.catalogDirectory = options.runtimeDirectory;
|
|
11804
12463
|
this.stdoutLogPath = join12(options.logsDirectory, "stdout.log");
|
|
11805
12464
|
this.stderrLogPath = join12(options.logsDirectory, "stderr.log");
|
|
11806
12465
|
this.lastConfigSignature = this.configSignature(options);
|
|
@@ -11906,7 +12565,9 @@ class ClaudeStreamJsonSession {
|
|
|
11906
12565
|
}
|
|
11907
12566
|
if (options.resetSession) {
|
|
11908
12567
|
this.claudeSessionId = null;
|
|
11909
|
-
await
|
|
12568
|
+
const catalog = await readSessionCatalog(this.catalogDirectory);
|
|
12569
|
+
catalog.activeSessionId = null;
|
|
12570
|
+
await writeSessionCatalog(this.catalogDirectory, catalog);
|
|
11910
12571
|
}
|
|
11911
12572
|
}
|
|
11912
12573
|
configSignature(options) {
|
|
@@ -11935,7 +12596,8 @@ class ClaudeStreamJsonSession {
|
|
|
11935
12596
|
await mkdir4(this.options.runtimeDirectory, { recursive: true });
|
|
11936
12597
|
await mkdir4(this.options.logsDirectory, { recursive: true });
|
|
11937
12598
|
await writeFile4(join12(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
|
|
11938
|
-
|
|
12599
|
+
const catalog = await readSessionCatalog(this.catalogDirectory);
|
|
12600
|
+
this.claudeSessionId = catalog.activeSessionId;
|
|
11939
12601
|
const args = [
|
|
11940
12602
|
"--verbose",
|
|
11941
12603
|
"--print",
|
|
@@ -12014,8 +12676,11 @@ class ClaudeStreamJsonSession {
|
|
|
12014
12676
|
const nextSessionId = event.session_id ?? event.sessionId ?? null;
|
|
12015
12677
|
if (nextSessionId && nextSessionId !== this.claudeSessionId) {
|
|
12016
12678
|
this.claudeSessionId = nextSessionId;
|
|
12017
|
-
|
|
12018
|
-
|
|
12679
|
+
(async () => {
|
|
12680
|
+
const catalog = await readSessionCatalog(this.catalogDirectory);
|
|
12681
|
+
const updated = catalogRecordSession(catalog, nextSessionId, this.options.cwd);
|
|
12682
|
+
await writeSessionCatalog(this.catalogDirectory, updated);
|
|
12683
|
+
})();
|
|
12019
12684
|
}
|
|
12020
12685
|
return;
|
|
12021
12686
|
}
|
|
@@ -12079,7 +12744,9 @@ async function shutdownClaudeStreamJsonAgent(options, shutdownOptions = {}) {
|
|
|
12079
12744
|
const session = sessions.get(key);
|
|
12080
12745
|
if (!session) {
|
|
12081
12746
|
if (shutdownOptions.resetSession) {
|
|
12082
|
-
await
|
|
12747
|
+
const catalog = await readSessionCatalog(options.runtimeDirectory);
|
|
12748
|
+
catalog.activeSessionId = null;
|
|
12749
|
+
await writeSessionCatalog(options.runtimeDirectory, catalog);
|
|
12083
12750
|
}
|
|
12084
12751
|
return;
|
|
12085
12752
|
}
|
|
@@ -12089,8 +12756,107 @@ async function shutdownClaudeStreamJsonAgent(options, shutdownOptions = {}) {
|
|
|
12089
12756
|
|
|
12090
12757
|
// ../runtime/src/codex-app-server.ts
|
|
12091
12758
|
import { spawn as spawn3 } from "child_process";
|
|
12092
|
-
import {
|
|
12759
|
+
import { constants as constants3 } from "fs";
|
|
12760
|
+
import { access as access3, appendFile as appendFile3, mkdir as mkdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
12093
12761
|
import { delimiter as delimiter3, join as join13 } from "path";
|
|
12762
|
+
function normalizeCodexModelValue(value) {
|
|
12763
|
+
const trimmed = value?.trim();
|
|
12764
|
+
return trimmed ? trimmed : null;
|
|
12765
|
+
}
|
|
12766
|
+
function encodeCodexModelConfig(model) {
|
|
12767
|
+
return `model=${JSON.stringify(model)}`;
|
|
12768
|
+
}
|
|
12769
|
+
function parseCodexModelConfig(value) {
|
|
12770
|
+
const trimmed = value?.trim();
|
|
12771
|
+
if (!trimmed) {
|
|
12772
|
+
return null;
|
|
12773
|
+
}
|
|
12774
|
+
const separatorIndex = trimmed.indexOf("=");
|
|
12775
|
+
if (separatorIndex <= 0) {
|
|
12776
|
+
return null;
|
|
12777
|
+
}
|
|
12778
|
+
const key = trimmed.slice(0, separatorIndex).trim();
|
|
12779
|
+
if (key !== "model") {
|
|
12780
|
+
return null;
|
|
12781
|
+
}
|
|
12782
|
+
const rawValue = trimmed.slice(separatorIndex + 1).trim();
|
|
12783
|
+
if (!rawValue) {
|
|
12784
|
+
return null;
|
|
12785
|
+
}
|
|
12786
|
+
if (rawValue.startsWith('"') && rawValue.endsWith('"') || rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
12787
|
+
return rawValue.slice(1, -1) || null;
|
|
12788
|
+
}
|
|
12789
|
+
return rawValue;
|
|
12790
|
+
}
|
|
12791
|
+
function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
12792
|
+
const args = Array.isArray(launchArgs) ? launchArgs.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
12793
|
+
const normalized = [];
|
|
12794
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
12795
|
+
const current = args[index] ?? "";
|
|
12796
|
+
if (current === "--model" || current === "-m") {
|
|
12797
|
+
const model = normalizeCodexModelValue(args[index + 1]);
|
|
12798
|
+
if (model) {
|
|
12799
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
12800
|
+
index += 1;
|
|
12801
|
+
continue;
|
|
12802
|
+
}
|
|
12803
|
+
normalized.push(current);
|
|
12804
|
+
continue;
|
|
12805
|
+
}
|
|
12806
|
+
if (current.startsWith("--model=")) {
|
|
12807
|
+
const model = normalizeCodexModelValue(current.slice("--model=".length));
|
|
12808
|
+
if (model) {
|
|
12809
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
12810
|
+
continue;
|
|
12811
|
+
}
|
|
12812
|
+
}
|
|
12813
|
+
if (current.startsWith("-m=")) {
|
|
12814
|
+
const model = normalizeCodexModelValue(current.slice(3));
|
|
12815
|
+
if (model) {
|
|
12816
|
+
normalized.push("-c", encodeCodexModelConfig(model));
|
|
12817
|
+
continue;
|
|
12818
|
+
}
|
|
12819
|
+
}
|
|
12820
|
+
if (current === "-c" || current === "--config") {
|
|
12821
|
+
const next = args[index + 1];
|
|
12822
|
+
if (typeof next === "string") {
|
|
12823
|
+
const model = parseCodexModelConfig(next);
|
|
12824
|
+
normalized.push(current === "--config" ? "--config" : "-c", model ? encodeCodexModelConfig(model) : next);
|
|
12825
|
+
index += 1;
|
|
12826
|
+
continue;
|
|
12827
|
+
}
|
|
12828
|
+
}
|
|
12829
|
+
if (current.startsWith("--config=")) {
|
|
12830
|
+
const value = current.slice("--config=".length);
|
|
12831
|
+
const model = parseCodexModelConfig(value);
|
|
12832
|
+
normalized.push(model ? `--config=${encodeCodexModelConfig(model)}` : current);
|
|
12833
|
+
continue;
|
|
12834
|
+
}
|
|
12835
|
+
normalized.push(current);
|
|
12836
|
+
}
|
|
12837
|
+
return normalized;
|
|
12838
|
+
}
|
|
12839
|
+
function readCodexAppServerModelFromLaunchArgs(launchArgs) {
|
|
12840
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
12841
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
12842
|
+
const current = normalized[index] ?? "";
|
|
12843
|
+
if (current === "-c" || current === "--config") {
|
|
12844
|
+
const model = parseCodexModelConfig(normalized[index + 1]);
|
|
12845
|
+
if (model) {
|
|
12846
|
+
return model;
|
|
12847
|
+
}
|
|
12848
|
+
index += 1;
|
|
12849
|
+
continue;
|
|
12850
|
+
}
|
|
12851
|
+
if (current.startsWith("--config=")) {
|
|
12852
|
+
const model = parseCodexModelConfig(current.slice("--config=".length));
|
|
12853
|
+
if (model) {
|
|
12854
|
+
return model;
|
|
12855
|
+
}
|
|
12856
|
+
}
|
|
12857
|
+
}
|
|
12858
|
+
return null;
|
|
12859
|
+
}
|
|
12094
12860
|
function sessionKey2(options) {
|
|
12095
12861
|
return `${options.agentName}:${options.sessionId}`;
|
|
12096
12862
|
}
|
|
@@ -12186,7 +12952,7 @@ function isServerRequest2(message) {
|
|
|
12186
12952
|
function isNotification2(message) {
|
|
12187
12953
|
return Boolean(message && typeof message === "object" && "method" in message && !("id" in message));
|
|
12188
12954
|
}
|
|
12189
|
-
async function
|
|
12955
|
+
async function readOptionalFile2(filePath) {
|
|
12190
12956
|
try {
|
|
12191
12957
|
const raw = await readFile6(filePath, "utf8");
|
|
12192
12958
|
const trimmed = raw.trim();
|
|
@@ -12199,6 +12965,13 @@ function isMissingCodexRolloutError2(error) {
|
|
|
12199
12965
|
const message = errorMessage3(error).toLowerCase();
|
|
12200
12966
|
return message.includes("no rollout found for thread id");
|
|
12201
12967
|
}
|
|
12968
|
+
function resolveCodexCompletionGraceMs() {
|
|
12969
|
+
const parsed = Number.parseInt(process.env.OPENSCOUT_CODEX_COMPLETION_GRACE_MS ?? "", 10);
|
|
12970
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
12971
|
+
return parsed;
|
|
12972
|
+
}
|
|
12973
|
+
return 60000;
|
|
12974
|
+
}
|
|
12202
12975
|
|
|
12203
12976
|
class CodexAppServerSession {
|
|
12204
12977
|
options;
|
|
@@ -12388,7 +13161,7 @@ class CodexAppServerSession {
|
|
|
12388
13161
|
systemPrompt: options.systemPrompt,
|
|
12389
13162
|
threadId: options.threadId ?? null,
|
|
12390
13163
|
requireExistingThread: options.requireExistingThread === true,
|
|
12391
|
-
launchArgs:
|
|
13164
|
+
launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
|
|
12392
13165
|
});
|
|
12393
13166
|
}
|
|
12394
13167
|
async ensureStarted() {
|
|
@@ -12410,6 +13183,7 @@ class CodexAppServerSession {
|
|
|
12410
13183
|
await mkdir5(this.options.logsDirectory, { recursive: true });
|
|
12411
13184
|
await writeFile5(join13(this.options.runtimeDirectory, "prompt.txt"), this.options.systemPrompt);
|
|
12412
13185
|
const codexExecutable = await resolveCodexExecutable2();
|
|
13186
|
+
const launchArgs = normalizeCodexAppServerLaunchArgs(this.options.launchArgs);
|
|
12413
13187
|
const env = buildManagedAgentEnvironment({
|
|
12414
13188
|
agentName: this.options.agentName,
|
|
12415
13189
|
currentDirectory: this.options.cwd,
|
|
@@ -12420,7 +13194,8 @@ class CodexAppServerSession {
|
|
|
12420
13194
|
...buildScoutMcpCodexLaunchArgs({
|
|
12421
13195
|
currentDirectory: this.options.cwd,
|
|
12422
13196
|
env
|
|
12423
|
-
})
|
|
13197
|
+
}),
|
|
13198
|
+
...launchArgs
|
|
12424
13199
|
], {
|
|
12425
13200
|
cwd: this.options.cwd,
|
|
12426
13201
|
env,
|
|
@@ -12463,7 +13238,7 @@ class CodexAppServerSession {
|
|
|
12463
13238
|
}
|
|
12464
13239
|
async resumeOrStartThread() {
|
|
12465
13240
|
const requestedThreadId = this.options.threadId?.trim() || null;
|
|
12466
|
-
const storedThreadId = requestedThreadId ?? await
|
|
13241
|
+
const storedThreadId = requestedThreadId ?? await readOptionalFile2(this.threadIdPath);
|
|
12467
13242
|
if (storedThreadId) {
|
|
12468
13243
|
try {
|
|
12469
13244
|
const resumed = await this.request("thread/resume", {
|
|
@@ -12518,6 +13293,8 @@ class CodexAppServerSession {
|
|
|
12518
13293
|
turnId: "",
|
|
12519
13294
|
startedAt: Date.now(),
|
|
12520
13295
|
timer: null,
|
|
13296
|
+
graceTimer: null,
|
|
13297
|
+
timedOutAt: null,
|
|
12521
13298
|
messageOrder: [],
|
|
12522
13299
|
messageByItemId: new Map,
|
|
12523
13300
|
resolve: resolve4,
|
|
@@ -12525,20 +13302,43 @@ class CodexAppServerSession {
|
|
|
12525
13302
|
watchers: []
|
|
12526
13303
|
};
|
|
12527
13304
|
turn.timer = setTimeout(() => {
|
|
12528
|
-
this.
|
|
12529
|
-
return;
|
|
12530
|
-
});
|
|
12531
|
-
if (this.activeTurn === turn) {
|
|
12532
|
-
this.activeTurn = null;
|
|
12533
|
-
}
|
|
12534
|
-
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
12535
|
-
watcher.reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
12536
|
-
}
|
|
12537
|
-
reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
13305
|
+
this.scheduleTurnTimeout(turn, timeoutMs);
|
|
12538
13306
|
}, timeoutMs);
|
|
12539
13307
|
this.activeTurn = turn;
|
|
12540
13308
|
return turn;
|
|
12541
13309
|
}
|
|
13310
|
+
buildTurnTimeoutError(timeoutMs) {
|
|
13311
|
+
return new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`);
|
|
13312
|
+
}
|
|
13313
|
+
scheduleTurnTimeout(turn, timeoutMs) {
|
|
13314
|
+
if (turn.timedOutAt !== null) {
|
|
13315
|
+
return;
|
|
13316
|
+
}
|
|
13317
|
+
turn.timedOutAt = Date.now();
|
|
13318
|
+
this.interrupt().catch(() => {
|
|
13319
|
+
return;
|
|
13320
|
+
});
|
|
13321
|
+
const graceMs = resolveCodexCompletionGraceMs();
|
|
13322
|
+
if (graceMs <= 0) {
|
|
13323
|
+
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
13324
|
+
return;
|
|
13325
|
+
}
|
|
13326
|
+
turn.graceTimer = setTimeout(() => {
|
|
13327
|
+
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
13328
|
+
}, graceMs);
|
|
13329
|
+
}
|
|
13330
|
+
rejectTimedOutTurn(turn, timeoutMs) {
|
|
13331
|
+
if (this.activeTurn !== turn) {
|
|
13332
|
+
this.clearActiveTurn(turn);
|
|
13333
|
+
return;
|
|
13334
|
+
}
|
|
13335
|
+
this.clearActiveTurn(turn);
|
|
13336
|
+
const error = this.buildTurnTimeoutError(timeoutMs);
|
|
13337
|
+
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
13338
|
+
watcher.reject(error);
|
|
13339
|
+
}
|
|
13340
|
+
turn.reject(error);
|
|
13341
|
+
}
|
|
12542
13342
|
addTurnWatcher(turn, resolve4, reject, timeoutMs) {
|
|
12543
13343
|
const watcher = {
|
|
12544
13344
|
resolve: resolve4,
|
|
@@ -12572,6 +13372,9 @@ class CodexAppServerSession {
|
|
|
12572
13372
|
if (turn.timer) {
|
|
12573
13373
|
clearTimeout(turn.timer);
|
|
12574
13374
|
}
|
|
13375
|
+
if (turn.graceTimer) {
|
|
13376
|
+
clearTimeout(turn.graceTimer);
|
|
13377
|
+
}
|
|
12575
13378
|
if (this.activeTurn === turn) {
|
|
12576
13379
|
this.activeTurn = null;
|
|
12577
13380
|
}
|
|
@@ -12890,7 +13693,7 @@ function buildCollaborationContractPrompt(agentId) {
|
|
|
12890
13693
|
|
|
12891
13694
|
// ../runtime/src/broker-service.ts
|
|
12892
13695
|
import { spawnSync } from "child_process";
|
|
12893
|
-
import { existsSync as
|
|
13696
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
12894
13697
|
import { homedir as homedir13 } from "os";
|
|
12895
13698
|
import { basename as basename3, dirname as dirname5, join as join14, resolve as resolve4 } from "path";
|
|
12896
13699
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -12936,7 +13739,7 @@ function runtimePackageDir() {
|
|
|
12936
13739
|
return resolve4(moduleDir, "..");
|
|
12937
13740
|
}
|
|
12938
13741
|
function isInstalledRuntimePackageDir(candidate) {
|
|
12939
|
-
return
|
|
13742
|
+
return existsSync12(join14(candidate, "package.json")) && existsSync12(join14(candidate, "bin", "openscout-runtime.mjs"));
|
|
12940
13743
|
}
|
|
12941
13744
|
function findGlobalRuntimeDir() {
|
|
12942
13745
|
const candidates = [
|
|
@@ -12967,7 +13770,7 @@ function findWorkspaceRuntimeDir(startDir) {
|
|
|
12967
13770
|
let current = resolve4(startDir);
|
|
12968
13771
|
while (true) {
|
|
12969
13772
|
const candidate = join14(current, "packages", "runtime");
|
|
12970
|
-
if (
|
|
13773
|
+
if (existsSync12(join14(candidate, "package.json")) && existsSync12(join14(candidate, "src"))) {
|
|
12971
13774
|
return candidate;
|
|
12972
13775
|
}
|
|
12973
13776
|
const parent = dirname5(current);
|
|
@@ -12981,18 +13784,18 @@ function resolveBunExecutable2() {
|
|
|
12981
13784
|
if (explicit && explicit.trim().length > 0) {
|
|
12982
13785
|
return explicit;
|
|
12983
13786
|
}
|
|
12984
|
-
if (basename3(process.execPath).startsWith("bun") &&
|
|
13787
|
+
if (basename3(process.execPath).startsWith("bun") && existsSync12(process.execPath)) {
|
|
12985
13788
|
return process.execPath;
|
|
12986
13789
|
}
|
|
12987
13790
|
const pathEntries = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
12988
13791
|
for (const entry of pathEntries) {
|
|
12989
13792
|
const candidate = join14(entry, "bun");
|
|
12990
|
-
if (
|
|
13793
|
+
if (existsSync12(candidate)) {
|
|
12991
13794
|
return candidate;
|
|
12992
13795
|
}
|
|
12993
13796
|
}
|
|
12994
13797
|
const homeBun = join14(homedir13(), ".bun", "bin", "bun");
|
|
12995
|
-
if (
|
|
13798
|
+
if (existsSync12(homeBun)) {
|
|
12996
13799
|
return homeBun;
|
|
12997
13800
|
}
|
|
12998
13801
|
return "bun";
|
|
@@ -13182,10 +13985,10 @@ function launchctlPath() {
|
|
|
13182
13985
|
return "/bin/launchctl";
|
|
13183
13986
|
}
|
|
13184
13987
|
function readLogLines(path2) {
|
|
13185
|
-
if (!
|
|
13988
|
+
if (!existsSync12(path2)) {
|
|
13186
13989
|
return [];
|
|
13187
13990
|
}
|
|
13188
|
-
return
|
|
13991
|
+
return readFileSync7(path2, "utf8").split(`
|
|
13189
13992
|
`).map((line) => line.trim()).filter(Boolean);
|
|
13190
13993
|
}
|
|
13191
13994
|
function isPackageScriptBanner(line) {
|
|
@@ -13282,7 +14085,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
|
|
|
13282
14085
|
ensureServiceDirectories(config);
|
|
13283
14086
|
const launchctl = inspectLaunchctl(config);
|
|
13284
14087
|
const health = await fetchHealthSnapshot(config);
|
|
13285
|
-
const installed =
|
|
14088
|
+
const installed = existsSync12(config.launchAgentPath);
|
|
13286
14089
|
const lastLogLine = health.reachable ? readLastLogLine([config.stdoutLogPath, config.stderrLogPath]) : readLastLogLine([config.stderrLogPath, config.stdoutLogPath]);
|
|
13287
14090
|
return {
|
|
13288
14091
|
label: config.label,
|
|
@@ -13344,7 +14147,7 @@ async function restartBrokerService(config = resolveBrokerServiceConfig()) {
|
|
|
13344
14147
|
}
|
|
13345
14148
|
async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
|
|
13346
14149
|
await stopBrokerService(config);
|
|
13347
|
-
if (
|
|
14150
|
+
if (existsSync12(config.launchAgentPath)) {
|
|
13348
14151
|
rmSync2(config.launchAgentPath, { force: true });
|
|
13349
14152
|
}
|
|
13350
14153
|
return brokerServiceStatus(config);
|
|
@@ -13427,10 +14230,10 @@ function resolveProjectsRoot(projectPath) {
|
|
|
13427
14230
|
}
|
|
13428
14231
|
try {
|
|
13429
14232
|
const supportPaths = resolveOpenScoutSupportPaths();
|
|
13430
|
-
if (!
|
|
14233
|
+
if (!existsSync13(supportPaths.settingsPath)) {
|
|
13431
14234
|
return dirname6(projectPath);
|
|
13432
14235
|
}
|
|
13433
|
-
const raw = JSON.parse(
|
|
14236
|
+
const raw = JSON.parse(readFileSync8(supportPaths.settingsPath, "utf8"));
|
|
13434
14237
|
const workspaceRoot = raw.discovery?.workspaceRoots?.find((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
13435
14238
|
return workspaceRoot ? resolve5(workspaceRoot) : dirname6(projectPath);
|
|
13436
14239
|
} catch {
|
|
@@ -13443,8 +14246,14 @@ function resolveBrokerUrl() {
|
|
|
13443
14246
|
function nowSeconds2() {
|
|
13444
14247
|
return Math.floor(Date.now() / 1000);
|
|
13445
14248
|
}
|
|
14249
|
+
function scoutCliPath() {
|
|
14250
|
+
return join15(OPENSCOUT_REPO_ROOT, "packages", "cli", "bin", "scout.mjs");
|
|
14251
|
+
}
|
|
14252
|
+
function legacyNodeBrokerRelayCommand() {
|
|
14253
|
+
return `node ${JSON.stringify(scoutCliPath())}`;
|
|
14254
|
+
}
|
|
13446
14255
|
function brokerRelayCommand() {
|
|
13447
|
-
return `
|
|
14256
|
+
return `bun ${JSON.stringify(scoutCliPath())}`;
|
|
13448
14257
|
}
|
|
13449
14258
|
function titleCaseLocalAgentName(value) {
|
|
13450
14259
|
return value.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
|
|
@@ -13457,7 +14266,7 @@ function resolveScoutSkillPath() {
|
|
|
13457
14266
|
join15(process.env.HOME ?? "", ".agents", "skills", "relay-agent-comms", "SKILL.md")
|
|
13458
14267
|
];
|
|
13459
14268
|
for (const path2 of candidatePaths) {
|
|
13460
|
-
if (
|
|
14269
|
+
if (existsSync13(path2)) {
|
|
13461
14270
|
return path2;
|
|
13462
14271
|
}
|
|
13463
14272
|
}
|
|
@@ -13653,7 +14462,7 @@ function buildLegacyBrokerBackedRelayPrompt(agentId, projectName, projectPath, r
|
|
|
13653
14462
|
function legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath) {
|
|
13654
14463
|
const relayHub = resolveRelayHub();
|
|
13655
14464
|
const brokerUrl = resolveBrokerUrl();
|
|
13656
|
-
const relayCommandBases = ["openscout relay", brokerRelayCommand()];
|
|
14465
|
+
const relayCommandBases = ["openscout relay", brokerRelayCommand(), legacyNodeBrokerRelayCommand()];
|
|
13657
14466
|
const projectPathCandidates = projectPath.endsWith("/") ? [projectPath, projectPath.slice(0, -1)] : [projectPath, `${projectPath}/`];
|
|
13658
14467
|
const candidates = new Set;
|
|
13659
14468
|
for (const pathCandidate of projectPathCandidates) {
|
|
@@ -13664,12 +14473,28 @@ function legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPat
|
|
|
13664
14473
|
}
|
|
13665
14474
|
return Array.from(candidates);
|
|
13666
14475
|
}
|
|
14476
|
+
function generatedLocalAgentSystemPromptCandidates(agentId, projectName, projectPath) {
|
|
14477
|
+
const baseContext = buildLocalAgentTemplateContext(agentId, projectName, projectPath);
|
|
14478
|
+
const relayCommands = [brokerRelayCommand(), legacyNodeBrokerRelayCommand()];
|
|
14479
|
+
const transportModes = [undefined, "codex_app_server", "claude_stream_json"];
|
|
14480
|
+
const candidates = new Set;
|
|
14481
|
+
for (const relayCommand of relayCommands) {
|
|
14482
|
+
const context = {
|
|
14483
|
+
...baseContext,
|
|
14484
|
+
relayCommand
|
|
14485
|
+
};
|
|
14486
|
+
for (const transport of transportModes) {
|
|
14487
|
+
candidates.add(renderLocalAgentSystemPromptTemplate(buildLocalAgentSystemPromptTemplate(), context, transport ? { transport } : {}));
|
|
14488
|
+
}
|
|
14489
|
+
}
|
|
14490
|
+
return Array.from(candidates);
|
|
14491
|
+
}
|
|
13667
14492
|
function normalizeLocalAgentSystemPrompt(agentId, projectName, projectPath, systemPrompt) {
|
|
13668
14493
|
const trimmed = systemPrompt?.trim();
|
|
13669
14494
|
if (!trimmed) {
|
|
13670
14495
|
return;
|
|
13671
14496
|
}
|
|
13672
|
-
if (legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed)) {
|
|
14497
|
+
if (legacyLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed) || generatedLocalAgentSystemPromptCandidates(agentId, projectName, projectPath).includes(trimmed)) {
|
|
13673
14498
|
return;
|
|
13674
14499
|
}
|
|
13675
14500
|
return trimmed;
|
|
@@ -13749,6 +14574,128 @@ function normalizeLocalAgentCapabilities(value) {
|
|
|
13749
14574
|
function normalizeLocalAgentLaunchArgs(value) {
|
|
13750
14575
|
return Array.isArray(value) ? value.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
13751
14576
|
}
|
|
14577
|
+
function normalizeRequestedModel(value) {
|
|
14578
|
+
const trimmed = value?.trim();
|
|
14579
|
+
return trimmed ? trimmed : undefined;
|
|
14580
|
+
}
|
|
14581
|
+
function readClaudeLaunchModel(launchArgs) {
|
|
14582
|
+
for (let index = 0;index < launchArgs.length; index += 1) {
|
|
14583
|
+
const current = launchArgs[index] ?? "";
|
|
14584
|
+
if (current === "--model") {
|
|
14585
|
+
const next = launchArgs[index + 1]?.trim();
|
|
14586
|
+
return next || undefined;
|
|
14587
|
+
}
|
|
14588
|
+
if (current.startsWith("--model=")) {
|
|
14589
|
+
const next = current.slice("--model=".length).trim();
|
|
14590
|
+
return next || undefined;
|
|
14591
|
+
}
|
|
14592
|
+
}
|
|
14593
|
+
return;
|
|
14594
|
+
}
|
|
14595
|
+
function normalizeLaunchArgsForHarness(harness, value) {
|
|
14596
|
+
const normalized = normalizeLocalAgentLaunchArgs(value);
|
|
14597
|
+
if (harness === "codex") {
|
|
14598
|
+
return normalizeCodexAppServerLaunchArgs(normalized);
|
|
14599
|
+
}
|
|
14600
|
+
return normalized;
|
|
14601
|
+
}
|
|
14602
|
+
function readLaunchModelForHarness(harness, launchArgs) {
|
|
14603
|
+
if (harness === "codex") {
|
|
14604
|
+
return readCodexAppServerModelFromLaunchArgs(launchArgs) ?? undefined;
|
|
14605
|
+
}
|
|
14606
|
+
if (harness === "claude") {
|
|
14607
|
+
return readClaudeLaunchModel(launchArgs ?? []);
|
|
14608
|
+
}
|
|
14609
|
+
return;
|
|
14610
|
+
}
|
|
14611
|
+
function stripLaunchModelForHarness(harness, launchArgs) {
|
|
14612
|
+
if (harness === "codex") {
|
|
14613
|
+
const next = [];
|
|
14614
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
14615
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
14616
|
+
const current = normalized[index] ?? "";
|
|
14617
|
+
if (current === "-c" || current === "--config") {
|
|
14618
|
+
const value = normalized[index + 1];
|
|
14619
|
+
if (readCodexAppServerModelFromLaunchArgs(value ? [current, value] : [current])) {
|
|
14620
|
+
index += 1;
|
|
14621
|
+
continue;
|
|
14622
|
+
}
|
|
14623
|
+
next.push(current);
|
|
14624
|
+
if (value) {
|
|
14625
|
+
next.push(value);
|
|
14626
|
+
index += 1;
|
|
14627
|
+
}
|
|
14628
|
+
continue;
|
|
14629
|
+
}
|
|
14630
|
+
if (current.startsWith("--config=")) {
|
|
14631
|
+
if (readCodexAppServerModelFromLaunchArgs([current])) {
|
|
14632
|
+
continue;
|
|
14633
|
+
}
|
|
14634
|
+
}
|
|
14635
|
+
next.push(current);
|
|
14636
|
+
}
|
|
14637
|
+
return next;
|
|
14638
|
+
}
|
|
14639
|
+
if (harness === "claude") {
|
|
14640
|
+
const next = [];
|
|
14641
|
+
const normalized = normalizeLocalAgentLaunchArgs(launchArgs);
|
|
14642
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
14643
|
+
const current = normalized[index] ?? "";
|
|
14644
|
+
if (current === "--model") {
|
|
14645
|
+
index += 1;
|
|
14646
|
+
continue;
|
|
14647
|
+
}
|
|
14648
|
+
if (current.startsWith("--model=")) {
|
|
14649
|
+
continue;
|
|
14650
|
+
}
|
|
14651
|
+
next.push(current);
|
|
14652
|
+
}
|
|
14653
|
+
return next;
|
|
14654
|
+
}
|
|
14655
|
+
return normalizeLocalAgentLaunchArgs(launchArgs);
|
|
14656
|
+
}
|
|
14657
|
+
function buildLaunchArgsForRequestedModel(harness, model) {
|
|
14658
|
+
if (harness === "codex") {
|
|
14659
|
+
return normalizeCodexAppServerLaunchArgs(["--model", model]);
|
|
14660
|
+
}
|
|
14661
|
+
if (harness === "claude") {
|
|
14662
|
+
return ["--model", model];
|
|
14663
|
+
}
|
|
14664
|
+
return [];
|
|
14665
|
+
}
|
|
14666
|
+
function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
14667
|
+
const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
|
|
14668
|
+
const requestedModel = normalizeRequestedModel(model);
|
|
14669
|
+
if (!requestedModel) {
|
|
14670
|
+
return normalized;
|
|
14671
|
+
}
|
|
14672
|
+
return [
|
|
14673
|
+
...stripLaunchModelForHarness(harness, normalized),
|
|
14674
|
+
...buildLaunchArgsForRequestedModel(harness, requestedModel)
|
|
14675
|
+
];
|
|
14676
|
+
}
|
|
14677
|
+
function defaultHarnessForOverride(override, fallback = "claude") {
|
|
14678
|
+
return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
|
|
14679
|
+
}
|
|
14680
|
+
function launchArgsForOverrideHarness(override, harness) {
|
|
14681
|
+
const profileLaunchArgs = override.harnessProfiles?.[harness]?.launchArgs;
|
|
14682
|
+
if (profileLaunchArgs) {
|
|
14683
|
+
return normalizeLaunchArgsForHarness(harness, profileLaunchArgs);
|
|
14684
|
+
}
|
|
14685
|
+
if (harness === defaultHarnessForOverride(override, harness)) {
|
|
14686
|
+
return normalizeLaunchArgsForHarness(harness, override.launchArgs);
|
|
14687
|
+
}
|
|
14688
|
+
return [];
|
|
14689
|
+
}
|
|
14690
|
+
function overrideHarnessProfile(override, harness) {
|
|
14691
|
+
const profile = override.harnessProfiles?.[harness];
|
|
14692
|
+
return {
|
|
14693
|
+
cwd: normalizeProjectPath(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
|
|
14694
|
+
transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
|
|
14695
|
+
sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
|
|
14696
|
+
launchArgs: launchArgsForOverrideHarness(override, harness)
|
|
14697
|
+
};
|
|
14698
|
+
}
|
|
13752
14699
|
function normalizeManagedHarness2(value, fallback) {
|
|
13753
14700
|
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
13754
14701
|
}
|
|
@@ -13764,7 +14711,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
13764
14711
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
13765
14712
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
13766
14713
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
13767
|
-
launchArgs:
|
|
14714
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
13768
14715
|
};
|
|
13769
14716
|
}
|
|
13770
14717
|
const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
|
|
@@ -13773,7 +14720,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
13773
14720
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
13774
14721
|
transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
|
|
13775
14722
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
|
|
13776
|
-
launchArgs:
|
|
14723
|
+
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
|
|
13777
14724
|
};
|
|
13778
14725
|
}
|
|
13779
14726
|
if (!nextProfiles[defaultHarness]) {
|
|
@@ -13781,7 +14728,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
13781
14728
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
13782
14729
|
transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
|
|
13783
14730
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
|
|
13784
|
-
launchArgs:
|
|
14731
|
+
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
|
|
13785
14732
|
};
|
|
13786
14733
|
}
|
|
13787
14734
|
for (const harness of ["claude", "codex"]) {
|
|
@@ -13793,7 +14740,7 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
13793
14740
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
13794
14741
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
13795
14742
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
13796
|
-
launchArgs:
|
|
14743
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
13797
14744
|
};
|
|
13798
14745
|
}
|
|
13799
14746
|
return nextProfiles;
|
|
@@ -13817,14 +14764,14 @@ function recordForHarness(record, harnessOverride) {
|
|
|
13817
14764
|
tmuxSession: fallbackSessionId,
|
|
13818
14765
|
cwd: fallbackCwd,
|
|
13819
14766
|
transport: fallbackTransport,
|
|
13820
|
-
launchArgs:
|
|
14767
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
13821
14768
|
harnessProfiles: {
|
|
13822
14769
|
...normalized.harnessProfiles,
|
|
13823
14770
|
[selectedHarness]: {
|
|
13824
14771
|
cwd: fallbackCwd,
|
|
13825
14772
|
transport: fallbackTransport,
|
|
13826
14773
|
sessionId: fallbackSessionId,
|
|
13827
|
-
launchArgs:
|
|
14774
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
|
|
13828
14775
|
}
|
|
13829
14776
|
}
|
|
13830
14777
|
};
|
|
@@ -13866,7 +14813,7 @@ function normalizeLocalAgentRecord(agentId, record) {
|
|
|
13866
14813
|
harnessProfiles,
|
|
13867
14814
|
transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
|
|
13868
14815
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
13869
|
-
launchArgs: activeProfile?.launchArgs ??
|
|
14816
|
+
launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs)
|
|
13870
14817
|
};
|
|
13871
14818
|
}
|
|
13872
14819
|
function localAgentStatusFromRecord(agentId, record, source) {
|
|
@@ -13935,7 +14882,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
|
|
|
13935
14882
|
source: existing?.source && existing.source !== "inferred" ? existing.source : "manual",
|
|
13936
14883
|
startedAt: normalizedRecord.startedAt,
|
|
13937
14884
|
systemPrompt: normalizedRecord.systemPrompt,
|
|
13938
|
-
launchArgs:
|
|
14885
|
+
launchArgs: normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs),
|
|
13939
14886
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
13940
14887
|
defaultHarness: normalizeManagedHarness2(normalizedRecord.defaultHarness, "claude"),
|
|
13941
14888
|
harnessProfiles: normalizedRecord.harnessProfiles,
|
|
@@ -13956,7 +14903,7 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
13956
14903
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
|
|
13957
14904
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
13958
14905
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
13959
|
-
launchArgs:
|
|
14906
|
+
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
|
|
13960
14907
|
};
|
|
13961
14908
|
}
|
|
13962
14909
|
function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
@@ -13967,7 +14914,7 @@ function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
13967
14914
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "claude_stream_json" }),
|
|
13968
14915
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
13969
14916
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
13970
|
-
launchArgs:
|
|
14917
|
+
launchArgs: normalizeLaunchArgsForHarness("claude", record.launchArgs)
|
|
13971
14918
|
};
|
|
13972
14919
|
}
|
|
13973
14920
|
function isLocalAgentRecordOnline(agentName, record) {
|
|
@@ -14001,7 +14948,7 @@ function buildLocalAgentBootstrapPrompt(_harness, _systemPrompt, initialMessage)
|
|
|
14001
14948
|
return initialMessage;
|
|
14002
14949
|
}
|
|
14003
14950
|
function buildLocalAgentLaunchCommand(agentName, record, projectPath, promptFile, workerScript) {
|
|
14004
|
-
const extraArgs = shellQuoteArguments(
|
|
14951
|
+
const extraArgs = shellQuoteArguments(normalizeLaunchArgsForHarness(normalizeLocalAgentHarness(record.harness), record.launchArgs));
|
|
14005
14952
|
if (normalizeLocalAgentHarness(record.harness) === "codex") {
|
|
14006
14953
|
return `exec bash ${JSON.stringify(workerScript ?? join15(relayAgentRuntimeDirectory(agentName), "codex-worker.sh"))}`;
|
|
14007
14954
|
}
|
|
@@ -14157,7 +15104,7 @@ async function ensureLocalAgentOnline(agentName, record) {
|
|
|
14157
15104
|
await new Promise((resolve6) => setTimeout(resolve6, 100));
|
|
14158
15105
|
}
|
|
14159
15106
|
if (!isLocalAgentSessionAlive(normalizedRecord.tmuxSession)) {
|
|
14160
|
-
const stderrTail =
|
|
15107
|
+
const stderrTail = existsSync13(stderrLogFile) ? readFileSync8(stderrLogFile, "utf8").trim().split(/\r?\n/).slice(-10).join(`
|
|
14161
15108
|
`).trim() : "";
|
|
14162
15109
|
throw new Error(stderrTail ? `Relay agent ${agentName} failed to stay online:
|
|
14163
15110
|
${stderrTail}` : `Relay agent ${agentName} failed to stay online.`);
|
|
@@ -14267,6 +15214,7 @@ async function startLocalAgent(input) {
|
|
|
14267
15214
|
const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
|
|
14268
15215
|
const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
|
|
14269
15216
|
const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
|
|
15217
|
+
const launchArgs = applyRequestedModelToLaunchArgs(effectiveHarness, [], input.model);
|
|
14270
15218
|
const existingForInstance = overrides[instance.id];
|
|
14271
15219
|
if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
|
|
14272
15220
|
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.`);
|
|
@@ -14280,13 +15228,14 @@ async function startLocalAgent(input) {
|
|
|
14280
15228
|
projectConfigPath: coldProjectConfigPath,
|
|
14281
15229
|
source: "manual",
|
|
14282
15230
|
startedAt: nowSeconds2(),
|
|
15231
|
+
launchArgs,
|
|
14283
15232
|
defaultHarness: effectiveHarness,
|
|
14284
15233
|
harnessProfiles: {
|
|
14285
15234
|
[effectiveHarness]: {
|
|
14286
15235
|
cwd: effectiveCwd ?? projectRoot,
|
|
14287
15236
|
transport,
|
|
14288
15237
|
sessionId,
|
|
14289
|
-
launchArgs
|
|
15238
|
+
launchArgs
|
|
14290
15239
|
}
|
|
14291
15240
|
},
|
|
14292
15241
|
runtime: {
|
|
@@ -14307,6 +15256,9 @@ async function startLocalAgent(input) {
|
|
|
14307
15256
|
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.`);
|
|
14308
15257
|
}
|
|
14309
15258
|
const resolvedHarness = preferredHarness ?? matchingOverride.runtime?.harness;
|
|
15259
|
+
const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
|
|
15260
|
+
const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
|
|
15261
|
+
const nextLaunchArgs = applyRequestedModelToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), input.model);
|
|
14310
15262
|
overrides[instance.id] = {
|
|
14311
15263
|
agentId: instance.id,
|
|
14312
15264
|
definitionId: requestedDefinitionId,
|
|
@@ -14317,10 +15269,16 @@ async function startLocalAgent(input) {
|
|
|
14317
15269
|
source: "manual",
|
|
14318
15270
|
startedAt: matchingOverride.startedAt ?? nowSeconds2(),
|
|
14319
15271
|
systemPrompt: matchingOverride.systemPrompt,
|
|
14320
|
-
launchArgs: matchingOverride.launchArgs,
|
|
15272
|
+
launchArgs: nextHarness === nextDefaultHarness ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
14321
15273
|
capabilities: matchingOverride.capabilities,
|
|
14322
|
-
defaultHarness:
|
|
14323
|
-
harnessProfiles:
|
|
15274
|
+
defaultHarness: nextDefaultHarness,
|
|
15275
|
+
harnessProfiles: {
|
|
15276
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
15277
|
+
[nextHarness]: {
|
|
15278
|
+
...overrideHarnessProfile(matchingOverride, nextHarness),
|
|
15279
|
+
launchArgs: nextLaunchArgs
|
|
15280
|
+
}
|
|
15281
|
+
},
|
|
14324
15282
|
runtime: {
|
|
14325
15283
|
cwd: effectiveCwd ?? matchingOverride.runtime?.cwd ?? matchingProjectRoot,
|
|
14326
15284
|
harness: resolvedHarness,
|
|
@@ -14332,6 +15290,22 @@ async function startLocalAgent(input) {
|
|
|
14332
15290
|
await writeRelayAgentOverrides(overrides);
|
|
14333
15291
|
targetAgentId = instance.id;
|
|
14334
15292
|
} else {
|
|
15293
|
+
if (input.model) {
|
|
15294
|
+
const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
|
|
15295
|
+
const nextLaunchArgs = applyRequestedModelToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), input.model);
|
|
15296
|
+
overrides[matchingAgentId] = {
|
|
15297
|
+
...matchingOverride,
|
|
15298
|
+
launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
15299
|
+
harnessProfiles: {
|
|
15300
|
+
...matchingOverride.harnessProfiles ?? {},
|
|
15301
|
+
[existingHarness]: {
|
|
15302
|
+
...overrideHarnessProfile(matchingOverride, existingHarness),
|
|
15303
|
+
launchArgs: nextLaunchArgs
|
|
15304
|
+
}
|
|
15305
|
+
}
|
|
15306
|
+
};
|
|
15307
|
+
await writeRelayAgentOverrides(overrides);
|
|
15308
|
+
}
|
|
14335
15309
|
targetAgentId = matchingAgentId;
|
|
14336
15310
|
}
|
|
14337
15311
|
await ensureLocalAgentBindingOnline(targetAgentId, process.env.OPENSCOUT_NODE_ID ?? "local", {
|
|
@@ -14402,13 +15376,38 @@ async function interruptLocalAgent(agentId) {
|
|
|
14402
15376
|
return { ok: false, agentId };
|
|
14403
15377
|
}
|
|
14404
15378
|
}
|
|
15379
|
+
function readPersistedClaudeSessionId(agentName) {
|
|
15380
|
+
try {
|
|
15381
|
+
const runtimeDir = relayAgentRuntimeDirectory(agentName);
|
|
15382
|
+
const catalogPath = join15(runtimeDir, "session-catalog.json");
|
|
15383
|
+
if (existsSync13(catalogPath)) {
|
|
15384
|
+
const raw = readFileSync8(catalogPath, "utf8").trim();
|
|
15385
|
+
if (raw) {
|
|
15386
|
+
const catalog = JSON.parse(raw);
|
|
15387
|
+
if (typeof catalog.activeSessionId === "string" && catalog.activeSessionId.trim()) {
|
|
15388
|
+
return catalog.activeSessionId.trim();
|
|
15389
|
+
}
|
|
15390
|
+
}
|
|
15391
|
+
}
|
|
15392
|
+
const legacyPath = join15(runtimeDir, "claude-session-id.txt");
|
|
15393
|
+
if (existsSync13(legacyPath)) {
|
|
15394
|
+
const value = readFileSync8(legacyPath, "utf8").trim();
|
|
15395
|
+
return value || null;
|
|
15396
|
+
}
|
|
15397
|
+
return null;
|
|
15398
|
+
} catch {
|
|
15399
|
+
return null;
|
|
15400
|
+
}
|
|
15401
|
+
}
|
|
14405
15402
|
function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
14406
15403
|
const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
|
|
15404
|
+
const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
|
|
14407
15405
|
const definitionId = normalizedRecord.definitionId ?? agentId;
|
|
14408
15406
|
const displayName = titleCaseLocalAgentName(definitionId);
|
|
14409
15407
|
const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
|
|
14410
15408
|
const instance = buildRelayAgentInstance(definitionId, projectRoot);
|
|
14411
15409
|
const actorId = instance.id;
|
|
15410
|
+
const externalSessionId = normalizedRecord.transport === "claude_stream_json" ? readPersistedClaudeSessionId(definitionId) : null;
|
|
14412
15411
|
return {
|
|
14413
15412
|
actor: {
|
|
14414
15413
|
id: actorId,
|
|
@@ -14427,7 +15426,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
14427
15426
|
defaultSelector: instance.defaultSelector,
|
|
14428
15427
|
nodeQualifier: instance.nodeQualifier,
|
|
14429
15428
|
workspaceQualifier: instance.workspaceQualifier,
|
|
14430
|
-
branch: instance.branch
|
|
15429
|
+
branch: instance.branch,
|
|
15430
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
14431
15431
|
}
|
|
14432
15432
|
},
|
|
14433
15433
|
agent: {
|
|
@@ -14454,7 +15454,8 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
14454
15454
|
defaultSelector: instance.defaultSelector,
|
|
14455
15455
|
nodeQualifier: instance.nodeQualifier,
|
|
14456
15456
|
workspaceQualifier: instance.workspaceQualifier,
|
|
14457
|
-
branch: instance.branch
|
|
15457
|
+
branch: instance.branch,
|
|
15458
|
+
...configuredModel ? { model: configuredModel } : {}
|
|
14458
15459
|
},
|
|
14459
15460
|
agentClass: "general",
|
|
14460
15461
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
@@ -14486,7 +15487,9 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
14486
15487
|
selector: instance.selector,
|
|
14487
15488
|
nodeQualifier: instance.nodeQualifier,
|
|
14488
15489
|
workspaceQualifier: instance.workspaceQualifier,
|
|
14489
|
-
branch: instance.branch
|
|
15490
|
+
branch: instance.branch,
|
|
15491
|
+
...configuredModel ? { model: configuredModel } : {},
|
|
15492
|
+
...externalSessionId ? { externalSessionId } : {}
|
|
14490
15493
|
}
|
|
14491
15494
|
}
|
|
14492
15495
|
};
|
|
@@ -15811,7 +16814,7 @@ async function deriveNewAgentName(projectName, branch, harness) {
|
|
|
15811
16814
|
async function createGitWorktree(projectRoot, agentName) {
|
|
15812
16815
|
const { execSync: execSync3 } = await import("child_process");
|
|
15813
16816
|
const { join: join17 } = await import("path");
|
|
15814
|
-
const { mkdirSync: mkdirSync9, existsSync:
|
|
16817
|
+
const { mkdirSync: mkdirSync9, existsSync: existsSync14 } = await import("fs");
|
|
15815
16818
|
try {
|
|
15816
16819
|
execSync3("git rev-parse --git-dir", { cwd: projectRoot, stdio: "pipe" });
|
|
15817
16820
|
} catch {
|
|
@@ -15820,7 +16823,7 @@ async function createGitWorktree(projectRoot, agentName) {
|
|
|
15820
16823
|
const branchName = `scout/${agentName}`;
|
|
15821
16824
|
const worktreeDir = join17(projectRoot, ".scout-worktrees");
|
|
15822
16825
|
const worktreePath = join17(worktreeDir, agentName);
|
|
15823
|
-
if (
|
|
16826
|
+
if (existsSync14(worktreePath)) {
|
|
15824
16827
|
return { path: worktreePath, branch: branchName };
|
|
15825
16828
|
}
|
|
15826
16829
|
mkdirSync9(worktreeDir, { recursive: true });
|
|
@@ -15862,6 +16865,20 @@ function readSyncStatus(bridge, sessionId) {
|
|
|
15862
16865
|
oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
|
|
15863
16866
|
};
|
|
15864
16867
|
}
|
|
16868
|
+
function resolveSyncSessionId(bridge, preferredSessionId) {
|
|
16869
|
+
if (preferredSessionId) {
|
|
16870
|
+
return preferredSessionId;
|
|
16871
|
+
}
|
|
16872
|
+
let latestSessionId = null;
|
|
16873
|
+
let latestActivityAt = Number.NEGATIVE_INFINITY;
|
|
16874
|
+
for (const session of bridge.getSessionSummaries()) {
|
|
16875
|
+
if (session.lastActivityAt > latestActivityAt) {
|
|
16876
|
+
latestActivityAt = session.lastActivityAt;
|
|
16877
|
+
latestSessionId = session.sessionId;
|
|
16878
|
+
}
|
|
16879
|
+
}
|
|
16880
|
+
return latestSessionId;
|
|
16881
|
+
}
|
|
15865
16882
|
function sessionRegistryErrorResponse(reqId, error) {
|
|
15866
16883
|
if (!isSessionRegistryError(error)) {
|
|
15867
16884
|
return null;
|
|
@@ -15931,23 +16948,36 @@ async function handleRPCInner(bridge, req, deviceId) {
|
|
|
15931
16948
|
return { id: req.id, result: { ok: true } };
|
|
15932
16949
|
}
|
|
15933
16950
|
case "sync/replay": {
|
|
15934
|
-
const p = req.params;
|
|
15935
|
-
if (
|
|
15936
|
-
return { id: req.id, error: { code: -32602, message: "
|
|
16951
|
+
const p = req.params ?? {};
|
|
16952
|
+
if (typeof p.lastSeq !== "number") {
|
|
16953
|
+
return { id: req.id, error: { code: -32602, message: "lastSeq is required" } };
|
|
15937
16954
|
}
|
|
15938
|
-
const
|
|
16955
|
+
const sessionId = resolveSyncSessionId(bridge, p.sessionId);
|
|
16956
|
+
if (!sessionId) {
|
|
16957
|
+
return { id: req.id, result: { events: [] } };
|
|
16958
|
+
}
|
|
16959
|
+
const events2 = replaySyncEvents(bridge, sessionId, p.lastSeq);
|
|
15939
16960
|
return { id: req.id, result: { events: events2 } };
|
|
15940
16961
|
}
|
|
15941
16962
|
case "sync/status": {
|
|
15942
16963
|
const p = req.params ?? {};
|
|
15943
|
-
|
|
15944
|
-
|
|
16964
|
+
const sessionCount = bridge.listSessions().length;
|
|
16965
|
+
const sessionId = resolveSyncSessionId(bridge, p.sessionId);
|
|
16966
|
+
if (!sessionId) {
|
|
16967
|
+
return {
|
|
16968
|
+
id: req.id,
|
|
16969
|
+
result: {
|
|
16970
|
+
currentSeq: 0,
|
|
16971
|
+
oldestBufferedSeq: 0,
|
|
16972
|
+
sessionCount
|
|
16973
|
+
}
|
|
16974
|
+
};
|
|
15945
16975
|
}
|
|
15946
16976
|
return {
|
|
15947
16977
|
id: req.id,
|
|
15948
16978
|
result: {
|
|
15949
|
-
...readSyncStatus(bridge,
|
|
15950
|
-
sessionCount
|
|
16979
|
+
...readSyncStatus(bridge, sessionId),
|
|
16980
|
+
sessionCount
|
|
15951
16981
|
}
|
|
15952
16982
|
};
|
|
15953
16983
|
}
|
|
@@ -16188,7 +17218,7 @@ async function handleRPCInner(bridge, req, deviceId) {
|
|
|
16188
17218
|
return { id: req.id, error: { code: -32000, message: "Only .jsonl files can be read" } };
|
|
16189
17219
|
}
|
|
16190
17220
|
try {
|
|
16191
|
-
const content =
|
|
17221
|
+
const content = readFileSync9(p.path, "utf-8");
|
|
16192
17222
|
const lines = content.split(`
|
|
16193
17223
|
`).filter((l) => l.trim().length > 0);
|
|
16194
17224
|
const trimmed = lines.length > 500 ? lines.slice(-500) : lines;
|
|
@@ -33081,7 +34111,7 @@ class ThreadEventPlane {
|
|
|
33081
34111
|
}
|
|
33082
34112
|
// ../runtime/src/mobile-push.ts
|
|
33083
34113
|
import { Database as Database2 } from "bun:sqlite";
|
|
33084
|
-
import { mkdirSync as mkdirSync9, readFileSync as
|
|
34114
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync10 } from "fs";
|
|
33085
34115
|
import { connect as connectHttp2 } from "http2";
|
|
33086
34116
|
import { createPrivateKey, sign as signWithKey } from "crypto";
|
|
33087
34117
|
import { dirname as dirname7, join as join18 } from "path";
|
|
@@ -33278,7 +34308,7 @@ function loadApnsCredentials() {
|
|
|
33278
34308
|
privateKeyPem = Buffer.from(inlineBase64, "base64").toString("utf8");
|
|
33279
34309
|
}
|
|
33280
34310
|
if (!privateKeyPem && path2) {
|
|
33281
|
-
privateKeyPem =
|
|
34311
|
+
privateKeyPem = readFileSync10(path2, "utf8");
|
|
33282
34312
|
}
|
|
33283
34313
|
if (!teamId || !keyId || !privateKeyPem) {
|
|
33284
34314
|
return null;
|
|
@@ -33452,7 +34482,7 @@ async function broadcastApnsAlertToActiveMobileDevices(alert) {
|
|
|
33452
34482
|
};
|
|
33453
34483
|
}
|
|
33454
34484
|
// ../../apps/desktop/src/core/pairing/runtime/bridge/router.ts
|
|
33455
|
-
import { readFileSync as
|
|
34485
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync4, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
33456
34486
|
import { execSync as execSync4 } from "child_process";
|
|
33457
34487
|
import { basename as basename7, isAbsolute as isAbsolute4, join as join19, relative as relative3 } from "path";
|
|
33458
34488
|
import { homedir as homedir16 } from "os";
|
|
@@ -33935,6 +34965,64 @@ var mobileRouter = t.router({
|
|
|
33935
34965
|
limit: typeof input.limit === "number" ? input.limit : null
|
|
33936
34966
|
}, resolveMobileCurrentDirectory2());
|
|
33937
34967
|
}),
|
|
34968
|
+
webHandoff: procedure.input(exports_external.object({
|
|
34969
|
+
kind: exports_external.enum(["session", "file_change"]),
|
|
34970
|
+
sessionId: exports_external.string(),
|
|
34971
|
+
turnId: exports_external.string().optional(),
|
|
34972
|
+
blockId: exports_external.string().optional()
|
|
34973
|
+
})).mutation(({ input, ctx }) => {
|
|
34974
|
+
if (!ctx.deviceId) {
|
|
34975
|
+
throw new TRPCError({
|
|
34976
|
+
code: "UNAUTHORIZED",
|
|
34977
|
+
message: "Secure web handoff requires a paired mobile device"
|
|
34978
|
+
});
|
|
34979
|
+
}
|
|
34980
|
+
const snapshot = ctx.bridge.getSessionSnapshot(input.sessionId);
|
|
34981
|
+
if (!snapshot) {
|
|
34982
|
+
throw new TRPCError({
|
|
34983
|
+
code: "NOT_FOUND",
|
|
34984
|
+
message: `No session: ${input.sessionId}`
|
|
34985
|
+
});
|
|
34986
|
+
}
|
|
34987
|
+
let scope;
|
|
34988
|
+
let title = snapshot.session.name || snapshot.session.id;
|
|
34989
|
+
if (input.kind === "file_change") {
|
|
34990
|
+
if (!input.turnId || !input.blockId) {
|
|
34991
|
+
throw new TRPCError({
|
|
34992
|
+
code: "BAD_REQUEST",
|
|
34993
|
+
message: "turnId and blockId are required for file_change handoffs"
|
|
34994
|
+
});
|
|
34995
|
+
}
|
|
34996
|
+
const turn = snapshot.turns.find((candidate) => candidate.id === input.turnId);
|
|
34997
|
+
const block = turn?.blocks.find((candidate) => candidate.block.id === input.blockId)?.block;
|
|
34998
|
+
if (!turn || !block || block.type !== "action" || block.action.kind !== "file_change") {
|
|
34999
|
+
throw new TRPCError({
|
|
35000
|
+
code: "NOT_FOUND",
|
|
35001
|
+
message: "File change block not found"
|
|
35002
|
+
});
|
|
35003
|
+
}
|
|
35004
|
+
scope = {
|
|
35005
|
+
kind: "file_change",
|
|
35006
|
+
sessionId: input.sessionId,
|
|
35007
|
+
turnId: input.turnId,
|
|
35008
|
+
blockId: input.blockId
|
|
35009
|
+
};
|
|
35010
|
+
title = block.action.path || title;
|
|
35011
|
+
} else {
|
|
35012
|
+
scope = {
|
|
35013
|
+
kind: "session",
|
|
35014
|
+
sessionId: input.sessionId
|
|
35015
|
+
};
|
|
35016
|
+
}
|
|
35017
|
+
const issued = issueWebHandoff(scope, ctx.deviceId);
|
|
35018
|
+
return {
|
|
35019
|
+
kind: input.kind,
|
|
35020
|
+
path: pathForWebHandoffScope(scope),
|
|
35021
|
+
token: issued.token,
|
|
35022
|
+
expiresAt: issued.expiresAt,
|
|
35023
|
+
title
|
|
35024
|
+
};
|
|
35025
|
+
}),
|
|
33938
35026
|
createSession: procedure.input(exports_external.object({
|
|
33939
35027
|
workspaceId: exports_external.string(),
|
|
33940
35028
|
harness: exports_external.string().optional(),
|
|
@@ -34110,7 +35198,7 @@ var historyRouter = t.router({
|
|
|
34110
35198
|
});
|
|
34111
35199
|
}
|
|
34112
35200
|
try {
|
|
34113
|
-
const content =
|
|
35201
|
+
const content = readFileSync11(input.path, "utf-8");
|
|
34114
35202
|
const lines = content.split(`
|
|
34115
35203
|
`).filter((l) => l.trim().length > 0);
|
|
34116
35204
|
const trimmed = lines.length > 500 ? lines.slice(-500) : lines;
|
|
@@ -34148,15 +35236,42 @@ function readSyncStatus2(bridge, sessionId) {
|
|
|
34148
35236
|
oldestBufferedSeq: bridge.oldestBufferedSeq(sessionId)
|
|
34149
35237
|
};
|
|
34150
35238
|
}
|
|
35239
|
+
function resolveSyncSessionId2(bridge, preferredSessionId) {
|
|
35240
|
+
if (preferredSessionId) {
|
|
35241
|
+
return preferredSessionId;
|
|
35242
|
+
}
|
|
35243
|
+
let latestSessionId = null;
|
|
35244
|
+
let latestActivityAt = Number.NEGATIVE_INFINITY;
|
|
35245
|
+
for (const session of bridge.getSessionSummaries()) {
|
|
35246
|
+
if (session.lastActivityAt > latestActivityAt) {
|
|
35247
|
+
latestActivityAt = session.lastActivityAt;
|
|
35248
|
+
latestSessionId = session.sessionId;
|
|
35249
|
+
}
|
|
35250
|
+
}
|
|
35251
|
+
return latestSessionId;
|
|
35252
|
+
}
|
|
34151
35253
|
var syncRouter = t.router({
|
|
34152
|
-
replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string() })).query(({ input, ctx }) => {
|
|
34153
|
-
const
|
|
35254
|
+
replay: procedure.input(exports_external.object({ lastSeq: exports_external.number(), sessionId: exports_external.string().optional() })).query(({ input, ctx }) => {
|
|
35255
|
+
const sessionId = resolveSyncSessionId2(ctx.bridge, input.sessionId);
|
|
35256
|
+
if (!sessionId) {
|
|
35257
|
+
return { events: [] };
|
|
35258
|
+
}
|
|
35259
|
+
const events2 = replaySyncEvents2(ctx.bridge, sessionId, input.lastSeq);
|
|
34154
35260
|
return { events: events2 };
|
|
34155
35261
|
}),
|
|
34156
|
-
status: procedure.input(exports_external.object({ sessionId: exports_external.string() })).query(({ input, ctx }) => {
|
|
35262
|
+
status: procedure.input(exports_external.object({ sessionId: exports_external.string().optional() }).optional()).query(({ input, ctx }) => {
|
|
35263
|
+
const sessionCount = ctx.bridge.listSessions().length;
|
|
35264
|
+
const sessionId = resolveSyncSessionId2(ctx.bridge, input?.sessionId);
|
|
35265
|
+
if (!sessionId) {
|
|
35266
|
+
return {
|
|
35267
|
+
currentSeq: 0,
|
|
35268
|
+
oldestBufferedSeq: 0,
|
|
35269
|
+
sessionCount
|
|
35270
|
+
};
|
|
35271
|
+
}
|
|
34157
35272
|
return {
|
|
34158
|
-
...readSyncStatus2(ctx.bridge,
|
|
34159
|
-
sessionCount
|
|
35273
|
+
...readSyncStatus2(ctx.bridge, sessionId),
|
|
35274
|
+
sessionCount
|
|
34160
35275
|
};
|
|
34161
35276
|
})
|
|
34162
35277
|
});
|
|
@@ -38658,7 +39773,7 @@ async function startPairingRuntime(options) {
|
|
|
38658
39773
|
secure: config2.secure,
|
|
38659
39774
|
identity: config2.secure ? identity2 : undefined
|
|
38660
39775
|
});
|
|
38661
|
-
const fileServer = startFileServer({ port: config2.port + 2 });
|
|
39776
|
+
const fileServer = startFileServer({ port: config2.port + 2, bridge });
|
|
38662
39777
|
const relayUrl = options?.relayUrl?.trim() || config2.relay || null;
|
|
38663
39778
|
if (!relayUrl) {
|
|
38664
39779
|
fileServer.stop();
|