@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.
Files changed (166) hide show
  1. package/dist/account-22LMP5PX.js +255 -0
  2. package/dist/account-22LMP5PX.js.map +1 -0
  3. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-WTFQNLK6.js} +5 -4
  4. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-WTFQNLK6.js.map} +1 -1
  5. package/dist/{attachment-SJC7D7FY.js → attachment-U3NKVPEY.js} +5 -4
  6. package/dist/{attachment-SJC7D7FY.js.map → attachment-U3NKVPEY.js.map} +1 -1
  7. package/dist/augmentor-5GMNAX4M.js +198 -0
  8. package/dist/augmentor-5GMNAX4M.js.map +1 -0
  9. package/dist/{auth-EN6NQCJY.js → auth-VQG2CDUD.js} +3 -2
  10. package/dist/{chunk-A4OFTUVV.js → chunk-3X3NW7QA.js} +7 -4
  11. package/dist/chunk-3X3NW7QA.js.map +1 -0
  12. package/dist/{chunk-N7HFALX3.js → chunk-46IUJXSD.js} +2 -2
  13. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  14. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  15. package/dist/{chunk-B2VISN2V.js → chunk-7VMO2WOZ.js} +238 -68
  16. package/dist/chunk-7VMO2WOZ.js.map +1 -0
  17. package/dist/{chunk-QFDOWABX.js → chunk-CMGI7UZD.js} +6 -3
  18. package/dist/chunk-CMGI7UZD.js.map +1 -0
  19. package/dist/{chunk-XQXVG7RA.js → chunk-FNPMWDUQ.js} +3 -3
  20. package/dist/chunk-G4UYGRZR.js +2 -0
  21. package/dist/{chunk-I4QLI3JO.js → chunk-HDFYUFBI.js} +2 -2
  22. package/dist/chunk-JMJVGMXI.js +92 -0
  23. package/dist/chunk-JMJVGMXI.js.map +1 -0
  24. package/dist/{chunk-SJJW77WN.js → chunk-JYGNMKZ5.js} +129 -38
  25. package/dist/chunk-JYGNMKZ5.js.map +1 -0
  26. package/dist/{chunk-52IO7ACJ.js → chunk-KCHKFYVX.js} +68 -10
  27. package/dist/{chunk-52IO7ACJ.js.map → chunk-KCHKFYVX.js.map} +1 -1
  28. package/dist/{chunk-ZD3KHESI.js → chunk-KRWRUU6R.js} +27 -7
  29. package/dist/chunk-KRWRUU6R.js.map +1 -0
  30. package/dist/{chunk-CLAWT7BJ.js → chunk-NHTEKEYE.js} +3 -3
  31. package/dist/{chunk-WOHL4FTB.js → chunk-PRBRSIZP.js} +16 -10
  32. package/dist/chunk-PRBRSIZP.js.map +1 -0
  33. package/dist/{chunk-GKY2KXO5.js → chunk-UETBLPKM.js} +154 -37
  34. package/dist/chunk-UETBLPKM.js.map +1 -0
  35. package/dist/{chunk-4PNOV7OV.js → chunk-VGYN2KU2.js} +9284 -1421
  36. package/dist/chunk-VGYN2KU2.js.map +1 -0
  37. package/dist/{chunk-Q4BO4Z5V.js → chunk-XJ5YYYGC.js} +4 -4
  38. package/dist/{credential-COLTBV6D.js → credential-E6KUHRGU.js} +24 -20
  39. package/dist/credential-E6KUHRGU.js.map +1 -0
  40. package/dist/{credential-template-EEPYFG7R.js → credential-template-4GPOPBGD.js} +30 -28
  41. package/dist/credential-template-4GPOPBGD.js.map +1 -0
  42. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-XCLCY6S7.js} +5 -4
  43. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-XCLCY6S7.js.map} +1 -1
  44. package/dist/{docs-3SSORHB5.js → docs-COCZYMW6.js} +95 -10
  45. package/dist/docs-COCZYMW6.js.map +1 -0
  46. package/dist/{doctor-OTSIWV6G.js → doctor-ERIVTCQS.js} +9 -5
  47. package/dist/doctor-ERIVTCQS.js.map +1 -0
  48. package/dist/environment-7Y4BV6XY.js +221 -0
  49. package/dist/environment-7Y4BV6XY.js.map +1 -0
  50. package/dist/{feature-flag-T2LRZME3.js → feature-flag-MHONEX6T.js} +164 -16
  51. package/dist/feature-flag-MHONEX6T.js.map +1 -0
  52. package/dist/index.js +123 -58
  53. package/dist/index.js.map +1 -1
  54. package/dist/{init-TGWRECT2.js → init-OROOM4HF.js} +5 -4
  55. package/dist/{init-TGWRECT2.js.map → init-OROOM4HF.js.map} +1 -1
  56. package/dist/{install-DXSNWZKO.js → install-OJFXHY6U.js} +7 -7
  57. package/dist/{issues-XIZOPX6T.js → issues-32GONR6C.js} +5 -4
  58. package/dist/{issues-XIZOPX6T.js.map → issues-32GONR6C.js.map} +1 -1
  59. package/dist/{knobs-NBINZIUB.js → knobs-UQGGY3CP.js} +7 -5
  60. package/dist/knobs-UQGGY3CP.js.map +1 -0
  61. package/dist/{list-3TDHK637.js → list-ZMIKEC5P.js} +5 -4
  62. package/dist/{list-3TDHK637.js.map → list-ZMIKEC5P.js.map} +1 -1
  63. package/dist/{local-HICVD6PU.js → local-PUD24GGQ.js} +7 -6
  64. package/dist/{local-HICVD6PU.js.map → local-PUD24GGQ.js.map} +1 -1
  65. package/dist/{login-UOIUXVUK.js → login-BSO7YX2D.js} +5 -4
  66. package/dist/{login-UOIUXVUK.js.map → login-BSO7YX2D.js.map} +1 -1
  67. package/dist/{mcp-54IJEOUA.js → mcp-4LS7OJUT.js} +24 -25
  68. package/dist/mcp-4LS7OJUT.js.map +1 -0
  69. package/dist/{pdf-extract-ABWV3L5X.js → pdf-extract-YHE54XU4.js} +2 -2
  70. package/dist/product-3JKSJCBC.js +346 -0
  71. package/dist/product-3JKSJCBC.js.map +1 -0
  72. package/dist/{property-UXLOLPTD.js → property-K5DBL4UE.js} +76 -4
  73. package/dist/property-K5DBL4UE.js.map +1 -0
  74. package/dist/{psql-BYUUNLGG.js → psql-PQFUFTSE.js} +3 -2
  75. package/dist/{psql-BYUUNLGG.js.map → psql-PQFUFTSE.js.map} +1 -1
  76. package/dist/{record-4TR2UIIS.js → record-EIIYLFJ4.js} +16 -17
  77. package/dist/record-EIIYLFJ4.js.map +1 -0
  78. package/dist/{redis-35VBUYW7.js → redis-BU36EZZI.js} +3 -2
  79. package/dist/redis-BU36EZZI.js.map +1 -0
  80. package/dist/{release-OOLTUBUT.js → release-LYC2P7DZ.js} +5 -4
  81. package/dist/{release-OOLTUBUT.js.map → release-LYC2P7DZ.js.map} +1 -1
  82. package/dist/{sandbox-XGSUU6G7.js → sandbox-CKHXXZHU.js} +55 -10
  83. package/dist/sandbox-CKHXXZHU.js.map +1 -0
  84. package/dist/{scenarios-GWLJEGHA.js → scenarios-2XJ2G3KR.js} +5 -4
  85. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-2XJ2G3KR.js.map} +1 -1
  86. package/dist/{session-MYFNZPRP.js → session-UTOD5ZYH.js} +77 -10
  87. package/dist/session-UTOD5ZYH.js.map +1 -0
  88. package/dist/{skill-KM34LDMO.js → skill-GMAERMQL.js} +220 -70
  89. package/dist/skill-GMAERMQL.js.map +1 -0
  90. package/dist/skills/debug-run/SKILL.md +40 -23
  91. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  92. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  93. package/dist/skills/debug-run/history/1.3.1.md +279 -0
  94. package/dist/skills/debug-run/version.json +1 -1
  95. package/dist/skills/optimize-cache/SKILL.md +233 -0
  96. package/dist/{spec-RA3CFEKG.js → spec-2NP36U3Y.js} +54 -25
  97. package/dist/spec-2NP36U3Y.js.map +1 -0
  98. package/dist/{src-LB4HBTJQ.js → src-4T6GHXVO.js} +19 -11
  99. package/dist/src-4T6GHXVO.js.map +1 -0
  100. package/dist/{src-N3TPKGTY.js → src-CG52HEE7.js} +10 -7
  101. package/dist/{src-UVDX5UP6.js → src-PK5OZXCM.js} +4 -2
  102. package/dist/src-UEOOC425.js +5941 -0
  103. package/dist/src-UEOOC425.js.map +1 -0
  104. package/dist/{start-PSEQSQA3.js → start-3C7EEFW2.js} +5 -4
  105. package/dist/{start-PSEQSQA3.js.map → start-3C7EEFW2.js.map} +1 -1
  106. package/dist/{tag-YY6YAABQ.js → tag-JSHOMLBR.js} +6 -5
  107. package/dist/{task-IQC3YR5K.js → task-YGWVYTBJ.js} +5 -4
  108. package/dist/{task-IQC3YR5K.js.map → task-YGWVYTBJ.js.map} +1 -1
  109. package/dist/{test-suite-DZNJFXDW.js → test-suite-LI7ZH367.js} +12 -7
  110. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-LI7ZH367.js.map} +1 -1
  111. package/dist/trace-UMLOSIIR.js +16 -0
  112. package/dist/{triage-ORG45R5W.js → triage-ZWJG4NBJ.js} +6 -5
  113. package/dist/triage-ZWJG4NBJ.js.map +1 -0
  114. package/dist/{upgrade-L2VYMP2V.js → upgrade-S7CGEK6H.js} +3 -3
  115. package/dist/{workflow-XYFH5LTP.js → workflow-T4HSAOGW.js} +316 -241
  116. package/dist/workflow-T4HSAOGW.js.map +1 -0
  117. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-XVEZ36KF.js} +140 -7
  118. package/dist/workflow-cache-XVEZ36KF.js.map +1 -0
  119. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-T2BULXZ7.js} +9 -8
  120. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-T2BULXZ7.js.map} +1 -1
  121. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-XXEZICN2.js} +5 -6
  122. package/dist/workflow-proposals-XXEZICN2.js.map +1 -0
  123. package/package.json +2 -1
  124. package/dist/chunk-4PNOV7OV.js.map +0 -1
  125. package/dist/chunk-A4OFTUVV.js.map +0 -1
  126. package/dist/chunk-B2VISN2V.js.map +0 -1
  127. package/dist/chunk-GKY2KXO5.js.map +0 -1
  128. package/dist/chunk-JICSYFRJ.js +0 -46
  129. package/dist/chunk-JICSYFRJ.js.map +0 -1
  130. package/dist/chunk-QFDOWABX.js.map +0 -1
  131. package/dist/chunk-SJJW77WN.js.map +0 -1
  132. package/dist/chunk-WOHL4FTB.js.map +0 -1
  133. package/dist/chunk-ZD3KHESI.js.map +0 -1
  134. package/dist/credential-COLTBV6D.js.map +0 -1
  135. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  136. package/dist/docs-3SSORHB5.js.map +0 -1
  137. package/dist/doctor-OTSIWV6G.js.map +0 -1
  138. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  139. package/dist/knobs-NBINZIUB.js.map +0 -1
  140. package/dist/mcp-54IJEOUA.js.map +0 -1
  141. package/dist/property-UXLOLPTD.js.map +0 -1
  142. package/dist/record-4TR2UIIS.js.map +0 -1
  143. package/dist/redis-35VBUYW7.js.map +0 -1
  144. package/dist/sandbox-XGSUU6G7.js.map +0 -1
  145. package/dist/session-MYFNZPRP.js.map +0 -1
  146. package/dist/skill-KM34LDMO.js.map +0 -1
  147. package/dist/spec-RA3CFEKG.js.map +0 -1
  148. package/dist/src-LB4HBTJQ.js.map +0 -1
  149. package/dist/trace-HXIW7U2D.js +0 -14
  150. package/dist/workflow-XYFH5LTP.js.map +0 -1
  151. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  152. package/dist/workflow-proposals-MTUDX46O.js.map +0 -1
  153. /package/dist/{auth-EN6NQCJY.js.map → auth-VQG2CDUD.js.map} +0 -0
  154. /package/dist/{chunk-N7HFALX3.js.map → chunk-46IUJXSD.js.map} +0 -0
  155. /package/dist/{chunk-XQXVG7RA.js.map → chunk-FNPMWDUQ.js.map} +0 -0
  156. /package/dist/{pdf-extract-ABWV3L5X.js.map → chunk-G4UYGRZR.js.map} +0 -0
  157. /package/dist/{chunk-I4QLI3JO.js.map → chunk-HDFYUFBI.js.map} +0 -0
  158. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-NHTEKEYE.js.map} +0 -0
  159. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-XJ5YYYGC.js.map} +0 -0
  160. /package/dist/{install-DXSNWZKO.js.map → install-OJFXHY6U.js.map} +0 -0
  161. /package/dist/{src-N3TPKGTY.js.map → pdf-extract-YHE54XU4.js.map} +0 -0
  162. /package/dist/{src-UVDX5UP6.js.map → src-CG52HEE7.js.map} +0 -0
  163. /package/dist/{tag-YY6YAABQ.js.map → src-PK5OZXCM.js.map} +0 -0
  164. /package/dist/{trace-HXIW7U2D.js.map → tag-JSHOMLBR.js.map} +0 -0
  165. /package/dist/{triage-ORG45R5W.js.map → trace-UMLOSIIR.js.map} +0 -0
  166. /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":";;;;;;;;;;;;;;;AAeA,OAAO,aAAa;AAqCpB,SAAS,KAAK,SAAwB;AACpC,UAAQ,MAAM,UAAU,OAAO,EAAE;AACjC,UAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,mBAAmB,MAAwB;AAClD,QAAM,KAAK,kBAAkB,IAAI;AACjC,MAAI,CAAC,GAAI,MAAK,wCAAwC;AACtD,SAAO;AACT;AAEA,SAAS,UAAU,MAAwB;AACzC,QAAM,KAAK,YAAY,MAAM,MAAM;AACnC,MAAI,CAAC,GAAI,MAAK,gCAAgC;AAC9C,SAAO;AACT;AAEA,SAAS,aAAmB;AAC1B,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gDAYkC;AAChD;AAEA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,aAAa,mBAAmB,IAAI;AAC1C,QAAM,SAAS,YAAY,MAAM,UAAU;AAC3C,QAAM,OAAO,YAAY,MAAM,UAAU,MAAM;AAC/C,QAAM,KAAK,SAAS,WAAW,mBAAmB,MAAM,CAAC,KAAK;AAC9D,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,UAAU,cAAc,EAAE;AAAA,EAC/C;AACA,MAAI,CAAC,IAAI,GAAI,MAAK,IAAI,SAAS,eAAe;AAC9C,QAAM,aAAa,IAAI,MAAM,cAAc,CAAC;AAC5C,MAAI,MAAM;AACR,YAAQ,IAAI,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAC/C;AAAA,EACF;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ,IAAI,kCAAkC;AAC9C;AAAA,EACF;AACA,aAAW,KAAK,YAAY;AAC1B,UAAM,SAAS,EAAE,UAAU,EAAE,SAAS;AACtC,YAAQ;AAAA,MACN,GAAG,EAAE,EAAE,MAAM,MAAM,MAAM,EAAE,OAAO;AAAA,cACjB,EAAE,aAAa;AAAA,cACf,EAAE,iBAAiB,mBAAmB,EAAE,gBAAgB,aAC5D,EAAE,oBAAoB,aAAa,EAAE,UAAU,MACzD,EAAE,qBAAqB;AAAA,iBAAoB,EAAE,kBAAkB,KAAK;AAAA,IACzE;AAAA,EACF;AACF;AAEA,eAAe,UAAU,MAAgB,QAAgB,OAA8B;AACrF,QAAM,KAAK,UAAU,IAAI;AACzB,QAAM,MAAM,MAAM,WAAwB,QAAQ,OAAO,OAAO,mBAAmB,EAAE,EAAE;AACvF,MAAI,CAAC,IAAI,GAAI,MAAK,IAAI,SAAS,eAAe;AAC9C,QAAM,IAAI,IAAI,MAAM;AACpB,MAAI,CAAC,EAAG,MAAK,qBAAqB;AAElC,UAAQ,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AACxC;AAEA,eAAe,YAAY,MAAgB,QAAgB,OAA8B;AACvF,QAAM,aAAa,mBAAmB,IAAI;AAC1C,QAAM,OAAO,YAAY,MAAM,UAAU,MAAM;AAC/C,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,UAAU;AAAA,EAC/B;AACA,MAAI,CAAC,IAAI,GAAI,MAAK,IAAI,SAAS,eAAe;AAC9C,QAAM,aAAa,IAAI,MAAM,cAAc,CAAC;AAC5C,QAAM,WAAmC,CAAC;AAC1C,MAAI,gBAAgB;AACpB,MAAI,oBAAoB;AACxB,aAAW,KAAK,YAAY;AAC1B,UAAM,MAAM,EAAE,UAAU,EAAE,SAAS;AACnC,aAAS,GAAG,KAAK,SAAS,GAAG,KAAK,KAAK;AACvC,qBAAiB,EAAE;AACnB,yBAAqB,EAAE;AAAA,EACzB;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO,WAAW;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA,iBAAiB,gBAAgB,IAAI,EAAE,oBAAoB,eAAe,QAAQ,CAAC,IAAI;AAAA,EACzF;AACA,MAAI,MAAM;AACR,YAAQ,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC1C;AAAA,EACF;AACA,UAAQ,IAAI,2BAA2B,UAAU,GAAG;AACpD,UAAQ,IAAI,YAAY,MAAM,KAAK,EAAE;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,EAAG,SAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE;AACzE,UAAQ,IAAI,eAAe,aAAa,mBAAmB,iBAAiB,WAAW,MAAM,eAAe,EAAE;AAChH;AAEA,eAAe,cAAc,MAAgB,QAAgB,OAA8B;AACzF,QAAM,aAAa,mBAAmB,IAAI;AAC1C,QAAM,OAAO,YAAY,MAAM,UAAU,MAAM;AAC/C,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,UAAU;AAAA,EAC/B;AACA,MAAI,CAAC,IAAI,GAAI,MAAK,IAAI,SAAS,eAAe;AAC9C,QAAM,UAAU,IAAI,MAAM,WAAW,CAAC;AACtC,MAAI,MAAM;AACR,YAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAC5C;AAAA,EACF;AACA,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAI,0DAA0D;AACtE;AAAA,EACF;AACA,aAAW,KAAK,SAAS;AACvB,UAAM,QAAQ,EAAE,cAAc,QAAQ,EAAE,cAAc,OAAO,IAAI,EAAE,SAAS,KAAK,EAAE,SAAS,MAAM;AAClG,YAAQ,IAAI,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK,MAAM,EAAE,OAAO,YAAO,EAAE,MAAM,EAAE;AAAA,EAC5E;AACF;AAEA,eAAe,cAAc,MAAgB,QAAgB,OAA8B;AACzF,QAAM,KAAK,UAAU,IAAI;AACzB,QAAM,SAAS,YAAY,MAAM,UAAU;AAC3C,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,EAAE;AAAA,IACrB,SAAS,EAAE,OAAO,IAAI;AAAA,EACxB;AACA,MAAI,CAAC,IAAI,GAAI,MAAK,IAAI,SAAS,eAAe;AAC9C,UAAQ,IAAI,sBAAsB,EAAE,GAAG;AACzC;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,KAAK,UAAU,IAAI;AACzB,QAAM,MAAM,MAAM,WAA2B,QAAQ,OAAO,QAAQ,mBAAmB,EAAE,SAAS;AAClG,MAAI,CAAC,IAAI,GAAI,MAAK,IAAI,SAAS,eAAe;AAC9C,UAAQ,IAAI,qBAAqB,EAAE,GAAG;AACxC;AAEA,eAAsB,aAAa,MAAgB,QAAgB,OAA8B;AAC/F,QAAM,CAAC,QAAQ,GAAG,IAAI,IAAI;AAC1B,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,iBAAW;AACX;AAAA,IACF,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF,KAAK;AACH,YAAM,UAAU,MAAM,QAAQ,KAAK;AACnC;AAAA,IACF,KAAK;AACH,YAAM,YAAY,MAAM,QAAQ,KAAK;AACrC;AAAA,IACF,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF;AACE,WAAK,8BAA8B,MAAM,kCAAkC;AAAA,EAC/E;AACF;","names":[]}
@@ -12,7 +12,8 @@ import {
12
12
  resolveConfig,
13
13
  resolveOptionalConfig,
14
14
  saveAuth
15
- } from "./chunk-A4OFTUVV.js";
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-EN6NQCJY.js.map
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) ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
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) ?? process.env.CANARY_API_TOKEN ?? null;
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 = process.env.CANARY_API_TOKEN ?? await readStoredToken() ?? null;
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-A4OFTUVV.js.map
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-JICSYFRJ.js";
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-N7HFALX3.js.map
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-IMPZAX6L.js.map
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":";;;AAOO,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":[]}
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":[]}