@openscout/scout 0.2.70 → 0.2.73
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/LICENSE +202 -0
- package/README.md +59 -3
- package/bin/openscout-runtime.mjs +23 -2
- package/bin/scout +34 -0
- package/bin/scout.mjs +195 -18
- package/bin/scoutd +0 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/assets/RepoDiffViewer-D8gg36W3.js +56 -0
- package/dist/client/assets/{addon-fit-BNV7JPhj.js → addon-fit-D4KdY-pj.js} +1 -1
- package/dist/client/assets/{addon-webgl-B-_Y5L3F.js → addon-webgl-Bo_tmKBp.js} +1 -1
- package/dist/client/assets/arc.es-D0gujIKy.js +188 -0
- package/dist/client/assets/embed-entry-CUVNwyB_.js +1 -0
- package/dist/client/assets/index-CRkjiso5.js +257 -0
- package/dist/client/assets/index-D_RX2cRb.css +1 -0
- package/dist/client/assets/{xterm-Cx1oABPt.js → xterm-RfflDFod.js} +1 -1
- package/dist/client/favicon-16.png +0 -0
- package/dist/client/favicon-32.png +0 -0
- package/dist/client/favicon.ico +0 -0
- package/dist/client/favicon.svg +48 -0
- package/dist/client/index.html +42 -2
- package/dist/client/openscout-icon.png +0 -0
- package/dist/client/site.webmanifest +19 -0
- package/dist/client/web-app-icon-192.png +0 -0
- package/dist/client/web-app-icon-512.png +0 -0
- package/dist/drizzle/0000_curly_iron_monger.sql +592 -0
- package/dist/drizzle/0001_invocation-status-columns.sql +7 -0
- package/dist/drizzle/0002_invocation-flight-metadata.sql +21 -0
- package/dist/drizzle/README.md +81 -0
- package/dist/drizzle/meta/0000_snapshot.json +4575 -0
- package/dist/drizzle/meta/0001_snapshot.json +4624 -0
- package/dist/drizzle/meta/0002_snapshot.json +4631 -0
- package/dist/drizzle/meta/_journal.json +27 -0
- package/dist/main.mjs +77080 -60152
- package/dist/node/main.mjs +7607 -0
- package/dist/openscout-terminal-relay.mjs +3830 -152
- package/dist/{pair-supervisor.mjs → pairing-runtime-controller.mjs} +50837 -41212
- package/dist/runtime/base-daemon.mjs +2219 -488
- package/dist/runtime/broker-daemon.mjs +39082 -25118
- package/dist/runtime/broker-process-manager.mjs +2048 -392
- package/dist/runtime/mesh-discover.mjs +2012 -391
- package/dist/scout-control-plane-web.mjs +58128 -34736
- package/dist/scout-web-server.mjs +58128 -34736
- package/dist/statusline.mjs +287 -0
- package/package.json +8 -4
- package/dist/client/assets/index-BJri_z5a.css +0 -1
- package/dist/client/assets/index-Ccjo5BZz.js +0 -199
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// ../../apps/desktop/src/cli/commands/statusline.ts
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
|
|
5
|
+
// ../runtime/src/claude-statusline.ts
|
|
6
|
+
import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
|
|
7
|
+
import { basename, dirname, join as join2 } from "path";
|
|
8
|
+
|
|
9
|
+
// ../runtime/src/support-paths.ts
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
var OPENSCOUT_RPC_CUTOVER_MARKER = "rpc-runtime-cutover-v1";
|
|
13
|
+
function resolveOpenScoutSupportPaths() {
|
|
14
|
+
const home = homedir();
|
|
15
|
+
const supportDirectory = process.env.OPENSCOUT_SUPPORT_DIRECTORY ?? join(home, "Library", "Application Support", "OpenScout");
|
|
16
|
+
const logsDirectory = join(supportDirectory, "logs");
|
|
17
|
+
const runtimeDirectory = join(supportDirectory, "runtime");
|
|
18
|
+
const catalogDirectory = join(supportDirectory, "catalog");
|
|
19
|
+
const controlHome = process.env.OPENSCOUT_CONTROL_HOME ?? join(home, ".openscout", "control-plane");
|
|
20
|
+
const knowledgeDirectory = join(controlHome, "knowledge");
|
|
21
|
+
return {
|
|
22
|
+
supportDirectory,
|
|
23
|
+
logsDirectory,
|
|
24
|
+
appLogsDirectory: join(logsDirectory, "app"),
|
|
25
|
+
brokerLogsDirectory: join(logsDirectory, "broker"),
|
|
26
|
+
runtimeDirectory,
|
|
27
|
+
catalogDirectory,
|
|
28
|
+
relayAgentsDirectory: join(runtimeDirectory, "agents"),
|
|
29
|
+
settingsPath: join(supportDirectory, "settings.json"),
|
|
30
|
+
harnessCatalogPath: join(catalogDirectory, "harness-catalog.json"),
|
|
31
|
+
relayAgentsRegistryPath: join(supportDirectory, "relay-agents.json"),
|
|
32
|
+
managedInstallsPath: join(supportDirectory, "managed-installs.json"),
|
|
33
|
+
hostInfoPath: join(supportDirectory, ".host-info"),
|
|
34
|
+
relayHubDirectory: process.env.OPENSCOUT_RELAY_HUB ?? join(home, ".openscout", "relay"),
|
|
35
|
+
controlHome,
|
|
36
|
+
knowledgeDirectory,
|
|
37
|
+
knowledgeQmdDirectory: join(knowledgeDirectory, "qmd"),
|
|
38
|
+
knowledgeSqlitePath: join(knowledgeDirectory, "knowledge.sqlite"),
|
|
39
|
+
desktopStatusPath: join(supportDirectory, "agent-status.json"),
|
|
40
|
+
workspaceStatePath: join(supportDirectory, "workspace-state.json"),
|
|
41
|
+
cutoverMarkerPath: join(supportDirectory, OPENSCOUT_RPC_CUTOVER_MARKER)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ../runtime/src/claude-statusline.ts
|
|
46
|
+
function resolveClaudeStatuslineDirectory() {
|
|
47
|
+
return join2(resolveOpenScoutSupportPaths().runtimeDirectory, "statusline");
|
|
48
|
+
}
|
|
49
|
+
function resolveClaudeStatuslineDelegatePath() {
|
|
50
|
+
return join2(resolveClaudeStatuslineDirectory(), "claude-delegate.json");
|
|
51
|
+
}
|
|
52
|
+
function resolveClaudeStatuslineWrapperPath() {
|
|
53
|
+
return join2(resolveClaudeStatuslineDirectory(), "claude-statusline.sh");
|
|
54
|
+
}
|
|
55
|
+
function isRecord(value) {
|
|
56
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
57
|
+
}
|
|
58
|
+
function stringValue(value) {
|
|
59
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
60
|
+
}
|
|
61
|
+
function numberValue(value) {
|
|
62
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
63
|
+
return;
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function percentLabel(value) {
|
|
67
|
+
const number = numberValue(value);
|
|
68
|
+
if (number === undefined)
|
|
69
|
+
return null;
|
|
70
|
+
if (Math.abs(number - Math.round(number)) < 0.05) {
|
|
71
|
+
return `${Math.round(number)}%`;
|
|
72
|
+
}
|
|
73
|
+
return `${number.toFixed(1)}%`;
|
|
74
|
+
}
|
|
75
|
+
function recordValue(value) {
|
|
76
|
+
return isRecord(value) ? value : null;
|
|
77
|
+
}
|
|
78
|
+
function parseClaudeStatuslinePayload(input) {
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(input);
|
|
81
|
+
return isRecord(parsed) ? parsed : null;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function normalizeClaudeStatuslineSnapshot(input, capturedAt = Date.now()) {
|
|
87
|
+
const workspace = recordValue(input.workspace);
|
|
88
|
+
const cwd = stringValue(input.cwd) ?? stringValue(workspace?.current_dir);
|
|
89
|
+
const snapshot = {
|
|
90
|
+
...input,
|
|
91
|
+
openscoutCapturedAt: numberValue(input.openscoutCapturedAt) ?? capturedAt
|
|
92
|
+
};
|
|
93
|
+
if (cwd && !stringValue(snapshot.cwd)) {
|
|
94
|
+
snapshot.cwd = cwd;
|
|
95
|
+
}
|
|
96
|
+
return snapshot;
|
|
97
|
+
}
|
|
98
|
+
async function captureClaudeStatuslineSnapshot(input, options = {}) {
|
|
99
|
+
const parsed = typeof input === "string" ? parseClaudeStatuslinePayload(input) : input;
|
|
100
|
+
if (!parsed) {
|
|
101
|
+
return { captured: false, reason: typeof input === "string" ? "invalid-json" : "invalid-record" };
|
|
102
|
+
}
|
|
103
|
+
if (!isRecord(parsed)) {
|
|
104
|
+
return { captured: false, reason: "invalid-record" };
|
|
105
|
+
}
|
|
106
|
+
const directory = options.directory ?? resolveClaudeStatuslineDirectory();
|
|
107
|
+
const latestPath = join2(directory, "claude-latest.json");
|
|
108
|
+
const historyPath = join2(directory, "claude-history.jsonl");
|
|
109
|
+
const snapshot = normalizeClaudeStatuslineSnapshot(parsed, options.capturedAt);
|
|
110
|
+
const line = JSON.stringify(snapshot);
|
|
111
|
+
await mkdir(directory, { recursive: true });
|
|
112
|
+
await Promise.all([
|
|
113
|
+
writeFile(latestPath, `${line}
|
|
114
|
+
`, "utf8"),
|
|
115
|
+
appendFile(historyPath, `${line}
|
|
116
|
+
`, "utf8")
|
|
117
|
+
]);
|
|
118
|
+
return {
|
|
119
|
+
captured: true,
|
|
120
|
+
latestPath,
|
|
121
|
+
historyPath,
|
|
122
|
+
snapshot
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function readClaudeStatuslineDelegate(path = resolveClaudeStatuslineDelegatePath()) {
|
|
126
|
+
try {
|
|
127
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
128
|
+
if (!isRecord(parsed))
|
|
129
|
+
return null;
|
|
130
|
+
const command = stringValue(parsed.command);
|
|
131
|
+
if (!command)
|
|
132
|
+
return null;
|
|
133
|
+
return {
|
|
134
|
+
version: 1,
|
|
135
|
+
command,
|
|
136
|
+
source: parsed.source === "manual" ? "manual" : "claude-settings.statusLine",
|
|
137
|
+
installedAt: numberValue(parsed.installedAt) ?? Date.now(),
|
|
138
|
+
...isRecord(parsed.statusLine) ? { statusLine: parsed.statusLine } : {}
|
|
139
|
+
};
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function isOpenScoutClaudeStatuslineCommand(command, wrapperPath = resolveClaudeStatuslineWrapperPath()) {
|
|
145
|
+
const trimmed = command.trim();
|
|
146
|
+
if (!trimmed)
|
|
147
|
+
return false;
|
|
148
|
+
if (trimmed.includes(wrapperPath))
|
|
149
|
+
return true;
|
|
150
|
+
return /(?:^|\s|["'])\S*scout(?:["']|\s)+statusline\s+claude(?:\s|$)/u.test(trimmed);
|
|
151
|
+
}
|
|
152
|
+
function formatClaudeStatuslineFallback(snapshot) {
|
|
153
|
+
if (!snapshot) {
|
|
154
|
+
return "Scout | Claude status";
|
|
155
|
+
}
|
|
156
|
+
const model = recordValue(snapshot.model);
|
|
157
|
+
const workspace = recordValue(snapshot.workspace);
|
|
158
|
+
const context = recordValue(snapshot.context_window);
|
|
159
|
+
const rateLimits = recordValue(snapshot.rate_limits);
|
|
160
|
+
const fiveHour = recordValue(rateLimits?.five_hour);
|
|
161
|
+
const sevenDay = recordValue(rateLimits?.seven_day);
|
|
162
|
+
const cwd = stringValue(snapshot.cwd) ?? stringValue(workspace?.current_dir);
|
|
163
|
+
const parts = [
|
|
164
|
+
"Scout",
|
|
165
|
+
stringValue(model?.display_name) ?? stringValue(model?.id) ?? "Claude"
|
|
166
|
+
];
|
|
167
|
+
if (cwd) {
|
|
168
|
+
parts.push(basename(cwd));
|
|
169
|
+
}
|
|
170
|
+
const contextPercent = percentLabel(context?.used_percentage);
|
|
171
|
+
if (contextPercent) {
|
|
172
|
+
parts.push(`ctx ${contextPercent}`);
|
|
173
|
+
}
|
|
174
|
+
const fiveHourPercent = percentLabel(fiveHour?.used_percentage);
|
|
175
|
+
if (fiveHourPercent) {
|
|
176
|
+
parts.push(`5h ${fiveHourPercent}`);
|
|
177
|
+
}
|
|
178
|
+
const sevenDayPercent = percentLabel(sevenDay?.used_percentage);
|
|
179
|
+
if (sevenDayPercent) {
|
|
180
|
+
parts.push(`7d ${sevenDayPercent}`);
|
|
181
|
+
}
|
|
182
|
+
return parts.join(" | ");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ../../apps/desktop/src/cli/commands/statusline.ts
|
|
186
|
+
var DELEGATE_TIMEOUT_MS = 150;
|
|
187
|
+
var MAX_DELEGATE_OUTPUT_BYTES = 64 * 1024;
|
|
188
|
+
async function runStatuslineCommand(context, args) {
|
|
189
|
+
const [provider, ...rest] = args;
|
|
190
|
+
if (provider !== "claude") {
|
|
191
|
+
context.output.writeText("usage: scout statusline claude");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (process.stdin.isTTY) {
|
|
195
|
+
context.output.writeText("usage: scout statusline claude < statusline.json");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const delegateRequested = rest.includes("--delegate") || context.env.OPENSCOUT_STATUSLINE_RUN_DELEGATE === "1";
|
|
199
|
+
const noDelegate = rest.includes("--no-delegate") || context.env.OPENSCOUT_STATUSLINE_DELEGATE === "1";
|
|
200
|
+
const raw = await readStdin();
|
|
201
|
+
const snapshot = parseClaudeStatuslinePayload(raw);
|
|
202
|
+
try {
|
|
203
|
+
await captureClaudeStatuslineSnapshot(snapshot ?? raw);
|
|
204
|
+
} catch {}
|
|
205
|
+
if (delegateRequested && !noDelegate) {
|
|
206
|
+
const delegate = await readClaudeStatuslineDelegate();
|
|
207
|
+
if (delegate?.command && !isOpenScoutClaudeStatuslineCommand(delegate.command)) {
|
|
208
|
+
const delegated = await runDelegateStatusline(delegate.command, raw, context.env);
|
|
209
|
+
if (delegated) {
|
|
210
|
+
writeStatusline(delegated);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
writeStatusline(formatClaudeStatuslineFallback(snapshot));
|
|
216
|
+
}
|
|
217
|
+
async function readStdin() {
|
|
218
|
+
let out = "";
|
|
219
|
+
process.stdin.setEncoding("utf8");
|
|
220
|
+
for await (const chunk of process.stdin) {
|
|
221
|
+
out += typeof chunk === "string" ? chunk : String(chunk);
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
function writeStatusline(value) {
|
|
226
|
+
const normalized = value.trimEnd();
|
|
227
|
+
process.stdout.write(`${normalized}
|
|
228
|
+
`);
|
|
229
|
+
}
|
|
230
|
+
function runDelegateStatusline(command, input, env) {
|
|
231
|
+
return new Promise((resolve) => {
|
|
232
|
+
const child = spawn("sh", ["-lc", command], {
|
|
233
|
+
env: {
|
|
234
|
+
...env,
|
|
235
|
+
OPENSCOUT_STATUSLINE_DELEGATE: "1"
|
|
236
|
+
},
|
|
237
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
238
|
+
});
|
|
239
|
+
let settled = false;
|
|
240
|
+
let stdout = "";
|
|
241
|
+
const done = (value) => {
|
|
242
|
+
if (settled)
|
|
243
|
+
return;
|
|
244
|
+
settled = true;
|
|
245
|
+
clearTimeout(timer);
|
|
246
|
+
resolve(value?.trimEnd() || null);
|
|
247
|
+
};
|
|
248
|
+
const timer = setTimeout(() => {
|
|
249
|
+
child.kill("SIGTERM");
|
|
250
|
+
done(null);
|
|
251
|
+
}, DELEGATE_TIMEOUT_MS);
|
|
252
|
+
child.stdout.setEncoding("utf8");
|
|
253
|
+
child.stdout.on("data", (chunk) => {
|
|
254
|
+
if (stdout.length >= MAX_DELEGATE_OUTPUT_BYTES)
|
|
255
|
+
return;
|
|
256
|
+
stdout += String(chunk).slice(0, MAX_DELEGATE_OUTPUT_BYTES - stdout.length);
|
|
257
|
+
});
|
|
258
|
+
child.once("error", () => done(null));
|
|
259
|
+
child.once("close", (code) => done(code === 0 ? stdout : null));
|
|
260
|
+
child.stdin.on("error", () => {
|
|
261
|
+
return;
|
|
262
|
+
});
|
|
263
|
+
child.stdin.end(input);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/statusline.ts
|
|
268
|
+
try {
|
|
269
|
+
const args = process.argv[2] === "statusline" ? process.argv.slice(3) : process.argv.slice(2);
|
|
270
|
+
const context = {
|
|
271
|
+
env: process.env,
|
|
272
|
+
output: {
|
|
273
|
+
writeText(value) {
|
|
274
|
+
process.stdout.write(`${value}
|
|
275
|
+
`);
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
stderr(message) {
|
|
279
|
+
process.stderr.write(`${message}
|
|
280
|
+
`);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
await runStatuslineCommand(context, args);
|
|
284
|
+
} catch {
|
|
285
|
+
process.stdout.write(`Scout | Claude status
|
|
286
|
+
`);
|
|
287
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openscout/scout",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.73",
|
|
4
4
|
"description": "Public Scout package that installs the `scout` command and bundled local runtime",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/arach/openscout#readme",
|
|
16
16
|
"bin": {
|
|
17
|
-
"scout": "./bin/scout
|
|
17
|
+
"scout": "./bin/scout",
|
|
18
|
+
"openscout-runtime": "./bin/openscout-runtime.mjs"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"bun": ">=1.2"
|
|
18
22
|
},
|
|
19
23
|
"files": [
|
|
20
24
|
"bin",
|
|
@@ -22,7 +26,7 @@
|
|
|
22
26
|
"README.md"
|
|
23
27
|
],
|
|
24
28
|
"dependencies": {
|
|
25
|
-
"node-pty": "1.2.0-beta.12",
|
|
29
|
+
"@lydell/node-pty": "1.2.0-beta.12",
|
|
26
30
|
"picomatch": "^4.0.4",
|
|
27
31
|
"ws": "^8.18.3"
|
|
28
32
|
},
|