@tangle-network/traces 0.11.5 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -2
- package/dist/{chunk-VTYOCFGF.js → chunk-MEAEXKUV.js} +1496 -271
- package/dist/cli.js +163 -61
- package/dist/index.d.ts +451 -1
- package/dist/index.js +33 -1
- package/docs/trace-analysts.md +39 -1
- package/examples/external-engines.ts +14 -1
- package/package.json +6 -5
- package/skills/build-trace-analyst/SKILL.md +13 -0
- package/skills/inspect-agent-traces/SKILL.md +30 -4
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
ACTOR_ATTR,
|
|
4
|
-
ATTR,
|
|
5
|
-
CORRUPTION_RECEIPT_DISPLAY_LIMIT,
|
|
6
3
|
DEFAULT_SANDBOX_BASE_URL,
|
|
7
4
|
appendAll,
|
|
5
|
+
assembleSessionBundle,
|
|
8
6
|
buildPolicyEvidenceRecord,
|
|
9
7
|
buildSessionIndexFromRows,
|
|
10
8
|
cmdReplayVerify,
|
|
@@ -13,7 +11,6 @@ import {
|
|
|
13
11
|
collectSessionSelection,
|
|
14
12
|
commandAnalyzer,
|
|
15
13
|
commandRedactor,
|
|
16
|
-
describeSessionRelationship,
|
|
17
14
|
executeUpload,
|
|
18
15
|
exportTraceEvidenceFile,
|
|
19
16
|
fileRunContextSupervisorRunReader,
|
|
@@ -31,6 +28,8 @@ import {
|
|
|
31
28
|
parseSession,
|
|
32
29
|
parseSince,
|
|
33
30
|
planUpload,
|
|
31
|
+
primeAnalyzer,
|
|
32
|
+
projectSessionBundle,
|
|
34
33
|
readOtlpInput,
|
|
35
34
|
readSessionIndexFile,
|
|
36
35
|
renderInspectionReport,
|
|
@@ -46,7 +45,7 @@ import {
|
|
|
46
45
|
serializeSessionIndex,
|
|
47
46
|
serializeSpans,
|
|
48
47
|
serializeTraceStreamEvent,
|
|
49
|
-
|
|
48
|
+
sessionReportSource,
|
|
50
49
|
streamSessions,
|
|
51
50
|
totalAgenticFailureMessage,
|
|
52
51
|
traceStreamEventsFromSpans,
|
|
@@ -59,7 +58,7 @@ import {
|
|
|
59
58
|
writePolicyEvidenceFile,
|
|
60
59
|
writeSessionIndexFile,
|
|
61
60
|
writeTraceEvidenceExportFile
|
|
62
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-MEAEXKUV.js";
|
|
63
62
|
|
|
64
63
|
// src/cli.ts
|
|
65
64
|
import { readFileSync } from "fs";
|
|
@@ -74,6 +73,71 @@ import {
|
|
|
74
73
|
rollupSupervisorRuns
|
|
75
74
|
} from "@tangle-network/agent-eval/supervisor-run";
|
|
76
75
|
import { createDspyRlmTraceEngine } from "@tangle-network/agent-eval/analyst";
|
|
76
|
+
|
|
77
|
+
// src/analyst-model-call.ts
|
|
78
|
+
import {
|
|
79
|
+
callLlm,
|
|
80
|
+
costReceiptFromLlm,
|
|
81
|
+
costReceiptFromLlmError,
|
|
82
|
+
LlmCallError
|
|
83
|
+
} from "@tangle-network/agent-eval";
|
|
84
|
+
var PROVIDER_ERROR_BODY_LIMIT = 2e3;
|
|
85
|
+
function createAnalystModelCall(opts) {
|
|
86
|
+
const { apiKey, baseUrl } = opts;
|
|
87
|
+
return async ({ request, callId, signal }) => {
|
|
88
|
+
try {
|
|
89
|
+
const req = structuredClone(request);
|
|
90
|
+
const response = await callLlm(req, {
|
|
91
|
+
apiKey,
|
|
92
|
+
baseUrl,
|
|
93
|
+
signal,
|
|
94
|
+
idempotencyKey: callId
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
succeeded: true,
|
|
98
|
+
response,
|
|
99
|
+
receipt: costReceiptFromLlm(response),
|
|
100
|
+
execution: {
|
|
101
|
+
callId,
|
|
102
|
+
baseUrl,
|
|
103
|
+
requestedModel: req.model,
|
|
104
|
+
servedModel: response.servedModel ?? null,
|
|
105
|
+
finishReason: response.finishReason ?? null,
|
|
106
|
+
durationMs: response.durationMs
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
111
|
+
const aborted = signal.aborted;
|
|
112
|
+
const message = aborted ? `AbortError: ${err.message}` : err.message;
|
|
113
|
+
const detail = err instanceof LlmCallError ? { status: err.status, body: err.body.slice(0, PROVIDER_ERROR_BODY_LIMIT) } : {};
|
|
114
|
+
return {
|
|
115
|
+
succeeded: false,
|
|
116
|
+
error: message,
|
|
117
|
+
// costReceiptFromLlmError recovers the provider receipt when the
|
|
118
|
+
// response completed but violated the contract; otherwise usage and
|
|
119
|
+
// cost stay explicitly unknown.
|
|
120
|
+
receipt: costReceiptFromLlmError(err) ?? {
|
|
121
|
+
model: request.model,
|
|
122
|
+
inputTokens: 0,
|
|
123
|
+
outputTokens: 0,
|
|
124
|
+
costUnknown: true,
|
|
125
|
+
usageUnknown: true
|
|
126
|
+
},
|
|
127
|
+
execution: {
|
|
128
|
+
callId,
|
|
129
|
+
baseUrl,
|
|
130
|
+
requestedModel: request.model,
|
|
131
|
+
aborted,
|
|
132
|
+
error: message,
|
|
133
|
+
...detail
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/cli.ts
|
|
77
141
|
var DEFAULT_ANALYST_MODEL = "gpt-5-mini";
|
|
78
142
|
var TANGLE_ROUTER_BASE_URL = "https://router.tangle.tools/v1";
|
|
79
143
|
function packageVersion() {
|
|
@@ -185,6 +249,9 @@ function parseArgs(argv) {
|
|
|
185
249
|
case "--mode":
|
|
186
250
|
a.mode = next();
|
|
187
251
|
break;
|
|
252
|
+
case "--view":
|
|
253
|
+
a.view = next();
|
|
254
|
+
break;
|
|
188
255
|
case "--metadata":
|
|
189
256
|
a.metadata = next();
|
|
190
257
|
break;
|
|
@@ -296,6 +363,7 @@ var CURRENT_SESSION_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
296
363
|
"convert",
|
|
297
364
|
"index",
|
|
298
365
|
"evidence",
|
|
366
|
+
"bundle",
|
|
299
367
|
"stream"
|
|
300
368
|
]);
|
|
301
369
|
var WORKFLOW_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -324,6 +392,12 @@ function applyCurrentSessionSelection(args) {
|
|
|
324
392
|
if (!sessionId) throw new Error("--current requires CODEX_THREAD_ID from the active Codex session");
|
|
325
393
|
return { ...args, session: sessionId };
|
|
326
394
|
}
|
|
395
|
+
function validateViewSelection(args) {
|
|
396
|
+
if (args.view === void 0 || args.command === "bundle-view") return args;
|
|
397
|
+
throw new Error(
|
|
398
|
+
`--view is not supported by ${args.command}. A view is a projection of an assembled bundle: run \`traces bundle ... --out <dir>\` first, then \`traces bundle-view <dir> --view evidence-only --out <view-dir>\`.`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
327
401
|
function validateWorkflowSelection(args) {
|
|
328
402
|
if (!args.workflow && !args.latestTurn) return args;
|
|
329
403
|
if (!WORKFLOW_COMMANDS.has(args.command)) {
|
|
@@ -422,15 +496,19 @@ function buildAnalysisEngine(model, budgetUsd) {
|
|
|
422
496
|
const baseUrl = process.env.OPENAI_BASE_URL || (tangleKey ? TANGLE_ROUTER_BASE_URL : "https://api.openai.com/v1");
|
|
423
497
|
const python = process.env.TRACES_PYTHON;
|
|
424
498
|
return createDspyRlmTraceEngine({
|
|
425
|
-
apiKey,
|
|
426
|
-
baseUrl
|
|
499
|
+
call: createAnalystModelCall({ apiKey, baseUrl }),
|
|
500
|
+
callRef: `traces-cli:${baseUrl}#${model}`,
|
|
501
|
+
recordExecution: (observation) => {
|
|
502
|
+
analystLog(
|
|
503
|
+
`[analyst] model call ${observation.sequence} ${observation.succeeded ? "ok" : "FAIL"} ${observation.model}`,
|
|
504
|
+
observation.succeeded ? void 0 : { error: observation.error }
|
|
505
|
+
);
|
|
506
|
+
},
|
|
427
507
|
model,
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
// reservation, and the fail-closed ledger then refuses every later call
|
|
433
|
-
// in the run.
|
|
508
|
+
// Pinned, not defaulted: one findings array from a current coding model
|
|
509
|
+
// needs this cap, and glm-5.2 counts reasoning tokens in
|
|
510
|
+
// completion_tokens — a smaller cap breaches its cost reservation and the
|
|
511
|
+
// fail-closed ledger then refuses every later call in the run.
|
|
434
512
|
maxOutputTokens: 16384,
|
|
435
513
|
// maxCostUsd defaults to $1 per analyst — a proxy-side ceiling separate
|
|
436
514
|
// from --budget. With the larger token cap the per-call reservation grows
|
|
@@ -466,47 +544,6 @@ ${adapter.harness}: ${refs.length} session(s)`);
|
|
|
466
544
|
}
|
|
467
545
|
}
|
|
468
546
|
}
|
|
469
|
-
function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
470
|
-
const root = spans.find((item) => item.parent_span_id === null) ?? spans[0];
|
|
471
|
-
const prompt = spans.find(
|
|
472
|
-
(item) => item.name === "user.prompt" && item.attributes[ACTOR_ATTR] === "human"
|
|
473
|
-
) ?? spans.find((item) => item.name === "user.prompt") ?? spans.find(
|
|
474
|
-
(item) => item.attributes["span.type"] === "interaction" && typeof item.attributes.content === "string"
|
|
475
|
-
);
|
|
476
|
-
const content = typeof prompt?.attributes.content === "string" ? prompt.attributes.content : "";
|
|
477
|
-
const firstLine = content.split(/\r?\n/, 1)[0].trim();
|
|
478
|
-
const subject = firstLine.length > 240 ? `${firstLine.slice(0, 240)}\u2026 [+${firstLine.length - 240} chars]` : firstLine;
|
|
479
|
-
const role = root?.attributes["traces.session.role"];
|
|
480
|
-
const parentSessionId = root?.attributes["traces.parent_session_id"];
|
|
481
|
-
const relationship = describeSessionRelationship(ref, spans);
|
|
482
|
-
const corruptionDigest = root?.attributes[ATTR.CORRUPTION_DIGEST];
|
|
483
|
-
const sessionId = [
|
|
484
|
-
sessionIdOverride,
|
|
485
|
-
root ? sessionIdFromAttributes(root.attributes) : void 0,
|
|
486
|
-
root?.trace_id,
|
|
487
|
-
ref.sessionId
|
|
488
|
-
].find((value) => typeof value === "string" && value.length > 0);
|
|
489
|
-
return {
|
|
490
|
-
sessionId,
|
|
491
|
-
path: ref.path,
|
|
492
|
-
subject,
|
|
493
|
-
role: role === "operator" || role === "child" ? role : "unknown",
|
|
494
|
-
...typeof parentSessionId === "string" ? { parentSessionId } : {},
|
|
495
|
-
childSessionIds: relationship.childSessionIds,
|
|
496
|
-
...relationship.depth !== void 0 ? { depth: relationship.depth } : {},
|
|
497
|
-
...relationship.agentNickname ? { agentNickname: relationship.agentNickname } : {},
|
|
498
|
-
...relationship.agentRole ? { agentRole: relationship.agentRole } : {},
|
|
499
|
-
...relationship.agentPath ? { agentPath: relationship.agentPath } : {},
|
|
500
|
-
...relationship.taskScope ? { taskScope: relationship.taskScope } : {},
|
|
501
|
-
...relationship.turnId ? { turnId: relationship.turnId } : {},
|
|
502
|
-
integrity: ref.integrity?.status ?? "complete",
|
|
503
|
-
...ref.integrity ? {
|
|
504
|
-
corruptionCount: ref.integrity.corruptions.length,
|
|
505
|
-
...typeof corruptionDigest === "string" ? { corruptionDigest } : {},
|
|
506
|
-
corruptions: ref.integrity.corruptions.slice(0, CORRUPTION_RECEIPT_DISPLAY_LIMIT)
|
|
507
|
-
} : {}
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
547
|
async function collectOtlpSpans(path) {
|
|
511
548
|
const input = await readOtlpInput(path);
|
|
512
549
|
if (input.spans.length === 0) {
|
|
@@ -528,7 +565,7 @@ async function collectOtlpSpans(path) {
|
|
|
528
565
|
spans: [...input.spans],
|
|
529
566
|
harness: "otlp",
|
|
530
567
|
cwds: [],
|
|
531
|
-
sources: [...byTrace].map(([traceId, spans]) =>
|
|
568
|
+
sources: [...byTrace].map(([traceId, spans]) => sessionReportSource({
|
|
532
569
|
harness: "otlp",
|
|
533
570
|
sessionId: traceId,
|
|
534
571
|
path: fileByTrace.get(traceId) ?? path,
|
|
@@ -558,7 +595,7 @@ async function collectSpans(args) {
|
|
|
558
595
|
for (const row of selection.rows) {
|
|
559
596
|
harnesses.add(row.adapter.harness);
|
|
560
597
|
appendAll(spans, row.spans);
|
|
561
|
-
sources.push(
|
|
598
|
+
sources.push(sessionReportSource(row.ref, row.spans));
|
|
562
599
|
if (row.ref.cwd) cwds.push(row.ref.cwd);
|
|
563
600
|
}
|
|
564
601
|
return {
|
|
@@ -677,6 +714,46 @@ async function cmdIndex(args) {
|
|
|
677
714
|
process.stdout.write(serializeSessionIndex(index));
|
|
678
715
|
}
|
|
679
716
|
}
|
|
717
|
+
async function cmdBundle(args) {
|
|
718
|
+
if (!args.session) {
|
|
719
|
+
throw new Error("bundle needs --session <id|path>; run `traces list` to pick a session ID");
|
|
720
|
+
}
|
|
721
|
+
if (!args.out) throw new Error("bundle needs --out <dir> \u2014 a new or empty directory for the bundle");
|
|
722
|
+
const { adapter, ref } = await resolveSelectedSession(args);
|
|
723
|
+
const result = await assembleSessionBundle({
|
|
724
|
+
adapter,
|
|
725
|
+
ref,
|
|
726
|
+
outDir: args.out,
|
|
727
|
+
minLoopOccurrences: args.minLoop,
|
|
728
|
+
log: analystLog
|
|
729
|
+
});
|
|
730
|
+
const { manifest } = result;
|
|
731
|
+
console.log(
|
|
732
|
+
`session bundle (${manifest.view} view) \u2192 ${result.directory} (${manifest.files.length} file(s), ${manifest.ledgerSlices.length} ledger slice(s), ${manifest.absent.length} recorded absent)`
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
var PROJECTABLE_VIEWS = ["evidence-only"];
|
|
736
|
+
async function cmdBundleView(args) {
|
|
737
|
+
if (!args.input) {
|
|
738
|
+
throw new Error("bundle-view needs the bundle directory to project; run `traces bundle --out <dir>` first");
|
|
739
|
+
}
|
|
740
|
+
if (!args.view) {
|
|
741
|
+
throw new Error(`bundle-view needs --view <${PROJECTABLE_VIEWS.join("|")}>`);
|
|
742
|
+
}
|
|
743
|
+
if (!PROJECTABLE_VIEWS.includes(args.view)) {
|
|
744
|
+
throw new Error(`unknown view "${args.view}"; projectable views: ${PROJECTABLE_VIEWS.join(", ")}`);
|
|
745
|
+
}
|
|
746
|
+
if (!args.out) throw new Error("bundle-view needs --out <dir> \u2014 a new or empty directory for the view");
|
|
747
|
+
const result = await projectSessionBundle({
|
|
748
|
+
bundleDir: args.input,
|
|
749
|
+
outDir: args.out,
|
|
750
|
+
view: args.view
|
|
751
|
+
});
|
|
752
|
+
const { manifest, leakCheck } = result;
|
|
753
|
+
console.log(
|
|
754
|
+
`${manifest.view} view \u2192 ${result.directory} (${manifest.files.length} file(s) carried, ${manifest.excluded.length} excluded, ${leakCheck.matches} session-text match(es) against ${leakCheck.sourceSignatures} signature(s) from ${leakCheck.comparedSources.length} excluded source(s))`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
680
757
|
async function cmdInspect(args) {
|
|
681
758
|
if (!args.input) throw new Error("inspect needs an index file; run `traces index --out session-index.json` first");
|
|
682
759
|
const index = await readSessionIndexFile(args.input);
|
|
@@ -890,7 +967,7 @@ async function collectImportedSpans(args) {
|
|
|
890
967
|
spans: result.spans,
|
|
891
968
|
harness: result.format,
|
|
892
969
|
cwds: [],
|
|
893
|
-
sources: [...sessions].map(([sessionId, spans]) =>
|
|
970
|
+
sources: [...sessions].map(([sessionId, spans]) => sessionReportSource({
|
|
894
971
|
harness: result.format,
|
|
895
972
|
sessionId,
|
|
896
973
|
path: args.input,
|
|
@@ -908,7 +985,7 @@ function analystLog(msg, fields) {
|
|
|
908
985
|
}
|
|
909
986
|
function externalAnalyzersFromArgs(args) {
|
|
910
987
|
return args.analyzers.map(
|
|
911
|
-
(spec) => spec === "halo" ? haloAnalyzer({ defaultPrompt: args.analyzerPrompt, model: args.model }) : spec === "hodoscope" ? hodoscopeAnalyzer({ summarizeModel: args.model }) : commandAnalyzer({ name: spec, command: spec, args: (p, prompt) => prompt ? [p, prompt] : [p] })
|
|
988
|
+
(spec) => spec === "halo" ? haloAnalyzer({ defaultPrompt: args.analyzerPrompt, model: args.model }) : spec === "hodoscope" ? hodoscopeAnalyzer({ summarizeModel: args.model }) : spec === "prime" ? primeAnalyzer({ defaultPrompt: args.analyzerPrompt, ...args.model ? { model: args.model } : {} }) : commandAnalyzer({ name: spec, command: spec, args: (p, prompt) => prompt ? [p, prompt] : [p] })
|
|
912
989
|
);
|
|
913
990
|
}
|
|
914
991
|
async function cmdInvestigate(args) {
|
|
@@ -1312,6 +1389,17 @@ Commands:
|
|
|
1312
1389
|
improve Write findings, evidence, report, and canonical trace artifacts
|
|
1313
1390
|
convert Emit OTLP-JSONL only, to --otlp-out (HALO: use analyze --analyzer halo)
|
|
1314
1391
|
index Emit a reusable session index JSON for later investigation
|
|
1392
|
+
bundle Assemble one session's durable evidence directory: transcript +
|
|
1393
|
+
subagents, derived index/report/evidence/OTLP, the repo's .evolve
|
|
1394
|
+
ledger sliced to the session window, git log, and a sha256 manifest
|
|
1395
|
+
(needs --session <id|path> and --out <new-dir>). This is the FULL
|
|
1396
|
+
view: it holds the session's own words, for a reader who cites them
|
|
1397
|
+
bundle-view <bundle-dir>
|
|
1398
|
+
Project an assembled bundle into a narrower view for a consumer that
|
|
1399
|
+
must NOT read the session's own words. --view evidence-only carries
|
|
1400
|
+
the counted artifacts and structured ledger rows, excludes the
|
|
1401
|
+
transcript, the report, the spans and every prose file by name, and
|
|
1402
|
+
refuses to write a view whose text repeats an excluded source
|
|
1315
1403
|
inspect Read a session index and print ranked improvement findings
|
|
1316
1404
|
export Convert evidence/events files to OpenInference JSONL for HALO
|
|
1317
1405
|
import-codetracebench
|
|
@@ -1365,6 +1453,9 @@ Options:
|
|
|
1365
1453
|
--metadata <json> analyze/export file: attach JSON object fields as span attributes
|
|
1366
1454
|
--attr <k=v> analyze/export file: attach one span attribute (repeatable)
|
|
1367
1455
|
--mode <kind> stream: visualizer | findings | agent (default visualizer)
|
|
1456
|
+
--view <name> bundle-view: which projection to write. evidence-only is the
|
|
1457
|
+
writer's view; the full view is the bundle itself, so there is
|
|
1458
|
+
nothing to project into it
|
|
1368
1459
|
--replay, --once stream: scan once and exit (default for positional input / --session)
|
|
1369
1460
|
--once watch <target>: print ONE snapshot and exit, for scripts and agents
|
|
1370
1461
|
--no-spans stream: omit per-span pulse events
|
|
@@ -1378,7 +1469,10 @@ Options:
|
|
|
1378
1469
|
--model <id> Model for --llm, HALO, and Hodoscope (default for --llm: ${DEFAULT_ANALYST_MODEL})
|
|
1379
1470
|
--config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, or external analyzers
|
|
1380
1471
|
--budget <usd> USD cap for agentic analysts
|
|
1381
|
-
--analyzer <id> analyze: also run halo, hodoscope, or an installed command (repeatable)
|
|
1472
|
+
--analyzer <id> analyze: also run halo, hodoscope, prime, or an installed command (repeatable)
|
|
1473
|
+
prime posts the full span projection to an OpenAI-compatible bridge
|
|
1474
|
+
(TRACES_PRIME_BRIDGE_URL, default http://localhost:4181;
|
|
1475
|
+
TRACES_PRIME_MODEL, default prime/zai/glm-5.2; TRACES_PRIME_TIMEOUT_MS)
|
|
1382
1476
|
--analyzer-prompt <p> analyze: prompt passed to external analyzers (default: diagnose)
|
|
1383
1477
|
--verify-findings analyze: execute the findings as sandbox replay proofs and mark
|
|
1384
1478
|
each VERIFIED (receipt path) or UNVERIFIABLE (reason). Needs
|
|
@@ -1432,7 +1526,9 @@ async function main() {
|
|
|
1432
1526
|
else usage();
|
|
1433
1527
|
return;
|
|
1434
1528
|
}
|
|
1435
|
-
const args = validateOtlpSelection(
|
|
1529
|
+
const args = validateOtlpSelection(
|
|
1530
|
+
validateViewSelection(validateWorkflowSelection(applyCurrentSessionSelection(parsedArgs)))
|
|
1531
|
+
);
|
|
1436
1532
|
switch (args.command) {
|
|
1437
1533
|
case "help":
|
|
1438
1534
|
if (args.input === "import-codetracebench") usageImportCodeTraceBench();
|
|
@@ -1461,6 +1557,12 @@ async function main() {
|
|
|
1461
1557
|
case "index":
|
|
1462
1558
|
await cmdIndex(args);
|
|
1463
1559
|
break;
|
|
1560
|
+
case "bundle":
|
|
1561
|
+
await cmdBundle(args);
|
|
1562
|
+
break;
|
|
1563
|
+
case "bundle-view":
|
|
1564
|
+
await cmdBundleView(args);
|
|
1565
|
+
break;
|
|
1464
1566
|
case "inspect":
|
|
1465
1567
|
await cmdInspect(args);
|
|
1466
1568
|
break;
|