latchkey 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -13
- package/dist/integrations/SKILL.md +9 -2
- package/dist/package.json +1 -1
- package/dist/scripts/codegen/codeGenerator.d.ts +27 -0
- package/dist/scripts/codegen/codeGenerator.d.ts.map +1 -0
- package/dist/scripts/codegen/codeGenerator.js +220 -0
- package/dist/scripts/codegen/codeGenerator.js.map +1 -0
- package/dist/scripts/codegen/index.d.ts +27 -0
- package/dist/scripts/codegen/index.d.ts.map +1 -0
- package/dist/scripts/codegen/index.js +189 -0
- package/dist/scripts/codegen/index.js.map +1 -0
- package/dist/scripts/codegen/injectedScript.d.ts +6 -0
- package/dist/scripts/codegen/injectedScript.d.ts.map +1 -0
- package/dist/scripts/codegen/injectedScript.js +657 -0
- package/dist/scripts/codegen/injectedScript.js.map +1 -0
- package/dist/scripts/codegen/requestMetadataCollector.d.ts +15 -0
- package/dist/scripts/codegen/requestMetadataCollector.d.ts.map +1 -0
- package/dist/scripts/codegen/requestMetadataCollector.js +48 -0
- package/dist/scripts/codegen/requestMetadataCollector.js.map +1 -0
- package/dist/scripts/codegen/types.d.ts +77 -0
- package/dist/scripts/codegen/types.d.ts.map +1 -0
- package/dist/scripts/codegen/types.js +10 -0
- package/dist/scripts/codegen/types.js.map +1 -0
- package/dist/scripts/codegen.d.ts +24 -0
- package/dist/scripts/codegen.d.ts.map +1 -0
- package/dist/scripts/codegen.js +95 -0
- package/dist/scripts/codegen.js.map +1 -0
- package/dist/scripts/cryptFile.js +7 -2
- package/dist/scripts/cryptFile.js.map +1 -1
- package/dist/src/apiCredentialStore.d.ts +1 -1
- package/dist/src/apiCredentialStore.d.ts.map +1 -1
- package/dist/src/apiCredentialStore.js +1 -1
- package/dist/src/apiCredentialStore.js.map +1 -1
- package/dist/src/apiCredentials.d.ts +11 -114
- package/dist/src/apiCredentials.d.ts.map +1 -1
- package/dist/src/apiCredentials.js +9 -101
- package/dist/src/apiCredentials.js.map +1 -1
- package/dist/src/apiCredentialsSerialization.d.ts +119 -0
- package/dist/src/apiCredentialsSerialization.d.ts.map +1 -0
- package/dist/src/apiCredentialsSerialization.js +90 -0
- package/dist/src/apiCredentialsSerialization.js.map +1 -0
- package/dist/src/browserConfig.d.ts +1 -5
- package/dist/src/browserConfig.d.ts.map +1 -1
- package/dist/src/browserConfig.js +2 -8
- package/dist/src/browserConfig.js.map +1 -1
- package/dist/src/cli.js +20 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/cliCommands.d.ts +0 -1
- package/dist/src/cliCommands.d.ts.map +1 -1
- package/dist/src/cliCommands.js +50 -40
- package/dist/src/cliCommands.js.map +1 -1
- package/dist/src/config.d.ts +4 -3
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +17 -29
- package/dist/src/config.js.map +1 -1
- package/dist/src/curl.d.ts +10 -0
- package/dist/src/curl.d.ts.map +1 -1
- package/dist/src/curl.js +88 -0
- package/dist/src/curl.js.map +1 -1
- package/dist/src/encryptedStorage.d.ts +10 -2
- package/dist/src/encryptedStorage.d.ts.map +1 -1
- package/dist/src/encryptedStorage.js +30 -38
- package/dist/src/encryptedStorage.js.map +1 -1
- package/dist/src/index.d.ts +5 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/keychain.d.ts +0 -4
- package/dist/src/keychain.d.ts.map +1 -1
- package/dist/src/keychain.js +0 -13
- package/dist/src/keychain.js.map +1 -1
- package/dist/src/migrations.d.ts +9 -0
- package/dist/src/migrations.d.ts.map +1 -0
- package/dist/src/migrations.js +77 -0
- package/dist/src/migrations.js.map +1 -0
- package/dist/src/oauthUtils.d.ts +4 -1
- package/dist/src/oauthUtils.d.ts.map +1 -1
- package/dist/src/oauthUtils.js.map +1 -1
- package/dist/src/playwrightUtils.d.ts +2 -2
- package/dist/src/playwrightUtils.d.ts.map +1 -1
- package/dist/src/playwrightUtils.js +4 -4
- package/dist/src/playwrightUtils.js.map +1 -1
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +18 -2
- package/dist/src/registry.js.map +1 -1
- package/dist/src/services/aws.d.ts +44 -0
- package/dist/src/services/aws.d.ts.map +1 -0
- package/dist/src/services/aws.js +237 -0
- package/dist/src/services/aws.js.map +1 -0
- package/dist/src/services/base.d.ts +14 -0
- package/dist/src/services/base.d.ts.map +1 -1
- package/dist/src/services/base.js +23 -11
- package/dist/src/services/base.js.map +1 -1
- package/dist/src/services/calendly.d.ts +12 -0
- package/dist/src/services/calendly.d.ts.map +1 -0
- package/dist/src/services/calendly.js +18 -0
- package/dist/src/services/calendly.js.map +1 -0
- package/dist/src/services/discord.d.ts +1 -0
- package/dist/src/services/discord.d.ts.map +1 -1
- package/dist/src/services/discord.js +3 -0
- package/dist/src/services/discord.js.map +1 -1
- package/dist/src/services/dropbox.d.ts +2 -1
- package/dist/src/services/dropbox.d.ts.map +1 -1
- package/dist/src/services/dropbox.js +5 -0
- package/dist/src/services/dropbox.js.map +1 -1
- package/dist/src/services/figma.d.ts +12 -0
- package/dist/src/services/figma.d.ts.map +1 -0
- package/dist/src/services/figma.js +14 -0
- package/dist/src/services/figma.js.map +1 -0
- package/dist/src/services/github.d.ts +1 -0
- package/dist/src/services/github.d.ts.map +1 -1
- package/dist/src/services/github.js +3 -0
- package/dist/src/services/github.js.map +1 -1
- package/dist/src/services/gitlab.d.ts +12 -0
- package/dist/src/services/gitlab.d.ts.map +1 -0
- package/dist/src/services/gitlab.js +14 -0
- package/dist/src/services/gitlab.js.map +1 -0
- package/dist/src/services/google/analytics.d.ts +11 -0
- package/dist/src/services/google/analytics.d.ts.map +1 -0
- package/dist/src/services/google/analytics.js +22 -0
- package/dist/src/services/google/analytics.js.map +1 -0
- package/dist/src/services/google/base.d.ts +73 -0
- package/dist/src/services/google/base.d.ts.map +1 -0
- package/dist/src/services/google/base.js +323 -0
- package/dist/src/services/google/base.js.map +1 -0
- package/dist/src/services/google/calendar.d.ts +11 -0
- package/dist/src/services/google/calendar.d.ts.map +1 -0
- package/dist/src/services/google/calendar.js +22 -0
- package/dist/src/services/google/calendar.js.map +1 -0
- package/dist/src/services/google/directions.d.ts +14 -0
- package/dist/src/services/google/directions.d.ts.map +1 -0
- package/dist/src/services/google/directions.js +49 -0
- package/dist/src/services/google/directions.js.map +1 -0
- package/dist/src/services/google/docs.d.ts +11 -0
- package/dist/src/services/google/docs.d.ts.map +1 -0
- package/dist/src/services/google/docs.js +19 -0
- package/dist/src/services/google/docs.js.map +1 -0
- package/dist/src/services/google/drive.d.ts +11 -0
- package/dist/src/services/google/drive.d.ts.map +1 -0
- package/dist/src/services/google/drive.js +19 -0
- package/dist/src/services/google/drive.js.map +1 -0
- package/dist/src/services/google/gmail.d.ts +11 -0
- package/dist/src/services/google/gmail.d.ts.map +1 -0
- package/dist/src/services/google/gmail.js +24 -0
- package/dist/src/services/google/gmail.js.map +1 -0
- package/dist/src/services/google/maps.d.ts +39 -0
- package/dist/src/services/google/maps.d.ts.map +1 -0
- package/dist/src/services/google/maps.js +94 -0
- package/dist/src/services/google/maps.js.map +1 -0
- package/dist/src/services/google/people.d.ts +11 -0
- package/dist/src/services/google/people.d.ts.map +1 -0
- package/dist/src/services/google/people.js +22 -0
- package/dist/src/services/google/people.js.map +1 -0
- package/dist/src/services/google/sheets.d.ts +11 -0
- package/dist/src/services/google/sheets.d.ts.map +1 -0
- package/dist/src/services/google/sheets.js +19 -0
- package/dist/src/services/google/sheets.js.map +1 -0
- package/dist/src/services/googleAnalytics.d.ts +11 -0
- package/dist/src/services/googleAnalytics.d.ts.map +1 -0
- package/dist/src/services/googleAnalytics.js +18 -0
- package/dist/src/services/googleAnalytics.js.map +1 -0
- package/dist/src/services/googleMaps.d.ts +12 -0
- package/dist/src/services/googleMaps.d.ts.map +1 -0
- package/dist/src/services/googleMaps.js +17 -0
- package/dist/src/services/googleMaps.js.map +1 -0
- package/dist/src/services/index.d.ts +19 -2
- package/dist/src/services/index.d.ts.map +1 -1
- package/dist/src/services/index.js +19 -2
- package/dist/src/services/index.js.map +1 -1
- package/dist/src/services/linear.d.ts +1 -0
- package/dist/src/services/linear.d.ts.map +1 -1
- package/dist/src/services/linear.js +3 -0
- package/dist/src/services/linear.js.map +1 -1
- package/dist/src/services/mailchimp.d.ts +2 -1
- package/dist/src/services/mailchimp.d.ts.map +1 -1
- package/dist/src/services/mailchimp.js +4 -3
- package/dist/src/services/mailchimp.js.map +1 -1
- package/dist/src/services/notion.d.ts +1 -0
- package/dist/src/services/notion.d.ts.map +1 -1
- package/dist/src/services/notion.js +3 -0
- package/dist/src/services/notion.js.map +1 -1
- package/dist/src/services/sentry.d.ts +14 -0
- package/dist/src/services/sentry.d.ts.map +1 -0
- package/dist/src/services/sentry.js +43 -0
- package/dist/src/services/sentry.js.map +1 -0
- package/dist/src/services/slack.d.ts +30 -1
- package/dist/src/services/slack.d.ts.map +1 -1
- package/dist/src/services/slack.js +45 -2
- package/dist/src/services/slack.js.map +1 -1
- package/dist/src/services/stripe.d.ts +12 -0
- package/dist/src/services/stripe.d.ts.map +1 -0
- package/dist/src/services/stripe.js +14 -0
- package/dist/src/services/stripe.js.map +1 -0
- package/dist/src/services/telegram.d.ts +40 -0
- package/dist/src/services/telegram.d.ts.map +1 -0
- package/dist/src/services/telegram.js +73 -0
- package/dist/src/services/telegram.js.map +1 -0
- package/dist/src/services/yelp.d.ts +12 -0
- package/dist/src/services/yelp.d.ts.map +1 -0
- package/dist/src/services/yelp.js +16 -0
- package/dist/src/services/yelp.js.map +1 -0
- package/dist/src/services/zoom.d.ts +12 -0
- package/dist/src/services/zoom.d.ts.map +1 -0
- package/dist/src/services/zoom.js +18 -0
- package/dist/src/services/zoom.js.map +1 -0
- package/dist/tests/apiCredentialStore.test.js +2 -19
- package/dist/tests/apiCredentialStore.test.js.map +1 -1
- package/dist/tests/apiCredentials.test.js +139 -178
- package/dist/tests/apiCredentials.test.js.map +1 -1
- package/dist/tests/cli.test.js +160 -260
- package/dist/tests/cli.test.js.map +1 -1
- package/dist/tests/encryptedStorage.test.js +0 -4
- package/dist/tests/encryptedStorage.test.js.map +1 -1
- package/dist/tests/encryptedStorageKeyGeneration.test.d.ts +2 -0
- package/dist/tests/encryptedStorageKeyGeneration.test.d.ts.map +1 -0
- package/dist/tests/encryptedStorageKeyGeneration.test.js +22 -0
- package/dist/tests/encryptedStorageKeyGeneration.test.js.map +1 -0
- package/dist/tests/encryption.test.js +3 -35
- package/dist/tests/encryption.test.js.map +1 -1
- package/dist/tests/migrations.test.d.ts +2 -0
- package/dist/tests/migrations.test.d.ts.map +1 -0
- package/dist/tests/migrations.test.js +164 -0
- package/dist/tests/migrations.test.js.map +1 -0
- package/dist/tests/playwrightDownload.test.js +2 -65
- package/dist/tests/playwrightDownload.test.js.map +1 -1
- package/dist/tests/registry.test.js +49 -75
- package/dist/tests/registry.test.js.map +1 -1
- package/dist/tests/servicesAgainstRecordings.test.js +1 -1
- package/dist/tests/servicesAgainstRecordings.test.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@ Inject API credentials into local agent requests.
|
|
|
5
5
|
## Quick example
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
+
# User stores the credentials.
|
|
9
|
+
latchkey auth set slack -H "Authorization: Bearer xoxb-your-token"
|
|
10
|
+
|
|
11
|
+
# Agent makes http calls.
|
|
8
12
|
latchkey curl -X POST 'https://slack.com/api/conversations.create' \
|
|
9
13
|
-H 'Content-Type: application/json' \
|
|
10
14
|
-d '{"name":"something-urgent"}'
|
|
@@ -16,14 +20,15 @@ Latchkey is a command-line tool that injects credentials into curl
|
|
|
16
20
|
requests to known public APIs.
|
|
17
21
|
|
|
18
22
|
- `latchkey services list`
|
|
19
|
-
-
|
|
23
|
+
- List supported third-party services (Slack, Google Workspace, Linear, GitHub, etc.).
|
|
20
24
|
- `latchkey curl <arguments>`
|
|
21
25
|
- Automatically inject credentials to your otherwise standard curl calls to public APIs.
|
|
22
26
|
- Credentials must already exist (see below).
|
|
23
27
|
- `latchkey auth set <service_name> <curl_arguments>`
|
|
24
|
-
- Manually store credentials for a service
|
|
28
|
+
- Manually store credentials for a service as arbitrary curl arguments.
|
|
25
29
|
- `latchkey auth browser <service_name>`
|
|
26
30
|
- Open a browser login pop-up window and store the resulting API credentials.
|
|
31
|
+
- This also allows agents to prompt users for credentials.
|
|
27
32
|
- Only some services support this option.
|
|
28
33
|
|
|
29
34
|
Latchkey is primarily designed for AI agents. By invoking
|
|
@@ -134,12 +139,29 @@ so you can use the same interface you are used to. The return
|
|
|
134
139
|
code, stdout and stderr are passed back from curl to the caller
|
|
135
140
|
of `latchkey`.
|
|
136
141
|
|
|
142
|
+
### Indirect credentials
|
|
143
|
+
|
|
144
|
+
Some services can't express their credentials as static curl
|
|
145
|
+
arguments. For example:
|
|
146
|
+
|
|
147
|
+
- AWS requires a signature that changes with each request.
|
|
148
|
+
- Telegram expects bot tokens to be directly part of the URL.
|
|
149
|
+
|
|
150
|
+
In similar cases, when supported, you can use the `latchkey auth set-nocurl` command, e.g.
|
|
151
|
+
like this:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
latchkey auth set-nocurl telegram <bot-token>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Latchkey will then modify subsequent `latchkey curl` requests as
|
|
158
|
+
needed. You can find more information (including the expected
|
|
159
|
+
signature) by calling `latchkey services info <service_name>`.
|
|
160
|
+
|
|
137
161
|
### Remembering API credentials
|
|
138
162
|
|
|
139
|
-
Your API credentials and browser state are
|
|
140
|
-
under `~/.latchkey`.
|
|
141
|
-
(which is the case on most systems), the data is properly
|
|
142
|
-
encrypted.
|
|
163
|
+
Your API credentials and browser state are encrypted and stored
|
|
164
|
+
by default under `~/.latchkey`.
|
|
143
165
|
|
|
144
166
|
|
|
145
167
|
### Inspecting the status of stored credentials
|
|
@@ -197,15 +219,10 @@ latchkey auth clear
|
|
|
197
219
|
You can set these environment variables to override certain
|
|
198
220
|
defaults:
|
|
199
221
|
|
|
200
|
-
- `
|
|
201
|
-
containing stored API credentials
|
|
202
|
-
- `LATCHKEY_BROWSER_STATE`: path to the (typically encrypted) file
|
|
203
|
-
containing the state (cookies, local storage, etc.) of
|
|
204
|
-
the browser used for the login popup
|
|
222
|
+
- `LATCHKEY_DIRECTORY`: path to the directory where Latchkey stores its data (defaults to `~/.latchkey`)
|
|
205
223
|
- `LATCHKEY_CURL`: path to the curl binary
|
|
206
|
-
- `LATCHKEY_CONFIG`: path to the configuration file
|
|
207
|
-
(defaults to `~/.latchkey/config.json`)
|
|
208
224
|
- `LATCHKEY_KEYRING_SERVICE_NAME`, `LATCHKEY_KEYRING_ACCOUNT_NAME`: identifiers that are used to store the encryption password in your keyring
|
|
225
|
+
- `LATCHKEY_ENCRYPTION_KEY`: override the encryption key, e.g. when a keyring is not available. Example: `export LATCHKEY_ENCRYPTION_KEY="$(openssl rand -base64 32)"`
|
|
209
226
|
- `LATCHKEY_DISABLE_BROWSER`: when set (to any non-empty value), disables the browser login flow; commands that would trigger a browser login (`auth browser`, `auth browser-prepare`) will fail with an error instead
|
|
210
227
|
|
|
211
228
|
|
|
@@ -218,3 +235,10 @@ the browser used for the login popup
|
|
|
218
235
|
- Using agents for automated access may be prohibited by some services' ToS.
|
|
219
236
|
- We reserve the right to change the license of future releases of Latchkey.
|
|
220
237
|
- Latchkey was not tested on Windows.
|
|
238
|
+
|
|
239
|
+
## Currently supported services
|
|
240
|
+
|
|
241
|
+
Latchkey currently offers varying levels of support for the
|
|
242
|
+
following services: AWS, Calendly, Discord, Dropbox, Figma, GitHub, GitLab,
|
|
243
|
+
Gmail, Google Analytics, Google Calendar, Google Docs, Google Drive, Google Sheets,
|
|
244
|
+
Linear, Mailchimp, Notion, Sentry, Slack, Stripe, Telegram, Yelp, Zoom, and more.
|
|
@@ -68,10 +68,17 @@ latchkey services info slack
|
|
|
68
68
|
Returns auth options, credentials status, and developer notes
|
|
69
69
|
about the service. If `browser` is not present in the
|
|
70
70
|
`authOptions` field, the service requires the user to directly
|
|
71
|
-
set API credentials via `latchkey auth set`
|
|
72
|
-
requests.
|
|
71
|
+
set API credentials via `latchkey auth set` or `latchkey auth
|
|
72
|
+
set-nocurl` before making requests.
|
|
73
73
|
|
|
74
74
|
## Notes
|
|
75
75
|
|
|
76
76
|
- All curl arguments are passed through unchanged
|
|
77
77
|
- Return code, stdout and stderr are passed back from curl
|
|
78
|
+
|
|
79
|
+
## Currently supported services
|
|
80
|
+
|
|
81
|
+
Latchkey currently offers varying levels of support for the
|
|
82
|
+
following services: AWS, Calendly, Discord, Dropbox, Figma, GitHub, GitLab,
|
|
83
|
+
Gmail, Google Analytics, Google Calendar, Google Docs, Google Drive, Google Sheets,
|
|
84
|
+
Linear, Mailchimp, Notion, Sentry, Slack, Stripe, Telegram, Yelp, Zoom, and more.
|
package/dist/package.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates TypeScript code from recorded actions.
|
|
3
|
+
* Uses page.getByRole where possible and outputs element ancestry for AI post-processing.
|
|
4
|
+
*/
|
|
5
|
+
import type { ElementInfo, RecordedAction } from './types.js';
|
|
6
|
+
export declare class CodeGenerator {
|
|
7
|
+
private readonly actions;
|
|
8
|
+
private readonly outputPath;
|
|
9
|
+
private actionCounter;
|
|
10
|
+
private initialUrl;
|
|
11
|
+
private apiKeyAncestry;
|
|
12
|
+
constructor(outputPath: string);
|
|
13
|
+
setInitialUrl(url: string): void;
|
|
14
|
+
addAction(action: RecordedAction): void;
|
|
15
|
+
setApiKeyAncestry(ancestry: ElementInfo[]): void;
|
|
16
|
+
private escapeString;
|
|
17
|
+
private generateGetByRole;
|
|
18
|
+
private generateGetByPlaceholder;
|
|
19
|
+
private generateGetByLabel;
|
|
20
|
+
private generateLocator;
|
|
21
|
+
private formatElementInfo;
|
|
22
|
+
private formatAncestryComments;
|
|
23
|
+
private generateActionCode;
|
|
24
|
+
generateCode(): string;
|
|
25
|
+
flush(): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=codeGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeGenerator.d.ts","sourceRoot":"","sources":["../../../scripts/codegen/codeGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,cAAc,CAA4B;gBAEtC,UAAU,EAAE,MAAM;IAI9B,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAKhC,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKvC,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAKhD,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,sBAAsB;IAkB9B,OAAO,CAAC,kBAAkB;IAuD1B,YAAY,IAAI,MAAM;IAwDtB,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates TypeScript code from recorded actions.
|
|
3
|
+
* Uses page.getByRole where possible and outputs element ancestry for AI post-processing.
|
|
4
|
+
*/
|
|
5
|
+
import { writeFileSync } from 'node:fs';
|
|
6
|
+
export class CodeGenerator {
|
|
7
|
+
actions = [];
|
|
8
|
+
outputPath;
|
|
9
|
+
actionCounter = 0;
|
|
10
|
+
initialUrl;
|
|
11
|
+
apiKeyAncestry;
|
|
12
|
+
constructor(outputPath) {
|
|
13
|
+
this.outputPath = outputPath;
|
|
14
|
+
}
|
|
15
|
+
setInitialUrl(url) {
|
|
16
|
+
this.initialUrl = url;
|
|
17
|
+
this.flush();
|
|
18
|
+
}
|
|
19
|
+
addAction(action) {
|
|
20
|
+
this.actions.push(action);
|
|
21
|
+
this.flush();
|
|
22
|
+
}
|
|
23
|
+
setApiKeyAncestry(ancestry) {
|
|
24
|
+
this.apiKeyAncestry = ancestry;
|
|
25
|
+
this.flush();
|
|
26
|
+
}
|
|
27
|
+
escapeString(str) {
|
|
28
|
+
return str
|
|
29
|
+
.replace(/\\/g, '\\\\')
|
|
30
|
+
.replace(/'/g, "\\'")
|
|
31
|
+
.replace(/\n/g, '\\n')
|
|
32
|
+
.replace(/\r/g, '\\r');
|
|
33
|
+
}
|
|
34
|
+
generateGetByRole(element) {
|
|
35
|
+
if (!element.role)
|
|
36
|
+
return null;
|
|
37
|
+
const role = element.role;
|
|
38
|
+
const name = element.accessibleName;
|
|
39
|
+
if (name) {
|
|
40
|
+
return `page.getByRole('${role}', { name: '${this.escapeString(name)}' })`;
|
|
41
|
+
}
|
|
42
|
+
return `page.getByRole('${role}')`;
|
|
43
|
+
}
|
|
44
|
+
generateGetByPlaceholder(element) {
|
|
45
|
+
if (!element.placeholder)
|
|
46
|
+
return null;
|
|
47
|
+
return `page.getByPlaceholder('${this.escapeString(element.placeholder)}')`;
|
|
48
|
+
}
|
|
49
|
+
generateGetByLabel(element) {
|
|
50
|
+
if (!element.accessibleName)
|
|
51
|
+
return null;
|
|
52
|
+
if (element.tag !== 'input' && element.tag !== 'textarea' && element.tag !== 'select')
|
|
53
|
+
return null;
|
|
54
|
+
return `page.getByLabel('${this.escapeString(element.accessibleName)}')`;
|
|
55
|
+
}
|
|
56
|
+
generateLocator(target) {
|
|
57
|
+
// Try getByRole first (Playwright best practice)
|
|
58
|
+
let locator = this.generateGetByRole(target);
|
|
59
|
+
if (locator) {
|
|
60
|
+
return { locator, strategy: 'getByRole' };
|
|
61
|
+
}
|
|
62
|
+
// Try getByPlaceholder
|
|
63
|
+
locator = this.generateGetByPlaceholder(target);
|
|
64
|
+
if (locator) {
|
|
65
|
+
return { locator, strategy: 'getByPlaceholder' };
|
|
66
|
+
}
|
|
67
|
+
// Try getByLabel
|
|
68
|
+
locator = this.generateGetByLabel(target);
|
|
69
|
+
if (locator) {
|
|
70
|
+
return { locator, strategy: 'getByLabel' };
|
|
71
|
+
}
|
|
72
|
+
// Fallback to locator with a simple selector
|
|
73
|
+
if (target.id) {
|
|
74
|
+
return { locator: `page.locator('#${target.id}')`, strategy: 'id' };
|
|
75
|
+
}
|
|
76
|
+
if (target.className) {
|
|
77
|
+
const firstClass = target.className.split(/\s+/)[0] ?? '';
|
|
78
|
+
return { locator: `page.locator('.${firstClass}')`, strategy: 'class' };
|
|
79
|
+
}
|
|
80
|
+
return { locator: `page.locator('${target.tag}')`, strategy: 'tag' };
|
|
81
|
+
}
|
|
82
|
+
formatElementInfo(element) {
|
|
83
|
+
const parts = [`tag: ${element.tag}`];
|
|
84
|
+
if (element.id) {
|
|
85
|
+
parts.push(`id: "${element.id}"`);
|
|
86
|
+
}
|
|
87
|
+
if (element.className) {
|
|
88
|
+
parts.push(`class: "${element.className}"`);
|
|
89
|
+
}
|
|
90
|
+
if (element.name) {
|
|
91
|
+
parts.push(`name: "${element.name}"`);
|
|
92
|
+
}
|
|
93
|
+
if (element.role) {
|
|
94
|
+
parts.push(`role: ${element.role}`);
|
|
95
|
+
}
|
|
96
|
+
if (element.accessibleName) {
|
|
97
|
+
parts.push(`accessibleName: "${this.escapeString(element.accessibleName)}"`);
|
|
98
|
+
}
|
|
99
|
+
if (element.inputType) {
|
|
100
|
+
parts.push(`type: ${element.inputType}`);
|
|
101
|
+
}
|
|
102
|
+
if (element.placeholder) {
|
|
103
|
+
parts.push(`placeholder: "${this.escapeString(element.placeholder)}"`);
|
|
104
|
+
}
|
|
105
|
+
return `{ ${parts.join(', ')} }`;
|
|
106
|
+
}
|
|
107
|
+
formatAncestryComments(ancestry) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
lines.push(` // Element ancestry (root -> target):`);
|
|
110
|
+
// Output ancestry in reverse order (root first, target last)
|
|
111
|
+
for (let i = ancestry.length - 1; i >= 0; i--) {
|
|
112
|
+
const element = ancestry[i];
|
|
113
|
+
if (element) {
|
|
114
|
+
const depth = ancestry.length - 1 - i;
|
|
115
|
+
const indent = ' '.repeat(depth);
|
|
116
|
+
const marker = i === 0 ? ' [TARGET]' : '';
|
|
117
|
+
lines.push(` // ${indent}${this.formatElementInfo(element)}${marker}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return lines;
|
|
121
|
+
}
|
|
122
|
+
generateActionCode(action) {
|
|
123
|
+
// Navigation doesn't need ancestry
|
|
124
|
+
if (action.type === 'navigate') {
|
|
125
|
+
return ` await page.goto('${this.escapeString(action.url ?? '')}');`;
|
|
126
|
+
}
|
|
127
|
+
const ancestry = action.ancestry ?? [];
|
|
128
|
+
const target = ancestry[0];
|
|
129
|
+
if (!target) {
|
|
130
|
+
return ` // Action ${action.type} with no element info`;
|
|
131
|
+
}
|
|
132
|
+
this.actionCounter++;
|
|
133
|
+
const actionId = this.actionCounter;
|
|
134
|
+
// Determine the action method
|
|
135
|
+
let actionMethod;
|
|
136
|
+
switch (action.type) {
|
|
137
|
+
case 'click':
|
|
138
|
+
actionMethod = '.click()';
|
|
139
|
+
break;
|
|
140
|
+
case 'fill':
|
|
141
|
+
actionMethod = `.fill('${this.escapeString(action.value ?? '')}')`;
|
|
142
|
+
break;
|
|
143
|
+
case 'press':
|
|
144
|
+
actionMethod = `.press('${this.escapeString(action.key ?? '')}')`;
|
|
145
|
+
break;
|
|
146
|
+
case 'select':
|
|
147
|
+
actionMethod = `.selectOption('${this.escapeString(action.value ?? '')}')`;
|
|
148
|
+
break;
|
|
149
|
+
case 'check':
|
|
150
|
+
actionMethod = '.check()';
|
|
151
|
+
break;
|
|
152
|
+
case 'uncheck':
|
|
153
|
+
actionMethod = '.uncheck()';
|
|
154
|
+
break;
|
|
155
|
+
default: {
|
|
156
|
+
const unknownType = action.type;
|
|
157
|
+
return ` // Unknown action: ${String(unknownType)}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const { locator: primaryLocator, strategy: locatorType } = this.generateLocator(target);
|
|
161
|
+
// Build the output with ancestry information (root -> target order)
|
|
162
|
+
const lines = [];
|
|
163
|
+
lines.push(` // ===== ACTION ${String(actionId)}: ${action.type} =====`);
|
|
164
|
+
lines.push(...this.formatAncestryComments(ancestry));
|
|
165
|
+
lines.push(` // Locator strategy: ${locatorType}`);
|
|
166
|
+
lines.push(` await ${primaryLocator}${actionMethod};`);
|
|
167
|
+
lines.push('');
|
|
168
|
+
return lines.join('\n');
|
|
169
|
+
}
|
|
170
|
+
generateCode() {
|
|
171
|
+
// Reset counter for consistent output
|
|
172
|
+
this.actionCounter = 0;
|
|
173
|
+
// Generate initial URL navigation if set
|
|
174
|
+
const initialUrlCode = this.initialUrl
|
|
175
|
+
? ` await page.goto('${this.escapeString(this.initialUrl)}');\n\n`
|
|
176
|
+
: '';
|
|
177
|
+
const header = `// Generated by Latchkey Codegen
|
|
178
|
+
// Each action includes element ancestry for AI post-processing to synthesize optimal selectors.
|
|
179
|
+
// The active locator uses page.getByRole where possible (Playwright best practice).
|
|
180
|
+
|
|
181
|
+
const { chromium } = require('playwright');
|
|
182
|
+
|
|
183
|
+
(async () => {
|
|
184
|
+
const browser = await chromium.launch({ headless: false });
|
|
185
|
+
const context = await browser.newContext();
|
|
186
|
+
const page = await context.newPage();
|
|
187
|
+
|
|
188
|
+
${initialUrlCode}`;
|
|
189
|
+
const footer = ` // ---------------------
|
|
190
|
+
await context.close();
|
|
191
|
+
await browser.close();
|
|
192
|
+
})();
|
|
193
|
+
`;
|
|
194
|
+
const actionLines = this.actions.map((action) => this.generateActionCode(action));
|
|
195
|
+
// Generate API key extraction code if ancestry was set
|
|
196
|
+
let apiKeyCode = '';
|
|
197
|
+
if (this.apiKeyAncestry && this.apiKeyAncestry.length > 0) {
|
|
198
|
+
const ancestry = this.apiKeyAncestry;
|
|
199
|
+
const target = ancestry[0];
|
|
200
|
+
if (target) {
|
|
201
|
+
const { locator: primaryLocator, strategy: locatorType } = this.generateLocator(target);
|
|
202
|
+
// Build ancestry information
|
|
203
|
+
const ancestryLines = [];
|
|
204
|
+
ancestryLines.push(` // ===== API KEY EXTRACTION =====`);
|
|
205
|
+
ancestryLines.push(...this.formatAncestryComments(ancestry));
|
|
206
|
+
ancestryLines.push(` // Locator strategy: ${locatorType}`);
|
|
207
|
+
ancestryLines.push(` const apiKey = await ${primaryLocator}.textContent();`);
|
|
208
|
+
ancestryLines.push(` console.log('API Key:', apiKey);`);
|
|
209
|
+
ancestryLines.push(` // ===== END API KEY EXTRACTION =====`);
|
|
210
|
+
ancestryLines.push('');
|
|
211
|
+
apiKeyCode = '\n' + ancestryLines.join('\n') + '\n';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return header + actionLines.join('\n') + apiKeyCode + footer;
|
|
215
|
+
}
|
|
216
|
+
flush() {
|
|
217
|
+
writeFileSync(this.outputPath, this.generateCode(), 'utf-8');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=codeGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeGenerator.js","sourceRoot":"","sources":["../../../scripts/codegen/codeGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codegen module that records browser actions and generates TypeScript code:
|
|
3
|
+
* - Records user interactions (clicks, fills, navigations, etc.)
|
|
4
|
+
* - Always generates TypeScript code
|
|
5
|
+
* - Records all HTTP request metadata to a file
|
|
6
|
+
* - Includes a custom toolbar with additional buttons
|
|
7
|
+
*
|
|
8
|
+
* The session has two phases:
|
|
9
|
+
* - Pre-login: No recording, requests marked as pre-login
|
|
10
|
+
* - Post-login: User interactions are recorded, requests marked as post-login
|
|
11
|
+
*/
|
|
12
|
+
import type { CodegenOptions, CodegenResult } from './types.js';
|
|
13
|
+
export type { CodegenOptions, CodegenResult, ElementInfo, RecordedAction, RecordingPhase, RequestMetadata, } from './types.js';
|
|
14
|
+
export { CodegenError } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Run the codegen which opens a browser with recording enabled.
|
|
17
|
+
* Injects a custom toolbar and records user actions and HTTP request metadata.
|
|
18
|
+
*
|
|
19
|
+
* Creates a scripts/recordings/$name/ directory with:
|
|
20
|
+
* - actions.js: Recorded user actions
|
|
21
|
+
* - requests.json: HTTP request metadata
|
|
22
|
+
* - prompt.txt: Instructions for creating a service definition
|
|
23
|
+
*
|
|
24
|
+
* @returns Result containing the API key ancestry if selected by the user
|
|
25
|
+
*/
|
|
26
|
+
export declare function runCodegen(options: CodegenOptions): Promise<CodegenResult>;
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../scripts/codegen/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAId,MAAM,YAAY,CAAC;AAUpB,YAAY,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAyB1C;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAuKhF"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codegen module that records browser actions and generates TypeScript code:
|
|
3
|
+
* - Records user interactions (clicks, fills, navigations, etc.)
|
|
4
|
+
* - Always generates TypeScript code
|
|
5
|
+
* - Records all HTTP request metadata to a file
|
|
6
|
+
* - Includes a custom toolbar with additional buttons
|
|
7
|
+
*
|
|
8
|
+
* The session has two phases:
|
|
9
|
+
* - Pre-login: No recording, requests marked as pre-login
|
|
10
|
+
* - Post-login: User interactions are recorded, requests marked as post-login
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { dirname, join, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { chromium } from 'playwright';
|
|
16
|
+
import { CodeGenerator } from './codeGenerator.js';
|
|
17
|
+
import { createInjectedScript } from './injectedScript.js';
|
|
18
|
+
import { RequestMetadataCollector } from './requestMetadataCollector.js';
|
|
19
|
+
// Get the directory of this module
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = dirname(__filename);
|
|
22
|
+
// Recordings directory relative to this module (scripts/recordings)
|
|
23
|
+
const RECORDINGS_DIRECTORY = resolve(__dirname, '..', 'recordings');
|
|
24
|
+
export { CodegenError } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Generate the prompt.txt content for creating a service definition.
|
|
27
|
+
*/
|
|
28
|
+
function generatePromptContent(name) {
|
|
29
|
+
return `Create a new service definition for ${name} with browser login support.
|
|
30
|
+
|
|
31
|
+
Typically, in a browser login session, the user will need to log in manually, and then the automation kicks in to generate an API key. The code needs to detect when the login stage has finished, and then use the Playwright API to perform automatic interactions, and finally retrieve the API key.
|
|
32
|
+
|
|
33
|
+
To help you derive this logic, I have recorded a sample user session:
|
|
34
|
+
|
|
35
|
+
The metadata of all requests during the session is recorded in scripts/recordings/${name}/requests.json. Each element contains a "phase" field that tells you whether this is before or after login.
|
|
36
|
+
Note that because the phase is derived from the user clicking a button that says "I'm logged in", some requests immediately after logging in may be marked incorrectly as pre-login. Examine the difference between these two sets of data and try to derive the simplest possible criteria. Some good candidates are:
|
|
37
|
+
|
|
38
|
+
- A request to the original URL with a difference in HTTP status code (the pre-login one will be a redirection, the post-login one will be 200). Note that the post-login request is likely to be marked incorrectly as pre-login because it's the very first request after login. Use the timestamp to figure out if that's the case.
|
|
39
|
+
|
|
40
|
+
- The presence of some kind of "auth" or "user" header in the request header.
|
|
41
|
+
|
|
42
|
+
The actions for generating an API key after the user has logged in is recorded in scripts/recordings/${name}/actions.js. For each element, think about how to derive a stable selector that doesn't depend on the user's language. Usually this means using readable IDs or CSS classes, which are unlikely to change. Some elements, such as submit buttons, may already be unique when matched by the type. If everything fails, fall back to using role + label.
|
|
43
|
+
|
|
44
|
+
Reference other service implementations to understand which patterns we use.
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Run the codegen which opens a browser with recording enabled.
|
|
49
|
+
* Injects a custom toolbar and records user actions and HTTP request metadata.
|
|
50
|
+
*
|
|
51
|
+
* Creates a scripts/recordings/$name/ directory with:
|
|
52
|
+
* - actions.js: Recorded user actions
|
|
53
|
+
* - requests.json: HTTP request metadata
|
|
54
|
+
* - prompt.txt: Instructions for creating a service definition
|
|
55
|
+
*
|
|
56
|
+
* @returns Result containing the API key ancestry if selected by the user
|
|
57
|
+
*/
|
|
58
|
+
export async function runCodegen(options) {
|
|
59
|
+
const { name, url } = options;
|
|
60
|
+
// Create recordings directory
|
|
61
|
+
const recordingsDirectory = join(RECORDINGS_DIRECTORY, name);
|
|
62
|
+
if (!existsSync(recordingsDirectory)) {
|
|
63
|
+
mkdirSync(recordingsDirectory, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
const actionsFile = join(recordingsDirectory, 'actions.js');
|
|
66
|
+
const requestsFile = join(recordingsDirectory, 'requests.json');
|
|
67
|
+
const promptFile = join(recordingsDirectory, 'prompt.txt');
|
|
68
|
+
// Write prompt.txt
|
|
69
|
+
writeFileSync(promptFile, generatePromptContent(name), 'utf-8');
|
|
70
|
+
const launchOptions = {
|
|
71
|
+
headless: false,
|
|
72
|
+
};
|
|
73
|
+
if (options.executablePath) {
|
|
74
|
+
launchOptions.executablePath = options.executablePath;
|
|
75
|
+
}
|
|
76
|
+
const browser = await chromium.launch(launchOptions);
|
|
77
|
+
const context = await browser.newContext();
|
|
78
|
+
const requestCollector = new RequestMetadataCollector(requestsFile);
|
|
79
|
+
const codeGenerator = new CodeGenerator(actionsFile);
|
|
80
|
+
// Session state
|
|
81
|
+
let currentPhase = 'pre-login';
|
|
82
|
+
let apiKeyAncestry;
|
|
83
|
+
// Track HTTP requests and responses
|
|
84
|
+
context.on('response', (response) => {
|
|
85
|
+
const request = response.request();
|
|
86
|
+
requestCollector.addRequest(request, response);
|
|
87
|
+
});
|
|
88
|
+
context.on('requestfailed', (request) => {
|
|
89
|
+
requestCollector.addRequest(request, null);
|
|
90
|
+
});
|
|
91
|
+
// Inject our combined recorder and toolbar script into every page
|
|
92
|
+
const injectedScript = createInjectedScript();
|
|
93
|
+
await context.addInitScript(injectedScript);
|
|
94
|
+
// Expose function to receive recorded actions from the page
|
|
95
|
+
// Only records actions during post-login phase
|
|
96
|
+
await context.exposeFunction('__latchkeyRecordAction', (action) => {
|
|
97
|
+
// Only record actions during post-login phase
|
|
98
|
+
if (currentPhase !== 'post-login') {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
codeGenerator.addAction({
|
|
102
|
+
type: action.type,
|
|
103
|
+
ancestry: action.ancestry,
|
|
104
|
+
value: action.value,
|
|
105
|
+
key: action.key,
|
|
106
|
+
url: action.url,
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
// Expose function to get current phase (called by toolbar)
|
|
111
|
+
await context.exposeFunction('__latchkeyGetPhase', () => {
|
|
112
|
+
return currentPhase;
|
|
113
|
+
});
|
|
114
|
+
// Expose function called when "I've logged in" button is clicked
|
|
115
|
+
await context.exposeFunction('__latchkeyTransitionToPostLogin', () => {
|
|
116
|
+
console.log('[Latchkey] Transitioning to post-login phase');
|
|
117
|
+
currentPhase = 'post-login';
|
|
118
|
+
requestCollector.setPhase('post-login');
|
|
119
|
+
});
|
|
120
|
+
// Expose function called when API key element is selected
|
|
121
|
+
await context.exposeFunction('__latchkeyApiKeyElementSelected', (ancestry) => {
|
|
122
|
+
console.log(`[Latchkey] API key element selected with ${String(ancestry.length)} ancestors`);
|
|
123
|
+
apiKeyAncestry = ancestry;
|
|
124
|
+
codeGenerator.setApiKeyAncestry(ancestry);
|
|
125
|
+
});
|
|
126
|
+
const page = await context.newPage();
|
|
127
|
+
// Helper function to inject script after page load (needed because addInitScript
|
|
128
|
+
// runs before DOM exists, so toolbar won't appear without this)
|
|
129
|
+
async function injectScriptIfNeeded(targetPage) {
|
|
130
|
+
try {
|
|
131
|
+
await targetPage.evaluate(injectedScript);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Ignore errors (e.g., if page is closed or navigating)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Navigate to initial URL
|
|
138
|
+
codeGenerator.setInitialUrl(url);
|
|
139
|
+
await page.goto(url);
|
|
140
|
+
// Inject script after page loads
|
|
141
|
+
await injectScriptIfNeeded(page);
|
|
142
|
+
// Re-inject script after navigations
|
|
143
|
+
page.on('load', () => {
|
|
144
|
+
void injectScriptIfNeeded(page);
|
|
145
|
+
});
|
|
146
|
+
// Track navigations (only during post-login phase)
|
|
147
|
+
page.on('framenavigated', (frame) => {
|
|
148
|
+
// Only track main frame navigations during post-login
|
|
149
|
+
if (frame === page.mainFrame() && currentPhase === 'post-login') {
|
|
150
|
+
const url = frame.url();
|
|
151
|
+
// Don't record about:blank
|
|
152
|
+
if (url && url !== 'about:blank') {
|
|
153
|
+
codeGenerator.addAction({
|
|
154
|
+
type: 'navigate',
|
|
155
|
+
url: url,
|
|
156
|
+
timestamp: Date.now(),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
// Wait for browser to close
|
|
162
|
+
await new Promise((resolve) => {
|
|
163
|
+
const cleanup = () => {
|
|
164
|
+
resolve();
|
|
165
|
+
};
|
|
166
|
+
browser.on('disconnected', cleanup);
|
|
167
|
+
context.on('close', cleanup);
|
|
168
|
+
page.on('close', cleanup);
|
|
169
|
+
});
|
|
170
|
+
// Ensure browser is fully closed
|
|
171
|
+
try {
|
|
172
|
+
await browser.close();
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Browser may already be closed, ignore
|
|
176
|
+
}
|
|
177
|
+
// Final flush of collected data
|
|
178
|
+
requestCollector.flush();
|
|
179
|
+
codeGenerator.flush();
|
|
180
|
+
console.log(`\nRecording saved to ${recordingsDirectory}/`);
|
|
181
|
+
console.log(` - actions.js: Recorded user actions`);
|
|
182
|
+
console.log(` - requests.json: HTTP request metadata`);
|
|
183
|
+
console.log(` - prompt.txt: Instructions for creating a service definition`);
|
|
184
|
+
if (apiKeyAncestry) {
|
|
185
|
+
console.log(`\nAPI key element ancestry captured with ${String(apiKeyAncestry.length)} elements`);
|
|
186
|
+
}
|
|
187
|
+
return { apiKeyAncestry };
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../scripts/codegen/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectedScript.d.ts","sourceRoot":"","sources":["../../../scripts/codegen/injectedScript.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA2oB7C"}
|