@inneranimalmedia/agentsam-sdk 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTSAM.md +55 -0
- package/README.md +12 -8
- package/bin/agentsam +2 -0
- package/docs/AGENTSAM_ASTRA_OPENAI_INTEGRATION.md +1363 -0
- package/docs/CLI_SHELL.md +163 -53
- package/docs/RELEASES.md +16 -7
- package/package.json +20 -8
- package/packages/connectors/cloudflare/package.json +10 -0
- package/packages/connectors/cloudflare/src/index.js +127 -0
- package/packages/connectors/cloudflare/src/owner.js +76 -0
- package/packages/connectors/cloudflare/src/routes.js +223 -0
- package/packages/connectors/cloudflare/src/vault.js +80 -0
- package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
- package/packages/identity/package.json +2 -2
- package/packages/identity/src/contracts/auth-config.js +18 -7
- package/packages/identity/tests/auth-config.test.mjs +9 -5
- package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
- package/protocol/README.md +1 -0
- package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
- package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
- package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
- package/protocol/capabilities/manifest.json +47 -0
- package/protocol/context/context-budget.schema.json +10 -15
- package/protocol/context/context-item.schema.json +4 -5
- package/protocol/context/resolved-context-pack.schema.json +19 -14
- package/protocol/models/README.md +373 -0
- package/protocol/models/model-inventory-v2.schema.json +212 -0
- package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
- package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
- package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
- package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
- package/skills/catalog.json +18 -0
- package/src/agent/capability-adapter.js +25 -13
- package/src/agent/index.js +1 -0
- package/src/agent/responses-runner.js +325 -0
- package/src/cli.js +98 -28
- package/src/cloudflare/cpu-profile.js +115 -0
- package/src/cloudflare/index.js +14 -0
- package/src/cloudflare/wrangler.js +132 -0
- package/src/commands/account-auth.js +47 -0
- package/src/commands/cloudflare.js +58 -0
- package/src/commands/connections.js +93 -0
- package/src/commands/context-economics.js +114 -0
- package/src/commands/deploy.js +39 -3
- package/src/commands/eval.js +63 -0
- package/src/commands/interactive.js +2 -5
- package/src/commands/models.js +85 -40
- package/src/commands/preferences.js +101 -59
- package/src/commands/resume.js +67 -0
- package/src/commands/security.js +5 -3
- package/src/commands/shell.js +370 -109
- package/src/commands/tunnel.js +2 -2
- package/src/commands/whoami.js +86 -0
- package/src/context/budget.js +68 -6
- package/src/context/index.js +3 -1
- package/src/context/rehydrate.js +35 -0
- package/src/context/resolve.js +44 -12
- package/src/errors/diagnostic.js +160 -0
- package/src/errors/index.js +9 -0
- package/src/eval/context.js +191 -0
- package/src/eval/index.js +1 -0
- package/src/index.js +55 -1
- package/src/lib/account-session.js +98 -0
- package/src/lib/agent-instructions.js +73 -0
- package/src/lib/auth.js +4 -0
- package/src/lib/cli-preferences.js +28 -24
- package/src/lib/deploy/git-guard.js +69 -0
- package/src/lib/deploy/health.js +57 -0
- package/src/lib/deploy/local-studio.js +283 -0
- package/src/lib/deploy/secret-scan.js +65 -0
- package/src/lib/detect-context.js +2 -2
- package/src/lib/execution-approvals.js +59 -0
- package/src/lib/local-sessions.js +127 -0
- package/src/lib/provider-credentials.js +83 -0
- package/src/lib/scaffold/templates/worker-api/index.js +101 -20
- package/src/lib/scaffold/wizards/worker-api.js +27 -11
- package/src/lib/slash-commands.js +22 -16
- package/src/models/catalog.js +135 -0
- package/src/models/index.js +7 -0
- package/src/providers/index.js +5 -0
- package/src/providers/openai-responses.js +275 -0
- package/src/security/process.js +35 -9
- package/src/telemetry/contracts.js +203 -0
- package/src/telemetry/events.js +48 -0
- package/src/telemetry/index.js +8 -0
- package/src/tools/hydrate.js +35 -0
- package/src/tools/index.js +1 -0
- package/src/ui/boot.js +15 -17
- package/test/account-session.test.mjs +36 -0
- package/test/cli-preferences.test.mjs +26 -5
- package/test/cloudflare-connector.test.mjs +96 -0
- package/test/cloudflare-runtime.test.mjs +75 -0
- package/test/context.test.mjs +61 -12
- package/test/deploy-health-scan.test.mjs +67 -0
- package/test/error-diagnostics.test.mjs +59 -0
- package/test/eval-context.test.mjs +37 -0
- package/test/execution-approvals.test.mjs +27 -0
- package/test/local-sessions.test.mjs +42 -0
- package/test/local-studio-deploy.test.mjs +83 -0
- package/test/model-catalog.test.mjs +43 -0
- package/test/models.test.mjs +30 -16
- package/test/npm10-lock.test.mjs +29 -0
- package/test/openai-responses.test.mjs +95 -0
- package/test/provider-credentials.test.mjs +52 -0
- package/test/rehydrate.test.mjs +25 -0
- package/test/release-hygiene.test.mjs +4 -4
- package/test/responses-runner.test.mjs +148 -0
- package/test/shell.test.mjs +47 -20
- package/test/smoke.mjs +4 -1
- package/test/telemetry.test.mjs +79 -0
- package/test/tools-search.test.mjs +14 -1
- package/test/whoami-resume.test.mjs +56 -0
package/src/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { runOllama } from './commands/ollama.js';
|
|
|
15
15
|
import { runModels } from './commands/models.js';
|
|
16
16
|
import { runTunnel } from './commands/tunnel.js';
|
|
17
17
|
import { runDeploy } from './commands/deploy.js';
|
|
18
|
+
import { runConnections } from './commands/connections.js';
|
|
18
19
|
import { runIdentityPreview } from './commands/identity-preview.js';
|
|
19
20
|
import { runIdentityInit } from './commands/identity-init.js';
|
|
20
21
|
import { runContext } from './commands/context.js';
|
|
@@ -30,14 +31,26 @@ import { runSecurity } from './commands/security.js';
|
|
|
30
31
|
import { runRecon } from './commands/recon.js';
|
|
31
32
|
import { runCad } from './commands/cad.js';
|
|
32
33
|
import { runSkills } from './commands/skills.js';
|
|
34
|
+
import { runEval } from './commands/eval.js';
|
|
35
|
+
import { runCloudflare } from './commands/cloudflare.js';
|
|
36
|
+
import { runWhoami } from './commands/whoami.js';
|
|
37
|
+
import { runResume } from './commands/resume.js';
|
|
38
|
+
import { runLogin, runLogout } from './commands/account-auth.js';
|
|
33
39
|
import { applyPresetSelection, runAdd, runCapabilities, runDev, runInspect } from './commands/product.js';
|
|
34
40
|
import { listPresets, resolvePreset } from './presets/index.js';
|
|
35
41
|
import fs from 'node:fs';
|
|
36
42
|
import { repositoryRoot } from './knowledge/config.js';
|
|
37
|
-
import {
|
|
43
|
+
import { resolveAccountSdkKey } from './lib/account-session.js';
|
|
44
|
+
import { renderDiagnosticError } from './errors/index.js';
|
|
38
45
|
|
|
39
46
|
const VERSION = pkg.version;
|
|
40
47
|
|
|
48
|
+
function reportCliError(error) {
|
|
49
|
+
if (error?.reported) return;
|
|
50
|
+
const rendered = renderDiagnosticError(error).split('\n').map((line) => ` ${line}`).join('\n');
|
|
51
|
+
console.error(`\n${rendered}\n`);
|
|
52
|
+
}
|
|
53
|
+
|
|
41
54
|
function createPrompt() {
|
|
42
55
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
43
56
|
return {
|
|
@@ -76,7 +89,13 @@ function printHelp() {
|
|
|
76
89
|
agentsam security Dependency scan, log triage, and verified repair (--help)
|
|
77
90
|
agentsam status [--json] Live local Git + DB + API + PTY status
|
|
78
91
|
agentsam db init|status Manage the project-local SQLite database
|
|
79
|
-
agentsam models
|
|
92
|
+
agentsam models Verify configured providers and selectable hosted/local models
|
|
93
|
+
agentsam login Authenticate IAM and persist a secure machine-local session
|
|
94
|
+
agentsam logout Remove the local IAM session; provider keys stay untouched
|
|
95
|
+
agentsam whoami [--json] Authenticated IAM identity + safe credential status
|
|
96
|
+
agentsam resume [session] Resume a saved Agent Sam session; omit id for picker
|
|
97
|
+
agentsam eval context Offline context-strategy/economics fixtures (--help)
|
|
98
|
+
agentsam cloudflare Native Wrangler reads + Worker CPU profile analysis (--help)
|
|
80
99
|
agentsam start-local Local PTY on ws://127.0.0.1:3099 (no tunnel, no Cloudflare)
|
|
81
100
|
agentsam ollama Opt-in local Ollama setup/status/model management
|
|
82
101
|
agentsam shell Interactive Agent Sam slash-command shell
|
|
@@ -143,11 +162,13 @@ function parseInitArgs(argv) {
|
|
|
143
162
|
}
|
|
144
163
|
|
|
145
164
|
function parseDeployArgs(argv) {
|
|
146
|
-
const opts = { target: '', accountId: '' };
|
|
165
|
+
const opts = { target: '', accountId: '', dryRun: false, plan: false };
|
|
147
166
|
for (let i = 0; i < argv.length; i += 1) {
|
|
148
167
|
const arg = argv[i];
|
|
149
168
|
if (arg === '--target') opts.target = argv[++i] || '';
|
|
150
169
|
else if (arg === '--account-id') opts.accountId = argv[++i] || '';
|
|
170
|
+
else if (arg === '--dry-run') opts.dryRun = true;
|
|
171
|
+
else if (arg === '--plan') opts.plan = true;
|
|
151
172
|
}
|
|
152
173
|
return opts;
|
|
153
174
|
}
|
|
@@ -206,7 +227,7 @@ async function runLocalInit(config) {
|
|
|
206
227
|
console.log(` ${step}`);
|
|
207
228
|
}
|
|
208
229
|
|
|
209
|
-
const sdkKey =
|
|
230
|
+
const sdkKey = resolveAccountSdkKey({ env: process.env }).value;
|
|
210
231
|
if (prompt && sdkKey) {
|
|
211
232
|
console.log('\n Optional — BYOK keys for IAM dashboard Agent Sam (skip with Enter):\n');
|
|
212
233
|
await promptOptionalByokKeys(sdkKey, prompt);
|
|
@@ -262,12 +283,12 @@ async function initInteractive(partial = {}) {
|
|
|
262
283
|
if (runTarget !== 'local') {
|
|
263
284
|
const { detectContext, missingForInit } = await import('./lib/detect-context.js');
|
|
264
285
|
const ctx = await detectContext();
|
|
265
|
-
if (missingForInit(ctx,
|
|
286
|
+
if (missingForInit(ctx, resolveAccountSdkKey({ env: process.env }).value, { runTarget }).length) {
|
|
266
287
|
printContextSummary(ctx);
|
|
267
288
|
}
|
|
268
289
|
}
|
|
269
290
|
|
|
270
|
-
const prompt =
|
|
291
|
+
const prompt = resolveAccountSdkKey({ env: process.env }).value ? createPrompt() : null;
|
|
271
292
|
try {
|
|
272
293
|
await runLocalInit({ projectName, lane: laneKey, runTarget, prompt });
|
|
273
294
|
} finally {
|
|
@@ -297,7 +318,7 @@ if (command === '--version' || command === '-v') {
|
|
|
297
318
|
try { await runInteractive(); }
|
|
298
319
|
catch (e) {
|
|
299
320
|
if (e?.code !== 'AGENTSAM_SETUP_CANCELLED') {
|
|
300
|
-
|
|
321
|
+
reportCliError(e);
|
|
301
322
|
process.exitCode = 1;
|
|
302
323
|
}
|
|
303
324
|
}
|
|
@@ -315,52 +336,94 @@ if (command === '--version' || command === '-v') {
|
|
|
315
336
|
applyPresetSelection(created.dir, preset);
|
|
316
337
|
console.log(` ✓ Preset ${preset.id}\n ✓ Features ${preset.features.join(', ') || 'none'}\n ✓ Capabilities ${preset.capabilities.length}\n`);
|
|
317
338
|
}
|
|
318
|
-
} catch (e) {
|
|
339
|
+
} catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
319
340
|
} else if (command === 'add') {
|
|
320
341
|
try { await runAdd(rest); }
|
|
321
|
-
catch (e) {
|
|
342
|
+
catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
322
343
|
} else if (command === 'dev') {
|
|
323
344
|
try { await runDev(rest); }
|
|
324
|
-
catch (e) {
|
|
345
|
+
catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
325
346
|
} else if (command === 'inspect') {
|
|
326
347
|
try { await runInspect(rest); }
|
|
327
|
-
catch (e) {
|
|
348
|
+
catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
328
349
|
} else if (command === 'capabilities') {
|
|
329
350
|
try { await runCapabilities(rest); }
|
|
330
|
-
catch (e) {
|
|
351
|
+
catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
331
352
|
} else if (command === 'context') {
|
|
332
353
|
try {
|
|
333
354
|
await runContext(rest);
|
|
334
355
|
} catch (e) {
|
|
335
|
-
|
|
356
|
+
reportCliError(e);
|
|
336
357
|
process.exit(1);
|
|
337
358
|
}
|
|
338
359
|
} else if (command === 'status') {
|
|
339
360
|
try {
|
|
340
361
|
await runStatus(rest);
|
|
341
362
|
} catch (e) {
|
|
342
|
-
|
|
363
|
+
reportCliError(e);
|
|
343
364
|
process.exit(1);
|
|
344
365
|
}
|
|
345
366
|
} else if (command === 'db') {
|
|
346
367
|
try {
|
|
347
368
|
await runDb(rest);
|
|
348
369
|
} catch (e) {
|
|
349
|
-
|
|
370
|
+
reportCliError(e);
|
|
350
371
|
process.exit(1);
|
|
351
372
|
}
|
|
352
373
|
} else if (command === 'models') {
|
|
353
374
|
try {
|
|
354
375
|
await runModels(rest);
|
|
355
376
|
} catch (e) {
|
|
356
|
-
|
|
377
|
+
reportCliError(e);
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
} else if (command === 'eval') {
|
|
381
|
+
try {
|
|
382
|
+
await runEval(rest);
|
|
383
|
+
} catch (e) {
|
|
384
|
+
reportCliError(e);
|
|
357
385
|
process.exit(1);
|
|
358
386
|
}
|
|
387
|
+
} else if (command === 'cloudflare' || command === 'cf') {
|
|
388
|
+
try {
|
|
389
|
+
await runCloudflare(rest);
|
|
390
|
+
} catch (e) {
|
|
391
|
+
if (!e?.reported) reportCliError(e);
|
|
392
|
+
process.exitCode = 1;
|
|
393
|
+
}
|
|
394
|
+
} else if (command === 'login') {
|
|
395
|
+
try {
|
|
396
|
+
await runLogin(rest);
|
|
397
|
+
} catch (e) {
|
|
398
|
+
reportCliError(e);
|
|
399
|
+
process.exitCode = 1;
|
|
400
|
+
}
|
|
401
|
+
} else if (command === 'logout') {
|
|
402
|
+
try {
|
|
403
|
+
runLogout(rest);
|
|
404
|
+
} catch (e) {
|
|
405
|
+
reportCliError(e);
|
|
406
|
+
process.exitCode = 1;
|
|
407
|
+
}
|
|
408
|
+
} else if (command === 'whoami') {
|
|
409
|
+
try {
|
|
410
|
+
await runWhoami(rest);
|
|
411
|
+
} catch (e) {
|
|
412
|
+
reportCliError(e);
|
|
413
|
+
process.exitCode = 1;
|
|
414
|
+
}
|
|
415
|
+
} else if (command === 'resume') {
|
|
416
|
+
try {
|
|
417
|
+
await runResume(rest);
|
|
418
|
+
} catch (e) {
|
|
419
|
+
reportCliError(e);
|
|
420
|
+
process.exitCode = 1;
|
|
421
|
+
}
|
|
359
422
|
} else if (command === 'shell') {
|
|
360
423
|
try {
|
|
361
424
|
await runShell(rest);
|
|
362
425
|
} catch (e) {
|
|
363
|
-
|
|
426
|
+
reportCliError(e);
|
|
364
427
|
process.exit(1);
|
|
365
428
|
}
|
|
366
429
|
} else if (command === 'start-local') {
|
|
@@ -369,35 +432,42 @@ if (command === '--version' || command === '-v') {
|
|
|
369
432
|
try {
|
|
370
433
|
await runOllama(rest);
|
|
371
434
|
} catch (e) {
|
|
372
|
-
|
|
435
|
+
reportCliError(e);
|
|
373
436
|
process.exitCode = 1;
|
|
374
437
|
}
|
|
375
438
|
} else if (command === 'tunnel') {
|
|
376
439
|
try {
|
|
377
440
|
await runTunnel(rest);
|
|
378
441
|
} catch (e) {
|
|
379
|
-
|
|
442
|
+
reportCliError(e);
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
} else if (command === 'connections' || command === 'connection') {
|
|
446
|
+
try {
|
|
447
|
+
await runConnections(rest);
|
|
448
|
+
} catch (e) {
|
|
449
|
+
reportCliError(e);
|
|
380
450
|
process.exit(1);
|
|
381
451
|
}
|
|
382
452
|
} else if (command === 'deploy') {
|
|
383
453
|
try {
|
|
384
454
|
await runDeploy(parseDeployArgs(rest));
|
|
385
455
|
} catch (e) {
|
|
386
|
-
|
|
456
|
+
reportCliError(e);
|
|
387
457
|
process.exit(1);
|
|
388
458
|
}
|
|
389
459
|
} else if (command === 'dockerize') {
|
|
390
460
|
try {
|
|
391
461
|
await runDockerize(rest);
|
|
392
462
|
} catch (e) {
|
|
393
|
-
|
|
463
|
+
reportCliError(e);
|
|
394
464
|
process.exit(1);
|
|
395
465
|
}
|
|
396
466
|
} else if (command === 'cad') {
|
|
397
467
|
try {
|
|
398
468
|
await runCad(rest);
|
|
399
469
|
} catch (e) {
|
|
400
|
-
|
|
470
|
+
reportCliError(e);
|
|
401
471
|
process.exitCode = 1;
|
|
402
472
|
}
|
|
403
473
|
} else if (command === 'security' || command === 'sca') {
|
|
@@ -406,7 +476,7 @@ if (command === '--version' || command === '-v') {
|
|
|
406
476
|
try {
|
|
407
477
|
runSkills(rest);
|
|
408
478
|
} catch (e) {
|
|
409
|
-
|
|
479
|
+
reportCliError(e);
|
|
410
480
|
process.exitCode = 1;
|
|
411
481
|
}
|
|
412
482
|
} else if (command === 'merkle') {
|
|
@@ -419,14 +489,14 @@ if (command === '--version' || command === '-v') {
|
|
|
419
489
|
try {
|
|
420
490
|
await runMini(rest);
|
|
421
491
|
} catch (e) {
|
|
422
|
-
|
|
492
|
+
reportCliError(e);
|
|
423
493
|
process.exitCode = 1;
|
|
424
494
|
}
|
|
425
495
|
} else if (['index', 'search', 'repo'].includes(command)) {
|
|
426
496
|
try {
|
|
427
497
|
const commands = await import('./commands/knowledge.js');
|
|
428
498
|
await ({ index: commands.runKnowledge, search: commands.runSearch, repo: commands.runRepository })[command](rest);
|
|
429
|
-
} catch (e) {
|
|
499
|
+
} catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
430
500
|
} else if (command === 'init') {
|
|
431
501
|
try {
|
|
432
502
|
const existing = !rest.includes('--name') && (rest.includes('.') || rest.includes('--existing') || rest.includes('--cwd') || fs.existsSync(path.join(repositoryRoot(), '.git')));
|
|
@@ -434,21 +504,21 @@ if (command === '--version' || command === '-v') {
|
|
|
434
504
|
else if (rest.includes('--help') || rest.includes('-h')) printHelp();
|
|
435
505
|
else if (rest.some((a) => a.startsWith('--'))) await initFromArgs(rest);
|
|
436
506
|
else await initInteractive({});
|
|
437
|
-
} catch (e) {
|
|
507
|
+
} catch (e) { reportCliError(e); process.exitCode = 1; }
|
|
438
508
|
} else if (command === 'identity') {
|
|
439
509
|
const sub = rest[0];
|
|
440
510
|
if (sub === 'preview') {
|
|
441
511
|
try {
|
|
442
512
|
await runIdentityPreview(rest.slice(1));
|
|
443
513
|
} catch (e) {
|
|
444
|
-
|
|
514
|
+
reportCliError(e);
|
|
445
515
|
process.exit(1);
|
|
446
516
|
}
|
|
447
517
|
} else if (sub === 'init') {
|
|
448
518
|
try {
|
|
449
519
|
await runIdentityInit(rest);
|
|
450
520
|
} catch (e) {
|
|
451
|
-
|
|
521
|
+
reportCliError(e);
|
|
452
522
|
process.exit(1);
|
|
453
523
|
}
|
|
454
524
|
} else {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
5
|
+
function number(value) { const n = Number(value ?? 0); return Number.isFinite(n) && n >= 0 ? n : 0; }
|
|
6
|
+
|
|
7
|
+
function parseProfile(value) {
|
|
8
|
+
if (typeof value === 'string') return JSON.parse(value);
|
|
9
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new TypeError('Cloudflare CPU profile must be a Chrome .cpuprofile object');
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function frameOf(node = {}) {
|
|
14
|
+
const frame = node.callFrame || {};
|
|
15
|
+
return {
|
|
16
|
+
node_id: node.id,
|
|
17
|
+
function: clean(frame.functionName) || '(anonymous)',
|
|
18
|
+
url: clean(frame.url) || null,
|
|
19
|
+
line: Number.isInteger(frame.lineNumber) && frame.lineNumber >= 0 ? frame.lineNumber + 1 : null,
|
|
20
|
+
column: Number.isInteger(frame.columnNumber) && frame.columnNumber >= 0 ? frame.columnNumber + 1 : null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function summarizeCloudflareCpuProfile(value, options = {}) {
|
|
25
|
+
const profile = parseProfile(value);
|
|
26
|
+
if (!Array.isArray(profile.nodes) || !Array.isArray(profile.samples)) throw new TypeError('CPU profile requires nodes[] and samples[]');
|
|
27
|
+
const deltas = Array.isArray(profile.timeDeltas) ? profile.timeDeltas : [];
|
|
28
|
+
const byId = new Map(profile.nodes.map((node) => [node.id, node]));
|
|
29
|
+
const totals = new Map();
|
|
30
|
+
let totalUs = 0;
|
|
31
|
+
for (let index = 0; index < profile.samples.length; index += 1) {
|
|
32
|
+
const id = profile.samples[index];
|
|
33
|
+
const deltaUs = number(deltas[index]);
|
|
34
|
+
totalUs += deltaUs;
|
|
35
|
+
totals.set(id, (totals.get(id) || 0) + deltaUs);
|
|
36
|
+
}
|
|
37
|
+
if (!totalUs && Number.isFinite(profile.endTime) && Number.isFinite(profile.startTime)) totalUs = Math.max(0, Number(profile.endTime) - Number(profile.startTime));
|
|
38
|
+
const maxFrames = Number.isInteger(options.maxFrames) && options.maxFrames > 0 ? Math.min(options.maxFrames, 100) : 25;
|
|
39
|
+
const frames = [...totals.entries()].map(([id, selfUs]) => {
|
|
40
|
+
const frame = frameOf(byId.get(id));
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
...frame,
|
|
43
|
+
self_us: selfUs,
|
|
44
|
+
self_ms: selfUs / 1000,
|
|
45
|
+
percent: totalUs > 0 ? (selfUs / totalUs) * 100 : 0,
|
|
46
|
+
garbage_collection: /(?:garbage collector|\bgc\b)/i.test(frame.function),
|
|
47
|
+
});
|
|
48
|
+
}).sort((a, b) => b.self_us - a.self_us).slice(0, maxFrames);
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
schema_version: 1,
|
|
51
|
+
profile_kind: 'chrome-cpu-profile',
|
|
52
|
+
samples: profile.samples.length,
|
|
53
|
+
total_profile_us: totalUs,
|
|
54
|
+
total_profile_ms: totalUs / 1000,
|
|
55
|
+
top_frames: Object.freeze(frames),
|
|
56
|
+
garbage_collection_ms: frames.filter((row) => row.garbage_collection).reduce((sum, row) => sum + row.self_ms, 0),
|
|
57
|
+
timer_semantics: 'Deployed Workers timers do not advance during CPU-only execution; use local workerd/DevTools CPU profiles plus production CPU metrics.',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function within(root, file) { return file === root || file.startsWith(`${root}${path.sep}`); }
|
|
62
|
+
|
|
63
|
+
export function summarizeCloudflareCpuProfileFile(input = {}) {
|
|
64
|
+
const cwd = path.resolve(input.cwd || process.cwd());
|
|
65
|
+
const file = path.resolve(cwd, clean(input.file));
|
|
66
|
+
if (!clean(input.file)) throw new TypeError('cpu profile file is required');
|
|
67
|
+
if (!within(cwd, file)) throw new Error('cpu_profile_outside_cwd');
|
|
68
|
+
const stat = fs.statSync(file);
|
|
69
|
+
if (!stat.isFile()) throw new Error('cpu_profile_not_file');
|
|
70
|
+
if (stat.size > 64 * 1024 * 1024) throw new Error('cpu_profile_too_large');
|
|
71
|
+
return Object.freeze({ file: path.relative(cwd, file) || path.basename(file), ...summarizeCloudflareCpuProfile(fs.readFileSync(file, 'utf8'), input) });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sourceItems(cwd, sources = [], maxChars = 24_000) {
|
|
75
|
+
const rows = [];
|
|
76
|
+
let chars = 0;
|
|
77
|
+
for (const source of sources.slice(0, 12)) {
|
|
78
|
+
const file = path.resolve(cwd, String(source));
|
|
79
|
+
if (!within(cwd, file) || !fs.existsSync(file) || !fs.statSync(file).isFile()) continue;
|
|
80
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
81
|
+
const excerpt = text.slice(0, Math.min(8_000, Math.max(0, maxChars - chars)));
|
|
82
|
+
if (!excerpt) break;
|
|
83
|
+
rows.push(Object.freeze({ ref: `file:${path.relative(cwd, file)}`, chars: excerpt.length, content: excerpt }));
|
|
84
|
+
chars += excerpt.length;
|
|
85
|
+
if (chars >= maxChars) break;
|
|
86
|
+
}
|
|
87
|
+
return Object.freeze(rows);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function buildCloudflareCpuAuditPacket(input = {}) {
|
|
91
|
+
const cwd = path.resolve(input.cwd || process.cwd());
|
|
92
|
+
const profile = input.profile ? summarizeCloudflareCpuProfile(input.profile, input) : summarizeCloudflareCpuProfileFile({ ...input, cwd });
|
|
93
|
+
return Object.freeze({
|
|
94
|
+
schema_version: 1,
|
|
95
|
+
primitive: 'cloudflare.cpu.audit',
|
|
96
|
+
rules: Object.freeze({ read_only: true, may_edit: false, may_deploy: false, production_timer_cpu_measurement_valid: false }),
|
|
97
|
+
profile,
|
|
98
|
+
source_evidence: sourceItems(cwd, input.sources || [], Number(input.maxSourceChars || 24_000)),
|
|
99
|
+
questions: Object.freeze([
|
|
100
|
+
'Which frames dominate self CPU time?',
|
|
101
|
+
'Is garbage collection material?',
|
|
102
|
+
'Which source changes are most likely to reduce CPU without changing behavior?',
|
|
103
|
+
'What local production-like request should reproduce the hotspot?',
|
|
104
|
+
'What production metric/log evidence should confirm improvement?',
|
|
105
|
+
]),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function runCloudflareCpuAudit(input = {}) {
|
|
110
|
+
if (typeof input.reasoner !== 'function') throw new TypeError('cloudflare.cpu.audit requires an injected reasoner(packet) function');
|
|
111
|
+
const packet = buildCloudflareCpuAuditPacket(input);
|
|
112
|
+
const result = await input.reasoner(structuredClone(packet));
|
|
113
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) throw new TypeError('cloudflare.cpu.audit reasoner must return an object');
|
|
114
|
+
return Object.freeze({ schema_version: 1, primitive: 'cloudflare.cpu.audit', profile: packet.profile, analysis: result });
|
|
115
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
WRANGLER_NATIVE_COMMANDS,
|
|
3
|
+
WRANGLER_OPERATION_FAMILIES,
|
|
4
|
+
buildWranglerInvocation,
|
|
5
|
+
listWranglerNativeCommands,
|
|
6
|
+
parseWranglerErrorEvidence,
|
|
7
|
+
runWranglerNative,
|
|
8
|
+
} from './wrangler.js';
|
|
9
|
+
export {
|
|
10
|
+
summarizeCloudflareCpuProfile,
|
|
11
|
+
summarizeCloudflareCpuProfileFile,
|
|
12
|
+
buildCloudflareCpuAuditPacket,
|
|
13
|
+
runCloudflareCpuAudit,
|
|
14
|
+
} from './cpu-profile.js';
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { runProcess } from '../security/process.js';
|
|
4
|
+
import { AgentSamDiagnosticError, redactDiagnosticValue } from '../errors/index.js';
|
|
5
|
+
|
|
6
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
7
|
+
function positiveInteger(value, fallback) { const n = Number(value); return Number.isInteger(n) && n > 0 ? n : fallback; }
|
|
8
|
+
|
|
9
|
+
export const WRANGLER_NATIVE_COMMANDS = Object.freeze([
|
|
10
|
+
Object.freeze({ id: 'whoami', argv: ['whoami', '--json'], risk: 'read', output: 'json', description: 'Read authenticated Cloudflare user/account membership without exposing the auth token.' }),
|
|
11
|
+
Object.freeze({ id: 'deployments.list', argv: ['deployments', 'list', '--json'], risk: 'read', output: 'json', description: 'List recent Worker deployments.' }),
|
|
12
|
+
Object.freeze({ id: 'versions.list', argv: ['versions', 'list', '--json'], risk: 'read', output: 'json', description: 'List recent Worker versions.' }),
|
|
13
|
+
Object.freeze({ id: 'types.check', argv: ['types', '--check'], risk: 'read', output: 'text', description: 'Check generated Worker binding/runtime types without rewriting them.' }),
|
|
14
|
+
Object.freeze({ id: 'queues.list', argv: ['queues', 'list'], risk: 'read', output: 'text', description: 'List Workers Queues visible to the active Cloudflare identity.' }),
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export const WRANGLER_OPERATION_FAMILIES = Object.freeze([
|
|
18
|
+
Object.freeze({ family: 'identity', examples: ['whoami', 'auth list', 'auth activate'], default_risk: 'read/config' }),
|
|
19
|
+
Object.freeze({ family: 'development', examples: ['dev', 'types --check'], default_risk: 'local-runtime' }),
|
|
20
|
+
Object.freeze({ family: 'observability', examples: ['tail --format json', 'deployments list', 'versions list'], default_risk: 'read/stream' }),
|
|
21
|
+
Object.freeze({ family: 'delivery', examples: ['deploy', 'versions deploy', 'rollback'], default_risk: 'remote-write' }),
|
|
22
|
+
Object.freeze({ family: 'data', examples: ['d1', 'r2', 'kv', 'queues', 'hyperdrive', 'vectorize'], default_risk: 'read-or-remote-write' }),
|
|
23
|
+
Object.freeze({ family: 'compute', examples: ['containers', 'browser', 'ai', 'workflows'], default_risk: 'read-or-remote-write' }),
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export function listWranglerNativeCommands() { return WRANGLER_NATIVE_COMMANDS.map((row) => ({ ...row, argv: [...row.argv] })); }
|
|
27
|
+
|
|
28
|
+
function descriptor(id) {
|
|
29
|
+
const row = WRANGLER_NATIVE_COMMANDS.find((item) => item.id === clean(id));
|
|
30
|
+
if (!row) throw new RangeError(`unsupported_wrangler_native_command:${id}`);
|
|
31
|
+
return row;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveConfig(cwd, explicit = '') {
|
|
35
|
+
if (clean(explicit)) {
|
|
36
|
+
const file = path.resolve(cwd, explicit);
|
|
37
|
+
if (!file.startsWith(`${cwd}${path.sep}`) && file !== cwd) throw new Error('cloudflare_config_outside_cwd');
|
|
38
|
+
if (!fs.existsSync(file)) throw new Error(`cloudflare_config_not_found:${explicit}`);
|
|
39
|
+
return file;
|
|
40
|
+
}
|
|
41
|
+
for (const name of ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml']) {
|
|
42
|
+
const file = path.join(cwd, name);
|
|
43
|
+
if (fs.existsSync(file)) return file;
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function buildWranglerInvocation(id, input = {}) {
|
|
49
|
+
const row = descriptor(id);
|
|
50
|
+
const cwd = path.resolve(input.cwd || process.cwd());
|
|
51
|
+
const args = [...row.argv];
|
|
52
|
+
if (id === 'whoami' && clean(input.account)) args.push('--account', clean(input.account));
|
|
53
|
+
if ((id === 'deployments.list' || id === 'versions.list') && clean(input.name)) args.push('--name', clean(input.name));
|
|
54
|
+
if (id === 'types.check' && clean(input.path)) args.splice(1, 0, clean(input.path));
|
|
55
|
+
if (id === 'queues.list' && input.page != null) args.push('--page', String(positiveInteger(input.page, 1)));
|
|
56
|
+
const config = resolveConfig(cwd, input.config);
|
|
57
|
+
if (config) args.push('--config', config);
|
|
58
|
+
if (clean(input.env)) args.push('--env', clean(input.env));
|
|
59
|
+
if (clean(input.profile)) args.push('--profile', clean(input.profile));
|
|
60
|
+
return Object.freeze({ command_id: row.id, command: 'wrangler', args: Object.freeze(args), cwd, risk: row.risk, output: row.output });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parseJsonOutput(text) {
|
|
64
|
+
const source = clean(text);
|
|
65
|
+
if (!source) return null;
|
|
66
|
+
try { return JSON.parse(source); } catch { return null; }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function stripAnsi(value) { return String(value || '').replace(/\x1b\[[0-?]*[ -\/]*[@-~]/g, ''); }
|
|
70
|
+
function nestedError(payload) {
|
|
71
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
72
|
+
const rows = [payload.error, ...(Array.isArray(payload.errors) ? payload.errors : [])].filter((row) => row && typeof row === 'object');
|
|
73
|
+
return rows[0] || payload;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function parseWranglerErrorEvidence(stderr = '', stdout = '') {
|
|
77
|
+
const errorText = stripAnsi(stderr);
|
|
78
|
+
const outputText = stripAnsi(stdout);
|
|
79
|
+
let payload = parseJsonOutput(outputText) || parseJsonOutput(errorText);
|
|
80
|
+
const error = nestedError(payload);
|
|
81
|
+
const combined = `${errorText}\n${outputText}`;
|
|
82
|
+
const regexCode = combined.match(/\[code:\s*([A-Za-z0-9_.:-]+)\]/i)?.[1] || combined.match(/\bcode[:=\s]+([A-Za-z0-9_.:-]+)/i)?.[1] || '';
|
|
83
|
+
const code = clean(error?.code || regexCode) || null;
|
|
84
|
+
const requestId = clean(error?.request_id || error?.requestId || combined.match(/\brequest[_ -]?id[:=\s]+([A-Za-z0-9_-]+)/i)?.[1]) || null;
|
|
85
|
+
const rayId = clean(error?.ray_id || error?.rayId || combined.match(/\b(?:cf[- ]?ray|ray id)[:=\s]+([A-Za-z0-9-]+)/i)?.[1]) || null;
|
|
86
|
+
const message = clean(error?.message || error?.error || errorText.split('\n').find((line) => clean(line)) || outputText.split('\n').find((line) => clean(line))) || 'Wrangler operation failed';
|
|
87
|
+
return Object.freeze({ code, request_id: requestId, ray_id: rayId, message, details: payload ? redactDiagnosticValue(payload) : null });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function runWranglerNative(id, input = {}, options = {}) {
|
|
91
|
+
const plan = buildWranglerInvocation(id, input);
|
|
92
|
+
const runner = options.run || runProcess;
|
|
93
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : 30_000;
|
|
94
|
+
const result = await runner(options.bin || 'npx', ['--yes', 'wrangler', ...plan.args], {
|
|
95
|
+
cwd: plan.cwd,
|
|
96
|
+
timeoutMs,
|
|
97
|
+
signal: options.signal,
|
|
98
|
+
maxBytes: options.maxBytes || 2 * 1024 * 1024,
|
|
99
|
+
});
|
|
100
|
+
if (result.code !== 0) {
|
|
101
|
+
const evidence = parseWranglerErrorEvidence(result.stderr, result.stdout);
|
|
102
|
+
throw new AgentSamDiagnosticError({
|
|
103
|
+
source: 'cloudflare',
|
|
104
|
+
kind: 'wrangler_error',
|
|
105
|
+
code: evidence.code || 'wrangler_exit_nonzero',
|
|
106
|
+
message: evidence.message || `Wrangler ${plan.command_id} exited ${result.code}`,
|
|
107
|
+
retriable: false,
|
|
108
|
+
retry_strategy: 'inspect_error',
|
|
109
|
+
operation: plan.command_id,
|
|
110
|
+
exit_code: result.code,
|
|
111
|
+
request_id: evidence.request_id,
|
|
112
|
+
ray_id: evidence.ray_id,
|
|
113
|
+
cwd: plan.cwd,
|
|
114
|
+
stderr: redactDiagnosticValue(String(result.stderr || '').slice(0, 12_000)) || null,
|
|
115
|
+
stdout: redactDiagnosticValue(String(result.stdout || '').slice(0, 4_000)) || null,
|
|
116
|
+
details: evidence.details,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const parsed = plan.output === 'json' ? parseJsonOutput(result.stdout) : null;
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
ok: true,
|
|
122
|
+
schema_version: 1,
|
|
123
|
+
command_id: plan.command_id,
|
|
124
|
+
risk: plan.risk,
|
|
125
|
+
cwd: plan.cwd,
|
|
126
|
+
exit_code: result.code,
|
|
127
|
+
format: parsed == null ? 'text' : 'json',
|
|
128
|
+
data: parsed == null ? undefined : redactDiagnosticValue(parsed),
|
|
129
|
+
stdout: parsed == null ? String(result.stdout || '').slice(0, 24_000) : undefined,
|
|
130
|
+
stderr: String(result.stderr || '').slice(0, 8_000) || undefined,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { authenticateViaBrowser } from '../lib/auth.js';
|
|
2
|
+
import { clearAccountSession, readAccountSession, saveAccountSession } from '../lib/account-session.js';
|
|
3
|
+
import { collectWhoami, renderWhoami } from './whoami.js';
|
|
4
|
+
|
|
5
|
+
function writeLine(write, value = '') { write(`${value}\n`); }
|
|
6
|
+
|
|
7
|
+
export async function runLogin(argv = [], options = {}) {
|
|
8
|
+
const allowed = new Set(['--json']);
|
|
9
|
+
const unknown = argv.filter((arg) => !allowed.has(arg));
|
|
10
|
+
if (unknown.length) throw new Error(`unknown login option: ${unknown[0]}`);
|
|
11
|
+
const write = options.write || ((text) => process.stdout.write(text));
|
|
12
|
+
const authenticate = options.authenticateImpl || authenticateViaBrowser;
|
|
13
|
+
const session = await authenticate();
|
|
14
|
+
if (!String(session?.access_token || '').trim().startsWith('sdk_')) throw new Error('Agent Sam login did not return a valid SDK session');
|
|
15
|
+
// authenticateViaBrowser persists by default. Keep injected transports/test flows equivalent.
|
|
16
|
+
if (!readAccountSession({ home: options.home })) saveAccountSession(session, { home: options.home });
|
|
17
|
+
const status = await collectWhoami({ home: options.home, env: options.env || process.env, contextLoader: options.contextLoader });
|
|
18
|
+
if (argv.includes('--json')) writeLine(write, JSON.stringify(status, null, 2));
|
|
19
|
+
else {
|
|
20
|
+
writeLine(write, '');
|
|
21
|
+
writeLine(write, ' Agent Sam login complete.');
|
|
22
|
+
write(renderWhoami(status));
|
|
23
|
+
}
|
|
24
|
+
return status;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function runLogout(argv = [], options = {}) {
|
|
28
|
+
const allowed = new Set(['--json']);
|
|
29
|
+
const unknown = argv.filter((arg) => !allowed.has(arg));
|
|
30
|
+
if (unknown.length) throw new Error(`unknown logout option: ${unknown[0]}`);
|
|
31
|
+
const write = options.write || ((text) => process.stdout.write(text));
|
|
32
|
+
const removed = clearAccountSession({ home: options.home });
|
|
33
|
+
const result = {
|
|
34
|
+
schema_version: 1,
|
|
35
|
+
local_session_removed: removed,
|
|
36
|
+
provider_credentials_unchanged: true,
|
|
37
|
+
note: 'Local IAM session removed. Provider credentials were not deleted or revoked.',
|
|
38
|
+
};
|
|
39
|
+
if (argv.includes('--json')) writeLine(write, JSON.stringify(result, null, 2));
|
|
40
|
+
else {
|
|
41
|
+
writeLine(write, '');
|
|
42
|
+
writeLine(write, removed ? ' Signed out of the local Agent Sam IAM session.' : ' No local Agent Sam IAM session was stored.');
|
|
43
|
+
writeLine(write, ' Provider credentials were not deleted or revoked.');
|
|
44
|
+
writeLine(write, '');
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { listWranglerNativeCommands, runWranglerNative, summarizeCloudflareCpuProfileFile, WRANGLER_OPERATION_FAMILIES } from '../cloudflare/index.js';
|
|
3
|
+
import { renderDiagnosticError } from '../errors/index.js';
|
|
4
|
+
|
|
5
|
+
function parse(argv = []) {
|
|
6
|
+
const subcommand = argv[0] || 'status';
|
|
7
|
+
const takesAction = subcommand === 'run' || subcommand === 'cpu';
|
|
8
|
+
const out = { subcommand, action: takesAction ? (argv[1] || '') : '', cwd: process.cwd(), json: false, name: '', account: '', config: '', env: '', profile: '', path: '', page: null, file: '' };
|
|
9
|
+
for (let i = takesAction ? 2 : 1; i < argv.length; i += 1) {
|
|
10
|
+
const arg = argv[i];
|
|
11
|
+
if (arg === '--json') out.json = true;
|
|
12
|
+
else if (arg === '--cwd') out.cwd = argv[++i] || out.cwd;
|
|
13
|
+
else if (arg === '--name') out.name = argv[++i] || '';
|
|
14
|
+
else if (arg === '--account') out.account = argv[++i] || '';
|
|
15
|
+
else if (arg === '--config') out.config = argv[++i] || '';
|
|
16
|
+
else if (arg === '--env') out.env = argv[++i] || '';
|
|
17
|
+
else if (arg === '--profile') out.profile = argv[++i] || '';
|
|
18
|
+
else if (arg === '--path') out.path = argv[++i] || '';
|
|
19
|
+
else if (arg === '--page') out.page = Number(argv[++i] || 1);
|
|
20
|
+
else if (arg === '--file') out.file = argv[++i] || '';
|
|
21
|
+
else if (arg === '--help' || arg === '-h') out.help = true;
|
|
22
|
+
else if (out.subcommand === 'cpu' && out.action === 'analyze' && !out.file) out.file = arg;
|
|
23
|
+
else throw new Error(`unknown cloudflare option: ${arg}`);
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const help = `Agent Sam · Cloudflare\n\n agentsam cloudflare status [--cwd PATH] [--json]\n agentsam cloudflare commands [--json]\n agentsam cloudflare run <whoami|deployments.list|versions.list|types.check|queues.list> [options] [--json]\n agentsam cloudflare cpu analyze <profile.cpuprofile> [--cwd PATH] [--json]\n\nSafe native runner is read-only. Deploy, rollback, secret reads, D1 mutation, R2 writes, and long-running tail/dev sessions remain explicit operator actions.\n`;
|
|
29
|
+
|
|
30
|
+
export async function runCloudflare(argv = [], options = {}) {
|
|
31
|
+
const args = parse(argv);
|
|
32
|
+
if (options.cwd && !argv.includes('--cwd')) args.cwd = path.resolve(options.cwd);
|
|
33
|
+
const write = options.write || ((value) => process.stdout.write(value));
|
|
34
|
+
if (args.help) { write(help); return null; }
|
|
35
|
+
try {
|
|
36
|
+
let result;
|
|
37
|
+
if (args.subcommand === 'status') {
|
|
38
|
+
result = await runWranglerNative('whoami', args, options);
|
|
39
|
+
} else if (args.subcommand === 'commands') {
|
|
40
|
+
result = { schema_version: 1, native: listWranglerNativeCommands(), families: WRANGLER_OPERATION_FAMILIES };
|
|
41
|
+
} else if (args.subcommand === 'run') {
|
|
42
|
+
if (!args.action) throw new Error('cloudflare native command id required');
|
|
43
|
+
result = await runWranglerNative(args.action, args, options);
|
|
44
|
+
} else if (args.subcommand === 'cpu' && args.action === 'analyze') {
|
|
45
|
+
result = summarizeCloudflareCpuProfileFile({ cwd: path.resolve(args.cwd), file: args.file });
|
|
46
|
+
} else {
|
|
47
|
+
write(help);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
write(args.json ? `${JSON.stringify(result)}\n` : `${JSON.stringify(result, null, 2)}\n`);
|
|
51
|
+
return result;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (args.json) write(`${JSON.stringify({ ok: false, error: error?.diagnostic || { code: error?.code || 'cloudflare_operation_failed', message: error?.message || String(error) } })}\n`);
|
|
54
|
+
else write(`${renderDiagnosticError(error)}\n`);
|
|
55
|
+
error.reported = true;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|