@donna-orchestrator/bridge 0.2.21
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 +45 -0
- package/dist/cli.js +301 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +98 -0
- package/dist/config.js.map +1 -0
- package/dist/executor.js +150 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.js +248 -0
- package/dist/index.js.map +1 -0
- package/dist/install.js +194 -0
- package/dist/install.js.map +1 -0
- package/dist/launchd.js +146 -0
- package/dist/launchd.js.map +1 -0
- package/dist/lifecycle.js +228 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/pair.js +88 -0
- package/dist/pair.js.map +1 -0
- package/dist/preflight.js +117 -0
- package/dist/preflight.js.map +1 -0
- package/dist/scope-guard.js +25 -0
- package/dist/scope-guard.js.map +1 -0
- package/dist/stream-parser.js +116 -0
- package/dist/stream-parser.js.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/uninstall.js +172 -0
- package/dist/uninstall.js.map +1 -0
- package/dist/workspace.js +362 -0
- package/dist/workspace.js.map +1 -0
- package/dist/xcode-probe.js +272 -0
- package/dist/xcode-probe.js.map +1 -0
- package/package.json +26 -0
- package/templates/launchagent-bridge.plist.template +28 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge lifecycle CLI subcommands (#295).
|
|
3
|
+
*
|
|
4
|
+
* `start` — load the LaunchAgent if not already loaded (the agent itself
|
|
5
|
+
* runs `donna-bridge start` to actually connect, so when invoked
|
|
6
|
+
* directly we either delegate to launchctl or, for tests/dev,
|
|
7
|
+
* connect inline via `runBridge`).
|
|
8
|
+
* `stop` — `launchctl bootout gui/<uid>/<label>`.
|
|
9
|
+
* `status` — print LaunchAgent loaded-state + the last line of the log file.
|
|
10
|
+
* `logs --follow` — tail `~/Library/Logs/Donna/bridge.log`.
|
|
11
|
+
*
|
|
12
|
+
* The LaunchAgent's `ProgramArguments` is `[donna-bridge, start]`. When
|
|
13
|
+
* launchd spawns it, `runStartInline` runs and connects to the orchestrator.
|
|
14
|
+
* When the user types `donna-bridge start` interactively, we prefer to ask
|
|
15
|
+
* launchd to bootstrap (so the process survives a logout); the inline path
|
|
16
|
+
* is used by tests + the `--inline` debug flag.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import readline from 'node:readline';
|
|
22
|
+
import { spawn } from 'node:child_process';
|
|
23
|
+
import { loadConfig, getDefaults } from './config.js';
|
|
24
|
+
import { isAgentLoaded, loadAgent, unloadAgent } from './launchd.js';
|
|
25
|
+
import { runXcodeProbe, reportToCapabilityTools, readToolReport } from './xcode-probe.js';
|
|
26
|
+
import { runBridge } from './index.js';
|
|
27
|
+
import { createWorkspaceHandler } from './workspace.js';
|
|
28
|
+
function defaultIO() {
|
|
29
|
+
return {
|
|
30
|
+
stdout: (m) => process.stdout.write(m),
|
|
31
|
+
stderr: (m) => process.stderr.write(m),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function requireConfig(io) {
|
|
35
|
+
const config = loadConfig();
|
|
36
|
+
if (!config) {
|
|
37
|
+
io.stderr(`No config found. Run \`donna-bridge install\` first.\n`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
42
|
+
/** `donna-bridge start` — bootstrap the agent (or run inline). */
|
|
43
|
+
export async function runStart(opts = {}) {
|
|
44
|
+
const io = opts.io ?? defaultIO();
|
|
45
|
+
const config = requireConfig(io);
|
|
46
|
+
if (!config)
|
|
47
|
+
return 1;
|
|
48
|
+
if (!opts.inline) {
|
|
49
|
+
const defaults = getDefaults();
|
|
50
|
+
if (!fs.existsSync(defaults.plistPath)) {
|
|
51
|
+
io.stderr(`LaunchAgent plist missing at ${defaults.plistPath}.\n` +
|
|
52
|
+
`Re-run \`donna-bridge install\` to regenerate it.\n`);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await loadAgent(defaults.plistPath, os.userInfo().uid);
|
|
57
|
+
io.stdout(`✅ LaunchAgent loaded (gui/${os.userInfo().uid}/${defaults.label})\n`);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
io.stderr(`Failed to bootstrap LaunchAgent: ${err.message}\n`);
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Inline path — keep the process alive and run the bridge loop.
|
|
66
|
+
if (!config.xcodePath) {
|
|
67
|
+
io.stderr(`config.xcodePath is unset. Run \`donna-bridge xcode select <version>\`.\n`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
const defaults = getDefaults();
|
|
71
|
+
const simulatorDeviceSetPath = config.simulatorDeviceSetPath ?? defaults.deviceSetPath;
|
|
72
|
+
// Refresh the tool report so the hello payload's capabilities.tools is fresh.
|
|
73
|
+
let report = readToolReport();
|
|
74
|
+
try {
|
|
75
|
+
report = await runXcodeProbe({ developerDir: config.xcodePath });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Probe failures don't block startup — we fall back to the persisted report.
|
|
79
|
+
}
|
|
80
|
+
const tools = report ? reportToCapabilityTools(report) : {};
|
|
81
|
+
const workspace = createWorkspaceHandler({ projectsDir: defaults.projectsDir });
|
|
82
|
+
const handle = runBridge({
|
|
83
|
+
config: {
|
|
84
|
+
orchestratorUrl: config.orchestratorUrl,
|
|
85
|
+
token: config.token,
|
|
86
|
+
runnerId: config.runnerId,
|
|
87
|
+
xcodePath: config.xcodePath,
|
|
88
|
+
simulatorDeviceSetPath,
|
|
89
|
+
tools,
|
|
90
|
+
},
|
|
91
|
+
workspace,
|
|
92
|
+
});
|
|
93
|
+
// Keep the process alive until SIGTERM / SIGINT.
|
|
94
|
+
await new Promise((resolve) => {
|
|
95
|
+
const stop = () => {
|
|
96
|
+
handle.close();
|
|
97
|
+
resolve();
|
|
98
|
+
};
|
|
99
|
+
process.once('SIGTERM', stop);
|
|
100
|
+
process.once('SIGINT', stop);
|
|
101
|
+
});
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
export async function runStop(io = defaultIO()) {
|
|
105
|
+
const defaults = getDefaults();
|
|
106
|
+
if (!fs.existsSync(defaults.plistPath)) {
|
|
107
|
+
io.stdout(`LaunchAgent plist not present at ${defaults.plistPath}; nothing to stop.\n`);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
await unloadAgent(defaults.plistPath, os.userInfo().uid);
|
|
112
|
+
io.stdout(`✅ LaunchAgent stopped (gui/${os.userInfo().uid}/${defaults.label})\n`);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
io.stderr(`Failed to bootout LaunchAgent: ${err.message}\n`);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build the status report without printing it — useful for tests and for
|
|
122
|
+
* `--json` callers (out of scope for this ticket but cheap to keep).
|
|
123
|
+
*/
|
|
124
|
+
export async function buildStatus() {
|
|
125
|
+
const config = loadConfig();
|
|
126
|
+
const defaults = getDefaults();
|
|
127
|
+
const plistPresent = fs.existsSync(defaults.plistPath);
|
|
128
|
+
let agentLoaded = false;
|
|
129
|
+
try {
|
|
130
|
+
agentLoaded = await isAgentLoaded(defaults.label, os.userInfo().uid);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
agentLoaded = false;
|
|
134
|
+
}
|
|
135
|
+
const logExists = fs.existsSync(defaults.logPath);
|
|
136
|
+
let lastLogLine;
|
|
137
|
+
if (logExists) {
|
|
138
|
+
lastLogLine = readLastLine(defaults.logPath);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
configPath: path.join(defaults.deviceSetPath, '..'),
|
|
142
|
+
configPresent: !!config,
|
|
143
|
+
orchestratorUrl: config?.orchestratorUrl,
|
|
144
|
+
runnerId: config?.runnerId,
|
|
145
|
+
xcodePath: config?.xcodePath,
|
|
146
|
+
plistPath: defaults.plistPath,
|
|
147
|
+
plistPresent,
|
|
148
|
+
agentLoaded,
|
|
149
|
+
logPath: defaults.logPath,
|
|
150
|
+
logExists,
|
|
151
|
+
lastLogLine,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export async function runStatusCmd(io = defaultIO()) {
|
|
155
|
+
const status = await buildStatus();
|
|
156
|
+
io.stdout(`donna-bridge status\n`);
|
|
157
|
+
io.stdout(` config: ${status.configPresent ? 'present' : 'missing'}\n`);
|
|
158
|
+
if (status.configPresent) {
|
|
159
|
+
io.stdout(` orchestrator: ${status.orchestratorUrl ?? '(unset)'}\n`);
|
|
160
|
+
io.stdout(` runner_id: ${status.runnerId ?? '(unset)'}\n`);
|
|
161
|
+
io.stdout(` xcode_path: ${status.xcodePath ?? '(unset)'}\n`);
|
|
162
|
+
}
|
|
163
|
+
io.stdout(` plist: ${status.plistPresent ? status.plistPath : 'missing'}\n`);
|
|
164
|
+
io.stdout(` agent_loaded: ${status.agentLoaded ? 'yes' : 'no'}\n`);
|
|
165
|
+
io.stdout(` log: ${status.logPath}\n`);
|
|
166
|
+
if (status.logExists && status.lastLogLine) {
|
|
167
|
+
io.stdout(` last_log: ${status.lastLogLine}\n`);
|
|
168
|
+
}
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
export async function runLogs(opts = {}) {
|
|
172
|
+
const io = opts.io ?? defaultIO();
|
|
173
|
+
const defaults = getDefaults();
|
|
174
|
+
if (!fs.existsSync(defaults.logPath)) {
|
|
175
|
+
io.stderr(`Log file ${defaults.logPath} does not exist yet.\n`);
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
if (!opts.follow) {
|
|
179
|
+
// Print the existing log content once and exit.
|
|
180
|
+
const data = fs.readFileSync(defaults.logPath, 'utf-8');
|
|
181
|
+
io.stdout(data);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
// `tail -F` (capital F) tracks the file across rotation — what users
|
|
186
|
+
// expect from `--follow`. Inherit stdio so the user sees output live.
|
|
187
|
+
const proc = spawn('tail', ['-F', defaults.logPath], { stdio: 'inherit' });
|
|
188
|
+
proc.on('error', (err) => {
|
|
189
|
+
io.stderr(`tail failed: ${err.message}\n`);
|
|
190
|
+
resolve(1);
|
|
191
|
+
});
|
|
192
|
+
proc.on('close', (code) => resolve(code ?? 0));
|
|
193
|
+
// Forward SIGINT to the tail process so Ctrl-C cleanly terminates it.
|
|
194
|
+
const onSigint = () => proc.kill('SIGINT');
|
|
195
|
+
process.once('SIGINT', onSigint);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Read the last non-empty line of a (potentially large) text file without
|
|
200
|
+
* loading the whole thing. Reads the last 4 KB; good enough for log files.
|
|
201
|
+
*/
|
|
202
|
+
function readLastLine(filePath) {
|
|
203
|
+
try {
|
|
204
|
+
const fd = fs.openSync(filePath, 'r');
|
|
205
|
+
try {
|
|
206
|
+
const stats = fs.fstatSync(fd);
|
|
207
|
+
const size = stats.size;
|
|
208
|
+
const readLen = Math.min(4096, size);
|
|
209
|
+
const buf = Buffer.alloc(readLen);
|
|
210
|
+
fs.readSync(fd, buf, 0, readLen, size - readLen);
|
|
211
|
+
const lines = buf.toString('utf-8').split('\n').map((l) => l.trim()).filter(Boolean);
|
|
212
|
+
const last = lines.pop();
|
|
213
|
+
if (!last)
|
|
214
|
+
return undefined;
|
|
215
|
+
return last.length > 200 ? `${last.slice(0, 200)}…` : last;
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
fs.closeSync(fd);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Avoid unused-import warning when readline isn't reachable through the path
|
|
226
|
+
// above; readline is retained for future interactive prompts in this module.
|
|
227
|
+
void readline;
|
|
228
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../lifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/pair.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge pairing (#295).
|
|
3
|
+
*
|
|
4
|
+
* Two paths:
|
|
5
|
+
* 1. Standard remote pairing — POST the 6-digit code to
|
|
6
|
+
* `/api/v1/runners/pair/complete` and receive a long-lived token (per
|
|
7
|
+
* #272). This is the same endpoint the native runner uses.
|
|
8
|
+
* 2. Local pairing (`--local-pair`) — POST to `/api/v1/runners/local-pair`
|
|
9
|
+
* with the runner id and a flag describing the host. The endpoint
|
|
10
|
+
* itself is implemented in #296 (gated to loopback). We just know the
|
|
11
|
+
* URL here.
|
|
12
|
+
*
|
|
13
|
+
* Both paths return `{ token, runner_id }`. The CLI persists the response
|
|
14
|
+
* to `~/.donna-bridge/config.json` via `bridge/config.ts`.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Convert a WS URL to its corresponding HTTP base URL. `wss://host/path` →
|
|
18
|
+
* `https://host`. We strip the path entirely because all pairing endpoints
|
|
19
|
+
* live under `/api/v1/runners/...` (mirrors `runner/mcp-donna-derive.ts`).
|
|
20
|
+
*/
|
|
21
|
+
export function deriveHttpBase(wsUrl) {
|
|
22
|
+
let url;
|
|
23
|
+
try {
|
|
24
|
+
url = new URL(wsUrl);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error(`invalid orchestrator URL: ${wsUrl}`);
|
|
28
|
+
}
|
|
29
|
+
const proto = url.protocol === 'wss:' ? 'https:' : url.protocol === 'ws:' ? 'http:' : url.protocol;
|
|
30
|
+
return `${proto}//${url.host}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Exchange a 6-digit pairing code for a long-lived token. Mirrors the
|
|
34
|
+
* `runner/native/cli.ts#runPair` flow but written here so the bridge stays
|
|
35
|
+
* self-contained (`bridge/` has no `runner/` imports).
|
|
36
|
+
*/
|
|
37
|
+
export async function pairWithCode(opts) {
|
|
38
|
+
if (!/^\d{6}$/.test(opts.pairingCode)) {
|
|
39
|
+
throw new Error(`pair: --pairing-code must be a 6-digit numeric string`);
|
|
40
|
+
}
|
|
41
|
+
const base = deriveHttpBase(opts.orchestratorUrl);
|
|
42
|
+
const url = `${base}/api/v1/runners/pair/complete`;
|
|
43
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
44
|
+
const res = await fetchFn(url, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body: JSON.stringify({ pairing_code: opts.pairingCode, runner_id: opts.runnerId }),
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const text = await res.text();
|
|
51
|
+
throw new Error(`pair: orchestrator rejected pairing (HTTP ${res.status}): ${text}`);
|
|
52
|
+
}
|
|
53
|
+
const body = (await res.json());
|
|
54
|
+
if (!body.token || !body.runner_id) {
|
|
55
|
+
throw new Error(`pair: unexpected response shape: ${JSON.stringify(body)}`);
|
|
56
|
+
}
|
|
57
|
+
return { token: body.token, runner_id: body.runner_id };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Local-pair flow — hits `POST /api/v1/runners/local-pair`. The endpoint is
|
|
61
|
+
* gated to loopback by #296. Here we just send the request; if the
|
|
62
|
+
* orchestrator URL is not a loopback address (127.0.0.1 / localhost), the
|
|
63
|
+
* server-side gate refuses with HTTP 403 and we surface that to the user.
|
|
64
|
+
*
|
|
65
|
+
* Body shape: `{ runner_id }`. Response: same `{ token, runner_id }` as the
|
|
66
|
+
* remote flow. Endpoint behaviour is finalized in #296; the CLI flag is
|
|
67
|
+
* implemented here per the ticket scope.
|
|
68
|
+
*/
|
|
69
|
+
export async function pairLocally(opts) {
|
|
70
|
+
const base = deriveHttpBase(opts.orchestratorUrl);
|
|
71
|
+
const url = `${base}/api/v1/runners/local-pair`;
|
|
72
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
73
|
+
const res = await fetchFn(url, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: { 'Content-Type': 'application/json' },
|
|
76
|
+
body: JSON.stringify({ runner_id: opts.runnerId }),
|
|
77
|
+
});
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
const text = await res.text();
|
|
80
|
+
throw new Error(`pair: local-pair rejected (HTTP ${res.status}): ${text}`);
|
|
81
|
+
}
|
|
82
|
+
const body = (await res.json());
|
|
83
|
+
if (!body.token || !body.runner_id) {
|
|
84
|
+
throw new Error(`pair: unexpected local-pair response shape: ${JSON.stringify(body)}`);
|
|
85
|
+
}
|
|
86
|
+
return { token: body.token, runner_id: body.runner_id };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=pair.js.map
|
package/dist/pair.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pair.js","sourceRoot":"","sources":["../pair.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge install pre-flight gate (#295).
|
|
3
|
+
*
|
|
4
|
+
* Checks that the four required tools (claude CLI, xcodebuild, git, node ≥ 22)
|
|
5
|
+
* are present on the host before pairing. Each missing tool surfaces an exact
|
|
6
|
+
* install hint — no AI judgment, just the canned guide from the spec.
|
|
7
|
+
*
|
|
8
|
+
* Pure-ish: shells out to each binary but never throws on missing tools. The
|
|
9
|
+
* caller (`bridge/install.ts`) inspects the result and bails when any required
|
|
10
|
+
* check fails.
|
|
11
|
+
*/
|
|
12
|
+
import { execFile } from 'node:child_process';
|
|
13
|
+
const PROBE_TIMEOUT_MS = 5_000;
|
|
14
|
+
const MIN_NODE_MAJOR = 22;
|
|
15
|
+
function execFileP(bin, args) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
execFile(bin, args, { timeout: PROBE_TIMEOUT_MS, encoding: 'utf-8' }, (err, stdout, stderr) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
reject(err);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
resolve({ stdout: stdout ?? '', stderr: stderr ?? '' });
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function extractVersion(raw) {
|
|
27
|
+
const firstLine = raw.split('\n')[0]?.trim() ?? '';
|
|
28
|
+
const m = firstLine.match(/(\d+\.\d+(?:\.\d+)?(?:[-+][\w.]+)?)/);
|
|
29
|
+
return m ? m[1] : firstLine || undefined;
|
|
30
|
+
}
|
|
31
|
+
const INSTALL_HINTS = {
|
|
32
|
+
claude: 'Install Claude CLI: npm install -g @anthropic-ai/claude-code',
|
|
33
|
+
xcodebuild: 'Install Xcode from the Mac App Store (xcodebuild ships with the full Xcode app)',
|
|
34
|
+
git: 'Install git: xcode-select --install',
|
|
35
|
+
node: `Install Node.js ≥ ${MIN_NODE_MAJOR}: brew install node`,
|
|
36
|
+
};
|
|
37
|
+
async function probeTool(name) {
|
|
38
|
+
const hint = INSTALL_HINTS[name];
|
|
39
|
+
try {
|
|
40
|
+
let outcome;
|
|
41
|
+
let version;
|
|
42
|
+
switch (name) {
|
|
43
|
+
case 'claude':
|
|
44
|
+
outcome = await execFileP('claude', ['--version']);
|
|
45
|
+
version = extractVersion(outcome.stdout || outcome.stderr);
|
|
46
|
+
break;
|
|
47
|
+
case 'xcodebuild':
|
|
48
|
+
outcome = await execFileP('xcodebuild', ['-version']);
|
|
49
|
+
// "Xcode 16.2\nBuild version ..." — grab the numeric token after Xcode.
|
|
50
|
+
{
|
|
51
|
+
const m = outcome.stdout.match(/Xcode\s+(\d+(?:\.\d+)+)/i);
|
|
52
|
+
version = m ? m[1] : extractVersion(outcome.stdout);
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case 'git':
|
|
56
|
+
outcome = await execFileP('git', ['--version']);
|
|
57
|
+
version = extractVersion(outcome.stdout);
|
|
58
|
+
break;
|
|
59
|
+
case 'node': {
|
|
60
|
+
// `process.execPath --version` keeps the probe deterministic
|
|
61
|
+
// regardless of which node is first on PATH.
|
|
62
|
+
outcome = await execFileP(process.execPath, ['--version']);
|
|
63
|
+
version = extractVersion(outcome.stdout);
|
|
64
|
+
// Enforce ≥ 22.
|
|
65
|
+
if (version) {
|
|
66
|
+
const major = parseInt(version.split('.')[0] ?? '0', 10);
|
|
67
|
+
if (Number.isFinite(major) && major < MIN_NODE_MAJOR) {
|
|
68
|
+
return {
|
|
69
|
+
name,
|
|
70
|
+
ok: false,
|
|
71
|
+
version,
|
|
72
|
+
installHint: hint,
|
|
73
|
+
error: `node ${version} is too old; need ≥ ${MIN_NODE_MAJOR}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { name, ok: true, version, installHint: hint };
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return {
|
|
84
|
+
name,
|
|
85
|
+
ok: false,
|
|
86
|
+
installHint: hint,
|
|
87
|
+
error: err instanceof Error ? err.message : String(err),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export async function runPreflight() {
|
|
92
|
+
const checks = await Promise.all([
|
|
93
|
+
probeTool('claude'),
|
|
94
|
+
probeTool('xcodebuild'),
|
|
95
|
+
probeTool('git'),
|
|
96
|
+
probeTool('node'),
|
|
97
|
+
]);
|
|
98
|
+
return { ok: checks.every((c) => c.ok), checks };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render the pre-flight outcome as plain text. The installer prints this on
|
|
102
|
+
* failure so the user gets every missing tool's hint in a single block.
|
|
103
|
+
*/
|
|
104
|
+
export function formatPreflightTable(result) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
for (const c of result.checks) {
|
|
107
|
+
if (c.ok) {
|
|
108
|
+
lines.push(` [ok] ${c.name.padEnd(11)} ${c.version ?? ''}`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
lines.push(` [fail] ${c.name.padEnd(11)} ${c.error ?? 'missing'}`);
|
|
112
|
+
lines.push(` → ${c.installHint}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return lines.join('\n') + '\n';
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Tool tokens that mark a stage as Apple-platform. Lower-cased for comparison. */
|
|
2
|
+
const APPLE_TOOL_TOKENS = new Set(['xcode', 'xcodebuild', 'swift', 'simctl']);
|
|
3
|
+
/**
|
|
4
|
+
* Decide whether the given `runner:execute` payload represents an Apple-
|
|
5
|
+
* platform stage that this bridge is allowed to run.
|
|
6
|
+
*
|
|
7
|
+
* Pure function — no side effects, no env reads. The caller is responsible
|
|
8
|
+
* for emitting `runner:scope_violation` when this returns false.
|
|
9
|
+
*/
|
|
10
|
+
export function isAppleStage(payload) {
|
|
11
|
+
if (!payload)
|
|
12
|
+
return false;
|
|
13
|
+
if (payload.applePlatform === true)
|
|
14
|
+
return true;
|
|
15
|
+
if (Array.isArray(payload.requiredTools)) {
|
|
16
|
+
for (const tool of payload.requiredTools) {
|
|
17
|
+
if (typeof tool !== 'string')
|
|
18
|
+
continue;
|
|
19
|
+
if (APPLE_TOOL_TOKENS.has(tool.toLowerCase()))
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=scope-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-guard.js","sourceRoot":"","sources":["../scope-guard.ts"],"names":[],"mappings":"AAkBA,mFAAmF;AACnF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgD;IAC3E,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,OAAO,CAAC,aAAa,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YACzC,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,SAAS;YACvC,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Platform Bridge — stream-json parser (#294).
|
|
3
|
+
*
|
|
4
|
+
* Duplicated narrowly from `runner/stream-parser.ts` to keep the bridge a
|
|
5
|
+
* standalone library with no cross-tree imports — `runner/` is built by a
|
|
6
|
+
* separate tsconfig (`tsconfig.runner.json`) with its own `rootDir`, and the
|
|
7
|
+
* runner Docker build context doesn't ship anything outside `runner/`. The
|
|
8
|
+
* #294 ticket explicitly allows duplicating "the narrow piece" when extraction
|
|
9
|
+
* would force a wider refactor.
|
|
10
|
+
*
|
|
11
|
+
* Keep this file in lockstep with `runner/stream-parser.ts` — the two parse
|
|
12
|
+
* the same Claude CLI stream-json schema. If the schema changes, update both.
|
|
13
|
+
*/
|
|
14
|
+
// Claude model pricing (per million tokens) — same table as the runner.
|
|
15
|
+
const MODEL_PRICING = {
|
|
16
|
+
'claude-opus-4-6': { input: 5, output: 25 },
|
|
17
|
+
'claude-sonnet-4-6': { input: 3, output: 15 },
|
|
18
|
+
'claude-haiku-4-5-20251001': { input: 1, output: 5 },
|
|
19
|
+
default: { input: 3, output: 15 },
|
|
20
|
+
};
|
|
21
|
+
function calculateCost(model, inputTokens, outputTokens) {
|
|
22
|
+
const pricing = MODEL_PRICING[model];
|
|
23
|
+
if (!pricing) {
|
|
24
|
+
console.warn(`[bridge-stream] Unknown model "${model}" — using default pricing for cost estimate`);
|
|
25
|
+
}
|
|
26
|
+
return ((inputTokens * (pricing || MODEL_PRICING.default).input +
|
|
27
|
+
outputTokens * (pricing || MODEL_PRICING.default).output) /
|
|
28
|
+
1_000_000);
|
|
29
|
+
}
|
|
30
|
+
// 50MB output buffer cap — truncate from front if exceeded to avoid OOM.
|
|
31
|
+
export const MAX_BUFFER_SIZE = 50 * 1024 * 1024;
|
|
32
|
+
export function processStreamLine(line, state) {
|
|
33
|
+
if (!line.trim())
|
|
34
|
+
return;
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(line);
|
|
37
|
+
const events = Array.isArray(parsed) ? parsed : [parsed];
|
|
38
|
+
for (const event of events) {
|
|
39
|
+
if (event.type === 'assistant' && event.message?.content) {
|
|
40
|
+
for (const block of event.message.content) {
|
|
41
|
+
if (block.type === 'text') {
|
|
42
|
+
state.onOutput(block.text, 'text');
|
|
43
|
+
}
|
|
44
|
+
else if (block.type === 'tool_use') {
|
|
45
|
+
const desc = summarizeToolUse(block.name, block.input);
|
|
46
|
+
state.onOutput(`\n[tool: ${block.name}] ${desc}\n`, 'tool');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (event.type === 'content_block_delta' && event.delta?.text) {
|
|
51
|
+
state.onOutput(event.delta.text, 'text');
|
|
52
|
+
}
|
|
53
|
+
else if (event.type === 'result') {
|
|
54
|
+
const output = event.structured_output || event.result;
|
|
55
|
+
if (output) {
|
|
56
|
+
const resultStr = typeof output === 'string' ? output : JSON.stringify(output);
|
|
57
|
+
state.onOutput(resultStr, 'result');
|
|
58
|
+
}
|
|
59
|
+
else if (state.jsonSchema && event.subtype === 'error_max_turns') {
|
|
60
|
+
console.error(`⚠️ [bridge-stream] --json-schema used but model hit max_turns (${event.num_turns}) without producing structured_output`);
|
|
61
|
+
state.onOutput('[error] Model exhausted max turns before producing structured JSON output. Increase maxTurns.\n', 'text');
|
|
62
|
+
}
|
|
63
|
+
if (event.usage && state.onUsage) {
|
|
64
|
+
const model = event.model || state.model || 'unknown';
|
|
65
|
+
const inputTokens = event.usage.input_tokens || 0;
|
|
66
|
+
const outputTokens = event.usage.output_tokens || 0;
|
|
67
|
+
const costUsd = event.cost_usd ?? calculateCost(model, inputTokens, outputTokens);
|
|
68
|
+
state.onUsage({ inputTokens, outputTokens, model, costUsd });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (event.type === 'system') {
|
|
72
|
+
state.detectedModel = event.model || state.model || 'unknown';
|
|
73
|
+
state.onOutput(`[claude] session started (model: ${state.detectedModel})\n`, 'text');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
state.onOutput(line, 'text');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function summarizeToolUse(name, input) {
|
|
82
|
+
if (!input)
|
|
83
|
+
return name;
|
|
84
|
+
if (input.description)
|
|
85
|
+
return String(input.description);
|
|
86
|
+
if (input.command) {
|
|
87
|
+
const cmd = String(input.command);
|
|
88
|
+
return cmd.length > 120 ? cmd.slice(0, 120) + '…' : cmd;
|
|
89
|
+
}
|
|
90
|
+
const filePath = input.file_path || input.path;
|
|
91
|
+
if (filePath) {
|
|
92
|
+
const p = String(filePath);
|
|
93
|
+
const short = p.includes('/') ? p.split('/').slice(-2).join('/') : p;
|
|
94
|
+
if (name === 'Read')
|
|
95
|
+
return `Reading ${short}`;
|
|
96
|
+
if (name === 'Write')
|
|
97
|
+
return `Writing ${short}`;
|
|
98
|
+
if (name === 'Edit')
|
|
99
|
+
return `Editing ${short}`;
|
|
100
|
+
return `${name} ${short}`;
|
|
101
|
+
}
|
|
102
|
+
if (input.pattern) {
|
|
103
|
+
const pat = String(input.pattern);
|
|
104
|
+
if (name === 'Glob')
|
|
105
|
+
return `Finding files matching ${pat}`;
|
|
106
|
+
if (name === 'Grep')
|
|
107
|
+
return `Searching for "${pat}"`;
|
|
108
|
+
return `${name} "${pat}"`;
|
|
109
|
+
}
|
|
110
|
+
if (input.prompt) {
|
|
111
|
+
const prompt = String(input.prompt);
|
|
112
|
+
return prompt.length > 100 ? prompt.slice(0, 100) + '…' : prompt;
|
|
113
|
+
}
|
|
114
|
+
return name;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=stream-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-parser.js","sourceRoot":"","sources":["../stream-parser.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Platform Bridge — types (#294).
|
|
3
|
+
*
|
|
4
|
+
* The bridge is a single-purpose runner-side library that connects a developer
|
|
5
|
+
* Mac to the orchestrator via WebSocket and executes Apple-platform stages
|
|
6
|
+
* only (Xcode / Swift). Non-Apple dispatches are refused via
|
|
7
|
+
* `runner:scope_violation`.
|
|
8
|
+
*
|
|
9
|
+
* These types are duplicated narrowly from `src/types.ts` (the wire protocol)
|
|
10
|
+
* rather than imported, because the bridge ships as a standalone library with
|
|
11
|
+
* its own dependency surface — it must not depend on the orchestrator
|
|
12
|
+
* compilation root. The shapes are pinned by the cross-tree tests in
|
|
13
|
+
* `tests/unit/bridge-*.test.ts`.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|