@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,325 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Chippy background service worker.
|
|
3
|
+
// Ensures the content script + bundle scripts are injected into tabs even when
|
|
4
|
+
// Chrome's declarative content_scripts injection fails (e.g. after extension
|
|
5
|
+
// reload, on service-worker-served pages, or on fast-loading pages).
|
|
6
|
+
//
|
|
7
|
+
// Injection is split into EARLY_STARTUP_SCRIPTS and LATE_BUNDLE_SCRIPTS. The
|
|
8
|
+
// first group must beat react-dom evaluation; the second group can wait until
|
|
9
|
+
// the page owner is known and only installs the bridge/probe UI.
|
|
10
|
+
|
|
11
|
+
const DEFAULT_HELPER = 'http://127.0.0.1:7823';
|
|
12
|
+
// A packed build (`chippy pack`) prepends globalThis.__CHIPPY_BUILD to this file
|
|
13
|
+
// with the plugins it enables until the user saves their own selection.
|
|
14
|
+
const BUILD_DEFAULT_PLUGINS = Array.isArray(globalThis.__CHIPPY_BUILD?.defaultPlugins) ? globalThis.__CHIPPY_BUILD.defaultPlugins : [];
|
|
15
|
+
const CONTENT_SCRIPTS = ['panel-settings.js', 'content-script.js'];
|
|
16
|
+
// Registers early startup for the navigation after an armed reload.
|
|
17
|
+
importScripts('armed-reload-startup.js');
|
|
18
|
+
const EXTENSION_INTEGRATION = Object.freeze({
|
|
19
|
+
kind: 'extension',
|
|
20
|
+
who: 'Chippy browser extension',
|
|
21
|
+
source: '.agents/skills/chippy/extension/background.js',
|
|
22
|
+
reinjectsOnReload: true,
|
|
23
|
+
reloadAction: 'No user action required; the enabled extension re-injects Chippy after navigation.',
|
|
24
|
+
});
|
|
25
|
+
// IIFE bundle of Chippy's avalanche adapter. Loads in MAIN world before
|
|
26
|
+
// the document parses so __AVALANCHE__ is published before any React mounts.
|
|
27
|
+
const EARLY_STARTUP_SCRIPTS = [
|
|
28
|
+
// MUST be first. Installs the React DevTools hook shim BEFORE react-dom or
|
|
29
|
+
// any page-React module evaluates. Without it, react-dom does not register
|
|
30
|
+
// its renderer into hook.renderers, and Avalanche debug-render cannot find
|
|
31
|
+
// ReactCurrentDispatcher to capture hook call stacks. That collapses
|
|
32
|
+
// debugHook() to a flat fallback (no custom-hook folders, no callerLoc).
|
|
33
|
+
'devtools-hook-shim.js',
|
|
34
|
+
'avalanche.iife.js',
|
|
35
|
+
'early-bootstrap.js',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const LATE_BUNDLE_SCRIPTS = [
|
|
39
|
+
'inspect-react.js',
|
|
40
|
+
'panel-dom.js',
|
|
41
|
+
'layout-lab.js',
|
|
42
|
+
'resolve-probes.js',
|
|
43
|
+
'chippy-page-bridge.js',
|
|
44
|
+
];
|
|
45
|
+
// Late bundle is bridge/probes only. Avalanche lifecycle is owned by
|
|
46
|
+
// EARLY_STARTUP_SCRIPTS; re-injecting avalanche code here causes duplicate
|
|
47
|
+
// globals and races bootstrap ordering.
|
|
48
|
+
|
|
49
|
+
// Inject content script into a tab, ignoring errors (already injected, restricted page, etc.)
|
|
50
|
+
async function ensureContentScript(tabId) {
|
|
51
|
+
try {
|
|
52
|
+
await chrome.scripting.executeScript({
|
|
53
|
+
target: { tabId },
|
|
54
|
+
files: CONTENT_SCRIPTS,
|
|
55
|
+
});
|
|
56
|
+
} catch {
|
|
57
|
+
// Ignore — tab may be restricted (chrome://, edge://, etc.)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async function getStartupConfig(origin, helperBaseUrl, clientId, plugins) {
|
|
63
|
+
const helperBase = (helperBaseUrl || DEFAULT_HELPER).replace(/\/+$/, '');
|
|
64
|
+
const config = {
|
|
65
|
+
enabled: true,
|
|
66
|
+
helperBaseUrl: helperBaseUrl || DEFAULT_HELPER,
|
|
67
|
+
avalanche: null,
|
|
68
|
+
// Plugins the user enabled in the panel settings, or the build's defaults.
|
|
69
|
+
plugins: Array.isArray(plugins) ? plugins : [],
|
|
70
|
+
contextUiEnabled: true,
|
|
71
|
+
integration: EXTENSION_INTEGRATION,
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
if (clientId) {
|
|
75
|
+
const rc = await fetch(`${helperBase}/__chippy/api/avalanche-arm?clientId=${encodeURIComponent(clientId)}`, { signal: AbortSignal.timeout(1000) });
|
|
76
|
+
if (rc.ok) {
|
|
77
|
+
const data = await rc.json();
|
|
78
|
+
if (data.armed && data.config) {
|
|
79
|
+
config.avalanche = data.config;
|
|
80
|
+
return config;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const ro = await fetch(`${helperBase}/__chippy/api/avalanche-arm?origin=${encodeURIComponent(origin)}`, { signal: AbortSignal.timeout(1000) });
|
|
85
|
+
if (ro.ok) {
|
|
86
|
+
const data = await ro.json();
|
|
87
|
+
if (data.armed && data.config) config.avalanche = data.config;
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
return config;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function lookupClientIdForTab(tabId, helperBaseUrl) {
|
|
94
|
+
const helperBase = (helperBaseUrl || DEFAULT_HELPER).replace(/\/+$/, '');
|
|
95
|
+
try {
|
|
96
|
+
const r = await fetch(`${helperBase}/__chippy/api/pages`, { signal: AbortSignal.timeout(1000) });
|
|
97
|
+
if (!r.ok) return null;
|
|
98
|
+
const data = await r.json();
|
|
99
|
+
const pages = Array.isArray(data?.pages) ? data.pages : [];
|
|
100
|
+
const hit = pages.find((p) => Number(p?.tabId) === Number(tabId));
|
|
101
|
+
return hit?.clientId || null;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Extension is the DEFAULT/fallback integration. Any explicitly user-driven
|
|
108
|
+
// owner (Storybook addon, direct import, etc.) outranks it: when a live owner
|
|
109
|
+
// with integration.kind !== 'extension' already holds window.__CHIPPY_OWNER__,
|
|
110
|
+
// the extension stands down instead of stomping the lock. Runs in the page
|
|
111
|
+
// (MAIN) world because __CHIPPY_OWNER__ only exists there.
|
|
112
|
+
async function detectLiveForeignOwner(tabId) {
|
|
113
|
+
try {
|
|
114
|
+
const results = await chrome.scripting.executeScript({
|
|
115
|
+
target: { tabId },
|
|
116
|
+
world: 'MAIN',
|
|
117
|
+
injectImmediately: true,
|
|
118
|
+
func: () => {
|
|
119
|
+
const owner = window.__CHIPPY_OWNER__;
|
|
120
|
+
if (!owner || !owner.integration) return null;
|
|
121
|
+
// Extension never yields to another extension owner (that is itself).
|
|
122
|
+
if (owner.integration.kind === 'extension') return null;
|
|
123
|
+
// Respect the owner's own liveness contract; a stale owner is ignored.
|
|
124
|
+
const staleAfterMs = owner.staleAfterMs;
|
|
125
|
+
const heartbeatAt = owner.heartbeatAt || 0;
|
|
126
|
+
if (staleAfterMs && Date.now() - heartbeatAt > staleAfterMs) return null;
|
|
127
|
+
return {
|
|
128
|
+
ownerId: owner.ownerId || null,
|
|
129
|
+
kind: owner.integration.kind || null,
|
|
130
|
+
who: owner.integration.who || null,
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return results?.[0]?.result || null;
|
|
135
|
+
} catch {
|
|
136
|
+
// If we cannot probe the page world, do not block injection.
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function injectEarlyStartup(tabId, startupConfig) {
|
|
142
|
+
// Timing-critical path: do NOT run an owner-detection preflight before these
|
|
143
|
+
// startup scripts. reload-with-avalanche relies on devtools-hook-shim +
|
|
144
|
+
// avalanche.iife.js landing as early as possible on navigation. Owner/yield
|
|
145
|
+
// checks happen later in ensureBundleScripts before bridge/probes activation.
|
|
146
|
+
try {
|
|
147
|
+
await chrome.scripting.executeScript({
|
|
148
|
+
target: { tabId },
|
|
149
|
+
world: 'MAIN',
|
|
150
|
+
injectImmediately: true,
|
|
151
|
+
func: (cfg, integration) => {
|
|
152
|
+
window.__CHIPPY_STARTUP_CONFIG = cfg;
|
|
153
|
+
// Stamp the extension as the page owner so its identity is stable no
|
|
154
|
+
// matter which config-write path (early startup, bundle, or content
|
|
155
|
+
// script apply-config) wins. getIntegrationIdentity() falls back to
|
|
156
|
+
// __CHIPPY_OWNER__.integration, so this guarantees integration is never
|
|
157
|
+
// null on an extension-owned page. We reach here only when no live
|
|
158
|
+
// foreign owner exists (checked above), so we never overwrite the
|
|
159
|
+
// Storybook addon / import owner.
|
|
160
|
+
const existing = window.__CHIPPY_OWNER__;
|
|
161
|
+
const existingLive =
|
|
162
|
+
existing &&
|
|
163
|
+
existing.integration &&
|
|
164
|
+
existing.integration.kind !== 'extension' &&
|
|
165
|
+
(!existing.staleAfterMs || Date.now() - (existing.heartbeatAt || 0) <= existing.staleAfterMs);
|
|
166
|
+
if (!existingLive) {
|
|
167
|
+
window.__CHIPPY_OWNER__ = {
|
|
168
|
+
ownerId: 'chippy-extension',
|
|
169
|
+
integration,
|
|
170
|
+
heartbeatAt: Date.now(),
|
|
171
|
+
staleAfterMs: 15000,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
args: [startupConfig, EXTENSION_INTEGRATION],
|
|
176
|
+
});
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return { ok: false, step: 'startup-config', error: e.message };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const file of EARLY_STARTUP_SCRIPTS) {
|
|
182
|
+
try {
|
|
183
|
+
await chrome.scripting.executeScript({
|
|
184
|
+
target: { tabId },
|
|
185
|
+
files: [file],
|
|
186
|
+
world: 'MAIN',
|
|
187
|
+
injectImmediately: true,
|
|
188
|
+
});
|
|
189
|
+
} catch (e) {
|
|
190
|
+
return { ok: false, step: `startup:${file}`, error: e.message };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { ok: true, detail: `${EARLY_STARTUP_SCRIPTS.length} early scripts injected` };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Write Chippy config into the page world and inject non-startup bundle scripts.
|
|
198
|
+
async function ensureBundleScripts(tabId, helperBaseUrl, options = {}) {
|
|
199
|
+
const foreignOwner = await detectLiveForeignOwner(tabId);
|
|
200
|
+
if (foreignOwner) {
|
|
201
|
+
return { ok: true, yielded: true, detail: `extension yielded to ${foreignOwner.who || foreignOwner.kind}` };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const config = {
|
|
205
|
+
helperBaseUrl: helperBaseUrl || DEFAULT_HELPER,
|
|
206
|
+
activated: true,
|
|
207
|
+
contextUiEnabled: true,
|
|
208
|
+
integration: EXTENSION_INTEGRATION,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
if (options.avalanche) config.avalanche = options.avalanche;
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
await chrome.scripting.executeScript({
|
|
215
|
+
target: { tabId },
|
|
216
|
+
func: (nextConfig) => {
|
|
217
|
+
window.__CHIPPY = window.__CHIPPY || {};
|
|
218
|
+
window.__CHIPPY.config = {
|
|
219
|
+
...(window.__CHIPPY.config || {}),
|
|
220
|
+
...nextConfig,
|
|
221
|
+
};
|
|
222
|
+
try {
|
|
223
|
+
window.__CHIPPY?.pageBridge?.applyConfig?.(window.__CHIPPY.config);
|
|
224
|
+
} catch {}
|
|
225
|
+
},
|
|
226
|
+
args: [config],
|
|
227
|
+
world: 'MAIN',
|
|
228
|
+
});
|
|
229
|
+
} catch (e) {
|
|
230
|
+
return { ok: false, step: 'set-config', error: e.message };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
for (const file of LATE_BUNDLE_SCRIPTS) {
|
|
234
|
+
try {
|
|
235
|
+
await chrome.scripting.executeScript({
|
|
236
|
+
target: { tabId },
|
|
237
|
+
files: [file],
|
|
238
|
+
world: 'MAIN',
|
|
239
|
+
});
|
|
240
|
+
} catch (e) {
|
|
241
|
+
return { ok: false, step: `inject:${file}`, error: e.message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return { ok: true, detail: `${LATE_BUNDLE_SCRIPTS.length} late scripts injected via background` };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Full injection: content script + bundle
|
|
249
|
+
async function ensureAll(tabId, helperBaseUrl, options = {}) {
|
|
250
|
+
await ensureContentScript(tabId);
|
|
251
|
+
return ensureBundleScripts(tabId, helperBaseUrl, options);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// When popup/content-script ask us to ensure injection
|
|
255
|
+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
256
|
+
const messageType = message?.type;
|
|
257
|
+
|
|
258
|
+
if (messageType === 'chippy-ensure-content-script' && message.tabId) {
|
|
259
|
+
ensureContentScript(message.tabId).then(() => sendResponse({ ok: true })).catch((e) => {
|
|
260
|
+
sendResponse({ ok: false, step: 'ensure-content-script', error: e.message });
|
|
261
|
+
});
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (messageType === 'chippy-inject-bundle') {
|
|
266
|
+
const tabId = message.tabId || sender.tab?.id;
|
|
267
|
+
if (!tabId) {
|
|
268
|
+
sendResponse({ ok: false, step: 'resolve-tab', error: 'No tabId available for bundle injection' });
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
ensureBundleScripts(tabId, message.helperBaseUrl || DEFAULT_HELPER, message.options || {})
|
|
273
|
+
.then((result) => sendResponse(result))
|
|
274
|
+
.catch((e) => sendResponse({ ok: false, step: 'inject-bundle', error: e.message }));
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return false;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Early startup-critical injection on navigation commit.
|
|
282
|
+
chrome.webNavigation.onCommitted.addListener(async (details) => {
|
|
283
|
+
if (details.frameId !== 0) return;
|
|
284
|
+
const tabId = details.tabId;
|
|
285
|
+
try {
|
|
286
|
+
const tab = await chrome.tabs.get(tabId);
|
|
287
|
+
if (!tab?.url) return;
|
|
288
|
+
const origin = new URL(tab.url).origin;
|
|
289
|
+
const stored = await chrome.storage.local.get({ helperBaseUrl: DEFAULT_HELPER, enabledOrigins: {}, plugins: BUILD_DEFAULT_PLUGINS });
|
|
290
|
+
if (!stored.enabledOrigins?.[origin]) return;
|
|
291
|
+
const clientId = await lookupClientIdForTab(tabId, stored.helperBaseUrl);
|
|
292
|
+
const startupConfig = await getStartupConfig(origin, stored.helperBaseUrl, clientId, stored.plugins);
|
|
293
|
+
await injectEarlyStartup(tabId, startupConfig);
|
|
294
|
+
if (startupConfig && startupConfig.avalanche && startupConfig.avalanche.autoStart) {
|
|
295
|
+
try {
|
|
296
|
+
await fetch(`${(stored.helperBaseUrl || DEFAULT_HELPER).replace(/\/+$/, '')}/__chippy/api/avalanche-arm`, {
|
|
297
|
+
method: 'POST',
|
|
298
|
+
headers: { 'Content-Type': 'application/json' },
|
|
299
|
+
body: JSON.stringify({ origin, clientId, autoStart: false }),
|
|
300
|
+
signal: AbortSignal.timeout(1000),
|
|
301
|
+
});
|
|
302
|
+
} catch {}
|
|
303
|
+
}
|
|
304
|
+
} catch (e) {
|
|
305
|
+
console.warn(`[chippy-bg] early-inject failed tab=${tabId}: ${e.message}`);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Re-inject non-startup bundle on navigation complete for enabled origins.
|
|
310
|
+
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|
311
|
+
if (changeInfo.status !== 'complete' || !tab.url) return;
|
|
312
|
+
|
|
313
|
+
chrome.storage.local.get({ helperBaseUrl: DEFAULT_HELPER, enabledOrigins: {}, plugins: BUILD_DEFAULT_PLUGINS }, async (stored) => {
|
|
314
|
+
try {
|
|
315
|
+
const origin = new URL(tab.url).origin;
|
|
316
|
+
if (!stored.enabledOrigins?.[origin]) return;
|
|
317
|
+
const clientId = await lookupClientIdForTab(tabId, stored.helperBaseUrl);
|
|
318
|
+
const startupConfig = await getStartupConfig(origin, stored.helperBaseUrl, clientId, stored.plugins);
|
|
319
|
+
const options = startupConfig.avalanche ? { avalanche: startupConfig.avalanche } : {};
|
|
320
|
+
ensureAll(tabId, stored.helperBaseUrl, options).catch((e) => {
|
|
321
|
+
console.warn(`[chippy-bg] late-inject failed for ${origin} in tab ${tabId}: ${e.message}`);
|
|
322
|
+
});
|
|
323
|
+
} catch {}
|
|
324
|
+
});
|
|
325
|
+
});
|