oc2token 0.1.0 → 0.1.1
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 +40 -15
- package/dist/src/accounting/index.d.ts +1 -1
- package/dist/src/accounting/index.js +1 -1
- package/dist/src/accounting/index.js.map +1 -1
- package/dist/src/accounting/reducer.d.ts +1 -2
- package/dist/src/accounting/reducer.js +0 -22
- package/dist/src/accounting/reducer.js.map +1 -1
- package/dist/src/antigravity/discovery.d.ts +4 -0
- package/dist/src/antigravity/discovery.js +25 -2
- package/dist/src/antigravity/discovery.js.map +1 -1
- package/dist/src/antigravity/scanner.js +173 -156
- package/dist/src/antigravity/scanner.js.map +1 -1
- package/dist/src/application.js +87 -75
- package/dist/src/application.js.map +1 -1
- package/dist/src/cache/lock.js +4 -1
- package/dist/src/cache/lock.js.map +1 -1
- package/dist/src/cache/schema.js +13 -1
- package/dist/src/cache/schema.js.map +1 -1
- package/dist/src/cache/store.d.ts +1 -0
- package/dist/src/cache/store.js +27 -0
- package/dist/src/cache/store.js.map +1 -1
- package/dist/src/cli.js +174 -46
- package/dist/src/cli.js.map +1 -1
- package/dist/src/codex/discovery.d.ts +4 -0
- package/dist/src/codex/discovery.js +59 -47
- package/dist/src/codex/discovery.js.map +1 -1
- package/dist/src/codex/scanner.js +37 -11
- package/dist/src/codex/scanner.js.map +1 -1
- package/dist/src/collector/collector.js +44 -14
- package/dist/src/collector/collector.js.map +1 -1
- package/dist/src/collector/types.d.ts +8 -0
- package/dist/src/collector/types.js.map +1 -1
- package/dist/src/dashboard/render/dashboard.js +18 -5
- package/dist/src/dashboard/render/dashboard.js.map +1 -1
- package/dist/src/dashboard/render/format.d.ts +4 -0
- package/dist/src/dashboard/render/format.js +9 -0
- package/dist/src/dashboard/render/format.js.map +1 -1
- package/dist/src/dashboard/render/types.d.ts +14 -0
- package/dist/src/dashboard/render/types.js +40 -13
- package/dist/src/dashboard/render/types.js.map +1 -1
- package/dist/src/dashboard/scheduler/refresh-scheduler.js +2 -1
- package/dist/src/dashboard/scheduler/refresh-scheduler.js.map +1 -1
- package/dist/src/dashboard/settings/index.js +29 -4
- package/dist/src/dashboard/settings/index.js.map +1 -1
- package/dist/src/dashboard/state/refresh-coordinator.d.ts +7 -0
- package/dist/src/dashboard/state/refresh-coordinator.js +30 -0
- package/dist/src/dashboard/state/refresh-coordinator.js.map +1 -1
- package/dist/src/domain/records.d.ts +15 -1
- package/dist/src/domain/records.js +15 -1
- package/dist/src/domain/records.js.map +1 -1
- package/dist/src/domain/tokens.d.ts +12 -0
- package/dist/src/domain/tokens.js +19 -3
- package/dist/src/domain/tokens.js.map +1 -1
- package/dist/src/domain/windows.js +37 -13
- package/dist/src/domain/windows.js.map +1 -1
- package/dist/src/opencode/adapter.js +12 -3
- package/dist/src/opencode/adapter.js.map +1 -1
- package/dist/src/opencode/client.d.ts +10 -0
- package/dist/src/opencode/client.js +17 -3
- package/dist/src/opencode/client.js.map +1 -1
- package/dist/src/opencode/stats.js +33 -9
- package/dist/src/opencode/stats.js.map +1 -1
- package/dist/src/opencode/transport.d.ts +10 -0
- package/dist/src/opencode/transport.js +23 -9
- package/dist/src/opencode/transport.js.map +1 -1
- package/dist/src/output/table.js +6 -2
- package/dist/src/output/table.js.map +1 -1
- package/dist/src/pricing/pricing.d.ts +30 -0
- package/dist/src/pricing/pricing.js +67 -27
- package/dist/src/pricing/pricing.js.map +1 -1
- package/package.json +32 -7
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ OpenCode 2 records rich token telemetry locally, but the raw message stream is n
|
|
|
44
44
|
- **Trend sparkline** per window, not one row per bucket
|
|
45
45
|
- **Privacy by design** — only normalized token counters are cached; no prompts, tool inputs, or session titles are stored
|
|
46
46
|
- **Resilient collection** — paginated assistant-message aggregation with retry, dedup by `tokenRevision`, fallback when the filtered stats API ignores the range
|
|
47
|
-
- **Scriptable** — stable JSON `schemaVersion:
|
|
47
|
+
- **Scriptable** — stable JSON `schemaVersion: 4`, deterministic table output, exit codes `0` / `1` / `3`
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -163,7 +163,7 @@ oc2token --no-color # or NO_COLOR=1 for plain output
|
|
|
163
163
|
| `?` | Help |
|
|
164
164
|
| `q` / `Ctrl+C` | Quit |
|
|
165
165
|
|
|
166
|
-
Settings persist under the cache directory (
|
|
166
|
+
Settings persist under the cache directory (`~/Library/Caches/oc2token` on macOS by default; `$XDG_CACHE_HOME/oc2token` when `XDG_CACHE_HOME` is set, otherwise `~/Library/Caches/oc2token` — see `src/application.ts:47-50`). Override with `--cache-dir`. At least one provider must stay enabled.
|
|
167
167
|
|
|
168
168
|
**Theme.** Purple carries structure, orange carries activity and focus, cyan carries inputs. Respects `NO_COLOR` and `--no-color`.
|
|
169
169
|
|
|
@@ -172,7 +172,7 @@ Settings persist under the cache directory (`~/.cache/oc2token` on macOS/Linux o
|
|
|
172
172
|
## 🖥️ CLI reference
|
|
173
173
|
|
|
174
174
|
```
|
|
175
|
-
oc2token 0.1.
|
|
175
|
+
oc2token 0.1.1
|
|
176
176
|
|
|
177
177
|
Usage:
|
|
178
178
|
oc2token [hour|day|week]
|
|
@@ -183,7 +183,7 @@ Options:
|
|
|
183
183
|
--once Collect once and exit
|
|
184
184
|
--json Emit stable JSON and exit
|
|
185
185
|
--format <mode> auto, dashboard, table, or json
|
|
186
|
-
--refresh <seconds> Auto-refresh cadence; 0 is manual-only (60–14400)
|
|
186
|
+
--refresh <seconds> Auto-refresh cadence; 0 is manual-only (0 or 60–14400)
|
|
187
187
|
--timezone <IANA> Zone for local day / ISO week boundaries
|
|
188
188
|
--project <id> Restrict collection to an OpenCode project
|
|
189
189
|
--cache-dir <path> Override the normalized metadata cache directory
|
|
@@ -201,6 +201,14 @@ Options:
|
|
|
201
201
|
| `1` | Connection / validation / usage error |
|
|
202
202
|
| `3` | Partial results — JSON emitted but coverage incomplete |
|
|
203
203
|
|
|
204
|
+
### 💰 Costs (estimates only)
|
|
205
|
+
|
|
206
|
+
`costs`, `costsByProvider`, `costsByProject`, and per-breakdown `cost` fields are **rough USD estimates**, not bills. Pricing is compiled from public provider pages **as of 2026-09-02** (`src/pricing/pricing.ts:11-16`); rates change without notice and subscription/Zen at-cost pricing may differ from what you actually pay.
|
|
207
|
+
|
|
208
|
+
- Unknown models return `undefined` pricing (`pricingForModel` → `undefined`), surfaced as `null` cost in JSON. There is **no generic fallback price** — an unknown model contributes tokens but no dollars.
|
|
209
|
+
- `estimatedCostForBreakdowns(breakdowns, strict=true)` returns `undefined` if **any** model is unknown; the non-strict/partial variant skips unknown models instead. Window-level `costs` use the partial (skip-unknown) aggregation.
|
|
210
|
+
- `recorded_total` remains the source of truth for usage; costs are a convenience overlay.
|
|
211
|
+
|
|
204
212
|
### Filtering providers
|
|
205
213
|
|
|
206
214
|
```sh
|
|
@@ -224,7 +232,7 @@ oc2token --once --json --source codex | jq .totals.day.recorded_total
|
|
|
224
232
|
|----------|--------|-----------|
|
|
225
233
|
| **opencode** | OpenCode 2 local service (`@opencode-ai/client`) | session/message pagination with `tokenRevision` dedup |
|
|
226
234
|
| **codex** | `~/.codex/sessions` rollouts (JSONL) | file scan + parse |
|
|
227
|
-
| **antigravity** | Antigravity SQLite +
|
|
235
|
+
| **antigravity** | Antigravity SQLite (`gen_metadata` protobuf via `node:sqlite` + `src/antigravity/proto.ts`) | proto + scanner |
|
|
228
236
|
|
|
229
237
|
All three emit the same `UsageRecord` shape and are merged by `Aggregator → CachedUsageSource → Dashboard`. `doctor` reports each independently:
|
|
230
238
|
|
|
@@ -250,28 +258,33 @@ oc2token --format table
|
|
|
250
258
|
|
|
251
259
|
<p align="center"><img src="assets/screenshots/table.svg" width="100%" alt="Table output" /></p>
|
|
252
260
|
|
|
253
|
-
### JSON (`schemaVersion:
|
|
261
|
+
### JSON (`schemaVersion: 4`)
|
|
254
262
|
|
|
255
263
|
```sh
|
|
256
264
|
oc2token --json | jq .
|
|
257
265
|
oc2token --once --json > snapshot.json
|
|
258
266
|
```
|
|
259
267
|
|
|
260
|
-
Emits **all three windows**, `totalsByProvider`/`totalsByProject`, trends
|
|
268
|
+
Emits **all three windows**, `costs`, `totalsByProvider`/`totalsByProject`, `trends`, and coverage in one stable contract:
|
|
261
269
|
|
|
262
270
|
```json
|
|
263
271
|
{
|
|
264
|
-
"schemaVersion":
|
|
272
|
+
"schemaVersion": 4,
|
|
265
273
|
"source": "unified",
|
|
266
|
-
"version": "0.1.
|
|
274
|
+
"version": "0.1.1",
|
|
267
275
|
"windows": { "hour": { "kind": "hour", "from": "…", "to": "…", "label": "last 60 minutes" }, "day": {}, "week": {} },
|
|
268
276
|
"totals": { "hour": { "recorded_total": 15582 }, "day": {}, "week": {} },
|
|
269
|
-
"
|
|
270
|
-
"
|
|
277
|
+
"costs": { "hour": 0.042, "day": null, "week": null },
|
|
278
|
+
"trends": { "hour": [{ "label": "…", "totals": {}, "from": "…", "to": "…" }], "day": [], "week": [] },
|
|
279
|
+
"providersByWindow": { "day": [ { "name": "opencode", "totals": {}, "cost": 0.01 } ] },
|
|
280
|
+
"totalsByProvider": { "day": { "opencode": { "recorded_total": 21785 } } },
|
|
281
|
+
"totalsByProject": { "day": { "/Users/you/project": { "recorded_total": 9001 } } },
|
|
271
282
|
"coverage": { "complete": true, "sessionsScanned": 14, "sessionsDiscovered": 14 }
|
|
272
283
|
}
|
|
273
284
|
```
|
|
274
285
|
|
|
286
|
+
> `costs` / `cost` fields are **estimates only** (see [Costs](#-costs-estimates-only)). Unknown models yield `null`/`undefined` cost, never a guessed price. `totalsByProject` keys are absolute project paths — treat snapshots as containing PII if you share them.
|
|
287
|
+
|
|
275
288
|
Use `--format json` for pretty-printed JSON without exiting interactive mode fallback.
|
|
276
289
|
|
|
277
290
|
---
|
|
@@ -281,8 +294,8 @@ Use `--format json` for pretty-printed JSON without exiting interactive mode fal
|
|
|
281
294
|
| Option | CLI | Env / Persisted |
|
|
282
295
|
|--------|-----|-----------------|
|
|
283
296
|
| Timezone | `--timezone Europe/Berlin` | Local system zone by default; validated as IANA |
|
|
284
|
-
| Refresh | `--refresh 300` (
|
|
285
|
-
| Cache dir | `--cache-dir /tmp/oc2` |
|
|
297
|
+
| Refresh | `--refresh 300` (`0` manual-only, or 60–14400) | Persisted via Settings panel (`s`) |
|
|
298
|
+
| Cache dir | `--cache-dir /tmp/oc2` | `~/Library/Caches/oc2token`, or `$XDG_CACHE_HOME/oc2token` when set |
|
|
286
299
|
| Project filter | `--project my-project` | Scopes OpenCode collection only |
|
|
287
300
|
| Providers | `--source codex` | Persisted via Settings panel |
|
|
288
301
|
| Color | `--no-color` | `NO_COLOR=1` disables all ANSI |
|
|
@@ -303,6 +316,18 @@ It **does not** persist:
|
|
|
303
316
|
|
|
304
317
|
All collection is local-first; no network calls besides the loopback OpenCode service.
|
|
305
318
|
|
|
319
|
+
### Paths are PII
|
|
320
|
+
|
|
321
|
+
`project` is persisted as an **absolute filesystem path** (e.g. `/Users/you/work/client-app`) in the normalized cache, in `--json` output (`totalsByProject`, `projects*`), and in table/dashboard project rows. Sharing a snapshot, cache directory, or screenshot exposes your directory layout and username. There is **no `--redact-projects` flag** — if you need to share output, scrub paths manually before posting.
|
|
322
|
+
|
|
323
|
+
### Snapshot sanitizer is denylist-based
|
|
324
|
+
|
|
325
|
+
`src/cache/schema.ts:47,225-259` strips known-sensitive keys (`prompt`, `tool_input/output`, `api_key`, `authorization`, `password`, `secret`, `session_title/name`, `raw_content`, plus `content`/`parts`/`title`) from cached snapshots. This is a **denylist, not an allowlist**: a future field with a novel sensitive name would pass through. Treat the cache as usage-metadata-only by construction (records go through `normalizeRecord`), and review sanitizer keys when new snapshot fields are added.
|
|
326
|
+
|
|
327
|
+
### `doctor` prints the endpoint
|
|
328
|
+
|
|
329
|
+
`oc2token doctor` prints `Endpoint: <url>` (loopback server URL) and per-provider status lines. The URL itself is not a credential (auth headers are never printed), but redact it in public bug reports anyway.
|
|
330
|
+
|
|
306
331
|
---
|
|
307
332
|
|
|
308
333
|
## 🧭 How it works
|
|
@@ -319,7 +344,7 @@ All collection is local-first; no network calls besides the loopback OpenCode se
|
|
|
319
344
|
│ Aggregator │ dedup by sessionID/messageID + tokenRevision
|
|
320
345
|
└──────┬──────┘
|
|
321
346
|
▼
|
|
322
|
-
┌──────────────┐ file lock · schema
|
|
347
|
+
┌──────────────┐ file lock · schema 2 · atomic writes
|
|
323
348
|
│ CachedUsage │
|
|
324
349
|
└──────┬──────┘
|
|
325
350
|
▼
|
|
@@ -352,7 +377,7 @@ src/
|
|
|
352
377
|
opencode/ transport & doctor
|
|
353
378
|
codex/ rollout discovery & scanner
|
|
354
379
|
antigravity/ sqlite + proto + scanner
|
|
355
|
-
cache/ filesystem lock & store (schema
|
|
380
|
+
cache/ filesystem lock & store (schema 2)
|
|
356
381
|
dashboard/ render, state, settings
|
|
357
382
|
domain/ windows, tokens, records, contracts
|
|
358
383
|
output/ dashboard / table / json renderers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { UsageRecordReducer, compareFreshness, reduceUsageRecords,
|
|
1
|
+
export { UsageRecordReducer, compareFreshness, reduceUsageRecords, } from "./reducer.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { UsageRecordReducer, compareFreshness, reduceUsageRecords,
|
|
1
|
+
export { UsageRecordReducer, compareFreshness, reduceUsageRecords, } from "./reducer.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accounting/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accounting/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,cAAc,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { UsageTotals } from "../domain/index.js";
|
|
2
2
|
import { type UsageRecord, type UsageWindow } from "../domain/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* A deterministic, synchronous reducer for usage records.
|
|
@@ -18,4 +18,3 @@ export declare class UsageRecordReducer {
|
|
|
18
18
|
}
|
|
19
19
|
export declare function compareFreshness(left: UsageRecord, right: UsageRecord): number;
|
|
20
20
|
export declare function reduceUsageRecords(records: ReadonlyArray<UsageRecord>): ReadonlyArray<UsageRecord>;
|
|
21
|
-
export declare function sumRecordComponents(records: ReadonlyArray<UsageRecord>): UsageTotals;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { parseTokenComponents, toUsageTotals, } from "../domain/index.js";
|
|
2
1
|
import { sumUsageRecords, } from "../domain/index.js";
|
|
3
|
-
const COMPONENTS = ["input", "output", "reasoning", "cacheRead", "cacheWrite"];
|
|
4
2
|
function compareRevision(left, right) {
|
|
5
3
|
if (left === right) {
|
|
6
4
|
return 0;
|
|
@@ -82,24 +80,4 @@ export function reduceUsageRecords(records) {
|
|
|
82
80
|
reducer.upsertMany(records);
|
|
83
81
|
return reducer.records();
|
|
84
82
|
}
|
|
85
|
-
export function sumRecordComponents(records) {
|
|
86
|
-
const totals = {
|
|
87
|
-
input: 0,
|
|
88
|
-
output: 0,
|
|
89
|
-
reasoning: 0,
|
|
90
|
-
cacheRead: 0,
|
|
91
|
-
cacheWrite: 0,
|
|
92
|
-
};
|
|
93
|
-
for (const record of records) {
|
|
94
|
-
const normalized = parseTokenComponents(record);
|
|
95
|
-
for (const component of COMPONENTS) {
|
|
96
|
-
const next = totals[component] + normalized[component];
|
|
97
|
-
if (!Number.isSafeInteger(next)) {
|
|
98
|
-
throw new RangeError(`Summing ${component} exceeds the safe integer range`);
|
|
99
|
-
}
|
|
100
|
-
totals[component] = next;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return toUsageTotals(totals);
|
|
104
|
-
}
|
|
105
83
|
//# sourceMappingURL=reducer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.js","sourceRoot":"","sources":["../../../src/accounting/reducer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reducer.js","sourceRoot":"","sources":["../../../src/accounting/reducer.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,5 +14,9 @@ export declare function defaultAntigravityHome(): string;
|
|
|
14
14
|
* `resolveAntigravityHome` or explicit `root` parameter.
|
|
15
15
|
* For test fixtures `ANTIGRAVITY_HOME` may point directly to a directory
|
|
16
16
|
* containing `*.db` files — that directory is scanned as well.
|
|
17
|
+
*
|
|
18
|
+
* Scans are flat (no recursion, fixed known subpaths), symlinks are skipped
|
|
19
|
+
* via withFileTypes (never followed), and results are capped at
|
|
20
|
+
* MAX_DATABASE_FILES so huge homes fail bounded instead of hanging.
|
|
17
21
|
*/
|
|
18
22
|
export declare function discoverAntigravityDatabases(root?: string): string[];
|
|
@@ -14,6 +14,9 @@ function resolveAntigravityHome(root) {
|
|
|
14
14
|
}
|
|
15
15
|
return defaultAntigravityHome();
|
|
16
16
|
}
|
|
17
|
+
/** Bounds so a huge $HOME can never hang discovery. */
|
|
18
|
+
const MAX_DATABASE_FILES = 5_000;
|
|
19
|
+
const MAX_ENTRIES_PER_DIR = 10_000;
|
|
17
20
|
/**
|
|
18
21
|
* Scan Antigravity SQLite conversation databases.
|
|
19
22
|
*
|
|
@@ -29,20 +32,36 @@ function resolveAntigravityHome(root) {
|
|
|
29
32
|
* `resolveAntigravityHome` or explicit `root` parameter.
|
|
30
33
|
* For test fixtures `ANTIGRAVITY_HOME` may point directly to a directory
|
|
31
34
|
* containing `*.db` files — that directory is scanned as well.
|
|
35
|
+
*
|
|
36
|
+
* Scans are flat (no recursion, fixed known subpaths), symlinks are skipped
|
|
37
|
+
* via withFileTypes (never followed), and results are capped at
|
|
38
|
+
* MAX_DATABASE_FILES so huge homes fail bounded instead of hanging.
|
|
32
39
|
*/
|
|
33
40
|
export function discoverAntigravityDatabases(root) {
|
|
34
41
|
const base = resolveAntigravityHome(root);
|
|
35
42
|
const seen = new Set();
|
|
36
43
|
const result = [];
|
|
37
44
|
const tryDir = (dir) => {
|
|
45
|
+
if (result.length >= MAX_DATABASE_FILES)
|
|
46
|
+
return;
|
|
38
47
|
let entries;
|
|
39
48
|
try {
|
|
40
|
-
entries = readdirSync(dir);
|
|
49
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
41
50
|
}
|
|
42
51
|
catch {
|
|
43
52
|
return;
|
|
44
53
|
}
|
|
45
|
-
|
|
54
|
+
let scanned = 0;
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
if (result.length >= MAX_DATABASE_FILES)
|
|
57
|
+
return;
|
|
58
|
+
if (scanned >= MAX_ENTRIES_PER_DIR)
|
|
59
|
+
break;
|
|
60
|
+
scanned += 1;
|
|
61
|
+
// Never follow symlinks; only regular files can be databases.
|
|
62
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
63
|
+
continue;
|
|
64
|
+
const file = entry.name;
|
|
46
65
|
// Only *.db — ignore -shm/-wal sidecars and .pb files
|
|
47
66
|
if (!file.toLowerCase().endsWith(".db")) {
|
|
48
67
|
continue;
|
|
@@ -82,9 +101,13 @@ export function discoverAntigravityDatabases(root) {
|
|
|
82
101
|
join("antigravity-ide", "implicit"),
|
|
83
102
|
];
|
|
84
103
|
for (const rel of relativeFromHomedir) {
|
|
104
|
+
if (result.length >= MAX_DATABASE_FILES)
|
|
105
|
+
break;
|
|
85
106
|
tryDir(join(base, rel));
|
|
86
107
|
}
|
|
87
108
|
for (const rel of relativeFromGemini) {
|
|
109
|
+
if (result.length >= MAX_DATABASE_FILES)
|
|
110
|
+
break;
|
|
88
111
|
tryDir(join(base, rel));
|
|
89
112
|
}
|
|
90
113
|
// Also handle case where base is already a .gemini/antigravity-cli/conversations path's parent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/antigravity/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,MAAM,UAAU,sBAAsB;IACpC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAa;IAC3C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACzC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,sBAAsB,EAAE,CAAC;AAClC,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/antigravity/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,MAAM,UAAU,sBAAsB;IACpC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAa;IAC3C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACzC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,sBAAsB,EAAE,CAAC;AAClC,CAAC;AAED,uDAAuD;AACvD,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,4BAA4B,CAAC,IAAa;IACxD,MAAM,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,MAAM,MAAM,GAAG,CAAC,GAAW,EAAQ,EAAE;QACnC,IAAI,MAAM,CAAC,MAAM,IAAI,kBAAkB;YAAE,OAAO;QAChD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,MAAM,CAAC,MAAM,IAAI,kBAAkB;gBAAE,OAAO;YAChD,IAAI,OAAO,IAAI,mBAAmB;gBAAE,MAAM;YAC1C,OAAO,IAAI,CAAC,CAAC;YACb,8DAA8D;YAC9D,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBAAE,SAAS;YACxD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YACxB,sDAAsD;YACtD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxC,SAAS;YACX,CAAC;YACD,mEAAmE;YACnE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/G,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,8EAA8E;IAC9E,+CAA+C;IAC/C,MAAM,CAAC,IAAI,CAAC,CAAC;IAEb,8EAA8E;IAC9E,2CAA2C;IAC3C,oEAAoE;IACpE,oFAAoF;IACpF,MAAM,mBAAmB,GAAG;QAC1B,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,eAAe,CAAC;QAC/C,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,eAAe,CAAC;QACnD,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,UAAU,CAAC;QAC9C,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,eAAe,CAAC;QACnD,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,UAAU,CAAC;KAC/C,CAAC;IACF,MAAM,kBAAkB,GAAG;QACzB,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC;QACpC,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC;QACxC,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC;QACnC,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC;QACxC,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC;KACpC,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,MAAM,IAAI,kBAAkB;YAAE,MAAM;QAC/C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,MAAM,IAAI,kBAAkB;YAAE,MAAM;QAC/C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,+FAA+F;IAC/F,4CAA4C;IAE5C,MAAM,CAAC,IAAI,EAAE,CAAC;IACd,OAAO,MAAM,CAAC;AAChB,CAAC"}
|