@atlassian/chippy 0.0.0-stage → 0.0.1-beta.2
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/LICENSE +201 -0
- package/README.md +276 -2
- package/bin/chippy.mjs +92 -0
- package/bin/pack.mjs +204 -0
- package/bin/skill-dir.mjs +15 -0
- package/lib/avalanche.js +2601 -0
- package/lib/avalanche.js.map +7 -0
- package/lib/bootstrap.d.ts +1 -0
- package/lib/bootstrap.js +70 -0
- package/lib/bootstrap.js.map +7 -0
- package/lib/browser.js +15 -0
- package/lib/browser.js.map +7 -0
- package/lib/extension/avalanche.es.js +5535 -0
- package/lib/extension/avalanche.es.js.map +1 -0
- package/lib/extension/chippy-page-bridge.js +1953 -0
- package/lib/extension/inspect-react.js +216 -0
- package/lib/extension/layout-lab.js +1453 -0
- package/lib/extension/panel-dom.js +90 -0
- package/lib/extension/resolve-probes.js +342 -0
- package/lib/mcp-skill/LICENSE +201 -0
- package/lib/mcp-skill/README.md +148 -0
- package/lib/mcp-skill/SKILL.md +335 -0
- package/lib/mcp-skill/browser-scripts/playwright-probes.js +368 -0
- package/lib/mcp-skill/browser-scripts/playwright-session.js +114 -0
- package/lib/mcp-skill/extension/armed-reload-startup.js +83 -0
- package/lib/mcp-skill/extension/avalanche.es.js +5535 -0
- package/lib/mcp-skill/extension/avalanche.es.js.map +1 -0
- package/lib/mcp-skill/extension/avalanche.iife.js +5536 -0
- package/lib/mcp-skill/extension/avalanche.iife.js.map +1 -0
- package/lib/mcp-skill/extension/background.js +325 -0
- package/lib/mcp-skill/extension/chippy-page-bridge.js +1953 -0
- package/lib/mcp-skill/extension/content-script.js +190 -0
- package/lib/mcp-skill/extension/devtools-hook-shim.js +85 -0
- package/lib/mcp-skill/extension/early-bootstrap.js +49 -0
- package/lib/mcp-skill/extension/icon-128.png +0 -0
- package/lib/mcp-skill/extension/icon-16.png +0 -0
- package/lib/mcp-skill/extension/icon-32.png +0 -0
- package/lib/mcp-skill/extension/icon-48.png +0 -0
- package/lib/mcp-skill/extension/inspect-react.js +216 -0
- package/lib/mcp-skill/extension/layout-lab.js +1453 -0
- package/lib/mcp-skill/extension/logo.svg +14 -0
- package/lib/mcp-skill/extension/manifest.json +69 -0
- package/lib/mcp-skill/extension/panel-dom.js +90 -0
- package/lib/mcp-skill/extension/panel-settings.js +173 -0
- package/lib/mcp-skill/extension/popup.html +41 -0
- package/lib/mcp-skill/extension/popup.js +80 -0
- package/lib/mcp-skill/extension/probe-descriptor.js +241 -0
- package/lib/mcp-skill/extension/resolve-probes.js +342 -0
- package/lib/mcp-skill/extension/startup-handoff.js +30 -0
- package/lib/mcp-skill/package.json +1 -0
- package/lib/mcp-skill/references/README.md +56 -0
- package/lib/mcp-skill/references/active-stimulus.md +85 -0
- package/lib/mcp-skill/references/avalanche-detector.md +731 -0
- package/lib/mcp-skill/references/bdd-router.md +310 -0
- package/lib/mcp-skill/references/browser-runtime-api.md +193 -0
- package/lib/mcp-skill/references/command-output-formats.md +1085 -0
- package/lib/mcp-skill/references/command-surface.md +183 -0
- package/lib/mcp-skill/references/cynefin.md +99 -0
- package/lib/mcp-skill/references/debug-hook.md +161 -0
- package/lib/mcp-skill/references/fixing-performance.md +490 -0
- package/lib/mcp-skill/references/hook-metrics.md +199 -0
- package/lib/mcp-skill/references/hooks-inspection.md +61 -0
- package/lib/mcp-skill/references/jira-taxonomy.md +81 -0
- package/lib/mcp-skill/references/mcp.md +122 -0
- package/lib/mcp-skill/references/mode-a-injection.md +102 -0
- package/lib/mcp-skill/references/operating-model.md +128 -0
- package/lib/mcp-skill/references/playwright-cli-setup.md +105 -0
- package/lib/mcp-skill/references/playwright-probes.md +205 -0
- package/lib/mcp-skill/references/procedures.md +545 -0
- package/lib/mcp-skill/references/recipes.txt +114 -0
- package/lib/mcp-skill/references/stability-render.md +428 -0
- package/lib/mcp-skill/references/test-mode-playbook.md +194 -0
- package/lib/mcp-skill/references/troubleshooting.md +59 -0
- package/lib/mcp-skill/scripts/avalanche-analyze.cjs +495 -0
- package/lib/mcp-skill/scripts/build-avalanche-command.js +34 -0
- package/lib/mcp-skill/scripts/build-fiber-state-command.js +19 -0
- package/lib/mcp-skill/scripts/chippy-helper-server.js +1768 -0
- package/lib/mcp-skill/scripts/chippy-mcp.cjs +273 -0
- package/lib/mcp-skill/scripts/chippy.sh +1252 -0
- package/lib/mcp-skill/scripts/format-probe-result.js +34 -0
- package/lib/mcp-skill/scripts/helper-access.js +158 -0
- package/lib/mcp-skill/scripts/helper-proxy.js +87 -0
- package/lib/mcp-skill/scripts/page-socket-trust.js +81 -0
- package/lib/mcp-skill/scripts/probe-annotation-channel.js +116 -0
- package/lib/mcp-skill/scripts/search-recipes.js +248 -0
- package/lib/mcp-skill/snippets/README.md +89 -0
- package/lib/mcp-skill/snippets/count-context-consumers.js +40 -0
- package/lib/mcp-skill/snippets/find-context-providers.js +38 -0
- package/lib/mcp-skill/snippets/find-legacy-context.js +47 -0
- package/lib/mcp-skill/snippets/find-prop.js +83 -0
- package/lib/mcp-skill/snippets/hook-metrics-sample.js +69 -0
- package/lib/mcp-skill/snippets/hook-metrics.js +60 -0
- package/lib/mcp-skill/snippets/hook-tracker.js +198 -0
- package/lib/mcp-skill/snippets/stability-nudge.js +101 -0
- package/lib/package.json +1 -0
- package/lib/plugin.js +119 -0
- package/lib/plugin.js.map +7 -0
- package/lib/storybook/avalanche-panel-model.js +79 -0
- package/lib/storybook/brand.jsx +6 -0
- package/lib/storybook/connection-fallback.jsx +47 -0
- package/lib/storybook/constants.js +15 -0
- package/lib/storybook/context-card.jsx +171 -0
- package/lib/storybook/manager-owner.js +42 -0
- package/lib/storybook/manager.jsx +425 -0
- package/lib/storybook/preset.js +7 -0
- package/lib/storybook/preview.js +298 -0
- package/lib/storybook/probe-annotation-editor.jsx +52 -0
- package/lib/storybook/probe-intent.js +23 -0
- package/lib/storybook/setup.js +104 -0
- package/lib/types/avalanche/commit-metrics-adapter.d.ts +76 -0
- package/lib/types/avalanche/commit-stats-indices.d.ts +41 -0
- package/lib/types/avalanche/commit-surface-ownership.d.ts +9 -0
- package/lib/types/avalanche/detector.d.ts +183 -0
- package/lib/types/avalanche/interaction-capture.d.ts +25 -0
- package/lib/types/avalanche/internals.d.ts +189 -0
- package/lib/types/avalanche/report-serializer.d.ts +283 -0
- package/lib/types/avalanche/reporter.d.ts +610 -0
- package/lib/types/chippy-bridge/fiber-source.d.ts +48 -0
- package/lib/types/chippy-bridge/react-renderer.d.ts +74 -0
- package/lib/types/chippy-bridge/recorded-source.d.ts +44 -0
- package/lib/types/chippy-bridge/source-map.d.ts +30 -0
- package/lib/types/debug-render/core.d.ts +78 -0
- package/lib/types/layout-lab/fiber-boundaries.d.ts +51 -0
- package/lib/types/layout-lab/fiber-walk.d.ts +131 -0
- package/lib/types/layout-lab/marker-context.d.ts +94 -0
- package/lib/types/package/avalanche.d.ts +6 -0
- package/lib/types/package/browser.d.ts +10 -0
- package/lib/types/package/plugin.d.ts +15 -0
- package/lib/types/plugins/atl/commit-metrics-adapter.d.ts +65 -0
- package/lib/types/plugins/atl/metrics-slot.d.ts +37 -0
- package/lib/types/plugins/index.d.ts +49 -0
- package/lib/types/plugins/jira/context-provider.d.ts +36 -0
- package/package.json +106 -4
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generated from src/avalanche/offline-cli.ts by `yarn chippy:mcp:build`; do not edit.
|
|
3
|
+
|
|
4
|
+
// src/avalanche/offline-cli.ts
|
|
5
|
+
var import_node_fs = require("node:fs");
|
|
6
|
+
|
|
7
|
+
// src/chippy-bridge/avalanche-commands.ts
|
|
8
|
+
var UPSTREAM_COMMIT_STAT_KEYS = [
|
|
9
|
+
"collateral",
|
|
10
|
+
"sources",
|
|
11
|
+
"restarts",
|
|
12
|
+
"performedWork",
|
|
13
|
+
"effects",
|
|
14
|
+
"memos",
|
|
15
|
+
"callbacks",
|
|
16
|
+
"contextUpdates",
|
|
17
|
+
"unmountedComponents",
|
|
18
|
+
"unmountedSubtrees"
|
|
19
|
+
];
|
|
20
|
+
function tupleToStats(tuple) {
|
|
21
|
+
if (!Array.isArray(tuple)) return null;
|
|
22
|
+
const out = {};
|
|
23
|
+
for (let i = 0; i < UPSTREAM_COMMIT_STAT_KEYS.length; i++) {
|
|
24
|
+
out[UPSTREAM_COMMIT_STAT_KEYS[i]] = Number(tuple[i] || 0);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function compactLatestAction(action) {
|
|
29
|
+
if (!action || typeof action !== "object") return action ?? null;
|
|
30
|
+
const location = action.location && typeof action.location === "object" ? action.location : {};
|
|
31
|
+
const dom = location.dom && typeof location.dom === "object" ? location.dom : {};
|
|
32
|
+
const targetFiber = location.targetFiber && typeof location.targetFiber === "object" ? location.targetFiber : null;
|
|
33
|
+
const ownerStack = Array.isArray(targetFiber?.ownerStack) ? targetFiber.ownerStack : [];
|
|
34
|
+
return {
|
|
35
|
+
lastCommitId: Number(action.lastCommitId ?? 0),
|
|
36
|
+
deltaTime: Number(action.deltaTime ?? 0),
|
|
37
|
+
action: typeof action.action === "string" ? action.action : null,
|
|
38
|
+
dom: {
|
|
39
|
+
tag: typeof dom.tag === "string" ? dom.tag : null,
|
|
40
|
+
text: typeof dom.text === "string" ? dom.text : "",
|
|
41
|
+
nearestTestId: typeof dom.nearestTestId === "string" ? dom.nearestTestId : null
|
|
42
|
+
},
|
|
43
|
+
targetFiber: targetFiber ? {
|
|
44
|
+
name: typeof targetFiber.name === "string" ? targetFiber.name : null,
|
|
45
|
+
key: targetFiber.key ?? null,
|
|
46
|
+
ownerStackKind: targetFiber.ownerStackKind === "parents" ? "parents" : "owners",
|
|
47
|
+
ownerNames: ownerStack.map((entry) => entry && typeof entry.name === "string" ? entry.name : null).filter(Boolean).slice(0, 10),
|
|
48
|
+
ownerCount: ownerStack.length
|
|
49
|
+
} : null
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getReport(api) {
|
|
53
|
+
if (!api || typeof api.report !== "function") return null;
|
|
54
|
+
try {
|
|
55
|
+
const r = api.report();
|
|
56
|
+
if (r && typeof r.toSerializable === "function") {
|
|
57
|
+
return { raw: r, serial: r.toSerializable() };
|
|
58
|
+
}
|
|
59
|
+
return { raw: r, serial: r };
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return { error: e?.message || String(e) };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readAvalancheStatus(api, mod) {
|
|
65
|
+
if (!api || typeof api.status !== "function") {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
available: !!mod,
|
|
69
|
+
error: "__AVALANCHE__ API not available",
|
|
70
|
+
moduleKeys: mod ? Object.keys(mod) : null
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const status = api.status();
|
|
75
|
+
return { ok: true, available: true, status };
|
|
76
|
+
} catch (e) {
|
|
77
|
+
return { ok: false, available: true, error: e?.message || String(e) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function evalAvalanche(command, api, mod) {
|
|
81
|
+
const action = String(command?.action || "status");
|
|
82
|
+
if (!api) {
|
|
83
|
+
return { ok: false, available: false, action, error: "__AVALANCHE__ not available" };
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
switch (action) {
|
|
87
|
+
case "install": {
|
|
88
|
+
const opts = command && typeof command.options === "object" ? command.options : void 0;
|
|
89
|
+
const res = api.install?.(opts);
|
|
90
|
+
return { ok: true, available: true, action, result: res, status: readAvalancheStatus(api, mod).status };
|
|
91
|
+
}
|
|
92
|
+
case "uninstall":
|
|
93
|
+
api.uninstall?.();
|
|
94
|
+
return { ok: true, available: true, action, status: readAvalancheStatus(api, mod).status };
|
|
95
|
+
case "reset":
|
|
96
|
+
case "clear":
|
|
97
|
+
api.reset?.();
|
|
98
|
+
return { ok: true, available: true, action, status: readAvalancheStatus(api, mod).status };
|
|
99
|
+
case "status":
|
|
100
|
+
return { ok: true, available: true, action, status: readAvalancheStatus(api, mod).status };
|
|
101
|
+
case "export": {
|
|
102
|
+
const r = getReport(api);
|
|
103
|
+
if (!r) return { ok: false, available: true, action, error: "report() unavailable" };
|
|
104
|
+
if ("error" in r) return { ok: false, available: true, action, error: r.error };
|
|
105
|
+
return { ok: true, available: true, action, payload: r.serial };
|
|
106
|
+
}
|
|
107
|
+
case "export-last": {
|
|
108
|
+
const count = Number(command?.count);
|
|
109
|
+
const n = Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
110
|
+
let res;
|
|
111
|
+
try {
|
|
112
|
+
res = api.exportLast?.(n);
|
|
113
|
+
} catch (e) {
|
|
114
|
+
return { ok: false, available: true, action, error: e?.message || String(e) };
|
|
115
|
+
}
|
|
116
|
+
if (res && typeof res.toSerializable === "function") {
|
|
117
|
+
res = res.toSerializable();
|
|
118
|
+
}
|
|
119
|
+
return { ok: true, available: true, action, count: n, payload: res };
|
|
120
|
+
}
|
|
121
|
+
case "debug-hook": {
|
|
122
|
+
const sourceId = String(command?.sourceId || "");
|
|
123
|
+
const hookIndex = Number(command?.hookIndex);
|
|
124
|
+
const windowSize = Number(command?.window);
|
|
125
|
+
if (!sourceId || !Number.isFinite(hookIndex)) {
|
|
126
|
+
return { ok: false, available: true, action, error: "debug-hook requires sourceId and numeric hookIndex" };
|
|
127
|
+
}
|
|
128
|
+
if (typeof api.debugHook !== "function") {
|
|
129
|
+
return { ok: false, available: true, action, error: "debugHook() unavailable on __AVALANCHE__ API" };
|
|
130
|
+
}
|
|
131
|
+
const payload = api.debugHook(
|
|
132
|
+
sourceId,
|
|
133
|
+
Math.floor(hookIndex),
|
|
134
|
+
Number.isFinite(windowSize) ? Math.max(0, Math.floor(windowSize)) : void 0
|
|
135
|
+
);
|
|
136
|
+
return { ok: true, available: true, action, payload };
|
|
137
|
+
}
|
|
138
|
+
case "hot-sources": {
|
|
139
|
+
const limit = Number(command?.limit);
|
|
140
|
+
const n = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
|
|
141
|
+
const r = getReport(api);
|
|
142
|
+
if (!r || "error" in r) {
|
|
143
|
+
return { ok: false, available: true, action, error: r && "error" in r ? r.error : "report() unavailable" };
|
|
144
|
+
}
|
|
145
|
+
const sources = Array.isArray(r.serial.sources) ? r.serial.sources : [];
|
|
146
|
+
const items = sources.slice().sort((a, b) => Number(b?.collateralUpdates || 0) - Number(a?.collateralUpdates || 0)).slice(0, n).map((s) => ({
|
|
147
|
+
sourceId: s?.sourceId ?? null,
|
|
148
|
+
depth: Number(s?.depth || 0),
|
|
149
|
+
causedUpdates: Number(s?.causedUpdates || 0),
|
|
150
|
+
collateralUpdates: Number(s?.collateralUpdates || 0),
|
|
151
|
+
maxCollateralUpdates: Number(s?.maxCollateralUpdates || 0),
|
|
152
|
+
maxPenetrationDepth: Number(s?.maxPenetrationDepth || 0),
|
|
153
|
+
restartWaste: Number(s?.restartWaste || 0)
|
|
154
|
+
}));
|
|
155
|
+
return { ok: true, available: true, action, total: sources.length, payload: { count: items.length, items } };
|
|
156
|
+
}
|
|
157
|
+
case "commit-peek": {
|
|
158
|
+
const commitId = Number(command?.commitId);
|
|
159
|
+
if (!Number.isFinite(commitId)) {
|
|
160
|
+
return { ok: false, available: true, action, error: "commit-peek requires numeric commitId" };
|
|
161
|
+
}
|
|
162
|
+
const include = Array.isArray(command?.include) && command.include.length ? command.include : ["stats", "sources", "timing", "metrics"];
|
|
163
|
+
const allowedInclude = ["stats", "sources", "timing", "metrics", "atl", "unmounts", "actions", "rendered"];
|
|
164
|
+
const invalidInclude = include.filter((key) => !allowedInclude.includes(key));
|
|
165
|
+
if (invalidInclude.length) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
available: true,
|
|
169
|
+
action,
|
|
170
|
+
error: "unsupported commit-peek include value(s): " + invalidInclude.join(","),
|
|
171
|
+
validInclude: allowedInclude
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const maxItems = Number.isFinite(Number(command?.maxItems)) ? Math.max(1, Math.floor(Number(command.maxItems))) : 50;
|
|
175
|
+
const r = getReport(api);
|
|
176
|
+
if (!r || "error" in r) {
|
|
177
|
+
return { ok: false, available: true, action, error: r && "error" in r ? r.error : "report() unavailable" };
|
|
178
|
+
}
|
|
179
|
+
const ser = r.serial || {};
|
|
180
|
+
const tuple = ser.commitStats?.[commitId] || ser.commitStats?.[String(commitId)] || null;
|
|
181
|
+
const sourceIds = ser.sourcesByCommit?.[commitId] || ser.sourcesByCommit?.[String(commitId)] || [];
|
|
182
|
+
const timing = ser.commitTimings?.[commitId] || ser.commitTimings?.[String(commitId)] || null;
|
|
183
|
+
const metricsMap = ser.metricsByCommit || ser.atlByCommit;
|
|
184
|
+
const metrics = metricsMap?.[commitId] || metricsMap?.[String(commitId)] || null;
|
|
185
|
+
const actions = ser.interactionsByCommit?.[commitId] || ser.interactionsByCommit?.[String(commitId)] || [];
|
|
186
|
+
const rendered = ser.renderedByCommit?.[commitId] || ser.renderedByCommit?.[String(commitId)] || [];
|
|
187
|
+
if (!tuple && !sourceIds.length && !timing && !metrics && !actions.length && !rendered.length) {
|
|
188
|
+
return { ok: true, available: true, action, commitId, found: false };
|
|
189
|
+
}
|
|
190
|
+
const sourcesById = /* @__PURE__ */ new Map();
|
|
191
|
+
const allSources = ser.sources || [];
|
|
192
|
+
for (const s of allSources) {
|
|
193
|
+
if (s && s.sourceId != null) sourcesById.set(s.sourceId, s);
|
|
194
|
+
}
|
|
195
|
+
const out = { commitId, found: true };
|
|
196
|
+
if (include.includes("stats")) out.stats = tupleToStats(tuple);
|
|
197
|
+
if (include.includes("sources")) {
|
|
198
|
+
const entries = Array.isArray(sourceIds) ? sourceIds : [];
|
|
199
|
+
const slice = entries.slice(0, maxItems).map((entry) => {
|
|
200
|
+
const e = entry;
|
|
201
|
+
const sid = typeof e === "object" && e !== null && "sourceId" in e ? e.sourceId : entry;
|
|
202
|
+
const s = sourcesById.get(sid);
|
|
203
|
+
return s ? {
|
|
204
|
+
sourceId: s.sourceId,
|
|
205
|
+
depth: s.depth,
|
|
206
|
+
collateralAtCommit: typeof e === "object" && e !== null && "collateral" in e ? e.collateral : null,
|
|
207
|
+
rankAtCommit: typeof e === "object" && e !== null && "rank" in e ? e.rank : null,
|
|
208
|
+
trashers: typeof e === "object" && e !== null && "trashers" in e ? e.trashers : void 0,
|
|
209
|
+
causedUpdatesTotal: s.causedUpdates,
|
|
210
|
+
collateralUpdatesTotal: s.collateralUpdates
|
|
211
|
+
} : { sourceId: sid, missing: true };
|
|
212
|
+
});
|
|
213
|
+
out.sources = slice;
|
|
214
|
+
out.sourcesTotal = entries.length;
|
|
215
|
+
}
|
|
216
|
+
if (include.includes("timing")) out.timing = timing;
|
|
217
|
+
if (include.includes("metrics")) out.metrics = metrics;
|
|
218
|
+
if (include.includes("atl")) out.atl = metrics;
|
|
219
|
+
if (include.includes("actions")) {
|
|
220
|
+
out.actions = actions.slice(0, maxItems);
|
|
221
|
+
out.actionsTotal = actions.length;
|
|
222
|
+
}
|
|
223
|
+
if (include.includes("rendered")) {
|
|
224
|
+
out.rendered = rendered.slice(0, maxItems);
|
|
225
|
+
out.renderedTotal = rendered.length;
|
|
226
|
+
}
|
|
227
|
+
if (include.includes("unmounts")) {
|
|
228
|
+
const raw = r.raw || {};
|
|
229
|
+
const samples = raw.unmountsByCommit?.[commitId] || raw.unmountsByCommit?.[String(commitId)] || null;
|
|
230
|
+
out.unmounts = samples ? samples.slice(0, maxItems) : [];
|
|
231
|
+
out.unmountsTotal = Array.isArray(samples) ? samples.length : 0;
|
|
232
|
+
}
|
|
233
|
+
return { ok: true, available: true, action, payload: out };
|
|
234
|
+
}
|
|
235
|
+
case "describe-action": {
|
|
236
|
+
const actionId = Number(command?.actionId);
|
|
237
|
+
if (!Number.isFinite(actionId)) {
|
|
238
|
+
return { ok: false, available: true, action, error: "describe-action requires numeric actionId" };
|
|
239
|
+
}
|
|
240
|
+
const r = getReport(api);
|
|
241
|
+
if (!r || "error" in r) {
|
|
242
|
+
return { ok: false, available: true, action, error: r && "error" in r ? r.error : "report() unavailable" };
|
|
243
|
+
}
|
|
244
|
+
const ser = r.serial || {};
|
|
245
|
+
const byCommit = ser.interactionsByCommit || {};
|
|
246
|
+
for (const [commitId, items] of Object.entries(byCommit)) {
|
|
247
|
+
const actions = Array.isArray(items) ? items : [];
|
|
248
|
+
const found = actions.find((item) => Number(item?.id) === actionId);
|
|
249
|
+
if (found) {
|
|
250
|
+
return {
|
|
251
|
+
ok: true,
|
|
252
|
+
available: true,
|
|
253
|
+
action,
|
|
254
|
+
payload: { actionId, commitId: Number(commitId), found: true, action: found }
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return { ok: true, available: true, action, payload: { actionId, found: false } };
|
|
259
|
+
}
|
|
260
|
+
case "commit-list": {
|
|
261
|
+
const top = Number.isFinite(Number(command?.top)) ? Math.max(1, Math.floor(Number(command.top))) : 20;
|
|
262
|
+
const by = String(command?.by || "collateral");
|
|
263
|
+
const allowedKeys = UPSTREAM_COMMIT_STAT_KEYS.concat(["sources"]);
|
|
264
|
+
if (!allowedKeys.includes(by)) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
available: true,
|
|
268
|
+
action,
|
|
269
|
+
error: "unsupported commit-list sort key: " + by,
|
|
270
|
+
validBy: allowedKeys
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const sortKey = by;
|
|
274
|
+
const r = getReport(api);
|
|
275
|
+
if (!r || "error" in r) {
|
|
276
|
+
return { ok: false, available: true, action, error: r && "error" in r ? r.error : "report() unavailable" };
|
|
277
|
+
}
|
|
278
|
+
const ser = r.serial || {};
|
|
279
|
+
const stats = ser.commitStats || {};
|
|
280
|
+
const sourcesByCommit = ser.sourcesByCommit || {};
|
|
281
|
+
const ids = Object.keys(stats);
|
|
282
|
+
const rows = ids.map((id) => {
|
|
283
|
+
const tuple = stats[id];
|
|
284
|
+
const stat = tupleToStats(tuple);
|
|
285
|
+
const srcs = sourcesByCommit[id];
|
|
286
|
+
const sourceCount = Array.isArray(srcs) ? srcs.length : srcs && typeof srcs === "object" ? Object.keys(srcs).length : 0;
|
|
287
|
+
const actions = ser.interactionsByCommit?.[id] || [];
|
|
288
|
+
return { commitId: Number(id), stat, sourceCount, latestAction: actions.length ? compactLatestAction(actions[actions.length - 1]) : null };
|
|
289
|
+
});
|
|
290
|
+
rows.sort((a, b) => {
|
|
291
|
+
const av = sortKey === "sources" ? a.sourceCount || 0 : a.stat?.[sortKey] || 0;
|
|
292
|
+
const bv = sortKey === "sources" ? b.sourceCount || 0 : b.stat?.[sortKey] || 0;
|
|
293
|
+
return bv - av;
|
|
294
|
+
});
|
|
295
|
+
return { ok: true, available: true, action, by: sortKey, total: rows.length, rows: rows.slice(0, top) };
|
|
296
|
+
}
|
|
297
|
+
// Removed: start, stop, metrics, help — not present on the current public surface.
|
|
298
|
+
default:
|
|
299
|
+
return { ok: false, available: true, action, error: "Unknown avalanche action: " + action };
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
return { ok: false, available: true, action, error: error?.message || String(error) };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/avalanche/offline.ts
|
|
307
|
+
var PER_COMMIT_KEYS = [
|
|
308
|
+
"commitStats",
|
|
309
|
+
"suspenseByCommit",
|
|
310
|
+
"sourcesByCommit",
|
|
311
|
+
"interactionsByCommit",
|
|
312
|
+
"renderedByCommit",
|
|
313
|
+
"metricsByCommit",
|
|
314
|
+
"atlByCommit",
|
|
315
|
+
"timing",
|
|
316
|
+
"commitTimings"
|
|
317
|
+
];
|
|
318
|
+
function isSavedReport(value) {
|
|
319
|
+
const v = value;
|
|
320
|
+
return !!v && typeof v === "object" && !!v.meta && typeof v.commitStats === "object" && v.commitStats !== null;
|
|
321
|
+
}
|
|
322
|
+
function unwrapSavedReport(value) {
|
|
323
|
+
const candidates = [value, value?.payload, value?.result?.payload, value?.result];
|
|
324
|
+
return candidates.find(isSavedReport) ?? null;
|
|
325
|
+
}
|
|
326
|
+
function commitIds(report) {
|
|
327
|
+
return Object.keys(report.commitStats).map(Number).filter(Number.isFinite).sort((a, b) => a - b);
|
|
328
|
+
}
|
|
329
|
+
function keepLastCommits(report, count) {
|
|
330
|
+
if (!(count > 0)) return report;
|
|
331
|
+
const keep = new Set(commitIds(report).slice(-count).map(String));
|
|
332
|
+
const out = { ...report };
|
|
333
|
+
for (const key of PER_COMMIT_KEYS) {
|
|
334
|
+
const map = report[key];
|
|
335
|
+
if (!map || typeof map !== "object") continue;
|
|
336
|
+
out[key] = Object.fromEntries(Object.entries(map).filter(([id]) => keep.has(id)));
|
|
337
|
+
}
|
|
338
|
+
out.meta = { ...report.meta, keptLastCommits: keep.size };
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
function dispatch(report, command) {
|
|
342
|
+
const result = evalAvalanche(command, { report: () => report }, null);
|
|
343
|
+
const { available: _available, ...rest } = result;
|
|
344
|
+
return rest;
|
|
345
|
+
}
|
|
346
|
+
function summary(report) {
|
|
347
|
+
const totals = Object.fromEntries(UPSTREAM_COMMIT_STAT_KEYS.map((key) => [key, 0]));
|
|
348
|
+
for (const tuple of Object.values(report.commitStats)) {
|
|
349
|
+
const stats = tupleToStats(tuple);
|
|
350
|
+
if (!stats) continue;
|
|
351
|
+
for (const key of UPSTREAM_COMMIT_STAT_KEYS) totals[key] += stats[key];
|
|
352
|
+
}
|
|
353
|
+
const ids = commitIds(report);
|
|
354
|
+
return {
|
|
355
|
+
ok: true,
|
|
356
|
+
action: "summary",
|
|
357
|
+
commits: ids.length,
|
|
358
|
+
firstCommitId: ids[0] ?? null,
|
|
359
|
+
lastCommitId: ids.at(-1) ?? null,
|
|
360
|
+
sources: report.sources?.length ?? 0,
|
|
361
|
+
totals,
|
|
362
|
+
meta: report.meta
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function sourceSearch(report, options) {
|
|
366
|
+
let pattern;
|
|
367
|
+
try {
|
|
368
|
+
pattern = new RegExp(options.name, "i");
|
|
369
|
+
} catch (error) {
|
|
370
|
+
return { ok: false, action: "source-search", error: `invalid --name pattern: ${error.message}` };
|
|
371
|
+
}
|
|
372
|
+
const top = options.top && options.top > 0 ? options.top : 50;
|
|
373
|
+
const sources = (report.sources ?? []).filter((source) => pattern.test(String(source.sourceId ?? "")));
|
|
374
|
+
const rendered = /* @__PURE__ */ new Map();
|
|
375
|
+
const renderedByCommit = report.renderedByCommit ?? {};
|
|
376
|
+
for (const [commitId, entries] of Object.entries(renderedByCommit)) {
|
|
377
|
+
for (const entry of entries ?? []) {
|
|
378
|
+
if (!pattern.test(String(entry?.name ?? ""))) continue;
|
|
379
|
+
const row = rendered.get(entry.sourceId) ?? { name: entry.name, renders: 0, selfSource: 0, commits: [] };
|
|
380
|
+
row.renders += 1;
|
|
381
|
+
if (entry.isSelfSource) row.selfSource += 1;
|
|
382
|
+
row.commits.push(Number(commitId));
|
|
383
|
+
rendered.set(entry.sourceId, row);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const renderedRows = [...rendered.entries()].map(([sourceId, row]) => ({ sourceId, ...row, commits: row.commits.slice(0, 20) })).sort((a, b) => b.renders - a.renders);
|
|
387
|
+
return {
|
|
388
|
+
ok: true,
|
|
389
|
+
action: "source-search",
|
|
390
|
+
name: options.name,
|
|
391
|
+
sourcesTotal: sources.length,
|
|
392
|
+
sources: sources.slice(0, top),
|
|
393
|
+
renderedAvailable: Object.keys(renderedByCommit).length > 0,
|
|
394
|
+
renderedTotal: renderedRows.length,
|
|
395
|
+
rendered: renderedRows.slice(0, top)
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function analyzeSavedReport(report, command, args) {
|
|
399
|
+
const positional = args._;
|
|
400
|
+
const number = (flag, index) => {
|
|
401
|
+
const value = Number(args[flag] ?? positional[index]);
|
|
402
|
+
return Number.isFinite(value) ? value : void 0;
|
|
403
|
+
};
|
|
404
|
+
switch (command) {
|
|
405
|
+
case "summary":
|
|
406
|
+
return summary(report);
|
|
407
|
+
case "commit-list":
|
|
408
|
+
return dispatch(report, { action: "commit-list", top: number("top", 0), by: String(args.by ?? positional[1] ?? "collateral") });
|
|
409
|
+
case "commit-peek": {
|
|
410
|
+
const include = String(args.include ?? positional[1] ?? "");
|
|
411
|
+
return dispatch(report, {
|
|
412
|
+
action: "commit-peek",
|
|
413
|
+
commitId: number("commitId", 0),
|
|
414
|
+
include: include ? include.split(",").map((item) => item.trim()).filter(Boolean) : void 0,
|
|
415
|
+
maxItems: number("maxItems", 2)
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
case "hot-sources":
|
|
419
|
+
return dispatch(report, { action: "hot-sources", limit: number("limit", 0) });
|
|
420
|
+
case "describe-action":
|
|
421
|
+
return dispatch(report, { action: "describe-action", actionId: number("actionId", 0) });
|
|
422
|
+
case "source-search": {
|
|
423
|
+
const name = args.name ?? positional[0];
|
|
424
|
+
if (typeof name !== "string" || !name) return { ok: false, action: "source-search", error: "source-search requires --name <regex>" };
|
|
425
|
+
return sourceSearch(report, { name, top: number("top", 1) });
|
|
426
|
+
}
|
|
427
|
+
default:
|
|
428
|
+
return { ok: false, action: command, error: `unknown offline command: ${command}` };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/avalanche/offline-cli.ts
|
|
433
|
+
var USAGE = `Usage: avalanche-analyze.cjs <file> <command> [args]
|
|
434
|
+
|
|
435
|
+
Commands:
|
|
436
|
+
summary
|
|
437
|
+
commit-list [top] [by] (--top N --by KEY)
|
|
438
|
+
commit-peek <commitId> [include] [max] (--include csv --maxItems N)
|
|
439
|
+
hot-sources [limit]
|
|
440
|
+
describe-action <actionId>
|
|
441
|
+
source-search --name <regex> [--top N]
|
|
442
|
+
|
|
443
|
+
commit-list sort keys: collateral sources restarts performedWork effects memos
|
|
444
|
+
callbacks contextUpdates unmountedComponents unmountedSubtrees
|
|
445
|
+
commit-peek include values: stats,sources,timing,metrics,actions,rendered (atl: deprecated alias of metrics)
|
|
446
|
+
`;
|
|
447
|
+
function fail(message) {
|
|
448
|
+
process.stderr.write(`avalanche-analyze: ${message}
|
|
449
|
+
`);
|
|
450
|
+
process.exit(2);
|
|
451
|
+
}
|
|
452
|
+
function parseArgs(argv) {
|
|
453
|
+
const args = { _: [] };
|
|
454
|
+
for (let i = 0; i < argv.length; i++) {
|
|
455
|
+
const arg = argv[i];
|
|
456
|
+
if (!arg.startsWith("--")) {
|
|
457
|
+
args._.push(arg);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const eq = arg.indexOf("=");
|
|
461
|
+
if (eq > -1) args[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
462
|
+
else if (argv[i + 1] !== void 0 && !argv[i + 1].startsWith("--")) args[arg.slice(2)] = argv[++i];
|
|
463
|
+
else args[arg.slice(2)] = true;
|
|
464
|
+
}
|
|
465
|
+
return args;
|
|
466
|
+
}
|
|
467
|
+
function parseJson(text, label) {
|
|
468
|
+
try {
|
|
469
|
+
return JSON.parse(text);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
return fail(`invalid JSON in ${label}: ${error.message}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function main(argv) {
|
|
475
|
+
if (!argv.length || argv[0] === "-h" || argv[0] === "--help") {
|
|
476
|
+
process.stdout.write(USAGE);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (argv[0] === "normalize") {
|
|
480
|
+
const report2 = unwrapSavedReport(parseJson((0, import_node_fs.readFileSync)(0, "utf8"), "helper response"));
|
|
481
|
+
if (!report2) fail("response has no Avalanche export (expected meta and commitStats); check avalanche-status");
|
|
482
|
+
const trimmed = keepLastCommits(report2, Number(argv[1]) || 0);
|
|
483
|
+
process.stdout.write(JSON.stringify(trimmed));
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const [file, command, ...rest] = argv;
|
|
487
|
+
if (!command) fail("command missing");
|
|
488
|
+
const report = unwrapSavedReport(parseJson((0, import_node_fs.readFileSync)(file, "utf8"), file));
|
|
489
|
+
if (!report) fail(`${file} is not an Avalanche export (expected meta and commitStats)`);
|
|
490
|
+
const result = analyzeSavedReport(report, command, parseArgs(rest));
|
|
491
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
492
|
+
`);
|
|
493
|
+
if (result.ok === false) process.exitCode = 1;
|
|
494
|
+
}
|
|
495
|
+
main(process.argv.slice(2));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Extracted from chippy-helper-server.js so tests can import it.
|
|
3
|
+
// Single responsibility: translate POST body fields into a bridge command.
|
|
4
|
+
// Every new forwarded field must have a corresponding test in
|
|
5
|
+
// src/chippy-bridge/wire-forwarding.test.ts.
|
|
6
|
+
|
|
7
|
+
function buildAvalancheCommand(body = {}, requestId = 'test-request-id') {
|
|
8
|
+
const command = {
|
|
9
|
+
type: 'eval-avalanche',
|
|
10
|
+
requestId,
|
|
11
|
+
action: String(body.action || 'status'),
|
|
12
|
+
};
|
|
13
|
+
if (body.options && typeof body.options === 'object') command.options = body.options;
|
|
14
|
+
|
|
15
|
+
// Legacy fields (start.max, export-last.count) — kept for backward compat
|
|
16
|
+
// with older callers; new bridge ignores them in favour of upstream-shape
|
|
17
|
+
// arguments. Will be removed once external callers migrate.
|
|
18
|
+
if (body.max != null) command.max = Number(body.max);
|
|
19
|
+
if (body.count != null) command.count = Number(body.count);
|
|
20
|
+
if (body.limit != null) command.limit = Number(body.limit);
|
|
21
|
+
if (body.commitId != null) command.commitId = Number(body.commitId);
|
|
22
|
+
if (body.top != null) command.top = Number(body.top);
|
|
23
|
+
if (body.by != null) command.by = String(body.by);
|
|
24
|
+
if (body.depth != null) command.depth = Number(body.depth);
|
|
25
|
+
if (body.maxItems != null) command.maxItems = Number(body.maxItems);
|
|
26
|
+
if (body.sourceId != null) command.sourceId = String(body.sourceId);
|
|
27
|
+
if (body.hookIndex != null) command.hookIndex = Number(body.hookIndex);
|
|
28
|
+
if (body.window != null) command.window = Number(body.window);
|
|
29
|
+
if (body.actionId != null) command.actionId = Number(body.actionId);
|
|
30
|
+
if (Array.isArray(body.include)) command.include = body.include.map(String);
|
|
31
|
+
return command;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { buildAvalancheCommand };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
function buildFiberStateCaptureCommand(body = {}) {
|
|
4
|
+
return {
|
|
5
|
+
type: 'capture-fiber-state',
|
|
6
|
+
options: { maxFibers: body.maxFibers },
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function buildFiberStateCompareCommand(before, after, body = {}) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'compare-fiber-states',
|
|
13
|
+
before,
|
|
14
|
+
after,
|
|
15
|
+
options: { maxChanges: body.maxChanges },
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { buildFiberStateCaptureCommand, buildFiberStateCompareCommand };
|