@crewx/app 0.1.0-rc.9 → 0.1.0-rc.91
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/SKILL.md +39 -4
- package/build-app/index.d.mts +64 -0
- package/build-app/index.mjs +7 -0
- package/build-app/load.cjs +1 -0
- package/build-app/manifest.mjs +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +3 -91
- package/dist/src/engine.d.ts +5 -2
- package/dist/src/engine.js +8 -1200
- package/dist/src/image-artifact.d.ts +24 -0
- package/dist/src/image-artifact.js +1 -0
- package/dist/src/receipt-wait.d.ts +24 -0
- package/dist/src/receipt-wait.js +1 -0
- package/dist/src/usage.d.ts +27 -0
- package/dist/src/usage.js +8 -0
- package/package.json +6 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/src/engine.d.ts.map +0 -1
- package/dist/src/engine.js.map +0 -1
package/dist/src/engine.js
CHANGED
|
@@ -1,1200 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.executeAppCommand = executeAppCommand;
|
|
37
|
-
const node_crypto_1 = require("node:crypto");
|
|
38
|
-
const node_child_process_1 = require("node:child_process");
|
|
39
|
-
const fs = __importStar(require("node:fs"));
|
|
40
|
-
const http = __importStar(require("node:http"));
|
|
41
|
-
const https = __importStar(require("node:https"));
|
|
42
|
-
const path = __importStar(require("node:path"));
|
|
43
|
-
const node_url_1 = require("node:url");
|
|
44
|
-
const crewx_api_credential_1 = require("@crewx/shared/crewx-api-credential");
|
|
45
|
-
const REQUEST_TIMEOUT_MS = 15000;
|
|
46
|
-
const DEV_ACTION_TIMEOUT_MS = 60000;
|
|
47
|
-
const MAX_BUILD_OUTPUT_BYTES = 64 * 1024;
|
|
48
|
-
const APP_ID_PREFIX = 'uic_';
|
|
49
|
-
const APP_DEV_API_PREFIX = '/api/v1';
|
|
50
|
-
const APP_DEV_CAPABILITIES_PATH = '/api/v1/app-dev/capabilities';
|
|
51
|
-
const APP_DEV_SESSIONS_PATH = '/api/v1/app-dev/sessions';
|
|
52
|
-
const APP_DEV_TOO_OLD_EXIT = 12;
|
|
53
|
-
const APP_DEV_CHECK_FAILED_EXIT = 13;
|
|
54
|
-
const REQUEST_ID_PATTERN = /^[A-Za-z0-9_-]{8,128}$/;
|
|
55
|
-
const LOOPBACK_HOSTS = new Set(['127.0.0.1', '[::1]', 'localhost']);
|
|
56
|
-
const DEFAULT_APP_DEV_ENDPOINT = 'http://localhost:8150';
|
|
57
|
-
const BLOCKED_ROUTE_MESSAGE = 'Do not work around this by clicking the screen or using another browser. Report that you could not use the App route.';
|
|
58
|
-
const UNAUTHORIZED_MESSAGE = 'Do not retry. Report the failure.';
|
|
59
|
-
function isRecord(value) {
|
|
60
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
61
|
-
}
|
|
62
|
-
function failure(exitCode, code, status, message) {
|
|
63
|
-
let stderr = message;
|
|
64
|
-
if (!stderr && (exitCode === 3 || exitCode === 9 || exitCode === 10 || exitCode === 11)) {
|
|
65
|
-
stderr = BLOCKED_ROUTE_MESSAGE;
|
|
66
|
-
}
|
|
67
|
-
else if (!stderr && exitCode === 4) {
|
|
68
|
-
stderr = UNAUTHORIZED_MESSAGE;
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
exitCode,
|
|
72
|
-
payload: { ok: false, exit: exitCode, error: { code, status } },
|
|
73
|
-
...(stderr ? { stderr } : {}),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function success(payload) {
|
|
77
|
-
return { exitCode: 0, payload };
|
|
78
|
-
}
|
|
79
|
-
function usageFailure() {
|
|
80
|
-
return failure(2, 'USAGE', null);
|
|
81
|
-
}
|
|
82
|
-
function parseCommand(args) {
|
|
83
|
-
const command = args[0];
|
|
84
|
-
if (command !== 'list' && command !== 'open' && command !== 'state' && command !== 'run' && command !== 'receipt') {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
const positional = [];
|
|
88
|
-
const options = new Map();
|
|
89
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
90
|
-
const arg = args[index];
|
|
91
|
-
if (!arg.startsWith('--')) {
|
|
92
|
-
positional.push(arg);
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
const separator = arg.indexOf('=');
|
|
96
|
-
const key = separator >= 0 ? arg.slice(2, separator) : arg.slice(2);
|
|
97
|
-
const inlineValue = separator >= 0 ? arg.slice(separator + 1) : undefined;
|
|
98
|
-
if ((key !== 'input' && key !== 'request-id') || options.has(key))
|
|
99
|
-
return null;
|
|
100
|
-
const value = inlineValue ?? args[index + 1];
|
|
101
|
-
if (value === undefined)
|
|
102
|
-
return null;
|
|
103
|
-
if (inlineValue === undefined)
|
|
104
|
-
index += 1;
|
|
105
|
-
options.set(key, value);
|
|
106
|
-
}
|
|
107
|
-
if ((command === 'list' && positional.length !== 0)
|
|
108
|
-
|| (command === 'open' && positional.length !== 1)
|
|
109
|
-
|| (command === 'state' && (positional.length !== 1 || !positional[0].startsWith(APP_ID_PREFIX)))
|
|
110
|
-
|| (command === 'run' && positional.length !== 2)
|
|
111
|
-
|| (command === 'receipt' && positional.length !== 1)) {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
if (command !== 'run' && (options.has('input') || options.has('request-id')))
|
|
115
|
-
return null;
|
|
116
|
-
let input = {};
|
|
117
|
-
if (options.has('input')) {
|
|
118
|
-
const raw = options.get('input');
|
|
119
|
-
try {
|
|
120
|
-
const parsed = JSON.parse(raw === '-' ? fs.readFileSync(0, 'utf8') : raw);
|
|
121
|
-
if (!isRecord(parsed))
|
|
122
|
-
return null;
|
|
123
|
-
input = parsed;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const requestId = options.get('request-id');
|
|
130
|
-
if (requestId !== undefined && !REQUEST_ID_PATTERN.test(requestId))
|
|
131
|
-
return null;
|
|
132
|
-
return {
|
|
133
|
-
command,
|
|
134
|
-
positional,
|
|
135
|
-
input,
|
|
136
|
-
...(requestId !== undefined ? { requestId } : {}),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function getEndpoint(raw) {
|
|
140
|
-
if (!raw)
|
|
141
|
-
return null;
|
|
142
|
-
let endpoint;
|
|
143
|
-
try {
|
|
144
|
-
endpoint = new node_url_1.URL(raw);
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
if (!LOOPBACK_HOSTS.has(endpoint.hostname)
|
|
150
|
-
|| (endpoint.protocol !== 'http:' && endpoint.protocol !== 'https:')
|
|
151
|
-
|| endpoint.username !== ''
|
|
152
|
-
|| endpoint.password !== ''
|
|
153
|
-
|| endpoint.search !== ''
|
|
154
|
-
|| endpoint.hash !== ''
|
|
155
|
-
|| (endpoint.pathname !== '' && endpoint.pathname !== '/')) {
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
return endpoint;
|
|
159
|
-
}
|
|
160
|
-
function segment(value) {
|
|
161
|
-
return encodeURIComponent(value);
|
|
162
|
-
}
|
|
163
|
-
function basePath(workspaceSlug, threadId) {
|
|
164
|
-
return [
|
|
165
|
-
'api', 'v1', 'ws', segment(workspaceSlug), 'threads', segment(threadId),
|
|
166
|
-
].join('/');
|
|
167
|
-
}
|
|
168
|
-
function pathFor(context, ...segments) {
|
|
169
|
-
return `/${context.basePath}/${segments.map(segment).join('/')}`;
|
|
170
|
-
}
|
|
171
|
-
function responseErrorCode(data, fallback) {
|
|
172
|
-
if (!isRecord(data) || !isRecord(data['error']))
|
|
173
|
-
return fallback;
|
|
174
|
-
const code = data['error']['code'];
|
|
175
|
-
return typeof code === 'string' && code.length > 0 ? code : fallback;
|
|
176
|
-
}
|
|
177
|
-
function classifyHttpFailure(response) {
|
|
178
|
-
const { status, data } = response;
|
|
179
|
-
if (status >= 300 && status < 400)
|
|
180
|
-
return failure(9, 'ENDPOINT_REJECTED', status);
|
|
181
|
-
if (status === 401)
|
|
182
|
-
return failure(4, responseErrorCode(data, 'UNAUTHORIZED'), status);
|
|
183
|
-
if (status === 403)
|
|
184
|
-
return failure(5, responseErrorCode(data, 'FORBIDDEN'), status);
|
|
185
|
-
if (status === 404)
|
|
186
|
-
return failure(6, responseErrorCode(data, 'NOT_FOUND'), status);
|
|
187
|
-
if (status === 409) {
|
|
188
|
-
const code = responseErrorCode(data, 'CONFLICT');
|
|
189
|
-
if (code === 'APPROVAL_ALREADY_PENDING') {
|
|
190
|
-
const error = isRecord(data) && isRecord(data['error']) ? data['error'] : undefined;
|
|
191
|
-
const details = error && isRecord(error['details']) ? error['details'] : undefined;
|
|
192
|
-
const receiptId = error?.['receiptId'] ?? details?.['receiptId']
|
|
193
|
-
?? (isRecord(data) ? data['receiptId'] : undefined);
|
|
194
|
-
return {
|
|
195
|
-
exitCode: 8,
|
|
196
|
-
payload: {
|
|
197
|
-
ok: false,
|
|
198
|
-
exit: 8,
|
|
199
|
-
error: { code, status },
|
|
200
|
-
...(typeof receiptId === 'string' ? { receiptId } : {}),
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
return failure(7, code, status);
|
|
205
|
-
}
|
|
206
|
-
return failure(1, responseErrorCode(data, 'ERROR'), status);
|
|
207
|
-
}
|
|
208
|
-
function networkFailure(error) {
|
|
209
|
-
const errorCode = isRecord(error) && typeof error['code'] === 'string'
|
|
210
|
-
? error['code']
|
|
211
|
-
: '';
|
|
212
|
-
const codexBlocked = process.env['CODEX_SANDBOX_NETWORK_DISABLED'] === '1';
|
|
213
|
-
const errnoBlocked = !codexBlocked && (errorCode === 'EPERM' || errorCode === 'EACCES');
|
|
214
|
-
return codexBlocked || errnoBlocked
|
|
215
|
-
? failure(11, 'SANDBOX_BLOCKED', null)
|
|
216
|
-
: failure(10, 'SERVER_UNREACHABLE', null);
|
|
217
|
-
}
|
|
218
|
-
function requestJson(endpoint, authorizationToken, method, requestPath, body, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
219
|
-
const requestFunction = endpoint.protocol === 'https:' ? https.request : http.request;
|
|
220
|
-
const hostname = endpoint.hostname === '[::1]'
|
|
221
|
-
? '::1'
|
|
222
|
-
: endpoint.hostname;
|
|
223
|
-
const port = endpoint.port ? Number(endpoint.port) : undefined;
|
|
224
|
-
const bodyText = body === undefined ? undefined : JSON.stringify(body);
|
|
225
|
-
const request = requestFunction({
|
|
226
|
-
protocol: endpoint.protocol,
|
|
227
|
-
hostname,
|
|
228
|
-
...(port !== undefined ? { port } : {}),
|
|
229
|
-
method,
|
|
230
|
-
path: requestPath,
|
|
231
|
-
headers: {
|
|
232
|
-
Authorization: `Bearer ${authorizationToken}`,
|
|
233
|
-
Accept: 'application/json',
|
|
234
|
-
...(bodyText !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
235
|
-
},
|
|
236
|
-
}, (response) => {
|
|
237
|
-
const status = response.statusCode ?? 0;
|
|
238
|
-
if (status >= 300 && status < 400) {
|
|
239
|
-
response.destroy();
|
|
240
|
-
clearTimeout(timeout);
|
|
241
|
-
resolveResponse({ status, data: undefined, invalidJson: false, rawText: '' });
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const chunks = [];
|
|
245
|
-
response.setEncoding('utf8');
|
|
246
|
-
response.on('data', (chunk) => chunks.push(chunk));
|
|
247
|
-
response.once('error', (error) => failResponse(error));
|
|
248
|
-
response.once('end', () => {
|
|
249
|
-
clearTimeout(timeout);
|
|
250
|
-
const raw = chunks.join('');
|
|
251
|
-
try {
|
|
252
|
-
resolveResponse({ status, data: JSON.parse(raw), invalidJson: false, rawText: raw });
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
resolveResponse({ status, data: undefined, invalidJson: true, rawText: raw });
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
let socketError;
|
|
260
|
-
let resolveResponse;
|
|
261
|
-
let failResponse;
|
|
262
|
-
const result = new Promise((resolve, reject) => {
|
|
263
|
-
resolveResponse = resolve;
|
|
264
|
-
failResponse = reject;
|
|
265
|
-
});
|
|
266
|
-
const timeout = setTimeout(() => {
|
|
267
|
-
const error = new Error('Request timed out');
|
|
268
|
-
error.code = 'ETIMEDOUT';
|
|
269
|
-
request.destroy(error);
|
|
270
|
-
}, timeoutMs);
|
|
271
|
-
request.on('socket', (socket) => {
|
|
272
|
-
socket.once('error', (error) => {
|
|
273
|
-
socketError = error;
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
request.once('error', (error) => {
|
|
277
|
-
clearTimeout(timeout);
|
|
278
|
-
failResponse(socketError ?? error);
|
|
279
|
-
});
|
|
280
|
-
if (bodyText !== undefined)
|
|
281
|
-
request.write(bodyText);
|
|
282
|
-
request.end();
|
|
283
|
-
return result;
|
|
284
|
-
}
|
|
285
|
-
async function performRequest(context, method, requestPath, body) {
|
|
286
|
-
try {
|
|
287
|
-
const response = await requestJson(context.endpoint, context.ticket, method, requestPath, body);
|
|
288
|
-
if (response.status < 200 || response.status >= 300) {
|
|
289
|
-
return { ok: false, outcome: classifyHttpFailure(response) };
|
|
290
|
-
}
|
|
291
|
-
if (response.invalidJson || !isRecord(response.data) || response.data['success'] !== true) {
|
|
292
|
-
return {
|
|
293
|
-
ok: false,
|
|
294
|
-
outcome: failure(1, responseErrorCode(response.data, 'ERROR'), response.status),
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
const data = response.data['data'];
|
|
298
|
-
if (!isRecord(data)) {
|
|
299
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
300
|
-
}
|
|
301
|
-
return { ok: true, value: data, status: response.status };
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
return { ok: false, outcome: networkFailure(error) };
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
function appDevTooOld(status) {
|
|
308
|
-
return failure(APP_DEV_TOO_OLD_EXIT, 'SERVER_TOO_OLD', status, 'The running CrewX does not support App dev mode.');
|
|
309
|
-
}
|
|
310
|
-
function appDevCredentialFailure(error) {
|
|
311
|
-
if (error instanceof crewx_api_credential_1.CrewxCredentialError) {
|
|
312
|
-
return failure(error.exitCode, error.code, null, error.message);
|
|
313
|
-
}
|
|
314
|
-
return failure(1, 'ERROR', null);
|
|
315
|
-
}
|
|
316
|
-
function appDevEndpoint() {
|
|
317
|
-
const raw = process.env['CREWX_ENDPOINT']?.trim() || DEFAULT_APP_DEV_ENDPOINT;
|
|
318
|
-
return getEndpoint(raw);
|
|
319
|
-
}
|
|
320
|
-
function isDirectory(directory) {
|
|
321
|
-
try {
|
|
322
|
-
return fs.statSync(directory).isDirectory();
|
|
323
|
-
}
|
|
324
|
-
catch {
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
function isFile(filePath) {
|
|
329
|
-
try {
|
|
330
|
-
return fs.statSync(filePath).isFile();
|
|
331
|
-
}
|
|
332
|
-
catch {
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function resolveWorkspaceRoot(startDirectory = process.cwd()) {
|
|
337
|
-
let current = path.resolve(startDirectory);
|
|
338
|
-
while (true) {
|
|
339
|
-
if (isDirectory(path.join(current, 'apps')) || isFile(path.join(current, 'crewx.yaml'))) {
|
|
340
|
-
try {
|
|
341
|
-
return fs.realpathSync(current);
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
return undefined;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
const parent = path.dirname(current);
|
|
348
|
-
if (parent === current)
|
|
349
|
-
return undefined;
|
|
350
|
-
current = parent;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
function readAppManifest(directory) {
|
|
354
|
-
try {
|
|
355
|
-
const parsed = JSON.parse(fs.readFileSync(path.join(directory, 'ui-plugin.json'), 'utf8'));
|
|
356
|
-
return isRecord(parsed) ? parsed : undefined;
|
|
357
|
-
}
|
|
358
|
-
catch {
|
|
359
|
-
return undefined;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
function isPathInside(parentDirectory, candidate) {
|
|
363
|
-
const relative = path.relative(parentDirectory, candidate);
|
|
364
|
-
return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
365
|
-
}
|
|
366
|
-
function resolveWorkspaceApp(workspacePath, appSelector) {
|
|
367
|
-
if (!appSelector || appSelector === '.' || appSelector === '..' || /[\\/]/.test(appSelector)) {
|
|
368
|
-
return failure(2, 'USAGE', null, 'App must be a folder name or pluginId.');
|
|
369
|
-
}
|
|
370
|
-
const appsDirectory = path.join(workspacePath, 'apps');
|
|
371
|
-
const directDirectory = path.join(appsDirectory, appSelector);
|
|
372
|
-
let appDirectory = isDirectory(directDirectory) ? directDirectory : undefined;
|
|
373
|
-
if (!appDirectory && isDirectory(appsDirectory)) {
|
|
374
|
-
try {
|
|
375
|
-
for (const item of fs.readdirSync(appsDirectory, { withFileTypes: true })) {
|
|
376
|
-
if (!item.isDirectory())
|
|
377
|
-
continue;
|
|
378
|
-
const candidate = path.join(appsDirectory, item.name);
|
|
379
|
-
const manifest = readAppManifest(candidate);
|
|
380
|
-
if (manifest?.['pluginId'] === appSelector) {
|
|
381
|
-
appDirectory = candidate;
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
catch {
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
let appId = appSelector;
|
|
390
|
-
if (appDirectory) {
|
|
391
|
-
try {
|
|
392
|
-
appDirectory = fs.realpathSync(appDirectory);
|
|
393
|
-
}
|
|
394
|
-
catch {
|
|
395
|
-
return failure(6, 'APP_INVALID', null);
|
|
396
|
-
}
|
|
397
|
-
if (!isPathInside(fs.realpathSync(appsDirectory), appDirectory)) {
|
|
398
|
-
return failure(2, 'USAGE', null, 'App folder must be inside the workspace apps directory.');
|
|
399
|
-
}
|
|
400
|
-
const manifest = readAppManifest(appDirectory);
|
|
401
|
-
if (typeof manifest?.['pluginId'] === 'string' && manifest['pluginId'].length > 0) {
|
|
402
|
-
appId = manifest['pluginId'];
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
appDirectory = directDirectory;
|
|
407
|
-
}
|
|
408
|
-
return { appId, appDirectory };
|
|
409
|
-
}
|
|
410
|
-
function successEnvelope(response) {
|
|
411
|
-
if (response.status < 200 || response.status >= 300) {
|
|
412
|
-
return { ok: false, outcome: classifyHttpFailure(response) };
|
|
413
|
-
}
|
|
414
|
-
if (response.invalidJson || !isRecord(response.data) || response.data['success'] !== true
|
|
415
|
-
|| !isRecord(response.data['data'])) {
|
|
416
|
-
return { ok: false, outcome: failure(1, responseErrorCode(response.data, 'ERROR'), response.status) };
|
|
417
|
-
}
|
|
418
|
-
return {
|
|
419
|
-
ok: true,
|
|
420
|
-
value: response.data,
|
|
421
|
-
status: response.status,
|
|
422
|
-
rawText: response.rawText,
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
async function checkAppDevCapabilities(endpoint, token) {
|
|
426
|
-
try {
|
|
427
|
-
const response = await requestJson(endpoint, token, 'GET', APP_DEV_CAPABILITIES_PATH);
|
|
428
|
-
if (response.status === 404 || (response.status >= 300 && response.status < 400)
|
|
429
|
-
|| response.invalidJson) {
|
|
430
|
-
return { ok: false, outcome: appDevTooOld(response.status) };
|
|
431
|
-
}
|
|
432
|
-
if (response.status < 200 || response.status >= 300) {
|
|
433
|
-
return { ok: false, outcome: classifyHttpFailure(response) };
|
|
434
|
-
}
|
|
435
|
-
if (!isRecord(response.data) || response.data['success'] !== true
|
|
436
|
-
|| !isRecord(response.data['data']) || response.data['data']['appDev'] !== 1) {
|
|
437
|
-
return { ok: false, outcome: appDevTooOld(response.status) };
|
|
438
|
-
}
|
|
439
|
-
return {
|
|
440
|
-
ok: true,
|
|
441
|
-
value: response.data,
|
|
442
|
-
status: response.status,
|
|
443
|
-
rawText: response.rawText,
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
catch (error) {
|
|
447
|
-
return { ok: false, outcome: networkFailure(error) };
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
async function prepareAppDevContext(appSelector) {
|
|
451
|
-
const endpoint = appDevEndpoint();
|
|
452
|
-
if (!endpoint)
|
|
453
|
-
return { ok: false, outcome: failure(9, 'ENDPOINT_REJECTED', null) };
|
|
454
|
-
let token;
|
|
455
|
-
try {
|
|
456
|
-
token = (0, crewx_api_credential_1.resolveCrewxApiCredential)('app-dev').token;
|
|
457
|
-
}
|
|
458
|
-
catch (error) {
|
|
459
|
-
return { ok: false, outcome: appDevCredentialFailure(error) };
|
|
460
|
-
}
|
|
461
|
-
const capabilities = await checkAppDevCapabilities(endpoint, token);
|
|
462
|
-
if (!capabilities.ok)
|
|
463
|
-
return { ok: false, outcome: capabilities.outcome };
|
|
464
|
-
const workspacePath = resolveWorkspaceRoot();
|
|
465
|
-
if (!workspacePath) {
|
|
466
|
-
return {
|
|
467
|
-
ok: false,
|
|
468
|
-
outcome: failure(2, 'WORKSPACE_NOT_FOUND', null, 'No workspace root with apps/ or crewx.yaml was found.'),
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
const app = resolveWorkspaceApp(workspacePath, appSelector);
|
|
472
|
-
if ('exitCode' in app)
|
|
473
|
-
return { ok: false, outcome: app };
|
|
474
|
-
let response;
|
|
475
|
-
try {
|
|
476
|
-
response = await requestJson(endpoint, token, 'POST', APP_DEV_SESSIONS_PATH, {
|
|
477
|
-
workspacePath,
|
|
478
|
-
appId: app.appId,
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
catch (error) {
|
|
482
|
-
return { ok: false, outcome: networkFailure(error) };
|
|
483
|
-
}
|
|
484
|
-
const opened = successEnvelope(response);
|
|
485
|
-
if (!opened.ok)
|
|
486
|
-
return { ok: false, outcome: opened.outcome };
|
|
487
|
-
const data = opened.value['data'];
|
|
488
|
-
if (!isRecord(data)
|
|
489
|
-
|| typeof data['workspaceSlug'] !== 'string'
|
|
490
|
-
|| typeof data['appId'] !== 'string'
|
|
491
|
-
|| typeof data['devPath'] !== 'string'
|
|
492
|
-
|| !data['devPath'].startsWith('/ws/')) {
|
|
493
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
494
|
-
}
|
|
495
|
-
if (!isDirectory(app.appDirectory)) {
|
|
496
|
-
return { ok: false, outcome: failure(6, 'APP_INVALID', response.status) };
|
|
497
|
-
}
|
|
498
|
-
return {
|
|
499
|
-
ok: true,
|
|
500
|
-
context: {
|
|
501
|
-
endpoint,
|
|
502
|
-
token,
|
|
503
|
-
workspacePath,
|
|
504
|
-
appId: data['appId'],
|
|
505
|
-
appDirectory: app.appDirectory,
|
|
506
|
-
devPath: data['devPath'],
|
|
507
|
-
},
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
function appDevPath(context, ...segments) {
|
|
511
|
-
return `${APP_DEV_API_PREFIX}${context.devPath}${segments.length > 0 ? `/${segments.map(segment).join('/')}` : ''}`;
|
|
512
|
-
}
|
|
513
|
-
async function performAppDevRequest(context, method, requestPath, body, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
514
|
-
try {
|
|
515
|
-
const response = await requestJson(context.endpoint, context.token, method, requestPath, body, timeoutMs);
|
|
516
|
-
return successEnvelope(response);
|
|
517
|
-
}
|
|
518
|
-
catch (error) {
|
|
519
|
-
return { ok: false, outcome: networkFailure(error) };
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
function appIdFrom(value) {
|
|
523
|
-
const direct = value['appId'] ?? value['id'];
|
|
524
|
-
if (typeof direct === 'string' && direct.length > 0)
|
|
525
|
-
return direct;
|
|
526
|
-
const definition = value['definition'];
|
|
527
|
-
if (isRecord(definition) && typeof definition['id'] === 'string')
|
|
528
|
-
return definition['id'];
|
|
529
|
-
return undefined;
|
|
530
|
-
}
|
|
531
|
-
function hashFrom(value) {
|
|
532
|
-
const direct = value['definitionHash'];
|
|
533
|
-
if (typeof direct === 'string' && direct.length > 0)
|
|
534
|
-
return direct;
|
|
535
|
-
const definition = value['definition'];
|
|
536
|
-
if (isRecord(definition) && typeof definition['hash'] === 'string') {
|
|
537
|
-
return definition['hash'];
|
|
538
|
-
}
|
|
539
|
-
return undefined;
|
|
540
|
-
}
|
|
541
|
-
function projectLauncher(value) {
|
|
542
|
-
if (!isRecord(value))
|
|
543
|
-
return undefined;
|
|
544
|
-
const appId = appIdFrom(value);
|
|
545
|
-
const definitionHash = hashFrom(value);
|
|
546
|
-
if (!appId || !definitionHash)
|
|
547
|
-
return undefined;
|
|
548
|
-
const actions = value['agentActions'];
|
|
549
|
-
return {
|
|
550
|
-
appId,
|
|
551
|
-
name: typeof value['name'] === 'string' ? value['name'] : '',
|
|
552
|
-
description: typeof value['description'] === 'string' ? value['description'] : '',
|
|
553
|
-
definitionHash,
|
|
554
|
-
agentActions: Array.isArray(actions) ? actions : [],
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
async function loadLaunchers(context) {
|
|
558
|
-
const response = await performRequest(context, 'GET', pathFor(context, 'ui-instances', 'apps'));
|
|
559
|
-
if (!response.ok)
|
|
560
|
-
return response;
|
|
561
|
-
const apps = response.value['apps'];
|
|
562
|
-
if (!Array.isArray(apps)) {
|
|
563
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
564
|
-
}
|
|
565
|
-
const projected = apps.map(projectLauncher);
|
|
566
|
-
if (projected.some((item) => item === undefined)) {
|
|
567
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
568
|
-
}
|
|
569
|
-
return { ok: true, value: projected, status: response.status };
|
|
570
|
-
}
|
|
571
|
-
function findLauncher(apps, appId) {
|
|
572
|
-
return apps.find((app) => app.appId === appId);
|
|
573
|
-
}
|
|
574
|
-
async function openLauncher(context, launcher) {
|
|
575
|
-
const response = await performRequest(context, 'POST', pathFor(context, 'ui-instances', 'open'), {
|
|
576
|
-
appId: launcher.appId,
|
|
577
|
-
expectedDefinitionHash: launcher.definitionHash,
|
|
578
|
-
});
|
|
579
|
-
if (!response.ok)
|
|
580
|
-
return response;
|
|
581
|
-
const instance = isRecord(response.value['instance']) ? response.value['instance'] : response.value;
|
|
582
|
-
if (!isRecord(instance)) {
|
|
583
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
584
|
-
}
|
|
585
|
-
const instanceId = instance['instanceId'] ?? instance['id'];
|
|
586
|
-
const revision = instance['revision'];
|
|
587
|
-
if (typeof instanceId !== 'string' || typeof revision !== 'number') {
|
|
588
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
589
|
-
}
|
|
590
|
-
const ai = isRecord(response.value['ai'])
|
|
591
|
-
? response.value['ai']
|
|
592
|
-
: isRecord(instance['ai']) ? instance['ai'] : undefined;
|
|
593
|
-
return {
|
|
594
|
-
ok: true,
|
|
595
|
-
status: response.status,
|
|
596
|
-
value: {
|
|
597
|
-
appId: launcher.appId,
|
|
598
|
-
definitionHash: launcher.definitionHash,
|
|
599
|
-
instanceId,
|
|
600
|
-
revision,
|
|
601
|
-
state: ai && Object.prototype.hasOwnProperty.call(ai, 'state') ? ai['state'] : null,
|
|
602
|
-
},
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
async function readInstanceState(context, instanceId) {
|
|
606
|
-
const response = await performRequest(context, 'GET', pathFor(context, 'ui-instances', instanceId));
|
|
607
|
-
if (!response.ok)
|
|
608
|
-
return response;
|
|
609
|
-
const value = response.value;
|
|
610
|
-
const responseInstanceId = value['instanceId'];
|
|
611
|
-
const revision = value['revision'];
|
|
612
|
-
if (typeof responseInstanceId !== 'string' || typeof revision !== 'number') {
|
|
613
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
614
|
-
}
|
|
615
|
-
const ai = isRecord(value['ai']) ? value['ai'] : undefined;
|
|
616
|
-
const passthrough = (key) => {
|
|
617
|
-
if (Object.prototype.hasOwnProperty.call(value, key))
|
|
618
|
-
return { [key]: value[key] };
|
|
619
|
-
if (ai && Object.prototype.hasOwnProperty.call(ai, key))
|
|
620
|
-
return { [key]: ai[key] };
|
|
621
|
-
return {};
|
|
622
|
-
};
|
|
623
|
-
return {
|
|
624
|
-
ok: true,
|
|
625
|
-
status: response.status,
|
|
626
|
-
value: {
|
|
627
|
-
instanceId: responseInstanceId,
|
|
628
|
-
revision,
|
|
629
|
-
state: ai && Object.prototype.hasOwnProperty.call(ai, 'state') ? ai['state'] : null,
|
|
630
|
-
buttons: ai && Object.prototype.hasOwnProperty.call(ai, 'buttons') ? ai['buttons'] : null,
|
|
631
|
-
...passthrough('agentAccess'),
|
|
632
|
-
...passthrough('modelContext'),
|
|
633
|
-
...passthrough('pendingApproval'),
|
|
634
|
-
},
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
async function runAction(context, instanceId, actionId, requestId, input) {
|
|
638
|
-
const response = await performRequest(context, 'POST', pathFor(context, 'ui-instances', instanceId, 'actions'), { actionId, requestId, input });
|
|
639
|
-
if (!response.ok)
|
|
640
|
-
return response;
|
|
641
|
-
const receipt = response.value['receipt'];
|
|
642
|
-
if (!isRecord(receipt)) {
|
|
643
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
644
|
-
}
|
|
645
|
-
const receiptId = receipt['receiptId'] ?? receipt['id'];
|
|
646
|
-
const approval = response.value['approval'];
|
|
647
|
-
if (response.status === 202 && isRecord(approval)) {
|
|
648
|
-
const approvalId = approval['approvalId'];
|
|
649
|
-
const expiresAt = approval['expiresAt'];
|
|
650
|
-
const resolvedRequestId = typeof receipt['requestId'] === 'string'
|
|
651
|
-
? receipt['requestId']
|
|
652
|
-
: typeof approval['requestId'] === 'string'
|
|
653
|
-
? approval['requestId']
|
|
654
|
-
: requestId;
|
|
655
|
-
if (typeof receiptId !== 'string' || typeof approvalId !== 'string'
|
|
656
|
-
|| (typeof expiresAt !== 'string' && typeof expiresAt !== 'number')) {
|
|
657
|
-
return { ok: false, outcome: failure(1, 'ERROR', response.status) };
|
|
658
|
-
}
|
|
659
|
-
return {
|
|
660
|
-
ok: false,
|
|
661
|
-
outcome: {
|
|
662
|
-
exitCode: 8,
|
|
663
|
-
payload: {
|
|
664
|
-
ok: false,
|
|
665
|
-
exit: 8,
|
|
666
|
-
error: { code: 'APPROVAL_PENDING', status: 202 },
|
|
667
|
-
receiptId,
|
|
668
|
-
requestId: resolvedRequestId,
|
|
669
|
-
approvalId,
|
|
670
|
-
expiresAt,
|
|
671
|
-
},
|
|
672
|
-
},
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
const status = receipt['status'] ?? (response.status === 202 ? 'requested' : 'settled');
|
|
676
|
-
const resolve = response.value['resolve'];
|
|
677
|
-
const ai = isRecord(resolve) && isRecord(resolve['ai']) ? resolve['ai'] : undefined;
|
|
678
|
-
return {
|
|
679
|
-
ok: true,
|
|
680
|
-
status: response.status,
|
|
681
|
-
value: {
|
|
682
|
-
receiptId: typeof receiptId === 'string' ? receiptId : '',
|
|
683
|
-
requestId: typeof receipt['requestId'] === 'string' ? receipt['requestId'] : requestId,
|
|
684
|
-
instanceId,
|
|
685
|
-
actionId: typeof receipt['actionId'] === 'string' ? receipt['actionId'] : actionId,
|
|
686
|
-
status,
|
|
687
|
-
outcome: Object.prototype.hasOwnProperty.call(receipt, 'outcome') ? receipt['outcome'] : null,
|
|
688
|
-
state: ai && Object.prototype.hasOwnProperty.call(ai, 'state') ? ai['state'] : null,
|
|
689
|
-
},
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
function contextFromEnvironment() {
|
|
693
|
-
const ticket = process.env['CREWX_TASK_TICKET'] ?? '';
|
|
694
|
-
const workspaceSlug = process.env['CREWX_WORKSPACE_SLUG'] ?? '';
|
|
695
|
-
const threadId = process.env['CREWX_THREAD_ID'] ?? '';
|
|
696
|
-
if (!ticket.startsWith('crewxtk_') || !workspaceSlug || !threadId) {
|
|
697
|
-
return failure(3, 'NO_TICKET', null);
|
|
698
|
-
}
|
|
699
|
-
const endpoint = getEndpoint(process.env['CREWX_ENDPOINT']);
|
|
700
|
-
if (!endpoint)
|
|
701
|
-
return failure(9, 'ENDPOINT_REJECTED', null);
|
|
702
|
-
return {
|
|
703
|
-
ticket,
|
|
704
|
-
workspaceSlug,
|
|
705
|
-
threadId,
|
|
706
|
-
endpoint,
|
|
707
|
-
basePath: basePath(workspaceSlug, threadId),
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
function isAppCommandOutcome(value) {
|
|
711
|
-
return 'exitCode' in value;
|
|
712
|
-
}
|
|
713
|
-
function parseSingleAppCommand(args, command) {
|
|
714
|
-
if (args[0] !== command)
|
|
715
|
-
return null;
|
|
716
|
-
const positional = [];
|
|
717
|
-
let flag = false;
|
|
718
|
-
for (const arg of args.slice(1)) {
|
|
719
|
-
if (arg === (command === 'dev' ? '--no-open' : '--json')) {
|
|
720
|
-
if (flag)
|
|
721
|
-
return null;
|
|
722
|
-
flag = true;
|
|
723
|
-
}
|
|
724
|
-
else if (arg.startsWith('--')) {
|
|
725
|
-
return null;
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
|
-
positional.push(arg);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
if (positional.length !== 1)
|
|
732
|
-
return null;
|
|
733
|
-
return { appSelector: positional[0], flag };
|
|
734
|
-
}
|
|
735
|
-
function standardJsonOutput(outcome) {
|
|
736
|
-
return `${JSON.stringify(outcome.payload)}\n`;
|
|
737
|
-
}
|
|
738
|
-
async function executeAppCheck(args) {
|
|
739
|
-
const parsed = parseSingleAppCommand(args, 'check');
|
|
740
|
-
if (!parsed)
|
|
741
|
-
return usageFailure();
|
|
742
|
-
const prepared = await prepareAppDevContext(parsed.appSelector);
|
|
743
|
-
if (!prepared.ok)
|
|
744
|
-
return prepared.outcome;
|
|
745
|
-
const response = await performAppDevRequest(prepared.context, 'GET', appDevPath(prepared.context, 'check'));
|
|
746
|
-
if (!response.ok)
|
|
747
|
-
return response.outcome;
|
|
748
|
-
const data = response.value['data'];
|
|
749
|
-
if (!isRecord(data))
|
|
750
|
-
return failure(1, 'ERROR', response.status);
|
|
751
|
-
const summary = isRecord(data['summary']) ? data['summary'] : {};
|
|
752
|
-
const items = Array.isArray(data['items']) ? data['items'] : [];
|
|
753
|
-
const errors = typeof summary['errors'] === 'number'
|
|
754
|
-
? summary['errors']
|
|
755
|
-
: items.filter((item) => isRecord(item) && item['severity'] === 'error').length;
|
|
756
|
-
const warnings = typeof summary['warnings'] === 'number'
|
|
757
|
-
? summary['warnings']
|
|
758
|
-
: items.filter((item) => isRecord(item) && item['severity'] === 'warning').length;
|
|
759
|
-
const exitCode = errors === 0 ? 0 : APP_DEV_CHECK_FAILED_EXIT;
|
|
760
|
-
if (parsed.flag) {
|
|
761
|
-
return {
|
|
762
|
-
exitCode,
|
|
763
|
-
payload: response.value,
|
|
764
|
-
stdout: `${response.rawText ?? standardJsonOutput({ exitCode, payload: response.value })}`
|
|
765
|
-
+ (response.rawText?.endsWith('\n') ? '' : '\n'),
|
|
766
|
-
suppressOutput: true,
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
const lines = items.flatMap((item) => {
|
|
770
|
-
if (!isRecord(item))
|
|
771
|
-
return [];
|
|
772
|
-
const severity = item['severity'];
|
|
773
|
-
const marker = severity === 'error' ? '✕' : severity === 'ok' ? '✓' : '!';
|
|
774
|
-
const where = typeof item['where'] === 'string' ? item['where'] : '';
|
|
775
|
-
const code = typeof item['code'] === 'string' ? item['code'] : '';
|
|
776
|
-
return [`${marker} ${where} ${code}`];
|
|
777
|
-
});
|
|
778
|
-
lines.push(`Summary: ${errors} errors, ${warnings} warnings`);
|
|
779
|
-
return {
|
|
780
|
-
exitCode,
|
|
781
|
-
payload: {},
|
|
782
|
-
stdout: `${lines.join('\n')}\n`,
|
|
783
|
-
suppressOutput: true,
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
async function getPairingDevUrl(context) {
|
|
787
|
-
let pairingToken;
|
|
788
|
-
try {
|
|
789
|
-
pairingToken = (0, crewx_api_credential_1.resolveCrewxApiCredential)('pairing').token;
|
|
790
|
-
}
|
|
791
|
-
catch (error) {
|
|
792
|
-
return appDevCredentialFailure(error);
|
|
793
|
-
}
|
|
794
|
-
let response;
|
|
795
|
-
try {
|
|
796
|
-
response = await requestJson(context.endpoint, pairingToken, 'POST', '/api/v1/auth/local/code');
|
|
797
|
-
}
|
|
798
|
-
catch (error) {
|
|
799
|
-
return networkFailure(error);
|
|
800
|
-
}
|
|
801
|
-
if (response.status < 200 || response.status >= 300) {
|
|
802
|
-
return classifyHttpFailure(response);
|
|
803
|
-
}
|
|
804
|
-
if (response.invalidJson || !isRecord(response.data) || typeof response.data['url'] !== 'string') {
|
|
805
|
-
return failure(1, 'PAIRING_CODE_INVALID', response.status);
|
|
806
|
-
}
|
|
807
|
-
try {
|
|
808
|
-
const pairingUrl = new node_url_1.URL(response.data['url']);
|
|
809
|
-
const pairingEndpoint = getEndpoint(pairingUrl.origin);
|
|
810
|
-
const match = pairingUrl.hash.match(/^#pair=(.+)$/);
|
|
811
|
-
if (!pairingEndpoint || !match)
|
|
812
|
-
return failure(1, 'PAIRING_CODE_INVALID', response.status);
|
|
813
|
-
return `${pairingEndpoint.origin}${context.devPath}#pair=${encodeURIComponent(match[1])}`;
|
|
814
|
-
}
|
|
815
|
-
catch {
|
|
816
|
-
return failure(1, 'PAIRING_CODE_INVALID', response.status);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
async function openPlatformUrl(url) {
|
|
820
|
-
const command = process.platform === 'darwin'
|
|
821
|
-
? 'open'
|
|
822
|
-
: process.platform === 'win32' ? 'explorer.exe' : 'xdg-open';
|
|
823
|
-
const child = (0, node_child_process_1.spawn)(command, [url], { detached: true, stdio: 'ignore' });
|
|
824
|
-
await new Promise((resolve, reject) => {
|
|
825
|
-
child.once('error', reject);
|
|
826
|
-
child.once('spawn', () => {
|
|
827
|
-
child.unref();
|
|
828
|
-
resolve();
|
|
829
|
-
});
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
async function executeAppDevAction(args) {
|
|
833
|
-
const parsed = parseCommand(['run', ...args.slice(2)]);
|
|
834
|
-
if (!parsed || parsed.command !== 'run')
|
|
835
|
-
return usageFailure();
|
|
836
|
-
const [appSelector, actionId] = parsed.positional;
|
|
837
|
-
const prepared = await prepareAppDevContext(appSelector);
|
|
838
|
-
if (!prepared.ok)
|
|
839
|
-
return prepared.outcome;
|
|
840
|
-
const state = await performAppDevRequest(prepared.context, 'GET', appDevPath(prepared.context, 'state'));
|
|
841
|
-
if (!state.ok)
|
|
842
|
-
return state.outcome;
|
|
843
|
-
const stateData = state.value['data'];
|
|
844
|
-
if (!isRecord(stateData) || typeof stateData['revision'] !== 'number') {
|
|
845
|
-
return failure(1, 'APP_DEV_RESPONSE_INVALID', state.status);
|
|
846
|
-
}
|
|
847
|
-
const requestId = parsed.requestId ?? `cxa_${(0, node_crypto_1.randomBytes)(16).toString('base64url')}`;
|
|
848
|
-
const response = await performAppDevRequest(prepared.context, 'POST', appDevPath(prepared.context, 'actions'), {
|
|
849
|
-
actionId,
|
|
850
|
-
requestId,
|
|
851
|
-
input: parsed.input,
|
|
852
|
-
expectedRevision: stateData['revision'],
|
|
853
|
-
}, DEV_ACTION_TIMEOUT_MS);
|
|
854
|
-
if (!response.ok)
|
|
855
|
-
return response.outcome;
|
|
856
|
-
const responseData = response.value['data'];
|
|
857
|
-
const receipt = isRecord(responseData) && isRecord(responseData['receipt'])
|
|
858
|
-
? responseData['receipt']
|
|
859
|
-
: undefined;
|
|
860
|
-
return {
|
|
861
|
-
exitCode: receipt?.['outcome'] === 'succeeded' ? 0 : 1,
|
|
862
|
-
payload: response.value,
|
|
863
|
-
stdout: `${response.rawText ?? standardJsonOutput({ exitCode: 0, payload: response.value })}`
|
|
864
|
-
+ (response.rawText?.endsWith('\n') ? '' : '\n'),
|
|
865
|
-
suppressOutput: true,
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
function hasBuildScript(appDirectory) {
|
|
869
|
-
try {
|
|
870
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(appDirectory, 'package.json'), 'utf8'));
|
|
871
|
-
if (!isRecord(packageJson) || !isRecord(packageJson['scripts']))
|
|
872
|
-
return false;
|
|
873
|
-
const build = packageJson['scripts']['build'];
|
|
874
|
-
return typeof build === 'string' && build.trim().length > 0;
|
|
875
|
-
}
|
|
876
|
-
catch {
|
|
877
|
-
return false;
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
function buildOutputPaths(appDirectory) {
|
|
881
|
-
const result = new Set([path.normalize('ui-plugin.json')]);
|
|
882
|
-
const manifest = readAppManifest(appDirectory);
|
|
883
|
-
const entry = manifest?.['entry'];
|
|
884
|
-
if (typeof entry === 'string' && entry.length > 0 && !path.isAbsolute(entry)) {
|
|
885
|
-
const normalized = path.normalize(entry);
|
|
886
|
-
if (normalized !== '..' && !normalized.startsWith(`..${path.sep}`))
|
|
887
|
-
result.add(normalized);
|
|
888
|
-
}
|
|
889
|
-
return result;
|
|
890
|
-
}
|
|
891
|
-
function outputFingerprint(appDirectory, relativePath) {
|
|
892
|
-
try {
|
|
893
|
-
const stats = fs.statSync(path.join(appDirectory, relativePath));
|
|
894
|
-
return `${stats.size}:${stats.mtimeMs}:${stats.ino}`;
|
|
895
|
-
}
|
|
896
|
-
catch {
|
|
897
|
-
return null;
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
function snapshotBuildOutputs(appDirectory, paths) {
|
|
901
|
-
return new Map([...paths].map((relativePath) => [
|
|
902
|
-
relativePath,
|
|
903
|
-
outputFingerprint(appDirectory, relativePath),
|
|
904
|
-
]));
|
|
905
|
-
}
|
|
906
|
-
function runNpmBuild(appDirectory, onChild) {
|
|
907
|
-
const startedAt = Date.now();
|
|
908
|
-
let output = '';
|
|
909
|
-
let settled = false;
|
|
910
|
-
const appendOutput = (chunk) => {
|
|
911
|
-
const next = Buffer.from(output + (Buffer.isBuffer(chunk) ? chunk.toString('utf8') : chunk), 'utf8');
|
|
912
|
-
output = next.length > MAX_BUILD_OUTPUT_BYTES
|
|
913
|
-
? next.subarray(next.length - MAX_BUILD_OUTPUT_BYTES).toString('utf8')
|
|
914
|
-
: next.toString('utf8');
|
|
915
|
-
};
|
|
916
|
-
return new Promise((resolve) => {
|
|
917
|
-
const child = (0, node_child_process_1.spawn)('npm', ['run', 'build'], {
|
|
918
|
-
cwd: appDirectory,
|
|
919
|
-
env: process.env,
|
|
920
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
921
|
-
});
|
|
922
|
-
onChild(child);
|
|
923
|
-
child.stdout?.on('data', appendOutput);
|
|
924
|
-
child.stderr?.on('data', appendOutput);
|
|
925
|
-
const finish = (ok) => {
|
|
926
|
-
if (settled)
|
|
927
|
-
return;
|
|
928
|
-
settled = true;
|
|
929
|
-
onChild(undefined);
|
|
930
|
-
resolve({
|
|
931
|
-
ok,
|
|
932
|
-
ms: Math.max(0, Date.now() - startedAt),
|
|
933
|
-
output,
|
|
934
|
-
});
|
|
935
|
-
};
|
|
936
|
-
child.once('error', (error) => {
|
|
937
|
-
appendOutput(`${error.message}\n`);
|
|
938
|
-
finish(false);
|
|
939
|
-
});
|
|
940
|
-
child.once('close', (code) => finish(code === 0));
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
function responseCode(outcome) {
|
|
944
|
-
const error = outcome.payload['error'];
|
|
945
|
-
return isRecord(error) && typeof error['code'] === 'string' ? error['code'] : 'ERROR';
|
|
946
|
-
}
|
|
947
|
-
async function postAppDevReload(context, body) {
|
|
948
|
-
const response = await performAppDevRequest(context, 'POST', appDevPath(context, 'reload'), body);
|
|
949
|
-
if (!response.ok)
|
|
950
|
-
return { errorCode: responseCode(response.outcome) };
|
|
951
|
-
const data = response.value['data'];
|
|
952
|
-
return isRecord(data) && typeof data['generation'] === 'number'
|
|
953
|
-
? { generation: data['generation'] }
|
|
954
|
-
: { errorCode: 'APP_DEV_RESPONSE_INVALID' };
|
|
955
|
-
}
|
|
956
|
-
async function runAppDevWatcher(context, buildEnabled) {
|
|
957
|
-
const parentPid = process.ppid;
|
|
958
|
-
let watcher;
|
|
959
|
-
try {
|
|
960
|
-
watcher = fs.watch(context.appDirectory, { recursive: true }, (_event, filename) => {
|
|
961
|
-
onFileChange(filename);
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
catch (error) {
|
|
965
|
-
return failure(1, 'WATCH_FAILED', null, error instanceof Error ? error.message : 'Unable to watch the App folder.');
|
|
966
|
-
}
|
|
967
|
-
let closed = false;
|
|
968
|
-
let stopRequested = false;
|
|
969
|
-
let cycleRunning = false;
|
|
970
|
-
let queuedChange = false;
|
|
971
|
-
let queuedPath = '';
|
|
972
|
-
let pendingPath = '';
|
|
973
|
-
let debounceTimer;
|
|
974
|
-
let parentCheckTimer;
|
|
975
|
-
let activeBuild;
|
|
976
|
-
let finalOutcome = { exitCode: 0, payload: {}, suppressOutput: true };
|
|
977
|
-
const recentOutputEvents = new Map();
|
|
978
|
-
let resolveRun;
|
|
979
|
-
const result = new Promise((resolve) => {
|
|
980
|
-
resolveRun = resolve;
|
|
981
|
-
});
|
|
982
|
-
const writeLine = (line) => {
|
|
983
|
-
process.stdout.write(`${line}\n`);
|
|
984
|
-
};
|
|
985
|
-
const finish = () => {
|
|
986
|
-
if (closed)
|
|
987
|
-
return;
|
|
988
|
-
closed = true;
|
|
989
|
-
if (debounceTimer)
|
|
990
|
-
clearTimeout(debounceTimer);
|
|
991
|
-
if (parentCheckTimer)
|
|
992
|
-
clearInterval(parentCheckTimer);
|
|
993
|
-
process.removeListener('SIGINT', onSignal);
|
|
994
|
-
process.removeListener('SIGTERM', onSignal);
|
|
995
|
-
resolveRun(finalOutcome);
|
|
996
|
-
};
|
|
997
|
-
const onSignal = () => {
|
|
998
|
-
if (stopRequested)
|
|
999
|
-
return;
|
|
1000
|
-
stopRequested = true;
|
|
1001
|
-
watcher.close();
|
|
1002
|
-
if (debounceTimer)
|
|
1003
|
-
clearTimeout(debounceTimer);
|
|
1004
|
-
if (activeBuild && activeBuild.exitCode === null)
|
|
1005
|
-
activeBuild.kill('SIGTERM');
|
|
1006
|
-
if (!cycleRunning)
|
|
1007
|
-
finish();
|
|
1008
|
-
};
|
|
1009
|
-
const onFileChange = (filename) => {
|
|
1010
|
-
if (stopRequested || closed)
|
|
1011
|
-
return;
|
|
1012
|
-
const relativePath = filename === null ? '' : path.normalize(filename.toString());
|
|
1013
|
-
const parts = relativePath.split(path.sep);
|
|
1014
|
-
if (parts.some((part) => part === 'node_modules' || part === '.crewx' || part === 'dist'))
|
|
1015
|
-
return;
|
|
1016
|
-
const outputPaths = buildEnabled ? buildOutputPaths(context.appDirectory) : new Set();
|
|
1017
|
-
if (activeBuild && outputPaths.has(relativePath))
|
|
1018
|
-
return;
|
|
1019
|
-
const suppressedUntil = recentOutputEvents.get(relativePath);
|
|
1020
|
-
if (suppressedUntil !== undefined) {
|
|
1021
|
-
if (Date.now() <= suppressedUntil)
|
|
1022
|
-
return;
|
|
1023
|
-
recentOutputEvents.delete(relativePath);
|
|
1024
|
-
}
|
|
1025
|
-
if (cycleRunning) {
|
|
1026
|
-
queuedChange = true;
|
|
1027
|
-
queuedPath = relativePath || 'App files';
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
pendingPath = relativePath || 'App files';
|
|
1031
|
-
if (debounceTimer)
|
|
1032
|
-
clearTimeout(debounceTimer);
|
|
1033
|
-
debounceTimer = setTimeout(() => {
|
|
1034
|
-
debounceTimer = undefined;
|
|
1035
|
-
const changedPath = pendingPath || 'App files';
|
|
1036
|
-
pendingPath = '';
|
|
1037
|
-
void runCycle(changedPath, false);
|
|
1038
|
-
}, 200);
|
|
1039
|
-
};
|
|
1040
|
-
const reloadLabel = async (body) => {
|
|
1041
|
-
const reload = await postAppDevReload(context, body);
|
|
1042
|
-
return reload.generation === undefined ? `failed ${reload.errorCode ?? 'ERROR'}` : String(reload.generation);
|
|
1043
|
-
};
|
|
1044
|
-
const runCycle = async (changedPath, initial) => {
|
|
1045
|
-
if (stopRequested || closed)
|
|
1046
|
-
return;
|
|
1047
|
-
cycleRunning = true;
|
|
1048
|
-
if (buildEnabled) {
|
|
1049
|
-
const outputPaths = buildOutputPaths(context.appDirectory);
|
|
1050
|
-
const before = snapshotBuildOutputs(context.appDirectory, outputPaths);
|
|
1051
|
-
const build = await runNpmBuild(context.appDirectory, (child) => {
|
|
1052
|
-
activeBuild = child;
|
|
1053
|
-
});
|
|
1054
|
-
const after = snapshotBuildOutputs(context.appDirectory, outputPaths);
|
|
1055
|
-
for (const [outputPath, fingerprint] of after) {
|
|
1056
|
-
if (before.get(outputPath) !== fingerprint) {
|
|
1057
|
-
recentOutputEvents.set(outputPath, Date.now() + 1000);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
if (stopRequested) {
|
|
1061
|
-
cycleRunning = false;
|
|
1062
|
-
finish();
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
const reloadBody = {
|
|
1066
|
-
reason: 'build',
|
|
1067
|
-
build: {
|
|
1068
|
-
ok: build.ok,
|
|
1069
|
-
ms: build.ms,
|
|
1070
|
-
...(!build.ok ? { output: build.output } : {}),
|
|
1071
|
-
},
|
|
1072
|
-
};
|
|
1073
|
-
const generationLabel = await reloadLabel(reloadBody);
|
|
1074
|
-
writeLine(`${initial ? 'initial' : changedPath} -> build ${build.ok ? 'ok' : 'failed'} ${build.ms}ms -> reload ${generationLabel}`);
|
|
1075
|
-
}
|
|
1076
|
-
else if (!initial) {
|
|
1077
|
-
const generationLabel = await reloadLabel({ reason: 'manual' });
|
|
1078
|
-
writeLine(`${changedPath} -> reload ${generationLabel}`);
|
|
1079
|
-
}
|
|
1080
|
-
cycleRunning = false;
|
|
1081
|
-
if (stopRequested) {
|
|
1082
|
-
finish();
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
if (queuedChange) {
|
|
1086
|
-
const nextPath = queuedPath || 'App files';
|
|
1087
|
-
queuedChange = false;
|
|
1088
|
-
queuedPath = '';
|
|
1089
|
-
void runCycle(nextPath, false);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
if (stopRequested)
|
|
1093
|
-
finish();
|
|
1094
|
-
};
|
|
1095
|
-
watcher.once('error', (error) => {
|
|
1096
|
-
finalOutcome = failure(1, 'WATCH_FAILED', null, error.message);
|
|
1097
|
-
onSignal();
|
|
1098
|
-
});
|
|
1099
|
-
process.once('SIGINT', onSignal);
|
|
1100
|
-
process.once('SIGTERM', onSignal);
|
|
1101
|
-
parentCheckTimer = setInterval(() => {
|
|
1102
|
-
if (process.ppid !== parentPid)
|
|
1103
|
-
onSignal();
|
|
1104
|
-
}, 1000);
|
|
1105
|
-
parentCheckTimer.unref();
|
|
1106
|
-
if (buildEnabled)
|
|
1107
|
-
void runCycle('initial', true);
|
|
1108
|
-
return result;
|
|
1109
|
-
}
|
|
1110
|
-
async function executeAppDev(args) {
|
|
1111
|
-
const parsed = parseSingleAppCommand(args, 'dev');
|
|
1112
|
-
if (!parsed)
|
|
1113
|
-
return usageFailure();
|
|
1114
|
-
const prepared = await prepareAppDevContext(parsed.appSelector);
|
|
1115
|
-
if (!prepared.ok)
|
|
1116
|
-
return prepared.outcome;
|
|
1117
|
-
const browserUrl = `${prepared.context.endpoint.origin}${prepared.context.devPath}`;
|
|
1118
|
-
process.stdout.write(`${browserUrl}\n`);
|
|
1119
|
-
if (!parsed.flag) {
|
|
1120
|
-
const pairingUrl = await getPairingDevUrl(prepared.context);
|
|
1121
|
-
if (typeof pairingUrl !== 'string')
|
|
1122
|
-
return pairingUrl;
|
|
1123
|
-
try {
|
|
1124
|
-
await openPlatformUrl(pairingUrl);
|
|
1125
|
-
}
|
|
1126
|
-
catch (error) {
|
|
1127
|
-
return failure(1, 'BROWSER_OPEN_FAILED', null, error instanceof Error ? error.message : 'Could not open the App dev tab.');
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
return runAppDevWatcher(prepared.context, hasBuildScript(prepared.context.appDirectory));
|
|
1131
|
-
}
|
|
1132
|
-
async function executeAppCommand(args) {
|
|
1133
|
-
if (args[0] === 'dev')
|
|
1134
|
-
return executeAppDev(args);
|
|
1135
|
-
if (args[0] === 'check')
|
|
1136
|
-
return executeAppCheck(args);
|
|
1137
|
-
if (args[0] === 'run' && args[1] === '--dev')
|
|
1138
|
-
return executeAppDevAction(args);
|
|
1139
|
-
const parsed = parseCommand(args);
|
|
1140
|
-
if (!parsed)
|
|
1141
|
-
return usageFailure();
|
|
1142
|
-
const context = contextFromEnvironment();
|
|
1143
|
-
if (isAppCommandOutcome(context))
|
|
1144
|
-
return context;
|
|
1145
|
-
if (parsed.command === 'list') {
|
|
1146
|
-
const result = await loadLaunchers(context);
|
|
1147
|
-
if (!result.ok)
|
|
1148
|
-
return result.outcome;
|
|
1149
|
-
return success({ ok: true, apps: result.value });
|
|
1150
|
-
}
|
|
1151
|
-
if (parsed.command === 'receipt') {
|
|
1152
|
-
const receiptId = parsed.positional[0];
|
|
1153
|
-
const response = await performRequest(context, 'GET', pathFor(context, 'ui-action-receipts', receiptId));
|
|
1154
|
-
if (!response.ok)
|
|
1155
|
-
return response.outcome;
|
|
1156
|
-
const receipt = response.value['receipt'];
|
|
1157
|
-
if (!isRecord(receipt))
|
|
1158
|
-
return failure(1, 'ERROR', response.status);
|
|
1159
|
-
return success({ ok: true, receipt });
|
|
1160
|
-
}
|
|
1161
|
-
if (parsed.command === 'state') {
|
|
1162
|
-
const result = await readInstanceState(context, parsed.positional[0]);
|
|
1163
|
-
if (!result.ok)
|
|
1164
|
-
return result.outcome;
|
|
1165
|
-
return success({ ok: true, ...result.value });
|
|
1166
|
-
}
|
|
1167
|
-
if (parsed.command === 'open') {
|
|
1168
|
-
const appId = parsed.positional[0];
|
|
1169
|
-
const list = await loadLaunchers(context);
|
|
1170
|
-
if (!list.ok)
|
|
1171
|
-
return list.outcome;
|
|
1172
|
-
const launcher = findLauncher(list.value, appId);
|
|
1173
|
-
if (!launcher)
|
|
1174
|
-
return failure(6, 'MCP_APP_NOT_FOUND', null);
|
|
1175
|
-
const opened = await openLauncher(context, launcher);
|
|
1176
|
-
if (!opened.ok)
|
|
1177
|
-
return opened.outcome;
|
|
1178
|
-
return success({ ok: true, ...opened.value });
|
|
1179
|
-
}
|
|
1180
|
-
const [target, actionId] = parsed.positional;
|
|
1181
|
-
const requestId = parsed.requestId ?? `cxa_${(0, node_crypto_1.randomBytes)(16).toString('base64url')}`;
|
|
1182
|
-
let instanceId = target;
|
|
1183
|
-
if (!target.startsWith(APP_ID_PREFIX)) {
|
|
1184
|
-
const list = await loadLaunchers(context);
|
|
1185
|
-
if (!list.ok)
|
|
1186
|
-
return list.outcome;
|
|
1187
|
-
const launcher = findLauncher(list.value, target);
|
|
1188
|
-
if (!launcher)
|
|
1189
|
-
return failure(6, 'MCP_APP_NOT_FOUND', null);
|
|
1190
|
-
const opened = await openLauncher(context, launcher);
|
|
1191
|
-
if (!opened.ok)
|
|
1192
|
-
return opened.outcome;
|
|
1193
|
-
instanceId = opened.value.instanceId;
|
|
1194
|
-
}
|
|
1195
|
-
const action = await runAction(context, instanceId, actionId, requestId, parsed.input);
|
|
1196
|
-
if (!action.ok)
|
|
1197
|
-
return action.outcome;
|
|
1198
|
-
return success({ ok: true, ...action.value });
|
|
1199
|
-
}
|
|
1200
|
-
//# sourceMappingURL=engine.js.map
|
|
1
|
+
"use strict";var We=exports&&exports.__createBinding||(Object.create?(function(e,n,t,r){if(r===void 0)r=t;var o=Object.getOwnPropertyDescriptor(n,t);if(!o||("get"in o?!n.__esModule:o.writable||o.configurable)){o={enumerable:true,get:function(){return n[t]}}}Object.defineProperty(e,r,o)}):(function(e,n,t,r){if(r===void 0)r=t;e[r]=n[t]}));var Ve=exports&&exports.__setModuleDefault||(Object.create?(function(e,n){Object.defineProperty(e,"default",{enumerable:true,value:n})}):function(e,n){e["default"]=n});var U=exports&&exports.__importStar||(function(){var e=function(n){e=Object.getOwnPropertyNames||function(t){var r=[];for(var o in t)if(Object.prototype.hasOwnProperty.call(t,o))r[r.length]=o;return r};return e(n)};return function(n){if(n&&n.__esModule)return n;var t={};if(n!=null){for(var r=e(n),o=0;o<r.length;o++)if(r[o]!=="default")We(t,n,r[o])}Ve(t,n);return t}})();Object.defineProperty(exports,"__esModule",{value:true});exports.executeAppCommand=zt;const pe=require("node:crypto");const de=require("node:child_process");const v=U(require("node:fs"));const le=U(require("node:http"));const fe=U(require("node:https"));const E=U(require("node:path"));const he=require("node:url");const te=require("@crewx/shared/crewx-api-credential");const D=require("./image-artifact.js");const j=require("./receipt-wait.js");const L=require("./usage.js");const N=15e3;const ne=3e4;const Je=6e4;const me=64*1024;const ye="uic_";const Ke="/api/v1";const He="/api/v1/app-dev/capabilities";const Xe="/api/v1/app-dev/sessions";const ze=12;const Ye=13;const Ze=/^[A-Za-z0-9_-]{8,128}$/;const Qe=new Set(["127.0.0.1","[::1]","localhost"]);const et="http://localhost:8150";const tt="Do not work around this by clicking the screen or using another browser. Report that you could not use the App route.";const nt="Do not retry. Report the failure.";const rt="The server did not answer in time. Retry once; if it happens again, report it.";function u(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function d(e,n,t,r){let o=r;if(!o&&(e===3||e===9||e===10||e===11)){o=tt}else if(!o&&e===4){o=nt}return{exitCode:e,payload:{ok:false,exit:e,error:{code:n,status:t}},...o?{stderr:o}:{}}}function P(e){return{exitCode:0,payload:e}}function B(){return d(2,"USAGE",null)}function ge(e){const n=e[0];if(n!=="list"&&n!=="open"&&n!=="state"&&n!=="run"&&n!=="receipt"){return null}const t=[];const r=new Map;for(let a=1;a<e.length;a+=1){const c=e[a];if(!c.startsWith("--")){t.push(c);continue}const p=c.indexOf("=");const l=p>=0?c.slice(2,p):c.slice(2);const f=p>=0?c.slice(p+1):void 0;if(l!=="input"&&l!=="request-id"&&l!=="wait"||r.has(l))return null;const y=f??e[a+1];if(y===void 0)return null;if(f===void 0)a+=1;r.set(l,y)}if(n==="list"&&t.length!==0||n==="open"&&t.length!==1||n==="state"&&(t.length!==1||!t[0].startsWith(ye))||n==="run"&&t.length!==2||n==="receipt"&&t.length!==1){return null}if(n!=="run"&&(r.has("input")||r.has("request-id")))return null;if(n!=="receipt"&&r.has("wait"))return null;let o={};if(r.has("input")){const a=r.get("input");try{const c=JSON.parse(a==="-"?v.readFileSync(0,"utf8"):a);if(!u(c))return null;o=c}catch{return null}}const i=r.get("request-id");if(i!==void 0&&!Ze.test(i))return null;const s=r.has("wait")?(0,j.parseWaitSeconds)(r.get("wait")):void 0;if(s===null)return null;return{command:n,positional:t,input:o,...i!==void 0?{requestId:i}:{},...s!==void 0?{waitSeconds:s}:{}}}function re(e){if(!e)return null;let n;try{n=new he.URL(e)}catch{return null}if(!Qe.has(n.hostname)||n.protocol!=="http:"&&n.protocol!=="https:"||n.username!==""||n.password!==""||n.search!==""||n.hash!==""||n.pathname!==""&&n.pathname!=="/"){return null}return n}function J(e){return encodeURIComponent(e)}function ot(e,n){return["api","v1","ws",J(e),"threads",J(n)].join("/")}function $(e,...n){return`/${e.basePath}/${n.map(J).join("/")}`}function x(e,n){if(!u(e)||!u(e["error"]))return n;const t=e["error"]["code"];return typeof t==="string"&&t.length>0?t:n}function K(e){const{status:n,data:t}=e;if(n>=300&&n<400)return d(9,"ENDPOINT_REJECTED",n);if(n===401)return d(4,x(t,"UNAUTHORIZED"),n);if(n===403)return d(5,x(t,"FORBIDDEN"),n);if(n===404)return d(6,x(t,"NOT_FOUND"),n);if(n===409){const r=x(t,"CONFLICT");if(r==="APPROVAL_ALREADY_PENDING"){const o=u(t)&&u(t["error"])?t["error"]:void 0;const i=o&&u(o["details"])?o["details"]:void 0;const s=o?.["receiptId"]??i?.["receiptId"]??(u(t)?t["receiptId"]:void 0);return{exitCode:8,payload:{ok:false,exit:8,error:{code:r,status:n},...typeof s==="string"?{receiptId:s}:{}}}}return d(7,r,n)}return d(1,x(t,"ERROR"),n)}function F(e,n=true){const t=u(e)&&typeof e["code"]==="string"?e["code"]:"";const r=process.env["CODEX_SANDBOX_NETWORK_DISABLED"]==="1";const o=!r&&(t==="EPERM"||t==="EACCES");if(r||o)return d(11,"SANDBOX_BLOCKED",null);if(n&&t==="ETIMEDOUT"&&u(e)&&e["connectionEstablished"]===true){return d(14,"TIMED_OUT",null,rt)}return d(10,"SERVER_UNREACHABLE",null)}function oe(e,n){if(e.exitCode!==14)return e;return{...e,payload:{...e.payload,requestId:n},stderr:["The request timed out and its outcome is unknown.",`Retry the same command once with --request-id ${n}; the server will return the same request's receipt without running the action twice.`,`If it times out again, report "server response delayed, outcome unknown" with requestId ${n}.`,"Do not report success or failure, click the screen, or use another route."].join("\n")}}function q(e,n,t,r,o,i=N){const s=e.protocol==="https:"?fe.request:le.request;const a=e.hostname==="[::1]"?"::1":e.hostname;const c=e.port?Number(e.port):void 0;const p=o===void 0?void 0:JSON.stringify(o);const l=s({protocol:e.protocol,hostname:a,...c!==void 0?{port:c}:{},method:t,path:r,headers:{Authorization:`Bearer ${n}`,Accept:"application/json",...p!==void 0?{"Content-Type":"application/json"}:{}}},m=>{const _=m.statusCode??0;if(_>=300&&_<400){m.destroy();clearTimeout(g);h({status:_,data:void 0,invalidJson:false,rawText:""});return}const b=[];m.setEncoding("utf8");m.on("data",R=>b.push(R));m.once("error",R=>w(R));m.once("end",()=>{clearTimeout(g);const R=b.join("");try{h({status:_,data:JSON.parse(R),invalidJson:false,rawText:R})}catch{h({status:_,data:void 0,invalidJson:true,rawText:R})}})});let f;let y=false;let h;let w;const A=new Promise((m,_)=>{h=m;w=_});const g=setTimeout(()=>{const m=new Error("Request timed out");m.code="ETIMEDOUT";m.connectionEstablished=y;l.destroy(m)},i);l.on("socket",m=>{const _=()=>{y=true};if(e.protocol==="https:"){const b=m;if(b.authorized)_();else b.once("secureConnect",_)}else{if(m.connecting)m.once("connect",_);else _()}m.once("error",b=>{f=b})});l.once("error",m=>{clearTimeout(g);w(f??m)});if(p!==void 0)l.write(p);l.end();return A}function st(e,n,t,r,o=N){const i=e.protocol==="https:"?fe.request:le.request;const s=e.hostname==="[::1]"?"::1":e.hostname;const a=e.port?Number(e.port):void 0;let c;let p;let l=false;let f;const y=new Promise((g,m)=>{c=g;p=m});const h=g=>{if(l)return;l=true;clearTimeout(f);c(g)};const w=g=>{if(l)return;l=true;clearTimeout(f);p(g)};const A=i({protocol:e.protocol,hostname:s,...a!==void 0?{port:a}:{},method:"GET",path:t,headers:{Authorization:`Bearer ${n}`,Accept:"image/png"}},g=>{const m=g.statusCode??0;const _=typeof g.headers["content-type"]==="string"?g.headers["content-type"]:void 0;if(m>=300&&m<400){g.destroy();h({status:m,contentType:_,bytes:new Uint8Array(0),rawText:""});return}if(m>=200&&m<300&&_?.split(";",1)[0]?.trim().toLowerCase()!=="image/png"){g.destroy();h({status:m,contentType:_,bytes:new Uint8Array(0),rawText:""});return}const b=[];let R=0;const W=m>=200&&m<300?r:64*1024;g.on("data",T=>{if(l)return;const I=typeof T==="string"?Buffer.from(T):T;const O=new Uint8Array(I.byteLength);O.set(I);R+=O.byteLength;if(R>W){g.destroy();w(new D.AppCaptureImageError(m>=200&&m<300?"IMAGE_BYTE_COUNT_MISMATCH":"IMAGE_DOWNLOAD_FAILED",m));return}b.push(O)});g.once("aborted",()=>w(new Error("Image response was interrupted")));g.once("error",T=>w(T));g.once("end",()=>{const T=new Uint8Array(R);let I=0;for(const O of b){T.set(O,I);I+=O.byteLength}h({status:m,contentType:_,bytes:T,rawText:m>=200&&m<300?"":Buffer.from(T).toString("utf8")})})});f=setTimeout(()=>{const g=new Error("Image request timed out");g.code="ETIMEDOUT";A.destroy(g)},o);A.once("error",g=>w(g));A.end();return y}function it(e){try{const n=JSON.parse(e);if(!u(n))return void 0;const t=u(n["error"])?n["error"]:void 0;return t&&typeof t["code"]==="string"?t["code"]:void 0}catch{return void 0}}async function at(e,n,t){let r;try{r=await st(e.endpoint,e.ticket,$(e,"ui-action-receipts",n,"image"),t)}catch(o){if(o instanceof D.AppCaptureImageError)throw o;throw new D.AppCaptureImageError("IMAGE_DOWNLOAD_FAILED")}if(r.status<200||r.status>=300){throw new D.AppCaptureImageError(it(r.rawText)==="IMAGE_EXPIRED"?"IMAGE_EXPIRED":"IMAGE_DOWNLOAD_FAILED",r.status)}if(r.contentType?.split(";",1)[0]?.trim().toLowerCase()!=="image/png"){throw new D.AppCaptureImageError("IMAGE_INVALID_MIME",r.status)}if(r.bytes.byteLength!==t){throw new D.AppCaptureImageError("IMAGE_BYTE_COUNT_MISMATCH",r.status)}return r.bytes}async function G(e,n,t,r,o=N){try{const i=await q(e.endpoint,e.ticket,n,t,r,o);if(i.status<200||i.status>=300){return{ok:false,outcome:K(i)}}if(i.invalidJson||!u(i.data)||i.data["success"]!==true){return{ok:false,outcome:d(1,x(i.data,"ERROR"),i.status)}}const s=i.data["data"];if(!u(s)){return{ok:false,outcome:d(1,"ERROR",i.status)}}return{ok:true,value:s,status:i.status}}catch(i){return{ok:false,outcome:F(i)}}}function we(e){return d(ze,"SERVER_TOO_OLD",e,"The running CrewX does not support App dev mode.")}function Ee(e){if(e instanceof te.CrewxCredentialError){return d(e.exitCode,e.code,null,e.message)}return d(1,"ERROR",null)}function ct(){const e=process.env["CREWX_ENDPOINT"]?.trim()||et;return re(e)}function H(e){try{return v.statSync(e).isDirectory()}catch{return false}}function se(e){try{return v.statSync(e).isFile()}catch{return false}}function Ie(e=process.cwd()){let n=E.resolve(e);while(true){if(H(E.join(n,"apps"))||se(E.join(n,"crewx.yaml"))){try{return v.realpathSync(n)}catch{return void 0}}const t=E.dirname(n);if(t===n)return void 0;n=t}}const Ae="manifest.json";const _e="ui-plugin.json";const ut="dev.crewx";function Oe(e){try{const n=JSON.parse(v.readFileSync(e,"utf8"));return u(n)?n:void 0}catch{return void 0}}function ie(e){const n=Oe(E.join(e,_e));const t=E.join(e,Ae);if(!se(t))return n;const r=Oe(t);if(!r)return void 0;const o=r["_meta"];const i=u(o)?o[ut]:void 0;const s={pluginId:r["name"],entry:u(i)?i["entry"]:void 0};if(n&&n["pluginId"]!==s["pluginId"])return void 0;return s}function be(e,n){const t=E.relative(e,n);return t!==""&&t!==".."&&!t.startsWith(`..${E.sep}`)&&!E.isAbsolute(t)}function pt(e,n){if(!n||n==="."||n===".."||/[\\/]/.test(n)){return d(2,"USAGE",null,"App must be a folder name or pluginId.")}const t=E.join(e,"apps");const r=E.join(t,n);let o=H(r)?r:void 0;if(!o&&H(t)){try{for(const s of v.readdirSync(t,{withFileTypes:true})){if(!s.isDirectory())continue;const a=E.join(t,s.name);const c=ie(a);if(c?.["pluginId"]===n){o=a;break}}}catch{}}let i=n;if(o){try{o=v.realpathSync(o)}catch{return d(6,"APP_INVALID",null)}if(!be(v.realpathSync(t),o)){return d(2,"USAGE",null,"App folder must be inside the workspace apps directory.")}const s=ie(o);if(typeof s?.["pluginId"]==="string"&&s["pluginId"].length>0){i=s["pluginId"]}}else{o=r}return{appId:i,appDirectory:o}}function ve(e){if(e.status<200||e.status>=300){return{ok:false,outcome:K(e)}}if(e.invalidJson||!u(e.data)||e.data["success"]!==true||!u(e.data["data"])){return{ok:false,outcome:d(1,x(e.data,"ERROR"),e.status)}}return{ok:true,value:e.data,status:e.status,rawText:e.rawText}}async function dt(e,n){try{const t=await q(e,n,"GET",He);if(t.status===404||t.status>=300&&t.status<400||t.invalidJson){return{ok:false,outcome:we(t.status)}}if(t.status<200||t.status>=300){return{ok:false,outcome:K(t)}}if(!u(t.data)||t.data["success"]!==true||!u(t.data["data"])||t.data["data"]["appDev"]!==1){return{ok:false,outcome:we(t.status)}}return{ok:true,value:t.data,status:t.status,rawText:t.rawText}}catch(t){return{ok:false,outcome:F(t,false)}}}async function ae(e){const n=ct();if(!n)return{ok:false,outcome:d(9,"ENDPOINT_REJECTED",null)};let t;try{t=(0,te.resolveCrewxApiCredential)("app-dev").token}catch(p){return{ok:false,outcome:Ee(p)}}const r=await dt(n,t);if(!r.ok)return{ok:false,outcome:r.outcome};const o=Ie();if(!o){return{ok:false,outcome:d(2,"WORKSPACE_NOT_FOUND",null,"No workspace root with apps/ or crewx.yaml was found.")}}const i=pt(o,e);if("exitCode"in i)return{ok:false,outcome:i};let s;try{s=await q(n,t,"POST",Xe,{workspacePath:o,appId:i.appId})}catch(p){return{ok:false,outcome:F(p,false)}}const a=ve(s);if(!a.ok)return{ok:false,outcome:a.outcome};const c=a.value["data"];if(!u(c)||typeof c["workspaceSlug"]!=="string"||typeof c["appId"]!=="string"||typeof c["devPath"]!=="string"||!c["devPath"].startsWith("/ws/")){return{ok:false,outcome:d(1,"ERROR",s.status)}}if(!H(i.appDirectory)){return{ok:false,outcome:d(6,"APP_INVALID",s.status)}}return{ok:true,context:{endpoint:n,token:t,workspacePath:o,appId:c["appId"],appDirectory:i.appDirectory,devPath:c["devPath"]}}}function X(e,...n){return`${Ke}${e.devPath}${n.length>0?`/${n.map(J).join("/")}`:""}`}async function z(e,n,t,r,o=N){try{const i=await q(e.endpoint,e.token,n,t,r,o);return ve(i)}catch(i){return{ok:false,outcome:F(i,false)}}}function lt(e){const n=e["appId"]??e["id"];if(typeof n==="string"&&n.length>0)return n;const t=e["definition"];if(u(t)&&typeof t["id"]==="string")return t["id"];return void 0}function ft(e){const n=e["definitionHash"];if(typeof n==="string"&&n.length>0)return n;const t=e["definition"];if(u(t)&&typeof t["hash"]==="string"){return t["hash"]}return void 0}function ht(e){if(!u(e))return void 0;const n=lt(e);const t=ft(e);if(!n||!t)return void 0;const r=e["agentActions"];const o=yt(e["agentAccess"]);return{appId:n,name:typeof e["name"]==="string"?e["name"]:"",description:typeof e["description"]==="string"?e["description"]:"",definitionHash:t,agentActions:Array.isArray(r)?r:[],...o===void 0?{}:{agentAccess:o}}}const mt=new Set(["allowed","approval_required","denied"]);function yt(e){if(!u(e))return void 0;const n=new Map;for(const[t,r]of Object.entries(e)){if(mt.has(r))n.set(t,r)}return n.size>0?n:void 0}async function Y(e,n=N){const t=await G(e,"GET",$(e,"ui-instances","apps"),void 0,n);if(!t.ok)return t;const r=t.value["apps"];if(!Array.isArray(r)){return{ok:false,outcome:d(1,"ERROR",t.status)}}const o=r.map(ht);if(o.some(i=>i===void 0)){return{ok:false,outcome:d(1,"ERROR",t.status)}}return{ok:true,value:o,status:t.status}}function ce(e,n){return e.find(t=>t.appId===n)}async function Re(e,n,t=N){const r=await G(e,"POST",$(e,"ui-instances","open"),{appId:n.appId,expectedDefinitionHash:n.definitionHash},t);if(!r.ok)return r;const o=u(r.value["instance"])?r.value["instance"]:r.value;if(!u(o)){return{ok:false,outcome:d(1,"ERROR",r.status)}}const i=o["instanceId"]??o["id"];const s=o["revision"];if(typeof i!=="string"||typeof s!=="number"){return{ok:false,outcome:d(1,"ERROR",r.status)}}const a=u(r.value["ai"])?r.value["ai"]:u(o["ai"])?o["ai"]:void 0;return{ok:true,status:r.status,value:{appId:n.appId,definitionHash:n.definitionHash,instanceId:i,revision:s,state:a&&Object.prototype.hasOwnProperty.call(a,"state")?a["state"]:null}}}const gt=new Set(["ready","unknown","hidden","expired","stale","ambiguous","unsupported"]);const wt=new Set(["native","activation-required","unsupported"]);const Et=new Set(["top-left","top-center","top-right","middle-left","middle-center","middle-right","bottom-left","bottom-center","bottom-right"]);const It=2*1024;const At=3*1024;function M(e,n,t=[]){const r=new Set([...n,...t]);return n.every(o=>Object.prototype.hasOwnProperty.call(e,o))&&Object.keys(e).every(o=>r.has(o))}function Te(e){return Number.isSafeInteger(e)&&e>=0}function Z(e){return Te(e)&&e<=1e5}function _t(e){const n=["schema","documentId","documentName","truncated","modelRevision","asOf","open","measured","counts","placement","regions"];if(!u(e)||!M(e,n,["unfinishedInput"]))return void 0;if(e["schema"]!=="html-viewer-feedback/v1"||typeof e["documentId"]!=="string"||!/^[A-Za-z0-9_-]{1,64}$/.test(e["documentId"])||e["documentName"]!==null&&(typeof e["documentName"]!=="string"||e["documentName"].length>256||/[\\/\u0000-\u001f\u007f]/.test(e["documentName"]))||typeof e["truncated"]!=="boolean"||typeof e["modelRevision"]!=="string"||!/^[A-Za-z0-9_-]{1,64}$/.test(e["modelRevision"])||typeof e["asOf"]!=="string"||e["asOf"].length>40||!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/.test(e["asOf"])||!Number.isFinite(Date.parse(e["asOf"]))||typeof e["open"]!=="boolean"||typeof e["measured"]!=="boolean"||Object.prototype.hasOwnProperty.call(e,"unfinishedInput")&&typeof e["unfinishedInput"]!=="boolean"){return void 0}const t=e["counts"];const r=e["placement"];if(!u(t)||!M(t,["drawings","notes","byKind","descriptionPresent"])||!u(t["byKind"])||!M(t["byKind"],["pen","highlighter","arrow","rectangle"])||!u(r)||!M(r,["visible","partial","offscreen","uncertain","total"])||!Array.isArray(e["regions"])||e["regions"].length>9){return void 0}const o=t["byKind"];const i=[t["drawings"],t["notes"],o["pen"],o["highlighter"],o["arrow"],o["rectangle"]];const s=[r["visible"],r["partial"],r["offscreen"],r["uncertain"],r["total"]];if(!i.every(Z)||typeof t["descriptionPresent"]!=="boolean"||!s.every(Z)){return void 0}const a=o["pen"]+o["highlighter"]+o["arrow"]+o["rectangle"];const c=r["visible"]+r["partial"]+r["offscreen"]+r["uncertain"];if(!Number.isSafeInteger(a)||t["drawings"]!==a||!Number.isSafeInteger(c)||r["total"]!==c||r["total"]!==t["drawings"]+t["notes"]){return void 0}const p=[];const l=new Set;for(const y of e["regions"]){if(!u(y)||!M(y,["region","drawings","notes"])||!Et.has(y["region"])||l.has(y["region"])||!Z(y["drawings"])||!Z(y["notes"])){return void 0}l.add(y["region"]);p.push({region:y["region"],drawings:y["drawings"],notes:y["notes"]})}const f={schema:"html-viewer-feedback/v1",documentId:e["documentId"],documentName:e["documentName"],truncated:e["truncated"],modelRevision:e["modelRevision"],asOf:e["asOf"],open:e["open"],measured:e["measured"],counts:{drawings:t["drawings"],notes:t["notes"],byKind:{pen:o["pen"],highlighter:o["highlighter"],arrow:o["arrow"],rectangle:o["rectangle"]},descriptionPresent:t["descriptionPresent"]},placement:{visible:r["visible"],partial:r["partial"],offscreen:r["offscreen"],uncertain:r["uncertain"],total:r["total"]},regions:p,...Object.prototype.hasOwnProperty.call(e,"unfinishedInput")?{unfinishedInput:e["unfinishedInput"]}:{}};return Buffer.byteLength(JSON.stringify(f),"utf8")<=It?f:void 0}function Ot(e){if(!u(e)||!M(e,["status"],["receivedAt","summary","capability"])||!gt.has(e["status"])||Object.prototype.hasOwnProperty.call(e,"receivedAt")&&!Te(e["receivedAt"])||Object.prototype.hasOwnProperty.call(e,"capability")&&!wt.has(e["capability"])){return void 0}const n=Object.prototype.hasOwnProperty.call(e,"summary")?_t(e["summary"]):void 0;if(Object.prototype.hasOwnProperty.call(e,"summary")&&n===void 0)return void 0;const t={status:e["status"],...Object.prototype.hasOwnProperty.call(e,"receivedAt")?{receivedAt:e["receivedAt"]}:{},...n===void 0?{}:{summary:n},...Object.prototype.hasOwnProperty.call(e,"capability")?{capability:e["capability"]}:{}};return Buffer.byteLength(JSON.stringify(t),"utf8")<=At?t:void 0}async function bt(e,n){const t=await G(e,"GET",$(e,"ui-instances",n));if(!t.ok)return t;const r=t.value;const o=r["instanceId"];const i=r["revision"];if(typeof o!=="string"||typeof i!=="number"){return{ok:false,outcome:d(1,"ERROR",t.status)}}const s=u(r["ai"])?r["ai"]:void 0;const a=p=>{if(Object.prototype.hasOwnProperty.call(r,p))return{[p]:r[p]};if(s&&Object.prototype.hasOwnProperty.call(s,p))return{[p]:s[p]};return{}};const c=Ot(r["feedback"]);return{ok:true,status:t.status,value:{instanceId:o,revision:i,state:s&&Object.prototype.hasOwnProperty.call(s,"state")?s["state"]:null,buttons:s&&Object.prototype.hasOwnProperty.call(s,"buttons")?s["buttons"]:null,...a("agentAccess"),...a("modelContext"),...a("pendingApproval"),...c===void 0?{}:{feedback:c}}}}const De="the pixels are not yet in model context; open this exact PNG with the provider's image-reading tool before describing a marked target.";async function Se(e,n,t,r){if(!Pe(n))return void 0;const o=u(t)?t:void 0;if(!o||!Object.prototype.hasOwnProperty.call(o,"crewxImage")){if(n["viewCall"]===true)throw new D.AppCaptureImageError("IMAGE_INVALID_DESCRIPTOR");return void 0}if(r.length===0){throw new D.AppCaptureImageError("IMAGE_INVALID_DESCRIPTOR")}const i=await(0,D.materializeAppCaptureImage)({cwd:process.cwd(),receiptId:r,descriptor:o["crewxImage"],download:s=>at(e,r,s)});return{result:{...o,crewxImage:i},image:i}}function Pe(e){return e["status"]==="settled"&&e["outcome"]==="succeeded"}function Ce(e){return Pe(e)&&e["viewCall"]===true}function Q(e,n,t,r){const o=t instanceof D.AppCaptureImageError?t:new D.AppCaptureImageError("IMAGE_WRITE_FAILED");const i=o.code==="IMAGE_EXPIRED"?["The App action receipt is preserved, but its image has expired on the server (IMAGE_EXPIRED).",`Reading this receipt again will not return the pixels. If you still need the image, refresh with crewx app state ${r??"<instanceId>"} and request a new capture with a NEW --request-id: keep the same viewRef from the task prompt and use documentId/modelRevision from feedback.summary.`,"If that bound view or a ready summary is unavailable, report that a new capture cannot currently be made; do not substitute another view.","The pixels are not in model context; do not describe them from metadata or text."].join("\n"):[`The App action receipt is preserved, but its image was not delivered (${o.code}).`,`Recover the same result with crewx app receipt ${n}; do not rerun the action.`,"The pixels are not in model context. Do not describe them from metadata or text. If the image is still retrievable, open the returned local PNG with the provider's image-reading tool before describing a marked target."].join("\n");return{exitCode:4,payload:{...e,ok:false,exit:4,error:{code:o.code,status:o.status},receiptId:n},stderr:i}}async function vt(e,n,t,r,o,i=N){const s=await G(e,"POST",$(e,"ui-instances",n,"actions"),{actionId:t,requestId:r,input:o},i);if(!s.ok)return s;const a=s.value["receipt"];if(!u(a)){return{ok:false,outcome:d(1,"ERROR",s.status)}}const c=a["receiptId"]??a["id"];const p=s.value["approval"];if(s.status===202&&u(p)){const h=p["approvalId"];const w=p["expiresAt"];const A=typeof a["requestId"]==="string"?a["requestId"]:typeof p["requestId"]==="string"?p["requestId"]:r;if(typeof c!=="string"||typeof h!=="string"||typeof w!=="string"&&typeof w!=="number"){return{ok:false,outcome:d(1,"ERROR",s.status)}}return{ok:false,outcome:{exitCode:8,payload:{ok:false,exit:8,error:{code:"APPROVAL_PENDING",status:202},receiptId:c,requestId:A,approvalId:h,expiresAt:w}}}}const l=a["status"]??(s.status===202?"requested":"settled");const f=s.value["resolve"];const y=u(f)&&u(f["ai"])?f["ai"]:void 0;return{ok:true,status:s.status,value:{receiptId:typeof c==="string"?c:"",requestId:typeof a["requestId"]==="string"?a["requestId"]:r,instanceId:n,actionId:typeof a["actionId"]==="string"?a["actionId"]:t,status:l,outcome:Object.prototype.hasOwnProperty.call(a,"outcome")?a["outcome"]:null,...typeof a["errorCode"]==="string"?{errorCode:a["errorCode"]}:{},state:y&&Object.prototype.hasOwnProperty.call(y,"state")?y["state"]:null,...Object.prototype.hasOwnProperty.call(s.value,"result")?{result:s.value["result"]}:{},...Object.prototype.hasOwnProperty.call(s.value,"resultExpired")?{resultExpired:s.value["resultExpired"]}:{},receipt:a}}}function Rt(e){const n=e.payload["error"];return e.exitCode===14||u(n)&&n["status"]===429}async function ke(e,n){const t=await G(e,"GET",$(e,"ui-action-receipts",n));if(!t.ok){return{state:"failed",retryable:Rt(t.outcome),value:t.outcome}}const r=t.value["receipt"];if(!u(r))return{state:"failed",retryable:false,value:d(1,"ERROR",t.status)};const o={ok:true,receipt:r,outcome:Object.prototype.hasOwnProperty.call(r,"outcome")?r["outcome"]:null,...typeof r["errorCode"]==="string"?{errorCode:r["errorCode"]}:{},...Object.prototype.hasOwnProperty.call(t.value,"result")?{result:t.value["result"]}:{},...Object.prototype.hasOwnProperty.call(t.value,"resultExpired")?{resultExpired:t.value["resultExpired"]}:{}};if(t.value["resultExpired"]===true&&Ce(r)){return{state:"failed",retryable:false,value:Q(o,n,new D.AppCaptureImageError("IMAGE_EXPIRED"))}}if(t.value["resultExpired"]!==true){try{const i=await Se(e,r,t.value["result"],n);if(i){return{state:"settled",value:{...P({...o,result:i.result,image:i.image}),stderr:De}}}}catch(i){return{state:"failed",retryable:false,value:Q(o,n,i)}}}return{state:r["status"]==="requested"?"pending":"settled",value:P(o)}}function Tt(e,n){return{exitCode:124,payload:{ok:false,exit:124,error:{code:"WAIT_TIMEOUT",status:null},receiptId:e},suppressOutput:true,stderr:`\u2192 Receipt ${e} is still pending after ${n}s. Do not end your turn: run crewx app receipt ${e} --wait=${(0,j.nextWaitSeconds)(n)} again.`}}function Ne(){const e=process.env["CREWX_TASK_TICKET"]??"";const n=process.env["CREWX_WORKSPACE_SLUG"]??"";const t=process.env["CREWX_THREAD_ID"]??"";if(!e.startsWith("crewxtk_")||!n||!t){return d(3,"NO_TICKET",null)}const r=re(process.env["CREWX_ENDPOINT"]);if(!r)return d(9,"ENDPOINT_REJECTED",null);return{ticket:e,workspaceSlug:n,threadId:t,endpoint:r,basePath:ot(n,t)}}function xe(e){return"exitCode"in e}function je(e){return String(e).padStart(2,"0")}function Dt(e){const n=typeof e==="number"?new Date(e):typeof e==="string"?new Date(/^\d+$/.test(e)?Number(e):e):void 0;if(!n||Number.isNaN(n.getTime()))return void 0;return`${je(n.getHours())}:${je(n.getMinutes())}`}function St(e){if(e.exitCode!==8)return e;const n=Dt(e.payload["expiresAt"]);const t=e.payload["receiptId"];const r=[`\u2192 Waiting for the user's approval in the App window${n?` (expires ${n})`:""}. Do not end your turn: ending it cancels the request.`,...typeof t==="string"&&t.length>0?[`\u2192 Wait: crewx app receipt ${t} --wait=${j.RECEIPT_WAIT_DEFAULT_SECONDS} (repeat on exit 124 until it settles)`]:[],"\u2192 Report success only if it settles as succeeded."];return{...e,stderr:[e.stderr,...r].filter(Boolean).join("\n")}}const Pt=256*1024;function Ct(e){try{const n=v.realpathSync(e);const t=v.realpathSync(E.join(n,"SKILL.md"));if(!be(n,t)||!se(t))return void 0;const r=v.openSync(t,"r");try{const o=new Uint8Array(Pt);const i=v.readSync(r,o,0,o.length,0);return(0,L.manualBody)(Buffer.from(o.buffer,0,i).toString("utf8"))}finally{v.closeSync(r)}}catch{return void 0}}async function kt(e){const n=Ie();if(!n)return void 0;try{const t=await Promise.resolve().then(()=>U(require("@crewx/sdk")));const r=await t.discoverWorkspaceApps(n);const o=r.apps.find(i=>i.appId===e)??r.apps.find(i=>i.pluginId===e);if(!o)return void 0;return{candidate:o,directory:E.join(n,o.relativePath),resolveAccess:t.resolveAgentAccess}}catch{return void 0}}async function Nt(e){if(e.length!==2||e[1].startsWith("--"))return B();const n=e[1];const t=Ne();if(xe(t))return t;const r=await Y(t);if(!r.ok)return r.outcome;const o=ce(r.value,n);if(!o)return d(6,"MCP_APP_NOT_FOUND",null);const i=o.agentActions.filter(c=>typeof c==="string");const s=await kt(n);const a=s?(0,L.renderUsage)(Ct(s.directory),(0,L.renderActionsSection)(n,s.candidate,(0,L.buildActionRows)(s.candidate,new Set(i),c=>s.resolveAccess(c),o.agentAccess)),s.candidate.manifest.csp?.connectDomains):(0,L.renderUsage)(void 0,(0,L.renderServerOnlyActions)(n,i));return{exitCode:0,payload:{ok:true},stdout:a}}function Le(e,n){if(e[0]!==n)return null;const t=[];let r=false;for(const o of e.slice(1)){if(o===(n==="dev"?"--no-open":"--json")){if(r)return null;r=true}else if(o.startsWith("--")){return null}else{t.push(o)}}if(t.length!==1)return null;return{appSelector:t[0],flag:r}}function $e(e){return`${JSON.stringify(e.payload)}
|
|
2
|
+
`}function xt(e){const n=e["detail"];if(e["rule"]!=="manifest"||!u(n))return"";const t=typeof n["path"]==="string"?n["path"]:"";const r=typeof n["rule"]==="string"?n["rule"]:"";if(!r)return"";return` ${t?`${t} `:""}(${r})`}async function jt(e){const n=Le(e,"check");if(!n)return B();const t=await ae(n.appSelector);if(!t.ok)return t.outcome;const r=await z(t.context,"GET",X(t.context,"check"));if(!r.ok)return r.outcome;const o=r.value["data"];if(!u(o))return d(1,"ERROR",r.status);const i=u(o["summary"])?o["summary"]:{};const s=Array.isArray(o["items"])?o["items"]:[];const a=typeof i["errors"]==="number"?i["errors"]:s.filter(f=>u(f)&&f["severity"]==="error").length;const c=typeof i["warnings"]==="number"?i["warnings"]:s.filter(f=>u(f)&&f["severity"]==="warning").length;const p=a===0?0:Ye;if(n.flag){return{exitCode:p,payload:r.value,stdout:`${r.rawText??$e({exitCode:p,payload:r.value})}`+(r.rawText?.endsWith("\n")?"":"\n"),suppressOutput:true}}const l=s.flatMap(f=>{if(!u(f))return[];const y=f["severity"];const h=y==="error"?"\u2715":y==="ok"?"\u2713":"!";const w=typeof f["where"]==="string"?f["where"]:"";const A=typeof f["code"]==="string"?f["code"]:"";return[`${h} ${w} ${A}${xt(f)}`]});l.push(`Summary: ${a} errors, ${c} warnings`);return{exitCode:p,payload:{},stdout:`${l.join("\n")}
|
|
3
|
+
`,suppressOutput:true}}async function Lt(e){let n;try{n=(0,te.resolveCrewxApiCredential)("pairing").token}catch(r){return Ee(r)}let t;try{t=await q(e.endpoint,n,"POST","/api/v1/auth/local/code")}catch(r){return F(r,false)}if(t.status<200||t.status>=300){return K(t)}if(t.invalidJson||!u(t.data)||typeof t.data["url"]!=="string"){return d(1,"PAIRING_CODE_INVALID",t.status)}try{const r=new he.URL(t.data["url"]);const o=re(r.origin);const i=r.hash.match(/^#pair=(.+)$/);if(!o||!i)return d(1,"PAIRING_CODE_INVALID",t.status);return`${o.origin}${e.devPath}#pair=${encodeURIComponent(i[1])}`}catch{return d(1,"PAIRING_CODE_INVALID",t.status)}}async function $t(e){const n=process.platform==="darwin"?"open":process.platform==="win32"?"explorer.exe":"xdg-open";const t=(0,de.spawn)(n,[e],{detached:true,stdio:"ignore"});await new Promise((r,o)=>{t.once("error",o);t.once("spawn",()=>{t.unref();r()})})}async function Mt(e){const n=ge(["run",...e.slice(2)]);if(!n||n.command!=="run")return B();const[t,r]=n.positional;const o=await ae(t);if(!o.ok)return o.outcome;const i=await z(o.context,"GET",X(o.context,"state"));if(!i.ok)return i.outcome;const s=i.value["data"];if(!u(s)||typeof s["revision"]!=="number"){return d(1,"APP_DEV_RESPONSE_INVALID",i.status)}const a=n.requestId??`cxa_${(0,pe.randomBytes)(16).toString("base64url")}`;const c=await z(o.context,"POST",X(o.context,"actions"),{actionId:r,requestId:a,input:n.input,expectedRevision:s["revision"]},Je);if(!c.ok)return c.outcome;const p=c.value["data"];const l=u(p)&&u(p["receipt"])?p["receipt"]:void 0;return{exitCode:l?.["outcome"]==="succeeded"?0:1,payload:c.value,stdout:`${c.rawText??$e({exitCode:0,payload:c.value})}`+(c.rawText?.endsWith("\n")?"":"\n"),suppressOutput:true}}function Ut(e){try{const n=JSON.parse(v.readFileSync(E.join(e,"package.json"),"utf8"));if(!u(n)||!u(n["scripts"]))return false;const t=n["scripts"]["build"];return typeof t==="string"&&t.trim().length>0}catch{return false}}function Me(e){const n=new Set([E.normalize(Ae),E.normalize(_e)]);const t=ie(e);const r=t?.["entry"];if(typeof r==="string"&&r.length>0&&!E.isAbsolute(r)){const o=E.normalize(r);if(o!==".."&&!o.startsWith(`..${E.sep}`))n.add(o)}return n}function Bt(e,n){try{const t=v.statSync(E.join(e,n));return`${t.size}:${t.mtimeMs}:${t.ino}`}catch{return null}}function Ue(e,n){return new Map([...n].map(t=>[t,Bt(e,t)]))}function Ft(e,n){const t=Date.now();let r="";let o=false;const i=s=>{const a=Buffer.from(r+(Buffer.isBuffer(s)?s.toString("utf8"):s),"utf8");r=a.length>me?a.subarray(a.length-me).toString("utf8"):a.toString("utf8")};return new Promise(s=>{const a=(0,de.spawn)("npm",["run","build"],{cwd:e,env:process.env,stdio:["ignore","pipe","pipe"]});n(a);a.stdout?.on("data",i);a.stderr?.on("data",i);const c=p=>{if(o)return;o=true;n(void 0);s({ok:p,ms:Math.max(0,Date.now()-t),output:r})};a.once("error",p=>{i(`${p.message}
|
|
4
|
+
`);c(false)});a.once("close",p=>c(p===0))})}function qt(e){const n=e.payload["error"];return u(n)&&typeof n["code"]==="string"?n["code"]:"ERROR"}async function Gt(e,n){const t=await z(e,"POST",X(e,"reload"),n);if(!t.ok)return{errorCode:qt(t.outcome)};const r=t.value["data"];return u(r)&&typeof r["generation"]==="number"?{generation:r["generation"]}:{errorCode:"APP_DEV_RESPONSE_INVALID"}}async function Wt(e,n){const t=process.ppid;let r;try{r=v.watch(e.appDirectory,{recursive:true},(I,O)=>{R(O)})}catch(I){return d(1,"WATCH_FAILED",null,I instanceof Error?I.message:"Unable to watch the App folder.")}let o=false;let i=false;let s=false;let a=false;let c="";let p="";let l;let f;let y;let h={exitCode:0,payload:{},suppressOutput:true};const w=new Map;let A;const g=new Promise(I=>{A=I});const m=I=>{process.stdout.write(`${I}
|
|
5
|
+
`)};const _=()=>{if(o)return;o=true;if(l)clearTimeout(l);if(f)clearInterval(f);process.removeListener("SIGINT",b);process.removeListener("SIGTERM",b);A(h)};const b=()=>{if(i)return;i=true;r.close();if(l)clearTimeout(l);if(y&&y.exitCode===null)y.kill("SIGTERM");if(!s)_()};const R=I=>{if(i||o)return;const O=I===null?"":E.normalize(I.toString());const C=O.split(E.sep);if(C.some(k=>k==="node_modules"||k===".crewx"||k==="dist"))return;const ee=n?Me(e.appDirectory):new Set;if(y&&ee.has(O))return;const S=w.get(O);if(S!==void 0){if(Date.now()<=S)return;w.delete(O)}if(s){a=true;c=O||"App files";return}p=O||"App files";if(l)clearTimeout(l);l=setTimeout(()=>{l=void 0;const k=p||"App files";p="";void T(k,false)},200)};const W=async I=>{const O=await Gt(e,I);return O.generation===void 0?`failed ${O.errorCode??"ERROR"}`:String(O.generation)};const T=async(I,O)=>{if(i||o)return;s=true;if(n){const C=Me(e.appDirectory);const ee=Ue(e.appDirectory,C);const S=await Ft(e.appDirectory,V=>{y=V});const k=Ue(e.appDirectory,C);for(const[V,Ge]of k){if(ee.get(V)!==Ge){w.set(V,Date.now()+1e3)}}if(i){s=false;_();return}const Fe={reason:"build",build:{ok:S.ok,ms:S.ms,...!S.ok?{output:S.output}:{}}};const qe=await W(Fe);m(`${O?"initial":I} -> build ${S.ok?"ok":"failed"} ${S.ms}ms -> reload ${qe}`)}else if(!O){const C=await W({reason:"manual"});m(`${I} -> reload ${C}`)}s=false;if(i){_();return}if(a){const C=c||"App files";a=false;c="";void T(C,false);return}if(i)_()};r.once("error",I=>{h=d(1,"WATCH_FAILED",null,I.message);b()});process.once("SIGINT",b);process.once("SIGTERM",b);f=setInterval(()=>{if(process.ppid!==t)b()},1e3);f.unref();if(n)void T("initial",true);return g}async function Vt(e){const n=Le(e,"dev");if(!n)return B();const t=await ae(n.appSelector);if(!t.ok)return t.outcome;const r=`${t.context.endpoint.origin}${t.context.devPath}`;process.stdout.write(`${r}
|
|
6
|
+
`);if(!n.flag){const o=await Lt(t.context);if(typeof o!=="string")return o;try{await $t(o)}catch(i){return d(1,"BROWSER_OPEN_FAILED",null,i instanceof Error?i.message:"Could not open the App dev tab.")}}return Wt(t.context,Ut(t.context.appDirectory))}const Be="usage: crewx app build [<appDir>] [--check | --watch]";const Jt=["crewx app <command>",""," list List Apps available to this task"," open <appId> Open an App in this conversation"," state <instanceId> Read AI-visible state, buttons, and agent access"," run <instanceId|appId> <actionId> [--input <JSON>|-] [--request-id <id>] Run one available action"," receipt <receiptId> [--wait=<seconds>] Read a safe action receipt"," build [<appDir>] [--check | --watch] Build a @Tool App: server.ts -> dist/ and the actions (manifest.json, or dist/tools.json)"," dev <app> [--no-open] Start a foreground App dev session"," check <app> [--json] Run the App dev checks"," run --dev <app> <actionId> [--input <JSON>|-] Run an App action through the dev checks"," usage [<appId>] Show the guide, or one App's manual and action table",""].join("\n");const Kt=[Be,""," Builds <appDir> (default: the current directory) from its server.ts: dist/server.mjs, dist/entry.mjs,",' and the actions of manifest.json. With no "bin" and no crewx.app.command in package.json the App is'," server.ts only (tool mode): dist/server.mjs, dist/main.mjs and dist/tools.json, and manifest.json"," carries no actions. --check writes nothing and exits 1 when a generated file is stale."," --watch rebuilds on every change until Ctrl-C.",""].join("\n");function Ht(){const e=[E.join(__dirname,"..","..","build-app","load.cjs"),E.join(__dirname,"..","build-app","load.cjs")];const n=e.find(t=>v.existsSync(t));if(!n)return Promise.reject(new Error("the App build is not part of this install"));return require(n).loadAppBuilder()}function ue(e){return e instanceof Error?e.message:String(e)}async function Xt(e){if(e.slice(1).some(s=>s==="--help"||s==="-h")){return{exitCode:0,payload:{ok:true},stdout:Kt}}let n;try{n=await Ht()}catch(s){return d(1,"BUILD_UNAVAILABLE",null,ue(s))}let t;try{t=n.parseArguments(e.slice(1))}catch(s){return d(2,"USAGE",null,`${ue(s)} \u2192 ${Be}`)}const{appDirectory:r}=t;const o=E.relative(process.cwd(),r);const i=o===""?"crewx app build":`crewx app build ${o}`;if(t.watch){const s=new AbortController;const a=()=>s.abort();process.once("SIGINT",a);process.once("SIGTERM",a);try{await n.watchApp(r,c=>process.stdout.write(`${c}
|
|
7
|
+
`),{signal:s.signal})}finally{process.removeListener("SIGINT",a);process.removeListener("SIGTERM",a)}return{exitCode:0,payload:{},suppressOutput:true}}try{const s=await n.buildApp({appDirectory:r,check:t.check});if(t.check&&s.staleFiles.length>0){return{...d(1,"BUILD_STALE",null,`Stale generated files: ${s.staleFiles.join(", ")} \u2192 Fix: ${i}`),payload:{ok:false,exit:1,error:{code:"BUILD_STALE",status:null},appDir:r,stale:s.staleFiles}}}if(t.check){return P({ok:true,appDir:r,check:true,inSync:true,files:s.files,actions:s.actions})}const a=E.basename(r);const c=E.basename(E.dirname(r))==="apps";return{...P({ok:true,appDir:r,files:s.files,actions:s.actions}),stderr:c?`\u2192 Next: crewx app check ${a}`:`\u2192 Next: put ${a} in the workspace apps/ folder, then crewx app check ${a}`}}catch(s){return d(1,"BUILD_FAILED",null,ue(s))}}async function zt(e,{clock:n=j.realClock}={}){if(e.length===1&&(e[0]==="--help"||e[0]==="-h")){return{exitCode:0,payload:{ok:true},stdout:Jt}}if(e[0]==="build")return Xt(e);if(e[0]==="dev")return Vt(e);if(e[0]==="check")return jt(e);if(e[0]==="run"&&e[1]==="--dev")return Mt(e);if(e[0]==="usage")return Nt(e);const t=ge(e);if(!t)return B();const r=Ne();if(xe(r))return r;if(t.command==="list"){const h=await Y(r);if(!h.ok)return h.outcome;return P({ok:true,apps:h.value})}if(t.command==="receipt"){const h=t.positional[0];const w=(0,j.clampWaitSeconds)(t.waitSeconds??0);if(w===0)return(await ke(r,h)).value;const A=await(0,j.pollUntilSettled)(()=>ke(r,h),w,n);if(A.state!=="pending")return A.value;return Tt(h,w)}if(t.command==="state"){const h=await bt(r,t.positional[0]);if(!h.ok)return h.outcome;return P({ok:true,...h.value})}if(t.command==="open"){const h=t.positional[0];const w=await Y(r);if(!w.ok)return w.outcome;const A=ce(w.value,h);if(!A)return d(6,"MCP_APP_NOT_FOUND",null);const g=await Re(r,A);if(!g.ok)return g.outcome;return P({ok:true,...g.value})}const[o,i]=t.positional;const s=t.requestId??`cxa_${(0,pe.randomBytes)(16).toString("base64url")}`;let a=o;if(!o.startsWith(ye)){const h=await Y(r,ne);if(!h.ok)return oe(h.outcome,s);const w=ce(h.value,o);if(!w)return d(6,"MCP_APP_NOT_FOUND",null);const A=await Re(r,w,ne);if(!A.ok)return oe(A.outcome,s);a=A.value.instanceId}const c=await vt(r,a,i,s,t.input,ne);if(!c.ok)return St(oe(c.outcome,s));const{receipt:p,...l}=c.value;const f={ok:true,...l};const y=typeof l["receiptId"]==="string"?l["receiptId"]:"";if(l["resultExpired"]===true&&u(p)&&Ce(p)){return Q({...f,receipt:p},y,new D.AppCaptureImageError("IMAGE_EXPIRED"),a)}if(u(p)&&l["resultExpired"]!==true){try{const h=await Se(r,p,l["result"],y);if(h){return{...P({...f,receipt:p,result:h.result,image:h.image}),stderr:`\u2192 Check: crewx app state ${c.value.instanceId}
|
|
8
|
+
${De}`}}}catch(h){return Q({...f,receipt:p},y,h,a)}}return{...P(f),stderr:`\u2192 Check: crewx app state ${c.value.instanceId}`}}
|