@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
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @donna-orchestrator/bridge
|
|
2
|
+
|
|
3
|
+
Apple Platform Bridge — a small thin-client that connects a developer Mac to a
|
|
4
|
+
[Donna orchestrator](https://github.com/donna-orchestrator/donna-the-orchestrator-mvp)
|
|
5
|
+
and executes **Apple-platform stages only** (Xcode / Swift / xcodebuild).
|
|
6
|
+
|
|
7
|
+
The bridge is *not* a general-purpose macOS runner. It refuses non-Apple
|
|
8
|
+
dispatches with `runner:scope_violation` so it can stay narrowly scoped to the
|
|
9
|
+
one workload Docker can't host: building and testing iOS / macOS / watchOS /
|
|
10
|
+
tvOS / visionOS apps that need a real Xcode + signed simulator. For a full
|
|
11
|
+
runner that handles any stage, wait for issue #85.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install -g @donna-orchestrator/bridge
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The package only installs on `darwin` (`os: ["darwin"]` in `package.json`). It
|
|
20
|
+
requires Node `>=22`.
|
|
21
|
+
|
|
22
|
+
## Pair against an orchestrator
|
|
23
|
+
|
|
24
|
+
Two pairing flows are supported:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
# Same-Mac install — talks to a local orchestrator over loopback, no code:
|
|
28
|
+
donna-bridge install --orchestrator http://127.0.0.1:4000 --local-pair
|
|
29
|
+
|
|
30
|
+
# Remote orchestrator — paste the 6-digit code shown in Settings → Runners:
|
|
31
|
+
donna-bridge install --orchestrator https://donna.example.com --pairing-code 123456
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The installer registers a LaunchAgent that keeps the bridge running across
|
|
35
|
+
reboots. `donna-bridge status` shows the agent's state; `donna-bridge logs
|
|
36
|
+
--follow` tails the log file.
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
See [`docs/apple-platform-bridge/getting-started.md`](https://github.com/donna-orchestrator/donna-the-orchestrator-mvp/blob/main/docs/apple-platform-bridge/getting-started.md)
|
|
41
|
+
for the full setup walkthrough, troubleshooting, and migration notes.
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
Same as Donna (MIT).
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `donna-bridge` CLI entry point (#295).
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
* install --orchestrator <url> [--pairing-code <6-digit> | --local-pair] [--runner-id <id>]
|
|
7
|
+
* start [--inline]
|
|
8
|
+
* stop
|
|
9
|
+
* status
|
|
10
|
+
* logs [--follow]
|
|
11
|
+
* xcode select <version|--path <abs>>
|
|
12
|
+
* uninstall [--purge]
|
|
13
|
+
*
|
|
14
|
+
* No `commander` dependency — uses Node's `parseArgs` to keep `bridge/` free of
|
|
15
|
+
* non-`ws` runtime deps (matches `runner/native/cli.ts`).
|
|
16
|
+
*
|
|
17
|
+
* The bin entry that wires `donna-bridge` → this file is a #296 concern; this
|
|
18
|
+
* ticket implements the dispatcher as an exported `main(argv)` so the bin
|
|
19
|
+
* shim can be a one-line `import('./cli.js').then(({ main }) => main(process.argv))`.
|
|
20
|
+
*/
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { parseArgs } from 'node:util';
|
|
24
|
+
import { patchConfig, loadConfig, getConfigPath } from './config.js';
|
|
25
|
+
import { runInstall } from './install.js';
|
|
26
|
+
import { runLogs, runStart, runStatusCmd, runStop } from './lifecycle.js';
|
|
27
|
+
import { runUninstall } from './uninstall.js';
|
|
28
|
+
import { listXcodeBundles } from './xcode-probe.js';
|
|
29
|
+
const HELP = `donna-bridge — connect a developer Mac to a Donna orchestrator (Apple-platform stages only)
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
donna-bridge install --orchestrator <url> --pairing-code <6-digit> [--runner-id <id>]
|
|
33
|
+
donna-bridge install --orchestrator <url> --local-pair [--runner-id <id>]
|
|
34
|
+
donna-bridge start [--inline]
|
|
35
|
+
donna-bridge stop
|
|
36
|
+
donna-bridge status
|
|
37
|
+
donna-bridge logs [--follow]
|
|
38
|
+
donna-bridge xcode select <version>
|
|
39
|
+
donna-bridge xcode select --path <abs>
|
|
40
|
+
donna-bridge uninstall [--purge]
|
|
41
|
+
|
|
42
|
+
Subcommands:
|
|
43
|
+
install One-time pairing + LaunchAgent install
|
|
44
|
+
start Bootstrap the LaunchAgent (or run inline with --inline)
|
|
45
|
+
stop Bootout the LaunchAgent
|
|
46
|
+
status Print config + LaunchAgent state + last log line
|
|
47
|
+
logs Print the bridge log (use --follow to tail)
|
|
48
|
+
xcode Manage the bridge's active Xcode bundle
|
|
49
|
+
uninstall Remove LaunchAgent + token (use --purge for projects + simulators)
|
|
50
|
+
`;
|
|
51
|
+
const INSTALL_HELP = `donna-bridge install — pair with an orchestrator and install the LaunchAgent
|
|
52
|
+
|
|
53
|
+
Usage:
|
|
54
|
+
donna-bridge install --orchestrator <url> --pairing-code <6-digit> [--runner-id <id>]
|
|
55
|
+
donna-bridge install --orchestrator <url> --local-pair [--runner-id <id>]
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
--orchestrator Orchestrator WS URL (e.g. wss://donna.example.com/ws/v1/runners)
|
|
59
|
+
--pairing-code 6-digit pairing code shown by the orchestrator
|
|
60
|
+
--local-pair Skip the pairing code (loopback installs only; gated server-side)
|
|
61
|
+
--runner-id Override the runner ID (defaults to a slug of the hostname)
|
|
62
|
+
--bridge-bin Path to the donna-bridge binary baked into the LaunchAgent
|
|
63
|
+
--skip-launch-agent Skip launchctl bootstrap (testing / dry runs)
|
|
64
|
+
`;
|
|
65
|
+
const XCODE_HELP = `donna-bridge xcode — manage the bridge's active Xcode bundle
|
|
66
|
+
|
|
67
|
+
Usage:
|
|
68
|
+
donna-bridge xcode select <version>
|
|
69
|
+
donna-bridge xcode select --path <abs>
|
|
70
|
+
|
|
71
|
+
Subcommands:
|
|
72
|
+
select <version> Pick among /Applications/Xcode*.app bundles by marketing version
|
|
73
|
+
select --path <abs> Point config.xcodePath at an arbitrary Developer dir
|
|
74
|
+
|
|
75
|
+
The bridge maintains its own xcodePath in ~/.donna-bridge/config.json; this
|
|
76
|
+
does NOT change the system-wide \`xcode-select\` pointer.
|
|
77
|
+
`;
|
|
78
|
+
const LOGS_HELP = `donna-bridge logs — print the bridge log
|
|
79
|
+
|
|
80
|
+
Usage:
|
|
81
|
+
donna-bridge logs Dump the log file once and exit
|
|
82
|
+
donna-bridge logs --follow Tail the log file (Ctrl-C to stop)
|
|
83
|
+
`;
|
|
84
|
+
const UNINSTALL_HELP = `donna-bridge uninstall — reverse \`donna-bridge install\`
|
|
85
|
+
|
|
86
|
+
Usage:
|
|
87
|
+
donna-bridge uninstall # remove LaunchAgent + token; keep projects + simulators
|
|
88
|
+
donna-bridge uninstall --purge # also remove ~/donna-projects + simulator device set
|
|
89
|
+
|
|
90
|
+
The bridge never touches the orchestrator DB. Worktrees and the simulator
|
|
91
|
+
device set are kept by default — pass --purge to wipe them.
|
|
92
|
+
`;
|
|
93
|
+
function printHelpAndExit(text, code = 0) {
|
|
94
|
+
process.stdout.write(text);
|
|
95
|
+
process.exit(code);
|
|
96
|
+
}
|
|
97
|
+
function fail(message, code = 1) {
|
|
98
|
+
process.stderr.write(`${message}\n`);
|
|
99
|
+
process.exit(code);
|
|
100
|
+
}
|
|
101
|
+
export async function runInstallCmd(args) {
|
|
102
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
103
|
+
printHelpAndExit(INSTALL_HELP);
|
|
104
|
+
let parsed;
|
|
105
|
+
try {
|
|
106
|
+
parsed = parseArgs({
|
|
107
|
+
args,
|
|
108
|
+
options: {
|
|
109
|
+
orchestrator: { type: 'string' },
|
|
110
|
+
'pairing-code': { type: 'string' },
|
|
111
|
+
'local-pair': { type: 'boolean' },
|
|
112
|
+
'runner-id': { type: 'string' },
|
|
113
|
+
'bridge-bin': { type: 'string' },
|
|
114
|
+
'skip-launch-agent': { type: 'boolean' },
|
|
115
|
+
},
|
|
116
|
+
strict: true,
|
|
117
|
+
allowPositionals: false,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
fail(`install: ${err.message}\n\n${INSTALL_HELP}`);
|
|
122
|
+
}
|
|
123
|
+
const v = parsed.values;
|
|
124
|
+
if (!v.orchestrator)
|
|
125
|
+
fail(`install: --orchestrator is required\n\n${INSTALL_HELP}`);
|
|
126
|
+
if (!v['local-pair'] && !v['pairing-code']) {
|
|
127
|
+
fail(`install: --pairing-code or --local-pair is required\n\n${INSTALL_HELP}`);
|
|
128
|
+
}
|
|
129
|
+
if (v['local-pair'] && v['pairing-code']) {
|
|
130
|
+
fail(`install: --local-pair and --pairing-code are mutually exclusive\n\n${INSTALL_HELP}`);
|
|
131
|
+
}
|
|
132
|
+
const result = await runInstall({
|
|
133
|
+
orchestratorUrl: v.orchestrator,
|
|
134
|
+
pairingCode: v['pairing-code'],
|
|
135
|
+
localPair: v['local-pair'] === true,
|
|
136
|
+
runnerId: v['runner-id'],
|
|
137
|
+
bridgeBin: v['bridge-bin'],
|
|
138
|
+
skipLaunchAgent: v['skip-launch-agent'] === true,
|
|
139
|
+
});
|
|
140
|
+
return result.ok ? 0 : 1;
|
|
141
|
+
}
|
|
142
|
+
export async function runStartCmd(args) {
|
|
143
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
144
|
+
printHelpAndExit(HELP);
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = parseArgs({
|
|
148
|
+
args,
|
|
149
|
+
options: { inline: { type: 'boolean' } },
|
|
150
|
+
strict: true,
|
|
151
|
+
allowPositionals: false,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
fail(`start: ${err.message}`);
|
|
156
|
+
}
|
|
157
|
+
return runStart({ inline: parsed.values.inline === true });
|
|
158
|
+
}
|
|
159
|
+
export async function runStopCmd(args) {
|
|
160
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
161
|
+
printHelpAndExit(HELP);
|
|
162
|
+
return runStop();
|
|
163
|
+
}
|
|
164
|
+
export async function runStatusCmdEntry(args) {
|
|
165
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
166
|
+
printHelpAndExit(HELP);
|
|
167
|
+
return runStatusCmd();
|
|
168
|
+
}
|
|
169
|
+
export async function runLogsCmd(args) {
|
|
170
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
171
|
+
printHelpAndExit(LOGS_HELP);
|
|
172
|
+
let parsed;
|
|
173
|
+
try {
|
|
174
|
+
parsed = parseArgs({
|
|
175
|
+
args,
|
|
176
|
+
options: { follow: { type: 'boolean' } },
|
|
177
|
+
strict: true,
|
|
178
|
+
allowPositionals: false,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
fail(`logs: ${err.message}\n\n${LOGS_HELP}`);
|
|
183
|
+
}
|
|
184
|
+
return runLogs({ follow: parsed.values.follow === true });
|
|
185
|
+
}
|
|
186
|
+
export async function runXcodeCmd(args) {
|
|
187
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
188
|
+
printHelpAndExit(XCODE_HELP, args.length === 0 ? 1 : 0);
|
|
189
|
+
}
|
|
190
|
+
const [sub, ...rest] = args;
|
|
191
|
+
if (sub !== 'select') {
|
|
192
|
+
fail(`xcode: unknown subcommand "${sub}"\n\n${XCODE_HELP}`);
|
|
193
|
+
}
|
|
194
|
+
// Two shapes: `select <version>` OR `select --path <abs>`. The runner CLI
|
|
195
|
+
// takes the same approach (#290) — we copy it here.
|
|
196
|
+
const pathIdx = rest.indexOf('--path');
|
|
197
|
+
if (pathIdx !== -1) {
|
|
198
|
+
const abs = rest[pathIdx + 1];
|
|
199
|
+
if (!abs)
|
|
200
|
+
fail(`xcode select: --path requires an absolute Developer dir\n\n${XCODE_HELP}`);
|
|
201
|
+
if (!path.isAbsolute(abs)) {
|
|
202
|
+
fail(`xcode select: --path argument must be absolute (got: ${abs})`);
|
|
203
|
+
}
|
|
204
|
+
if (!fs.existsSync(abs)) {
|
|
205
|
+
fail(`xcode select: --path ${abs} does not exist`);
|
|
206
|
+
}
|
|
207
|
+
if (!loadConfig()) {
|
|
208
|
+
fail(`xcode select: no config at ${getConfigPath()}. Run \`donna-bridge install\` first.`);
|
|
209
|
+
}
|
|
210
|
+
patchConfig({ xcodePath: abs });
|
|
211
|
+
process.stdout.write(`✅ Selected Xcode developer dir ${abs}\n`);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
const [version, ...extra] = rest;
|
|
215
|
+
if (!version)
|
|
216
|
+
fail(`xcode select: <version> or --path <abs> required\n\n${XCODE_HELP}`);
|
|
217
|
+
if (extra.length > 0) {
|
|
218
|
+
fail(`xcode select: unexpected extra arguments: ${extra.join(' ')}\n\n${XCODE_HELP}`);
|
|
219
|
+
}
|
|
220
|
+
if (!loadConfig()) {
|
|
221
|
+
fail(`xcode select: no config at ${getConfigPath()}. Run \`donna-bridge install\` first.`);
|
|
222
|
+
}
|
|
223
|
+
const bundles = await listXcodeBundles();
|
|
224
|
+
const match = bundles.find((b) => b.version === version);
|
|
225
|
+
if (!match) {
|
|
226
|
+
const available = bundles
|
|
227
|
+
.map((b) => ` ${b.version ?? '(no version)'} → ${b.path}`)
|
|
228
|
+
.join('\n');
|
|
229
|
+
fail(`xcode select: no Xcode bundle with version ${version} found.\n` +
|
|
230
|
+
`Available bundles:\n${available || ' (none)'}\n\nUse \`--path\` to point at an arbitrary Developer dir.`);
|
|
231
|
+
}
|
|
232
|
+
patchConfig({ xcodePath: match.developerDir });
|
|
233
|
+
process.stdout.write(`✅ Selected Xcode ${match.version} (${match.developerDir})\n`);
|
|
234
|
+
return 0;
|
|
235
|
+
}
|
|
236
|
+
export async function runUninstallCmd(args) {
|
|
237
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
238
|
+
printHelpAndExit(UNINSTALL_HELP);
|
|
239
|
+
let parsed;
|
|
240
|
+
try {
|
|
241
|
+
parsed = parseArgs({
|
|
242
|
+
args,
|
|
243
|
+
options: {
|
|
244
|
+
purge: { type: 'boolean' },
|
|
245
|
+
yes: { type: 'boolean' },
|
|
246
|
+
},
|
|
247
|
+
strict: true,
|
|
248
|
+
allowPositionals: false,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
fail(`uninstall: ${err.message}\n\n${UNINSTALL_HELP}`);
|
|
253
|
+
}
|
|
254
|
+
await runUninstall({
|
|
255
|
+
purge: parsed.values.purge === true,
|
|
256
|
+
yes: parsed.values.yes === true,
|
|
257
|
+
});
|
|
258
|
+
return 0;
|
|
259
|
+
}
|
|
260
|
+
export async function main(argv) {
|
|
261
|
+
const [, , subcommand, ...rest] = argv;
|
|
262
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
263
|
+
printHelpAndExit(HELP);
|
|
264
|
+
}
|
|
265
|
+
let exit = 0;
|
|
266
|
+
switch (subcommand) {
|
|
267
|
+
case 'install':
|
|
268
|
+
exit = await runInstallCmd(rest);
|
|
269
|
+
break;
|
|
270
|
+
case 'start':
|
|
271
|
+
exit = await runStartCmd(rest);
|
|
272
|
+
break;
|
|
273
|
+
case 'stop':
|
|
274
|
+
exit = await runStopCmd(rest);
|
|
275
|
+
break;
|
|
276
|
+
case 'status':
|
|
277
|
+
exit = await runStatusCmdEntry(rest);
|
|
278
|
+
break;
|
|
279
|
+
case 'logs':
|
|
280
|
+
exit = await runLogsCmd(rest);
|
|
281
|
+
break;
|
|
282
|
+
case 'xcode':
|
|
283
|
+
exit = await runXcodeCmd(rest);
|
|
284
|
+
break;
|
|
285
|
+
case 'uninstall':
|
|
286
|
+
exit = await runUninstallCmd(rest);
|
|
287
|
+
break;
|
|
288
|
+
default:
|
|
289
|
+
fail(`Unknown subcommand: ${subcommand}\n\n${HELP}`);
|
|
290
|
+
}
|
|
291
|
+
if (exit !== 0)
|
|
292
|
+
process.exit(exit);
|
|
293
|
+
}
|
|
294
|
+
// Only run when invoked as the entry point. The test suite imports this
|
|
295
|
+
// module to drive individual subcommand functions; main() must NOT run during
|
|
296
|
+
// import.
|
|
297
|
+
if (import.meta.url === `file://${process.argv[1]}` ||
|
|
298
|
+
process.argv[1]?.endsWith('cli.js')) {
|
|
299
|
+
main(process.argv).catch((err) => fail(err instanceof Error ? err.message : String(err)));
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge config — `~/.donna-bridge/config.json` (#295).
|
|
3
|
+
*
|
|
4
|
+
* Distinct from `~/.donna-runner/` so a developer Mac can host both a full
|
|
5
|
+
* native runner and the narrow Apple-platform bridge without their config
|
|
6
|
+
* files stepping on each other (the spec explicitly calls this out).
|
|
7
|
+
*
|
|
8
|
+
* Only `orchestratorUrl`, `token`, `runnerId`, and `xcodePath` are required
|
|
9
|
+
* to start the bridge; `simulatorDeviceSetPath` is optional and populated by
|
|
10
|
+
* the install flow (defaults to `~/Library/Developer/CoreSimulator/DonnaDevices`).
|
|
11
|
+
*
|
|
12
|
+
* `DONNA_BRIDGE_CONFIG_DIR` overrides the directory for tests.
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import os from 'node:os';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
const DEFAULT_DEVICE_SET = path.join(os.homedir(), 'Library', 'Developer', 'CoreSimulator', 'DonnaDevices');
|
|
18
|
+
const DEFAULT_LOG_PATH = path.join(os.homedir(), 'Library', 'Logs', 'Donna', 'bridge.log');
|
|
19
|
+
const DEFAULT_PLIST_PATH = path.join(os.homedir(), 'Library', 'LaunchAgents', 'net.donna.bridge.plist');
|
|
20
|
+
const DEFAULT_LABEL = 'net.donna.bridge';
|
|
21
|
+
const DEFAULT_PROJECTS_DIR = path.join(os.homedir(), 'donna-projects');
|
|
22
|
+
export function getConfigDir() {
|
|
23
|
+
const override = process.env.DONNA_BRIDGE_CONFIG_DIR;
|
|
24
|
+
if (override && override.length > 0)
|
|
25
|
+
return override;
|
|
26
|
+
return path.join(os.homedir(), '.donna-bridge');
|
|
27
|
+
}
|
|
28
|
+
export function getConfigPath() {
|
|
29
|
+
return path.join(getConfigDir(), 'config.json');
|
|
30
|
+
}
|
|
31
|
+
export function getToolReportPath() {
|
|
32
|
+
return path.join(getConfigDir(), 'tool-report.json');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolved paths the rest of the CLI keys off of. Centralized here so tests
|
|
36
|
+
* can override directory roots via `DONNA_BRIDGE_CONFIG_DIR` / `HOME`.
|
|
37
|
+
*/
|
|
38
|
+
export function getDefaults() {
|
|
39
|
+
return {
|
|
40
|
+
deviceSetPath: DEFAULT_DEVICE_SET,
|
|
41
|
+
logPath: DEFAULT_LOG_PATH,
|
|
42
|
+
plistPath: DEFAULT_PLIST_PATH,
|
|
43
|
+
label: DEFAULT_LABEL,
|
|
44
|
+
projectsDir: DEFAULT_PROJECTS_DIR,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function loadConfig() {
|
|
48
|
+
const file = getConfigPath();
|
|
49
|
+
if (!fs.existsSync(file))
|
|
50
|
+
return null;
|
|
51
|
+
const raw = fs.readFileSync(file, 'utf-8');
|
|
52
|
+
return JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Atomic write with 0600 perms — the token is a long-lived JWT and must not
|
|
56
|
+
* be world-readable. Pattern mirrors `runner/native/config.ts`.
|
|
57
|
+
*/
|
|
58
|
+
export function saveConfig(config) {
|
|
59
|
+
const dir = getConfigDir();
|
|
60
|
+
if (!fs.existsSync(dir)) {
|
|
61
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
62
|
+
}
|
|
63
|
+
const target = getConfigPath();
|
|
64
|
+
const tmp = `${target}.tmp`;
|
|
65
|
+
fs.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
66
|
+
fs.renameSync(tmp, target);
|
|
67
|
+
fs.chmodSync(target, 0o600);
|
|
68
|
+
}
|
|
69
|
+
/** Delete the config file (used by uninstall). No-op when absent. */
|
|
70
|
+
export function deleteConfig() {
|
|
71
|
+
const file = getConfigPath();
|
|
72
|
+
if (fs.existsSync(file)) {
|
|
73
|
+
fs.rmSync(file, { force: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function defaultConfig(orchestratorUrl, token, runnerId) {
|
|
77
|
+
return {
|
|
78
|
+
orchestratorUrl,
|
|
79
|
+
token,
|
|
80
|
+
runnerId,
|
|
81
|
+
simulatorDeviceSetPath: getDefaults().deviceSetPath,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Update specific fields without clobbering other state. Used by
|
|
86
|
+
* `donna-bridge xcode select` and by the install flow once it knows the
|
|
87
|
+
* resolved Xcode path.
|
|
88
|
+
*/
|
|
89
|
+
export function patchConfig(patch) {
|
|
90
|
+
const existing = loadConfig();
|
|
91
|
+
if (!existing) {
|
|
92
|
+
throw new Error(`patchConfig: no config found at ${getConfigPath()}. Run \`donna-bridge install\` first.`);
|
|
93
|
+
}
|
|
94
|
+
const merged = { ...existing, ...patch };
|
|
95
|
+
saveConfig(merged);
|
|
96
|
+
return merged;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../config.ts"],"names":[],"mappings":"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"}
|
package/dist/executor.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Platform Bridge — executor (#294).
|
|
3
|
+
*
|
|
4
|
+
* Spawns `claude --print` with Apple-platform env injection. Modelled on
|
|
5
|
+
* `runner/executor.ts`'s native path, but trimmed: no sandbox tier (the
|
|
6
|
+
* bridge runs as the developer's own user — sandboxing is #86 follow-up),
|
|
7
|
+
* no plugin loading, no MCP merging beyond passing the supplied config
|
|
8
|
+
* through. The bridge is a narrow library, deliberately.
|
|
9
|
+
*
|
|
10
|
+
* The stream-json parser is reused from `runner/stream-parser.ts` so the
|
|
11
|
+
* two executors stay aligned on parsing semantics. The ticket explicitly
|
|
12
|
+
* recommends this (see "Open Questions" in 294).
|
|
13
|
+
*/
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
import { processStreamLine, MAX_BUFFER_SIZE } from './stream-parser.js';
|
|
16
|
+
/**
|
|
17
|
+
* Marker prefix the bridge emits when the Claude CLI binary is missing or
|
|
18
|
+
* not executable. Mirrors `CLI_BINARY_MISSING` from `runner/executor.ts`.
|
|
19
|
+
* The orchestrator (#260) routes on this marker.
|
|
20
|
+
*/
|
|
21
|
+
export const BRIDGE_CLI_MISSING_MARKER = 'CLI_BINARY_MISSING';
|
|
22
|
+
/**
|
|
23
|
+
* Spawn `claude --print` for an Apple-platform stage. Returns the exit code.
|
|
24
|
+
*
|
|
25
|
+
* Env injection:
|
|
26
|
+
* - `DEVELOPER_DIR` from `config.xcodePath`
|
|
27
|
+
* - `CORE_SIMULATOR_DEVICE_SET_PATH` from `config.simulatorDeviceSetPath`
|
|
28
|
+
* - `payload.env` (caller-supplied; wins over the above on collision —
|
|
29
|
+
* matches `runner/executor.ts` semantics for `extraEnv`)
|
|
30
|
+
*
|
|
31
|
+
* Notes:
|
|
32
|
+
* - The bridge is a native runner. Unlike Docker runners it does NOT add
|
|
33
|
+
* `--dangerously-skip-permissions`. Sandboxing for the spawned process
|
|
34
|
+
* is out of scope here (#86).
|
|
35
|
+
* - When `payload.jsonSchema` is set we use `--output-format json`,
|
|
36
|
+
* otherwise `--output-format stream-json` (same logic as the runner).
|
|
37
|
+
*/
|
|
38
|
+
export function executeBridgeCLI(options) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const { payload, config, onOutput, onUsage, onRateLimit, onSpawn } = options;
|
|
41
|
+
const outputFormat = payload.jsonSchema ? 'json' : 'stream-json';
|
|
42
|
+
const args = ['--print', '--output-format', outputFormat];
|
|
43
|
+
if (outputFormat === 'stream-json' || payload.verbose) {
|
|
44
|
+
args.push('--verbose');
|
|
45
|
+
}
|
|
46
|
+
if (payload.model)
|
|
47
|
+
args.push('--model', payload.model);
|
|
48
|
+
if (payload.fallbackModel)
|
|
49
|
+
args.push('--fallback-model', payload.fallbackModel);
|
|
50
|
+
if (payload.maxBudgetUsd)
|
|
51
|
+
args.push('--max-budget-usd', String(payload.maxBudgetUsd));
|
|
52
|
+
if (payload.allowedTools?.length)
|
|
53
|
+
args.push('--allowedTools', payload.allowedTools.join(','));
|
|
54
|
+
if (payload.disallowedTools?.length)
|
|
55
|
+
args.push('--disallowedTools', payload.disallowedTools.join(','));
|
|
56
|
+
if (payload.maxTurns)
|
|
57
|
+
args.push('--max-turns', String(payload.maxTurns));
|
|
58
|
+
if (payload.appendSystemPrompt)
|
|
59
|
+
args.push('--append-system-prompt', payload.appendSystemPrompt);
|
|
60
|
+
if (payload.jsonSchema)
|
|
61
|
+
args.push('--json-schema', payload.jsonSchema);
|
|
62
|
+
if (payload.mcpConfig)
|
|
63
|
+
args.push('--mcp-config', payload.mcpConfig);
|
|
64
|
+
if (payload.pluginDirs?.length) {
|
|
65
|
+
for (const dir of payload.pluginDirs) {
|
|
66
|
+
args.push('--plugin-dir', dir);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Prompt via stdin to avoid E2BIG on large prompts (#260 / runner parity).
|
|
70
|
+
args.push('--');
|
|
71
|
+
args.push('-');
|
|
72
|
+
const state = {
|
|
73
|
+
onOutput,
|
|
74
|
+
onUsage,
|
|
75
|
+
jsonSchema: payload.jsonSchema,
|
|
76
|
+
model: payload.model,
|
|
77
|
+
detectedModel: payload.model || 'unknown',
|
|
78
|
+
};
|
|
79
|
+
const claudeBin = config.claudeBin || process.env.CLAUDE_BIN || 'claude';
|
|
80
|
+
// Env injection — DEVELOPER_DIR + CORE_SIMULATOR_DEVICE_SET_PATH are the
|
|
81
|
+
// whole point of this bridge. Spread caller env LAST so per-card overrides
|
|
82
|
+
// (e.g. GIT_AUTHOR_NAME for commits) win, matching `runner/executor.ts`.
|
|
83
|
+
const env = {
|
|
84
|
+
...process.env,
|
|
85
|
+
DEVELOPER_DIR: config.xcodePath,
|
|
86
|
+
CORE_SIMULATOR_DEVICE_SET_PATH: config.simulatorDeviceSetPath,
|
|
87
|
+
...(payload.env || {}),
|
|
88
|
+
};
|
|
89
|
+
let proc;
|
|
90
|
+
try {
|
|
91
|
+
proc = spawn(claudeBin, args, {
|
|
92
|
+
cwd: payload.workDir,
|
|
93
|
+
env,
|
|
94
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
95
|
+
// detached so a future kill switch can SIGTERM the process group.
|
|
96
|
+
detached: process.platform !== 'win32',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const e = err;
|
|
101
|
+
if (e.code === 'ENOENT' || e.code === 'EACCES') {
|
|
102
|
+
reject(new Error(`${BRIDGE_CLI_MISSING_MARKER}: spawn ${e.code} for ${claudeBin} — ${e.message}`));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
reject(new Error(`Failed to spawn claude CLI: ${e.message}`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
onSpawn?.(proc);
|
|
109
|
+
// Push the prompt over stdin.
|
|
110
|
+
proc.stdin?.write(payload.prompt);
|
|
111
|
+
proc.stdin?.end();
|
|
112
|
+
let buffer = '';
|
|
113
|
+
proc.stdout?.on('data', (data) => {
|
|
114
|
+
buffer += data.toString();
|
|
115
|
+
if (buffer.length > MAX_BUFFER_SIZE) {
|
|
116
|
+
const truncated = buffer.length - MAX_BUFFER_SIZE;
|
|
117
|
+
buffer = buffer.slice(-MAX_BUFFER_SIZE);
|
|
118
|
+
console.warn(`[bridge-executor] Buffer truncated: dropped ${truncated} bytes from front`);
|
|
119
|
+
}
|
|
120
|
+
const lines = buffer.split('\n');
|
|
121
|
+
buffer = lines.pop() || '';
|
|
122
|
+
for (const line of lines) {
|
|
123
|
+
processStreamLine(line, state);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
proc.stderr?.on('data', (data) => {
|
|
127
|
+
const text = data.toString();
|
|
128
|
+
onOutput(`[stderr] ${text}`, 'stderr');
|
|
129
|
+
const lower = text.toLowerCase();
|
|
130
|
+
if ((lower.includes('429') || lower.includes('rate_limit') || lower.includes('overloaded') || lower.includes('rate limit')) &&
|
|
131
|
+
onRateLimit) {
|
|
132
|
+
onRateLimit();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
proc.on('error', (err) => {
|
|
136
|
+
if (err.code === 'ENOENT' || err.code === 'EACCES') {
|
|
137
|
+
reject(new Error(`${BRIDGE_CLI_MISSING_MARKER}: spawn ${err.code} for ${claudeBin} — ${err.message}`));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
reject(new Error(`Failed to spawn claude CLI: ${err.message}`));
|
|
141
|
+
});
|
|
142
|
+
proc.on('close', (code) => {
|
|
143
|
+
if (buffer.trim()) {
|
|
144
|
+
processStreamLine(buffer, state);
|
|
145
|
+
}
|
|
146
|
+
resolve(code ?? 1);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../executor.ts"],"names":[],"mappings":"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"}
|