latchkey 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +10 -6
  2. package/dist/package.json +1 -1
  3. package/dist/scripts/codegen/codeGenerator.d.ts +27 -0
  4. package/dist/scripts/codegen/codeGenerator.d.ts.map +1 -0
  5. package/dist/scripts/codegen/codeGenerator.js +220 -0
  6. package/dist/scripts/codegen/codeGenerator.js.map +1 -0
  7. package/dist/scripts/codegen/index.d.ts +27 -0
  8. package/dist/scripts/codegen/index.d.ts.map +1 -0
  9. package/dist/scripts/codegen/index.js +189 -0
  10. package/dist/scripts/codegen/index.js.map +1 -0
  11. package/dist/scripts/codegen/injectedScript.d.ts +6 -0
  12. package/dist/scripts/codegen/injectedScript.d.ts.map +1 -0
  13. package/dist/scripts/codegen/injectedScript.js +657 -0
  14. package/dist/scripts/codegen/injectedScript.js.map +1 -0
  15. package/dist/scripts/codegen/requestMetadataCollector.d.ts +15 -0
  16. package/dist/scripts/codegen/requestMetadataCollector.d.ts.map +1 -0
  17. package/dist/scripts/codegen/requestMetadataCollector.js +48 -0
  18. package/dist/scripts/codegen/requestMetadataCollector.js.map +1 -0
  19. package/dist/scripts/codegen/types.d.ts +77 -0
  20. package/dist/scripts/codegen/types.d.ts.map +1 -0
  21. package/dist/scripts/codegen/types.js +10 -0
  22. package/dist/scripts/codegen/types.js.map +1 -0
  23. package/dist/scripts/codegen.d.ts +24 -0
  24. package/dist/scripts/codegen.d.ts.map +1 -0
  25. package/dist/scripts/codegen.js +95 -0
  26. package/dist/scripts/codegen.js.map +1 -0
  27. package/dist/scripts/cryptFile.js +7 -2
  28. package/dist/scripts/cryptFile.js.map +1 -1
  29. package/dist/src/config.d.ts.map +1 -1
  30. package/dist/src/config.js +6 -12
  31. package/dist/src/config.js.map +1 -1
  32. package/dist/src/encryptedStorage.d.ts +1 -2
  33. package/dist/src/encryptedStorage.d.ts.map +1 -1
  34. package/dist/src/encryptedStorage.js +18 -38
  35. package/dist/src/encryptedStorage.js.map +1 -1
  36. package/dist/src/index.d.ts +1 -1
  37. package/dist/src/index.d.ts.map +1 -1
  38. package/dist/src/index.js +1 -1
  39. package/dist/src/index.js.map +1 -1
  40. package/dist/src/keychain.d.ts +0 -4
  41. package/dist/src/keychain.d.ts.map +1 -1
  42. package/dist/src/keychain.js +0 -13
  43. package/dist/src/keychain.js.map +1 -1
  44. 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
- - Get a list of supported third-party services (Slack, Google Workspace, Linear, GitHub, etc.).
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 in the form of arbitrary curl arguments.
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
@@ -136,10 +141,8 @@ of `latchkey`.
136
141
 
137
142
  ### Remembering API credentials
138
143
 
139
- Your API credentials and browser state are stored by default
140
- under `~/.latchkey`. When a functioning keyring is detected
141
- (which is the case on most systems), the data is properly
142
- encrypted.
144
+ Your API credentials and browser state are encrypted and stored
145
+ by default under `~/.latchkey`.
143
146
 
144
147
 
145
148
  ### Inspecting the status of stored credentials
@@ -206,6 +209,7 @@ the browser used for the login popup
206
209
  - `LATCHKEY_CONFIG`: path to the configuration file
207
210
  (defaults to `~/.latchkey/config.json`)
208
211
  - `LATCHKEY_KEYRING_SERVICE_NAME`, `LATCHKEY_KEYRING_ACCOUNT_NAME`: identifiers that are used to store the encryption password in your keyring
212
+ - `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
213
  - `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
214
 
211
215
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latchkey",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A CLI tool that injects API credentials into curl requests for known third-party services",
5
5
  "author": "Imbue <hynek@imbue.com>",
6
6
  "repository": {
@@ -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,6 @@
1
+ /**
2
+ * Creates the combined script injected into pages.
3
+ * Contains both the interaction recorder and the toolbar UI.
4
+ */
5
+ export declare function createInjectedScript(): string;
6
+ //# sourceMappingURL=injectedScript.d.ts.map
@@ -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"}