@crewx/app 0.1.0-rc.27 → 0.1.0-rc.29

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.
@@ -1,1536 +1,7 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
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 receipt_wait_js_1 = require("./receipt-wait.js");
46
- const usage_js_1 = require("./usage.js");
47
- const REQUEST_TIMEOUT_MS = 15000;
48
- const RUN_REQUEST_TIMEOUT_MS = 30000;
49
- const DEV_ACTION_TIMEOUT_MS = 60000;
50
- const MAX_BUILD_OUTPUT_BYTES = 64 * 1024;
51
- const APP_ID_PREFIX = 'uic_';
52
- const APP_DEV_API_PREFIX = '/api/v1';
53
- const APP_DEV_CAPABILITIES_PATH = '/api/v1/app-dev/capabilities';
54
- const APP_DEV_SESSIONS_PATH = '/api/v1/app-dev/sessions';
55
- const APP_DEV_TOO_OLD_EXIT = 12;
56
- const APP_DEV_CHECK_FAILED_EXIT = 13;
57
- const REQUEST_ID_PATTERN = /^[A-Za-z0-9_-]{8,128}$/;
58
- const LOOPBACK_HOSTS = new Set(['127.0.0.1', '[::1]', 'localhost']);
59
- const DEFAULT_APP_DEV_ENDPOINT = 'http://localhost:8150';
60
- 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.';
61
- const UNAUTHORIZED_MESSAGE = 'Do not retry. Report the failure.';
62
- const REQUEST_TIMED_OUT_MESSAGE = 'The server did not answer in time. Retry once; if it happens again, report it.';
63
- function isRecord(value) {
64
- return value !== null && typeof value === 'object' && !Array.isArray(value);
65
- }
66
- function failure(exitCode, code, status, message) {
67
- let stderr = message;
68
- if (!stderr && (exitCode === 3 || exitCode === 9 || exitCode === 10 || exitCode === 11)) {
69
- stderr = BLOCKED_ROUTE_MESSAGE;
70
- }
71
- else if (!stderr && exitCode === 4) {
72
- stderr = UNAUTHORIZED_MESSAGE;
73
- }
74
- return {
75
- exitCode,
76
- payload: { ok: false, exit: exitCode, error: { code, status } },
77
- ...(stderr ? { stderr } : {}),
78
- };
79
- }
80
- function success(payload) {
81
- return { exitCode: 0, payload };
82
- }
83
- function usageFailure() {
84
- return failure(2, 'USAGE', null);
85
- }
86
- function parseCommand(args) {
87
- const command = args[0];
88
- if (command !== 'list' && command !== 'open' && command !== 'state' && command !== 'run' && command !== 'receipt') {
89
- return null;
90
- }
91
- const positional = [];
92
- const options = new Map();
93
- for (let index = 1; index < args.length; index += 1) {
94
- const arg = args[index];
95
- if (!arg.startsWith('--')) {
96
- positional.push(arg);
97
- continue;
98
- }
99
- const separator = arg.indexOf('=');
100
- const key = separator >= 0 ? arg.slice(2, separator) : arg.slice(2);
101
- const inlineValue = separator >= 0 ? arg.slice(separator + 1) : undefined;
102
- if ((key !== 'input' && key !== 'request-id' && key !== 'wait') || options.has(key))
103
- return null;
104
- const value = inlineValue ?? args[index + 1];
105
- if (value === undefined)
106
- return null;
107
- if (inlineValue === undefined)
108
- index += 1;
109
- options.set(key, value);
110
- }
111
- if ((command === 'list' && positional.length !== 0)
112
- || (command === 'open' && positional.length !== 1)
113
- || (command === 'state' && (positional.length !== 1 || !positional[0].startsWith(APP_ID_PREFIX)))
114
- || (command === 'run' && positional.length !== 2)
115
- || (command === 'receipt' && positional.length !== 1)) {
116
- return null;
117
- }
118
- if (command !== 'run' && (options.has('input') || options.has('request-id')))
119
- return null;
120
- if (command !== 'receipt' && options.has('wait'))
121
- return null;
122
- let input = {};
123
- if (options.has('input')) {
124
- const raw = options.get('input');
125
- try {
126
- const parsed = JSON.parse(raw === '-' ? fs.readFileSync(0, 'utf8') : raw);
127
- if (!isRecord(parsed))
128
- return null;
129
- input = parsed;
130
- }
131
- catch {
132
- return null;
133
- }
134
- }
135
- const requestId = options.get('request-id');
136
- if (requestId !== undefined && !REQUEST_ID_PATTERN.test(requestId))
137
- return null;
138
- const waitSeconds = options.has('wait') ? (0, receipt_wait_js_1.parseWaitSeconds)(options.get('wait')) : undefined;
139
- if (waitSeconds === null)
140
- return null;
141
- return {
142
- command,
143
- positional,
144
- input,
145
- ...(requestId !== undefined ? { requestId } : {}),
146
- ...(waitSeconds !== undefined ? { waitSeconds } : {}),
147
- };
148
- }
149
- function getEndpoint(raw) {
150
- if (!raw)
151
- return null;
152
- let endpoint;
153
- try {
154
- endpoint = new node_url_1.URL(raw);
155
- }
156
- catch {
157
- return null;
158
- }
159
- if (!LOOPBACK_HOSTS.has(endpoint.hostname)
160
- || (endpoint.protocol !== 'http:' && endpoint.protocol !== 'https:')
161
- || endpoint.username !== ''
162
- || endpoint.password !== ''
163
- || endpoint.search !== ''
164
- || endpoint.hash !== ''
165
- || (endpoint.pathname !== '' && endpoint.pathname !== '/')) {
166
- return null;
167
- }
168
- return endpoint;
169
- }
170
- function segment(value) {
171
- return encodeURIComponent(value);
172
- }
173
- function basePath(workspaceSlug, threadId) {
174
- return [
175
- 'api', 'v1', 'ws', segment(workspaceSlug), 'threads', segment(threadId),
176
- ].join('/');
177
- }
178
- function pathFor(context, ...segments) {
179
- return `/${context.basePath}/${segments.map(segment).join('/')}`;
180
- }
181
- function responseErrorCode(data, fallback) {
182
- if (!isRecord(data) || !isRecord(data['error']))
183
- return fallback;
184
- const code = data['error']['code'];
185
- return typeof code === 'string' && code.length > 0 ? code : fallback;
186
- }
187
- function classifyHttpFailure(response) {
188
- const { status, data } = response;
189
- if (status >= 300 && status < 400)
190
- return failure(9, 'ENDPOINT_REJECTED', status);
191
- if (status === 401)
192
- return failure(4, responseErrorCode(data, 'UNAUTHORIZED'), status);
193
- if (status === 403)
194
- return failure(5, responseErrorCode(data, 'FORBIDDEN'), status);
195
- if (status === 404)
196
- return failure(6, responseErrorCode(data, 'NOT_FOUND'), status);
197
- if (status === 409) {
198
- const code = responseErrorCode(data, 'CONFLICT');
199
- if (code === 'APPROVAL_ALREADY_PENDING') {
200
- const error = isRecord(data) && isRecord(data['error']) ? data['error'] : undefined;
201
- const details = error && isRecord(error['details']) ? error['details'] : undefined;
202
- const receiptId = error?.['receiptId'] ?? details?.['receiptId']
203
- ?? (isRecord(data) ? data['receiptId'] : undefined);
204
- return {
205
- exitCode: 8,
206
- payload: {
207
- ok: false,
208
- exit: 8,
209
- error: { code, status },
210
- ...(typeof receiptId === 'string' ? { receiptId } : {}),
211
- },
212
- };
213
- }
214
- return failure(7, code, status);
215
- }
216
- return failure(1, responseErrorCode(data, 'ERROR'), status);
217
- }
218
- function networkFailure(error, classifyTimeout = true) {
219
- const errorCode = isRecord(error) && typeof error['code'] === 'string'
220
- ? error['code']
221
- : '';
222
- const codexBlocked = process.env['CODEX_SANDBOX_NETWORK_DISABLED'] === '1';
223
- const errnoBlocked = !codexBlocked && (errorCode === 'EPERM' || errorCode === 'EACCES');
224
- if (codexBlocked || errnoBlocked)
225
- return failure(11, 'SANDBOX_BLOCKED', null);
226
- if (classifyTimeout && errorCode === 'ETIMEDOUT'
227
- && isRecord(error) && error['connectionEstablished'] === true) {
228
- return failure(14, 'TIMED_OUT', null, REQUEST_TIMED_OUT_MESSAGE);
229
- }
230
- return failure(10, 'SERVER_UNREACHABLE', null);
231
- }
232
- function withRunTimeoutGuidance(outcome, requestId) {
233
- if (outcome.exitCode !== 14)
234
- return outcome;
235
- return {
236
- ...outcome,
237
- payload: { ...outcome.payload, requestId },
238
- stderr: [
239
- 'The request timed out and its outcome is unknown.',
240
- `Retry the same command once with --request-id ${requestId}; the server will return the same request's receipt without running the action twice.`,
241
- `If it times out again, report "server response delayed, outcome unknown" with requestId ${requestId}.`,
242
- 'Do not report success or failure, click the screen, or use another route.',
243
- ].join('\n'),
244
- };
245
- }
246
- function requestJson(endpoint, authorizationToken, method, requestPath, body, timeoutMs = REQUEST_TIMEOUT_MS) {
247
- const requestFunction = endpoint.protocol === 'https:' ? https.request : http.request;
248
- const hostname = endpoint.hostname === '[::1]'
249
- ? '::1'
250
- : endpoint.hostname;
251
- const port = endpoint.port ? Number(endpoint.port) : undefined;
252
- const bodyText = body === undefined ? undefined : JSON.stringify(body);
253
- const request = requestFunction({
254
- protocol: endpoint.protocol,
255
- hostname,
256
- ...(port !== undefined ? { port } : {}),
257
- method,
258
- path: requestPath,
259
- headers: {
260
- Authorization: `Bearer ${authorizationToken}`,
261
- Accept: 'application/json',
262
- ...(bodyText !== undefined ? { 'Content-Type': 'application/json' } : {}),
263
- },
264
- }, (response) => {
265
- const status = response.statusCode ?? 0;
266
- if (status >= 300 && status < 400) {
267
- response.destroy();
268
- clearTimeout(timeout);
269
- resolveResponse({ status, data: undefined, invalidJson: false, rawText: '' });
270
- return;
271
- }
272
- const chunks = [];
273
- response.setEncoding('utf8');
274
- response.on('data', (chunk) => chunks.push(chunk));
275
- response.once('error', (error) => failResponse(error));
276
- response.once('end', () => {
277
- clearTimeout(timeout);
278
- const raw = chunks.join('');
279
- try {
280
- resolveResponse({ status, data: JSON.parse(raw), invalidJson: false, rawText: raw });
281
- }
282
- catch {
283
- resolveResponse({ status, data: undefined, invalidJson: true, rawText: raw });
284
- }
285
- });
286
- });
287
- let socketError;
288
- let connectionEstablished = false;
289
- let resolveResponse;
290
- let failResponse;
291
- const result = new Promise((resolve, reject) => {
292
- resolveResponse = resolve;
293
- failResponse = reject;
294
- });
295
- const timeout = setTimeout(() => {
296
- const error = new Error('Request timed out');
297
- error.code = 'ETIMEDOUT';
298
- error.connectionEstablished = connectionEstablished;
299
- request.destroy(error);
300
- }, timeoutMs);
301
- request.on('socket', (socket) => {
302
- const markConnected = () => {
303
- connectionEstablished = true;
304
- };
305
- if (endpoint.protocol === 'https:') {
306
- const tlsSocket = socket;
307
- if (tlsSocket.authorized)
308
- markConnected();
309
- else
310
- tlsSocket.once('secureConnect', markConnected);
311
- }
312
- else {
313
- if (socket.connecting)
314
- socket.once('connect', markConnected);
315
- else
316
- markConnected();
317
- }
318
- socket.once('error', (error) => {
319
- socketError = error;
320
- });
321
- });
322
- request.once('error', (error) => {
323
- clearTimeout(timeout);
324
- failResponse(socketError ?? error);
325
- });
326
- if (bodyText !== undefined)
327
- request.write(bodyText);
328
- request.end();
329
- return result;
330
- }
331
- async function performRequest(context, method, requestPath, body, timeoutMs = REQUEST_TIMEOUT_MS) {
332
- try {
333
- const response = await requestJson(context.endpoint, context.ticket, method, requestPath, body, timeoutMs);
334
- if (response.status < 200 || response.status >= 300) {
335
- return { ok: false, outcome: classifyHttpFailure(response) };
336
- }
337
- if (response.invalidJson || !isRecord(response.data) || response.data['success'] !== true) {
338
- return {
339
- ok: false,
340
- outcome: failure(1, responseErrorCode(response.data, 'ERROR'), response.status),
341
- };
342
- }
343
- const data = response.data['data'];
344
- if (!isRecord(data)) {
345
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
346
- }
347
- return { ok: true, value: data, status: response.status };
348
- }
349
- catch (error) {
350
- return { ok: false, outcome: networkFailure(error) };
351
- }
352
- }
353
- function appDevTooOld(status) {
354
- return failure(APP_DEV_TOO_OLD_EXIT, 'SERVER_TOO_OLD', status, 'The running CrewX does not support App dev mode.');
355
- }
356
- function appDevCredentialFailure(error) {
357
- if (error instanceof crewx_api_credential_1.CrewxCredentialError) {
358
- return failure(error.exitCode, error.code, null, error.message);
359
- }
360
- return failure(1, 'ERROR', null);
361
- }
362
- function appDevEndpoint() {
363
- const raw = process.env['CREWX_ENDPOINT']?.trim() || DEFAULT_APP_DEV_ENDPOINT;
364
- return getEndpoint(raw);
365
- }
366
- function isDirectory(directory) {
367
- try {
368
- return fs.statSync(directory).isDirectory();
369
- }
370
- catch {
371
- return false;
372
- }
373
- }
374
- function isFile(filePath) {
375
- try {
376
- return fs.statSync(filePath).isFile();
377
- }
378
- catch {
379
- return false;
380
- }
381
- }
382
- function resolveWorkspaceRoot(startDirectory = process.cwd()) {
383
- let current = path.resolve(startDirectory);
384
- while (true) {
385
- if (isDirectory(path.join(current, 'apps')) || isFile(path.join(current, 'crewx.yaml'))) {
386
- try {
387
- return fs.realpathSync(current);
388
- }
389
- catch {
390
- return undefined;
391
- }
392
- }
393
- const parent = path.dirname(current);
394
- if (parent === current)
395
- return undefined;
396
- current = parent;
397
- }
398
- }
399
- const APP_MANIFEST_FILE = 'manifest.json';
400
- const LEGACY_APP_MANIFEST_FILE = 'ui-plugin.json';
401
- const CREWX_META_NAMESPACE = 'dev.crewx';
402
- function readJsonObject(filePath) {
403
- try {
404
- const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
405
- return isRecord(parsed) ? parsed : undefined;
406
- }
407
- catch {
408
- return undefined;
409
- }
410
- }
411
- function readAppManifest(directory) {
412
- const legacy = readJsonObject(path.join(directory, LEGACY_APP_MANIFEST_FILE));
413
- const modernPath = path.join(directory, APP_MANIFEST_FILE);
414
- if (!isFile(modernPath))
415
- return legacy;
416
- const modern = readJsonObject(modernPath);
417
- if (!modern)
418
- return undefined;
419
- const meta = modern['_meta'];
420
- const crewx = isRecord(meta) ? meta[CREWX_META_NAMESPACE] : undefined;
421
- const view = {
422
- pluginId: modern['name'],
423
- entry: isRecord(crewx) ? crewx['entry'] : undefined,
424
- };
425
- if (legacy && legacy['pluginId'] !== view['pluginId'])
426
- return undefined;
427
- return view;
428
- }
429
- function isPathInside(parentDirectory, candidate) {
430
- const relative = path.relative(parentDirectory, candidate);
431
- return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
432
- }
433
- function resolveWorkspaceApp(workspacePath, appSelector) {
434
- if (!appSelector || appSelector === '.' || appSelector === '..' || /[\\/]/.test(appSelector)) {
435
- return failure(2, 'USAGE', null, 'App must be a folder name or pluginId.');
436
- }
437
- const appsDirectory = path.join(workspacePath, 'apps');
438
- const directDirectory = path.join(appsDirectory, appSelector);
439
- let appDirectory = isDirectory(directDirectory) ? directDirectory : undefined;
440
- if (!appDirectory && isDirectory(appsDirectory)) {
441
- try {
442
- for (const item of fs.readdirSync(appsDirectory, { withFileTypes: true })) {
443
- if (!item.isDirectory())
444
- continue;
445
- const candidate = path.join(appsDirectory, item.name);
446
- const manifest = readAppManifest(candidate);
447
- if (manifest?.['pluginId'] === appSelector) {
448
- appDirectory = candidate;
449
- break;
450
- }
451
- }
452
- }
453
- catch {
454
- }
455
- }
456
- let appId = appSelector;
457
- if (appDirectory) {
458
- try {
459
- appDirectory = fs.realpathSync(appDirectory);
460
- }
461
- catch {
462
- return failure(6, 'APP_INVALID', null);
463
- }
464
- if (!isPathInside(fs.realpathSync(appsDirectory), appDirectory)) {
465
- return failure(2, 'USAGE', null, 'App folder must be inside the workspace apps directory.');
466
- }
467
- const manifest = readAppManifest(appDirectory);
468
- if (typeof manifest?.['pluginId'] === 'string' && manifest['pluginId'].length > 0) {
469
- appId = manifest['pluginId'];
470
- }
471
- }
472
- else {
473
- appDirectory = directDirectory;
474
- }
475
- return { appId, appDirectory };
476
- }
477
- function successEnvelope(response) {
478
- if (response.status < 200 || response.status >= 300) {
479
- return { ok: false, outcome: classifyHttpFailure(response) };
480
- }
481
- if (response.invalidJson || !isRecord(response.data) || response.data['success'] !== true
482
- || !isRecord(response.data['data'])) {
483
- return { ok: false, outcome: failure(1, responseErrorCode(response.data, 'ERROR'), response.status) };
484
- }
485
- return {
486
- ok: true,
487
- value: response.data,
488
- status: response.status,
489
- rawText: response.rawText,
490
- };
491
- }
492
- async function checkAppDevCapabilities(endpoint, token) {
493
- try {
494
- const response = await requestJson(endpoint, token, 'GET', APP_DEV_CAPABILITIES_PATH);
495
- if (response.status === 404 || (response.status >= 300 && response.status < 400)
496
- || response.invalidJson) {
497
- return { ok: false, outcome: appDevTooOld(response.status) };
498
- }
499
- if (response.status < 200 || response.status >= 300) {
500
- return { ok: false, outcome: classifyHttpFailure(response) };
501
- }
502
- if (!isRecord(response.data) || response.data['success'] !== true
503
- || !isRecord(response.data['data']) || response.data['data']['appDev'] !== 1) {
504
- return { ok: false, outcome: appDevTooOld(response.status) };
505
- }
506
- return {
507
- ok: true,
508
- value: response.data,
509
- status: response.status,
510
- rawText: response.rawText,
511
- };
512
- }
513
- catch (error) {
514
- return { ok: false, outcome: networkFailure(error, false) };
515
- }
516
- }
517
- async function prepareAppDevContext(appSelector) {
518
- const endpoint = appDevEndpoint();
519
- if (!endpoint)
520
- return { ok: false, outcome: failure(9, 'ENDPOINT_REJECTED', null) };
521
- let token;
522
- try {
523
- token = (0, crewx_api_credential_1.resolveCrewxApiCredential)('app-dev').token;
524
- }
525
- catch (error) {
526
- return { ok: false, outcome: appDevCredentialFailure(error) };
527
- }
528
- const capabilities = await checkAppDevCapabilities(endpoint, token);
529
- if (!capabilities.ok)
530
- return { ok: false, outcome: capabilities.outcome };
531
- const workspacePath = resolveWorkspaceRoot();
532
- if (!workspacePath) {
533
- return {
534
- ok: false,
535
- outcome: failure(2, 'WORKSPACE_NOT_FOUND', null, 'No workspace root with apps/ or crewx.yaml was found.'),
536
- };
537
- }
538
- const app = resolveWorkspaceApp(workspacePath, appSelector);
539
- if ('exitCode' in app)
540
- return { ok: false, outcome: app };
541
- let response;
542
- try {
543
- response = await requestJson(endpoint, token, 'POST', APP_DEV_SESSIONS_PATH, {
544
- workspacePath,
545
- appId: app.appId,
546
- });
547
- }
548
- catch (error) {
549
- return { ok: false, outcome: networkFailure(error, false) };
550
- }
551
- const opened = successEnvelope(response);
552
- if (!opened.ok)
553
- return { ok: false, outcome: opened.outcome };
554
- const data = opened.value['data'];
555
- if (!isRecord(data)
556
- || typeof data['workspaceSlug'] !== 'string'
557
- || typeof data['appId'] !== 'string'
558
- || typeof data['devPath'] !== 'string'
559
- || !data['devPath'].startsWith('/ws/')) {
560
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
561
- }
562
- if (!isDirectory(app.appDirectory)) {
563
- return { ok: false, outcome: failure(6, 'APP_INVALID', response.status) };
564
- }
565
- return {
566
- ok: true,
567
- context: {
568
- endpoint,
569
- token,
570
- workspacePath,
571
- appId: data['appId'],
572
- appDirectory: app.appDirectory,
573
- devPath: data['devPath'],
574
- },
575
- };
576
- }
577
- function appDevPath(context, ...segments) {
578
- return `${APP_DEV_API_PREFIX}${context.devPath}${segments.length > 0 ? `/${segments.map(segment).join('/')}` : ''}`;
579
- }
580
- async function performAppDevRequest(context, method, requestPath, body, timeoutMs = REQUEST_TIMEOUT_MS) {
581
- try {
582
- const response = await requestJson(context.endpoint, context.token, method, requestPath, body, timeoutMs);
583
- return successEnvelope(response);
584
- }
585
- catch (error) {
586
- return { ok: false, outcome: networkFailure(error, false) };
587
- }
588
- }
589
- function appIdFrom(value) {
590
- const direct = value['appId'] ?? value['id'];
591
- if (typeof direct === 'string' && direct.length > 0)
592
- return direct;
593
- const definition = value['definition'];
594
- if (isRecord(definition) && typeof definition['id'] === 'string')
595
- return definition['id'];
596
- return undefined;
597
- }
598
- function hashFrom(value) {
599
- const direct = value['definitionHash'];
600
- if (typeof direct === 'string' && direct.length > 0)
601
- return direct;
602
- const definition = value['definition'];
603
- if (isRecord(definition) && typeof definition['hash'] === 'string') {
604
- return definition['hash'];
605
- }
606
- return undefined;
607
- }
608
- function projectLauncher(value) {
609
- if (!isRecord(value))
610
- return undefined;
611
- const appId = appIdFrom(value);
612
- const definitionHash = hashFrom(value);
613
- if (!appId || !definitionHash)
614
- return undefined;
615
- const actions = value['agentActions'];
616
- const agentAccess = projectAgentAccess(value['agentAccess']);
617
- return {
618
- appId,
619
- name: typeof value['name'] === 'string' ? value['name'] : '',
620
- description: typeof value['description'] === 'string' ? value['description'] : '',
621
- definitionHash,
622
- agentActions: Array.isArray(actions) ? actions : [],
623
- ...(agentAccess === undefined ? {} : { agentAccess }),
624
- };
625
- }
626
- const AGENT_ACCESS_LEVELS = new Set(['allowed', 'approval_required', 'denied']);
627
- function projectAgentAccess(value) {
628
- if (!isRecord(value))
629
- return undefined;
630
- const levels = new Map();
631
- for (const [actionId, level] of Object.entries(value)) {
632
- if (AGENT_ACCESS_LEVELS.has(level))
633
- levels.set(actionId, level);
634
- }
635
- return levels.size > 0 ? levels : undefined;
636
- }
637
- async function loadLaunchers(context, timeoutMs = REQUEST_TIMEOUT_MS) {
638
- const response = await performRequest(context, 'GET', pathFor(context, 'ui-instances', 'apps'), undefined, timeoutMs);
639
- if (!response.ok)
640
- return response;
641
- const apps = response.value['apps'];
642
- if (!Array.isArray(apps)) {
643
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
644
- }
645
- const projected = apps.map(projectLauncher);
646
- if (projected.some((item) => item === undefined)) {
647
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
648
- }
649
- return { ok: true, value: projected, status: response.status };
650
- }
651
- function findLauncher(apps, appId) {
652
- return apps.find((app) => app.appId === appId);
653
- }
654
- async function openLauncher(context, launcher, timeoutMs = REQUEST_TIMEOUT_MS) {
655
- const response = await performRequest(context, 'POST', pathFor(context, 'ui-instances', 'open'), {
656
- appId: launcher.appId,
657
- expectedDefinitionHash: launcher.definitionHash,
658
- }, timeoutMs);
659
- if (!response.ok)
660
- return response;
661
- const instance = isRecord(response.value['instance']) ? response.value['instance'] : response.value;
662
- if (!isRecord(instance)) {
663
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
664
- }
665
- const instanceId = instance['instanceId'] ?? instance['id'];
666
- const revision = instance['revision'];
667
- if (typeof instanceId !== 'string' || typeof revision !== 'number') {
668
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
669
- }
670
- const ai = isRecord(response.value['ai'])
671
- ? response.value['ai']
672
- : isRecord(instance['ai']) ? instance['ai'] : undefined;
673
- return {
674
- ok: true,
675
- status: response.status,
676
- value: {
677
- appId: launcher.appId,
678
- definitionHash: launcher.definitionHash,
679
- instanceId,
680
- revision,
681
- state: ai && Object.prototype.hasOwnProperty.call(ai, 'state') ? ai['state'] : null,
682
- },
683
- };
684
- }
685
- async function readInstanceState(context, instanceId) {
686
- const response = await performRequest(context, 'GET', pathFor(context, 'ui-instances', instanceId));
687
- if (!response.ok)
688
- return response;
689
- const value = response.value;
690
- const responseInstanceId = value['instanceId'];
691
- const revision = value['revision'];
692
- if (typeof responseInstanceId !== 'string' || typeof revision !== 'number') {
693
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
694
- }
695
- const ai = isRecord(value['ai']) ? value['ai'] : undefined;
696
- const passthrough = (key) => {
697
- if (Object.prototype.hasOwnProperty.call(value, key))
698
- return { [key]: value[key] };
699
- if (ai && Object.prototype.hasOwnProperty.call(ai, key))
700
- return { [key]: ai[key] };
701
- return {};
702
- };
703
- return {
704
- ok: true,
705
- status: response.status,
706
- value: {
707
- instanceId: responseInstanceId,
708
- revision,
709
- state: ai && Object.prototype.hasOwnProperty.call(ai, 'state') ? ai['state'] : null,
710
- buttons: ai && Object.prototype.hasOwnProperty.call(ai, 'buttons') ? ai['buttons'] : null,
711
- ...passthrough('agentAccess'),
712
- ...passthrough('modelContext'),
713
- ...passthrough('pendingApproval'),
714
- },
715
- };
716
- }
717
- async function runAction(context, instanceId, actionId, requestId, input, timeoutMs = REQUEST_TIMEOUT_MS) {
718
- const response = await performRequest(context, 'POST', pathFor(context, 'ui-instances', instanceId, 'actions'), { actionId, requestId, input }, timeoutMs);
719
- if (!response.ok)
720
- return response;
721
- const receipt = response.value['receipt'];
722
- if (!isRecord(receipt)) {
723
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
724
- }
725
- const receiptId = receipt['receiptId'] ?? receipt['id'];
726
- const approval = response.value['approval'];
727
- if (response.status === 202 && isRecord(approval)) {
728
- const approvalId = approval['approvalId'];
729
- const expiresAt = approval['expiresAt'];
730
- const resolvedRequestId = typeof receipt['requestId'] === 'string'
731
- ? receipt['requestId']
732
- : typeof approval['requestId'] === 'string'
733
- ? approval['requestId']
734
- : requestId;
735
- if (typeof receiptId !== 'string' || typeof approvalId !== 'string'
736
- || (typeof expiresAt !== 'string' && typeof expiresAt !== 'number')) {
737
- return { ok: false, outcome: failure(1, 'ERROR', response.status) };
738
- }
739
- return {
740
- ok: false,
741
- outcome: {
742
- exitCode: 8,
743
- payload: {
744
- ok: false,
745
- exit: 8,
746
- error: { code: 'APPROVAL_PENDING', status: 202 },
747
- receiptId,
748
- requestId: resolvedRequestId,
749
- approvalId,
750
- expiresAt,
751
- },
752
- },
753
- };
754
- }
755
- const status = receipt['status'] ?? (response.status === 202 ? 'requested' : 'settled');
756
- const resolve = response.value['resolve'];
757
- const ai = isRecord(resolve) && isRecord(resolve['ai']) ? resolve['ai'] : undefined;
758
- return {
759
- ok: true,
760
- status: response.status,
761
- value: {
762
- receiptId: typeof receiptId === 'string' ? receiptId : '',
763
- requestId: typeof receipt['requestId'] === 'string' ? receipt['requestId'] : requestId,
764
- instanceId,
765
- actionId: typeof receipt['actionId'] === 'string' ? receipt['actionId'] : actionId,
766
- status,
767
- outcome: Object.prototype.hasOwnProperty.call(receipt, 'outcome') ? receipt['outcome'] : null,
768
- ...(typeof receipt['errorCode'] === 'string' ? { errorCode: receipt['errorCode'] } : {}),
769
- state: ai && Object.prototype.hasOwnProperty.call(ai, 'state') ? ai['state'] : null,
770
- ...(Object.prototype.hasOwnProperty.call(response.value, 'result') ? { result: response.value['result'] } : {}),
771
- ...(Object.prototype.hasOwnProperty.call(response.value, 'resultExpired')
772
- ? { resultExpired: response.value['resultExpired'] }
773
- : {}),
774
- },
775
- };
776
- }
777
- function isRetryablePollFailure(outcome) {
778
- const error = outcome.payload['error'];
779
- return outcome.exitCode === 14
780
- || (isRecord(error) && error['status'] === 429);
781
- }
782
- async function readReceipt(context, receiptId) {
783
- const response = await performRequest(context, 'GET', pathFor(context, 'ui-action-receipts', receiptId));
784
- if (!response.ok) {
785
- return { state: 'failed', retryable: isRetryablePollFailure(response.outcome), value: response.outcome };
786
- }
787
- const receipt = response.value['receipt'];
788
- if (!isRecord(receipt))
789
- return { state: 'failed', retryable: false, value: failure(1, 'ERROR', response.status) };
790
- return {
791
- state: receipt['status'] === 'requested' ? 'pending' : 'settled',
792
- value: success({
793
- ok: true,
794
- receipt,
795
- outcome: Object.prototype.hasOwnProperty.call(receipt, 'outcome') ? receipt['outcome'] : null,
796
- ...(typeof receipt['errorCode'] === 'string' ? { errorCode: receipt['errorCode'] } : {}),
797
- ...(Object.prototype.hasOwnProperty.call(response.value, 'result') ? { result: response.value['result'] } : {}),
798
- ...(Object.prototype.hasOwnProperty.call(response.value, 'resultExpired')
799
- ? { resultExpired: response.value['resultExpired'] }
800
- : {}),
801
- }),
802
- };
803
- }
804
- function receiptStillPending(receiptId, waitedSeconds) {
805
- return {
806
- exitCode: 124,
807
- payload: { ok: false, exit: 124, error: { code: 'WAIT_TIMEOUT', status: null }, receiptId },
808
- suppressOutput: true,
809
- stderr: `→ Receipt ${receiptId} is still pending after ${waitedSeconds}s. Do not end your turn: run crewx app receipt ${receiptId} --wait=${(0, receipt_wait_js_1.nextWaitSeconds)(waitedSeconds)} again.`,
810
- };
811
- }
812
- function contextFromEnvironment() {
813
- const ticket = process.env['CREWX_TASK_TICKET'] ?? '';
814
- const workspaceSlug = process.env['CREWX_WORKSPACE_SLUG'] ?? '';
815
- const threadId = process.env['CREWX_THREAD_ID'] ?? '';
816
- if (!ticket.startsWith('crewxtk_') || !workspaceSlug || !threadId) {
817
- return failure(3, 'NO_TICKET', null);
818
- }
819
- const endpoint = getEndpoint(process.env['CREWX_ENDPOINT']);
820
- if (!endpoint)
821
- return failure(9, 'ENDPOINT_REJECTED', null);
822
- return {
823
- ticket,
824
- workspaceSlug,
825
- threadId,
826
- endpoint,
827
- basePath: basePath(workspaceSlug, threadId),
828
- };
829
- }
830
- function isAppCommandOutcome(value) {
831
- return 'exitCode' in value;
832
- }
833
- function padTime(value) {
834
- return String(value).padStart(2, '0');
835
- }
836
- function localExpiryTime(expiresAt) {
837
- const date = typeof expiresAt === 'number'
838
- ? new Date(expiresAt)
839
- : typeof expiresAt === 'string'
840
- ? new Date(/^\d+$/.test(expiresAt) ? Number(expiresAt) : expiresAt)
841
- : undefined;
842
- if (!date || Number.isNaN(date.getTime()))
843
- return undefined;
844
- return `${padTime(date.getHours())}:${padTime(date.getMinutes())}`;
845
- }
846
- function withApprovalHint(outcome) {
847
- if (outcome.exitCode !== 8)
848
- return outcome;
849
- const time = localExpiryTime(outcome.payload['expiresAt']);
850
- const receiptId = outcome.payload['receiptId'];
851
- const lines = [
852
- `→ Waiting for the user's approval in the App window${time ? ` (expires ${time})` : ''}. Do not end your turn: ending it cancels the request.`,
853
- ...(typeof receiptId === 'string' && receiptId.length > 0
854
- ? [`→ Wait: crewx app receipt ${receiptId} --wait=${receipt_wait_js_1.RECEIPT_WAIT_DEFAULT_SECONDS} (repeat on exit 124 until it settles)`]
855
- : []),
856
- '→ Report success only if it settles as succeeded.',
857
- ];
858
- return { ...outcome, stderr: [outcome.stderr, ...lines].filter(Boolean).join('\n') };
859
- }
860
- const MANUAL_READ_LIMIT_BYTES = 256 * 1024;
861
- function readAppManual(appDirectory) {
862
- try {
863
- const realDirectory = fs.realpathSync(appDirectory);
864
- const realFile = fs.realpathSync(path.join(realDirectory, 'SKILL.md'));
865
- if (!isPathInside(realDirectory, realFile) || !isFile(realFile))
866
- return undefined;
867
- const descriptor = fs.openSync(realFile, 'r');
868
- try {
869
- const bytes = new Uint8Array(MANUAL_READ_LIMIT_BYTES);
870
- const bytesRead = fs.readSync(descriptor, bytes, 0, bytes.length, 0);
871
- return (0, usage_js_1.manualBody)(Buffer.from(bytes.buffer, 0, bytesRead).toString('utf8'));
872
- }
873
- finally {
874
- fs.closeSync(descriptor);
875
- }
876
- }
877
- catch {
878
- return undefined;
879
- }
880
- }
881
- async function findLocalApp(appId) {
882
- const root = resolveWorkspaceRoot();
883
- if (!root)
884
- return undefined;
885
- try {
886
- const sdk = await Promise.resolve().then(() => __importStar(require('@crewx/sdk')));
887
- const discovered = await sdk.discoverWorkspaceApps(root);
888
- const candidate = discovered.apps.find((app) => app.appId === appId)
889
- ?? discovered.apps.find((app) => app.pluginId === appId);
890
- if (!candidate)
891
- return undefined;
892
- return {
893
- candidate,
894
- directory: path.join(root, candidate.relativePath),
895
- resolveAccess: sdk.resolveAgentAccess,
896
- };
897
- }
898
- catch {
899
- return undefined;
900
- }
901
- }
902
- async function executeAppUsage(args) {
903
- if (args.length !== 2 || args[1].startsWith('--'))
904
- return usageFailure();
905
- const appId = args[1];
906
- const context = contextFromEnvironment();
907
- if (isAppCommandOutcome(context))
908
- return context;
909
- const list = await loadLaunchers(context);
910
- if (!list.ok)
911
- return list.outcome;
912
- const launcher = findLauncher(list.value, appId);
913
- if (!launcher)
914
- return failure(6, 'MCP_APP_NOT_FOUND', null);
915
- const serverActions = launcher.agentActions.filter((item) => typeof item === 'string');
916
- const local = await findLocalApp(appId);
917
- const text = local
918
- ? (0, usage_js_1.renderUsage)(readAppManual(local.directory), (0, usage_js_1.renderActionsSection)(appId, local.candidate, (0, usage_js_1.buildActionRows)(local.candidate, new Set(serverActions), (action) => local.resolveAccess(action), launcher.agentAccess)), local.candidate.manifest.csp?.connectDomains)
919
- : (0, usage_js_1.renderUsage)(undefined, (0, usage_js_1.renderServerOnlyActions)(appId, serverActions));
920
- return { exitCode: 0, payload: { ok: true }, stdout: text };
921
- }
922
- function parseSingleAppCommand(args, command) {
923
- if (args[0] !== command)
924
- return null;
925
- const positional = [];
926
- let flag = false;
927
- for (const arg of args.slice(1)) {
928
- if (arg === (command === 'dev' ? '--no-open' : '--json')) {
929
- if (flag)
930
- return null;
931
- flag = true;
932
- }
933
- else if (arg.startsWith('--')) {
934
- return null;
935
- }
936
- else {
937
- positional.push(arg);
938
- }
939
- }
940
- if (positional.length !== 1)
941
- return null;
942
- return { appSelector: positional[0], flag };
943
- }
944
- function standardJsonOutput(outcome) {
945
- return `${JSON.stringify(outcome.payload)}\n`;
946
- }
947
- function manifestReasonSuffix(item) {
948
- const detail = item['detail'];
949
- if (item['rule'] !== 'manifest' || !isRecord(detail))
950
- return '';
951
- const path = typeof detail['path'] === 'string' ? detail['path'] : '';
952
- const rule = typeof detail['rule'] === 'string' ? detail['rule'] : '';
953
- if (!rule)
954
- return '';
955
- return ` ${path ? `${path} ` : ''}(${rule})`;
956
- }
957
- async function executeAppCheck(args) {
958
- const parsed = parseSingleAppCommand(args, 'check');
959
- if (!parsed)
960
- return usageFailure();
961
- const prepared = await prepareAppDevContext(parsed.appSelector);
962
- if (!prepared.ok)
963
- return prepared.outcome;
964
- const response = await performAppDevRequest(prepared.context, 'GET', appDevPath(prepared.context, 'check'));
965
- if (!response.ok)
966
- return response.outcome;
967
- const data = response.value['data'];
968
- if (!isRecord(data))
969
- return failure(1, 'ERROR', response.status);
970
- const summary = isRecord(data['summary']) ? data['summary'] : {};
971
- const items = Array.isArray(data['items']) ? data['items'] : [];
972
- const errors = typeof summary['errors'] === 'number'
973
- ? summary['errors']
974
- : items.filter((item) => isRecord(item) && item['severity'] === 'error').length;
975
- const warnings = typeof summary['warnings'] === 'number'
976
- ? summary['warnings']
977
- : items.filter((item) => isRecord(item) && item['severity'] === 'warning').length;
978
- const exitCode = errors === 0 ? 0 : APP_DEV_CHECK_FAILED_EXIT;
979
- if (parsed.flag) {
980
- return {
981
- exitCode,
982
- payload: response.value,
983
- stdout: `${response.rawText ?? standardJsonOutput({ exitCode, payload: response.value })}`
984
- + (response.rawText?.endsWith('\n') ? '' : '\n'),
985
- suppressOutput: true,
986
- };
987
- }
988
- const lines = items.flatMap((item) => {
989
- if (!isRecord(item))
990
- return [];
991
- const severity = item['severity'];
992
- const marker = severity === 'error' ? '✕' : severity === 'ok' ? '✓' : '!';
993
- const where = typeof item['where'] === 'string' ? item['where'] : '';
994
- const code = typeof item['code'] === 'string' ? item['code'] : '';
995
- return [`${marker} ${where} ${code}${manifestReasonSuffix(item)}`];
996
- });
997
- lines.push(`Summary: ${errors} errors, ${warnings} warnings`);
998
- return {
999
- exitCode,
1000
- payload: {},
1001
- stdout: `${lines.join('\n')}\n`,
1002
- suppressOutput: true,
1003
- };
1004
- }
1005
- async function getPairingDevUrl(context) {
1006
- let pairingToken;
1007
- try {
1008
- pairingToken = (0, crewx_api_credential_1.resolveCrewxApiCredential)('pairing').token;
1009
- }
1010
- catch (error) {
1011
- return appDevCredentialFailure(error);
1012
- }
1013
- let response;
1014
- try {
1015
- response = await requestJson(context.endpoint, pairingToken, 'POST', '/api/v1/auth/local/code');
1016
- }
1017
- catch (error) {
1018
- return networkFailure(error, false);
1019
- }
1020
- if (response.status < 200 || response.status >= 300) {
1021
- return classifyHttpFailure(response);
1022
- }
1023
- if (response.invalidJson || !isRecord(response.data) || typeof response.data['url'] !== 'string') {
1024
- return failure(1, 'PAIRING_CODE_INVALID', response.status);
1025
- }
1026
- try {
1027
- const pairingUrl = new node_url_1.URL(response.data['url']);
1028
- const pairingEndpoint = getEndpoint(pairingUrl.origin);
1029
- const match = pairingUrl.hash.match(/^#pair=(.+)$/);
1030
- if (!pairingEndpoint || !match)
1031
- return failure(1, 'PAIRING_CODE_INVALID', response.status);
1032
- return `${pairingEndpoint.origin}${context.devPath}#pair=${encodeURIComponent(match[1])}`;
1033
- }
1034
- catch {
1035
- return failure(1, 'PAIRING_CODE_INVALID', response.status);
1036
- }
1037
- }
1038
- async function openPlatformUrl(url) {
1039
- const command = process.platform === 'darwin'
1040
- ? 'open'
1041
- : process.platform === 'win32' ? 'explorer.exe' : 'xdg-open';
1042
- const child = (0, node_child_process_1.spawn)(command, [url], { detached: true, stdio: 'ignore' });
1043
- await new Promise((resolve, reject) => {
1044
- child.once('error', reject);
1045
- child.once('spawn', () => {
1046
- child.unref();
1047
- resolve();
1048
- });
1049
- });
1050
- }
1051
- async function executeAppDevAction(args) {
1052
- const parsed = parseCommand(['run', ...args.slice(2)]);
1053
- if (!parsed || parsed.command !== 'run')
1054
- return usageFailure();
1055
- const [appSelector, actionId] = parsed.positional;
1056
- const prepared = await prepareAppDevContext(appSelector);
1057
- if (!prepared.ok)
1058
- return prepared.outcome;
1059
- const state = await performAppDevRequest(prepared.context, 'GET', appDevPath(prepared.context, 'state'));
1060
- if (!state.ok)
1061
- return state.outcome;
1062
- const stateData = state.value['data'];
1063
- if (!isRecord(stateData) || typeof stateData['revision'] !== 'number') {
1064
- return failure(1, 'APP_DEV_RESPONSE_INVALID', state.status);
1065
- }
1066
- const requestId = parsed.requestId ?? `cxa_${(0, node_crypto_1.randomBytes)(16).toString('base64url')}`;
1067
- const response = await performAppDevRequest(prepared.context, 'POST', appDevPath(prepared.context, 'actions'), {
1068
- actionId,
1069
- requestId,
1070
- input: parsed.input,
1071
- expectedRevision: stateData['revision'],
1072
- }, DEV_ACTION_TIMEOUT_MS);
1073
- if (!response.ok)
1074
- return response.outcome;
1075
- const responseData = response.value['data'];
1076
- const receipt = isRecord(responseData) && isRecord(responseData['receipt'])
1077
- ? responseData['receipt']
1078
- : undefined;
1079
- return {
1080
- exitCode: receipt?.['outcome'] === 'succeeded' ? 0 : 1,
1081
- payload: response.value,
1082
- stdout: `${response.rawText ?? standardJsonOutput({ exitCode: 0, payload: response.value })}`
1083
- + (response.rawText?.endsWith('\n') ? '' : '\n'),
1084
- suppressOutput: true,
1085
- };
1086
- }
1087
- function hasBuildScript(appDirectory) {
1088
- try {
1089
- const packageJson = JSON.parse(fs.readFileSync(path.join(appDirectory, 'package.json'), 'utf8'));
1090
- if (!isRecord(packageJson) || !isRecord(packageJson['scripts']))
1091
- return false;
1092
- const build = packageJson['scripts']['build'];
1093
- return typeof build === 'string' && build.trim().length > 0;
1094
- }
1095
- catch {
1096
- return false;
1097
- }
1098
- }
1099
- function buildOutputPaths(appDirectory) {
1100
- const result = new Set([
1101
- path.normalize(APP_MANIFEST_FILE),
1102
- path.normalize(LEGACY_APP_MANIFEST_FILE),
1103
- ]);
1104
- const manifest = readAppManifest(appDirectory);
1105
- const entry = manifest?.['entry'];
1106
- if (typeof entry === 'string' && entry.length > 0 && !path.isAbsolute(entry)) {
1107
- const normalized = path.normalize(entry);
1108
- if (normalized !== '..' && !normalized.startsWith(`..${path.sep}`))
1109
- result.add(normalized);
1110
- }
1111
- return result;
1112
- }
1113
- function outputFingerprint(appDirectory, relativePath) {
1114
- try {
1115
- const stats = fs.statSync(path.join(appDirectory, relativePath));
1116
- return `${stats.size}:${stats.mtimeMs}:${stats.ino}`;
1117
- }
1118
- catch {
1119
- return null;
1120
- }
1121
- }
1122
- function snapshotBuildOutputs(appDirectory, paths) {
1123
- return new Map([...paths].map((relativePath) => [
1124
- relativePath,
1125
- outputFingerprint(appDirectory, relativePath),
1126
- ]));
1127
- }
1128
- function runNpmBuild(appDirectory, onChild) {
1129
- const startedAt = Date.now();
1130
- let output = '';
1131
- let settled = false;
1132
- const appendOutput = (chunk) => {
1133
- const next = Buffer.from(output + (Buffer.isBuffer(chunk) ? chunk.toString('utf8') : chunk), 'utf8');
1134
- output = next.length > MAX_BUILD_OUTPUT_BYTES
1135
- ? next.subarray(next.length - MAX_BUILD_OUTPUT_BYTES).toString('utf8')
1136
- : next.toString('utf8');
1137
- };
1138
- return new Promise((resolve) => {
1139
- const child = (0, node_child_process_1.spawn)('npm', ['run', 'build'], {
1140
- cwd: appDirectory,
1141
- env: process.env,
1142
- stdio: ['ignore', 'pipe', 'pipe'],
1143
- });
1144
- onChild(child);
1145
- child.stdout?.on('data', appendOutput);
1146
- child.stderr?.on('data', appendOutput);
1147
- const finish = (ok) => {
1148
- if (settled)
1149
- return;
1150
- settled = true;
1151
- onChild(undefined);
1152
- resolve({
1153
- ok,
1154
- ms: Math.max(0, Date.now() - startedAt),
1155
- output,
1156
- });
1157
- };
1158
- child.once('error', (error) => {
1159
- appendOutput(`${error.message}\n`);
1160
- finish(false);
1161
- });
1162
- child.once('close', (code) => finish(code === 0));
1163
- });
1164
- }
1165
- function responseCode(outcome) {
1166
- const error = outcome.payload['error'];
1167
- return isRecord(error) && typeof error['code'] === 'string' ? error['code'] : 'ERROR';
1168
- }
1169
- async function postAppDevReload(context, body) {
1170
- const response = await performAppDevRequest(context, 'POST', appDevPath(context, 'reload'), body);
1171
- if (!response.ok)
1172
- return { errorCode: responseCode(response.outcome) };
1173
- const data = response.value['data'];
1174
- return isRecord(data) && typeof data['generation'] === 'number'
1175
- ? { generation: data['generation'] }
1176
- : { errorCode: 'APP_DEV_RESPONSE_INVALID' };
1177
- }
1178
- async function runAppDevWatcher(context, buildEnabled) {
1179
- const parentPid = process.ppid;
1180
- let watcher;
1181
- try {
1182
- watcher = fs.watch(context.appDirectory, { recursive: true }, (_event, filename) => {
1183
- onFileChange(filename);
1184
- });
1185
- }
1186
- catch (error) {
1187
- return failure(1, 'WATCH_FAILED', null, error instanceof Error ? error.message : 'Unable to watch the App folder.');
1188
- }
1189
- let closed = false;
1190
- let stopRequested = false;
1191
- let cycleRunning = false;
1192
- let queuedChange = false;
1193
- let queuedPath = '';
1194
- let pendingPath = '';
1195
- let debounceTimer;
1196
- let parentCheckTimer;
1197
- let activeBuild;
1198
- let finalOutcome = { exitCode: 0, payload: {}, suppressOutput: true };
1199
- const recentOutputEvents = new Map();
1200
- let resolveRun;
1201
- const result = new Promise((resolve) => {
1202
- resolveRun = resolve;
1203
- });
1204
- const writeLine = (line) => {
1205
- process.stdout.write(`${line}\n`);
1206
- };
1207
- const finish = () => {
1208
- if (closed)
1209
- return;
1210
- closed = true;
1211
- if (debounceTimer)
1212
- clearTimeout(debounceTimer);
1213
- if (parentCheckTimer)
1214
- clearInterval(parentCheckTimer);
1215
- process.removeListener('SIGINT', onSignal);
1216
- process.removeListener('SIGTERM', onSignal);
1217
- resolveRun(finalOutcome);
1218
- };
1219
- const onSignal = () => {
1220
- if (stopRequested)
1221
- return;
1222
- stopRequested = true;
1223
- watcher.close();
1224
- if (debounceTimer)
1225
- clearTimeout(debounceTimer);
1226
- if (activeBuild && activeBuild.exitCode === null)
1227
- activeBuild.kill('SIGTERM');
1228
- if (!cycleRunning)
1229
- finish();
1230
- };
1231
- const onFileChange = (filename) => {
1232
- if (stopRequested || closed)
1233
- return;
1234
- const relativePath = filename === null ? '' : path.normalize(filename.toString());
1235
- const parts = relativePath.split(path.sep);
1236
- if (parts.some((part) => part === 'node_modules' || part === '.crewx' || part === 'dist'))
1237
- return;
1238
- const outputPaths = buildEnabled ? buildOutputPaths(context.appDirectory) : new Set();
1239
- if (activeBuild && outputPaths.has(relativePath))
1240
- return;
1241
- const suppressedUntil = recentOutputEvents.get(relativePath);
1242
- if (suppressedUntil !== undefined) {
1243
- if (Date.now() <= suppressedUntil)
1244
- return;
1245
- recentOutputEvents.delete(relativePath);
1246
- }
1247
- if (cycleRunning) {
1248
- queuedChange = true;
1249
- queuedPath = relativePath || 'App files';
1250
- return;
1251
- }
1252
- pendingPath = relativePath || 'App files';
1253
- if (debounceTimer)
1254
- clearTimeout(debounceTimer);
1255
- debounceTimer = setTimeout(() => {
1256
- debounceTimer = undefined;
1257
- const changedPath = pendingPath || 'App files';
1258
- pendingPath = '';
1259
- void runCycle(changedPath, false);
1260
- }, 200);
1261
- };
1262
- const reloadLabel = async (body) => {
1263
- const reload = await postAppDevReload(context, body);
1264
- return reload.generation === undefined ? `failed ${reload.errorCode ?? 'ERROR'}` : String(reload.generation);
1265
- };
1266
- const runCycle = async (changedPath, initial) => {
1267
- if (stopRequested || closed)
1268
- return;
1269
- cycleRunning = true;
1270
- if (buildEnabled) {
1271
- const outputPaths = buildOutputPaths(context.appDirectory);
1272
- const before = snapshotBuildOutputs(context.appDirectory, outputPaths);
1273
- const build = await runNpmBuild(context.appDirectory, (child) => {
1274
- activeBuild = child;
1275
- });
1276
- const after = snapshotBuildOutputs(context.appDirectory, outputPaths);
1277
- for (const [outputPath, fingerprint] of after) {
1278
- if (before.get(outputPath) !== fingerprint) {
1279
- recentOutputEvents.set(outputPath, Date.now() + 1000);
1280
- }
1281
- }
1282
- if (stopRequested) {
1283
- cycleRunning = false;
1284
- finish();
1285
- return;
1286
- }
1287
- const reloadBody = {
1288
- reason: 'build',
1289
- build: {
1290
- ok: build.ok,
1291
- ms: build.ms,
1292
- ...(!build.ok ? { output: build.output } : {}),
1293
- },
1294
- };
1295
- const generationLabel = await reloadLabel(reloadBody);
1296
- writeLine(`${initial ? 'initial' : changedPath} -> build ${build.ok ? 'ok' : 'failed'} ${build.ms}ms -> reload ${generationLabel}`);
1297
- }
1298
- else if (!initial) {
1299
- const generationLabel = await reloadLabel({ reason: 'manual' });
1300
- writeLine(`${changedPath} -> reload ${generationLabel}`);
1301
- }
1302
- cycleRunning = false;
1303
- if (stopRequested) {
1304
- finish();
1305
- return;
1306
- }
1307
- if (queuedChange) {
1308
- const nextPath = queuedPath || 'App files';
1309
- queuedChange = false;
1310
- queuedPath = '';
1311
- void runCycle(nextPath, false);
1312
- return;
1313
- }
1314
- if (stopRequested)
1315
- finish();
1316
- };
1317
- watcher.once('error', (error) => {
1318
- finalOutcome = failure(1, 'WATCH_FAILED', null, error.message);
1319
- onSignal();
1320
- });
1321
- process.once('SIGINT', onSignal);
1322
- process.once('SIGTERM', onSignal);
1323
- parentCheckTimer = setInterval(() => {
1324
- if (process.ppid !== parentPid)
1325
- onSignal();
1326
- }, 1000);
1327
- parentCheckTimer.unref();
1328
- if (buildEnabled)
1329
- void runCycle('initial', true);
1330
- return result;
1331
- }
1332
- async function executeAppDev(args) {
1333
- const parsed = parseSingleAppCommand(args, 'dev');
1334
- if (!parsed)
1335
- return usageFailure();
1336
- const prepared = await prepareAppDevContext(parsed.appSelector);
1337
- if (!prepared.ok)
1338
- return prepared.outcome;
1339
- const browserUrl = `${prepared.context.endpoint.origin}${prepared.context.devPath}`;
1340
- process.stdout.write(`${browserUrl}\n`);
1341
- if (!parsed.flag) {
1342
- const pairingUrl = await getPairingDevUrl(prepared.context);
1343
- if (typeof pairingUrl !== 'string')
1344
- return pairingUrl;
1345
- try {
1346
- await openPlatformUrl(pairingUrl);
1347
- }
1348
- catch (error) {
1349
- return failure(1, 'BROWSER_OPEN_FAILED', null, error instanceof Error ? error.message : 'Could not open the App dev tab.');
1350
- }
1351
- }
1352
- return runAppDevWatcher(prepared.context, hasBuildScript(prepared.context.appDirectory));
1353
- }
1354
- const BUILD_USAGE = 'usage: crewx app build [<appDir>] [--check | --watch]';
1355
- const APP_HELP = [
1356
- 'crewx app <command>',
1357
- '',
1358
- ' list List Apps available to this task',
1359
- ' open <appId> Open an App in this conversation',
1360
- ' state <instanceId> Read AI-visible state, buttons, and agent access',
1361
- ' run <instanceId|appId> <actionId> [--input <JSON>|-] [--request-id <id>] Run one available action',
1362
- ' receipt <receiptId> [--wait=<seconds>] Read a safe action receipt',
1363
- ' build [<appDir>] [--check | --watch] Build a @Tool App: server.ts -> dist/ and the actions (manifest.json, or dist/tools.json)',
1364
- ' dev <app> [--no-open] Start a foreground App dev session',
1365
- ' check <app> [--json] Run the App dev checks',
1366
- ' run --dev <app> <actionId> [--input <JSON>|-] Run an App action through the dev checks',
1367
- ' usage [<appId>] Show the guide, or one App\'s manual and action table',
1368
- '',
1369
- ].join('\n');
1370
- const BUILD_HELP = [
1371
- BUILD_USAGE,
1372
- '',
1373
- ' Builds <appDir> (default: the current directory) from its server.ts: dist/server.mjs, dist/entry.mjs,',
1374
- ' and the actions of manifest.json. With no "bin" and no crewx.app.command in package.json the App is',
1375
- ' server.ts only (tool mode): dist/server.mjs, dist/main.mjs and dist/tools.json, and manifest.json',
1376
- ' carries no actions. --check writes nothing and exits 1 when a generated file is stale.',
1377
- ' --watch rebuilds on every change until Ctrl-C.',
1378
- '',
1379
- ].join('\n');
1380
- function loadAppBuilder() {
1381
- const candidates = [
1382
- path.join(__dirname, '..', '..', 'build-app', 'load.cjs'),
1383
- path.join(__dirname, '..', 'build-app', 'load.cjs'),
1384
- ];
1385
- const shim = candidates.find((candidate) => fs.existsSync(candidate));
1386
- if (!shim)
1387
- return Promise.reject(new Error('the App build is not part of this install'));
1388
- return require(shim).loadAppBuilder();
1389
- }
1390
- function describeError(error) {
1391
- return error instanceof Error ? error.message : String(error);
1392
- }
1393
- async function executeAppBuild(args) {
1394
- if (args.slice(1).some((arg) => arg === '--help' || arg === '-h')) {
1395
- return { exitCode: 0, payload: { ok: true }, stdout: BUILD_HELP };
1396
- }
1397
- let builder;
1398
- try {
1399
- builder = await loadAppBuilder();
1400
- }
1401
- catch (error) {
1402
- return failure(1, 'BUILD_UNAVAILABLE', null, describeError(error));
1403
- }
1404
- let options;
1405
- try {
1406
- options = builder.parseArguments(args.slice(1));
1407
- }
1408
- catch (error) {
1409
- return failure(2, 'USAGE', null, `${describeError(error)} → ${BUILD_USAGE}`);
1410
- }
1411
- const { appDirectory } = options;
1412
- const shownDirectory = path.relative(process.cwd(), appDirectory);
1413
- const rebuildCommand = shownDirectory === '' ? 'crewx app build' : `crewx app build ${shownDirectory}`;
1414
- if (options.watch) {
1415
- const controller = new AbortController();
1416
- const stop = () => controller.abort();
1417
- process.once('SIGINT', stop);
1418
- process.once('SIGTERM', stop);
1419
- try {
1420
- await builder.watchApp(appDirectory, (line) => process.stdout.write(`${line}\n`), { signal: controller.signal });
1421
- }
1422
- finally {
1423
- process.removeListener('SIGINT', stop);
1424
- process.removeListener('SIGTERM', stop);
1425
- }
1426
- return { exitCode: 0, payload: {}, suppressOutput: true };
1427
- }
1428
- try {
1429
- const result = await builder.buildApp({ appDirectory, check: options.check });
1430
- if (options.check && result.staleFiles.length > 0) {
1431
- return {
1432
- ...failure(1, 'BUILD_STALE', null, `Stale generated files: ${result.staleFiles.join(', ')} → Fix: ${rebuildCommand}`),
1433
- payload: {
1434
- ok: false,
1435
- exit: 1,
1436
- error: { code: 'BUILD_STALE', status: null },
1437
- appDir: appDirectory,
1438
- stale: result.staleFiles,
1439
- },
1440
- };
1441
- }
1442
- if (options.check) {
1443
- return success({ ok: true, appDir: appDirectory, check: true, inSync: true, files: result.files, actions: result.actions });
1444
- }
1445
- const name = path.basename(appDirectory);
1446
- const inWorkspaceApps = path.basename(path.dirname(appDirectory)) === 'apps';
1447
- return {
1448
- ...success({ ok: true, appDir: appDirectory, files: result.files, actions: result.actions }),
1449
- stderr: inWorkspaceApps
1450
- ? `→ Next: crewx app check ${name}`
1451
- : `→ Next: put ${name} in the workspace apps/ folder, then crewx app check ${name}`,
1452
- };
1453
- }
1454
- catch (error) {
1455
- return failure(1, 'BUILD_FAILED', null, describeError(error));
1456
- }
1457
- }
1458
- async function executeAppCommand(args, { clock = receipt_wait_js_1.realClock } = {}) {
1459
- if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
1460
- return { exitCode: 0, payload: { ok: true }, stdout: APP_HELP };
1461
- }
1462
- if (args[0] === 'build')
1463
- return executeAppBuild(args);
1464
- if (args[0] === 'dev')
1465
- return executeAppDev(args);
1466
- if (args[0] === 'check')
1467
- return executeAppCheck(args);
1468
- if (args[0] === 'run' && args[1] === '--dev')
1469
- return executeAppDevAction(args);
1470
- if (args[0] === 'usage')
1471
- return executeAppUsage(args);
1472
- const parsed = parseCommand(args);
1473
- if (!parsed)
1474
- return usageFailure();
1475
- const context = contextFromEnvironment();
1476
- if (isAppCommandOutcome(context))
1477
- return context;
1478
- if (parsed.command === 'list') {
1479
- const result = await loadLaunchers(context);
1480
- if (!result.ok)
1481
- return result.outcome;
1482
- return success({ ok: true, apps: result.value });
1483
- }
1484
- if (parsed.command === 'receipt') {
1485
- const receiptId = parsed.positional[0];
1486
- const waitSeconds = (0, receipt_wait_js_1.clampWaitSeconds)(parsed.waitSeconds ?? 0);
1487
- if (waitSeconds === 0)
1488
- return (await readReceipt(context, receiptId)).value;
1489
- const last = await (0, receipt_wait_js_1.pollUntilSettled)(() => readReceipt(context, receiptId), waitSeconds, clock);
1490
- if (last.state !== 'pending')
1491
- return last.value;
1492
- return receiptStillPending(receiptId, waitSeconds);
1493
- }
1494
- if (parsed.command === 'state') {
1495
- const result = await readInstanceState(context, parsed.positional[0]);
1496
- if (!result.ok)
1497
- return result.outcome;
1498
- return success({ ok: true, ...result.value });
1499
- }
1500
- if (parsed.command === 'open') {
1501
- const appId = parsed.positional[0];
1502
- const list = await loadLaunchers(context);
1503
- if (!list.ok)
1504
- return list.outcome;
1505
- const launcher = findLauncher(list.value, appId);
1506
- if (!launcher)
1507
- return failure(6, 'MCP_APP_NOT_FOUND', null);
1508
- const opened = await openLauncher(context, launcher);
1509
- if (!opened.ok)
1510
- return opened.outcome;
1511
- return success({ ok: true, ...opened.value });
1512
- }
1513
- const [target, actionId] = parsed.positional;
1514
- const requestId = parsed.requestId ?? `cxa_${(0, node_crypto_1.randomBytes)(16).toString('base64url')}`;
1515
- let instanceId = target;
1516
- if (!target.startsWith(APP_ID_PREFIX)) {
1517
- const list = await loadLaunchers(context, RUN_REQUEST_TIMEOUT_MS);
1518
- if (!list.ok)
1519
- return withRunTimeoutGuidance(list.outcome, requestId);
1520
- const launcher = findLauncher(list.value, target);
1521
- if (!launcher)
1522
- return failure(6, 'MCP_APP_NOT_FOUND', null);
1523
- const opened = await openLauncher(context, launcher, RUN_REQUEST_TIMEOUT_MS);
1524
- if (!opened.ok)
1525
- return withRunTimeoutGuidance(opened.outcome, requestId);
1526
- instanceId = opened.value.instanceId;
1527
- }
1528
- const action = await runAction(context, instanceId, actionId, requestId, parsed.input, RUN_REQUEST_TIMEOUT_MS);
1529
- if (!action.ok)
1530
- return withApprovalHint(withRunTimeoutGuidance(action.outcome, requestId));
1531
- return {
1532
- ...success({ ok: true, ...action.value }),
1533
- stderr: `→ Check: crewx app state ${action.value.instanceId}`,
1534
- };
1535
- }
1536
- //# sourceMappingURL=engine.js.map
1
+ "use strict";var xe=exports&&exports.__createBinding||(Object.create?(function(e,t,n,r){if(r===void 0)r=n;var o=Object.getOwnPropertyDescriptor(t,n);if(!o||("get"in o?!t.__esModule:o.writable||o.configurable)){o={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,r,o)}):(function(e,t,n,r){if(r===void 0)r=n;e[r]=t[n]}));var Ne=exports&&exports.__setModuleDefault||(Object.create?(function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}):function(e,t){e["default"]=t});var j=exports&&exports.__importStar||(function(){var e=function(t){e=Object.getOwnPropertyNames||function(n){var r=[];for(var o in n)if(Object.prototype.hasOwnProperty.call(n,o))r[r.length]=o;return r};return e(t)};return function(t){if(t&&t.__esModule)return t;var n={};if(t!=null){for(var r=e(t),o=0;o<r.length;o++)if(r[o]!=="default")xe(n,t,r[o])}Ne(n,t);return n}})();Object.defineProperty(exports,"__esModule",{value:true});exports.executeAppCommand=bt;const ie=require("node:crypto");const ae=require("node:child_process");const v=j(require("node:fs"));const je=j(require("node:http"));const $e=j(require("node:https"));const h=j(require("node:path"));const ue=require("node:url");const z=require("@crewx/shared/crewx-api-credential");const b=require("./receipt-wait.js");const C=require("./usage.js");const x=15e3;const Q=3e4;const Le=6e4;const ce=64*1024;const le="uic_";const Ue="/api/v1";const qe="/api/v1/app-dev/capabilities";const Me="/api/v1/app-dev/sessions";const Be=12;const Fe=13;const We=/^[A-Za-z0-9_-]{8,128}$/;const Ge=new Set(["127.0.0.1","[::1]","localhost"]);const Ve="http://localhost:8150";const Je="Do not work around this by clicking the screen or using another browser. Report that you could not use the App route.";const He="Do not retry. Report the failure.";const Xe="The server did not answer in time. Retry once; if it happens again, report it.";function l(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function p(e,t,n,r){let o=r;if(!o&&(e===3||e===9||e===10||e===11)){o=Je}else if(!o&&e===4){o=He}return{exitCode:e,payload:{ok:false,exit:e,error:{code:t,status:n}},...o?{stderr:o}:{}}}function P(e){return{exitCode:0,payload:e}}function $(){return p(2,"USAGE",null)}function pe(e){const t=e[0];if(t!=="list"&&t!=="open"&&t!=="state"&&t!=="run"&&t!=="receipt"){return null}const n=[];const r=new Map;for(let a=1;a<e.length;a+=1){const u=e[a];if(!u.startsWith("--")){n.push(u);continue}const c=u.indexOf("=");const d=c>=0?u.slice(2,c):u.slice(2);const f=c>=0?u.slice(c+1):void 0;if(d!=="input"&&d!=="request-id"&&d!=="wait"||r.has(d))return null;const y=f??e[a+1];if(y===void 0)return null;if(f===void 0)a+=1;r.set(d,y)}if(t==="list"&&n.length!==0||t==="open"&&n.length!==1||t==="state"&&(n.length!==1||!n[0].startsWith(le))||t==="run"&&n.length!==2||t==="receipt"&&n.length!==1){return null}if(t!=="run"&&(r.has("input")||r.has("request-id")))return null;if(t!=="receipt"&&r.has("wait"))return null;let o={};if(r.has("input")){const a=r.get("input");try{const u=JSON.parse(a==="-"?v.readFileSync(0,"utf8"):a);if(!l(u))return null;o=u}catch{return null}}const i=r.get("request-id");if(i!==void 0&&!We.test(i))return null;const s=r.has("wait")?(0,b.parseWaitSeconds)(r.get("wait")):void 0;if(s===null)return null;return{command:t,positional:n,input:o,...i!==void 0?{requestId:i}:{},...s!==void 0?{waitSeconds:s}:{}}}function Y(e){if(!e)return null;let t;try{t=new ue.URL(e)}catch{return null}if(!Ge.has(t.hostname)||t.protocol!=="http:"&&t.protocol!=="https:"||t.username!==""||t.password!==""||t.search!==""||t.hash!==""||t.pathname!==""&&t.pathname!=="/"){return null}return t}function F(e){return encodeURIComponent(e)}function Ke(e,t){return["api","v1","ws",F(e),"threads",F(t)].join("/")}function L(e,...t){return`/${e.basePath}/${t.map(F).join("/")}`}function k(e,t){if(!l(e)||!l(e["error"]))return t;const n=e["error"]["code"];return typeof n==="string"&&n.length>0?n:t}function W(e){const{status:t,data:n}=e;if(t>=300&&t<400)return p(9,"ENDPOINT_REJECTED",t);if(t===401)return p(4,k(n,"UNAUTHORIZED"),t);if(t===403)return p(5,k(n,"FORBIDDEN"),t);if(t===404)return p(6,k(n,"NOT_FOUND"),t);if(t===409){const r=k(n,"CONFLICT");if(r==="APPROVAL_ALREADY_PENDING"){const o=l(n)&&l(n["error"])?n["error"]:void 0;const i=o&&l(o["details"])?o["details"]:void 0;const s=o?.["receiptId"]??i?.["receiptId"]??(l(n)?n["receiptId"]:void 0);return{exitCode:8,payload:{ok:false,exit:8,error:{code:r,status:t},...typeof s==="string"?{receiptId:s}:{}}}}return p(7,r,t)}return p(1,k(n,"ERROR"),t)}function U(e,t=true){const n=l(e)&&typeof e["code"]==="string"?e["code"]:"";const r=process.env["CODEX_SANDBOX_NETWORK_DISABLED"]==="1";const o=!r&&(n==="EPERM"||n==="EACCES");if(r||o)return p(11,"SANDBOX_BLOCKED",null);if(t&&n==="ETIMEDOUT"&&l(e)&&e["connectionEstablished"]===true){return p(14,"TIMED_OUT",null,Xe)}return p(10,"SERVER_UNREACHABLE",null)}function Z(e,t){if(e.exitCode!==14)return e;return{...e,payload:{...e.payload,requestId:t},stderr:["The request timed out and its outcome is unknown.",`Retry the same command once with --request-id ${t}; 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 ${t}.`,"Do not report success or failure, click the screen, or use another route."].join("\n")}}function q(e,t,n,r,o,i=x){const s=e.protocol==="https:"?$e.request:je.request;const a=e.hostname==="[::1]"?"::1":e.hostname;const u=e.port?Number(e.port):void 0;const c=o===void 0?void 0:JSON.stringify(o);const d=s({protocol:e.protocol,hostname:a,...u!==void 0?{port:u}:{},method:n,path:r,headers:{Authorization:`Bearer ${t}`,Accept:"application/json",...c!==void 0?{"Content-Type":"application/json"}:{}}},m=>{const w=m.statusCode??0;if(w>=300&&w<400){m.destroy();clearTimeout(N);_({status:w,data:void 0,invalidJson:false,rawText:""});return}const A=[];m.setEncoding("utf8");m.on("data",O=>A.push(O));m.once("error",O=>I(O));m.once("end",()=>{clearTimeout(N);const O=A.join("");try{_({status:w,data:JSON.parse(O),invalidJson:false,rawText:O})}catch{_({status:w,data:void 0,invalidJson:true,rawText:O})}})});let f;let y=false;let _;let I;const S=new Promise((m,w)=>{_=m;I=w});const N=setTimeout(()=>{const m=new Error("Request timed out");m.code="ETIMEDOUT";m.connectionEstablished=y;d.destroy(m)},i);d.on("socket",m=>{const w=()=>{y=true};if(e.protocol==="https:"){const A=m;if(A.authorized)w();else A.once("secureConnect",w)}else{if(m.connecting)m.once("connect",w);else w()}m.once("error",A=>{f=A})});d.once("error",m=>{clearTimeout(N);I(f??m)});if(c!==void 0)d.write(c);d.end();return S}async function M(e,t,n,r,o=x){try{const i=await q(e.endpoint,e.ticket,t,n,r,o);if(i.status<200||i.status>=300){return{ok:false,outcome:W(i)}}if(i.invalidJson||!l(i.data)||i.data["success"]!==true){return{ok:false,outcome:p(1,k(i.data,"ERROR"),i.status)}}const s=i.data["data"];if(!l(s)){return{ok:false,outcome:p(1,"ERROR",i.status)}}return{ok:true,value:s,status:i.status}}catch(i){return{ok:false,outcome:U(i)}}}function de(e){return p(Be,"SERVER_TOO_OLD",e,"The running CrewX does not support App dev mode.")}function fe(e){if(e instanceof z.CrewxCredentialError){return p(e.exitCode,e.code,null,e.message)}return p(1,"ERROR",null)}function ze(){const e=process.env["CREWX_ENDPOINT"]?.trim()||Ve;return Y(e)}function G(e){try{return v.statSync(e).isDirectory()}catch{return false}}function ee(e){try{return v.statSync(e).isFile()}catch{return false}}function he(e=process.cwd()){let t=h.resolve(e);while(true){if(G(h.join(t,"apps"))||ee(h.join(t,"crewx.yaml"))){try{return v.realpathSync(t)}catch{return void 0}}const n=h.dirname(t);if(n===t)return void 0;t=n}}const ye="manifest.json";const me="ui-plugin.json";const Qe="dev.crewx";function we(e){try{const t=JSON.parse(v.readFileSync(e,"utf8"));return l(t)?t:void 0}catch{return void 0}}function te(e){const t=we(h.join(e,me));const n=h.join(e,ye);if(!ee(n))return t;const r=we(n);if(!r)return void 0;const o=r["_meta"];const i=l(o)?o[Qe]:void 0;const s={pluginId:r["name"],entry:l(i)?i["entry"]:void 0};if(t&&t["pluginId"]!==s["pluginId"])return void 0;return s}function Ee(e,t){const n=h.relative(e,t);return n!==""&&n!==".."&&!n.startsWith(`..${h.sep}`)&&!h.isAbsolute(n)}function Ye(e,t){if(!t||t==="."||t===".."||/[\\/]/.test(t)){return p(2,"USAGE",null,"App must be a folder name or pluginId.")}const n=h.join(e,"apps");const r=h.join(n,t);let o=G(r)?r:void 0;if(!o&&G(n)){try{for(const s of v.readdirSync(n,{withFileTypes:true})){if(!s.isDirectory())continue;const a=h.join(n,s.name);const u=te(a);if(u?.["pluginId"]===t){o=a;break}}}catch{}}let i=t;if(o){try{o=v.realpathSync(o)}catch{return p(6,"APP_INVALID",null)}if(!Ee(v.realpathSync(n),o)){return p(2,"USAGE",null,"App folder must be inside the workspace apps directory.")}const s=te(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:W(e)}}if(e.invalidJson||!l(e.data)||e.data["success"]!==true||!l(e.data["data"])){return{ok:false,outcome:p(1,k(e.data,"ERROR"),e.status)}}return{ok:true,value:e.data,status:e.status,rawText:e.rawText}}async function Ze(e,t){try{const n=await q(e,t,"GET",qe);if(n.status===404||n.status>=300&&n.status<400||n.invalidJson){return{ok:false,outcome:de(n.status)}}if(n.status<200||n.status>=300){return{ok:false,outcome:W(n)}}if(!l(n.data)||n.data["success"]!==true||!l(n.data["data"])||n.data["data"]["appDev"]!==1){return{ok:false,outcome:de(n.status)}}return{ok:true,value:n.data,status:n.status,rawText:n.rawText}}catch(n){return{ok:false,outcome:U(n,false)}}}async function ne(e){const t=ze();if(!t)return{ok:false,outcome:p(9,"ENDPOINT_REJECTED",null)};let n;try{n=(0,z.resolveCrewxApiCredential)("app-dev").token}catch(c){return{ok:false,outcome:fe(c)}}const r=await Ze(t,n);if(!r.ok)return{ok:false,outcome:r.outcome};const o=he();if(!o){return{ok:false,outcome:p(2,"WORKSPACE_NOT_FOUND",null,"No workspace root with apps/ or crewx.yaml was found.")}}const i=Ye(o,e);if("exitCode"in i)return{ok:false,outcome:i};let s;try{s=await q(t,n,"POST",Me,{workspacePath:o,appId:i.appId})}catch(c){return{ok:false,outcome:U(c,false)}}const a=ve(s);if(!a.ok)return{ok:false,outcome:a.outcome};const u=a.value["data"];if(!l(u)||typeof u["workspaceSlug"]!=="string"||typeof u["appId"]!=="string"||typeof u["devPath"]!=="string"||!u["devPath"].startsWith("/ws/")){return{ok:false,outcome:p(1,"ERROR",s.status)}}if(!G(i.appDirectory)){return{ok:false,outcome:p(6,"APP_INVALID",s.status)}}return{ok:true,context:{endpoint:t,token:n,workspacePath:o,appId:u["appId"],appDirectory:i.appDirectory,devPath:u["devPath"]}}}function V(e,...t){return`${Ue}${e.devPath}${t.length>0?`/${t.map(F).join("/")}`:""}`}async function J(e,t,n,r,o=x){try{const i=await q(e.endpoint,e.token,t,n,r,o);return ve(i)}catch(i){return{ok:false,outcome:U(i,false)}}}function et(e){const t=e["appId"]??e["id"];if(typeof t==="string"&&t.length>0)return t;const n=e["definition"];if(l(n)&&typeof n["id"]==="string")return n["id"];return void 0}function tt(e){const t=e["definitionHash"];if(typeof t==="string"&&t.length>0)return t;const n=e["definition"];if(l(n)&&typeof n["hash"]==="string"){return n["hash"]}return void 0}function nt(e){if(!l(e))return void 0;const t=et(e);const n=tt(e);if(!t||!n)return void 0;const r=e["agentActions"];const o=ot(e["agentAccess"]);return{appId:t,name:typeof e["name"]==="string"?e["name"]:"",description:typeof e["description"]==="string"?e["description"]:"",definitionHash:n,agentActions:Array.isArray(r)?r:[],...o===void 0?{}:{agentAccess:o}}}const rt=new Set(["allowed","approval_required","denied"]);function ot(e){if(!l(e))return void 0;const t=new Map;for(const[n,r]of Object.entries(e)){if(rt.has(r))t.set(n,r)}return t.size>0?t:void 0}async function H(e,t=x){const n=await M(e,"GET",L(e,"ui-instances","apps"),void 0,t);if(!n.ok)return n;const r=n.value["apps"];if(!Array.isArray(r)){return{ok:false,outcome:p(1,"ERROR",n.status)}}const o=r.map(nt);if(o.some(i=>i===void 0)){return{ok:false,outcome:p(1,"ERROR",n.status)}}return{ok:true,value:o,status:n.status}}function re(e,t){return e.find(n=>n.appId===t)}async function ge(e,t,n=x){const r=await M(e,"POST",L(e,"ui-instances","open"),{appId:t.appId,expectedDefinitionHash:t.definitionHash},n);if(!r.ok)return r;const o=l(r.value["instance"])?r.value["instance"]:r.value;if(!l(o)){return{ok:false,outcome:p(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:p(1,"ERROR",r.status)}}const a=l(r.value["ai"])?r.value["ai"]:l(o["ai"])?o["ai"]:void 0;return{ok:true,status:r.status,value:{appId:t.appId,definitionHash:t.definitionHash,instanceId:i,revision:s,state:a&&Object.prototype.hasOwnProperty.call(a,"state")?a["state"]:null}}}async function st(e,t){const n=await M(e,"GET",L(e,"ui-instances",t));if(!n.ok)return n;const r=n.value;const o=r["instanceId"];const i=r["revision"];if(typeof o!=="string"||typeof i!=="number"){return{ok:false,outcome:p(1,"ERROR",n.status)}}const s=l(r["ai"])?r["ai"]:void 0;const a=u=>{if(Object.prototype.hasOwnProperty.call(r,u))return{[u]:r[u]};if(s&&Object.prototype.hasOwnProperty.call(s,u))return{[u]:s[u]};return{}};return{ok:true,status:n.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")}}}async function it(e,t,n,r,o,i=x){const s=await M(e,"POST",L(e,"ui-instances",t,"actions"),{actionId:n,requestId:r,input:o},i);if(!s.ok)return s;const a=s.value["receipt"];if(!l(a)){return{ok:false,outcome:p(1,"ERROR",s.status)}}const u=a["receiptId"]??a["id"];const c=s.value["approval"];if(s.status===202&&l(c)){const _=c["approvalId"];const I=c["expiresAt"];const S=typeof a["requestId"]==="string"?a["requestId"]:typeof c["requestId"]==="string"?c["requestId"]:r;if(typeof u!=="string"||typeof _!=="string"||typeof I!=="string"&&typeof I!=="number"){return{ok:false,outcome:p(1,"ERROR",s.status)}}return{ok:false,outcome:{exitCode:8,payload:{ok:false,exit:8,error:{code:"APPROVAL_PENDING",status:202},receiptId:u,requestId:S,approvalId:_,expiresAt:I}}}}const d=a["status"]??(s.status===202?"requested":"settled");const f=s.value["resolve"];const y=l(f)&&l(f["ai"])?f["ai"]:void 0;return{ok:true,status:s.status,value:{receiptId:typeof u==="string"?u:"",requestId:typeof a["requestId"]==="string"?a["requestId"]:r,instanceId:t,actionId:typeof a["actionId"]==="string"?a["actionId"]:n,status:d,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"]}:{}}}}function at(e){const t=e.payload["error"];return e.exitCode===14||l(t)&&t["status"]===429}async function Ae(e,t){const n=await M(e,"GET",L(e,"ui-action-receipts",t));if(!n.ok){return{state:"failed",retryable:at(n.outcome),value:n.outcome}}const r=n.value["receipt"];if(!l(r))return{state:"failed",retryable:false,value:p(1,"ERROR",n.status)};return{state:r["status"]==="requested"?"pending":"settled",value:P({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(n.value,"result")?{result:n.value["result"]}:{},...Object.prototype.hasOwnProperty.call(n.value,"resultExpired")?{resultExpired:n.value["resultExpired"]}:{}})}}function ut(e,t){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 ${t}s. Do not end your turn: run crewx app receipt ${e} --wait=${(0,b.nextWaitSeconds)(t)} again.`}}function Ie(){const e=process.env["CREWX_TASK_TICKET"]??"";const t=process.env["CREWX_WORKSPACE_SLUG"]??"";const n=process.env["CREWX_THREAD_ID"]??"";if(!e.startsWith("crewxtk_")||!t||!n){return p(3,"NO_TICKET",null)}const r=Y(process.env["CREWX_ENDPOINT"]);if(!r)return p(9,"ENDPOINT_REJECTED",null);return{ticket:e,workspaceSlug:t,threadId:n,endpoint:r,basePath:Ke(t,n)}}function _e(e){return"exitCode"in e}function Oe(e){return String(e).padStart(2,"0")}function ct(e){const t=typeof e==="number"?new Date(e):typeof e==="string"?new Date(/^\d+$/.test(e)?Number(e):e):void 0;if(!t||Number.isNaN(t.getTime()))return void 0;return`${Oe(t.getHours())}:${Oe(t.getMinutes())}`}function lt(e){if(e.exitCode!==8)return e;const t=ct(e.payload["expiresAt"]);const n=e.payload["receiptId"];const r=[`\u2192 Waiting for the user's approval in the App window${t?` (expires ${t})`:""}. Do not end your turn: ending it cancels the request.`,...typeof n==="string"&&n.length>0?[`\u2192 Wait: crewx app receipt ${n} --wait=${b.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 dt(e){try{const t=v.realpathSync(e);const n=v.realpathSync(h.join(t,"SKILL.md"));if(!Ee(t,n)||!ee(n))return void 0;const r=v.openSync(n,"r");try{const o=new Uint8Array(pt);const i=v.readSync(r,o,0,o.length,0);return(0,C.manualBody)(Buffer.from(o.buffer,0,i).toString("utf8"))}finally{v.closeSync(r)}}catch{return void 0}}async function ft(e){const t=he();if(!t)return void 0;try{const n=await Promise.resolve().then(()=>j(require("@crewx/sdk")));const r=await n.discoverWorkspaceApps(t);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:h.join(t,o.relativePath),resolveAccess:n.resolveAgentAccess}}catch{return void 0}}async function ht(e){if(e.length!==2||e[1].startsWith("--"))return $();const t=e[1];const n=Ie();if(_e(n))return n;const r=await H(n);if(!r.ok)return r.outcome;const o=re(r.value,t);if(!o)return p(6,"MCP_APP_NOT_FOUND",null);const i=o.agentActions.filter(u=>typeof u==="string");const s=await ft(t);const a=s?(0,C.renderUsage)(dt(s.directory),(0,C.renderActionsSection)(t,s.candidate,(0,C.buildActionRows)(s.candidate,new Set(i),u=>s.resolveAccess(u),o.agentAccess)),s.candidate.manifest.csp?.connectDomains):(0,C.renderUsage)(void 0,(0,C.renderServerOnlyActions)(t,i));return{exitCode:0,payload:{ok:true},stdout:a}}function Re(e,t){if(e[0]!==t)return null;const n=[];let r=false;for(const o of e.slice(1)){if(o===(t==="dev"?"--no-open":"--json")){if(r)return null;r=true}else if(o.startsWith("--")){return null}else{n.push(o)}}if(n.length!==1)return null;return{appSelector:n[0],flag:r}}function Se(e){return`${JSON.stringify(e.payload)}
2
+ `}function yt(e){const t=e["detail"];if(e["rule"]!=="manifest"||!l(t))return"";const n=typeof t["path"]==="string"?t["path"]:"";const r=typeof t["rule"]==="string"?t["rule"]:"";if(!r)return"";return` ${n?`${n} `:""}(${r})`}async function mt(e){const t=Re(e,"check");if(!t)return $();const n=await ne(t.appSelector);if(!n.ok)return n.outcome;const r=await J(n.context,"GET",V(n.context,"check"));if(!r.ok)return r.outcome;const o=r.value["data"];if(!l(o))return p(1,"ERROR",r.status);const i=l(o["summary"])?o["summary"]:{};const s=Array.isArray(o["items"])?o["items"]:[];const a=typeof i["errors"]==="number"?i["errors"]:s.filter(f=>l(f)&&f["severity"]==="error").length;const u=typeof i["warnings"]==="number"?i["warnings"]:s.filter(f=>l(f)&&f["severity"]==="warning").length;const c=a===0?0:Fe;if(t.flag){return{exitCode:c,payload:r.value,stdout:`${r.rawText??Se({exitCode:c,payload:r.value})}`+(r.rawText?.endsWith("\n")?"":"\n"),suppressOutput:true}}const d=s.flatMap(f=>{if(!l(f))return[];const y=f["severity"];const _=y==="error"?"\u2715":y==="ok"?"\u2713":"!";const I=typeof f["where"]==="string"?f["where"]:"";const S=typeof f["code"]==="string"?f["code"]:"";return[`${_} ${I} ${S}${yt(f)}`]});d.push(`Summary: ${a} errors, ${u} warnings`);return{exitCode:c,payload:{},stdout:`${d.join("\n")}
3
+ `,suppressOutput:true}}async function wt(e){let t;try{t=(0,z.resolveCrewxApiCredential)("pairing").token}catch(r){return fe(r)}let n;try{n=await q(e.endpoint,t,"POST","/api/v1/auth/local/code")}catch(r){return U(r,false)}if(n.status<200||n.status>=300){return W(n)}if(n.invalidJson||!l(n.data)||typeof n.data["url"]!=="string"){return p(1,"PAIRING_CODE_INVALID",n.status)}try{const r=new ue.URL(n.data["url"]);const o=Y(r.origin);const i=r.hash.match(/^#pair=(.+)$/);if(!o||!i)return p(1,"PAIRING_CODE_INVALID",n.status);return`${o.origin}${e.devPath}#pair=${encodeURIComponent(i[1])}`}catch{return p(1,"PAIRING_CODE_INVALID",n.status)}}async function Et(e){const t=process.platform==="darwin"?"open":process.platform==="win32"?"explorer.exe":"xdg-open";const n=(0,ae.spawn)(t,[e],{detached:true,stdio:"ignore"});await new Promise((r,o)=>{n.once("error",o);n.once("spawn",()=>{n.unref();r()})})}async function vt(e){const t=pe(["run",...e.slice(2)]);if(!t||t.command!=="run")return $();const[n,r]=t.positional;const o=await ne(n);if(!o.ok)return o.outcome;const i=await J(o.context,"GET",V(o.context,"state"));if(!i.ok)return i.outcome;const s=i.value["data"];if(!l(s)||typeof s["revision"]!=="number"){return p(1,"APP_DEV_RESPONSE_INVALID",i.status)}const a=t.requestId??`cxa_${(0,ie.randomBytes)(16).toString("base64url")}`;const u=await J(o.context,"POST",V(o.context,"actions"),{actionId:r,requestId:a,input:t.input,expectedRevision:s["revision"]},Le);if(!u.ok)return u.outcome;const c=u.value["data"];const d=l(c)&&l(c["receipt"])?c["receipt"]:void 0;return{exitCode:d?.["outcome"]==="succeeded"?0:1,payload:u.value,stdout:`${u.rawText??Se({exitCode:0,payload:u.value})}`+(u.rawText?.endsWith("\n")?"":"\n"),suppressOutput:true}}function gt(e){try{const t=JSON.parse(v.readFileSync(h.join(e,"package.json"),"utf8"));if(!l(t)||!l(t["scripts"]))return false;const n=t["scripts"]["build"];return typeof n==="string"&&n.trim().length>0}catch{return false}}function De(e){const t=new Set([h.normalize(ye),h.normalize(me)]);const n=te(e);const r=n?.["entry"];if(typeof r==="string"&&r.length>0&&!h.isAbsolute(r)){const o=h.normalize(r);if(o!==".."&&!o.startsWith(`..${h.sep}`))t.add(o)}return t}function At(e,t){try{const n=v.statSync(h.join(e,t));return`${n.size}:${n.mtimeMs}:${n.ino}`}catch{return null}}function Te(e,t){return new Map([...t].map(n=>[n,At(e,n)]))}function It(e,t){const n=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>ce?a.subarray(a.length-ce).toString("utf8"):a.toString("utf8")};return new Promise(s=>{const a=(0,ae.spawn)("npm",["run","build"],{cwd:e,env:process.env,stdio:["ignore","pipe","pipe"]});t(a);a.stdout?.on("data",i);a.stderr?.on("data",i);const u=c=>{if(o)return;o=true;t(void 0);s({ok:c,ms:Math.max(0,Date.now()-n),output:r})};a.once("error",c=>{i(`${c.message}
4
+ `);u(false)});a.once("close",c=>u(c===0))})}function _t(e){const t=e.payload["error"];return l(t)&&typeof t["code"]==="string"?t["code"]:"ERROR"}async function Ot(e,t){const n=await J(e,"POST",V(e,"reload"),t);if(!n.ok)return{errorCode:_t(n.outcome)};const r=n.value["data"];return l(r)&&typeof r["generation"]==="number"?{generation:r["generation"]}:{errorCode:"APP_DEV_RESPONSE_INVALID"}}async function Rt(e,t){const n=process.ppid;let r;try{r=v.watch(e.appDirectory,{recursive:true},(E,g)=>{O(g)})}catch(E){return p(1,"WATCH_FAILED",null,E instanceof Error?E.message:"Unable to watch the App folder.")}let o=false;let i=false;let s=false;let a=false;let u="";let c="";let d;let f;let y;let _={exitCode:0,payload:{},suppressOutput:true};const I=new Map;let S;const N=new Promise(E=>{S=E});const m=E=>{process.stdout.write(`${E}
5
+ `)};const w=()=>{if(o)return;o=true;if(d)clearTimeout(d);if(f)clearInterval(f);process.removeListener("SIGINT",A);process.removeListener("SIGTERM",A);S(_)};const A=()=>{if(i)return;i=true;r.close();if(d)clearTimeout(d);if(y&&y.exitCode===null)y.kill("SIGTERM");if(!s)w()};const O=E=>{if(i||o)return;const g=E===null?"":h.normalize(E.toString());const D=g.split(h.sep);if(D.some(T=>T==="node_modules"||T===".crewx"||T==="dist"))return;const K=t?De(e.appDirectory):new Set;if(y&&K.has(g))return;const R=I.get(g);if(R!==void 0){if(Date.now()<=R)return;I.delete(g)}if(s){a=true;u=g||"App files";return}c=g||"App files";if(d)clearTimeout(d);d=setTimeout(()=>{d=void 0;const T=c||"App files";c="";void X(T,false)},200)};const se=async E=>{const g=await Ot(e,E);return g.generation===void 0?`failed ${g.errorCode??"ERROR"}`:String(g.generation)};const X=async(E,g)=>{if(i||o)return;s=true;if(t){const D=De(e.appDirectory);const K=Te(e.appDirectory,D);const R=await It(e.appDirectory,B=>{y=B});const T=Te(e.appDirectory,D);for(const[B,Ce]of T){if(K.get(B)!==Ce){I.set(B,Date.now()+1e3)}}if(i){s=false;w();return}const ke={reason:"build",build:{ok:R.ok,ms:R.ms,...!R.ok?{output:R.output}:{}}};const be=await se(ke);m(`${g?"initial":E} -> build ${R.ok?"ok":"failed"} ${R.ms}ms -> reload ${be}`)}else if(!g){const D=await se({reason:"manual"});m(`${E} -> reload ${D}`)}s=false;if(i){w();return}if(a){const D=u||"App files";a=false;u="";void X(D,false);return}if(i)w()};r.once("error",E=>{_=p(1,"WATCH_FAILED",null,E.message);A()});process.once("SIGINT",A);process.once("SIGTERM",A);f=setInterval(()=>{if(process.ppid!==n)A()},1e3);f.unref();if(t)void X("initial",true);return N}async function St(e){const t=Re(e,"dev");if(!t)return $();const n=await ne(t.appSelector);if(!n.ok)return n.outcome;const r=`${n.context.endpoint.origin}${n.context.devPath}`;process.stdout.write(`${r}
6
+ `);if(!t.flag){const o=await wt(n.context);if(typeof o!=="string")return o;try{await Et(o)}catch(i){return p(1,"BROWSER_OPEN_FAILED",null,i instanceof Error?i.message:"Could not open the App dev tab.")}}return Rt(n.context,gt(n.context.appDirectory))}const Pe="usage: crewx app build [<appDir>] [--check | --watch]";const Dt=["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 Tt=[Pe,""," 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 Pt(){const e=[h.join(__dirname,"..","..","build-app","load.cjs"),h.join(__dirname,"..","build-app","load.cjs")];const t=e.find(n=>v.existsSync(n));if(!t)return Promise.reject(new Error("the App build is not part of this install"));return require(t).loadAppBuilder()}function oe(e){return e instanceof Error?e.message:String(e)}async function kt(e){if(e.slice(1).some(s=>s==="--help"||s==="-h")){return{exitCode:0,payload:{ok:true},stdout:Tt}}let t;try{t=await Pt()}catch(s){return p(1,"BUILD_UNAVAILABLE",null,oe(s))}let n;try{n=t.parseArguments(e.slice(1))}catch(s){return p(2,"USAGE",null,`${oe(s)} \u2192 ${Pe}`)}const{appDirectory:r}=n;const o=h.relative(process.cwd(),r);const i=o===""?"crewx app build":`crewx app build ${o}`;if(n.watch){const s=new AbortController;const a=()=>s.abort();process.once("SIGINT",a);process.once("SIGTERM",a);try{await t.watchApp(r,u=>process.stdout.write(`${u}
7
+ `),{signal:s.signal})}finally{process.removeListener("SIGINT",a);process.removeListener("SIGTERM",a)}return{exitCode:0,payload:{},suppressOutput:true}}try{const s=await t.buildApp({appDirectory:r,check:n.check});if(n.check&&s.staleFiles.length>0){return{...p(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(n.check){return P({ok:true,appDir:r,check:true,inSync:true,files:s.files,actions:s.actions})}const a=h.basename(r);const u=h.basename(h.dirname(r))==="apps";return{...P({ok:true,appDir:r,files:s.files,actions:s.actions}),stderr:u?`\u2192 Next: crewx app check ${a}`:`\u2192 Next: put ${a} in the workspace apps/ folder, then crewx app check ${a}`}}catch(s){return p(1,"BUILD_FAILED",null,oe(s))}}async function bt(e,{clock:t=b.realClock}={}){if(e.length===1&&(e[0]==="--help"||e[0]==="-h")){return{exitCode:0,payload:{ok:true},stdout:Dt}}if(e[0]==="build")return kt(e);if(e[0]==="dev")return St(e);if(e[0]==="check")return mt(e);if(e[0]==="run"&&e[1]==="--dev")return vt(e);if(e[0]==="usage")return ht(e);const n=pe(e);if(!n)return $();const r=Ie();if(_e(r))return r;if(n.command==="list"){const c=await H(r);if(!c.ok)return c.outcome;return P({ok:true,apps:c.value})}if(n.command==="receipt"){const c=n.positional[0];const d=(0,b.clampWaitSeconds)(n.waitSeconds??0);if(d===0)return(await Ae(r,c)).value;const f=await(0,b.pollUntilSettled)(()=>Ae(r,c),d,t);if(f.state!=="pending")return f.value;return ut(c,d)}if(n.command==="state"){const c=await st(r,n.positional[0]);if(!c.ok)return c.outcome;return P({ok:true,...c.value})}if(n.command==="open"){const c=n.positional[0];const d=await H(r);if(!d.ok)return d.outcome;const f=re(d.value,c);if(!f)return p(6,"MCP_APP_NOT_FOUND",null);const y=await ge(r,f);if(!y.ok)return y.outcome;return P({ok:true,...y.value})}const[o,i]=n.positional;const s=n.requestId??`cxa_${(0,ie.randomBytes)(16).toString("base64url")}`;let a=o;if(!o.startsWith(le)){const c=await H(r,Q);if(!c.ok)return Z(c.outcome,s);const d=re(c.value,o);if(!d)return p(6,"MCP_APP_NOT_FOUND",null);const f=await ge(r,d,Q);if(!f.ok)return Z(f.outcome,s);a=f.value.instanceId}const u=await it(r,a,i,s,n.input,Q);if(!u.ok)return lt(Z(u.outcome,s));return{...P({ok:true,...u.value}),stderr:`\u2192 Check: crewx app state ${u.value.instanceId}`}}