@buoy-gg/agent-core 7.0.41 → 7.0.43
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/lib/commonjs/blocks/receipts.js +1 -761
- package/lib/commonjs/blocks/runId.js +1 -31
- package/lib/commonjs/blocks/types.js +1 -140
- package/lib/commonjs/blocks/uiTool.js +6 -1405
- package/lib/commonjs/catalog/catalog.g.js +5 -4556
- package/lib/commonjs/catalog/catalog.source.json +249 -0
- package/lib/commonjs/catalog/catalog.types.g.js +1 -46
- package/lib/commonjs/catalog/normalizeParams.js +2 -332
- package/lib/commonjs/catalog/signature.js +1 -68
- package/lib/commonjs/catalog/snapshotReads.js +1 -263
- package/lib/commonjs/catalog/toProviderTools.js +2 -145
- package/lib/commonjs/catalog/validateParams.js +1 -129
- package/lib/commonjs/context/buildContextPack.js +1 -489
- package/lib/commonjs/effects/digest.js +1 -34
- package/lib/commonjs/effects/ledger.js +1 -345
- package/lib/commonjs/engine/askGate.js +1 -287
- package/lib/commonjs/engine/effectFor.js +1 -223
- package/lib/commonjs/engine/evidence.js +3 -111
- package/lib/commonjs/engine/historyBudget.js +1 -363
- package/lib/commonjs/engine/retrieve.js +1 -214
- package/lib/commonjs/engine/runAgentTurn.js +8 -1836
- package/lib/commonjs/engine/systemPrompt.js +54 -163
- package/lib/commonjs/engine/textToolCalls.js +1 -276
- package/lib/commonjs/engine/tokenCalibration.js +1 -81
- package/lib/commonjs/engine/verify.js +4 -297
- package/lib/commonjs/index.js +1 -384
- package/lib/commonjs/policy/labels.js +1 -319
- package/lib/commonjs/policy/policy.js +1 -148
- package/lib/commonjs/policy/redact.js +1 -172
- package/lib/commonjs/providers/anthropic.js +3 -445
- package/lib/commonjs/providers/openai.js +4 -324
- package/lib/commonjs/providers/problem.js +1 -98
- package/lib/commonjs/providers/sse.js +1 -240
- package/lib/commonjs/providers/streamTimer.js +1 -123
- package/lib/commonjs/providers/transport.js +1 -123
- package/lib/commonjs/providers/types.js +1 -6
- package/lib/commonjs/providers/xhrStream.js +1 -212
- package/lib/commonjs/realNow.js +1 -0
- package/lib/commonjs/session.js +1 -393
- package/lib/commonjs/types.js +0 -1
- package/lib/module/blocks/receipts.js +1 -755
- package/lib/module/blocks/runId.js +1 -26
- package/lib/module/blocks/types.js +1 -134
- package/lib/module/blocks/uiTool.js +6 -1396
- package/lib/module/catalog/catalog.g.js +5 -4552
- package/lib/module/catalog/catalog.source.json +249 -0
- package/lib/module/catalog/catalog.types.g.js +1 -42
- package/lib/module/catalog/normalizeParams.js +2 -326
- package/lib/module/catalog/signature.js +1 -63
- package/lib/module/catalog/snapshotReads.js +1 -259
- package/lib/module/catalog/toProviderTools.js +2 -139
- package/lib/module/catalog/validateParams.js +1 -124
- package/lib/module/context/buildContextPack.js +1 -485
- package/lib/module/effects/digest.js +1 -29
- package/lib/module/effects/ledger.js +1 -339
- package/lib/module/engine/askGate.js +1 -280
- package/lib/module/engine/effectFor.js +1 -220
- package/lib/module/engine/evidence.js +3 -105
- package/lib/module/engine/historyBudget.js +1 -355
- package/lib/module/engine/retrieve.js +1 -210
- package/lib/module/engine/runAgentTurn.js +8 -1827
- package/lib/module/engine/systemPrompt.js +54 -157
- package/lib/module/engine/textToolCalls.js +1 -270
- package/lib/module/engine/tokenCalibration.js +1 -75
- package/lib/module/engine/verify.js +4 -289
- package/lib/module/index.js +1 -35
- package/lib/module/policy/labels.js +1 -312
- package/lib/module/policy/policy.js +1 -142
- package/lib/module/policy/redact.js +1 -165
- package/lib/module/providers/anthropic.js +3 -441
- package/lib/module/providers/openai.js +4 -320
- package/lib/module/providers/problem.js +1 -92
- package/lib/module/providers/sse.js +1 -231
- package/lib/module/providers/streamTimer.js +1 -118
- package/lib/module/providers/transport.js +1 -119
- package/lib/module/providers/types.js +1 -4
- package/lib/module/providers/xhrStream.js +1 -207
- package/lib/module/realNow.js +1 -0
- package/lib/module/session.js +1 -369
- package/lib/module/types.js +0 -1
- package/lib/typescript/catalog/catalog.g.d.ts +3 -3
- package/lib/typescript/catalog/catalog.types.g.d.ts +3 -2
- package/lib/typescript/providers/problem.d.ts +0 -20
- package/lib/typescript/providers/streamTimer.d.ts +0 -24
- package/lib/typescript/realNow.d.ts +18 -0
- package/lib/web/index.mjs +52 -53
- package/package.json +1 -1
- package/lib/commonjs/blocks/receipts.js.map +0 -1
- package/lib/commonjs/blocks/runId.js.map +0 -1
- package/lib/commonjs/blocks/types.js.map +0 -1
- package/lib/commonjs/blocks/uiTool.js.map +0 -1
- package/lib/commonjs/catalog/catalog.g.js.map +0 -1
- package/lib/commonjs/catalog/catalog.types.g.js.map +0 -1
- package/lib/commonjs/catalog/normalizeParams.js.map +0 -1
- package/lib/commonjs/catalog/signature.js.map +0 -1
- package/lib/commonjs/catalog/snapshotReads.js.map +0 -1
- package/lib/commonjs/catalog/toProviderTools.js.map +0 -1
- package/lib/commonjs/catalog/validateParams.js.map +0 -1
- package/lib/commonjs/context/buildContextPack.js.map +0 -1
- package/lib/commonjs/effects/digest.js.map +0 -1
- package/lib/commonjs/effects/ledger.js.map +0 -1
- package/lib/commonjs/engine/askGate.js.map +0 -1
- package/lib/commonjs/engine/effectFor.js.map +0 -1
- package/lib/commonjs/engine/evidence.js.map +0 -1
- package/lib/commonjs/engine/historyBudget.js.map +0 -1
- package/lib/commonjs/engine/retrieve.js.map +0 -1
- package/lib/commonjs/engine/runAgentTurn.js.map +0 -1
- package/lib/commonjs/engine/systemPrompt.js.map +0 -1
- package/lib/commonjs/engine/textToolCalls.js.map +0 -1
- package/lib/commonjs/engine/tokenCalibration.js.map +0 -1
- package/lib/commonjs/engine/verify.js.map +0 -1
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/policy/labels.js.map +0 -1
- package/lib/commonjs/policy/policy.js.map +0 -1
- package/lib/commonjs/policy/redact.js.map +0 -1
- package/lib/commonjs/providers/anthropic.js.map +0 -1
- package/lib/commonjs/providers/openai.js.map +0 -1
- package/lib/commonjs/providers/problem.js.map +0 -1
- package/lib/commonjs/providers/sse.js.map +0 -1
- package/lib/commonjs/providers/streamTimer.js.map +0 -1
- package/lib/commonjs/providers/transport.js.map +0 -1
- package/lib/commonjs/providers/types.js.map +0 -1
- package/lib/commonjs/providers/xhrStream.js.map +0 -1
- package/lib/commonjs/session.js.map +0 -1
- package/lib/commonjs/types.js.map +0 -1
- package/lib/module/blocks/receipts.js.map +0 -1
- package/lib/module/blocks/runId.js.map +0 -1
- package/lib/module/blocks/types.js.map +0 -1
- package/lib/module/blocks/uiTool.js.map +0 -1
- package/lib/module/catalog/catalog.g.js.map +0 -1
- package/lib/module/catalog/catalog.types.g.js.map +0 -1
- package/lib/module/catalog/normalizeParams.js.map +0 -1
- package/lib/module/catalog/signature.js.map +0 -1
- package/lib/module/catalog/snapshotReads.js.map +0 -1
- package/lib/module/catalog/toProviderTools.js.map +0 -1
- package/lib/module/catalog/validateParams.js.map +0 -1
- package/lib/module/context/buildContextPack.js.map +0 -1
- package/lib/module/effects/digest.js.map +0 -1
- package/lib/module/effects/ledger.js.map +0 -1
- package/lib/module/engine/askGate.js.map +0 -1
- package/lib/module/engine/effectFor.js.map +0 -1
- package/lib/module/engine/evidence.js.map +0 -1
- package/lib/module/engine/historyBudget.js.map +0 -1
- package/lib/module/engine/retrieve.js.map +0 -1
- package/lib/module/engine/runAgentTurn.js.map +0 -1
- package/lib/module/engine/systemPrompt.js.map +0 -1
- package/lib/module/engine/textToolCalls.js.map +0 -1
- package/lib/module/engine/tokenCalibration.js.map +0 -1
- package/lib/module/engine/verify.js.map +0 -1
- package/lib/module/index.js.map +0 -1
- package/lib/module/policy/labels.js.map +0 -1
- package/lib/module/policy/policy.js.map +0 -1
- package/lib/module/policy/redact.js.map +0 -1
- package/lib/module/providers/anthropic.js.map +0 -1
- package/lib/module/providers/openai.js.map +0 -1
- package/lib/module/providers/problem.js.map +0 -1
- package/lib/module/providers/sse.js.map +0 -1
- package/lib/module/providers/streamTimer.js.map +0 -1
- package/lib/module/providers/transport.js.map +0 -1
- package/lib/module/providers/types.js.map +0 -1
- package/lib/module/providers/xhrStream.js.map +0 -1
- package/lib/module/session.js.map +0 -1
- package/lib/module/types.js.map +0 -1
- package/lib/typescript/blocks/receipts.d.ts.map +0 -1
- package/lib/typescript/blocks/runId.d.ts.map +0 -1
- package/lib/typescript/blocks/types.d.ts.map +0 -1
- package/lib/typescript/blocks/uiTool.d.ts.map +0 -1
- package/lib/typescript/catalog/catalog.g.d.ts.map +0 -1
- package/lib/typescript/catalog/catalog.types.g.d.ts.map +0 -1
- package/lib/typescript/catalog/normalizeParams.d.ts.map +0 -1
- package/lib/typescript/catalog/signature.d.ts.map +0 -1
- package/lib/typescript/catalog/snapshotReads.d.ts.map +0 -1
- package/lib/typescript/catalog/toProviderTools.d.ts.map +0 -1
- package/lib/typescript/catalog/validateParams.d.ts.map +0 -1
- package/lib/typescript/context/buildContextPack.d.ts.map +0 -1
- package/lib/typescript/effects/digest.d.ts.map +0 -1
- package/lib/typescript/effects/ledger.d.ts.map +0 -1
- package/lib/typescript/engine/askGate.d.ts.map +0 -1
- package/lib/typescript/engine/effectFor.d.ts.map +0 -1
- package/lib/typescript/engine/evidence.d.ts.map +0 -1
- package/lib/typescript/engine/historyBudget.d.ts.map +0 -1
- package/lib/typescript/engine/retrieve.d.ts.map +0 -1
- package/lib/typescript/engine/runAgentTurn.d.ts.map +0 -1
- package/lib/typescript/engine/systemPrompt.d.ts.map +0 -1
- package/lib/typescript/engine/textToolCalls.d.ts.map +0 -1
- package/lib/typescript/engine/tokenCalibration.d.ts.map +0 -1
- package/lib/typescript/engine/verify.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/policy/labels.d.ts.map +0 -1
- package/lib/typescript/policy/policy.d.ts.map +0 -1
- package/lib/typescript/policy/redact.d.ts.map +0 -1
- package/lib/typescript/providers/anthropic.d.ts.map +0 -1
- package/lib/typescript/providers/openai.d.ts.map +0 -1
- package/lib/typescript/providers/problem.d.ts.map +0 -1
- package/lib/typescript/providers/sse.d.ts.map +0 -1
- package/lib/typescript/providers/streamTimer.d.ts.map +0 -1
- package/lib/typescript/providers/transport.d.ts.map +0 -1
- package/lib/typescript/providers/types.d.ts.map +0 -1
- package/lib/typescript/providers/xhrStream.d.ts.map +0 -1
- package/lib/typescript/session.d.ts.map +0 -1
- package/lib/typescript/types.d.ts.map +0 -1
|
@@ -1,761 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.projectReceipt = projectReceipt;
|
|
7
|
-
exports.shallowDiff = shallowDiff;
|
|
8
|
-
var _runId = require("./runId");
|
|
9
|
-
var _types = require("./types");
|
|
10
|
-
/**
|
|
11
|
-
* Receipts — blocks the engine builds from tool results, deterministically.
|
|
12
|
-
*
|
|
13
|
-
* The model never authors these, so they cannot hallucinate, and a weak model
|
|
14
|
-
* cannot fail to produce them. They render the SAME redacted result the model
|
|
15
|
-
* gets, so what the user sees and what the model reasons about are one thing.
|
|
16
|
-
*
|
|
17
|
-
* A projector hands over the WHOLE list it was given. It used to truncate to a
|
|
18
|
-
* display cap and record the real count in `total`, which put the renderer in
|
|
19
|
-
* the position of advertising rows that had already been thrown away one layer
|
|
20
|
-
* up — a "+1 more" with nothing behind it. Paging is the renderer's job now
|
|
21
|
-
* (`usePaged`), and it can only page what it holds.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const id = (0, _runId.idFactory)("rc");
|
|
25
|
-
const rec = v => v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
|
|
26
|
-
const arr = v => Array.isArray(v) ? v : [];
|
|
27
|
-
const str = v => typeof v === "string" ? v : undefined;
|
|
28
|
-
const num = v => typeof v === "number" ? v : undefined;
|
|
29
|
-
function pathOf(url) {
|
|
30
|
-
try {
|
|
31
|
-
const u = new URL(url);
|
|
32
|
-
return u.pathname + (u.search.length > 1 ? "?…" : "");
|
|
33
|
-
} catch {
|
|
34
|
-
return url;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function requestSummary(r) {
|
|
38
|
-
const rid = str(r.id);
|
|
39
|
-
const url = str(r.url);
|
|
40
|
-
if (!rid || !url) return undefined;
|
|
41
|
-
return {
|
|
42
|
-
id: rid,
|
|
43
|
-
method: str(r.method) ?? "GET",
|
|
44
|
-
url,
|
|
45
|
-
status: num(r.status),
|
|
46
|
-
durationMs: num(r.durationMs),
|
|
47
|
-
// Both spellings: the snapshot projection says `overridden`, a raw event
|
|
48
|
-
// carries the `override` mark object.
|
|
49
|
-
mocked: r.overridden === true || r.override != null && typeof r.override === "object" || undefined
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* The keys a partial MERGE patch actually changed.
|
|
55
|
-
*
|
|
56
|
-
* A merge cannot remove a key, so a key the patch omits is a key that did not
|
|
57
|
-
* change — and must not be reported. Diffing a store against the patch instead
|
|
58
|
-
* of against the store did exactly that: a one-field bag edit rendered
|
|
59
|
-
* "− card_visa", "− tipRate", "− promoCode", telling the user the write had
|
|
60
|
-
* wiped four fields it never touched.
|
|
61
|
-
*/
|
|
62
|
-
function patchDiff(before, patch) {
|
|
63
|
-
const a = rec(before) ?? {};
|
|
64
|
-
const b = rec(patch) ?? {};
|
|
65
|
-
const out = [];
|
|
66
|
-
for (const k of Object.keys(b)) {
|
|
67
|
-
if (typeof a[k] === "function" || typeof b[k] === "function") continue;
|
|
68
|
-
if (JSON.stringify(a[k]) !== JSON.stringify(b[k])) out.push({
|
|
69
|
-
path: k,
|
|
70
|
-
before: a[k],
|
|
71
|
-
after: b[k]
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Shallow diff of two plain objects: top-level keys that differ. */
|
|
78
|
-
function shallowDiff(before, after) {
|
|
79
|
-
const a = rec(before) ?? {};
|
|
80
|
-
const b = rec(after) ?? {};
|
|
81
|
-
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
82
|
-
const out = [];
|
|
83
|
-
for (const k of keys) {
|
|
84
|
-
if (typeof a[k] === "function" || typeof b[k] === "function") continue;
|
|
85
|
-
if (JSON.stringify(a[k]) !== JSON.stringify(b[k])) {
|
|
86
|
-
out.push({
|
|
87
|
-
path: k,
|
|
88
|
-
before: a[k],
|
|
89
|
-
after: b[k]
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return out;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Group console entries by message, newest first, counting repeats. */
|
|
97
|
-
function groupLogs(entries) {
|
|
98
|
-
const map = new Map();
|
|
99
|
-
for (const e of entries) {
|
|
100
|
-
const r = rec(e);
|
|
101
|
-
if (!r) continue;
|
|
102
|
-
const message = str(r.message) ?? "";
|
|
103
|
-
const level = str(r.level) ?? "log";
|
|
104
|
-
const key = `${level}:${message}`;
|
|
105
|
-
const cur = map.get(key);
|
|
106
|
-
if (cur) cur.count += 1;else map.set(key, {
|
|
107
|
-
level,
|
|
108
|
-
message,
|
|
109
|
-
count: 1
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return [...map.values()];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** A bag-line / product heuristic: an array of objects with a name and a price/qty/image. */
|
|
116
|
-
function looksLikeItems(v) {
|
|
117
|
-
const items = arr(v).map(rec).filter(Boolean);
|
|
118
|
-
if (items.length < 2) return undefined;
|
|
119
|
-
const ok = items.every(i => str(i.name) && (i.image !== undefined || i.qty !== undefined || i.price !== undefined || i.unitPrice !== undefined));
|
|
120
|
-
return ok ? items : undefined;
|
|
121
|
-
}
|
|
122
|
-
function money(cents) {
|
|
123
|
-
const n = num(cents);
|
|
124
|
-
return n === undefined ? undefined : `$${(n / 100).toFixed(2)}`;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* The receipt for one successful call, or nothing. Adding a projector is the
|
|
128
|
-
* whole job of making a tool's result visible — no UI change needed.
|
|
129
|
-
*/
|
|
130
|
-
function projectReceipt(input) {
|
|
131
|
-
const {
|
|
132
|
-
toolId,
|
|
133
|
-
action,
|
|
134
|
-
params,
|
|
135
|
-
result
|
|
136
|
-
} = input;
|
|
137
|
-
const r = rec(result);
|
|
138
|
-
const source = input.effect === "read" ? "live" : "changed";
|
|
139
|
-
|
|
140
|
-
// ---- network --------------------------------------------------------
|
|
141
|
-
if (toolId === "network" && action === "getSnapshot" && r) {
|
|
142
|
-
const requests = arr(r.requests).map(rec).filter(Boolean).map(x => requestSummary(x)).filter(Boolean);
|
|
143
|
-
if (requests.length === 0) return undefined;
|
|
144
|
-
return {
|
|
145
|
-
id: id(),
|
|
146
|
-
kind: "requestList",
|
|
147
|
-
requests,
|
|
148
|
-
total: num(r.totalCaptured) ?? requests.length,
|
|
149
|
-
source
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
if (toolId === "network" && action === "getEventBody" && r) {
|
|
153
|
-
const reqId = str(params.id) ?? "";
|
|
154
|
-
const wasMocked = r.override != null && typeof r.override === "object";
|
|
155
|
-
return {
|
|
156
|
-
id: id(),
|
|
157
|
-
kind: "requestCard",
|
|
158
|
-
request: {
|
|
159
|
-
id: reqId,
|
|
160
|
-
method: str(r.method) ?? "",
|
|
161
|
-
url: str(r.url) ?? reqId,
|
|
162
|
-
status: num(r.status),
|
|
163
|
-
mocked: wasMocked || undefined
|
|
164
|
-
},
|
|
165
|
-
requestBody: r.requestData ?? undefined,
|
|
166
|
-
responseBody: r.responseData ?? undefined,
|
|
167
|
-
// A body an override rule synthesized must never render as "live".
|
|
168
|
-
source: wasMocked ? "mocked" : source
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
if (toolId === "network" && (action === "upsertOverrideRule" || action === "getOverrideRuleBody") && r) {
|
|
172
|
-
const rule = rec(r.rule) ?? r;
|
|
173
|
-
const ruleId = str(rule.id);
|
|
174
|
-
const pattern = str(rule.urlPattern);
|
|
175
|
-
if (!ruleId || !pattern) return undefined;
|
|
176
|
-
const kind = str(rule.kind) ?? "respond";
|
|
177
|
-
const asked = rec(params.rule) ?? {};
|
|
178
|
-
return {
|
|
179
|
-
id: id(),
|
|
180
|
-
kind: "ruleCard",
|
|
181
|
-
rule: {
|
|
182
|
-
id: ruleId,
|
|
183
|
-
name: str(rule.name) ?? str(asked.name),
|
|
184
|
-
urlPattern: pattern,
|
|
185
|
-
methods: arr(rule.methods ?? asked.methods).map(String),
|
|
186
|
-
kind,
|
|
187
|
-
status: num(rule.status) ?? num(asked.status),
|
|
188
|
-
delayMs: num(rule.delayMs) ?? num(asked.delayMs),
|
|
189
|
-
times: num(rule.times) ?? num(asked.times),
|
|
190
|
-
enabled: rule.enabled !== false,
|
|
191
|
-
hits: num(rule.hits)
|
|
192
|
-
},
|
|
193
|
-
source: action === "upsertOverrideRule" ? "changed" : "live"
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
if (toolId === "network" && action === "listOverrideRules" && r) {
|
|
197
|
-
const rules = arr(r.rules).map(rec).filter(Boolean);
|
|
198
|
-
if (rules.length === 0) return undefined;
|
|
199
|
-
return {
|
|
200
|
-
id: id(),
|
|
201
|
-
kind: "list",
|
|
202
|
-
title: `${rules.length} network override${rules.length === 1 ? "" : "s"}`,
|
|
203
|
-
items: rules.map(x => ({
|
|
204
|
-
id: str(x.id),
|
|
205
|
-
title: str(x.name) ?? str(x.urlPattern) ?? "rule",
|
|
206
|
-
subtitle: `${arr(x.methods).join("/") || "ANY"} ${str(x.urlPattern) ?? ""} → ${str(x.kind) ?? ""}${x.status ? ` ${String(x.status)}` : ""}`,
|
|
207
|
-
trailing: x.enabled === false ? "off" : `on · ${num(x.hits) ?? 0} hit${num(x.hits) === 1 ? "" : "s"}`,
|
|
208
|
-
tone: x.enabled === false ? "muted" : "warning"
|
|
209
|
-
})),
|
|
210
|
-
total: rules.length,
|
|
211
|
-
source: "live"
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ---- routes ---------------------------------------------------------
|
|
216
|
-
if (toolId === "route-events" && action === "getSnapshot" && r) {
|
|
217
|
-
const routes = [...new Set(arr(r.routes).map(String))];
|
|
218
|
-
if (routes.length === 0) return undefined;
|
|
219
|
-
const current = rec(r.currentRoute);
|
|
220
|
-
return {
|
|
221
|
-
id: id(),
|
|
222
|
-
kind: "routeChips",
|
|
223
|
-
routes: routes.map(p => ({
|
|
224
|
-
path: p,
|
|
225
|
-
dynamic: /\[.+\]/.test(p)
|
|
226
|
-
})),
|
|
227
|
-
current: str(current?.path),
|
|
228
|
-
total: routes.length,
|
|
229
|
-
source: "live"
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
if (toolId === "route-events" && action === "navigate" && r) {
|
|
233
|
-
const path = str(r.navigated) ?? str(params.path);
|
|
234
|
-
return path ? {
|
|
235
|
-
id: id(),
|
|
236
|
-
kind: "peek",
|
|
237
|
-
label: `Opened ${path}`,
|
|
238
|
-
path
|
|
239
|
-
} : undefined;
|
|
240
|
-
}
|
|
241
|
-
if (toolId === "route-events" && (action === "stackGoBack" || action === "stackPopToTop")) {
|
|
242
|
-
return {
|
|
243
|
-
id: id(),
|
|
244
|
-
kind: "peek",
|
|
245
|
-
label: action === "stackPopToTop" ? "Went back to the first screen" : "Went back"
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// ---- screen ---------------------------------------------------------
|
|
250
|
-
if (toolId === "highlight-updates" && action === "describeScreen" && r) {
|
|
251
|
-
const els = arr(r.elements).map(rec).filter(Boolean);
|
|
252
|
-
const interactive = els.filter(e => e.interactive === true && !String(str(e.testID) ?? "").startsWith("buoy-"));
|
|
253
|
-
if (interactive.length === 0) return undefined;
|
|
254
|
-
return {
|
|
255
|
-
id: id(),
|
|
256
|
-
kind: "screenElements",
|
|
257
|
-
elements: interactive.map(e => ({
|
|
258
|
-
nativeTag: num(e.nativeTag) ?? 0,
|
|
259
|
-
name: str(e.name) ?? "",
|
|
260
|
-
label: str(e.label) ?? str(e.text),
|
|
261
|
-
testID: str(e.testID),
|
|
262
|
-
interactive: true
|
|
263
|
-
})),
|
|
264
|
-
total: interactive.length,
|
|
265
|
-
source: "live"
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
if (toolId === "highlight-updates" && action === "tapElement" && r?.tapped === true) {
|
|
269
|
-
const m = rec(r.matched);
|
|
270
|
-
const what = str(m?.label) ?? str(m?.text) ?? str(m?.testID) ?? "element";
|
|
271
|
-
return {
|
|
272
|
-
id: id(),
|
|
273
|
-
kind: "peek",
|
|
274
|
-
label: `Tapped ${what}`
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// ---- console --------------------------------------------------------
|
|
279
|
-
if (toolId === "console" && action === "getSnapshot" && r) {
|
|
280
|
-
const entries = arr(r.entries);
|
|
281
|
-
if (entries.length === 0) return undefined;
|
|
282
|
-
const grouped = groupLogs(entries);
|
|
283
|
-
return {
|
|
284
|
-
id: id(),
|
|
285
|
-
kind: "logList",
|
|
286
|
-
entries: grouped,
|
|
287
|
-
total: num(r.totalCaptured) ?? entries.length,
|
|
288
|
-
source: "live"
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// ---- state stores ---------------------------------------------------
|
|
293
|
-
if (toolId === "zustand" && action === "getStoreState" && r?.found !== false) {
|
|
294
|
-
const state = rec(r?.currentState);
|
|
295
|
-
if (!state) return undefined;
|
|
296
|
-
const items = looksLikeItems(state.lines ?? state.items);
|
|
297
|
-
if (items) {
|
|
298
|
-
return {
|
|
299
|
-
id: id(),
|
|
300
|
-
kind: "list",
|
|
301
|
-
title: str(params.storeName),
|
|
302
|
-
items: items.map(i => ({
|
|
303
|
-
id: str(i.lineId) ?? str(i.id),
|
|
304
|
-
title: str(i.name) ?? "",
|
|
305
|
-
subtitle: [str(i.sizeName), i.qty !== undefined ? `×${String(i.qty)}` : undefined].filter(Boolean).join(" · "),
|
|
306
|
-
trailing: money(i.unitPrice ?? i.price),
|
|
307
|
-
image: str(i.image)
|
|
308
|
-
})),
|
|
309
|
-
total: items.length,
|
|
310
|
-
source: "live"
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
const pairs = Object.entries(state).filter(([, v]) => typeof v !== "function").map(([key, value]) => ({
|
|
314
|
-
key,
|
|
315
|
-
value
|
|
316
|
-
}));
|
|
317
|
-
return {
|
|
318
|
-
id: id(),
|
|
319
|
-
kind: "keyValue",
|
|
320
|
-
title: str(params.storeName),
|
|
321
|
-
pairs,
|
|
322
|
-
source: "live"
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
if (toolId === "zustand" && action === "setState" && r?.ok !== false) {
|
|
326
|
-
// Diff the store as it WAS against the store as it IS — never against the
|
|
327
|
-
// request. What was asked for and what landed are different things: a
|
|
328
|
-
// `path` edit carries no `state` at all (so the whole store read as
|
|
329
|
-
// deleted), a keyed list edit is expanded by the adapter before it is
|
|
330
|
-
// applied, and a merge leaves every omitted key alone. Reading both sides
|
|
331
|
-
// back is the only version of this that cannot lie, and it costs one local
|
|
332
|
-
// dispatch — no model tokens. `after` is absent only on a host that could
|
|
333
|
-
// not re-read the store; the patch-shaped fallbacks below are for that.
|
|
334
|
-
const beforeState = rec(input.before)?.currentState ?? input.before;
|
|
335
|
-
const afterState = input.after === undefined ? undefined : rec(input.after)?.currentState ?? input.after;
|
|
336
|
-
const changes = afterState !== undefined ? shallowDiff(beforeState, afterState) : params.replace === true ? shallowDiff(beforeState, params.state) : patchDiff(beforeState, params.state);
|
|
337
|
-
if (changes.length === 0) return undefined;
|
|
338
|
-
return {
|
|
339
|
-
id: id(),
|
|
340
|
-
kind: "diff",
|
|
341
|
-
title: `${str(params.storeName) ?? "store"} — what changed`,
|
|
342
|
-
changes,
|
|
343
|
-
source: "changed"
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
if (toolId === "zustand" && action === "listStores" && r) {
|
|
347
|
-
const stores = arr(r.stores).map(rec).filter(Boolean);
|
|
348
|
-
if (stores.length === 0) return undefined;
|
|
349
|
-
return {
|
|
350
|
-
id: id(),
|
|
351
|
-
kind: "list",
|
|
352
|
-
title: `${stores.length} store${stores.length === 1 ? "" : "s"}`,
|
|
353
|
-
items: stores.map(s => ({
|
|
354
|
-
id: str(s.name),
|
|
355
|
-
title: str(s.name) ?? "",
|
|
356
|
-
subtitle: arr(s.keys).slice(0, 6).join(", "),
|
|
357
|
-
trailing: s.isPersisted ? "persisted" : undefined
|
|
358
|
-
})),
|
|
359
|
-
total: stores.length,
|
|
360
|
-
source: "live"
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
if (toolId === "redux" && action === "getState" && r?.available !== false) {
|
|
364
|
-
const state = rec(r?.state) ?? r;
|
|
365
|
-
if (!state) return undefined;
|
|
366
|
-
const pairs = Object.entries(state).map(([key, value]) => ({
|
|
367
|
-
key,
|
|
368
|
-
value
|
|
369
|
-
}));
|
|
370
|
-
return {
|
|
371
|
-
id: id(),
|
|
372
|
-
kind: "keyValue",
|
|
373
|
-
title: "Redux state",
|
|
374
|
-
pairs,
|
|
375
|
-
source: "live"
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// ---- storage --------------------------------------------------------
|
|
380
|
-
if (toolId === "storage" && (action === "async.getItem" || action === "mmkv.get" || action === "secure.get")) {
|
|
381
|
-
const key = str(params.key) ?? "";
|
|
382
|
-
let value = r && "value" in r ? r.value : result;
|
|
383
|
-
if (r?.found === false || value === null || value === undefined) return undefined;
|
|
384
|
-
if (typeof value === "string") {
|
|
385
|
-
try {
|
|
386
|
-
value = JSON.parse(value);
|
|
387
|
-
} catch {
|
|
388
|
-
/* plain string */
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
const obj = rec(value);
|
|
392
|
-
const items = looksLikeItems(rec(obj?.state)?.lines ?? obj?.lines ?? obj?.items);
|
|
393
|
-
if (items) {
|
|
394
|
-
return {
|
|
395
|
-
id: id(),
|
|
396
|
-
kind: "list",
|
|
397
|
-
title: key,
|
|
398
|
-
items: items.map(i => ({
|
|
399
|
-
id: str(i.lineId) ?? str(i.id),
|
|
400
|
-
title: str(i.name) ?? "",
|
|
401
|
-
subtitle: [str(i.sizeName), i.qty !== undefined ? `×${String(i.qty)}` : undefined].filter(Boolean).join(" · "),
|
|
402
|
-
trailing: money(i.unitPrice ?? i.price),
|
|
403
|
-
image: str(i.image)
|
|
404
|
-
})),
|
|
405
|
-
total: items.length,
|
|
406
|
-
source: "live"
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
if (obj) {
|
|
410
|
-
const pairs = Object.entries(obj).map(([k, v]) => ({
|
|
411
|
-
key: k,
|
|
412
|
-
value: v
|
|
413
|
-
}));
|
|
414
|
-
return {
|
|
415
|
-
id: id(),
|
|
416
|
-
kind: "keyValue",
|
|
417
|
-
title: key,
|
|
418
|
-
pairs,
|
|
419
|
-
source: "live"
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
return {
|
|
423
|
-
id: id(),
|
|
424
|
-
kind: "keyValue",
|
|
425
|
-
title: key,
|
|
426
|
-
pairs: [{
|
|
427
|
-
key: "value",
|
|
428
|
-
value
|
|
429
|
-
}],
|
|
430
|
-
source: "live"
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
if (toolId === "storage" && action === "async.getAllKeys") {
|
|
434
|
-
const keys = arr(result).map(String);
|
|
435
|
-
if (keys.length === 0) return undefined;
|
|
436
|
-
return {
|
|
437
|
-
id: id(),
|
|
438
|
-
kind: "list",
|
|
439
|
-
title: `${keys.length} stored key${keys.length === 1 ? "" : "s"}`,
|
|
440
|
-
items: keys.map(k => ({
|
|
441
|
-
id: k,
|
|
442
|
-
title: k,
|
|
443
|
-
action: {
|
|
444
|
-
label: "Read",
|
|
445
|
-
dispatch: {
|
|
446
|
-
toolId: "storage",
|
|
447
|
-
action: "async.getItem",
|
|
448
|
-
params: {
|
|
449
|
-
key: k
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
})),
|
|
454
|
-
total: keys.length,
|
|
455
|
-
source: "live"
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
if (toolId === "storage" && (action === "async.setItem" || action === "mmkv.set" || action === "secure.set")) {
|
|
459
|
-
const key = str(params.key) ?? "";
|
|
460
|
-
const b = rec(input.before);
|
|
461
|
-
const beforeVal = b?.had ? b.value : undefined;
|
|
462
|
-
return {
|
|
463
|
-
id: id(),
|
|
464
|
-
kind: "diff",
|
|
465
|
-
title: `${key} — saved`,
|
|
466
|
-
changes: [{
|
|
467
|
-
path: key,
|
|
468
|
-
before: beforeVal,
|
|
469
|
-
after: params.value
|
|
470
|
-
}],
|
|
471
|
-
source: "changed"
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// ---- env ------------------------------------------------------------
|
|
476
|
-
if (toolId === "env" && action === "getSnapshot" && r) {
|
|
477
|
-
const env = rec(r.env);
|
|
478
|
-
if (!env || Object.keys(env).length === 0) return undefined;
|
|
479
|
-
const pairs = Object.entries(env).map(([key, value]) => ({
|
|
480
|
-
key,
|
|
481
|
-
value
|
|
482
|
-
}));
|
|
483
|
-
return {
|
|
484
|
-
id: id(),
|
|
485
|
-
kind: "keyValue",
|
|
486
|
-
title: `Environment — ${Object.keys(env).length} var${Object.keys(env).length === 1 ? "" : "s"}`,
|
|
487
|
-
pairs,
|
|
488
|
-
source: "live"
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// ---- sentry ---------------------------------------------------------
|
|
493
|
-
if (toolId === "sentry" && action === "getSnapshot" && r) {
|
|
494
|
-
const envelopes = arr(r.envelopes).map(rec).filter(Boolean);
|
|
495
|
-
const status = str(r.status);
|
|
496
|
-
if (envelopes.length === 0) {
|
|
497
|
-
// The status IS the answer when nothing was captured: "no client" and
|
|
498
|
-
// "nothing errored" are different findings and must not look alike.
|
|
499
|
-
if (status === "sdk-not-found" || status === "no-client") {
|
|
500
|
-
return {
|
|
501
|
-
id: id(),
|
|
502
|
-
kind: "notice",
|
|
503
|
-
tone: "warning",
|
|
504
|
-
text: "This app has no live Sentry client, so nothing can ever be captured here — that's different from \"nothing was sent\"."
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
return undefined;
|
|
508
|
-
}
|
|
509
|
-
return {
|
|
510
|
-
id: id(),
|
|
511
|
-
kind: "list",
|
|
512
|
-
title: `${num(r.totalCaptured) ?? envelopes.length} envelope${(num(r.totalCaptured) ?? envelopes.length) === 1 ? "" : "s"} sent to Sentry`,
|
|
513
|
-
items: envelopes.map(e => {
|
|
514
|
-
const items = arr(e.items).map(rec).filter(Boolean);
|
|
515
|
-
const first = items[0];
|
|
516
|
-
const isError = items.some(i => i.type === "event");
|
|
517
|
-
return {
|
|
518
|
-
id: e.id !== undefined ? String(e.id) : undefined,
|
|
519
|
-
title: str(first?.summary) ?? str(first?.type) ?? "envelope",
|
|
520
|
-
subtitle: items.map(i => str(i.type)).filter(Boolean).join(" · "),
|
|
521
|
-
trailing: str(e.origin),
|
|
522
|
-
tone: isError ? "danger" : undefined
|
|
523
|
-
};
|
|
524
|
-
}),
|
|
525
|
-
total: num(r.totalCaptured) ?? envelopes.length,
|
|
526
|
-
source: "live"
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// ---- impersonate ----------------------------------------------------
|
|
531
|
-
if (toolId === "impersonate" && action === "searchUsers") {
|
|
532
|
-
const users = arr(result).map(rec).filter(Boolean);
|
|
533
|
-
if (users.length === 0) return undefined;
|
|
534
|
-
return {
|
|
535
|
-
id: id(),
|
|
536
|
-
kind: "list",
|
|
537
|
-
title: `${users.length} user${users.length === 1 ? "" : "s"} found`,
|
|
538
|
-
items: users.map(u => ({
|
|
539
|
-
id: str(u.id),
|
|
540
|
-
title: str(u.displayName) ?? str(u.email) ?? str(u.id) ?? "user",
|
|
541
|
-
subtitle: [str(u.email), str(rec(u.metadata)?.role)].filter(Boolean).join(" · "),
|
|
542
|
-
image: str(u.avatarUrl),
|
|
543
|
-
// The tap goes through runGatedAction, so it carries the same policy,
|
|
544
|
-
// approval and ledger path a model call would.
|
|
545
|
-
action: {
|
|
546
|
-
label: "Impersonate",
|
|
547
|
-
dispatch: {
|
|
548
|
-
toolId: "impersonate",
|
|
549
|
-
action: "startImpersonation",
|
|
550
|
-
params: {
|
|
551
|
-
user: u
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
})),
|
|
556
|
-
total: users.length,
|
|
557
|
-
source: "live"
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
if (toolId === "impersonate" && action === "startImpersonation" && r?.ok !== false) {
|
|
561
|
-
const u = rec(params.user);
|
|
562
|
-
const who = str(u?.displayName) ?? str(u?.email) ?? str(u?.id) ?? "that user";
|
|
563
|
-
return {
|
|
564
|
-
id: id(),
|
|
565
|
-
kind: "notice",
|
|
566
|
-
tone: "warning",
|
|
567
|
-
text: `Now viewing the app as ${who}. Cached data was cleared so everything refetches as them — stopping clears caches again.`
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
if (toolId === "impersonate" && action === "stopImpersonation") {
|
|
571
|
-
return {
|
|
572
|
-
id: id(),
|
|
573
|
-
kind: "notice",
|
|
574
|
-
tone: "info",
|
|
575
|
-
text: "Stopped impersonating — back to the real account. Cached app data was cleared again."
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// ---- scenarios ------------------------------------------------------
|
|
580
|
-
if (toolId === "scenarios" && action === "listScenarios" && r) {
|
|
581
|
-
const all = [...arr(r.code), ...arr(r.device), ...arr(r.drafts)].map(rec).filter(Boolean);
|
|
582
|
-
if (all.length === 0) return undefined;
|
|
583
|
-
return {
|
|
584
|
-
id: id(),
|
|
585
|
-
kind: "list",
|
|
586
|
-
title: `${all.length} scenario${all.length === 1 ? "" : "s"}`,
|
|
587
|
-
items: all.map(s => ({
|
|
588
|
-
id: str(s.id),
|
|
589
|
-
title: str(s.name) ?? str(s.id) ?? "scenario",
|
|
590
|
-
subtitle: str(s.description),
|
|
591
|
-
trailing: str(s.source) === "draft" ? "draft — needs accepting" : num(s.stepCount) !== undefined ? `${String(num(s.stepCount))} step${num(s.stepCount) === 1 ? "" : "s"}` : undefined,
|
|
592
|
-
tone: str(s.source) === "draft" ? "muted" : undefined
|
|
593
|
-
})),
|
|
594
|
-
total: all.length,
|
|
595
|
-
source: "live"
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
if (toolId === "scenarios" && action === "run" && r) {
|
|
599
|
-
if (r.ok === false) {
|
|
600
|
-
const errs = arr(r.preflightErrors).map(String).join("; ");
|
|
601
|
-
return {
|
|
602
|
-
id: id(),
|
|
603
|
-
kind: "notice",
|
|
604
|
-
tone: "warning",
|
|
605
|
-
text: `The scenario did not run — pre-flight failed${errs ? `: ${errs}` : ""}. Nothing was applied.`
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
const steps = arr(r.steps).map(rec).filter(Boolean);
|
|
609
|
-
if (steps.length === 0) return undefined;
|
|
610
|
-
return {
|
|
611
|
-
id: id(),
|
|
612
|
-
kind: "steps",
|
|
613
|
-
title: `Ran ${str(r.name) ?? "the scenario"}`,
|
|
614
|
-
steps: steps.map(s => ({
|
|
615
|
-
text: str(s.label) ?? "step",
|
|
616
|
-
state: s.skipped === true ? "todo" : s.ok === false ? "failed" : "done"
|
|
617
|
-
}))
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// ---- time machine ---------------------------------------------------
|
|
622
|
-
if (toolId === "time-machine" && action === "list" && r) {
|
|
623
|
-
const snaps = arr(r.snapshots).map(rec).filter(Boolean);
|
|
624
|
-
if (snaps.length === 0) return undefined;
|
|
625
|
-
return {
|
|
626
|
-
id: id(),
|
|
627
|
-
kind: "list",
|
|
628
|
-
title: `${snaps.length} restore point${snaps.length === 1 ? "" : "s"}`,
|
|
629
|
-
items: snaps.map(s => ({
|
|
630
|
-
id: str(s.id),
|
|
631
|
-
title: str(s.name) ?? str(s.id) ?? "snapshot",
|
|
632
|
-
subtitle: str(rec(s.route)?.pathname),
|
|
633
|
-
trailing: s.baseline === true ? "baseline" : undefined
|
|
634
|
-
})),
|
|
635
|
-
total: snaps.length,
|
|
636
|
-
source: "live"
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
if (toolId === "time-machine" && action === "capture" && r) {
|
|
640
|
-
return {
|
|
641
|
-
id: id(),
|
|
642
|
-
kind: "notice",
|
|
643
|
-
tone: "info",
|
|
644
|
-
text: `Saved a restore point${typeof params.name === "string" ? ` — "${params.name}"` : ""}. The app can be brought back to this exact state any time.`
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
if (toolId === "time-machine" && action === "restore" && r) {
|
|
648
|
-
return {
|
|
649
|
-
id: id(),
|
|
650
|
-
kind: "notice",
|
|
651
|
-
tone: "warning",
|
|
652
|
-
text: "Restored the snapshot — the app's state just jumped back. What's on screen now reflects the restore point, not what happened since."
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
// ---- jotai ----------------------------------------------------------
|
|
657
|
-
if (toolId === "jotai" && action === "listAtoms" && r) {
|
|
658
|
-
const atoms = arr(r.atoms).map(rec).filter(Boolean);
|
|
659
|
-
if (atoms.length === 0) return undefined;
|
|
660
|
-
return {
|
|
661
|
-
id: id(),
|
|
662
|
-
kind: "list",
|
|
663
|
-
title: `${num(r.total) ?? atoms.length} atom${(num(r.total) ?? atoms.length) === 1 ? "" : "s"}`,
|
|
664
|
-
items: atoms.map(a => ({
|
|
665
|
-
id: str(a.label),
|
|
666
|
-
title: str(a.label) ?? "atom",
|
|
667
|
-
subtitle: a.writable === true ? "writable" : "read-only (derived)",
|
|
668
|
-
trailing: num(a.changes) !== undefined ? `${String(num(a.changes))} change${num(a.changes) === 1 ? "" : "s"}` : undefined
|
|
669
|
-
})),
|
|
670
|
-
total: num(r.total) ?? atoms.length,
|
|
671
|
-
source: "live"
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// ---- images ---------------------------------------------------------
|
|
676
|
-
if (toolId === "images" && action === "list" && r) {
|
|
677
|
-
const records = arr(r.records).map(rec).filter(Boolean);
|
|
678
|
-
const images = records.map(x => ({
|
|
679
|
-
url: str(x.uri) ?? "",
|
|
680
|
-
alt: str(x.uri)?.split("/").pop() ?? "image",
|
|
681
|
-
caption: x.ms !== undefined ? `${String(x.ms)} ms · ${str(x.cache) ?? ""}` : undefined
|
|
682
|
-
})).filter(i => i.url);
|
|
683
|
-
if (images.length === 0) return undefined;
|
|
684
|
-
return {
|
|
685
|
-
id: id(),
|
|
686
|
-
kind: "imageGrid",
|
|
687
|
-
title: "Images the app has loaded",
|
|
688
|
-
images: images.slice(0, _types.CAPS.gridMax),
|
|
689
|
-
total: num(r.total) ?? images.length,
|
|
690
|
-
source: "live"
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// ---- react-query ----------------------------------------------------
|
|
695
|
-
if (toolId === "query" && action === "setQueryData" && r?.ok !== false) {
|
|
696
|
-
/**
|
|
697
|
-
* What the CACHE looks like now against what it looked like before —
|
|
698
|
-
* never against the request, for the same reasons as the zustand diff
|
|
699
|
-
* above: a `path` edit carries no `data` at all, a keyed list edit is
|
|
700
|
-
* expanded by the adapter before it lands, and a merge leaves every
|
|
701
|
-
* omitted key alone. Both sides are read back through `getQueryData`, so
|
|
702
|
-
* the card shows what actually moved.
|
|
703
|
-
*/
|
|
704
|
-
const beforeData = rec(input.before)?.data ?? input.before;
|
|
705
|
-
const afterData = input.after === undefined ? undefined : rec(input.after)?.data ?? input.after;
|
|
706
|
-
const changes = afterData !== undefined ? shallowDiff(beforeData, afterData) : patchDiff(beforeData, params.data);
|
|
707
|
-
if (changes.length === 0) return undefined;
|
|
708
|
-
const key = Array.isArray(params.queryKey) ? params.queryKey.filter(k => typeof k === "string").join(" › ") : undefined;
|
|
709
|
-
return {
|
|
710
|
-
id: id(),
|
|
711
|
-
kind: "diff",
|
|
712
|
-
title: `${key || str(params.queryHash) || "query"} — what changed`,
|
|
713
|
-
changes,
|
|
714
|
-
source: "changed"
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
if (toolId === "query" && action === "listQueries" && r) {
|
|
718
|
-
const qs = arr(r.queries).map(rec).filter(Boolean);
|
|
719
|
-
if (qs.length === 0) return undefined;
|
|
720
|
-
return {
|
|
721
|
-
id: id(),
|
|
722
|
-
kind: "list",
|
|
723
|
-
title: `${qs.length} cached quer${qs.length === 1 ? "y" : "ies"}`,
|
|
724
|
-
items: qs.map(q => {
|
|
725
|
-
const key = arr(q.queryKey).filter(k => typeof k === "string").join(" › ") || str(q.queryHash) || "query";
|
|
726
|
-
const stale = q.isStale === true;
|
|
727
|
-
return {
|
|
728
|
-
id: str(q.queryHash),
|
|
729
|
-
title: key,
|
|
730
|
-
subtitle: `${str(q.status) ?? ""}${q.fetchStatus && q.fetchStatus !== "idle" ? ` · ${String(q.fetchStatus)}` : ""}`,
|
|
731
|
-
trailing: stale ? "stale" : "fresh",
|
|
732
|
-
tone: stale ? "warning" : "accent"
|
|
733
|
-
};
|
|
734
|
-
}),
|
|
735
|
-
total: qs.length,
|
|
736
|
-
source: "live"
|
|
737
|
-
};
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// ---- render measurement → chart --------------------------------------
|
|
741
|
-
if (toolId === "highlight-updates" && action === "endMeasurement" && r) {
|
|
742
|
-
const comps = arr(r.components ?? r.renders).map(rec).filter(Boolean);
|
|
743
|
-
const bars = comps.map(c => ({
|
|
744
|
-
label: str(c.name) ?? str(c.componentName) ?? "?",
|
|
745
|
-
value: num(c.renders) ?? num(c.count) ?? 0
|
|
746
|
-
})).filter(b => b.value > 0).sort((a, b) => b.value - a.value).slice(0, _types.CAPS.chartBars);
|
|
747
|
-
if (bars.length === 0) return undefined;
|
|
748
|
-
return {
|
|
749
|
-
id: id(),
|
|
750
|
-
kind: "chart",
|
|
751
|
-
title: "Renders during the measurement",
|
|
752
|
-
bars: bars.map(b => ({
|
|
753
|
-
...b,
|
|
754
|
-
unit: "renders"
|
|
755
|
-
})),
|
|
756
|
-
source: "live"
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
return undefined;
|
|
760
|
-
}
|
|
761
|
-
//# sourceMappingURL=receipts.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.projectReceipt=projectReceipt;exports.shallowDiff=shallowDiff;var _runId=require("./runId");var _types=require("./types");const id=(0,_runId.idFactory)("rc");const rec=a=>a&&typeof a==="object"&&!Array.isArray(a)?a:void 0;const arr=a=>Array.isArray(a)?a:[];const str=a=>typeof a==="string"?a:void 0;const num=a=>typeof a==="number"?a:void 0;function pathOf(a){try{const s=new URL(a);return s.pathname+(s.search.length>1?"?\u2026":"")}catch{return a}}function requestSummary(a){const s=str(a.id);const r=str(a.url);if(!s||!r)return void 0;return{id:s,method:str(a.method)??"GET",url:r,status:num(a.status),durationMs:num(a.durationMs),mocked:a.overridden===true||a.override!=null&&typeof a.override==="object"||void 0}}function patchDiff(a,s){const r=rec(a)??{};const o=rec(s)??{};const c=[];for(const n of Object.keys(o)){if(typeof r[n]==="function"||typeof o[n]==="function")continue;if(JSON.stringify(r[n])!==JSON.stringify(o[n]))c.push({path:n,before:r[n],after:o[n]})}return c}function shallowDiff(a,s){const r=rec(a)??{};const o=rec(s)??{};const c=new Set([...Object.keys(r),...Object.keys(o)]);const n=[];for(const u of c){if(typeof r[u]==="function"||typeof o[u]==="function")continue;if(JSON.stringify(r[u])!==JSON.stringify(o[u])){n.push({path:u,before:r[u],after:o[u]})}}return n}function groupLogs(a){const s=new Map;for(const r of a){const o=rec(r);if(!o)continue;const c=str(o.message)??"";const n=str(o.level)??"log";const u=`${n}:${c}`;const e=s.get(u);if(e)e.count+=1;else s.set(u,{level:n,message:c,count:1})}return[...s.values()]}function looksLikeItems(a){const s=arr(a).map(rec).filter(Boolean);if(s.length<2)return void 0;const r=s.every(o=>str(o.name)&&(o.image!==void 0||o.qty!==void 0||o.price!==void 0||o.unitPrice!==void 0));return r?s:void 0}function money(a){const s=num(a);return s===void 0?void 0:`$${(s/100).toFixed(2)}`}function projectReceipt(a){const{toolId:s,action:r,params:o,result:c}=a;const n=rec(c);const u=a.effect==="read"?"live":"changed";if(s==="network"&&r==="getSnapshot"&&n){const e=arr(n.requests).map(rec).filter(Boolean).map(t=>requestSummary(t)).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"requestList",requests:e,total:num(n.totalCaptured)??e.length,source:u}}if(s==="network"&&r==="getEventBody"&&n){const e=str(o.id)??"";const t=n.override!=null&&typeof n.override==="object";return{id:id(),kind:"requestCard",request:{id:e,method:str(n.method)??"",url:str(n.url)??e,status:num(n.status),mocked:t||void 0},requestBody:n.requestData??void 0,responseBody:n.responseData??void 0,source:t?"mocked":u}}if(s==="network"&&(r==="upsertOverrideRule"||r==="getOverrideRuleBody")&&n){const e=rec(n.rule)??n;const t=str(e.id);const i=str(e.urlPattern);if(!t||!i)return void 0;const d=str(e.kind)??"respond";const l=rec(o.rule)??{};return{id:id(),kind:"ruleCard",rule:{id:t,name:str(e.name)??str(l.name),urlPattern:i,methods:arr(e.methods??l.methods).map(String),kind:d,status:num(e.status)??num(l.status),delayMs:num(e.delayMs)??num(l.delayMs),times:num(e.times)??num(l.times),enabled:e.enabled!==false,hits:num(e.hits)},source:r==="upsertOverrideRule"?"changed":"live"}}if(s==="network"&&r==="listOverrideRules"&&n){const e=arr(n.rules).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} network override${e.length===1?"":"s"}`,items:e.map(t=>({id:str(t.id),title:str(t.name)??str(t.urlPattern)??"rule",subtitle:`${arr(t.methods).join("/")||"ANY"} ${str(t.urlPattern)??""} \u2192 ${str(t.kind)??""}${t.status?` ${String(t.status)}`:""}`,trailing:t.enabled===false?"off":`on \xB7 ${num(t.hits)??0} hit${num(t.hits)===1?"":"s"}`,tone:t.enabled===false?"muted":"warning"})),total:e.length,source:"live"}}if(s==="route-events"&&r==="getSnapshot"&&n){const e=[...new Set(arr(n.routes).map(String))];if(e.length===0)return void 0;const t=rec(n.currentRoute);return{id:id(),kind:"routeChips",routes:e.map(i=>({path:i,dynamic:/\[.+\]/.test(i)})),current:str(t?.path),total:e.length,source:"live"}}if(s==="route-events"&&r==="navigate"&&n){const e=str(n.navigated)??str(o.path);return e?{id:id(),kind:"peek",label:`Opened ${e}`,path:e}:void 0}if(s==="route-events"&&(r==="stackGoBack"||r==="stackPopToTop")){return{id:id(),kind:"peek",label:r==="stackPopToTop"?"Went back to the first screen":"Went back"}}if(s==="highlight-updates"&&r==="describeScreen"&&n){const e=arr(n.elements).map(rec).filter(Boolean);const t=e.filter(i=>i.interactive===true&&!String(str(i.testID)??"").startsWith("buoy-"));if(t.length===0)return void 0;return{id:id(),kind:"screenElements",elements:t.map(i=>({nativeTag:num(i.nativeTag)??0,name:str(i.name)??"",label:str(i.label)??str(i.text),testID:str(i.testID),interactive:true})),total:t.length,source:"live"}}if(s==="highlight-updates"&&r==="tapElement"&&n?.tapped===true){const e=rec(n.matched);const t=str(e?.label)??str(e?.text)??str(e?.testID)??"element";return{id:id(),kind:"peek",label:`Tapped ${t}`}}if(s==="console"&&r==="getSnapshot"&&n){const e=arr(n.entries);if(e.length===0)return void 0;const t=groupLogs(e);return{id:id(),kind:"logList",entries:t,total:num(n.totalCaptured)??e.length,source:"live"}}if(s==="zustand"&&r==="getStoreState"&&n?.found!==false){const e=rec(n?.currentState);if(!e)return void 0;const t=looksLikeItems(e.lines??e.items);if(t){return{id:id(),kind:"list",title:str(o.storeName),items:t.map(d=>({id:str(d.lineId)??str(d.id),title:str(d.name)??"",subtitle:[str(d.sizeName),d.qty!==void 0?`\xD7${String(d.qty)}`:void 0].filter(Boolean).join(" \xB7 "),trailing:money(d.unitPrice??d.price),image:str(d.image)})),total:t.length,source:"live"}}const i=Object.entries(e).filter(([,d])=>typeof d!=="function").map(([d,l])=>({key:d,value:l}));return{id:id(),kind:"keyValue",title:str(o.storeName),pairs:i,source:"live"}}if(s==="zustand"&&r==="setState"&&n?.ok!==false){const e=rec(a.before)?.currentState??a.before;const t=a.after===void 0?void 0:rec(a.after)?.currentState??a.after;const i=t!==void 0?shallowDiff(e,t):o.replace===true?shallowDiff(e,o.state):patchDiff(e,o.state);if(i.length===0)return void 0;return{id:id(),kind:"diff",title:`${str(o.storeName)??"store"} \u2014 what changed`,changes:i,source:"changed"}}if(s==="zustand"&&r==="listStores"&&n){const e=arr(n.stores).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} store${e.length===1?"":"s"}`,items:e.map(t=>({id:str(t.name),title:str(t.name)??"",subtitle:arr(t.keys).slice(0,6).join(", "),trailing:t.isPersisted?"persisted":void 0})),total:e.length,source:"live"}}if(s==="redux"&&r==="getState"&&n?.available!==false){const e=rec(n?.state)??n;if(!e)return void 0;const t=Object.entries(e).map(([i,d])=>({key:i,value:d}));return{id:id(),kind:"keyValue",title:"Redux state",pairs:t,source:"live"}}if(s==="storage"&&(r==="async.getItem"||r==="mmkv.get"||r==="secure.get")){const e=str(o.key)??"";let t=n&&"value"in n?n.value:c;if(n?.found===false||t===null||t===void 0)return void 0;if(typeof t==="string"){try{t=JSON.parse(t)}catch{}}const i=rec(t);const d=looksLikeItems(rec(i?.state)?.lines??i?.lines??i?.items);if(d){return{id:id(),kind:"list",title:e,items:d.map(l=>({id:str(l.lineId)??str(l.id),title:str(l.name)??"",subtitle:[str(l.sizeName),l.qty!==void 0?`\xD7${String(l.qty)}`:void 0].filter(Boolean).join(" \xB7 "),trailing:money(l.unitPrice??l.price),image:str(l.image)})),total:d.length,source:"live"}}if(i){const l=Object.entries(i).map(([p,f])=>({key:p,value:f}));return{id:id(),kind:"keyValue",title:e,pairs:l,source:"live"}}return{id:id(),kind:"keyValue",title:e,pairs:[{key:"value",value:t}],source:"live"}}if(s==="storage"&&r==="async.getAllKeys"){const e=arr(c).map(String);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} stored key${e.length===1?"":"s"}`,items:e.map(t=>({id:t,title:t,action:{label:"Read",dispatch:{toolId:"storage",action:"async.getItem",params:{key:t}}}})),total:e.length,source:"live"}}if(s==="storage"&&(r==="async.setItem"||r==="mmkv.set"||r==="secure.set")){const e=str(o.key)??"";const t=rec(a.before);const i=t?.had?t.value:void 0;return{id:id(),kind:"diff",title:`${e} \u2014 saved`,changes:[{path:e,before:i,after:o.value}],source:"changed"}}if(s==="env"&&r==="getSnapshot"&&n){const e=rec(n.env);if(!e||Object.keys(e).length===0)return void 0;const t=Object.entries(e).map(([i,d])=>({key:i,value:d}));return{id:id(),kind:"keyValue",title:`Environment \u2014 ${Object.keys(e).length} var${Object.keys(e).length===1?"":"s"}`,pairs:t,source:"live"}}if(s==="sentry"&&r==="getSnapshot"&&n){const e=arr(n.envelopes).map(rec).filter(Boolean);const t=str(n.status);if(e.length===0){if(t==="sdk-not-found"||t==="no-client"){return{id:id(),kind:"notice",tone:"warning",text:`This app has no live Sentry client, so nothing can ever be captured here \u2014 that's different from "nothing was sent".`}}return void 0}return{id:id(),kind:"list",title:`${num(n.totalCaptured)??e.length} envelope${(num(n.totalCaptured)??e.length)===1?"":"s"} sent to Sentry`,items:e.map(i=>{const d=arr(i.items).map(rec).filter(Boolean);const l=d[0];const p=d.some(f=>f.type==="event");return{id:i.id!==void 0?String(i.id):void 0,title:str(l?.summary)??str(l?.type)??"envelope",subtitle:d.map(f=>str(f.type)).filter(Boolean).join(" \xB7 "),trailing:str(i.origin),tone:p?"danger":void 0}}),total:num(n.totalCaptured)??e.length,source:"live"}}if(s==="impersonate"&&r==="searchUsers"){const e=arr(c).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} user${e.length===1?"":"s"} found`,items:e.map(t=>({id:str(t.id),title:str(t.displayName)??str(t.email)??str(t.id)??"user",subtitle:[str(t.email),str(rec(t.metadata)?.role)].filter(Boolean).join(" \xB7 "),image:str(t.avatarUrl),action:{label:"Impersonate",dispatch:{toolId:"impersonate",action:"startImpersonation",params:{user:t}}}})),total:e.length,source:"live"}}if(s==="impersonate"&&r==="startImpersonation"&&n?.ok!==false){const e=rec(o.user);const t=str(e?.displayName)??str(e?.email)??str(e?.id)??"that user";return{id:id(),kind:"notice",tone:"warning",text:`Now viewing the app as ${t}. Cached data was cleared so everything refetches as them \u2014 stopping clears caches again.`}}if(s==="impersonate"&&r==="stopImpersonation"){return{id:id(),kind:"notice",tone:"info",text:"Stopped impersonating \u2014 back to the real account. Cached app data was cleared again."}}if(s==="scenarios"&&r==="listScenarios"&&n){const e=[...arr(n.code),...arr(n.device),...arr(n.drafts)].map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} scenario${e.length===1?"":"s"}`,items:e.map(t=>({id:str(t.id),title:str(t.name)??str(t.id)??"scenario",subtitle:str(t.description),trailing:str(t.source)==="draft"?"draft \u2014 needs accepting":num(t.stepCount)!==void 0?`${String(num(t.stepCount))} step${num(t.stepCount)===1?"":"s"}`:void 0,tone:str(t.source)==="draft"?"muted":void 0})),total:e.length,source:"live"}}if(s==="scenarios"&&r==="run"&&n){if(n.ok===false){const t=arr(n.preflightErrors).map(String).join("; ");return{id:id(),kind:"notice",tone:"warning",text:`The scenario did not run \u2014 pre-flight failed${t?`: ${t}`:""}. Nothing was applied.`}}const e=arr(n.steps).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"steps",title:`Ran ${str(n.name)??"the scenario"}`,steps:e.map(t=>({text:str(t.label)??"step",state:t.skipped===true?"todo":t.ok===false?"failed":"done"}))}}if(s==="time-machine"&&r==="list"&&n){const e=arr(n.snapshots).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} restore point${e.length===1?"":"s"}`,items:e.map(t=>({id:str(t.id),title:str(t.name)??str(t.id)??"snapshot",subtitle:str(rec(t.route)?.pathname),trailing:t.baseline===true?"baseline":void 0})),total:e.length,source:"live"}}if(s==="time-machine"&&r==="capture"&&n){return{id:id(),kind:"notice",tone:"info",text:`Saved a restore point${typeof o.name==="string"?` \u2014 "${o.name}"`:""}. The app can be brought back to this exact state any time.`}}if(s==="time-machine"&&r==="restore"&&n){return{id:id(),kind:"notice",tone:"warning",text:"Restored the snapshot \u2014 the app's state just jumped back. What's on screen now reflects the restore point, not what happened since."}}if(s==="jotai"&&r==="listAtoms"&&n){const e=arr(n.atoms).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${num(n.total)??e.length} atom${(num(n.total)??e.length)===1?"":"s"}`,items:e.map(t=>({id:str(t.label),title:str(t.label)??"atom",subtitle:t.writable===true?"writable":"read-only (derived)",trailing:num(t.changes)!==void 0?`${String(num(t.changes))} change${num(t.changes)===1?"":"s"}`:void 0})),total:num(n.total)??e.length,source:"live"}}if(s==="images"&&r==="list"&&n){const e=arr(n.records).map(rec).filter(Boolean);const t=e.map(i=>({url:str(i.uri)??"",alt:str(i.uri)?.split("/").pop()??"image",caption:i.ms!==void 0?`${String(i.ms)} ms \xB7 ${str(i.cache)??""}`:void 0})).filter(i=>i.url);if(t.length===0)return void 0;return{id:id(),kind:"imageGrid",title:"Images the app has loaded",images:t.slice(0,_types.CAPS.gridMax),total:num(n.total)??t.length,source:"live"}}if(s==="query"&&r==="setQueryData"&&n?.ok!==false){const e=rec(a.before)?.data??a.before;const t=a.after===void 0?void 0:rec(a.after)?.data??a.after;const i=t!==void 0?shallowDiff(e,t):patchDiff(e,o.data);if(i.length===0)return void 0;const d=Array.isArray(o.queryKey)?o.queryKey.filter(l=>typeof l==="string").join(" \u203A "):void 0;return{id:id(),kind:"diff",title:`${d||str(o.queryHash)||"query"} \u2014 what changed`,changes:i,source:"changed"}}if(s==="query"&&r==="listQueries"&&n){const e=arr(n.queries).map(rec).filter(Boolean);if(e.length===0)return void 0;return{id:id(),kind:"list",title:`${e.length} cached quer${e.length===1?"y":"ies"}`,items:e.map(t=>{const i=arr(t.queryKey).filter(l=>typeof l==="string").join(" \u203A ")||str(t.queryHash)||"query";const d=t.isStale===true;return{id:str(t.queryHash),title:i,subtitle:`${str(t.status)??""}${t.fetchStatus&&t.fetchStatus!=="idle"?` \xB7 ${String(t.fetchStatus)}`:""}`,trailing:d?"stale":"fresh",tone:d?"warning":"accent"}}),total:e.length,source:"live"}}if(s==="highlight-updates"&&r==="endMeasurement"&&n){const e=arr(n.components??n.renders).map(rec).filter(Boolean);const t=e.map(i=>({label:str(i.name)??str(i.componentName)??"?",value:num(i.renders)??num(i.count)??0})).filter(i=>i.value>0).sort((i,d)=>d.value-i.value).slice(0,_types.CAPS.chartBars);if(t.length===0)return void 0;return{id:id(),kind:"chart",title:"Renders during the measurement",bars:t.map(i=>({...i,unit:"renders"})),source:"live"}}return void 0}
|