copilot-usage-studio 0.2.0 → 0.2.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/CHANGELOG.md +30 -2
- package/README.md +46 -13
- package/data/github-copilot-pricing.json +34 -6
- package/dist/copilot-usage-studio/browser/chunk-23NLHIOR.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-5JYQJM5G.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-7I2HDBC6.js +1 -0
- package/dist/copilot-usage-studio/browser/{chunk-TVSYR63W.js → chunk-OS2XPCVW.js} +2 -2
- package/dist/copilot-usage-studio/browser/{chunk-J47KKACI.js → chunk-QMNOUQ2X.js} +1 -1
- package/dist/copilot-usage-studio/browser/{chunk-NXH37FKU.js → chunk-RTOXDOOI.js} +1 -1
- package/dist/copilot-usage-studio/browser/chunk-TICIZRSM.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-WOIYAEKB.js +4 -0
- package/dist/copilot-usage-studio/browser/{chunk-QQOFM3HF.js → chunk-WWU6QMJC.js} +1 -1
- package/dist/copilot-usage-studio/browser/chunk-XMVGLQQH.js +1 -0
- package/dist/copilot-usage-studio/browser/index.html +2 -2
- package/dist/copilot-usage-studio/browser/main-XFPP45VE.js +1 -0
- package/dist/copilot-usage-studio/browser/styles-7RDDQXQV.css +1 -0
- package/docs/local-deployment.md +92 -17
- package/docs/pricing.md +5 -3
- package/docs/scanner-api.md +40 -0
- package/lib/cli.mjs +15 -1
- package/lib/local-runtime.mjs +499 -19
- package/lib/scanner-worker.mjs +25 -0
- package/package.json +20 -2
- package/scripts/pricing-utils.mjs +5 -0
- package/scripts/scan-vscode-sessions.mjs +387 -1475
- package/scripts/scanner-customization-evidence.mjs +613 -0
- package/scripts/scanner-customization-inventory.mjs +1022 -0
- package/scripts/scanner-memory.mjs +229 -0
- package/scripts/scanner-session-parser.mjs +1237 -0
- package/scripts/scanner-traversal.mjs +203 -0
- package/scripts/scanner-workspace.mjs +237 -0
- package/dist/copilot-usage-studio/browser/chunk-CUKTUQ6W.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-EYAHOEVS.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-FXNLFSUB.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-HJNYITFH.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-KDAJN6DF.js +0 -4
- package/dist/copilot-usage-studio/browser/main-TL27ZSEQ.js +0 -1
- package/dist/copilot-usage-studio/browser/styles-GZOQ5QIH.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*{box-sizing:border-box}html,body{margin:0;min-height:100%;font-family:Aptos,Segoe UI Variable,Segoe UI,Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;line-height:1.45;background:#090d14;color:#d7dde7;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}html[data-theme=light],html[data-theme=light] body{background:#f4f7fb;color:#182536}html[data-theme=dark],html[data-theme=dark] body{background:#090d14;color:#d7dde7}button,input,select{font:inherit}code{border:1px solid color-mix(in srgb,var(--accent, #6f5cff) 18%,var(--line, #d7e1ea));border-radius:5px;padding:1px 5px;background:color-mix(in srgb,var(--accent, #6f5cff) 8%,transparent);color:var(--text-strong, #182536);font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,monospace;font-size:.92em}.data-refresh{display:grid;justify-items:end;min-width:0}.data-refresh-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;border:1px solid var(--line);border-radius:9px;padding:5px 10px;background:var(--control-bg);color:var(--text-strong);font-size:12px;font-weight:760;cursor:pointer;box-shadow:var(--shadow-soft)}.data-refresh-button:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}.data-refresh-button:disabled{cursor:progress;opacity:.72}.refresh-icon{display:grid;place-items:center;width:16px;height:16px;font-size:17px;line-height:1;transform-origin:50% 50%}.refresh-icon.spinning{animation:refresh-spin .8s linear infinite}.data-refresh-status{max-width:190px;overflow:hidden;color:var(--muted-2);font-size:10px;line-height:1.2;text-overflow:ellipsis;white-space:nowrap}.data-refresh.error .data-refresh-status{color:var(--danger-text)}.host-vscode .data-refresh-status{display:none}@keyframes refresh-spin{to{transform:rotate(360deg)}}@media(max-width:760px){.data-refresh-status{display:none}}@media(max-width:520px){.data-refresh-button>span:last-child{display:none}.data-refresh-button{padding-inline:5px}}
|
package/docs/local-deployment.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
The app is intended to run locally, near the VS Code data it reads. It should not require a hosted SaaS backend for the core workflow.
|
|
4
4
|
|
|
5
|
-
Current release posture:
|
|
5
|
+
Current release posture: the VS Code Marketplace extension is the product path. The npm package remains a development and fallback host.
|
|
6
6
|
|
|
7
|
-
Packaging foundation now available: the scanner exposes an in-memory Node API through `lib/scanner-api.mjs`. The existing scan command
|
|
7
|
+
Packaging foundation now available: the scanner exposes an in-memory Node API through `lib/scanner-api.mjs`. The existing scan command, local runtime, npm host, and VS Code extension preview use the same scanner/runtime core so behavior stays aligned across hosts.
|
|
8
8
|
|
|
9
9
|
Supported source today: VS Code GitHub Copilot Chat and Agent local storage. Visual Studio, JetBrains IDEs, Copilot CLI, GitHub.com chat, and GitHub billing exports are outside the current importer scope.
|
|
10
10
|
|
|
@@ -76,7 +76,7 @@ npm run test:scripts
|
|
|
76
76
|
npm run build
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
Release copy should describe the app as a local Copilot usage inspector for VS Code. The strongest promise is: "VS Code already logs useful usage data locally; this app makes it understandable for developers who do not have billing-console access."
|
|
79
|
+
Release copy should describe the app as a local Copilot usage inspector for VS Code, now available as a local VSIX preview. The strongest promise is: "VS Code already logs useful usage data locally; this app makes it understandable for developers who do not have billing-console access."
|
|
80
80
|
|
|
81
81
|
### Packaged Data Location
|
|
82
82
|
|
|
@@ -92,13 +92,24 @@ The npm tarball contains the compiled UI, scanner, pricing table, and runtime co
|
|
|
92
92
|
|
|
93
93
|
GitHub Actions is the release control plane:
|
|
94
94
|
|
|
95
|
-
- `.github/workflows/ci.yml` runs the full release gate for pull requests
|
|
95
|
+
- `.github/workflows/ci.yml` runs the full release gate and packages a VSIX artifact for every pushed branch, plus pull requests to `main`.
|
|
96
96
|
- `.github/workflows/release.yml` runs automatically for version tags such as `v0.1.1`, with a manual existing-tag mode for repair and backfill.
|
|
97
|
-
- The release workflow verifies that the tag matches
|
|
97
|
+
- The release workflow verifies that the tag matches both package versions, runs the release gate, packages one VSIX, publishes that artifact to the VS Code Marketplace, and attaches the exact VSIX to the matching GitHub Release.
|
|
98
|
+
- Marketplace publication uses GitHub OIDC and Microsoft Entra workload identity federation. No Marketplace token or client secret is stored in GitHub.
|
|
98
99
|
- A failed workflow can be rerun safely: an existing npm version is accepted only when its published `gitHead` matches the exact tagged commit. Conflicting or unverifiable versions are refused.
|
|
99
|
-
-
|
|
100
|
+
- Every release and repair run passes the current release gate before publication.
|
|
100
101
|
|
|
101
|
-
This keeps GitHub,
|
|
102
|
+
This keeps GitHub, release artifacts, release notes, and the source tag tied to the same commit. Ordinary pushes never publish.
|
|
103
|
+
|
|
104
|
+
### Branch Validation
|
|
105
|
+
|
|
106
|
+
For feature work, push the branch and let CI run:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
git push -u origin your-branch
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The CI run proves the npm app and VS Code extension package from that branch. Download the `copilot-usage-studio-vsix` artifact from the workflow run when you want to test exactly what GitHub built. For a solo-maintainer flow, a pull request is useful for review and history, but branch CI is enough for a pre-merge smoke test.
|
|
102
113
|
|
|
103
114
|
### One-Time npm Setup
|
|
104
115
|
|
|
@@ -109,14 +120,34 @@ Commit and push the workflow files first, then configure npm Trusted Publishing
|
|
|
109
120
|
3. Set repository to `copilot-usage-studio`.
|
|
110
121
|
4. Set workflow filename to `release.yml`.
|
|
111
122
|
5. Allow the `npm publish` action.
|
|
112
|
-
6.
|
|
123
|
+
6. Set the trusted-publisher environment to `marketplace`, matching the protected environment used by the release job.
|
|
113
124
|
|
|
114
125
|
The workflow uses GitHub's OIDC identity, so no long-lived `NPM_TOKEN` repository secret is required. npm automatically adds provenance for a public package published from a public repository through Trusted Publishing.
|
|
115
126
|
|
|
127
|
+
### One-Time VS Code Marketplace Setup
|
|
128
|
+
|
|
129
|
+
1. In Microsoft Entra ID, create an app registration for Marketplace publishing. Record its application (client) ID and directory (tenant) ID. Do not create a client secret.
|
|
130
|
+
2. Add a federated credential to that app registration using the GitHub Actions scenario:
|
|
131
|
+
- organization: `fortunes-guardian`
|
|
132
|
+
- repository: `copilot-usage-studio`
|
|
133
|
+
- entity type: **Environment**
|
|
134
|
+
- environment: `marketplace`
|
|
135
|
+
3. In GitHub, create an environment named `marketplace`. Restrict deployment to protected version tags when practical.
|
|
136
|
+
4. Add environment variables `AZURE_CLIENT_ID` and `AZURE_TENANT_ID`. These identifiers are not credentials; GitHub's short-lived OIDC assertion is the credential.
|
|
137
|
+
5. After this workflow is merged to `main`, run **Marketplace Identity Check** from GitHub Actions.
|
|
138
|
+
6. Copy the profile ID from the workflow summary. In the Visual Studio Marketplace publisher management page, add that identity ID as a member of publisher `fortunes-guardian` with the **Contributor** role.
|
|
139
|
+
7. Rerun **Marketplace Identity Check** if publisher membership or federation changes.
|
|
140
|
+
|
|
141
|
+
The release job uses `azure/login` with GitHub OIDC, then publishes with `vsce --azure-credential`. No PAT, client secret, or certificate is created. The workflow publishes the already packaged `tmp/*.vsix`, and that exact file is uploaded to the GitHub Release.
|
|
142
|
+
|
|
143
|
+
References: [VS Code secure automated publishing](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#secure-automated-publishing-to-visual-studio-marketplace) and [GitHub OIDC with Azure](https://docs.github.com/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-azure).
|
|
144
|
+
|
|
116
145
|
### Publishing a Version
|
|
117
146
|
|
|
118
147
|
Start from an up-to-date, clean `main` branch. Choose the semantic-version bump that matches the change:
|
|
119
148
|
|
|
149
|
+
First, make sure `CHANGELOG.md` has a useful human summary under `Unreleased` or under the exact version heading you are about to publish. The GitHub Release body is generated from that curated changelog text, with commits included only as an audit trail.
|
|
150
|
+
|
|
120
151
|
```bash
|
|
121
152
|
git switch main
|
|
122
153
|
git pull --ff-only
|
|
@@ -125,18 +156,21 @@ npm version patch
|
|
|
125
156
|
git push origin main --follow-tags
|
|
126
157
|
```
|
|
127
158
|
|
|
128
|
-
`npm version patch` updates
|
|
159
|
+
`npm version patch` updates `package.json` and `package-lock.json`; the repository's `version` lifecycle hook synchronizes `vscode-extension/package.json` before npm creates the version commit and annotated `vX.Y.Z` tag. Use `npm version minor` for a backward-compatible feature release or `npm version major` for a breaking release.
|
|
129
160
|
|
|
130
161
|
After the tag is pushed:
|
|
131
162
|
|
|
132
163
|
1. Watch the **Release** workflow in GitHub Actions.
|
|
133
|
-
2. Confirm the new version appears
|
|
134
|
-
3. Confirm
|
|
135
|
-
4.
|
|
164
|
+
2. Confirm the new version appears in the VS Code Marketplace.
|
|
165
|
+
3. Confirm the new version appears on npm.
|
|
166
|
+
4. Confirm GitHub created a Release for the same tag and attached the same-version VSIX.
|
|
167
|
+
5. Confirm the release notes read well and include the curated highlights.
|
|
168
|
+
6. Install or update from the Marketplace and run one final extension smoke test.
|
|
169
|
+
7. Run `npx copilot-usage-studio@X.Y.Z --version` from a clean directory as a fallback-host smoke test.
|
|
136
170
|
|
|
137
171
|
If CI or release validation fails, fix the issue on `main` and publish a new version. Do not move or reuse a public release tag, and do not overwrite an npm version; both are immutable release coordinates.
|
|
138
172
|
|
|
139
|
-
The workflow also has a manual **Run workflow** action with a required existing tag. Use
|
|
173
|
+
The workflow also has a manual **Run workflow** action with a required existing tag. Use it to repair a release from that immutable tag. Duplicate npm and Marketplace publication is skipped safely, while the release gate reruns and the GitHub Release notes/artifact are repaired.
|
|
140
174
|
|
|
141
175
|
### Manual Emergency Fallback
|
|
142
176
|
|
|
@@ -176,7 +210,7 @@ Then serve the generated `dist/` output with a small local static server.
|
|
|
176
210
|
|
|
177
211
|
Why: Angular production output is just static assets plus the generated `public/data/sessions.json` copy. This is a good first packaging target because it avoids an app server and keeps the data model simple.
|
|
178
212
|
|
|
179
|
-
Current build status: `npm run build` passes. There
|
|
213
|
+
Current build status: `npm run build` passes. There are still Angular budget warnings for the initial bundle and `src/app/app.css`; they do not block the local preview release.
|
|
180
214
|
|
|
181
215
|
For day-to-day development, use the in-app **Refresh** action. `npm run refresh:data` remains the explicit command-line scan plus verification path.
|
|
182
216
|
|
|
@@ -190,13 +224,38 @@ npm run runtime
|
|
|
190
224
|
|
|
191
225
|
It listens on `http://127.0.0.1:4312/` by default and exposes:
|
|
192
226
|
|
|
193
|
-
- `GET /api/status`: scan state, timestamps, last error, and session count
|
|
227
|
+
- `GET /api/status`: scan state, timestamps, current progress, recent log lines, last error, and session count
|
|
228
|
+
- `GET /api/logs`: bounded in-memory runtime logs and the local log-file path
|
|
194
229
|
- `GET /api/sessions`: the current normalized in-memory `SessionData`
|
|
195
230
|
- `POST /api/scan`: run a scan, persist the new snapshot, and return it
|
|
196
231
|
- `GET /data/sessions.json`: compatibility route backed by the current in-memory snapshot
|
|
197
232
|
|
|
198
233
|
The runtime seeds itself from `public/data/sessions.json` when needed, then keeps its live cache under `tmp/local-runtime/`. This avoids triggering an Angular development rebuild every time the user refreshes data. It serves cached data while a scan is running, and a failed scan does not replace the last valid snapshot.
|
|
199
234
|
|
|
235
|
+
Runtime scans run in a child process. This keeps `/api/status` and `/api/logs` responsive even when a large VS Code profile is being imported or a workspace has unusually heavy local data.
|
|
236
|
+
|
|
237
|
+
### Startup Diagnostics
|
|
238
|
+
|
|
239
|
+
If the app appears stuck on startup, there are three places to inspect:
|
|
240
|
+
|
|
241
|
+
1. The app loading screen shows the current scan step, recent runtime log messages, cached session count, and the runtime log-file path.
|
|
242
|
+
2. The terminal running `npm start` or `npx copilot-usage-studio` prints scan progress such as discovered workspace folders and debug-log folder counts.
|
|
243
|
+
3. The status endpoint can be queried directly:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npx copilot-usage-studio status
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
For packaged `npx` runs, the runtime log file is stored beside the local session cache:
|
|
250
|
+
|
|
251
|
+
- Windows: `%LOCALAPPDATA%\Copilot Usage Studio\runtime.log`
|
|
252
|
+
- macOS: `~/Library/Application Support/Copilot Usage Studio/runtime.log`
|
|
253
|
+
- Linux: `${XDG_DATA_HOME:-~/.local/share}/copilot-usage-studio/runtime.log`
|
|
254
|
+
|
|
255
|
+
For repository development with `npm start`, the log is written to `tmp/local-runtime/runtime.log`.
|
|
256
|
+
|
|
257
|
+
The scanner is intentionally bounded. It scans known VS Code Copilot storage locations plus targeted `.github/instructions`, `.github/skills`, `.github/prompts`, and `.github/hooks` folders for the workspace. Recursive Markdown scans have depth and directory-count caps and skip common dependency/build folders such as `node_modules`, `.git`, `dist`, `build`, `target`, and virtual environments. It should not crawl arbitrary project directories.
|
|
258
|
+
|
|
200
259
|
To build the production UI and serve the complete local app with one command:
|
|
201
260
|
|
|
202
261
|
```bash
|
|
@@ -233,9 +292,25 @@ Why: this keeps the project local and scriptable without committing to a desktop
|
|
|
233
292
|
|
|
234
293
|
The shared scanner API, runtime, and npm executable are complete. The same runtime can be embedded by Electron later.
|
|
235
294
|
|
|
236
|
-
##
|
|
295
|
+
## VS Code Companion Extension Preview
|
|
296
|
+
|
|
297
|
+
A thin VS Code extension now calls the shared scanner from the local extension host and shows the existing UI in a webview. It remains a host, not a fork of the pricing and parsing logic.
|
|
298
|
+
|
|
299
|
+
Build a local preview VSIX:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
npm run vscode:package
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Install it locally:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
code --install-extension tmp/copilot-usage-studio-vscode-X.Y.Z.vsix --force
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The extension exposes the full app inside VS Code: Usage, Sessions, Memory, Customizations preview, Compare, Insights, and Prices. The npm/browser host remains useful for development and fallback testing, but the extension is the primary product surface.
|
|
237
312
|
|
|
238
|
-
|
|
313
|
+
The VSIX is a separate distribution artifact from the npm package. It is built from the same source tree. After Marketplace release is configured, the VS Code Marketplace should be the normal install route; `npx copilot-usage-studio` remains a development/fallback host.
|
|
239
314
|
|
|
240
315
|
Why not move the whole product into an extension: keeping the scanner and analysis core editor-independent preserves a path to a desktop app and possible Visual Studio support if Visual Studio exposes equivalent local evidence later.
|
|
241
316
|
|
package/docs/pricing.md
CHANGED
|
@@ -17,10 +17,10 @@ https://docs.github.com/en/copilot/concepts/billing/usage-based-billing-for-orga
|
|
|
17
17
|
Current version:
|
|
18
18
|
|
|
19
19
|
```text
|
|
20
|
-
github-copilot-usage-pricing-2026-
|
|
20
|
+
github-copilot-usage-pricing-2026-07-05
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
The source table says prices are per 1 million tokens. The committed snapshot was checked against GitHub Docs on
|
|
23
|
+
The source table says prices are per 1 million tokens. The committed snapshot was checked against GitHub Docs on July 5, 2026.
|
|
24
24
|
|
|
25
25
|
## Where Pricing Lives
|
|
26
26
|
|
|
@@ -40,7 +40,7 @@ This should be a deliberate maintainer update, not a runtime web fetch.
|
|
|
40
40
|
|
|
41
41
|
1. Open GitHub's official model pricing page and record the check date.
|
|
42
42
|
2. Update `data/github-copilot-pricing.json`, including model availability, all token buckets, and request-size tiers.
|
|
43
|
-
3. Keep tier thresholds machine-readable. Apply
|
|
43
|
+
3. Keep tier thresholds and raw-model aliases machine-readable. Apply tiers per model call using normal plus cached input, never aggregated session tokens.
|
|
44
44
|
4. Update the pricing version and snapshot date.
|
|
45
45
|
5. Add or update threshold boundary tests and verify that several smaller calls do not accidentally trigger a long-context tier.
|
|
46
46
|
6. Run `npm run refresh:data` to recalculate the local development snapshot.
|
|
@@ -169,6 +169,8 @@ The user should be able to inspect the cost inputs directly. If a session looks
|
|
|
169
169
|
- cached input or cache-write accounting
|
|
170
170
|
- a model fallback because the raw model id was not in the known GitHub table
|
|
171
171
|
|
|
172
|
+
Some VS Code logs can contain internal raw model ids that are not published as GitHub pricing rows. For example, a raw id such as `oswe-vscode-prime` stays visible in the UI and is estimated with the configured fallback pricing row. The app should not silently rename that id unless a reliable pricing alias is added.
|
|
173
|
+
|
|
172
174
|
The `GitHub prices` view therefore shows every rate row the app knows about, where it came from, and whether any imported session currently uses it.
|
|
173
175
|
|
|
174
176
|
The same view also shows the selected Business/Enterprise AI-credit allowance and how the current imported sessions compare with it.
|
package/docs/scanner-api.md
CHANGED
|
@@ -42,23 +42,63 @@ This is the intended path for:
|
|
|
42
42
|
const sessionData = await scanVsCodeSessions({
|
|
43
43
|
roots: ['/custom/Code/User'],
|
|
44
44
|
sqlite: true,
|
|
45
|
+
includeCustomizations: true,
|
|
45
46
|
generatedAt: new Date(),
|
|
47
|
+
onProgress: (event) => console.log(event.message),
|
|
46
48
|
});
|
|
47
49
|
```
|
|
48
50
|
|
|
49
51
|
- `roots`: optional array of VS Code `User` directories or individual workspace-storage directories. Defaults to stable VS Code and VS Code Insiders paths for the current operating system.
|
|
50
52
|
- `sqlite`: set to `false` to skip optional `state.vscdb` metadata enrichment. Debug-log token and usage import still works.
|
|
53
|
+
- `includeCustomizations`: set to `false` to skip instruction/skill/prompt/hook/agent inventory and evidence matching. The VS Code extension normally leaves this enabled because it exposes the Customizations view.
|
|
54
|
+
- `includeSystemCustomizations`: set to `true` to include customizations contributed by installed VS Code extensions or VS Code application extension folders. The default is `false` so startup focuses on user/repo customizations and avoids expensive evidence matching for system-provided skills.
|
|
51
55
|
- `generatedAt`: optional `Date` or date-compatible value, primarily for deterministic hosts and tests.
|
|
52
56
|
- `usdToEur`: legacy generated-contract conversion value. The product UI is USD-first and callers should normally leave this unset.
|
|
57
|
+
- `onProgress`: optional callback for host diagnostics. It receives scan-stage events such as root discovery, workspace discovery, debug-log folder counts, memory indexing, and completion. Hosts should treat these as user-facing diagnostics, not stable analytics data.
|
|
53
58
|
|
|
54
59
|
Duplicate roots are normalized and scanned once. Each call receives fresh ingestion diagnostics; counters and warnings do not leak from earlier scans in a long-running process.
|
|
55
60
|
|
|
56
61
|
Only one scan may run at a time within one Node process. Hosts should debounce filesystem events and await the current scan instead of starting overlapping work.
|
|
57
62
|
|
|
63
|
+
## Internal Scanner Modules
|
|
64
|
+
|
|
65
|
+
The public host boundary remains `lib/scanner-api.mjs`, but the scanner implementation is being decomposed so large-profile performance issues are easier to diagnose.
|
|
66
|
+
|
|
67
|
+
Current internal split:
|
|
68
|
+
|
|
69
|
+
- `scripts/scanner-traversal.mjs`: VS Code user-data root discovery, workspace-storage entry discovery, bounded recursive file traversal, dependency/build-folder skips, and root normalization.
|
|
70
|
+
- `scripts/scanner-workspace.mjs`: per-storage-entry orchestration for state DB enrichment, customization inventory/evidence, debug-log imports, chat-snapshot imports, memory imports, progress events, and per-entry diagnostics.
|
|
71
|
+
- `scripts/scanner-customization-inventory.mjs`: bounded discovery of user, workspace, repo, and configured Copilot customization files without crawling arbitrary repositories.
|
|
72
|
+
- `scripts/scanner-customization-evidence.mjs`: request-payload and side-file text matching for whether customization content is visible in local model-request logs.
|
|
73
|
+
- `scripts/scanner-memory.mjs`: Copilot memory/plan Markdown import plus memory recall linking from debug-log tool events.
|
|
74
|
+
- `scripts/scanner-session-parser.mjs`: Agent Debug Log and chat-snapshot session parsing, token/cache fields, model breakdowns, request payload summaries, trace events, source usage, and fallback chat estimates.
|
|
75
|
+
- `scripts/scan-vscode-sessions.mjs`: scan lifecycle, diagnostics lifetime, SQLite enrichment, workspace aggregation, final normalized session-data assembly, CLI argument parsing, and JSON persistence.
|
|
76
|
+
|
|
77
|
+
Traversal is intentionally separate because broad filesystem walking is the highest-risk performance path. The scanner should scan known VS Code Copilot storage plus targeted Copilot customization/memory roots; it must not crawl arbitrary repositories or home directories.
|
|
78
|
+
|
|
79
|
+
Next decomposition targets:
|
|
80
|
+
|
|
81
|
+
- pricing/token normalization
|
|
82
|
+
- SQLite/state enrichment helpers
|
|
83
|
+
|
|
84
|
+
## Incremental scans
|
|
85
|
+
|
|
86
|
+
The runtime passes the previous snapshot's `generatedAt` value as `incrementalSince` during a normal quick refresh. The scanner still discovers known VS Code storage entries, but it parses only debug-session `main.jsonl` files and chat snapshots modified after that cursor. The runtime merges changed sessions by stable session ID and retains unchanged sessions.
|
|
87
|
+
|
|
88
|
+
This provides two explicit modes: quick/incremental for normal startup, visibility refresh, and `Refresh Data`; full for recovery/debugging through `Copilot Usage Studio: Full Rescan`.
|
|
89
|
+
|
|
90
|
+
Incremental scans intentionally do not infer deletions. A full rescan rebuilds the snapshot and is the recovery path for deleted or externally rewritten sources. Memory roots remain indexed during quick scans because memory files are small and can be independently added or removed.
|
|
91
|
+
|
|
92
|
+
Customization analysis has its own cache boundary. Inventory records include a SHA-256 content fingerprint. If all current fingerprints match the previous analysis, only changed session logs are checked and prior evidence is retained. Any customization content change invalidates that shortcut.
|
|
93
|
+
|
|
94
|
+
The current delta cursor is timestamp-based because VS Code does not publish a stable scanner API or change journal for Agent Debug Logs. The full-rescan command exists specifically to recover from timestamp anomalies or external file restoration.
|
|
95
|
+
|
|
58
96
|
## Local Runtime Host
|
|
59
97
|
|
|
60
98
|
`lib/local-runtime.mjs` is the first shared API host. It keeps the last valid `SessionData` snapshot in memory, serves it while scans run, and persists successful refreshes.
|
|
61
99
|
|
|
100
|
+
The local runtime runs the scanner in a child process by default. This keeps the HTTP status/log endpoints responsive while large local VS Code profiles are being imported.
|
|
101
|
+
|
|
62
102
|
The runtime intentionally keeps scanner and transport concerns separate:
|
|
63
103
|
|
|
64
104
|
- the scanner knows how to read and normalize VS Code data
|
package/lib/cli.mjs
CHANGED
|
@@ -98,7 +98,21 @@ export async function runCli(args = process.argv.slice(2), dependencies = {}) {
|
|
|
98
98
|
}
|
|
99
99
|
if (options.command === 'status') {
|
|
100
100
|
const fetchStatus = dependencies.fetch ?? globalThis.fetch;
|
|
101
|
-
const
|
|
101
|
+
const timeoutMs = Number(dependencies.statusTimeoutMs ?? 5000);
|
|
102
|
+
const signal = Number.isFinite(timeoutMs) && timeoutMs > 0 && typeof AbortSignal !== 'undefined'
|
|
103
|
+
? AbortSignal.timeout(timeoutMs)
|
|
104
|
+
: undefined;
|
|
105
|
+
let response;
|
|
106
|
+
try {
|
|
107
|
+
response = await fetchStatus(`http://${options.host}:${options.port}/api/status`, { signal });
|
|
108
|
+
} catch (error) {
|
|
109
|
+
const timedOut = error instanceof Error && error.name === 'TimeoutError';
|
|
110
|
+
throw new Error(
|
|
111
|
+
timedOut
|
|
112
|
+
? `Local runtime did not respond on ${options.host}:${options.port} within ${timeoutMs}ms.`
|
|
113
|
+
: `Could not reach local runtime on ${options.host}:${options.port}.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
102
116
|
if (!response.ok) {
|
|
103
117
|
throw new Error(`Local runtime returned ${response.status}.`);
|
|
104
118
|
}
|