@runcofactor/cli 0.10.0 → 0.15.0
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/dist/account-22LMP5PX.js +255 -0
- package/dist/account-22LMP5PX.js.map +1 -0
- package/dist/{api-catalog-GFGCLVS4.js → api-catalog-WTFQNLK6.js} +5 -4
- package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-WTFQNLK6.js.map} +1 -1
- package/dist/{attachment-SJC7D7FY.js → attachment-U3NKVPEY.js} +5 -4
- package/dist/{attachment-SJC7D7FY.js.map → attachment-U3NKVPEY.js.map} +1 -1
- package/dist/augmentor-5GMNAX4M.js +198 -0
- package/dist/augmentor-5GMNAX4M.js.map +1 -0
- package/dist/{auth-EN6NQCJY.js → auth-VQG2CDUD.js} +3 -2
- package/dist/{chunk-A4OFTUVV.js → chunk-3X3NW7QA.js} +7 -4
- package/dist/chunk-3X3NW7QA.js.map +1 -0
- package/dist/{chunk-N7HFALX3.js → chunk-46IUJXSD.js} +2 -2
- package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
- package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
- package/dist/{chunk-B2VISN2V.js → chunk-7VMO2WOZ.js} +238 -68
- package/dist/chunk-7VMO2WOZ.js.map +1 -0
- package/dist/{chunk-QFDOWABX.js → chunk-CMGI7UZD.js} +6 -3
- package/dist/chunk-CMGI7UZD.js.map +1 -0
- package/dist/{chunk-XQXVG7RA.js → chunk-FNPMWDUQ.js} +3 -3
- package/dist/chunk-G4UYGRZR.js +2 -0
- package/dist/{chunk-I4QLI3JO.js → chunk-HDFYUFBI.js} +2 -2
- package/dist/chunk-JMJVGMXI.js +92 -0
- package/dist/chunk-JMJVGMXI.js.map +1 -0
- package/dist/{chunk-SJJW77WN.js → chunk-JYGNMKZ5.js} +129 -38
- package/dist/chunk-JYGNMKZ5.js.map +1 -0
- package/dist/{chunk-52IO7ACJ.js → chunk-KCHKFYVX.js} +68 -10
- package/dist/{chunk-52IO7ACJ.js.map → chunk-KCHKFYVX.js.map} +1 -1
- package/dist/{chunk-ZD3KHESI.js → chunk-KRWRUU6R.js} +27 -7
- package/dist/chunk-KRWRUU6R.js.map +1 -0
- package/dist/{chunk-CLAWT7BJ.js → chunk-NHTEKEYE.js} +3 -3
- package/dist/{chunk-WOHL4FTB.js → chunk-PRBRSIZP.js} +16 -10
- package/dist/chunk-PRBRSIZP.js.map +1 -0
- package/dist/{chunk-GKY2KXO5.js → chunk-UETBLPKM.js} +154 -37
- package/dist/chunk-UETBLPKM.js.map +1 -0
- package/dist/{chunk-4PNOV7OV.js → chunk-VGYN2KU2.js} +9284 -1421
- package/dist/chunk-VGYN2KU2.js.map +1 -0
- package/dist/{chunk-Q4BO4Z5V.js → chunk-XJ5YYYGC.js} +4 -4
- package/dist/{credential-COLTBV6D.js → credential-E6KUHRGU.js} +24 -20
- package/dist/credential-E6KUHRGU.js.map +1 -0
- package/dist/{credential-template-EEPYFG7R.js → credential-template-4GPOPBGD.js} +30 -28
- package/dist/credential-template-4GPOPBGD.js.map +1 -0
- package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-XCLCY6S7.js} +5 -4
- package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-XCLCY6S7.js.map} +1 -1
- package/dist/{docs-3SSORHB5.js → docs-COCZYMW6.js} +95 -10
- package/dist/docs-COCZYMW6.js.map +1 -0
- package/dist/{doctor-OTSIWV6G.js → doctor-ERIVTCQS.js} +9 -5
- package/dist/doctor-ERIVTCQS.js.map +1 -0
- package/dist/environment-7Y4BV6XY.js +221 -0
- package/dist/environment-7Y4BV6XY.js.map +1 -0
- package/dist/{feature-flag-T2LRZME3.js → feature-flag-MHONEX6T.js} +164 -16
- package/dist/feature-flag-MHONEX6T.js.map +1 -0
- package/dist/index.js +123 -58
- package/dist/index.js.map +1 -1
- package/dist/{init-TGWRECT2.js → init-OROOM4HF.js} +5 -4
- package/dist/{init-TGWRECT2.js.map → init-OROOM4HF.js.map} +1 -1
- package/dist/{install-DXSNWZKO.js → install-OJFXHY6U.js} +7 -7
- package/dist/{issues-XIZOPX6T.js → issues-32GONR6C.js} +5 -4
- package/dist/{issues-XIZOPX6T.js.map → issues-32GONR6C.js.map} +1 -1
- package/dist/{knobs-NBINZIUB.js → knobs-UQGGY3CP.js} +7 -5
- package/dist/knobs-UQGGY3CP.js.map +1 -0
- package/dist/{list-3TDHK637.js → list-ZMIKEC5P.js} +5 -4
- package/dist/{list-3TDHK637.js.map → list-ZMIKEC5P.js.map} +1 -1
- package/dist/{local-HICVD6PU.js → local-PUD24GGQ.js} +7 -6
- package/dist/{local-HICVD6PU.js.map → local-PUD24GGQ.js.map} +1 -1
- package/dist/{login-UOIUXVUK.js → login-BSO7YX2D.js} +5 -4
- package/dist/{login-UOIUXVUK.js.map → login-BSO7YX2D.js.map} +1 -1
- package/dist/{mcp-54IJEOUA.js → mcp-4LS7OJUT.js} +24 -25
- package/dist/mcp-4LS7OJUT.js.map +1 -0
- package/dist/{pdf-extract-ABWV3L5X.js → pdf-extract-YHE54XU4.js} +2 -2
- package/dist/product-3JKSJCBC.js +346 -0
- package/dist/product-3JKSJCBC.js.map +1 -0
- package/dist/{property-UXLOLPTD.js → property-K5DBL4UE.js} +76 -4
- package/dist/property-K5DBL4UE.js.map +1 -0
- package/dist/{psql-BYUUNLGG.js → psql-PQFUFTSE.js} +3 -2
- package/dist/{psql-BYUUNLGG.js.map → psql-PQFUFTSE.js.map} +1 -1
- package/dist/{record-4TR2UIIS.js → record-EIIYLFJ4.js} +16 -17
- package/dist/record-EIIYLFJ4.js.map +1 -0
- package/dist/{redis-35VBUYW7.js → redis-BU36EZZI.js} +3 -2
- package/dist/redis-BU36EZZI.js.map +1 -0
- package/dist/{release-OOLTUBUT.js → release-LYC2P7DZ.js} +5 -4
- package/dist/{release-OOLTUBUT.js.map → release-LYC2P7DZ.js.map} +1 -1
- package/dist/{sandbox-XGSUU6G7.js → sandbox-CKHXXZHU.js} +55 -10
- package/dist/sandbox-CKHXXZHU.js.map +1 -0
- package/dist/{scenarios-GWLJEGHA.js → scenarios-2XJ2G3KR.js} +5 -4
- package/dist/{scenarios-GWLJEGHA.js.map → scenarios-2XJ2G3KR.js.map} +1 -1
- package/dist/{session-MYFNZPRP.js → session-UTOD5ZYH.js} +77 -10
- package/dist/session-UTOD5ZYH.js.map +1 -0
- package/dist/{skill-KM34LDMO.js → skill-GMAERMQL.js} +220 -70
- package/dist/skill-GMAERMQL.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +40 -23
- package/dist/skills/debug-run/history/1.2.0.md +262 -0
- package/dist/skills/debug-run/history/1.3.0.md +273 -0
- package/dist/skills/debug-run/history/1.3.1.md +279 -0
- package/dist/skills/debug-run/version.json +1 -1
- package/dist/skills/optimize-cache/SKILL.md +233 -0
- package/dist/{spec-RA3CFEKG.js → spec-2NP36U3Y.js} +54 -25
- package/dist/spec-2NP36U3Y.js.map +1 -0
- package/dist/{src-LB4HBTJQ.js → src-4T6GHXVO.js} +19 -11
- package/dist/src-4T6GHXVO.js.map +1 -0
- package/dist/{src-N3TPKGTY.js → src-CG52HEE7.js} +10 -7
- package/dist/{src-UVDX5UP6.js → src-PK5OZXCM.js} +4 -2
- package/dist/src-UEOOC425.js +5941 -0
- package/dist/src-UEOOC425.js.map +1 -0
- package/dist/{start-PSEQSQA3.js → start-3C7EEFW2.js} +5 -4
- package/dist/{start-PSEQSQA3.js.map → start-3C7EEFW2.js.map} +1 -1
- package/dist/{tag-YY6YAABQ.js → tag-JSHOMLBR.js} +6 -5
- package/dist/{task-IQC3YR5K.js → task-YGWVYTBJ.js} +5 -4
- package/dist/{task-IQC3YR5K.js.map → task-YGWVYTBJ.js.map} +1 -1
- package/dist/{test-suite-DZNJFXDW.js → test-suite-LI7ZH367.js} +12 -7
- package/dist/{test-suite-DZNJFXDW.js.map → test-suite-LI7ZH367.js.map} +1 -1
- package/dist/trace-UMLOSIIR.js +16 -0
- package/dist/{triage-ORG45R5W.js → triage-ZWJG4NBJ.js} +6 -5
- package/dist/triage-ZWJG4NBJ.js.map +1 -0
- package/dist/{upgrade-L2VYMP2V.js → upgrade-S7CGEK6H.js} +3 -3
- package/dist/{workflow-XYFH5LTP.js → workflow-T4HSAOGW.js} +316 -241
- package/dist/workflow-T4HSAOGW.js.map +1 -0
- package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-XVEZ36KF.js} +140 -7
- package/dist/workflow-cache-XVEZ36KF.js.map +1 -0
- package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-T2BULXZ7.js} +9 -8
- package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-T2BULXZ7.js.map} +1 -1
- package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-XXEZICN2.js} +5 -6
- package/dist/workflow-proposals-XXEZICN2.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-4PNOV7OV.js.map +0 -1
- package/dist/chunk-A4OFTUVV.js.map +0 -1
- package/dist/chunk-B2VISN2V.js.map +0 -1
- package/dist/chunk-GKY2KXO5.js.map +0 -1
- package/dist/chunk-JICSYFRJ.js +0 -46
- package/dist/chunk-JICSYFRJ.js.map +0 -1
- package/dist/chunk-QFDOWABX.js.map +0 -1
- package/dist/chunk-SJJW77WN.js.map +0 -1
- package/dist/chunk-WOHL4FTB.js.map +0 -1
- package/dist/chunk-ZD3KHESI.js.map +0 -1
- package/dist/credential-COLTBV6D.js.map +0 -1
- package/dist/credential-template-EEPYFG7R.js.map +0 -1
- package/dist/docs-3SSORHB5.js.map +0 -1
- package/dist/doctor-OTSIWV6G.js.map +0 -1
- package/dist/feature-flag-T2LRZME3.js.map +0 -1
- package/dist/knobs-NBINZIUB.js.map +0 -1
- package/dist/mcp-54IJEOUA.js.map +0 -1
- package/dist/property-UXLOLPTD.js.map +0 -1
- package/dist/record-4TR2UIIS.js.map +0 -1
- package/dist/redis-35VBUYW7.js.map +0 -1
- package/dist/sandbox-XGSUU6G7.js.map +0 -1
- package/dist/session-MYFNZPRP.js.map +0 -1
- package/dist/skill-KM34LDMO.js.map +0 -1
- package/dist/spec-RA3CFEKG.js.map +0 -1
- package/dist/src-LB4HBTJQ.js.map +0 -1
- package/dist/trace-HXIW7U2D.js +0 -14
- package/dist/workflow-XYFH5LTP.js.map +0 -1
- package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
- package/dist/workflow-proposals-MTUDX46O.js.map +0 -1
- /package/dist/{auth-EN6NQCJY.js.map → auth-VQG2CDUD.js.map} +0 -0
- /package/dist/{chunk-N7HFALX3.js.map → chunk-46IUJXSD.js.map} +0 -0
- /package/dist/{chunk-XQXVG7RA.js.map → chunk-FNPMWDUQ.js.map} +0 -0
- /package/dist/{pdf-extract-ABWV3L5X.js.map → chunk-G4UYGRZR.js.map} +0 -0
- /package/dist/{chunk-I4QLI3JO.js.map → chunk-HDFYUFBI.js.map} +0 -0
- /package/dist/{chunk-CLAWT7BJ.js.map → chunk-NHTEKEYE.js.map} +0 -0
- /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-XJ5YYYGC.js.map} +0 -0
- /package/dist/{install-DXSNWZKO.js.map → install-OJFXHY6U.js.map} +0 -0
- /package/dist/{src-N3TPKGTY.js.map → pdf-extract-YHE54XU4.js.map} +0 -0
- /package/dist/{src-UVDX5UP6.js.map → src-CG52HEE7.js.map} +0 -0
- /package/dist/{tag-YY6YAABQ.js.map → src-PK5OZXCM.js.map} +0 -0
- /package/dist/{trace-HXIW7U2D.js.map → tag-JSHOMLBR.js.map} +0 -0
- /package/dist/{triage-ORG45R5W.js.map → trace-UMLOSIIR.js.map} +0 -0
- /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-S7CGEK6H.js.map} +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
apiRequest,
|
|
4
|
+
getApplicationArg
|
|
5
|
+
} from "./chunk-PRBRSIZP.js";
|
|
6
|
+
import "./chunk-3CZU7XCS.js";
|
|
7
|
+
import {
|
|
8
|
+
getArgValue
|
|
9
|
+
} from "./chunk-3X3NW7QA.js";
|
|
10
|
+
import "./chunk-JMJVGMXI.js";
|
|
11
|
+
import "./chunk-JWHYXEA2.js";
|
|
12
|
+
import "./chunk-JYGNMKZ5.js";
|
|
13
|
+
import "./chunk-VKVL7WBN.js";
|
|
14
|
+
|
|
15
|
+
// src/augmentor.ts
|
|
16
|
+
import process from "process";
|
|
17
|
+
function fail(message) {
|
|
18
|
+
console.error(`Error: ${message}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
function requireApplication(argv) {
|
|
22
|
+
const id = getApplicationArg(argv);
|
|
23
|
+
if (!id) fail("--application <propertyId> is required");
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
function requireId(argv) {
|
|
27
|
+
const id = getArgValue(argv, "--id");
|
|
28
|
+
if (!id) fail("--id <augmentorId> is required");
|
|
29
|
+
return id;
|
|
30
|
+
}
|
|
31
|
+
function printGuide() {
|
|
32
|
+
console.log(`canary augmentor \u2014 manage a property's custom augmentors
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
canary augmentor list --application <propertyId> [--status active|disabled|stale|needs_reauthoring] [--format table|json]
|
|
36
|
+
canary augmentor get --id <augmentorId> [--format table|json]
|
|
37
|
+
canary augmentor stats --application <propertyId> [--format table|json]
|
|
38
|
+
canary augmentor signals --application <propertyId> [--format table|json] # pending authoring queue
|
|
39
|
+
canary augmentor disable --id <augmentorId> [--reason "<text>"] # operator kill-switch
|
|
40
|
+
canary augmentor enable --id <augmentorId> # re-enable
|
|
41
|
+
|
|
42
|
+
Authoring is automatic: a ref-less coordinate click in a run queues a detection
|
|
43
|
+
signal that an offline agent turns into a verified augmentor. Use this CLI to
|
|
44
|
+
audit health and flip the operator kill-switch.`);
|
|
45
|
+
}
|
|
46
|
+
async function handleList(argv, apiUrl, token) {
|
|
47
|
+
const propertyId = requireApplication(argv);
|
|
48
|
+
const status = getArgValue(argv, "--status");
|
|
49
|
+
const json = getArgValue(argv, "--format") === "json";
|
|
50
|
+
const qs = status ? `?status=${encodeURIComponent(status)}` : "";
|
|
51
|
+
const res = await apiRequest(
|
|
52
|
+
apiUrl,
|
|
53
|
+
token,
|
|
54
|
+
"GET",
|
|
55
|
+
`/org/properties/${propertyId}/augmentors${qs}`
|
|
56
|
+
);
|
|
57
|
+
if (!res.ok) fail(res.error ?? "Unknown error");
|
|
58
|
+
const augmentors = res.data?.augmentors ?? [];
|
|
59
|
+
if (json) {
|
|
60
|
+
console.log(JSON.stringify(augmentors, null, 2));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (augmentors.length === 0) {
|
|
64
|
+
console.log("No augmentors for this property.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
for (const a of augmentors) {
|
|
68
|
+
const health = a.enabled ? a.status : "disabled(operator)";
|
|
69
|
+
console.log(
|
|
70
|
+
`${a.id} [${health}] ${a.urlNorm}
|
|
71
|
+
selector: ${a.matchSelector}
|
|
72
|
+
triggers: ${a.triggerMatchCount} interactions: ${a.interactionCount} misses: ${a.consecutiveMissCount} source: ${a.sourceType}` + (a.invalidationReason ? `
|
|
73
|
+
invalidated: ${a.invalidationReason}` : "")
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function handleGet(argv, apiUrl, token) {
|
|
78
|
+
const id = requireId(argv);
|
|
79
|
+
const res = await apiRequest(apiUrl, token, "GET", `/org/augmentors/${id}`);
|
|
80
|
+
if (!res.ok) fail(res.error ?? "Unknown error");
|
|
81
|
+
const a = res.data?.augmentor;
|
|
82
|
+
if (!a) fail("Augmentor not found");
|
|
83
|
+
console.log(JSON.stringify(a, null, 2));
|
|
84
|
+
}
|
|
85
|
+
async function handleStats(argv, apiUrl, token) {
|
|
86
|
+
const propertyId = requireApplication(argv);
|
|
87
|
+
const json = getArgValue(argv, "--format") === "json";
|
|
88
|
+
const res = await apiRequest(
|
|
89
|
+
apiUrl,
|
|
90
|
+
token,
|
|
91
|
+
"GET",
|
|
92
|
+
`/org/properties/${propertyId}/augmentors`
|
|
93
|
+
);
|
|
94
|
+
if (!res.ok) fail(res.error ?? "Unknown error");
|
|
95
|
+
const augmentors = res.data?.augmentors ?? [];
|
|
96
|
+
const byStatus = {};
|
|
97
|
+
let totalTriggers = 0;
|
|
98
|
+
let totalInteractions = 0;
|
|
99
|
+
for (const a of augmentors) {
|
|
100
|
+
const key = a.enabled ? a.status : "disabled";
|
|
101
|
+
byStatus[key] = (byStatus[key] ?? 0) + 1;
|
|
102
|
+
totalTriggers += a.triggerMatchCount;
|
|
103
|
+
totalInteractions += a.interactionCount;
|
|
104
|
+
}
|
|
105
|
+
const stats = {
|
|
106
|
+
total: augmentors.length,
|
|
107
|
+
byStatus,
|
|
108
|
+
totalTriggers,
|
|
109
|
+
totalInteractions,
|
|
110
|
+
// The payoff ratio: how often a trigger led to the agent using the healed element.
|
|
111
|
+
interactionRate: totalTriggers > 0 ? +(totalInteractions / totalTriggers).toFixed(3) : 0
|
|
112
|
+
};
|
|
113
|
+
if (json) {
|
|
114
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log(`Augmentors for property ${propertyId}:`);
|
|
118
|
+
console.log(` total: ${stats.total}`);
|
|
119
|
+
for (const [k, v] of Object.entries(byStatus)) console.log(` ${k}: ${v}`);
|
|
120
|
+
console.log(` triggers: ${totalTriggers} interactions: ${totalInteractions} rate: ${stats.interactionRate}`);
|
|
121
|
+
}
|
|
122
|
+
async function handleSignals(argv, apiUrl, token) {
|
|
123
|
+
const propertyId = requireApplication(argv);
|
|
124
|
+
const json = getArgValue(argv, "--format") === "json";
|
|
125
|
+
const res = await apiRequest(
|
|
126
|
+
apiUrl,
|
|
127
|
+
token,
|
|
128
|
+
"GET",
|
|
129
|
+
`/org/properties/${propertyId}/augmentors/signals`
|
|
130
|
+
);
|
|
131
|
+
if (!res.ok) fail(res.error ?? "Unknown error");
|
|
132
|
+
const signals = res.data?.signals ?? [];
|
|
133
|
+
if (json) {
|
|
134
|
+
console.log(JSON.stringify(signals, null, 2));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (signals.length === 0) {
|
|
138
|
+
console.log("No pending detection signals (authoring queue is empty).");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
for (const s of signals) {
|
|
142
|
+
const coord = s.viewportX !== null && s.viewportY !== null ? `(${s.viewportX}, ${s.viewportY})` : "(no coord)";
|
|
143
|
+
console.log(`${s.id} [${s.source} ${coord}] ${s.urlNorm} \u2014 ${s.status}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function handleDisable(argv, apiUrl, token) {
|
|
147
|
+
const id = requireId(argv);
|
|
148
|
+
const reason = getArgValue(argv, "--reason");
|
|
149
|
+
const res = await apiRequest(
|
|
150
|
+
apiUrl,
|
|
151
|
+
token,
|
|
152
|
+
"POST",
|
|
153
|
+
`/org/augmentors/${id}/disable`,
|
|
154
|
+
reason ? { reason } : void 0
|
|
155
|
+
);
|
|
156
|
+
if (!res.ok) fail(res.error ?? "Unknown error");
|
|
157
|
+
console.log(`Disabled augmentor ${id}.`);
|
|
158
|
+
}
|
|
159
|
+
async function handleEnable(argv, apiUrl, token) {
|
|
160
|
+
const id = requireId(argv);
|
|
161
|
+
const res = await apiRequest(apiUrl, token, "POST", `/org/augmentors/${id}/enable`);
|
|
162
|
+
if (!res.ok) fail(res.error ?? "Unknown error");
|
|
163
|
+
console.log(`Enabled augmentor ${id}.`);
|
|
164
|
+
}
|
|
165
|
+
async function runAugmentor(argv, apiUrl, token) {
|
|
166
|
+
const [action, ...rest] = argv;
|
|
167
|
+
switch (action) {
|
|
168
|
+
case void 0:
|
|
169
|
+
case "help":
|
|
170
|
+
case "--help":
|
|
171
|
+
printGuide();
|
|
172
|
+
return;
|
|
173
|
+
case "list":
|
|
174
|
+
await handleList(rest, apiUrl, token);
|
|
175
|
+
return;
|
|
176
|
+
case "get":
|
|
177
|
+
await handleGet(rest, apiUrl, token);
|
|
178
|
+
return;
|
|
179
|
+
case "stats":
|
|
180
|
+
await handleStats(rest, apiUrl, token);
|
|
181
|
+
return;
|
|
182
|
+
case "signals":
|
|
183
|
+
await handleSignals(rest, apiUrl, token);
|
|
184
|
+
return;
|
|
185
|
+
case "disable":
|
|
186
|
+
await handleDisable(rest, apiUrl, token);
|
|
187
|
+
return;
|
|
188
|
+
case "enable":
|
|
189
|
+
await handleEnable(rest, apiUrl, token);
|
|
190
|
+
return;
|
|
191
|
+
default:
|
|
192
|
+
fail(`Unknown augmentor command: ${action}. Run \`canary augmentor help\`.`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
export {
|
|
196
|
+
runAugmentor
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=augmentor-5GMNAX4M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/augmentor.ts"],"sourcesContent":["/**\n * CLI: `canary augmentor` — inspect & manage a property's custom augmentors\n *\n * Custom augmentors are persisted, customer-scoped collectors the cloud browser\n * agent runs before ariaSnapshot to heal semantic-snapshot blind spots (e.g. a\n * calendar of role-less <div> slots). They are authored AUTONOMOUSLY (a ref-less\n * coordinate click queues a detection signal → an offline authoring agent →\n * server-validated commit), so this CLI manages what that pipeline produces:\n * list/get, the operator enable/disable kill-switch, stats, and the pending\n * detection-signal queue.\n *\n * Thin client over the org-scoped /org/.../augmentors endpoints. Interactive and\n * flag-driven (every action has flags so agents can drive it from bash).\n */\n\nimport process from 'node:process';\nimport { getArgValue } from './auth.js';\nimport { apiRequest, getApplicationArg } from './cli-helpers.js';\n\ninterface Augmentor {\n id: string;\n propertyId: string;\n urlNorm: string;\n matchSelector: string;\n status: 'active' | 'disabled' | 'stale' | 'needs_reauthoring';\n enabled: boolean;\n sourceType: string;\n expectedMinCount: number;\n triggerMatchCount: number;\n consecutiveMissCount: number;\n interactionCount: number;\n lastTriggeredAt: string | null;\n lastInteractedAt: string | null;\n invalidationReason: string | null;\n createdAt: string;\n}\n\ninterface DetectionSignal {\n id: string;\n urlNorm: string;\n source: string;\n viewportX: number | null;\n viewportY: number | null;\n status: string;\n createdAt: string;\n}\n\ntype ListResponse = { ok: boolean; error?: string; data?: { augmentors: Augmentor[] } };\ntype GetResponse = { ok: boolean; error?: string; data?: { augmentor: Augmentor } };\ntype SignalsResponse = { ok: boolean; error?: string; data?: { signals: DetectionSignal[] } };\ntype MutateResponse = { ok: boolean; error?: string };\n\nfunction fail(message: string): never {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\nfunction requireApplication(argv: string[]): string {\n const id = getApplicationArg(argv);\n if (!id) fail('--application <propertyId> is required');\n return id!;\n}\n\nfunction requireId(argv: string[]): string {\n const id = getArgValue(argv, '--id');\n if (!id) fail('--id <augmentorId> is required');\n return id!;\n}\n\nfunction printGuide(): void {\n console.log(`canary augmentor — manage a property's custom augmentors\n\nUsage:\n canary augmentor list --application <propertyId> [--status active|disabled|stale|needs_reauthoring] [--format table|json]\n canary augmentor get --id <augmentorId> [--format table|json]\n canary augmentor stats --application <propertyId> [--format table|json]\n canary augmentor signals --application <propertyId> [--format table|json] # pending authoring queue\n canary augmentor disable --id <augmentorId> [--reason \"<text>\"] # operator kill-switch\n canary augmentor enable --id <augmentorId> # re-enable\n\nAuthoring is automatic: a ref-less coordinate click in a run queues a detection\nsignal that an offline agent turns into a verified augmentor. Use this CLI to\naudit health and flip the operator kill-switch.`);\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = requireApplication(argv);\n const status = getArgValue(argv, '--status');\n const json = getArgValue(argv, '--format') === 'json';\n const qs = status ? `?status=${encodeURIComponent(status)}` : '';\n const res = await apiRequest<ListResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${propertyId}/augmentors${qs}`\n );\n if (!res.ok) fail(res.error ?? 'Unknown error');\n const augmentors = res.data?.augmentors ?? [];\n if (json) {\n console.log(JSON.stringify(augmentors, null, 2));\n return;\n }\n if (augmentors.length === 0) {\n console.log('No augmentors for this property.');\n return;\n }\n for (const a of augmentors) {\n const health = a.enabled ? a.status : 'disabled(operator)';\n console.log(\n `${a.id} [${health}] ${a.urlNorm}\\n` +\n ` selector: ${a.matchSelector}\\n` +\n ` triggers: ${a.triggerMatchCount} interactions: ${a.interactionCount} ` +\n `misses: ${a.consecutiveMissCount} source: ${a.sourceType}` +\n (a.invalidationReason ? `\\n invalidated: ${a.invalidationReason}` : '')\n );\n }\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const id = requireId(argv);\n const res = await apiRequest<GetResponse>(apiUrl, token, 'GET', `/org/augmentors/${id}`);\n if (!res.ok) fail(res.error ?? 'Unknown error');\n const a = res.data?.augmentor;\n if (!a) fail('Augmentor not found');\n // A single augmentor is detail-dense — always pretty-print the full record.\n console.log(JSON.stringify(a, null, 2));\n}\n\nasync function handleStats(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = requireApplication(argv);\n const json = getArgValue(argv, '--format') === 'json';\n const res = await apiRequest<ListResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${propertyId}/augmentors`\n );\n if (!res.ok) fail(res.error ?? 'Unknown error');\n const augmentors = res.data?.augmentors ?? [];\n const byStatus: Record<string, number> = {};\n let totalTriggers = 0;\n let totalInteractions = 0;\n for (const a of augmentors) {\n const key = a.enabled ? a.status : 'disabled';\n byStatus[key] = (byStatus[key] ?? 0) + 1;\n totalTriggers += a.triggerMatchCount;\n totalInteractions += a.interactionCount;\n }\n const stats = {\n total: augmentors.length,\n byStatus,\n totalTriggers,\n totalInteractions,\n // The payoff ratio: how often a trigger led to the agent using the healed element.\n interactionRate: totalTriggers > 0 ? +(totalInteractions / totalTriggers).toFixed(3) : 0,\n };\n if (json) {\n console.log(JSON.stringify(stats, null, 2));\n return;\n }\n console.log(`Augmentors for property ${propertyId}:`);\n console.log(` total: ${stats.total}`);\n for (const [k, v] of Object.entries(byStatus)) console.log(` ${k}: ${v}`);\n console.log(` triggers: ${totalTriggers} interactions: ${totalInteractions} rate: ${stats.interactionRate}`);\n}\n\nasync function handleSignals(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = requireApplication(argv);\n const json = getArgValue(argv, '--format') === 'json';\n const res = await apiRequest<SignalsResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${propertyId}/augmentors/signals`\n );\n if (!res.ok) fail(res.error ?? 'Unknown error');\n const signals = res.data?.signals ?? [];\n if (json) {\n console.log(JSON.stringify(signals, null, 2));\n return;\n }\n if (signals.length === 0) {\n console.log('No pending detection signals (authoring queue is empty).');\n return;\n }\n for (const s of signals) {\n const coord = s.viewportX !== null && s.viewportY !== null ? `(${s.viewportX}, ${s.viewportY})` : '(no coord)';\n console.log(`${s.id} [${s.source} ${coord}] ${s.urlNorm} — ${s.status}`);\n }\n}\n\nasync function handleDisable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const id = requireId(argv);\n const reason = getArgValue(argv, '--reason');\n const res = await apiRequest<MutateResponse>(\n apiUrl,\n token,\n 'POST',\n `/org/augmentors/${id}/disable`,\n reason ? { reason } : undefined\n );\n if (!res.ok) fail(res.error ?? 'Unknown error');\n console.log(`Disabled augmentor ${id}.`);\n}\n\nasync function handleEnable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const id = requireId(argv);\n const res = await apiRequest<MutateResponse>(apiUrl, token, 'POST', `/org/augmentors/${id}/enable`);\n if (!res.ok) fail(res.error ?? 'Unknown error');\n console.log(`Enabled augmentor ${id}.`);\n}\n\nexport async function runAugmentor(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [action, ...rest] = argv;\n switch (action) {\n case undefined:\n case 'help':\n case '--help':\n printGuide();\n return;\n case 'list':\n await handleList(rest, apiUrl, token);\n return;\n case 'get':\n await handleGet(rest, apiUrl, token);\n return;\n case 'stats':\n await handleStats(rest, apiUrl, token);\n return;\n case 'signals':\n await handleSignals(rest, apiUrl, token);\n return;\n case 'disable':\n await handleDisable(rest, apiUrl, token);\n return;\n case 'enable':\n await handleEnable(rest, apiUrl, token);\n return;\n default:\n fail(`Unknown augmentor command: ${action}. Run \\`canary augmentor help\\`.`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
resolveConfig,
|
|
13
13
|
resolveOptionalConfig,
|
|
14
14
|
saveAuth
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-3X3NW7QA.js";
|
|
16
|
+
import "./chunk-JMJVGMXI.js";
|
|
16
17
|
import "./chunk-VKVL7WBN.js";
|
|
17
18
|
export {
|
|
18
19
|
ENV_URLS,
|
|
@@ -28,4 +29,4 @@ export {
|
|
|
28
29
|
resolveOptionalConfig,
|
|
29
30
|
saveAuth
|
|
30
31
|
};
|
|
31
|
-
//# sourceMappingURL=auth-
|
|
32
|
+
//# sourceMappingURL=auth-VQG2CDUD.js.map
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
readBrandEnv
|
|
4
|
+
} from "./chunk-JMJVGMXI.js";
|
|
2
5
|
|
|
3
6
|
// src/auth.ts
|
|
4
7
|
import fs from "fs/promises";
|
|
@@ -124,20 +127,20 @@ async function resolveConfigInternal(argv, opts) {
|
|
|
124
127
|
return null;
|
|
125
128
|
}
|
|
126
129
|
const storedApiUrl = await readStoredApiUrl(profile);
|
|
127
|
-
const apiUrl = getArgValue(argv, "--api-url") ?? (env ? ENV_URLS[env].api : void 0) ??
|
|
130
|
+
const apiUrl = getArgValue(argv, "--api-url") ?? (env ? ENV_URLS[env].api : void 0) ?? readBrandEnv("CANARY_API_URL") ?? storedApiUrl ?? "https://api.trycanary.ai";
|
|
128
131
|
const explicitToken = getArgValue(argv, "--token");
|
|
129
132
|
let token;
|
|
130
133
|
if (explicitToken) {
|
|
131
134
|
token = explicitToken;
|
|
132
135
|
} else if (profile) {
|
|
133
|
-
token = await readStoredToken(profile) ??
|
|
136
|
+
token = await readStoredToken(profile) ?? readBrandEnv("CANARY_API_TOKEN") ?? null;
|
|
134
137
|
if (!token && opts.required) {
|
|
135
138
|
console.error(`Error: No token found for "${env}" profile.`);
|
|
136
139
|
console.error(`Run: canary login --env ${env}`);
|
|
137
140
|
process.exit(1);
|
|
138
141
|
}
|
|
139
142
|
} else {
|
|
140
|
-
token =
|
|
143
|
+
token = readBrandEnv("CANARY_API_TOKEN") ?? await readStoredToken() ?? null;
|
|
141
144
|
if (!token && opts.required) {
|
|
142
145
|
console.error("Error: No API token found.");
|
|
143
146
|
console.error("Run: canary login");
|
|
@@ -167,4 +170,4 @@ export {
|
|
|
167
170
|
resolveConfig,
|
|
168
171
|
resolveOptionalConfig
|
|
169
172
|
};
|
|
170
|
-
//# sourceMappingURL=chunk-
|
|
173
|
+
//# sourceMappingURL=chunk-3X3NW7QA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { readBrandEnv } from './brand.js';\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\n/** Multi-profile auth file format */\ntype MultiProfileAuth = {\n profiles: Record<string, StoredAuth>;\n default: string;\n};\n\n/** Legacy flat format (single profile) */\ntype LegacyAuth = StoredAuth;\n\nconst AUTH_DIR = path.join(os.homedir(), '.config', 'canary-cli');\nconst AUTH_FILE = path.join(AUTH_DIR, 'auth.json');\n\n/* ── Shared argv helpers ─────────────────────────────────────────────── */\n\nexport function getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nexport function hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\n/* ── Environment / profile mapping ───────────────────────────────────── */\n\n/** Map --env values to profile names for multi-profile auth storage */\nexport function envToProfile(env: string): string {\n if (env === 'prod' || env === 'production') return 'production';\n if (env === 'dev') return 'dev';\n if (env === 'local') return 'local';\n return env;\n}\n\nconst VALID_ENVS = ['prod', 'dev', 'local'];\n\n/** Environment URL mappings shared across CLI commands */\nexport const ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: 'https://api.trycanary.ai',\n app: 'https://app.trycanary.ai',\n },\n production: {\n api: 'https://api.trycanary.ai',\n app: 'https://app.trycanary.ai',\n },\n dev: {\n api: 'https://api.dev.trycanary.ai',\n app: 'https://app.dev.trycanary.ai',\n },\n local: {\n api: 'http://localhost:3000',\n app: 'http://localhost:5173',\n },\n};\n\n/** Derive the web-app URL that corresponds to a given API URL. */\nexport function resolveAppUrl(apiUrl: string): string {\n for (const env of Object.values(ENV_URLS)) {\n if (env.api === apiUrl) return env.app;\n }\n // Fallback for custom API URLs: replace \"api.\" at protocol boundary or \"/api\" path\n return apiUrl.replace(/(:\\/\\/)api\\./, '$1app.').replace(/:3000\\b/, ':5173');\n}\n\nfunction isMultiProfile(data: unknown): data is MultiProfileAuth {\n return typeof data === 'object' && data !== null && 'profiles' in data;\n}\n\n/** Read the raw auth file and normalize to multi-profile format */\nasync function readAuthFile(): Promise<MultiProfileAuth | null> {\n try {\n const content = await fs.readFile(AUTH_FILE, 'utf8');\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n return data;\n }\n\n // Legacy flat format — treat as the \"default\" profile\n const legacy = data as LegacyAuth;\n return {\n profiles: { default: legacy },\n default: 'default',\n };\n } catch {\n return null;\n }\n}\n\nexport async function readStoredAuth(profile?: string): Promise<StoredAuth | null> {\n const auth = await readAuthFile();\n if (!auth) return null;\n\n const key = profile ?? auth.default;\n return auth.profiles[key] ?? null;\n}\n\nexport async function readStoredToken(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.apiUrl ?? null;\n}\n\n/** Return all stored tokens across every profile */\nexport async function readAllStoredTokens(): Promise<string[]> {\n const auth = await readAuthFile();\n if (!auth) return [];\n return Object.values(auth.profiles)\n .map((p) => p.token)\n .filter((t): t is string => !!t);\n}\n\nexport async function saveAuth(auth: StoredAuth, profile?: string): Promise<string> {\n await fs.mkdir(AUTH_DIR, { recursive: true, mode: 0o700 });\n\n // Read existing file (may be legacy or multi-profile)\n let existing: MultiProfileAuth;\n try {\n const content = await fs.readFile(AUTH_FILE, 'utf8');\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n existing = data;\n } else {\n // Migrate legacy flat format into profiles.default\n existing = {\n profiles: { default: data as LegacyAuth },\n default: 'default',\n };\n }\n } catch {\n existing = { profiles: {}, default: 'default' };\n }\n\n const key = profile ?? 'default';\n existing.profiles[key] = auth;\n\n // Set default to this profile if no default exists yet\n if (!existing.profiles[existing.default]) {\n existing.default = key;\n }\n\n await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), {\n encoding: 'utf8',\n mode: 0o600,\n });\n return AUTH_FILE;\n}\n\n/* ── Shared config resolution ────────────────────────────────────────── */\n\ntype ResolvedConfig = { apiUrl: string; token: string };\n\nasync function resolveConfigInternal(\n argv: string[],\n opts: { required: boolean }\n): Promise<ResolvedConfig | null> {\n const env = getArgValue(argv, '--env');\n const profile = env ? envToProfile(env) : undefined;\n\n if (env && !ENV_URLS[env]) {\n if (opts.required) {\n console.error(`Unknown environment: ${env}`);\n console.error(`Valid environments: ${VALID_ENVS.join(', ')}`);\n process.exit(1);\n }\n return null;\n }\n\n const storedApiUrl = await readStoredApiUrl(profile);\n const apiUrl =\n getArgValue(argv, '--api-url') ??\n (env ? ENV_URLS[env].api : undefined) ??\n readBrandEnv('CANARY_API_URL') ??\n storedApiUrl ??\n 'https://api.trycanary.ai';\n\n const explicitToken = getArgValue(argv, '--token');\n\n let token: string | null;\n if (explicitToken) {\n token = explicitToken;\n } else if (profile) {\n token = (await readStoredToken(profile)) ?? readBrandEnv('CANARY_API_TOKEN') ?? null;\n if (!token && opts.required) {\n console.error(`Error: No token found for \"${env}\" profile.`);\n console.error(`Run: canary login --env ${env}`);\n process.exit(1);\n }\n } else {\n token = readBrandEnv('CANARY_API_TOKEN') ?? (await readStoredToken()) ?? null;\n if (!token && opts.required) {\n console.error('Error: No API token found.');\n console.error('Run: canary login');\n process.exit(1);\n }\n }\n\n return token ? { apiUrl, token } : null;\n}\n\n/**\n * Resolve API URL and auth token from argv flags, env vars, and stored profiles.\n * Used by knobs, feature-flag, release, and debug-session commands.\n */\nexport async function resolveConfig(argv: string[]): Promise<ResolvedConfig> {\n return (await resolveConfigInternal(argv, { required: true }))!;\n}\n\nexport async function resolveOptionalConfig(argv: string[]): Promise<ResolvedConfig | null> {\n return resolveConfigInternal(argv, { required: false });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
packageName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JMJVGMXI.js";
|
|
5
5
|
|
|
6
6
|
// src/update-check.ts
|
|
7
7
|
import fs from "fs/promises";
|
|
@@ -85,4 +85,4 @@ export {
|
|
|
85
85
|
checkForUpdate,
|
|
86
86
|
clearUpdateCache
|
|
87
87
|
};
|
|
88
|
-
//# sourceMappingURL=chunk-
|
|
88
|
+
//# sourceMappingURL=chunk-46IUJXSD.js.map
|
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
|
|
3
|
+
// ../recording/src/locator-string.ts
|
|
4
|
+
function buildLocatorString(selectors) {
|
|
5
|
+
const parts = [];
|
|
6
|
+
if (selectors.testId) {
|
|
7
|
+
parts.push(`getByTestId(${JSON.stringify(selectors.testId)})`);
|
|
8
|
+
}
|
|
9
|
+
if (selectors.role) {
|
|
10
|
+
const { role, name, exact } = selectors.role;
|
|
11
|
+
if (name) {
|
|
12
|
+
const options = exact ? `{ name: ${JSON.stringify(name)}, exact: true }` : `{ name: ${JSON.stringify(name)} }`;
|
|
13
|
+
parts.push(`getByRole('${role}', ${options})`);
|
|
14
|
+
} else {
|
|
15
|
+
parts.push(`getByRole('${role}')`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (selectors.ariaLabel) {
|
|
19
|
+
parts.push(`getByLabel(${JSON.stringify(selectors.ariaLabel)})`);
|
|
20
|
+
}
|
|
21
|
+
if (selectors.placeholder) {
|
|
22
|
+
parts.push(`getByPlaceholder(${JSON.stringify(selectors.placeholder)})`);
|
|
23
|
+
}
|
|
24
|
+
if (selectors.text) {
|
|
25
|
+
const { text, exact } = selectors.text;
|
|
26
|
+
if (exact) {
|
|
27
|
+
parts.push(`getByText(${JSON.stringify(text)}, { exact: true })`);
|
|
28
|
+
} else {
|
|
29
|
+
parts.push(`getByText(${JSON.stringify(text)})`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (selectors.css) {
|
|
33
|
+
parts.push(`locator(${JSON.stringify(selectors.css)})`);
|
|
34
|
+
}
|
|
35
|
+
return parts.length > 0 ? parts[0] : 'locator("*")';
|
|
36
|
+
}
|
|
37
|
+
|
|
3
38
|
// ../recording/src/capture-script.ts
|
|
4
39
|
var INTERACTION_CAPTURE_SCRIPT = `
|
|
5
40
|
(function() {
|
|
@@ -175,6 +210,7 @@ var INTERACTION_CAPTURE_SCRIPT = `
|
|
|
175
210
|
`;
|
|
176
211
|
|
|
177
212
|
export {
|
|
213
|
+
buildLocatorString,
|
|
178
214
|
INTERACTION_CAPTURE_SCRIPT
|
|
179
215
|
};
|
|
180
|
-
//# sourceMappingURL=chunk-
|
|
216
|
+
//# sourceMappingURL=chunk-5GD47G2D.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../recording/src/capture-script.ts"],"sourcesContent":["/**\n * In-page interaction capture script injected via addInitScript.\n * Captures user interactions to window.__canaryRecordedEvents.\n *\n * @module record-interaction-script\n */\n\nexport const INTERACTION_CAPTURE_SCRIPT = `\n(function() {\n if (window.__canaryRecordedEvents) return;\n window.__canaryRecordedEvents = [];\n\n function push(event) {\n window.__canaryRecordedEvents.push(event);\n }\n\n // --- Input debouncing ---\n // Accumulate input per element, flush after 500ms idle as a single event\n var inputTimers = new Map();\n var inputState = new Map();\n\n function getElementKey(el) {\n return (el.id || '') + '|' + (el.getAttribute('aria-label') || '') + '|' + (el.tagName || '') + '|' + (el.getAttribute('name') || '');\n }\n\n function flushInput(key) {\n var state = inputState.get(key);\n if (!state) return;\n inputTimers.delete(key);\n inputState.delete(key);\n push({\n type: 'input',\n ts: state.ts,\n tagName: state.tagName,\n id: state.id || undefined,\n inputType: state.inputType || undefined,\n ariaLabel: state.ariaLabel || undefined,\n value: state.value,\n isFinal: true,\n });\n }\n\n function flushAllInputs() {\n inputTimers.forEach(function(timer) { clearTimeout(timer); });\n inputState.forEach(function(_, key) { flushInput(key); });\n }\n\n document.addEventListener('click', function(e) {\n var el = e.target;\n push({\n type: 'click',\n ts: Date.now(),\n x: e.clientX,\n y: e.clientY,\n tagName: el.tagName || '',\n id: el.id || undefined,\n testId: el.getAttribute('data-testid') || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n role: el.getAttribute('role') || undefined,\n textContent: (el.textContent || '').slice(0, 200).trim() || undefined,\n className: el.className && typeof el.className === 'string' ? el.className.slice(0, 200) : undefined,\n });\n }, true);\n\n document.addEventListener('input', function(e) {\n var el = e.target;\n var isPassword = el.type === 'password';\n var key = getElementKey(el);\n\n // Clear previous timer for this element\n var prevTimer = inputTimers.get(key);\n if (prevTimer) clearTimeout(prevTimer);\n\n // Update accumulated state\n inputState.set(key, {\n ts: Date.now(),\n tagName: el.tagName || '',\n id: el.id || undefined,\n inputType: el.type || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n value: isPassword ? '***' : (el.value || ''),\n });\n\n // Schedule flush after 500ms idle\n inputTimers.set(key, setTimeout(function() { flushInput(key); }, 500));\n }, true);\n\n // --- Change events for selects, checkboxes, radios ---\n document.addEventListener('change', function(e) {\n var el = e.target;\n var tagName = el.tagName || '';\n var inputType = (el.type || '').toLowerCase();\n\n // Only capture selects, checkboxes, and radios (text inputs are handled by input debouncing)\n var isSelect = tagName === 'SELECT';\n var isCheckOrRadio = inputType === 'checkbox' || inputType === 'radio';\n if (!isSelect && !isCheckOrRadio) return;\n\n var value;\n if (isCheckOrRadio) {\n value = el.checked ? 'checked' : 'unchecked';\n } else {\n value = el.value || '';\n }\n\n push({\n type: 'change',\n ts: Date.now(),\n tagName: tagName,\n id: el.id || undefined,\n inputType: inputType || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n value: value,\n });\n }, true);\n\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Enter' || e.key === 'Tab' || e.key === 'Escape') {\n push({\n type: 'keydown',\n ts: Date.now(),\n key: e.key,\n });\n }\n }, true);\n\n // --- SPA navigation: patch pushState/replaceState ---\n var origPushState = history.pushState;\n var origReplaceState = history.replaceState;\n\n history.pushState = function() {\n origPushState.apply(this, arguments);\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'pushState',\n });\n };\n\n history.replaceState = function() {\n origReplaceState.apply(this, arguments);\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'replaceState',\n });\n };\n\n window.addEventListener('popstate', function() {\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'popstate',\n });\n });\n\n window.addEventListener('beforeunload', function() {\n flushAllInputs();\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'beforeunload',\n });\n });\n\n // Exposed for discardCapturedEvents — cancels pending debounce timers\n // without flushing their events, then clears the array.\n window.__canaryFlushAndDiscard = function() {\n inputTimers.forEach(function(timer) { clearTimeout(timer); });\n inputTimers.clear();\n inputState.clear();\n window.__canaryRecordedEvents = [];\n };\n})();\n`;\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"sources":["../../recording/src/locator-string.ts","../../recording/src/capture-script.ts"],"sourcesContent":["import type { SelectorStrategy } from '@chatsdet/types';\n\n/**\n * Build a Playwright locator string from a selector strategy.\n *\n * Returns the locator string in priority order (testId, role, label, placeholder, text, css).\n * Used during replay to find elements and during recording to pre-render the locator each\n * captured event would produce.\n *\n * @param selectors - Selector strategy from a cached action or captured recording event.\n * @returns Playwright locator chain as a string, e.g. `getByRole('button', { name: 'Sign in' })`.\n */\nexport function buildLocatorString(selectors: SelectorStrategy): string {\n const parts: string[] = [];\n\n if (selectors.testId) {\n parts.push(`getByTestId(${JSON.stringify(selectors.testId)})`);\n }\n\n if (selectors.role) {\n const { role, name, exact } = selectors.role;\n if (name) {\n const options = exact\n ? `{ name: ${JSON.stringify(name)}, exact: true }`\n : `{ name: ${JSON.stringify(name)} }`;\n parts.push(`getByRole('${role}', ${options})`);\n } else {\n parts.push(`getByRole('${role}')`);\n }\n }\n\n if (selectors.ariaLabel) {\n parts.push(`getByLabel(${JSON.stringify(selectors.ariaLabel)})`);\n }\n\n if (selectors.placeholder) {\n parts.push(`getByPlaceholder(${JSON.stringify(selectors.placeholder)})`);\n }\n\n if (selectors.text) {\n const { text, exact } = selectors.text;\n if (exact) {\n parts.push(`getByText(${JSON.stringify(text)}, { exact: true })`);\n } else {\n parts.push(`getByText(${JSON.stringify(text)})`);\n }\n }\n\n if (selectors.css) {\n parts.push(`locator(${JSON.stringify(selectors.css)})`);\n }\n\n return parts.length > 0 ? parts[0] : 'locator(\"*\")';\n}\n","/**\n * In-page interaction capture script injected via addInitScript.\n * Captures user interactions to window.__canaryRecordedEvents.\n *\n * @module record-interaction-script\n */\n\nexport const INTERACTION_CAPTURE_SCRIPT = `\n(function() {\n if (window.__canaryRecordedEvents) return;\n window.__canaryRecordedEvents = [];\n\n function push(event) {\n window.__canaryRecordedEvents.push(event);\n }\n\n // --- Input debouncing ---\n // Accumulate input per element, flush after 500ms idle as a single event\n var inputTimers = new Map();\n var inputState = new Map();\n\n function getElementKey(el) {\n return (el.id || '') + '|' + (el.getAttribute('aria-label') || '') + '|' + (el.tagName || '') + '|' + (el.getAttribute('name') || '');\n }\n\n function flushInput(key) {\n var state = inputState.get(key);\n if (!state) return;\n inputTimers.delete(key);\n inputState.delete(key);\n push({\n type: 'input',\n ts: state.ts,\n tagName: state.tagName,\n id: state.id || undefined,\n inputType: state.inputType || undefined,\n ariaLabel: state.ariaLabel || undefined,\n value: state.value,\n isFinal: true,\n });\n }\n\n function flushAllInputs() {\n inputTimers.forEach(function(timer) { clearTimeout(timer); });\n inputState.forEach(function(_, key) { flushInput(key); });\n }\n\n document.addEventListener('click', function(e) {\n var el = e.target;\n push({\n type: 'click',\n ts: Date.now(),\n x: e.clientX,\n y: e.clientY,\n tagName: el.tagName || '',\n id: el.id || undefined,\n testId: el.getAttribute('data-testid') || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n role: el.getAttribute('role') || undefined,\n textContent: (el.textContent || '').slice(0, 200).trim() || undefined,\n className: el.className && typeof el.className === 'string' ? el.className.slice(0, 200) : undefined,\n });\n }, true);\n\n document.addEventListener('input', function(e) {\n var el = e.target;\n var isPassword = el.type === 'password';\n var key = getElementKey(el);\n\n // Clear previous timer for this element\n var prevTimer = inputTimers.get(key);\n if (prevTimer) clearTimeout(prevTimer);\n\n // Update accumulated state\n inputState.set(key, {\n ts: Date.now(),\n tagName: el.tagName || '',\n id: el.id || undefined,\n inputType: el.type || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n value: isPassword ? '***' : (el.value || ''),\n });\n\n // Schedule flush after 500ms idle\n inputTimers.set(key, setTimeout(function() { flushInput(key); }, 500));\n }, true);\n\n // --- Change events for selects, checkboxes, radios ---\n document.addEventListener('change', function(e) {\n var el = e.target;\n var tagName = el.tagName || '';\n var inputType = (el.type || '').toLowerCase();\n\n // Only capture selects, checkboxes, and radios (text inputs are handled by input debouncing)\n var isSelect = tagName === 'SELECT';\n var isCheckOrRadio = inputType === 'checkbox' || inputType === 'radio';\n if (!isSelect && !isCheckOrRadio) return;\n\n var value;\n if (isCheckOrRadio) {\n value = el.checked ? 'checked' : 'unchecked';\n } else {\n value = el.value || '';\n }\n\n push({\n type: 'change',\n ts: Date.now(),\n tagName: tagName,\n id: el.id || undefined,\n inputType: inputType || undefined,\n ariaLabel: el.getAttribute('aria-label') || undefined,\n value: value,\n });\n }, true);\n\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Enter' || e.key === 'Tab' || e.key === 'Escape') {\n push({\n type: 'keydown',\n ts: Date.now(),\n key: e.key,\n });\n }\n }, true);\n\n // --- SPA navigation: patch pushState/replaceState ---\n var origPushState = history.pushState;\n var origReplaceState = history.replaceState;\n\n history.pushState = function() {\n origPushState.apply(this, arguments);\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'pushState',\n });\n };\n\n history.replaceState = function() {\n origReplaceState.apply(this, arguments);\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'replaceState',\n });\n };\n\n window.addEventListener('popstate', function() {\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'popstate',\n });\n });\n\n window.addEventListener('beforeunload', function() {\n flushAllInputs();\n push({\n type: 'navigation',\n ts: Date.now(),\n url: window.location.href,\n navigationType: 'beforeunload',\n });\n });\n\n // Exposed for discardCapturedEvents — cancels pending debounce timers\n // without flushing their events, then clears the array.\n window.__canaryFlushAndDiscard = function() {\n inputTimers.forEach(function(timer) { clearTimeout(timer); });\n inputTimers.clear();\n inputState.clear();\n window.__canaryRecordedEvents = [];\n };\n})();\n`;\n"],"mappings":";;;AAYO,SAAS,mBAAmB,WAAqC;AACtE,QAAM,QAAkB,CAAC;AAEzB,MAAI,UAAU,QAAQ;AACpB,UAAM,KAAK,eAAe,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG;AAAA,EAC/D;AAEA,MAAI,UAAU,MAAM;AAClB,UAAM,EAAE,MAAM,MAAM,MAAM,IAAI,UAAU;AACxC,QAAI,MAAM;AACR,YAAM,UAAU,QACZ,WAAW,KAAK,UAAU,IAAI,CAAC,oBAC/B,WAAW,KAAK,UAAU,IAAI,CAAC;AACnC,YAAM,KAAK,cAAc,IAAI,MAAM,OAAO,GAAG;AAAA,IAC/C,OAAO;AACL,YAAM,KAAK,cAAc,IAAI,IAAI;AAAA,IACnC;AAAA,EACF;AAEA,MAAI,UAAU,WAAW;AACvB,UAAM,KAAK,cAAc,KAAK,UAAU,UAAU,SAAS,CAAC,GAAG;AAAA,EACjE;AAEA,MAAI,UAAU,aAAa;AACzB,UAAM,KAAK,oBAAoB,KAAK,UAAU,UAAU,WAAW,CAAC,GAAG;AAAA,EACzE;AAEA,MAAI,UAAU,MAAM;AAClB,UAAM,EAAE,MAAM,MAAM,IAAI,UAAU;AAClC,QAAI,OAAO;AACT,YAAM,KAAK,aAAa,KAAK,UAAU,IAAI,CAAC,oBAAoB;AAAA,IAClE,OAAO;AACL,YAAM,KAAK,aAAa,KAAK,UAAU,IAAI,CAAC,GAAG;AAAA,IACjD;AAAA,EACF;AAEA,MAAI,UAAU,KAAK;AACjB,UAAM,KAAK,WAAW,KAAK,UAAU,UAAU,GAAG,CAAC,GAAG;AAAA,EACxD;AAEA,SAAO,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI;AACvC;;;AC9CO,IAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
|