@canaryai/cli 0.2.9 → 0.2.13
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/{chunk-PWWQGYFG.js → chunk-ACRIE2YR.js} +5 -2
- package/dist/chunk-ACRIE2YR.js.map +1 -0
- package/dist/chunk-BOS2YLKH.js +233 -0
- package/dist/chunk-BOS2YLKH.js.map +1 -0
- package/dist/{chunk-XGO62PO2.js → chunk-IFOJT3A5.js} +1198 -262
- package/dist/chunk-IFOJT3A5.js.map +1 -0
- package/dist/{chunk-LC7ZVXPH.js → chunk-SVU2XTYZ.js} +19 -5
- package/dist/chunk-SVU2XTYZ.js.map +1 -0
- package/dist/{chunk-A44B2PEA.js → chunk-SYPQF57S.js} +40 -8
- package/dist/chunk-SYPQF57S.js.map +1 -0
- package/dist/{chunk-C2PGZRYK.js → chunk-Z3F373YR.js} +37 -11
- package/dist/chunk-Z3F373YR.js.map +1 -0
- package/dist/{debug-workflow-I3F36JBL.js → debug-workflow-K2LL6CO4.js} +10 -12
- package/dist/debug-workflow-K2LL6CO4.js.map +1 -0
- package/dist/{docs-REHST3YB.js → docs-SR7CW24Y.js} +19 -14
- package/dist/docs-SR7CW24Y.js.map +1 -0
- package/dist/{feature-flag-3HB5NTMY.js → feature-flag-BIPFVVNC.js} +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +83 -155
- package/dist/index.js.map +1 -1
- package/dist/init-KXAVWHYE.js +146 -0
- package/dist/init-KXAVWHYE.js.map +1 -0
- package/dist/{issues-YU57CHXS.js → issues-EWVB52CA.js} +37 -18
- package/dist/issues-EWVB52CA.js.map +1 -0
- package/dist/{knobs-QJ4IBLCT.js → knobs-VYABZESR.js} +3 -3
- package/dist/list-RCPYLS36.js +57 -0
- package/dist/list-RCPYLS36.js.map +1 -0
- package/dist/local-34FX3M5K.js +63 -0
- package/dist/local-34FX3M5K.js.map +1 -0
- package/dist/{local-browser-MKTJ36KY.js → local-browser-VPOSJS52.js} +4 -4
- package/dist/login-MSIM2VIH.js +130 -0
- package/dist/login-MSIM2VIH.js.map +1 -0
- package/dist/{mcp-ZOKM2AUE.js → mcp-YBR7G254.js} +7 -132
- package/dist/mcp-YBR7G254.js.map +1 -0
- package/dist/{psql-2YPIRMDY.js → psql-XO5BB5L5.js} +2 -2
- package/dist/{record-TNDBT3NY.js → record-DXXQHPGT.js} +10 -51
- package/dist/record-DXXQHPGT.js.map +1 -0
- package/dist/{redis-A7GWM23E.js → redis-CQTBPZ6F.js} +2 -2
- package/dist/{release-L4IXOHDF.js → release-DW7RPQSQ.js} +9 -5
- package/dist/release-DW7RPQSQ.js.map +1 -0
- package/dist/runner/preload.js +1 -1
- package/dist/{session-RNLKFS2Z.js → session-XQGCLWNC.js} +164 -75
- package/dist/session-XQGCLWNC.js.map +1 -0
- package/dist/skill-2TXI3IKP.js +424 -0
- package/dist/skill-2TXI3IKP.js.map +1 -0
- package/dist/{src-2WSMYBMJ.js → src-F7LQ5PY2.js} +8 -2
- package/dist/start-ZOMUD6LW.js +112 -0
- package/dist/start-ZOMUD6LW.js.map +1 -0
- package/dist/test.js +1 -1
- package/dist/test.js.map +1 -1
- package/dist/workflow-5UZTKX7X.js +624 -0
- package/dist/workflow-5UZTKX7X.js.map +1 -0
- package/package.json +1 -2
- package/dist/chunk-A44B2PEA.js.map +0 -1
- package/dist/chunk-C2PGZRYK.js.map +0 -1
- package/dist/chunk-DXIAHB72.js +0 -340
- package/dist/chunk-DXIAHB72.js.map +0 -1
- package/dist/chunk-LC7ZVXPH.js.map +0 -1
- package/dist/chunk-PWWQGYFG.js.map +0 -1
- package/dist/chunk-QLFSJG5O.js +0 -93
- package/dist/chunk-QLFSJG5O.js.map +0 -1
- package/dist/chunk-XGO62PO2.js.map +0 -1
- package/dist/debug-workflow-I3F36JBL.js.map +0 -1
- package/dist/docs-REHST3YB.js.map +0 -1
- package/dist/issues-YU57CHXS.js.map +0 -1
- package/dist/mcp-ZOKM2AUE.js.map +0 -1
- package/dist/record-TNDBT3NY.js.map +0 -1
- package/dist/release-L4IXOHDF.js.map +0 -1
- package/dist/session-RNLKFS2Z.js.map +0 -1
- package/dist/skill-CZ7SHI3P.js +0 -156
- package/dist/skill-CZ7SHI3P.js.map +0 -1
- /package/dist/{feature-flag-3HB5NTMY.js.map → feature-flag-BIPFVVNC.js.map} +0 -0
- /package/dist/{knobs-QJ4IBLCT.js.map → knobs-VYABZESR.js.map} +0 -0
- /package/dist/{local-browser-MKTJ36KY.js.map → local-browser-VPOSJS52.js.map} +0 -0
- /package/dist/{psql-2YPIRMDY.js.map → psql-XO5BB5L5.js.map} +0 -0
- /package/dist/{redis-A7GWM23E.js.map → redis-CQTBPZ6F.js.map} +0 -0
- /package/dist/{src-2WSMYBMJ.js.map → src-F7LQ5PY2.js.map} +0 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import "./chunk-VKVL7WBN.js";
|
|
3
|
+
|
|
4
|
+
// src/skill-templates/issue-log-xref.ts
|
|
5
|
+
var issueLogXrefTemplate = {
|
|
6
|
+
name: "issue-log-xref",
|
|
7
|
+
description: "Cross-reference Canary issues with server logs to find root causes",
|
|
8
|
+
render: () => `---
|
|
9
|
+
name: issue-log-xref
|
|
10
|
+
description: Cross-reference Canary QA issues with server logs to identify root causes. Use when investigating why a test failed, correlating browser errors with backend logs, or debugging issues found by Canary.
|
|
11
|
+
allowed-tools: Bash(canary issues:*), Bash(canary login:*), Grep, Read
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Issue Log Cross-Reference
|
|
15
|
+
|
|
16
|
+
Cross-reference Canary-detected issues with server logs to identify root causes, correlate errors, and provide actionable debugging context.
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- \`canary\` CLI installed and authenticated (\`canary login\`)
|
|
21
|
+
- Access to server logs (see Log Source Configuration below)
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
### 1. Fetch issues from Canary
|
|
26
|
+
|
|
27
|
+
List recent issues to find ones worth investigating:
|
|
28
|
+
|
|
29
|
+
\`\`\`bash
|
|
30
|
+
canary issues list --format markdown
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
Use filters to narrow down:
|
|
34
|
+
|
|
35
|
+
\`\`\`bash
|
|
36
|
+
canary issues list --severity high --status open --format markdown
|
|
37
|
+
\`\`\`
|
|
38
|
+
|
|
39
|
+
### 2. Get issue details
|
|
40
|
+
|
|
41
|
+
Once you identify an issue, fetch its full diagnostics:
|
|
42
|
+
|
|
43
|
+
\`\`\`bash
|
|
44
|
+
canary issues get <issue-id> --format markdown
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
Note the following from the issue details:
|
|
48
|
+
- **Timestamps** \u2014 when the issue was detected
|
|
49
|
+
- **URLs / endpoints** \u2014 which pages or API routes were involved
|
|
50
|
+
- **Error messages** \u2014 any visible error text captured in screenshots or DOM
|
|
51
|
+
- **HTTP status codes** \u2014 failed network requests
|
|
52
|
+
|
|
53
|
+
### 3. Access server logs
|
|
54
|
+
|
|
55
|
+
<!-- ============================================================
|
|
56
|
+
LOG SOURCE CONFIGURATION
|
|
57
|
+
|
|
58
|
+
Replace {{LOG_SOURCE_INSTRUCTIONS}} below with instructions
|
|
59
|
+
specific to your infrastructure. The AI agent reading this
|
|
60
|
+
skill will use these instructions to fetch logs.
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
|
|
64
|
+
**AWS CloudWatch:**
|
|
65
|
+
Query CloudWatch Logs Insights for the relevant log group:
|
|
66
|
+
\`\`\`bash
|
|
67
|
+
aws logs filter-log-events \\
|
|
68
|
+
--log-group-name /ecs/my-api \\
|
|
69
|
+
--start-time <epoch-ms> \\
|
|
70
|
+
--end-time <epoch-ms> \\
|
|
71
|
+
--filter-pattern "<error-text>"
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
**Local log files:**
|
|
75
|
+
Search local log files for matching entries:
|
|
76
|
+
\`\`\`bash
|
|
77
|
+
grep -i "<error-text>" /var/log/my-app/*.log
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
**Datadog:**
|
|
81
|
+
Use the Datadog CLI or API to query logs:
|
|
82
|
+
\`\`\`bash
|
|
83
|
+
dog logs list --query "service:my-api status:error" \\
|
|
84
|
+
--from <ISO-timestamp> --to <ISO-timestamp>
|
|
85
|
+
\`\`\`
|
|
86
|
+
|
|
87
|
+
**kubectl (Kubernetes):**
|
|
88
|
+
Fetch pod logs for the relevant service:
|
|
89
|
+
\`\`\`bash
|
|
90
|
+
kubectl logs -l app=my-api --since=1h | grep "<error-text>"
|
|
91
|
+
\`\`\`
|
|
92
|
+
============================================================ -->
|
|
93
|
+
|
|
94
|
+
{{LOG_SOURCE_INSTRUCTIONS}}
|
|
95
|
+
|
|
96
|
+
### 4. Cross-reference and correlate
|
|
97
|
+
|
|
98
|
+
Match the Canary issue with log entries using these correlation points:
|
|
99
|
+
|
|
100
|
+
- **Timestamps** \u2014 find log entries within a few seconds of the issue detection time
|
|
101
|
+
- **Error messages** \u2014 search logs for the same error text shown in the issue
|
|
102
|
+
- **HTTP status codes** \u2014 look for 4xx/5xx responses matching the failed requests
|
|
103
|
+
- **URLs / routes** \u2014 filter logs by the endpoint or page path from the issue
|
|
104
|
+
- **Stack traces** \u2014 identify the code path that produced the error
|
|
105
|
+
- **Request IDs** \u2014 if your app uses correlation/request IDs, trace the full request lifecycle
|
|
106
|
+
|
|
107
|
+
## Analysis Guidelines
|
|
108
|
+
|
|
109
|
+
When presenting findings:
|
|
110
|
+
|
|
111
|
+
1. **State the match confidence** \u2014 clear match, likely match, or circumstantial
|
|
112
|
+
2. **Show the timeline** \u2014 lay out events in chronological order across both sources
|
|
113
|
+
3. **Identify the root cause** \u2014 distinguish between the symptom (what Canary caught) and the cause (what the logs reveal)
|
|
114
|
+
4. **Suggest next steps** \u2014 point to the specific code, service, or configuration that needs attention
|
|
115
|
+
5. **Note gaps** \u2014 if logs are missing or incomplete, say so explicitly rather than speculating
|
|
116
|
+
`
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/skill-templates/browser-testing.ts
|
|
120
|
+
var browserTestingTemplate = {
|
|
121
|
+
name: "browser-testing",
|
|
122
|
+
description: "Use the Canary CLI to control a browser for testing web applications",
|
|
123
|
+
render: () => `---
|
|
124
|
+
name: browser-testing
|
|
125
|
+
description: Control a local browser via the Canary CLI to test web applications interactively. Use when the user asks to test, browse, interact with, or verify a web application using canary browser sessions.
|
|
126
|
+
allowed-tools: Bash(canary session:*), Bash(canary login:*), Read
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
# Browser Testing with Canary
|
|
130
|
+
|
|
131
|
+
Use the \`canary session\` CLI to launch and control a local browser for testing web applications. You interact with pages through **snapshots** (accessibility tree + screenshot), then issue commands to click, type, navigate, and assert.
|
|
132
|
+
|
|
133
|
+
## Prerequisites
|
|
134
|
+
|
|
135
|
+
- \`canary\` CLI installed and authenticated (\`canary login\`)
|
|
136
|
+
- Run \`canary session --help\` for the full command reference
|
|
137
|
+
|
|
138
|
+
## Environments & Authentication
|
|
139
|
+
|
|
140
|
+
Canary credentials are stored remotely and belong to a **property** (an app with a base URL). When the user asks you to test, determine the environment:
|
|
141
|
+
|
|
142
|
+
### Remote / Deployed environments (default)
|
|
143
|
+
|
|
144
|
+
Credentials are pre-configured with storage state. Just use \`--credential\` to load one:
|
|
145
|
+
|
|
146
|
+
\`\`\`bash
|
|
147
|
+
# Interactive picker \u2014 shows credential name, property name, and base URL
|
|
148
|
+
canary session start --credential
|
|
149
|
+
|
|
150
|
+
# By name
|
|
151
|
+
canary session start --credential "Admin User" --url https://staging.example.com
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
### Local development
|
|
155
|
+
|
|
156
|
+
Credentials stored in Canary won't work against localhost because session cookies are bound to the remote domain. When testing a local dev server:
|
|
157
|
+
|
|
158
|
+
1. **Ask the user to log in manually.** Start a session without credentials and tell them to authenticate in the browser:
|
|
159
|
+
|
|
160
|
+
\`\`\`bash
|
|
161
|
+
canary session start --url http://localhost:5173
|
|
162
|
+
\`\`\`
|
|
163
|
+
|
|
164
|
+
Then tell the user: "I've opened a browser to your local app. Please log in, and let me know when you're ready for me to continue."
|
|
165
|
+
|
|
166
|
+
2. **Wait for confirmation** before proceeding with testing.
|
|
167
|
+
|
|
168
|
+
3. Alternatively, if the user has a **local storage state file**, use it:
|
|
169
|
+
|
|
170
|
+
\`\`\`bash
|
|
171
|
+
canary session start --storage-state ./local-auth.json --url http://localhost:5173
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
**Rule:** If the URL is localhost or 127.0.0.1, do NOT use \`--credential\` \u2014 it will download remote session state that won't work locally. Instead, prompt the user to log in.
|
|
175
|
+
|
|
176
|
+
## Core Workflow
|
|
177
|
+
|
|
178
|
+
The fundamental loop is: **snapshot \u2192 read \u2192 act \u2192 snapshot \u2192 verify**.
|
|
179
|
+
|
|
180
|
+
### 1. Start a session
|
|
181
|
+
|
|
182
|
+
\`\`\`bash
|
|
183
|
+
# Start a headed browser (you can watch it)
|
|
184
|
+
canary session start --url https://your-app.com
|
|
185
|
+
|
|
186
|
+
# Start with a saved credential (interactive picker shows property + base URL)
|
|
187
|
+
canary session start --credential
|
|
188
|
+
|
|
189
|
+
# Start with a specific credential by name
|
|
190
|
+
canary session start --credential "Admin User" --url https://your-app.com
|
|
191
|
+
|
|
192
|
+
# Load a credential into an already-running session
|
|
193
|
+
canary session load-credential
|
|
194
|
+
\`\`\`
|
|
195
|
+
|
|
196
|
+
### 2. Take a snapshot to see the page
|
|
197
|
+
|
|
198
|
+
Snapshots are your primary way to understand what's on the page. They return an **accessibility tree** with element references (refs) and a **screenshot**.
|
|
199
|
+
|
|
200
|
+
\`\`\`bash
|
|
201
|
+
# Full snapshot: accessibility tree + screenshot
|
|
202
|
+
canary session snapshot
|
|
203
|
+
|
|
204
|
+
# Search for specific elements (fastest way to find what you need)
|
|
205
|
+
canary session snapshot --search "Submit"
|
|
206
|
+
canary session snapshot --search "Email|Password"
|
|
207
|
+
|
|
208
|
+
# Screenshot only (useful for visual verification)
|
|
209
|
+
canary session snapshot --mode screenshot
|
|
210
|
+
|
|
211
|
+
# Tree only (useful when you need ref details, no image)
|
|
212
|
+
canary session snapshot --mode tree
|
|
213
|
+
\`\`\`
|
|
214
|
+
|
|
215
|
+
**Reading snapshot output:**
|
|
216
|
+
|
|
217
|
+
The accessibility tree shows elements with refs like \`[ref=e15]\`. Use these refs in subsequent commands. The tree is structured to show the page hierarchy \u2014 forms, buttons, links, headings, etc.
|
|
218
|
+
|
|
219
|
+
When you use \`--search\`, matching elements are highlighted in the output and on the screenshot. Search is OR-matched and case-insensitive. Use search to **locate** elements, not to validate visual states \u2014 search overlays can obscure subtle UI changes.
|
|
220
|
+
|
|
221
|
+
### 3. Interact with the page
|
|
222
|
+
|
|
223
|
+
Always reference elements by their **ref** from the snapshot.
|
|
224
|
+
|
|
225
|
+
\`\`\`bash
|
|
226
|
+
# Click an element
|
|
227
|
+
canary session click --ref e15 --element "Submit button"
|
|
228
|
+
|
|
229
|
+
# Type into an input field
|
|
230
|
+
canary session type --ref e7 --text "user@example.com" --element "Email input"
|
|
231
|
+
|
|
232
|
+
# Type and submit (presses Enter after typing)
|
|
233
|
+
canary session type --ref e7 --text "search query" --element "Search box" --submit
|
|
234
|
+
|
|
235
|
+
# Fill multiple form fields at once
|
|
236
|
+
canary session fill --fields '[{"ref":"e3","value":"John"},{"ref":"e4","value":"john@example.com"}]'
|
|
237
|
+
|
|
238
|
+
# Select a dropdown option
|
|
239
|
+
canary session select --ref e12 --value "admin" --element "Role dropdown"
|
|
240
|
+
|
|
241
|
+
# Press a keyboard key
|
|
242
|
+
canary session press --key Enter
|
|
243
|
+
canary session press --key Escape
|
|
244
|
+
canary session press --key ArrowDown
|
|
245
|
+
|
|
246
|
+
# Hover to reveal hidden content (tooltips, dropdowns)
|
|
247
|
+
canary session hover --ref e8 --element "User menu"
|
|
248
|
+
\`\`\`
|
|
249
|
+
|
|
250
|
+
### 4. Navigate
|
|
251
|
+
|
|
252
|
+
\`\`\`bash
|
|
253
|
+
canary session navigate --url https://your-app.com/settings
|
|
254
|
+
canary session back
|
|
255
|
+
\`\`\`
|
|
256
|
+
|
|
257
|
+
### 5. Scroll
|
|
258
|
+
|
|
259
|
+
\`\`\`bash
|
|
260
|
+
# Scroll down the page
|
|
261
|
+
canary session scroll --direction down
|
|
262
|
+
|
|
263
|
+
# Scroll with more intensity (1-10, default 3)
|
|
264
|
+
canary session scroll --direction down --amount 8
|
|
265
|
+
|
|
266
|
+
# Scroll up
|
|
267
|
+
canary session scroll --direction up
|
|
268
|
+
\`\`\`
|
|
269
|
+
|
|
270
|
+
### 6. Verify results
|
|
271
|
+
|
|
272
|
+
After every action, take a snapshot to verify the result:
|
|
273
|
+
|
|
274
|
+
\`\`\`bash
|
|
275
|
+
# Check what happened after clicking submit
|
|
276
|
+
canary session snapshot
|
|
277
|
+
|
|
278
|
+
# Search for a success message
|
|
279
|
+
canary session snapshot --search "Success|saved"
|
|
280
|
+
|
|
281
|
+
# Check for error messages
|
|
282
|
+
canary session snapshot --search "Error|failed|invalid"
|
|
283
|
+
|
|
284
|
+
# Check console for JavaScript errors
|
|
285
|
+
canary session console --only-errors
|
|
286
|
+
|
|
287
|
+
# Check network requests for failed API calls
|
|
288
|
+
canary session network
|
|
289
|
+
\`\`\`
|
|
290
|
+
|
|
291
|
+
## Session Management
|
|
292
|
+
|
|
293
|
+
\`\`\`bash
|
|
294
|
+
# List all active sessions
|
|
295
|
+
canary session list
|
|
296
|
+
|
|
297
|
+
# Check session details
|
|
298
|
+
canary session status
|
|
299
|
+
|
|
300
|
+
# Stop a session
|
|
301
|
+
canary session stop
|
|
302
|
+
|
|
303
|
+
# Stop all sessions
|
|
304
|
+
canary session stop --all
|
|
305
|
+
|
|
306
|
+
# When multiple sessions are active, specify which one
|
|
307
|
+
canary session snapshot --session s1
|
|
308
|
+
canary session click --ref e5 --element "Button" --session s2
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
## Advanced Techniques
|
|
312
|
+
|
|
313
|
+
### Waiting for page changes
|
|
314
|
+
|
|
315
|
+
\`\`\`bash
|
|
316
|
+
# Wait for text to appear (after navigation or async action)
|
|
317
|
+
canary session wait --text "Dashboard loaded"
|
|
318
|
+
|
|
319
|
+
# Wait for text to disappear (loading spinner)
|
|
320
|
+
canary session wait --text "Loading..."
|
|
321
|
+
|
|
322
|
+
# Wait a fixed time (use sparingly)
|
|
323
|
+
canary session wait --time 2
|
|
324
|
+
\`\`\`
|
|
325
|
+
|
|
326
|
+
### Coordinate-based clicking
|
|
327
|
+
|
|
328
|
+
When ref-based clicking doesn't work (overlays, canvas elements), use coordinates. First take a snapshot, look at the screenshot, estimate coordinates, then click:
|
|
329
|
+
|
|
330
|
+
\`\`\`bash
|
|
331
|
+
# Click at specific coordinates
|
|
332
|
+
canary session click --x 450 --y 300 --element "Canvas element"
|
|
333
|
+
\`\`\`
|
|
334
|
+
|
|
335
|
+
### Tab management
|
|
336
|
+
|
|
337
|
+
\`\`\`bash
|
|
338
|
+
canary session tabs --action list
|
|
339
|
+
canary session tabs --action new
|
|
340
|
+
canary session tabs --action select --index 0
|
|
341
|
+
canary session tabs --action close --index 1
|
|
342
|
+
\`\`\`
|
|
343
|
+
|
|
344
|
+
### Evaluating JavaScript
|
|
345
|
+
|
|
346
|
+
\`\`\`bash
|
|
347
|
+
canary session evaluate --js "() => document.title"
|
|
348
|
+
canary session evaluate --js "() => window.location.href"
|
|
349
|
+
canary session evaluate --js "() => document.querySelectorAll('.error').length"
|
|
350
|
+
\`\`\`
|
|
351
|
+
|
|
352
|
+
### Taking labeled screenshots
|
|
353
|
+
|
|
354
|
+
\`\`\`bash
|
|
355
|
+
# Take a labeled screenshot for later reference
|
|
356
|
+
canary session screenshot --label "before-submit"
|
|
357
|
+
|
|
358
|
+
# Full page screenshot
|
|
359
|
+
canary session screenshot --full-page --label "full-page-state"
|
|
360
|
+
\`\`\`
|
|
361
|
+
|
|
362
|
+
### File downloads
|
|
363
|
+
|
|
364
|
+
\`\`\`bash
|
|
365
|
+
# List downloaded files
|
|
366
|
+
canary session evaluate --js "() => 'trigger download'"
|
|
367
|
+
# ... after download completes:
|
|
368
|
+
canary session list-downloads
|
|
369
|
+
canary session read-download
|
|
370
|
+
\`\`\`
|
|
371
|
+
|
|
372
|
+
## Testing Guidelines
|
|
373
|
+
|
|
374
|
+
1. **Always snapshot after actions** \u2014 never assume an action succeeded. Take a snapshot to verify.
|
|
375
|
+
2. **Use search to locate elements** \u2014 \`--search "Submit"\` is faster than scanning the full tree.
|
|
376
|
+
3. **Use refs, not coordinates** \u2014 ref-based interaction is more reliable. Only fall back to coordinates when refs fail.
|
|
377
|
+
4. **Check for errors** \u2014 after form submissions or navigation, check both the page content and console/network for errors.
|
|
378
|
+
5. **Wait for async operations** \u2014 if an action triggers an API call, use \`wait --text\` for the expected result rather than fixed delays.
|
|
379
|
+
6. **Load credentials for authenticated testing** \u2014 use \`canary session start --credential\` or \`canary session load-credential\` to test as specific users.
|
|
380
|
+
7. **Report what you find** \u2014 describe what you see in snapshots, what worked, what failed, and include relevant refs or error messages.
|
|
381
|
+
`
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/skill-templates/index.ts
|
|
385
|
+
var SKILL_TEMPLATES = {
|
|
386
|
+
"issue-log-xref": issueLogXrefTemplate,
|
|
387
|
+
"browser-testing": browserTestingTemplate
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// src/skill.ts
|
|
391
|
+
function printSkillHelp() {
|
|
392
|
+
const lines = [
|
|
393
|
+
"Usage: canary skill <name>",
|
|
394
|
+
"",
|
|
395
|
+
"Output an AI agent skill template to stdout.",
|
|
396
|
+
"Install a skill:",
|
|
397
|
+
" mkdir -p .claude/skills/<name> && canary skill <name> > .claude/skills/<name>/SKILL.md",
|
|
398
|
+
"",
|
|
399
|
+
"Available templates:"
|
|
400
|
+
];
|
|
401
|
+
for (const [key, template] of Object.entries(SKILL_TEMPLATES)) {
|
|
402
|
+
lines.push(` ${key.padEnd(24)} ${template.description}`);
|
|
403
|
+
}
|
|
404
|
+
console.log(lines.join("\n"));
|
|
405
|
+
}
|
|
406
|
+
async function runSkill(argv) {
|
|
407
|
+
const [name] = argv;
|
|
408
|
+
if (!name || name === "help" || name === "--help" || name === "-h") {
|
|
409
|
+
printSkillHelp();
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const template = SKILL_TEMPLATES[name];
|
|
413
|
+
if (!template) {
|
|
414
|
+
console.error(`Unknown skill template "${name}".
|
|
415
|
+
`);
|
|
416
|
+
printSkillHelp();
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
process.stdout.write(template.render());
|
|
420
|
+
}
|
|
421
|
+
export {
|
|
422
|
+
runSkill
|
|
423
|
+
};
|
|
424
|
+
//# sourceMappingURL=skill-2TXI3IKP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/skill-templates/issue-log-xref.ts","../src/skill-templates/browser-testing.ts","../src/skill-templates/index.ts","../src/skill.ts"],"sourcesContent":["export type SkillTemplate = {\n name: string;\n description: string;\n render: () => string;\n};\n\nexport const issueLogXrefTemplate: SkillTemplate = {\n name: \"issue-log-xref\",\n description:\n \"Cross-reference Canary issues with server logs to find root causes\",\n render: () => `---\nname: issue-log-xref\ndescription: Cross-reference Canary QA issues with server logs to identify root causes. Use when investigating why a test failed, correlating browser errors with backend logs, or debugging issues found by Canary.\nallowed-tools: Bash(canary issues:*), Bash(canary login:*), Grep, Read\n---\n\n# Issue Log Cross-Reference\n\nCross-reference Canary-detected issues with server logs to identify root causes, correlate errors, and provide actionable debugging context.\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- Access to server logs (see Log Source Configuration below)\n\n## Workflow\n\n### 1. Fetch issues from Canary\n\nList recent issues to find ones worth investigating:\n\n\\`\\`\\`bash\ncanary issues list --format markdown\n\\`\\`\\`\n\nUse filters to narrow down:\n\n\\`\\`\\`bash\ncanary issues list --severity high --status open --format markdown\n\\`\\`\\`\n\n### 2. Get issue details\n\nOnce you identify an issue, fetch its full diagnostics:\n\n\\`\\`\\`bash\ncanary issues get <issue-id> --format markdown\n\\`\\`\\`\n\nNote the following from the issue details:\n- **Timestamps** — when the issue was detected\n- **URLs / endpoints** — which pages or API routes were involved\n- **Error messages** — any visible error text captured in screenshots or DOM\n- **HTTP status codes** — failed network requests\n\n### 3. Access server logs\n\n<!-- ============================================================\n LOG SOURCE CONFIGURATION\n\n Replace {{LOG_SOURCE_INSTRUCTIONS}} below with instructions\n specific to your infrastructure. The AI agent reading this\n skill will use these instructions to fetch logs.\n\n Examples:\n\n **AWS CloudWatch:**\n Query CloudWatch Logs Insights for the relevant log group:\n \\`\\`\\`bash\n aws logs filter-log-events \\\\\n --log-group-name /ecs/my-api \\\\\n --start-time <epoch-ms> \\\\\n --end-time <epoch-ms> \\\\\n --filter-pattern \"<error-text>\"\n \\`\\`\\`\n\n **Local log files:**\n Search local log files for matching entries:\n \\`\\`\\`bash\n grep -i \"<error-text>\" /var/log/my-app/*.log\n \\`\\`\\`\n\n **Datadog:**\n Use the Datadog CLI or API to query logs:\n \\`\\`\\`bash\n dog logs list --query \"service:my-api status:error\" \\\\\n --from <ISO-timestamp> --to <ISO-timestamp>\n \\`\\`\\`\n\n **kubectl (Kubernetes):**\n Fetch pod logs for the relevant service:\n \\`\\`\\`bash\n kubectl logs -l app=my-api --since=1h | grep \"<error-text>\"\n \\`\\`\\`\n============================================================ -->\n\n{{LOG_SOURCE_INSTRUCTIONS}}\n\n### 4. Cross-reference and correlate\n\nMatch the Canary issue with log entries using these correlation points:\n\n- **Timestamps** — find log entries within a few seconds of the issue detection time\n- **Error messages** — search logs for the same error text shown in the issue\n- **HTTP status codes** — look for 4xx/5xx responses matching the failed requests\n- **URLs / routes** — filter logs by the endpoint or page path from the issue\n- **Stack traces** — identify the code path that produced the error\n- **Request IDs** — if your app uses correlation/request IDs, trace the full request lifecycle\n\n## Analysis Guidelines\n\nWhen presenting findings:\n\n1. **State the match confidence** — clear match, likely match, or circumstantial\n2. **Show the timeline** — lay out events in chronological order across both sources\n3. **Identify the root cause** — distinguish between the symptom (what Canary caught) and the cause (what the logs reveal)\n4. **Suggest next steps** — point to the specific code, service, or configuration that needs attention\n5. **Note gaps** — if logs are missing or incomplete, say so explicitly rather than speculating\n`,\n};\n","type SkillTemplate = {\n name: string;\n description: string;\n render: () => string;\n};\n\nexport const browserTestingTemplate: SkillTemplate = {\n name: 'browser-testing',\n description: 'Use the Canary CLI to control a browser for testing web applications',\n render: () => `---\nname: browser-testing\ndescription: Control a local browser via the Canary CLI to test web applications interactively. Use when the user asks to test, browse, interact with, or verify a web application using canary browser sessions.\nallowed-tools: Bash(canary session:*), Bash(canary login:*), Read\n---\n\n# Browser Testing with Canary\n\nUse the \\`canary session\\` CLI to launch and control a local browser for testing web applications. You interact with pages through **snapshots** (accessibility tree + screenshot), then issue commands to click, type, navigate, and assert.\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- Run \\`canary session --help\\` for the full command reference\n\n## Environments & Authentication\n\nCanary credentials are stored remotely and belong to a **property** (an app with a base URL). When the user asks you to test, determine the environment:\n\n### Remote / Deployed environments (default)\n\nCredentials are pre-configured with storage state. Just use \\`--credential\\` to load one:\n\n\\`\\`\\`bash\n# Interactive picker — shows credential name, property name, and base URL\ncanary session start --credential\n\n# By name\ncanary session start --credential \"Admin User\" --url https://staging.example.com\n\\`\\`\\`\n\n### Local development\n\nCredentials stored in Canary won't work against localhost because session cookies are bound to the remote domain. When testing a local dev server:\n\n1. **Ask the user to log in manually.** Start a session without credentials and tell them to authenticate in the browser:\n\n\\`\\`\\`bash\ncanary session start --url http://localhost:5173\n\\`\\`\\`\n\nThen tell the user: \"I've opened a browser to your local app. Please log in, and let me know when you're ready for me to continue.\"\n\n2. **Wait for confirmation** before proceeding with testing.\n\n3. Alternatively, if the user has a **local storage state file**, use it:\n\n\\`\\`\\`bash\ncanary session start --storage-state ./local-auth.json --url http://localhost:5173\n\\`\\`\\`\n\n**Rule:** If the URL is localhost or 127.0.0.1, do NOT use \\`--credential\\` — it will download remote session state that won't work locally. Instead, prompt the user to log in.\n\n## Core Workflow\n\nThe fundamental loop is: **snapshot → read → act → snapshot → verify**.\n\n### 1. Start a session\n\n\\`\\`\\`bash\n# Start a headed browser (you can watch it)\ncanary session start --url https://your-app.com\n\n# Start with a saved credential (interactive picker shows property + base URL)\ncanary session start --credential\n\n# Start with a specific credential by name\ncanary session start --credential \"Admin User\" --url https://your-app.com\n\n# Load a credential into an already-running session\ncanary session load-credential\n\\`\\`\\`\n\n### 2. Take a snapshot to see the page\n\nSnapshots are your primary way to understand what's on the page. They return an **accessibility tree** with element references (refs) and a **screenshot**.\n\n\\`\\`\\`bash\n# Full snapshot: accessibility tree + screenshot\ncanary session snapshot\n\n# Search for specific elements (fastest way to find what you need)\ncanary session snapshot --search \"Submit\"\ncanary session snapshot --search \"Email|Password\"\n\n# Screenshot only (useful for visual verification)\ncanary session snapshot --mode screenshot\n\n# Tree only (useful when you need ref details, no image)\ncanary session snapshot --mode tree\n\\`\\`\\`\n\n**Reading snapshot output:**\n\nThe accessibility tree shows elements with refs like \\`[ref=e15]\\`. Use these refs in subsequent commands. The tree is structured to show the page hierarchy — forms, buttons, links, headings, etc.\n\nWhen you use \\`--search\\`, matching elements are highlighted in the output and on the screenshot. Search is OR-matched and case-insensitive. Use search to **locate** elements, not to validate visual states — search overlays can obscure subtle UI changes.\n\n### 3. Interact with the page\n\nAlways reference elements by their **ref** from the snapshot.\n\n\\`\\`\\`bash\n# Click an element\ncanary session click --ref e15 --element \"Submit button\"\n\n# Type into an input field\ncanary session type --ref e7 --text \"user@example.com\" --element \"Email input\"\n\n# Type and submit (presses Enter after typing)\ncanary session type --ref e7 --text \"search query\" --element \"Search box\" --submit\n\n# Fill multiple form fields at once\ncanary session fill --fields '[{\"ref\":\"e3\",\"value\":\"John\"},{\"ref\":\"e4\",\"value\":\"john@example.com\"}]'\n\n# Select a dropdown option\ncanary session select --ref e12 --value \"admin\" --element \"Role dropdown\"\n\n# Press a keyboard key\ncanary session press --key Enter\ncanary session press --key Escape\ncanary session press --key ArrowDown\n\n# Hover to reveal hidden content (tooltips, dropdowns)\ncanary session hover --ref e8 --element \"User menu\"\n\\`\\`\\`\n\n### 4. Navigate\n\n\\`\\`\\`bash\ncanary session navigate --url https://your-app.com/settings\ncanary session back\n\\`\\`\\`\n\n### 5. Scroll\n\n\\`\\`\\`bash\n# Scroll down the page\ncanary session scroll --direction down\n\n# Scroll with more intensity (1-10, default 3)\ncanary session scroll --direction down --amount 8\n\n# Scroll up\ncanary session scroll --direction up\n\\`\\`\\`\n\n### 6. Verify results\n\nAfter every action, take a snapshot to verify the result:\n\n\\`\\`\\`bash\n# Check what happened after clicking submit\ncanary session snapshot\n\n# Search for a success message\ncanary session snapshot --search \"Success|saved\"\n\n# Check for error messages\ncanary session snapshot --search \"Error|failed|invalid\"\n\n# Check console for JavaScript errors\ncanary session console --only-errors\n\n# Check network requests for failed API calls\ncanary session network\n\\`\\`\\`\n\n## Session Management\n\n\\`\\`\\`bash\n# List all active sessions\ncanary session list\n\n# Check session details\ncanary session status\n\n# Stop a session\ncanary session stop\n\n# Stop all sessions\ncanary session stop --all\n\n# When multiple sessions are active, specify which one\ncanary session snapshot --session s1\ncanary session click --ref e5 --element \"Button\" --session s2\n\\`\\`\\`\n\n## Advanced Techniques\n\n### Waiting for page changes\n\n\\`\\`\\`bash\n# Wait for text to appear (after navigation or async action)\ncanary session wait --text \"Dashboard loaded\"\n\n# Wait for text to disappear (loading spinner)\ncanary session wait --text \"Loading...\"\n\n# Wait a fixed time (use sparingly)\ncanary session wait --time 2\n\\`\\`\\`\n\n### Coordinate-based clicking\n\nWhen ref-based clicking doesn't work (overlays, canvas elements), use coordinates. First take a snapshot, look at the screenshot, estimate coordinates, then click:\n\n\\`\\`\\`bash\n# Click at specific coordinates\ncanary session click --x 450 --y 300 --element \"Canvas element\"\n\\`\\`\\`\n\n### Tab management\n\n\\`\\`\\`bash\ncanary session tabs --action list\ncanary session tabs --action new\ncanary session tabs --action select --index 0\ncanary session tabs --action close --index 1\n\\`\\`\\`\n\n### Evaluating JavaScript\n\n\\`\\`\\`bash\ncanary session evaluate --js \"() => document.title\"\ncanary session evaluate --js \"() => window.location.href\"\ncanary session evaluate --js \"() => document.querySelectorAll('.error').length\"\n\\`\\`\\`\n\n### Taking labeled screenshots\n\n\\`\\`\\`bash\n# Take a labeled screenshot for later reference\ncanary session screenshot --label \"before-submit\"\n\n# Full page screenshot\ncanary session screenshot --full-page --label \"full-page-state\"\n\\`\\`\\`\n\n### File downloads\n\n\\`\\`\\`bash\n# List downloaded files\ncanary session evaluate --js \"() => 'trigger download'\"\n# ... after download completes:\ncanary session list-downloads\ncanary session read-download\n\\`\\`\\`\n\n## Testing Guidelines\n\n1. **Always snapshot after actions** — never assume an action succeeded. Take a snapshot to verify.\n2. **Use search to locate elements** — \\`--search \"Submit\"\\` is faster than scanning the full tree.\n3. **Use refs, not coordinates** — ref-based interaction is more reliable. Only fall back to coordinates when refs fail.\n4. **Check for errors** — after form submissions or navigation, check both the page content and console/network for errors.\n5. **Wait for async operations** — if an action triggers an API call, use \\`wait --text\\` for the expected result rather than fixed delays.\n6. **Load credentials for authenticated testing** — use \\`canary session start --credential\\` or \\`canary session load-credential\\` to test as specific users.\n7. **Report what you find** — describe what you see in snapshots, what worked, what failed, and include relevant refs or error messages.\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\nimport { issueLogXrefTemplate } from './issue-log-xref.js';\nimport { browserTestingTemplate } from './browser-testing.js';\n\n// SkillTemplate kept file-local (defined in issue-log-xref.ts)\n\nexport const SKILL_TEMPLATES: Record<string, SkillTemplate> = {\n 'issue-log-xref': issueLogXrefTemplate,\n 'browser-testing': browserTestingTemplate,\n};\n","import { SKILL_TEMPLATES } from \"./skill-templates/index.js\";\n\nfunction printSkillHelp() {\n const lines = [\n \"Usage: canary skill <name>\",\n \"\",\n \"Output an AI agent skill template to stdout.\",\n \"Install a skill:\",\n \" mkdir -p .claude/skills/<name> && canary skill <name> > .claude/skills/<name>/SKILL.md\",\n \"\",\n \"Available templates:\",\n ];\n\n for (const [key, template] of Object.entries(SKILL_TEMPLATES)) {\n lines.push(` ${key.padEnd(24)} ${template.description}`);\n }\n\n console.log(lines.join(\"\\n\"));\n}\n\nexport async function runSkill(argv: string[]) {\n const [name] = argv;\n\n if (!name || name === \"help\" || name === \"--help\" || name === \"-h\") {\n printSkillHelp();\n return;\n }\n\n const template = SKILL_TEMPLATES[name];\n if (!template) {\n console.error(`Unknown skill template \"${name}\".\\n`);\n printSkillHelp();\n process.exit(1);\n }\n\n process.stdout.write(template.render());\n}\n"],"mappings":"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","names":[]}
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
detectModalFromYaml,
|
|
30
30
|
detectPaginationInfo,
|
|
31
31
|
dispatchBrowserTool,
|
|
32
|
+
dispatchConsolidatedBrowserTool,
|
|
32
33
|
downsampleImage,
|
|
33
34
|
ensureAllowedHost,
|
|
34
35
|
estimateTextTokens,
|
|
@@ -54,6 +55,8 @@ import {
|
|
|
54
55
|
formatSemanticSnapshot,
|
|
55
56
|
getBrowserToolDefinitions,
|
|
56
57
|
getBrowserToolDefinitionsWithLifecycle,
|
|
58
|
+
getConsolidatedBrowserToolDefinitions,
|
|
59
|
+
getConsolidatedBrowserToolDefinitionsWithLifecycle,
|
|
57
60
|
gridInfoToRows,
|
|
58
61
|
guessMimeType,
|
|
59
62
|
hasDiffChanges,
|
|
@@ -73,7 +76,7 @@ import {
|
|
|
73
76
|
sortMatchesByContext,
|
|
74
77
|
withRecoveryNotice,
|
|
75
78
|
withRecoveryNoticeMCP
|
|
76
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-IFOJT3A5.js";
|
|
77
80
|
import "./chunk-XAA5VQ5N.js";
|
|
78
81
|
import {
|
|
79
82
|
consoleLogger,
|
|
@@ -113,6 +116,7 @@ export {
|
|
|
113
116
|
detectModalFromYaml,
|
|
114
117
|
detectPaginationInfo,
|
|
115
118
|
dispatchBrowserTool,
|
|
119
|
+
dispatchConsolidatedBrowserTool,
|
|
116
120
|
downsampleImage,
|
|
117
121
|
ensureAllowedHost,
|
|
118
122
|
estimateTextTokens,
|
|
@@ -140,6 +144,8 @@ export {
|
|
|
140
144
|
formatSemanticSnapshot,
|
|
141
145
|
getBrowserToolDefinitions,
|
|
142
146
|
getBrowserToolDefinitionsWithLifecycle,
|
|
147
|
+
getConsolidatedBrowserToolDefinitions,
|
|
148
|
+
getConsolidatedBrowserToolDefinitionsWithLifecycle,
|
|
143
149
|
gridInfoToRows,
|
|
144
150
|
guessMimeType,
|
|
145
151
|
hasDiffChanges,
|
|
@@ -161,4 +167,4 @@ export {
|
|
|
161
167
|
withRecoveryNotice,
|
|
162
168
|
withRecoveryNoticeMCP
|
|
163
169
|
};
|
|
164
|
-
//# sourceMappingURL=src-
|
|
170
|
+
//# sourceMappingURL=src-F7LQ5PY2.js.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
downloadStorageState,
|
|
4
|
+
fetchList,
|
|
5
|
+
promptChoice,
|
|
6
|
+
selectProperty
|
|
7
|
+
} from "./chunk-BOS2YLKH.js";
|
|
8
|
+
import {
|
|
9
|
+
createSession
|
|
10
|
+
} from "./chunk-Z3F373YR.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveConfig
|
|
13
|
+
} from "./chunk-ACRIE2YR.js";
|
|
14
|
+
import "./chunk-XAA5VQ5N.js";
|
|
15
|
+
import "./chunk-VKVL7WBN.js";
|
|
16
|
+
|
|
17
|
+
// src/local/start.ts
|
|
18
|
+
import process from "process";
|
|
19
|
+
async function runLocalStart(argv) {
|
|
20
|
+
const config = await resolveConfig(argv);
|
|
21
|
+
const property = await selectProperty(config.apiUrl, config.token);
|
|
22
|
+
if (!property) {
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const envs = await fetchList(
|
|
27
|
+
config.apiUrl,
|
|
28
|
+
config.token,
|
|
29
|
+
`/org/properties/${property.id}/environments/local`,
|
|
30
|
+
"items"
|
|
31
|
+
);
|
|
32
|
+
if (envs.length === 0) {
|
|
33
|
+
console.error("No local environments found. Run `canary local init` first.");
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let selectedEnv;
|
|
38
|
+
if (envs.length === 1) {
|
|
39
|
+
selectedEnv = envs[0];
|
|
40
|
+
} else {
|
|
41
|
+
const picked = await promptChoice(
|
|
42
|
+
"Select a local environment:",
|
|
43
|
+
envs.map((e) => ({ label: `${e.name} \u2014 ${e.url}`, value: e }))
|
|
44
|
+
);
|
|
45
|
+
if (!picked) {
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
selectedEnv = picked;
|
|
50
|
+
}
|
|
51
|
+
const allCredentials = await fetchList(
|
|
52
|
+
config.apiUrl,
|
|
53
|
+
config.token,
|
|
54
|
+
"/org/credentials",
|
|
55
|
+
"items"
|
|
56
|
+
);
|
|
57
|
+
const envCredentials = allCredentials.filter(
|
|
58
|
+
(c) => c.propertyId === property.id
|
|
59
|
+
);
|
|
60
|
+
let storageStatePath;
|
|
61
|
+
let credentialName;
|
|
62
|
+
if (envCredentials.length > 0) {
|
|
63
|
+
let credential;
|
|
64
|
+
if (envCredentials.length === 1) {
|
|
65
|
+
credential = envCredentials[0];
|
|
66
|
+
} else {
|
|
67
|
+
const picked = await promptChoice(
|
|
68
|
+
"Select a credential:",
|
|
69
|
+
envCredentials.map((c) => ({ label: c.name, value: c }))
|
|
70
|
+
);
|
|
71
|
+
if (!picked) {
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
credential = picked;
|
|
76
|
+
}
|
|
77
|
+
credentialName = credential.name;
|
|
78
|
+
if (credential.storageStateS3Key) {
|
|
79
|
+
console.log("Loading saved login state...");
|
|
80
|
+
storageStatePath = await downloadStorageState({
|
|
81
|
+
apiUrl: config.apiUrl,
|
|
82
|
+
token: config.token,
|
|
83
|
+
propertyId: property.id,
|
|
84
|
+
credentialId: credential.id,
|
|
85
|
+
prefix: "local"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
console.log(`Starting session at ${selectedEnv.url}...`);
|
|
90
|
+
const result = await createSession({
|
|
91
|
+
name: `local-${property.name}`,
|
|
92
|
+
url: selectedEnv.url,
|
|
93
|
+
storageStatePath,
|
|
94
|
+
credentialName
|
|
95
|
+
});
|
|
96
|
+
if (!result.ok || !result.data) {
|
|
97
|
+
console.error(`Failed to start session: ${result.error ?? "Unknown error"}`);
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
console.log(`
|
|
102
|
+
Session started: ${result.data.id}`);
|
|
103
|
+
console.log(` URL: ${selectedEnv.url}`);
|
|
104
|
+
if (credentialName) console.log(` Credential: ${credentialName}`);
|
|
105
|
+
if (storageStatePath) console.log(` Login state: loaded`);
|
|
106
|
+
console.log(`
|
|
107
|
+
Use \`canary session\` commands to interact with the browser.`);
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
runLocalStart
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=start-ZOMUD6LW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/local/start.ts"],"sourcesContent":["/**\n * `canary local start` — Start a headed session for a local environment.\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { resolveConfig } from '../auth.js';\nimport {\n selectProperty,\n promptChoice,\n fetchList,\n downloadStorageState,\n} from '../cli-helpers.js';\nimport { createSession } from '../session/daemon-client.js';\nimport type { CredentialListItem } from '../cli-helpers.js';\n\ninterface LocalEnvironment {\n id: string;\n name: string;\n url: string;\n propertyId: string;\n}\n\nexport async function runLocalStart(argv: string[]) {\n const config = await resolveConfig(argv);\n\n // Select property\n const property = await selectProperty(config.apiUrl, config.token);\n if (!property) {\n process.exitCode = 1;\n return;\n }\n\n // Get local environments for this property\n const envs = await fetchList<LocalEnvironment>(\n config.apiUrl,\n config.token,\n `/org/properties/${property.id}/environments/local`,\n 'items'\n );\n\n if (envs.length === 0) {\n console.error('No local environments found. Run `canary local init` first.');\n process.exitCode = 1;\n return;\n }\n\n let selectedEnv: LocalEnvironment;\n if (envs.length === 1) {\n selectedEnv = envs[0];\n } else {\n const picked = await promptChoice(\n 'Select a local environment:',\n envs.map((e) => ({ label: `${e.name} — ${e.url}`, value: e }))\n );\n if (!picked) {\n process.exitCode = 1;\n return;\n }\n selectedEnv = picked;\n }\n\n // Look up credentials for this environment\n const allCredentials = await fetchList<CredentialListItem>(\n config.apiUrl,\n config.token,\n '/org/credentials',\n 'items'\n );\n\n const envCredentials = allCredentials.filter(\n (c) => c.propertyId === property.id\n );\n\n let storageStatePath: string | undefined;\n let credentialName: string | undefined;\n\n if (envCredentials.length > 0) {\n // Pick a credential if multiple\n let credential: CredentialListItem;\n if (envCredentials.length === 1) {\n credential = envCredentials[0];\n } else {\n const picked = await promptChoice(\n 'Select a credential:',\n envCredentials.map((c) => ({ label: c.name, value: c }))\n );\n if (!picked) {\n process.exitCode = 1;\n return;\n }\n credential = picked;\n }\n\n credentialName = credential.name;\n\n // Download storage state if available\n if (credential.storageStateS3Key) {\n console.log('Loading saved login state...');\n storageStatePath = await downloadStorageState({\n apiUrl: config.apiUrl,\n token: config.token,\n propertyId: property.id,\n credentialId: credential.id,\n prefix: 'local',\n });\n }\n }\n\n // Create headed session\n console.log(`Starting session at ${selectedEnv.url}...`);\n const result = await createSession({\n name: `local-${property.name}`,\n url: selectedEnv.url,\n storageStatePath,\n credentialName,\n });\n\n if (!result.ok || !result.data) {\n console.error(`Failed to start session: ${result.error ?? 'Unknown error'}`);\n process.exitCode = 1;\n return;\n }\n\n console.log(`\\nSession started: ${result.data.id}`);\n console.log(` URL: ${selectedEnv.url}`);\n if (credentialName) console.log(` Credential: ${credentialName}`);\n if (storageStatePath) console.log(` Login state: loaded`);\n console.log(`\\nUse \\`canary session\\` commands to interact with the browser.`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAMA,OAAO,aAAa;AAkBpB,eAAsB,cAAc,MAAgB;AAClD,QAAM,SAAS,MAAM,cAAc,IAAI;AAGvC,QAAM,WAAW,MAAM,eAAe,OAAO,QAAQ,OAAO,KAAK;AACjE,MAAI,CAAC,UAAU;AACb,YAAQ,WAAW;AACnB;AAAA,EACF;AAGA,QAAM,OAAO,MAAM;AAAA,IACjB,OAAO;AAAA,IACP,OAAO;AAAA,IACP,mBAAmB,SAAS,EAAE;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,KAAK,WAAW,GAAG;AACrB,YAAQ,MAAM,6DAA6D;AAC3E,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,KAAK,WAAW,GAAG;AACrB,kBAAc,KAAK,CAAC;AAAA,EACtB,OAAO;AACL,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,EAAE,IAAI,WAAM,EAAE,GAAG,IAAI,OAAO,EAAE,EAAE;AAAA,IAC/D;AACA,QAAI,CAAC,QAAQ;AACX,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,kBAAc;AAAA,EAChB;AAGA,QAAM,iBAAiB,MAAM;AAAA,IAC3B,OAAO;AAAA,IACP,OAAO;AAAA,IACP;AAAA,IACA;AAAA,EACF;AAEA,QAAM,iBAAiB,eAAe;AAAA,IACpC,CAAC,MAAM,EAAE,eAAe,SAAS;AAAA,EACnC;AAEA,MAAI;AACJ,MAAI;AAEJ,MAAI,eAAe,SAAS,GAAG;AAE7B,QAAI;AACJ,QAAI,eAAe,WAAW,GAAG;AAC/B,mBAAa,eAAe,CAAC;AAAA,IAC/B,OAAO;AACL,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA,eAAe,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,EAAE,EAAE;AAAA,MACzD;AACA,UAAI,CAAC,QAAQ;AACX,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,mBAAa;AAAA,IACf;AAEA,qBAAiB,WAAW;AAG5B,QAAI,WAAW,mBAAmB;AAChC,cAAQ,IAAI,8BAA8B;AAC1C,yBAAmB,MAAM,qBAAqB;AAAA,QAC5C,QAAQ,OAAO;AAAA,QACf,OAAO,OAAO;AAAA,QACd,YAAY,SAAS;AAAA,QACrB,cAAc,WAAW;AAAA,QACzB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAGA,UAAQ,IAAI,uBAAuB,YAAY,GAAG,KAAK;AACvD,QAAM,SAAS,MAAM,cAAc;AAAA,IACjC,MAAM,SAAS,SAAS,IAAI;AAAA,IAC5B,KAAK,YAAY;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;AAC9B,YAAQ,MAAM,4BAA4B,OAAO,SAAS,eAAe,EAAE;AAC3E,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,mBAAsB,OAAO,KAAK,EAAE,EAAE;AAClD,UAAQ,IAAI,UAAU,YAAY,GAAG,EAAE;AACvC,MAAI,eAAgB,SAAQ,IAAI,iBAAiB,cAAc,EAAE;AACjE,MAAI,iBAAkB,SAAQ,IAAI,uBAAuB;AACzD,UAAQ,IAAI;AAAA,8DAAiE;AAC/E;","names":[]}
|
package/dist/test.js
CHANGED
package/dist/test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/test.ts","../src/runner/wrapper.ts"],"sourcesContent":["import * as playwright from \"@playwright/test\";\nimport { loadCanaryConfig } from \"./runner/config\";\nimport { getEventLog, markPatched, recordHealingEvent, setEventLogPath } from \"./runner/state\";\nimport { wrapPlaywright, type PlaywrightExports } from \"./runner/wrapper\";\n\nconst config = loadCanaryConfig();\nsetEventLogPath(config.eventLogPath);\ngetEventLog();\n\nconst patched = wrapPlaywright(playwright as unknown as PlaywrightExports, { debug: !!config.debug });\nmarkPatched();\n\nrecordHealingEvent({\n kind: \"unknown\",\n action: \"module_wrap_ready\",\n healed: false,\n});\n\nconst {\n test,\n expect,\n chromium,\n firefox,\n webkit,\n devices,\n request,\n selectors,\n defineConfig,\n} = patched as typeof import(\"@playwright/test\");\n\nexport { test, expect, chromium, firefox, webkit, devices, request, selectors, defineConfig };\nexport type * from \"@playwright/test\";\n","/**\n * Direct Playwright wrapper for the `canary/test` import path.\n *\n * Wraps `test`, `expect`, and the `page` fixture with healing-aware proxies.\n * Delegates all proxy/healing logic to `healing-helpers.ts`.\n */\n\nimport { recordHealingEvent } from
|
|
1
|
+
{"version":3,"sources":["../src/test.ts","../src/runner/wrapper.ts"],"sourcesContent":["import * as playwright from \"@playwright/test\";\nimport { loadCanaryConfig } from \"./runner/config\";\nimport { getEventLog, markPatched, recordHealingEvent, setEventLogPath } from \"./runner/state\";\nimport { wrapPlaywright, type PlaywrightExports } from \"./runner/wrapper\";\n\nconst config = loadCanaryConfig();\nsetEventLogPath(config.eventLogPath);\ngetEventLog();\n\nconst patched = wrapPlaywright(playwright as unknown as PlaywrightExports, { debug: !!config.debug });\nmarkPatched();\n\nrecordHealingEvent({\n kind: \"unknown\",\n action: \"module_wrap_ready\",\n healed: false,\n});\n\nconst {\n test,\n expect,\n chromium,\n firefox,\n webkit,\n devices,\n request,\n selectors,\n defineConfig,\n} = patched as typeof import(\"@playwright/test\");\n\nexport { test, expect, chromium, firefox, webkit, devices, request, selectors, defineConfig };\nexport type * from \"@playwright/test\";\n","/**\n * Direct Playwright wrapper for the `canary/test` import path.\n *\n * Wraps `test`, `expect`, and the `page` fixture with healing-aware proxies.\n * Delegates all proxy/healing logic to `healing-helpers.ts`.\n */\n\nimport { recordHealingEvent } from './state';\nimport { wrapExpect, wrapPage, type PlaywrightExports, type WrapOptions } from './healing-helpers';\n\nexport type { PlaywrightExports };\n\nexport function wrapPlaywright(real: PlaywrightExports, options: WrapOptions): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const patchedTest = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: 'page',\n action: 'fixture_initialized',\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: patchedTest,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (options.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright surface wrapped`);\n }\n return patched;\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,YAAY,gBAAgB;;;ACYrB,SAAS,eAAe,MAAyB,SAAyC;AAC/F,MAAK,KAAuC,gBAAiB,QAAO;AAEpE,QAAM,cAAc,KAAK,KAAK,OAAO;AAAA,IACnC,MAAM,OAAO,EAAE,KAAK,GAAG,QAAQ;AAC7B,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AACD,YAAM,cAAc,SAAS,MAAM,OAAO;AAC1C,YAAM,IAAI,WAAW;AAAA,IACvB;AAAA,EACF,CAAC;AAED,QAAMA,WAA6B;AAAA,IACjC,GAAG;AAAA,IACH,MAAM;AAAA,IACN,QAAQ,WAAW,KAAK,QAAQ,OAAO;AAAA,EACzC;AAEA,EAACA,SAA0C,kBAAkB;AAC7D,MAAI,QAAQ,OAAO;AAEjB,YAAQ,IAAI,4CAA4C;AAAA,EAC1D;AACA,SAAOA;AACT;;;ADlCA,IAAM,SAAS,iBAAiB;AAChC,gBAAgB,OAAO,YAAY;AACnC,YAAY;AAEZ,IAAM,UAAU,eAAe,YAA4C,EAAE,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC;AACpG,YAAY;AAEZ,mBAAmB;AAAA,EACjB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AACV,CAAC;AAED,IAAM;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;","names":["patched"]}
|