@limrun/api 0.19.3 → 0.20.0
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/CHANGELOG.md +15 -0
- package/client.d.mts +1 -0
- package/client.d.mts.map +1 -1
- package/client.d.ts +1 -0
- package/client.d.ts.map +1 -1
- package/client.js +10 -2
- package/client.js.map +1 -1
- package/client.mjs +10 -2
- package/client.mjs.map +1 -1
- package/exec-client.d.mts +101 -0
- package/exec-client.d.mts.map +1 -0
- package/exec-client.d.ts +101 -0
- package/exec-client.d.ts.map +1 -0
- package/exec-client.js +265 -0
- package/exec-client.js.map +1 -0
- package/exec-client.mjs +259 -0
- package/exec-client.mjs.map +1 -0
- package/folder-sync.d.mts +16 -2
- package/folder-sync.d.mts.map +1 -1
- package/folder-sync.d.ts +16 -2
- package/folder-sync.d.ts.map +1 -1
- package/folder-sync.js +43 -14
- package/folder-sync.js.map +1 -1
- package/folder-sync.mjs +43 -13
- package/folder-sync.mjs.map +1 -1
- package/index.d.mts +2 -0
- package/index.d.mts.map +1 -1
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/index.js +5 -1
- package/index.js.map +1 -1
- package/index.mjs +2 -0
- package/index.mjs.map +1 -1
- package/internal/parse.d.mts.map +1 -1
- package/internal/parse.d.ts.map +1 -1
- package/internal/parse.js +5 -0
- package/internal/parse.js.map +1 -1
- package/internal/parse.mjs +5 -0
- package/internal/parse.mjs.map +1 -1
- package/ios-client.d.mts +10 -3
- package/ios-client.d.mts.map +1 -1
- package/ios-client.d.ts +10 -3
- package/ios-client.d.ts.map +1 -1
- package/ios-client.js +19 -4
- package/ios-client.js.map +1 -1
- package/ios-client.mjs +18 -3
- package/ios-client.mjs.map +1 -1
- package/package.json +23 -1
- package/sandbox-client.d.mts +124 -0
- package/sandbox-client.d.mts.map +1 -0
- package/sandbox-client.d.ts +124 -0
- package/sandbox-client.d.ts.map +1 -0
- package/sandbox-client.js +149 -0
- package/sandbox-client.js.map +1 -0
- package/sandbox-client.mjs +146 -0
- package/sandbox-client.mjs.map +1 -0
- package/src/client.ts +10 -2
- package/src/exec-client.ts +333 -0
- package/src/folder-sync.ts +66 -18
- package/src/index.ts +16 -0
- package/src/internal/parse.ts +6 -0
- package/src/ios-client.ts +35 -5
- package/src/sandbox-client.ts +267 -0
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/exec-client.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Client for executing commands on limbuild server with streaming output.
|
|
4
|
+
*
|
|
5
|
+
* The interface is designed to be similar to Node.js's child_process.spawn()
|
|
6
|
+
* for familiarity and ease of extension.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ExecChildProcess = exports.ReadableStream = void 0;
|
|
10
|
+
exports.exec = exec;
|
|
11
|
+
const eventsource_client_1 = require("eventsource-client");
|
|
12
|
+
/**
|
|
13
|
+
* A Readable-like stream interface, similar to Node.js stream.Readable.
|
|
14
|
+
* Emits 'data' for each chunk and 'close' when the stream ends.
|
|
15
|
+
*/
|
|
16
|
+
class ReadableStream {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.dataListeners = [];
|
|
19
|
+
this.closeListeners = [];
|
|
20
|
+
this.closed = false;
|
|
21
|
+
}
|
|
22
|
+
on(event, listener) {
|
|
23
|
+
if (event === 'data') {
|
|
24
|
+
this.dataListeners.push(listener);
|
|
25
|
+
}
|
|
26
|
+
else if (event === 'close') {
|
|
27
|
+
this.closeListeners.push(listener);
|
|
28
|
+
}
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
emit(event, arg) {
|
|
32
|
+
if (event === 'data' && typeof arg === 'string') {
|
|
33
|
+
for (const l of this.dataListeners)
|
|
34
|
+
l(arg);
|
|
35
|
+
}
|
|
36
|
+
else if (event === 'close' && !this.closed) {
|
|
37
|
+
this.closed = true;
|
|
38
|
+
for (const l of this.closeListeners)
|
|
39
|
+
l();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ReadableStream = ReadableStream;
|
|
44
|
+
/**
|
|
45
|
+
* A ChildProcess-like object similar to Node.js's ChildProcess.
|
|
46
|
+
*
|
|
47
|
+
* Implements PromiseLike so it can be awaited directly.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // Stream-based (like Node.js spawn)
|
|
51
|
+
* const proc = exec({ command: 'xcodebuild' }, options);
|
|
52
|
+
* proc.stdout.on('data', (chunk) => process.stdout.write(chunk));
|
|
53
|
+
* proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
54
|
+
* proc.on('exit', (code) => console.log(`Exited with code ${code}`));
|
|
55
|
+
*
|
|
56
|
+
* // Promise-based (can be awaited)
|
|
57
|
+
* const { exitCode, status } = await proc;
|
|
58
|
+
*/
|
|
59
|
+
class ExecChildProcess {
|
|
60
|
+
constructor(request, options) {
|
|
61
|
+
/** Stdout stream - emits 'data' and 'close' events */
|
|
62
|
+
this.stdout = new ReadableStream();
|
|
63
|
+
/** Stderr stream - emits 'data' and 'close' events */
|
|
64
|
+
this.stderr = new ReadableStream();
|
|
65
|
+
this.exitListeners = [];
|
|
66
|
+
this.abortController = null;
|
|
67
|
+
this.sseConnection = null;
|
|
68
|
+
this.killed = false;
|
|
69
|
+
this.options = options;
|
|
70
|
+
this.log = options.log ?? (() => { });
|
|
71
|
+
this.resultPromise = this.run(request);
|
|
72
|
+
}
|
|
73
|
+
/** Implement PromiseLike so this object can be awaited */
|
|
74
|
+
then(onfulfilled, onrejected) {
|
|
75
|
+
return this.resultPromise.then(onfulfilled, onrejected);
|
|
76
|
+
}
|
|
77
|
+
/** Catch errors */
|
|
78
|
+
catch(onrejected) {
|
|
79
|
+
return this.resultPromise.catch(onrejected);
|
|
80
|
+
}
|
|
81
|
+
/** Finally handler */
|
|
82
|
+
finally(onfinally) {
|
|
83
|
+
return this.resultPromise.finally(onfinally);
|
|
84
|
+
}
|
|
85
|
+
/** Listen for process events */
|
|
86
|
+
on(event, listener) {
|
|
87
|
+
if (event === 'exit') {
|
|
88
|
+
this.exitListeners.push(listener);
|
|
89
|
+
}
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
/** Send a signal to terminate the process */
|
|
93
|
+
async kill() {
|
|
94
|
+
this.killed = true;
|
|
95
|
+
if (this.abortController) {
|
|
96
|
+
this.abortController.abort();
|
|
97
|
+
}
|
|
98
|
+
if (this.sseConnection) {
|
|
99
|
+
this.sseConnection.close();
|
|
100
|
+
this.sseConnection = null;
|
|
101
|
+
}
|
|
102
|
+
if (!this.execId) {
|
|
103
|
+
this.log('warn', 'Failed to cancel build: execId is not set');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
await fetch(`${this.options.apiUrl}/exec/${this.execId}/cancel`, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
Authorization: `Bearer ${this.options.token}`,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
this.log('info', 'Build cancelled');
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
this.log('warn', `Failed to cancel build: ${err}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async run(request) {
|
|
120
|
+
const { log } = this;
|
|
121
|
+
const { apiUrl, token } = this.options;
|
|
122
|
+
// 1. Trigger the build via POST /exec
|
|
123
|
+
log('debug', `POST ${apiUrl}/exec`);
|
|
124
|
+
const execRes = await fetch(`${apiUrl}/exec`, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: {
|
|
127
|
+
'Content-Type': 'application/json',
|
|
128
|
+
Authorization: `Bearer ${token}`,
|
|
129
|
+
},
|
|
130
|
+
body: JSON.stringify(request),
|
|
131
|
+
});
|
|
132
|
+
if (!execRes.ok) {
|
|
133
|
+
const text = await execRes.text();
|
|
134
|
+
throw new Error(`exec failed: ${execRes.status} ${text}`);
|
|
135
|
+
}
|
|
136
|
+
const execData = (await execRes.json());
|
|
137
|
+
this.execId = execData.execId;
|
|
138
|
+
log('info', `Build started: ${this.execId}`);
|
|
139
|
+
// 2. Connect to SSE for log streaming and completion detection
|
|
140
|
+
this.abortController = new AbortController();
|
|
141
|
+
const eventsUrl = `${apiUrl}/exec/${this.execId}/events`;
|
|
142
|
+
log('debug', `GET ${eventsUrl} (SSE)`);
|
|
143
|
+
// Promise that resolves when build completes (via exitCode event)
|
|
144
|
+
let sseCompletionResolve = null;
|
|
145
|
+
const sseCompletionPromise = new Promise((resolve) => {
|
|
146
|
+
sseCompletionResolve = resolve;
|
|
147
|
+
});
|
|
148
|
+
const ssePromise = this.connectSSE(eventsUrl, sseCompletionResolve);
|
|
149
|
+
// Wait for SSE to signal completion (with timeout fallback)
|
|
150
|
+
const timeoutMs = 3600 * 1000; // 1 hour max
|
|
151
|
+
let exitCode;
|
|
152
|
+
try {
|
|
153
|
+
exitCode = await Promise.race([
|
|
154
|
+
sseCompletionPromise,
|
|
155
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('SSE timeout')), timeoutMs)),
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
log('warn', 'SSE completion timeout');
|
|
160
|
+
exitCode = 1;
|
|
161
|
+
}
|
|
162
|
+
// Cleanup SSE connection
|
|
163
|
+
if (this.abortController) {
|
|
164
|
+
this.abortController.abort();
|
|
165
|
+
}
|
|
166
|
+
await ssePromise.catch(() => { });
|
|
167
|
+
// Emit close events on streams
|
|
168
|
+
this.stdout.emit('close');
|
|
169
|
+
this.stderr.emit('close');
|
|
170
|
+
// Emit exit event
|
|
171
|
+
for (const listener of this.exitListeners) {
|
|
172
|
+
listener(exitCode);
|
|
173
|
+
}
|
|
174
|
+
// Determine status from exit code
|
|
175
|
+
const status = exitCode === 0 ? 'SUCCEEDED'
|
|
176
|
+
: exitCode === -1 ? 'CANCELLED'
|
|
177
|
+
: 'FAILED';
|
|
178
|
+
const result = {
|
|
179
|
+
exitCode,
|
|
180
|
+
execId: this.execId,
|
|
181
|
+
status,
|
|
182
|
+
};
|
|
183
|
+
this.log('info', `Build finished: ${result.status} (exit ${result.exitCode})`);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
async connectSSE(eventsUrl, onComplete) {
|
|
187
|
+
return new Promise((resolve) => {
|
|
188
|
+
const authHeader = `Bearer ${this.options.token}`;
|
|
189
|
+
let resolved = false;
|
|
190
|
+
const resolveOnce = () => {
|
|
191
|
+
if (resolved)
|
|
192
|
+
return;
|
|
193
|
+
resolved = true;
|
|
194
|
+
this.sseConnection?.close();
|
|
195
|
+
this.sseConnection = null;
|
|
196
|
+
resolve();
|
|
197
|
+
};
|
|
198
|
+
try {
|
|
199
|
+
const eventSource = (0, eventsource_client_1.createEventSource)({
|
|
200
|
+
url: eventsUrl,
|
|
201
|
+
headers: { Authorization: authHeader },
|
|
202
|
+
onMessage: (message) => {
|
|
203
|
+
const data = typeof message.data === 'string' ? message.data : String(message.data ?? '');
|
|
204
|
+
const eventType = message.event;
|
|
205
|
+
if (eventType === 'stdout') {
|
|
206
|
+
this.stdout.emit('data', data);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (eventType === 'stderr') {
|
|
210
|
+
this.stderr.emit('data', data);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (eventType === 'exitCode') {
|
|
214
|
+
const exitCode = parseInt(data, 10);
|
|
215
|
+
if (Number.isNaN(exitCode)) {
|
|
216
|
+
this.log('warn', `SSE exitCode event has invalid data: ${data}`);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
this.log('debug', `Build completed via SSE: exitCode=${exitCode}`);
|
|
220
|
+
onComplete?.(exitCode);
|
|
221
|
+
resolveOnce();
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
onDisconnect: () => {
|
|
225
|
+
if (!this.killed) {
|
|
226
|
+
this.log('warn', 'SSE disconnected');
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
this.sseConnection = eventSource;
|
|
231
|
+
const abortSignal = this.abortController?.signal;
|
|
232
|
+
if (abortSignal) {
|
|
233
|
+
abortSignal.addEventListener('abort', () => {
|
|
234
|
+
resolveOnce();
|
|
235
|
+
}, { once: true });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
if (!this.killed) {
|
|
240
|
+
this.log('warn', `SSE setup failed: ${err}`);
|
|
241
|
+
}
|
|
242
|
+
resolveOnce();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
exports.ExecChildProcess = ExecChildProcess;
|
|
248
|
+
/**
|
|
249
|
+
* Execute a command on the limbuild server.
|
|
250
|
+
* Returns a ChildProcess-like object with stdout/stderr streams.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* const proc = exec({ command: 'xcodebuild' }, { apiUrl: '...', token: '...' });
|
|
254
|
+
*
|
|
255
|
+
* // Stream output
|
|
256
|
+
* proc.stdout.on('data', (chunk) => console.log('[stdout]', chunk));
|
|
257
|
+
* proc.stderr.on('data', (chunk) => console.error('[stderr]', chunk));
|
|
258
|
+
*
|
|
259
|
+
* // Wait for completion
|
|
260
|
+
* const { exitCode, status } = await proc;
|
|
261
|
+
*/
|
|
262
|
+
function exec(request, options) {
|
|
263
|
+
return new ExecChildProcess(request, options);
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=exec-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-client.js","sourceRoot":"","sources":["src/exec-client.ts"],"names":[],"mappings":"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"}
|
package/exec-client.mjs
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for executing commands on limbuild server with streaming output.
|
|
3
|
+
*
|
|
4
|
+
* The interface is designed to be similar to Node.js's child_process.spawn()
|
|
5
|
+
* for familiarity and ease of extension.
|
|
6
|
+
*/
|
|
7
|
+
import { createEventSource } from 'eventsource-client';
|
|
8
|
+
/**
|
|
9
|
+
* A Readable-like stream interface, similar to Node.js stream.Readable.
|
|
10
|
+
* Emits 'data' for each chunk and 'close' when the stream ends.
|
|
11
|
+
*/
|
|
12
|
+
export class ReadableStream {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.dataListeners = [];
|
|
15
|
+
this.closeListeners = [];
|
|
16
|
+
this.closed = false;
|
|
17
|
+
}
|
|
18
|
+
on(event, listener) {
|
|
19
|
+
if (event === 'data') {
|
|
20
|
+
this.dataListeners.push(listener);
|
|
21
|
+
}
|
|
22
|
+
else if (event === 'close') {
|
|
23
|
+
this.closeListeners.push(listener);
|
|
24
|
+
}
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
emit(event, arg) {
|
|
28
|
+
if (event === 'data' && typeof arg === 'string') {
|
|
29
|
+
for (const l of this.dataListeners)
|
|
30
|
+
l(arg);
|
|
31
|
+
}
|
|
32
|
+
else if (event === 'close' && !this.closed) {
|
|
33
|
+
this.closed = true;
|
|
34
|
+
for (const l of this.closeListeners)
|
|
35
|
+
l();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A ChildProcess-like object similar to Node.js's ChildProcess.
|
|
41
|
+
*
|
|
42
|
+
* Implements PromiseLike so it can be awaited directly.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Stream-based (like Node.js spawn)
|
|
46
|
+
* const proc = exec({ command: 'xcodebuild' }, options);
|
|
47
|
+
* proc.stdout.on('data', (chunk) => process.stdout.write(chunk));
|
|
48
|
+
* proc.stderr.on('data', (chunk) => process.stderr.write(chunk));
|
|
49
|
+
* proc.on('exit', (code) => console.log(`Exited with code ${code}`));
|
|
50
|
+
*
|
|
51
|
+
* // Promise-based (can be awaited)
|
|
52
|
+
* const { exitCode, status } = await proc;
|
|
53
|
+
*/
|
|
54
|
+
export class ExecChildProcess {
|
|
55
|
+
constructor(request, options) {
|
|
56
|
+
/** Stdout stream - emits 'data' and 'close' events */
|
|
57
|
+
this.stdout = new ReadableStream();
|
|
58
|
+
/** Stderr stream - emits 'data' and 'close' events */
|
|
59
|
+
this.stderr = new ReadableStream();
|
|
60
|
+
this.exitListeners = [];
|
|
61
|
+
this.abortController = null;
|
|
62
|
+
this.sseConnection = null;
|
|
63
|
+
this.killed = false;
|
|
64
|
+
this.options = options;
|
|
65
|
+
this.log = options.log ?? (() => { });
|
|
66
|
+
this.resultPromise = this.run(request);
|
|
67
|
+
}
|
|
68
|
+
/** Implement PromiseLike so this object can be awaited */
|
|
69
|
+
then(onfulfilled, onrejected) {
|
|
70
|
+
return this.resultPromise.then(onfulfilled, onrejected);
|
|
71
|
+
}
|
|
72
|
+
/** Catch errors */
|
|
73
|
+
catch(onrejected) {
|
|
74
|
+
return this.resultPromise.catch(onrejected);
|
|
75
|
+
}
|
|
76
|
+
/** Finally handler */
|
|
77
|
+
finally(onfinally) {
|
|
78
|
+
return this.resultPromise.finally(onfinally);
|
|
79
|
+
}
|
|
80
|
+
/** Listen for process events */
|
|
81
|
+
on(event, listener) {
|
|
82
|
+
if (event === 'exit') {
|
|
83
|
+
this.exitListeners.push(listener);
|
|
84
|
+
}
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
/** Send a signal to terminate the process */
|
|
88
|
+
async kill() {
|
|
89
|
+
this.killed = true;
|
|
90
|
+
if (this.abortController) {
|
|
91
|
+
this.abortController.abort();
|
|
92
|
+
}
|
|
93
|
+
if (this.sseConnection) {
|
|
94
|
+
this.sseConnection.close();
|
|
95
|
+
this.sseConnection = null;
|
|
96
|
+
}
|
|
97
|
+
if (!this.execId) {
|
|
98
|
+
this.log('warn', 'Failed to cancel build: execId is not set');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await fetch(`${this.options.apiUrl}/exec/${this.execId}/cancel`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
Authorization: `Bearer ${this.options.token}`,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
this.log('info', 'Build cancelled');
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
this.log('warn', `Failed to cancel build: ${err}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async run(request) {
|
|
115
|
+
const { log } = this;
|
|
116
|
+
const { apiUrl, token } = this.options;
|
|
117
|
+
// 1. Trigger the build via POST /exec
|
|
118
|
+
log('debug', `POST ${apiUrl}/exec`);
|
|
119
|
+
const execRes = await fetch(`${apiUrl}/exec`, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json',
|
|
123
|
+
Authorization: `Bearer ${token}`,
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify(request),
|
|
126
|
+
});
|
|
127
|
+
if (!execRes.ok) {
|
|
128
|
+
const text = await execRes.text();
|
|
129
|
+
throw new Error(`exec failed: ${execRes.status} ${text}`);
|
|
130
|
+
}
|
|
131
|
+
const execData = (await execRes.json());
|
|
132
|
+
this.execId = execData.execId;
|
|
133
|
+
log('info', `Build started: ${this.execId}`);
|
|
134
|
+
// 2. Connect to SSE for log streaming and completion detection
|
|
135
|
+
this.abortController = new AbortController();
|
|
136
|
+
const eventsUrl = `${apiUrl}/exec/${this.execId}/events`;
|
|
137
|
+
log('debug', `GET ${eventsUrl} (SSE)`);
|
|
138
|
+
// Promise that resolves when build completes (via exitCode event)
|
|
139
|
+
let sseCompletionResolve = null;
|
|
140
|
+
const sseCompletionPromise = new Promise((resolve) => {
|
|
141
|
+
sseCompletionResolve = resolve;
|
|
142
|
+
});
|
|
143
|
+
const ssePromise = this.connectSSE(eventsUrl, sseCompletionResolve);
|
|
144
|
+
// Wait for SSE to signal completion (with timeout fallback)
|
|
145
|
+
const timeoutMs = 3600 * 1000; // 1 hour max
|
|
146
|
+
let exitCode;
|
|
147
|
+
try {
|
|
148
|
+
exitCode = await Promise.race([
|
|
149
|
+
sseCompletionPromise,
|
|
150
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('SSE timeout')), timeoutMs)),
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
log('warn', 'SSE completion timeout');
|
|
155
|
+
exitCode = 1;
|
|
156
|
+
}
|
|
157
|
+
// Cleanup SSE connection
|
|
158
|
+
if (this.abortController) {
|
|
159
|
+
this.abortController.abort();
|
|
160
|
+
}
|
|
161
|
+
await ssePromise.catch(() => { });
|
|
162
|
+
// Emit close events on streams
|
|
163
|
+
this.stdout.emit('close');
|
|
164
|
+
this.stderr.emit('close');
|
|
165
|
+
// Emit exit event
|
|
166
|
+
for (const listener of this.exitListeners) {
|
|
167
|
+
listener(exitCode);
|
|
168
|
+
}
|
|
169
|
+
// Determine status from exit code
|
|
170
|
+
const status = exitCode === 0 ? 'SUCCEEDED'
|
|
171
|
+
: exitCode === -1 ? 'CANCELLED'
|
|
172
|
+
: 'FAILED';
|
|
173
|
+
const result = {
|
|
174
|
+
exitCode,
|
|
175
|
+
execId: this.execId,
|
|
176
|
+
status,
|
|
177
|
+
};
|
|
178
|
+
this.log('info', `Build finished: ${result.status} (exit ${result.exitCode})`);
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
async connectSSE(eventsUrl, onComplete) {
|
|
182
|
+
return new Promise((resolve) => {
|
|
183
|
+
const authHeader = `Bearer ${this.options.token}`;
|
|
184
|
+
let resolved = false;
|
|
185
|
+
const resolveOnce = () => {
|
|
186
|
+
if (resolved)
|
|
187
|
+
return;
|
|
188
|
+
resolved = true;
|
|
189
|
+
this.sseConnection?.close();
|
|
190
|
+
this.sseConnection = null;
|
|
191
|
+
resolve();
|
|
192
|
+
};
|
|
193
|
+
try {
|
|
194
|
+
const eventSource = createEventSource({
|
|
195
|
+
url: eventsUrl,
|
|
196
|
+
headers: { Authorization: authHeader },
|
|
197
|
+
onMessage: (message) => {
|
|
198
|
+
const data = typeof message.data === 'string' ? message.data : String(message.data ?? '');
|
|
199
|
+
const eventType = message.event;
|
|
200
|
+
if (eventType === 'stdout') {
|
|
201
|
+
this.stdout.emit('data', data);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (eventType === 'stderr') {
|
|
205
|
+
this.stderr.emit('data', data);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (eventType === 'exitCode') {
|
|
209
|
+
const exitCode = parseInt(data, 10);
|
|
210
|
+
if (Number.isNaN(exitCode)) {
|
|
211
|
+
this.log('warn', `SSE exitCode event has invalid data: ${data}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.log('debug', `Build completed via SSE: exitCode=${exitCode}`);
|
|
215
|
+
onComplete?.(exitCode);
|
|
216
|
+
resolveOnce();
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
onDisconnect: () => {
|
|
220
|
+
if (!this.killed) {
|
|
221
|
+
this.log('warn', 'SSE disconnected');
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
this.sseConnection = eventSource;
|
|
226
|
+
const abortSignal = this.abortController?.signal;
|
|
227
|
+
if (abortSignal) {
|
|
228
|
+
abortSignal.addEventListener('abort', () => {
|
|
229
|
+
resolveOnce();
|
|
230
|
+
}, { once: true });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
if (!this.killed) {
|
|
235
|
+
this.log('warn', `SSE setup failed: ${err}`);
|
|
236
|
+
}
|
|
237
|
+
resolveOnce();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Execute a command on the limbuild server.
|
|
244
|
+
* Returns a ChildProcess-like object with stdout/stderr streams.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* const proc = exec({ command: 'xcodebuild' }, { apiUrl: '...', token: '...' });
|
|
248
|
+
*
|
|
249
|
+
* // Stream output
|
|
250
|
+
* proc.stdout.on('data', (chunk) => console.log('[stdout]', chunk));
|
|
251
|
+
* proc.stderr.on('data', (chunk) => console.error('[stderr]', chunk));
|
|
252
|
+
*
|
|
253
|
+
* // Wait for completion
|
|
254
|
+
* const { exitCode, status } = await proc;
|
|
255
|
+
*/
|
|
256
|
+
export function exec(request, options) {
|
|
257
|
+
return new ExecChildProcess(request, options);
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=exec-client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-client.mjs","sourceRoot":"","sources":["src/exec-client.ts"],"names":[],"mappings":"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"}
|
package/folder-sync.d.mts
CHANGED
|
@@ -7,7 +7,7 @@ export type FolderSyncOptions = {
|
|
|
7
7
|
* Used to store the last-synced “basis” copies of files (and related sync metadata) so we can compute xdelta patches
|
|
8
8
|
* on subsequent syncs without re-downloading server state.
|
|
9
9
|
*
|
|
10
|
-
* Can be absolute or relative to process.cwd(). Defaults to `.
|
|
10
|
+
* Can be absolute or relative to process.cwd(). Defaults to `.limsync-cache/`.
|
|
11
11
|
*/
|
|
12
12
|
basisCacheDir?: string;
|
|
13
13
|
install?: boolean;
|
|
@@ -18,6 +18,21 @@ export type FolderSyncOptions = {
|
|
|
18
18
|
maxPatchBytes?: number;
|
|
19
19
|
/** Controls logging verbosity */
|
|
20
20
|
log?: (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Optional filter function to include/exclude files and directories.
|
|
23
|
+
* Called with the relative path from localFolderPath (using forward slashes).
|
|
24
|
+
* For directories, the path ends with '/'.
|
|
25
|
+
* Return true to include, false to exclude.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Exclude build folder
|
|
29
|
+
* filter: (path) => !path.startsWith('build/')
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Only include source files
|
|
33
|
+
* filter: (path) => path.startsWith('src/') || path.endsWith('.json')
|
|
34
|
+
*/
|
|
35
|
+
filter?: (relativePath: string) => boolean;
|
|
21
36
|
};
|
|
22
37
|
export type SyncFolderResult = {
|
|
23
38
|
installedAppPath?: string;
|
|
@@ -27,5 +42,4 @@ export type SyncFolderResult = {
|
|
|
27
42
|
};
|
|
28
43
|
export type SyncAppResult = SyncFolderResult;
|
|
29
44
|
export declare function syncApp(localFolderPath: string, opts: FolderSyncOptions): Promise<SyncFolderResult>;
|
|
30
|
-
export declare function syncFolder(localFolderPath: string, opts: FolderSyncOptions): Promise<SyncFolderResult>;
|
|
31
45
|
//# sourceMappingURL=folder-sync.d.mts.map
|
package/folder-sync.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folder-sync.d.mts","sourceRoot":"","sources":["src/folder-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"folder-sync.d.mts","sourceRoot":"","sources":["src/folder-sync.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,qBAAqB,GAAG,mBAAmB,GAAG,eAAe,CAAC;IAC3E,uFAAuF;IACvF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAmTF,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAE7C,wBAAsB,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4CzG"}
|
package/folder-sync.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type FolderSyncOptions = {
|
|
|
7
7
|
* Used to store the last-synced “basis” copies of files (and related sync metadata) so we can compute xdelta patches
|
|
8
8
|
* on subsequent syncs without re-downloading server state.
|
|
9
9
|
*
|
|
10
|
-
* Can be absolute or relative to process.cwd(). Defaults to `.
|
|
10
|
+
* Can be absolute or relative to process.cwd(). Defaults to `.limsync-cache/`.
|
|
11
11
|
*/
|
|
12
12
|
basisCacheDir?: string;
|
|
13
13
|
install?: boolean;
|
|
@@ -18,6 +18,21 @@ export type FolderSyncOptions = {
|
|
|
18
18
|
maxPatchBytes?: number;
|
|
19
19
|
/** Controls logging verbosity */
|
|
20
20
|
log?: (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Optional filter function to include/exclude files and directories.
|
|
23
|
+
* Called with the relative path from localFolderPath (using forward slashes).
|
|
24
|
+
* For directories, the path ends with '/'.
|
|
25
|
+
* Return true to include, false to exclude.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Exclude build folder
|
|
29
|
+
* filter: (path) => !path.startsWith('build/')
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Only include source files
|
|
33
|
+
* filter: (path) => path.startsWith('src/') || path.endsWith('.json')
|
|
34
|
+
*/
|
|
35
|
+
filter?: (relativePath: string) => boolean;
|
|
21
36
|
};
|
|
22
37
|
export type SyncFolderResult = {
|
|
23
38
|
installedAppPath?: string;
|
|
@@ -27,5 +42,4 @@ export type SyncFolderResult = {
|
|
|
27
42
|
};
|
|
28
43
|
export type SyncAppResult = SyncFolderResult;
|
|
29
44
|
export declare function syncApp(localFolderPath: string, opts: FolderSyncOptions): Promise<SyncFolderResult>;
|
|
30
|
-
export declare function syncFolder(localFolderPath: string, opts: FolderSyncOptions): Promise<SyncFolderResult>;
|
|
31
45
|
//# sourceMappingURL=folder-sync.d.ts.map
|
package/folder-sync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folder-sync.d.ts","sourceRoot":"","sources":["src/folder-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"folder-sync.d.ts","sourceRoot":"","sources":["src/folder-sync.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,qBAAqB,GAAG,mBAAmB,GAAG,eAAe,CAAC;IAC3E,uFAAuF;IACvF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAmTF,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAE7C,wBAAsB,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4CzG"}
|