@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/dist/index.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Platform Bridge — entry point (#294).
|
|
3
|
+
*
|
|
4
|
+
* Library form of the bridge: connect to the orchestrator, advertise the
|
|
5
|
+
* `apple_platform: true` capability, dispatch incoming work, refuse non-Apple
|
|
6
|
+
* dispatches with `runner:scope_violation`. No CLI surface (#295), no
|
|
7
|
+
* LaunchAgent (#295), no publish setup (#296) — those layer on top.
|
|
8
|
+
*
|
|
9
|
+
* The WS connection loop mirrors `runner/native/connection.ts`:
|
|
10
|
+
* - `Authorization: Bearer <token>` header (paired runners only, per #272).
|
|
11
|
+
* - Indefinite reconnect with exponential backoff capped at 30s.
|
|
12
|
+
*
|
|
13
|
+
* The bridge is single-purpose: every `runner:execute` is gated by
|
|
14
|
+
* `scope-guard.isAppleStage`. Refused dispatches reply with
|
|
15
|
+
* `runner:scope_violation` and return to idle without retrying.
|
|
16
|
+
*/
|
|
17
|
+
import WebSocket from 'ws';
|
|
18
|
+
import { executeBridgeCLI } from './executor.js';
|
|
19
|
+
import { isAppleStage } from './scope-guard.js';
|
|
20
|
+
const RECONNECT_CAP_MS = 30_000;
|
|
21
|
+
const SCOPE_VIOLATION_MESSAGE = 'donna-bridge accepts only Apple platform projects. This dispatch was rejected.';
|
|
22
|
+
export function runBridge(opts) {
|
|
23
|
+
const { config, workspace, onStateChange } = opts;
|
|
24
|
+
const WS = opts.webSocketImpl || WebSocket;
|
|
25
|
+
let ws = null;
|
|
26
|
+
let reconnectAttempts = 0;
|
|
27
|
+
let stopped = false;
|
|
28
|
+
let reconnectTimer = null;
|
|
29
|
+
function safeSend(payload) {
|
|
30
|
+
if (!ws || ws.readyState !== WebSocket.OPEN)
|
|
31
|
+
return;
|
|
32
|
+
ws.send(JSON.stringify(payload));
|
|
33
|
+
}
|
|
34
|
+
function sendScopeViolation(cardId) {
|
|
35
|
+
safeSend({
|
|
36
|
+
type: 'runner:scope_violation',
|
|
37
|
+
payload: {
|
|
38
|
+
cardId,
|
|
39
|
+
reason: 'not_apple_platform',
|
|
40
|
+
message: SCOPE_VIOLATION_MESSAGE,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function handleExecute(payload) {
|
|
45
|
+
if (!isAppleStage(payload)) {
|
|
46
|
+
console.log(`🚫 Bridge ${config.runnerId}: refusing non-Apple dispatch card=${payload.cardId.slice(0, 8)} stage=${payload.stage}`);
|
|
47
|
+
sendScopeViolation(payload.cardId);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
let usageData = null;
|
|
52
|
+
try {
|
|
53
|
+
const exitCode = await executeBridgeCLI({
|
|
54
|
+
payload,
|
|
55
|
+
config,
|
|
56
|
+
onOutput: (chunk, kind) => {
|
|
57
|
+
safeSend({
|
|
58
|
+
type: 'runner:output',
|
|
59
|
+
payload: { cardId: payload.cardId, chunk, kind },
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
onUsage: (usage) => {
|
|
63
|
+
usageData = usage;
|
|
64
|
+
},
|
|
65
|
+
onRateLimit: () => {
|
|
66
|
+
safeSend({ type: 'runner:rate_limit', payload: { cardId: payload.cardId } });
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
const duration = Date.now() - startTime;
|
|
70
|
+
safeSend({
|
|
71
|
+
type: 'runner:command_complete',
|
|
72
|
+
payload: {
|
|
73
|
+
cardId: payload.cardId,
|
|
74
|
+
exitCode,
|
|
75
|
+
duration,
|
|
76
|
+
usage: usageData || undefined,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
82
|
+
console.error(`❌ Bridge ${config.runnerId}: execute failed card=${payload.cardId.slice(0, 8)}: ${errMsg}`);
|
|
83
|
+
safeSend({
|
|
84
|
+
type: 'runner:command_error',
|
|
85
|
+
payload: { cardId: payload.cardId, error: errMsg },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function handleSetupWorkspace(req) {
|
|
90
|
+
try {
|
|
91
|
+
const result = await workspace.setup(req);
|
|
92
|
+
if (result.ok) {
|
|
93
|
+
safeSend({
|
|
94
|
+
type: 'runner:workspace_ready',
|
|
95
|
+
payload: { cardId: req.cardId, workDir: result.workDir, commit: result.commit },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
safeSend({
|
|
100
|
+
type: 'runner:workspace_error',
|
|
101
|
+
payload: { cardId: req.cardId, errorClass: result.errorClass, error: result.error },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
107
|
+
safeSend({
|
|
108
|
+
type: 'runner:workspace_error',
|
|
109
|
+
payload: { cardId: req.cardId, errorClass: 'unknown', error: `setup threw: ${errMsg}` },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function handleCleanupWorkspace(req) {
|
|
114
|
+
try {
|
|
115
|
+
await workspace.cleanup(req);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
console.error(`❌ Bridge ${config.runnerId}: cleanup threw for card=${req.cardId.slice(0, 8)}:`, err instanceof Error ? err.message : err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function handleMessage(msg) {
|
|
122
|
+
if (msg.type !== 'runner:ping') {
|
|
123
|
+
console.log(`💬 Bridge ${config.runnerId}: received ${msg.type}`);
|
|
124
|
+
}
|
|
125
|
+
switch (msg.type) {
|
|
126
|
+
case 'runner:ping':
|
|
127
|
+
safeSend({ type: 'runner:pong' });
|
|
128
|
+
break;
|
|
129
|
+
case 'runner:health_check':
|
|
130
|
+
// Minimal health-ack — the CLI layer (#295) will re-probe Xcode and
|
|
131
|
+
// claude here. The bridge core just confirms it's alive.
|
|
132
|
+
safeSend({
|
|
133
|
+
type: 'runner:health_check_ack',
|
|
134
|
+
payload: {
|
|
135
|
+
nonce: msg.payload.nonce,
|
|
136
|
+
claudeCli: { ok: true },
|
|
137
|
+
timestamp: new Date().toISOString(),
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
case 'runner:shutdown':
|
|
142
|
+
console.log(`🛑 Bridge ${config.runnerId}: orchestrator requested shutdown: ${msg.payload.reason}`);
|
|
143
|
+
stopped = true;
|
|
144
|
+
ws?.close();
|
|
145
|
+
break;
|
|
146
|
+
case 'runner:execute':
|
|
147
|
+
void handleExecute(msg.payload);
|
|
148
|
+
break;
|
|
149
|
+
case 'runner:setup_workspace':
|
|
150
|
+
void handleSetupWorkspace(msg.payload);
|
|
151
|
+
break;
|
|
152
|
+
case 'runner:cleanup_workspace':
|
|
153
|
+
void handleCleanupWorkspace(msg.payload);
|
|
154
|
+
break;
|
|
155
|
+
default: {
|
|
156
|
+
// Unknown / unsupported message — log and ignore. We deliberately do
|
|
157
|
+
// NOT acknowledge so the orchestrator's protocol-version drift is
|
|
158
|
+
// visible in its own logs.
|
|
159
|
+
const t = msg.type ?? 'unknown';
|
|
160
|
+
console.warn(`⚠️ Bridge ${config.runnerId}: ignoring unsupported message type=${t}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function connect() {
|
|
165
|
+
if (stopped)
|
|
166
|
+
return;
|
|
167
|
+
onStateChange?.('connecting');
|
|
168
|
+
console.log(`🔌 Bridge ${config.runnerId}: connecting to ${config.orchestratorUrl}`);
|
|
169
|
+
ws = new WS(config.orchestratorUrl, {
|
|
170
|
+
headers: { Authorization: `Bearer ${config.token}` },
|
|
171
|
+
});
|
|
172
|
+
ws.on('open', () => {
|
|
173
|
+
console.log(`🟢 Bridge ${config.runnerId}: connected`);
|
|
174
|
+
reconnectAttempts = 0;
|
|
175
|
+
onStateChange?.('connected');
|
|
176
|
+
// Hello payload — advertise the Apple-platform capability that #293's
|
|
177
|
+
// matcher routes on, plus the standard tool versions block.
|
|
178
|
+
safeSend({
|
|
179
|
+
type: 'runner:hello',
|
|
180
|
+
payload: {
|
|
181
|
+
runnerId: config.runnerId,
|
|
182
|
+
runnerType: 'native-macos',
|
|
183
|
+
platform: 'darwin',
|
|
184
|
+
arch: process.arch === 'arm64' ? 'arm64' : 'x86_64',
|
|
185
|
+
capabilities: {
|
|
186
|
+
apple_platform: true,
|
|
187
|
+
// Top-level claude_cli flag for parity with #260's handshake check.
|
|
188
|
+
// The CLI layer (#295) probes the binary; here we trust the config.
|
|
189
|
+
claude_cli: true,
|
|
190
|
+
tools: config.tools || {},
|
|
191
|
+
},
|
|
192
|
+
image: null,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
ws.on('message', (data) => {
|
|
197
|
+
let msg;
|
|
198
|
+
try {
|
|
199
|
+
msg = JSON.parse(data.toString());
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
console.error(`❌ Bridge ${config.runnerId}: failed to parse message:`, err);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
handleMessage(msg);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
console.error(`❌ Bridge ${config.runnerId}: handler threw:`, err);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
ws.on('close', () => {
|
|
213
|
+
console.log(`🔴 Bridge ${config.runnerId}: disconnected`);
|
|
214
|
+
onStateChange?.('disconnected');
|
|
215
|
+
ws = null;
|
|
216
|
+
attemptReconnect();
|
|
217
|
+
});
|
|
218
|
+
ws.on('error', (err) => {
|
|
219
|
+
console.error(`❌ Bridge ${config.runnerId}: WS error:`, err.message);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function attemptReconnect() {
|
|
223
|
+
if (stopped)
|
|
224
|
+
return;
|
|
225
|
+
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), RECONNECT_CAP_MS);
|
|
226
|
+
reconnectAttempts++;
|
|
227
|
+
console.log(`🔄 Bridge ${config.runnerId}: reconnecting in ${delay}ms (attempt ${reconnectAttempts})`);
|
|
228
|
+
reconnectTimer = setTimeout(connect, delay);
|
|
229
|
+
}
|
|
230
|
+
connect();
|
|
231
|
+
return {
|
|
232
|
+
close() {
|
|
233
|
+
stopped = true;
|
|
234
|
+
if (reconnectTimer) {
|
|
235
|
+
clearTimeout(reconnectTimer);
|
|
236
|
+
reconnectTimer = null;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
ws?.close();
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// ignore
|
|
243
|
+
}
|
|
244
|
+
ws = null;
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `donna-bridge install` flow (#295).
|
|
3
|
+
*
|
|
4
|
+
* Six steps, per the spec:
|
|
5
|
+
* 1. Pre-flight: claude / xcodebuild / git / node ≥ 22
|
|
6
|
+
* 2. Probe Xcode + simctl + swift → write `~/.donna-bridge/tool-report.json`
|
|
7
|
+
* 3. Pair with the orchestrator (remote 6-digit code OR `--local-pair`)
|
|
8
|
+
* 4. Persist config → `~/.donna-bridge/config.json`
|
|
9
|
+
* 5. Render + install LaunchAgent plist + `launchctl bootstrap`
|
|
10
|
+
* 6. Print summary
|
|
11
|
+
*
|
|
12
|
+
* Each step has a clear error surface: pre-flight failures list every missing
|
|
13
|
+
* tool's install hint; pairing failures show the HTTP status; LaunchAgent
|
|
14
|
+
* failures surface the `launchctl` stderr.
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { defaultConfig, getConfigPath, getDefaults, saveConfig, } from './config.js';
|
|
20
|
+
import { runPreflight, formatPreflightTable } from './preflight.js';
|
|
21
|
+
import { runXcodeProbe } from './xcode-probe.js';
|
|
22
|
+
import { pairLocally, pairWithCode } from './pair.js';
|
|
23
|
+
import { installPlist, loadAgent, renderBridgePlist } from './launchd.js';
|
|
24
|
+
function defaultIO() {
|
|
25
|
+
return {
|
|
26
|
+
stdout: (m) => process.stdout.write(m),
|
|
27
|
+
stderr: (m) => process.stderr.write(m),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Slugify the hostname to a stable runner-id (matches runner-side rules). */
|
|
31
|
+
export function hostnameRunnerId(hostname = os.hostname()) {
|
|
32
|
+
let slug = hostname.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
33
|
+
if (slug.length < 3)
|
|
34
|
+
slug = `bridge-${slug || 'host'}`.slice(0, 64);
|
|
35
|
+
if (slug.length > 64)
|
|
36
|
+
slug = slug.slice(0, 64);
|
|
37
|
+
slug = slug.replace(/^-+|-+$/g, '');
|
|
38
|
+
if (slug.length < 3)
|
|
39
|
+
slug = `bridge-${slug || 'host'}`.slice(0, 64);
|
|
40
|
+
return slug;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pick the active Xcode bundle from a probe report. Priority:
|
|
44
|
+
* 1. The bundle whose `developerDir` matches the system `xcode-select -p`
|
|
45
|
+
* pointer (handled at the install layer — we don't shell out here).
|
|
46
|
+
* 2. The first bundle that has a version (probe succeeded).
|
|
47
|
+
* 3. The first bundle, period.
|
|
48
|
+
*
|
|
49
|
+
* Returns `null` when no Xcode bundles are present.
|
|
50
|
+
*/
|
|
51
|
+
export function pickActiveXcode(report) {
|
|
52
|
+
if (report.xcodeBundles.length === 0)
|
|
53
|
+
return null;
|
|
54
|
+
const versioned = report.xcodeBundles.find((b) => b.version);
|
|
55
|
+
const pick = versioned ?? report.xcodeBundles[0];
|
|
56
|
+
return { path: pick.path, developerDir: pick.developerDir };
|
|
57
|
+
}
|
|
58
|
+
export async function runInstall(opts) {
|
|
59
|
+
const io = opts.io ?? defaultIO();
|
|
60
|
+
io.stdout(`\ndonna-bridge install\n`);
|
|
61
|
+
// ── 1. Pre-flight ──
|
|
62
|
+
io.stdout(`\nStep 1/5: pre-flight check\n`);
|
|
63
|
+
const preflight = await runPreflight();
|
|
64
|
+
io.stdout(formatPreflightTable(preflight));
|
|
65
|
+
if (!preflight.ok) {
|
|
66
|
+
io.stderr(`\nPre-flight failed. Install the listed tool(s) and re-run \`donna-bridge install\`.\n`);
|
|
67
|
+
return { ok: false, preflight, error: 'preflight_failed' };
|
|
68
|
+
}
|
|
69
|
+
// ── 2. Probe Xcode + simulators + Swift ──
|
|
70
|
+
io.stdout(`\nStep 2/5: probing Xcode, simulators, and Swift\n`);
|
|
71
|
+
const toolReport = await runXcodeProbe();
|
|
72
|
+
const xcodeChoice = pickActiveXcode(toolReport);
|
|
73
|
+
if (!xcodeChoice) {
|
|
74
|
+
io.stderr(`No Xcode.app bundle found in /Applications. Install Xcode from the App Store.\n`);
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
preflight,
|
|
78
|
+
toolReport,
|
|
79
|
+
error: 'no_xcode_bundle',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
io.stdout(` Xcode bundles: ${toolReport.xcodeBundles.length}\n`);
|
|
83
|
+
io.stdout(` Active Xcode: ${xcodeChoice.path}\n`);
|
|
84
|
+
io.stdout(` Simulator runtimes: ${toolReport.simulatorRuntimes.length}\n`);
|
|
85
|
+
io.stdout(` Report saved to ${path.join(path.dirname(getConfigPath()), 'tool-report.json')}\n`);
|
|
86
|
+
// ── 3. Pair ──
|
|
87
|
+
io.stdout(`\nStep 3/5: pairing with orchestrator at ${opts.orchestratorUrl}\n`);
|
|
88
|
+
const runnerId = opts.runnerId || hostnameRunnerId();
|
|
89
|
+
let pairResponse;
|
|
90
|
+
try {
|
|
91
|
+
if (opts.localPair) {
|
|
92
|
+
pairResponse = await pairLocally({
|
|
93
|
+
orchestratorUrl: opts.orchestratorUrl,
|
|
94
|
+
runnerId,
|
|
95
|
+
fetchImpl: opts.fetchImpl,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
if (!opts.pairingCode) {
|
|
100
|
+
const err = '--pairing-code is required (or pass --local-pair for loopback installs)';
|
|
101
|
+
io.stderr(`${err}\n`);
|
|
102
|
+
return { ok: false, preflight, toolReport, error: err };
|
|
103
|
+
}
|
|
104
|
+
pairResponse = await pairWithCode({
|
|
105
|
+
orchestratorUrl: opts.orchestratorUrl,
|
|
106
|
+
pairingCode: opts.pairingCode,
|
|
107
|
+
runnerId,
|
|
108
|
+
fetchImpl: opts.fetchImpl,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
114
|
+
io.stderr(`Pairing failed: ${message}\n`);
|
|
115
|
+
return { ok: false, preflight, toolReport, error: message };
|
|
116
|
+
}
|
|
117
|
+
io.stdout(` Paired as ${pairResponse.runner_id}\n`);
|
|
118
|
+
// ── 4. Persist config ──
|
|
119
|
+
const config = {
|
|
120
|
+
...defaultConfig(opts.orchestratorUrl, pairResponse.token, pairResponse.runner_id),
|
|
121
|
+
xcodePath: xcodeChoice.developerDir,
|
|
122
|
+
simulatorDeviceSetPath: getDefaults().deviceSetPath,
|
|
123
|
+
bridgeBin: opts.bridgeBin,
|
|
124
|
+
};
|
|
125
|
+
saveConfig(config);
|
|
126
|
+
io.stdout(` Config saved to ${getConfigPath()}\n`);
|
|
127
|
+
// Make sure the simulator device set dir exists. simctl will populate it
|
|
128
|
+
// on first use — we just create the dir with the right perms.
|
|
129
|
+
try {
|
|
130
|
+
fs.mkdirSync(config.simulatorDeviceSetPath, { recursive: true, mode: 0o700 });
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
/* best-effort */
|
|
134
|
+
}
|
|
135
|
+
// Same for the log dir + the projects dir.
|
|
136
|
+
const defaults = getDefaults();
|
|
137
|
+
try {
|
|
138
|
+
fs.mkdirSync(path.dirname(defaults.logPath), { recursive: true, mode: 0o755 });
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
/* best-effort */
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
fs.mkdirSync(defaults.projectsDir, { recursive: true, mode: 0o755 });
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/* best-effort */
|
|
148
|
+
}
|
|
149
|
+
// ── 5. LaunchAgent ──
|
|
150
|
+
io.stdout(`\nStep 4/5: installing LaunchAgent\n`);
|
|
151
|
+
if (opts.skipLaunchAgent) {
|
|
152
|
+
io.stdout(` (skipped — --skip-launch-agent or running in test mode)\n`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const bridgeBin = opts.bridgeBin || process.execPath;
|
|
156
|
+
if (!path.isAbsolute(bridgeBin)) {
|
|
157
|
+
io.stderr(`Refusing to write a relative bridgeBin into the plist: ${bridgeBin}\n`);
|
|
158
|
+
return { ok: false, preflight, toolReport, error: 'bridge_bin_not_absolute' };
|
|
159
|
+
}
|
|
160
|
+
const plistContents = renderBridgePlist({
|
|
161
|
+
label: defaults.label,
|
|
162
|
+
program: bridgeBin,
|
|
163
|
+
args: ['start'],
|
|
164
|
+
envVars: {},
|
|
165
|
+
stdoutPath: defaults.logPath,
|
|
166
|
+
stderrPath: defaults.logPath,
|
|
167
|
+
});
|
|
168
|
+
installPlist(defaults.plistPath, plistContents);
|
|
169
|
+
io.stdout(` Plist installed at ${defaults.plistPath}\n`);
|
|
170
|
+
try {
|
|
171
|
+
await loadAgent(defaults.plistPath, os.userInfo().uid);
|
|
172
|
+
io.stdout(` LaunchAgent loaded (gui/${os.userInfo().uid}/${defaults.label})\n`);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
176
|
+
io.stderr(`Failed to load LaunchAgent: ${message}\n`);
|
|
177
|
+
return { ok: false, preflight, toolReport, config, error: message };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ── 6. Summary ──
|
|
181
|
+
io.stdout(`\nStep 5/5: summary\n`);
|
|
182
|
+
io.stdout(` Orchestrator: ${opts.orchestratorUrl}\n`);
|
|
183
|
+
io.stdout(` Runner ID: ${pairResponse.runner_id}\n`);
|
|
184
|
+
io.stdout(` Active Xcode: ${xcodeChoice.path}\n`);
|
|
185
|
+
io.stdout(` DEVELOPER_DIR: ${xcodeChoice.developerDir}\n`);
|
|
186
|
+
io.stdout(` Simulator device set: ${config.simulatorDeviceSetPath}\n`);
|
|
187
|
+
io.stdout(` Logs: ${defaults.logPath}\n`);
|
|
188
|
+
io.stdout(` Projects root: ${defaults.projectsDir}\n`);
|
|
189
|
+
io.stdout(`\n The bridge accepts ONLY Apple-platform stages. Non-Apple\n`);
|
|
190
|
+
io.stdout(` dispatches are refused via runner:scope_violation.\n`);
|
|
191
|
+
io.stdout(`\n Run \`donna-bridge status\` to verify the LaunchAgent is loaded.\n\n`);
|
|
192
|
+
return { ok: true, preflight, toolReport, config };
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAEL,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAwB,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAmB,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAgC1E,SAAS,SAAS;IAChB,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;KACvC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,CAAC,QAAQ,EAAE;IAC/D,IAAI,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACvF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,GAAG,UAAU,IAAI,IAAI,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,GAAG,UAAU,IAAI,IAAI,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpE,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,MAAkB;IAChD,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAoB;IACnD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,SAAS,EAAE,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;IAEtC,sBAAsB;IACtB,EAAE,CAAC,MAAM,CAAC,gCAAgC,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAC;IACvC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,EAAE,CAAC,MAAM,CAAC,wFAAwF,CAAC,CAAC;QACpG,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC7D,CAAC;IAED,4CAA4C;IAC5C,EAAE,CAAC,MAAM,CAAC,oDAAoD,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,MAAM,aAAa,EAAE,CAAC;IACzC,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,EAAE,CAAC,MAAM,CAAC,iFAAiF,CAAC,CAAC;QAC7F,OAAO;YACL,EAAE,EAAE,KAAK;YACT,SAAS;YACT,UAAU;YACV,KAAK,EAAE,iBAAiB;SACzB,CAAC;IACJ,CAAC;IACD,EAAE,CAAC,MAAM,CAAC,oBAAoB,UAAU,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;IAClE,EAAE,CAAC,MAAM,CAAC,mBAAmB,WAAW,CAAC,IAAI,IAAI,CAAC,CAAC;IACnD,EAAE,CAAC,MAAM,CAAC,yBAAyB,UAAU,CAAC,iBAAiB,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5E,EAAE,CAAC,MAAM,CAAC,qBAAqB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAEjG,gBAAgB;IAChB,EAAE,CAAC,MAAM,CAAC,4CAA4C,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;IAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,gBAAgB,EAAE,CAAC;IAErD,IAAI,YAAkD,CAAC;IACvD,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,GAAG,MAAM,WAAW,CAAC;gBAC/B,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,QAAQ;gBACR,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtB,MAAM,GAAG,GAAG,yEAAyE,CAAC;gBACtF,EAAE,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;gBACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YAC1D,CAAC;YACD,YAAY,GAAG,MAAM,YAAY,CAAC;gBAChC,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,QAAQ;gBACR,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,EAAE,CAAC,MAAM,CAAC,mBAAmB,OAAO,IAAI,CAAC,CAAC;QAC1C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC9D,CAAC;IACD,EAAE,CAAC,MAAM,CAAC,eAAe,YAAY,CAAC,SAAS,IAAI,CAAC,CAAC;IAErD,0BAA0B;IAC1B,MAAM,MAAM,GAAsB;QAChC,GAAG,aAAa,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC;QAClF,SAAS,EAAE,WAAW,CAAC,YAAY;QACnC,sBAAsB,EAAE,WAAW,EAAE,CAAC,aAAa;QACnD,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC;IACF,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,EAAE,CAAC,MAAM,CAAC,qBAAqB,aAAa,EAAE,IAAI,CAAC,CAAC;IAEpD,yEAAyE;IACzE,8DAA8D;IAC9D,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,sBAAuB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjF,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;IACD,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjF,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;IAED,uBAAuB;IACvB,EAAE,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC;IAClD,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,EAAE,CAAC,MAAM,CAAC,6DAA6D,CAAC,CAAC;IAC3E,CAAC;SAAM,CAAC;QACN,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,EAAE,CAAC,MAAM,CAAC,0DAA0D,SAAS,IAAI,CAAC,CAAC;YACnF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;QAChF,CAAC;QACD,MAAM,aAAa,GAAG,iBAAiB,CAAC;YACtC,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,CAAC,OAAO,CAAC;YACf,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,QAAQ,CAAC,OAAO;YAC5B,UAAU,EAAE,QAAQ,CAAC,OAAO;SAC7B,CAAC,CAAC;QACH,YAAY,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAChD,EAAE,CAAC,MAAM,CAAC,wBAAwB,QAAQ,CAAC,SAAS,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;YACvD,EAAE,CAAC,MAAM,CAAC,6BAA6B,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,QAAQ,CAAC,KAAK,KAAK,CAAC,CAAC;QACnF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,EAAE,CAAC,MAAM,CAAC,+BAA+B,OAAO,IAAI,CAAC,CAAC;YACtD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACtE,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;IACnC,EAAE,CAAC,MAAM,CAAC,6BAA6B,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;IACjE,EAAE,CAAC,MAAM,CAAC,6BAA6B,YAAY,CAAC,SAAS,IAAI,CAAC,CAAC;IACnE,EAAE,CAAC,MAAM,CAAC,6BAA6B,WAAW,CAAC,IAAI,IAAI,CAAC,CAAC;IAC7D,EAAE,CAAC,MAAM,CAAC,6BAA6B,WAAW,CAAC,YAAY,IAAI,CAAC,CAAC;IACrE,EAAE,CAAC,MAAM,CAAC,6BAA6B,MAAM,CAAC,sBAAsB,IAAI,CAAC,CAAC;IAC1E,EAAE,CAAC,MAAM,CAAC,6BAA6B,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;IAC7D,EAAE,CAAC,MAAM,CAAC,6BAA6B,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC;IACjE,EAAE,CAAC,MAAM,CAAC,gEAAgE,CAAC,CAAC;IAC5E,EAAE,CAAC,MAAM,CAAC,wDAAwD,CAAC,CAAC;IACpE,EAAE,CAAC,MAAM,CAAC,0EAA0E,CAAC,CAAC;IACtF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AACrD,CAAC"}
|
package/dist/launchd.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LaunchAgent lifecycle for the bridge (#295).
|
|
3
|
+
*
|
|
4
|
+
* Renders `~/Library/LaunchAgents/net.donna.bridge.plist`, loads it via
|
|
5
|
+
* `launchctl bootstrap gui/<uid>`, and tears it back down via
|
|
6
|
+
* `launchctl bootout`. Mirrors `runner/native/launchd.ts` so the two
|
|
7
|
+
* components share the same plist-rendering / `bootstrap`-vs-`bootout`
|
|
8
|
+
* conventions — but kept here so `bridge/` has no `runner/` imports.
|
|
9
|
+
*/
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { execFile } from 'node:child_process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
const EXEC_TIMEOUT_MS = 10_000;
|
|
15
|
+
function execFileNoThrow(bin, args) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
execFile(bin, args, { timeout: EXEC_TIMEOUT_MS, encoding: 'utf-8' }, (err, stdout, stderr) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
const code = err.code;
|
|
20
|
+
if (code === 'ENOENT' || code === 'EACCES') {
|
|
21
|
+
reject(err);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const numericCode = err.code;
|
|
25
|
+
const exitCode = typeof numericCode === 'number' ? numericCode : 1;
|
|
26
|
+
resolve({ stdout: stdout ?? '', stderr: stderr ?? '', exitCode });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
resolve({ stdout: stdout ?? '', stderr: stderr ?? '', exitCode: 0 });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function execFileStrict(bin, args) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
execFile(bin, args, { timeout: EXEC_TIMEOUT_MS, encoding: 'utf-8' }, (err, stdout, stderr) => {
|
|
36
|
+
if (err) {
|
|
37
|
+
const e = err;
|
|
38
|
+
e.stdout = stdout ?? '';
|
|
39
|
+
e.stderr = stderr ?? '';
|
|
40
|
+
reject(e);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
resolve({ stdout: stdout ?? '', stderr: stderr ?? '', exitCode: 0 });
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function findTemplatePath() {
|
|
48
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
49
|
+
// Source and packaged forms — covers both `tsx`-imported source and the
|
|
50
|
+
// compiled tree under `dist/bridge/`.
|
|
51
|
+
const candidates = [
|
|
52
|
+
path.join(here, 'templates', 'launchagent-bridge.plist.template'),
|
|
53
|
+
path.join(here, '..', '..', 'bridge', 'templates', 'launchagent-bridge.plist.template'),
|
|
54
|
+
];
|
|
55
|
+
for (const c of candidates) {
|
|
56
|
+
if (fs.existsSync(c))
|
|
57
|
+
return c;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`launchagent-bridge.plist.template not found (searched: ${candidates.join(', ')})`);
|
|
60
|
+
}
|
|
61
|
+
let cachedTemplate;
|
|
62
|
+
function loadTemplate() {
|
|
63
|
+
if (cachedTemplate === undefined) {
|
|
64
|
+
cachedTemplate = fs.readFileSync(findTemplatePath(), 'utf-8');
|
|
65
|
+
}
|
|
66
|
+
return cachedTemplate;
|
|
67
|
+
}
|
|
68
|
+
function xmlEscape(s) {
|
|
69
|
+
return s
|
|
70
|
+
.replace(/&/g, '&')
|
|
71
|
+
.replace(/</g, '<')
|
|
72
|
+
.replace(/>/g, '>')
|
|
73
|
+
.replace(/"/g, '"')
|
|
74
|
+
.replace(/'/g, ''');
|
|
75
|
+
}
|
|
76
|
+
/** Render the plist with every placeholder substituted. All inputs XML-escaped. */
|
|
77
|
+
export function renderBridgePlist(spec) {
|
|
78
|
+
const tpl = loadTemplate();
|
|
79
|
+
const programArgs = spec.args
|
|
80
|
+
.map((a) => `\t\t<string>${xmlEscape(a)}</string>`)
|
|
81
|
+
.join('\n');
|
|
82
|
+
const envEntries = Object.entries(spec.envVars)
|
|
83
|
+
.map(([k, v]) => `\t\t<key>${xmlEscape(k)}</key>\n\t\t<string>${xmlEscape(v)}</string>`)
|
|
84
|
+
.join('\n');
|
|
85
|
+
return tpl
|
|
86
|
+
.replace('{{LABEL}}', xmlEscape(spec.label))
|
|
87
|
+
.replace('{{PROGRAM}}', xmlEscape(spec.program))
|
|
88
|
+
.replace('{{PROGRAM_ARGS}}', programArgs)
|
|
89
|
+
.replace('{{ENV_VARS}}', envEntries)
|
|
90
|
+
.replace('{{STDOUT_PATH}}', xmlEscape(spec.stdoutPath))
|
|
91
|
+
.replace('{{STDERR_PATH}}', xmlEscape(spec.stderrPath));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Write the plist contents at `plistPath` with mode 0644. The parent dir is
|
|
95
|
+
* created if missing. Idempotent — overwrites any previous plist.
|
|
96
|
+
*/
|
|
97
|
+
export function installPlist(plistPath, contents) {
|
|
98
|
+
fs.mkdirSync(path.dirname(plistPath), { recursive: true });
|
|
99
|
+
fs.writeFileSync(plistPath, contents, { mode: 0o644 });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Bootstrap the agent into the user's gui session. If the agent is already
|
|
103
|
+
* loaded, bootout first — this is the recommended way to reload a changed
|
|
104
|
+
* plist.
|
|
105
|
+
*/
|
|
106
|
+
export async function loadAgent(plistPath, uid) {
|
|
107
|
+
const label = readLabelFromPlistPath(plistPath);
|
|
108
|
+
if (label && (await isAgentLoaded(label, uid))) {
|
|
109
|
+
await execFileStrict('launchctl', ['bootout', `gui/${uid}/${label}`]);
|
|
110
|
+
}
|
|
111
|
+
await execFileStrict('launchctl', ['bootstrap', `gui/${uid}`, plistPath]);
|
|
112
|
+
}
|
|
113
|
+
/** Bootout the agent. No-op when not loaded. */
|
|
114
|
+
export async function unloadAgent(plistPath, uid) {
|
|
115
|
+
const label = readLabelFromPlistPath(plistPath);
|
|
116
|
+
if (!label) {
|
|
117
|
+
throw new Error(`could not extract Label from plist at ${plistPath}`);
|
|
118
|
+
}
|
|
119
|
+
if (!(await isAgentLoaded(label, uid)))
|
|
120
|
+
return;
|
|
121
|
+
await execFileStrict('launchctl', ['bootout', `gui/${uid}/${label}`]);
|
|
122
|
+
}
|
|
123
|
+
/** `launchctl print gui/<uid>/<label>` → 0 when loaded, non-zero otherwise. */
|
|
124
|
+
export async function isAgentLoaded(label, uid) {
|
|
125
|
+
const { exitCode } = await execFileNoThrow('launchctl', [
|
|
126
|
+
'print',
|
|
127
|
+
`gui/${uid}/${label}`,
|
|
128
|
+
]);
|
|
129
|
+
return exitCode === 0;
|
|
130
|
+
}
|
|
131
|
+
function readLabelFromPlistPath(plistPath) {
|
|
132
|
+
let xml;
|
|
133
|
+
try {
|
|
134
|
+
xml = fs.readFileSync(plistPath, 'utf-8');
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const m = xml.match(/<key>Label<\/key>\s*<string>([^<]*)<\/string>/);
|
|
140
|
+
return m ? m[1] : null;
|
|
141
|
+
}
|
|
142
|
+
/** For tests — clear the cached template so re-imports re-read disk. */
|
|
143
|
+
export function _resetTemplateCacheForTests() {
|
|
144
|
+
cachedTemplate = undefined;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=launchd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchd.js","sourceRoot":"","sources":["../launchd.ts"],"names":[],"mappings":"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"}
|