@milldr/crono 0.4.0 → 0.6.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 (81) hide show
  1. package/README.md +71 -13
  2. package/dist/automation/client.d.ts +5 -0
  3. package/dist/automation/client.d.ts.map +1 -0
  4. package/dist/automation/client.js +13 -0
  5. package/dist/automation/client.js.map +1 -0
  6. package/dist/automation/runner.d.ts +3 -0
  7. package/dist/automation/runner.d.ts.map +1 -0
  8. package/dist/automation/runner.js +137 -0
  9. package/dist/automation/runner.js.map +1 -0
  10. package/dist/automation/types.d.ts +66 -0
  11. package/dist/automation/types.d.ts.map +1 -0
  12. package/dist/automation/types.js +2 -0
  13. package/dist/automation/types.js.map +1 -0
  14. package/dist/commands/add.js +3 -3
  15. package/dist/commands/add.js.map +1 -1
  16. package/dist/commands/diary.d.ts +1 -1
  17. package/dist/commands/diary.d.ts.map +1 -1
  18. package/dist/commands/diary.js +3 -3
  19. package/dist/commands/diary.js.map +1 -1
  20. package/dist/commands/export.d.ts +1 -0
  21. package/dist/commands/export.d.ts.map +1 -1
  22. package/dist/commands/export.js +45 -3
  23. package/dist/commands/export.js.map +1 -1
  24. package/dist/commands/log.js +3 -3
  25. package/dist/commands/log.js.map +1 -1
  26. package/dist/commands/login.d.ts.map +1 -1
  27. package/dist/commands/login.js +53 -26
  28. package/dist/commands/login.js.map +1 -1
  29. package/dist/commands/quick-add.js +3 -3
  30. package/dist/commands/quick-add.js.map +1 -1
  31. package/dist/commands/recipes.d.ts +5 -0
  32. package/dist/commands/recipes.d.ts.map +1 -0
  33. package/dist/commands/recipes.js +35 -0
  34. package/dist/commands/recipes.js.map +1 -0
  35. package/dist/commands/weight.d.ts.map +1 -1
  36. package/dist/commands/weight.js +3 -3
  37. package/dist/commands/weight.js.map +1 -1
  38. package/dist/config.d.ts +6 -0
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +6 -0
  41. package/dist/config.js.map +1 -1
  42. package/dist/cronometer/export.d.ts +1 -1
  43. package/dist/cronometer/export.d.ts.map +1 -1
  44. package/dist/cronometer/export.js +1 -0
  45. package/dist/cronometer/export.js.map +1 -1
  46. package/dist/cronometer/parse.d.ts +14 -0
  47. package/dist/cronometer/parse.d.ts.map +1 -1
  48. package/dist/cronometer/parse.js +62 -0
  49. package/dist/cronometer/parse.js.map +1 -1
  50. package/dist/index.js +10 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/kernel/add-custom-food.d.ts.map +1 -1
  53. package/dist/kernel/add-custom-food.js +6 -127
  54. package/dist/kernel/add-custom-food.js.map +1 -1
  55. package/dist/kernel/client.d.ts +4 -56
  56. package/dist/kernel/client.d.ts.map +1 -1
  57. package/dist/kernel/client.js +29 -274
  58. package/dist/kernel/client.js.map +1 -1
  59. package/dist/kernel/food-dialog.d.ts +13 -0
  60. package/dist/kernel/food-dialog.d.ts.map +1 -0
  61. package/dist/kernel/food-dialog.js +294 -0
  62. package/dist/kernel/food-dialog.js.map +1 -0
  63. package/dist/kernel/log-food.d.ts.map +1 -1
  64. package/dist/kernel/log-food.js +2 -175
  65. package/dist/kernel/log-food.js.map +1 -1
  66. package/dist/kernel/login.d.ts +1 -1
  67. package/dist/kernel/login.d.ts.map +1 -1
  68. package/dist/kernel/login.js +34 -4
  69. package/dist/kernel/login.js.map +1 -1
  70. package/dist/kernel/quick-add.d.ts.map +1 -1
  71. package/dist/kernel/quick-add.js +9 -186
  72. package/dist/kernel/quick-add.js.map +1 -1
  73. package/dist/kernel/recipes.d.ts +10 -0
  74. package/dist/kernel/recipes.d.ts.map +1 -0
  75. package/dist/kernel/recipes.js +38 -0
  76. package/dist/kernel/recipes.js.map +1 -0
  77. package/dist/playwright/client.d.ts +6 -0
  78. package/dist/playwright/client.d.ts.map +1 -0
  79. package/dist/playwright/client.js +41 -0
  80. package/dist/playwright/client.js.map +1 -0
  81. package/package.json +3 -2
@@ -5,6 +5,7 @@
5
5
  * kernel.browsers.playwright.execute(). The code has access to
6
6
  * `page`, `context`, and `browser` from the Playwright environment.
7
7
  */
8
+ import { buildFoodDialogCode } from "./food-dialog.js";
8
9
  /**
9
10
  * Macro names as they appear in Cronometer's food search.
10
11
  * Each macro is a separate "Quick Add" food item.
@@ -101,194 +102,16 @@ export function buildQuickAddCode(entry) {
101
102
  return false;
102
103
  }
103
104
 
104
- // Helper: right-click an element from a list of selectors
105
- async function rightClickFirst(selectors, description) {
106
- for (const sel of selectors) {
107
- try {
108
- const el = page.locator(sel);
109
- if (await el.count() > 0) {
110
- await el.first().click({ button: 'right', timeout: 5000 });
111
- return true;
112
- }
113
- } catch {}
114
- }
115
- return false;
116
- }
117
-
118
105
  // Add each macro as a separate food entry
119
106
  for (const macro of macros) {
120
- // Right-click the meal category
121
- const clicked = await rightClickFirst([
122
- 'text="' + mealLabel + '"',
123
- ':has-text("' + mealLabel + '")',
124
- ]);
125
- if (!clicked) {
126
- return { success: false, error: 'Could not find meal category "' + mealLabel + '" in diary' };
127
- }
128
- const menuVisible = await page.waitForSelector('text="Add Food..."', { timeout: 3000 })
129
- .then(() => true)
130
- .catch(() => page.waitForSelector('text="Add Food"', { timeout: 2000 }).then(() => true).catch(() => false));
131
- if (!menuVisible) {
132
- return { success: false, error: 'Context menu did not appear after right-clicking "' + mealLabel + '"' };
133
- }
134
-
135
- // Click "Add Food..." in context menu
136
- const addFoodClicked = await clickFirst([
137
- 'text="Add Food..."',
138
- 'text="Add Food…"',
139
- 'text="Add Food"',
140
- '[role="menuitem"]:has-text("Add Food")',
141
- ]);
142
- if (!addFoodClicked) {
143
- return { success: false, error: 'Could not find "Add Food" in context menu' };
144
- }
145
- await page.waitForTimeout(200);
146
-
147
- // Wait for the "Add Food to Diary" dialog to appear
148
- try {
149
- await page.waitForSelector('text="Add Food to Diary"', { timeout: 5000 });
150
- } catch {
151
- return { success: false, error: 'Add Food to Diary dialog did not appear' };
152
- }
153
- await page.waitForTimeout(300);
154
-
155
- // Click the search bar and type the search term
156
- // GWT apps often need click + keyboard.type instead of fill()
157
- const searchSelectors = [
158
- 'input[placeholder*="Search all foods" i]',
159
- 'input[placeholder*="Search" i]',
160
- 'input[placeholder*="food" i]',
161
- 'input.gwt-TextBox',
162
- 'input[type="text"]',
163
- 'input[type="search"]',
164
- ];
165
- let searched = false;
166
- for (const sel of searchSelectors) {
167
- try {
168
- const el = page.locator(sel);
169
- if (await el.count() > 0) {
170
- await el.first().click();
171
- await page.waitForTimeout(200);
172
- // Clear any existing text, then type via keyboard for GWT compatibility
173
- await el.first().fill('');
174
- await page.keyboard.type(macro.searchName, { delay: 50 });
175
- searched = true;
176
- break;
177
- }
178
- } catch {}
179
- }
180
- if (!searched) {
181
- return { success: false, error: 'Could not find food search bar in Add Food dialog' };
182
- }
183
- await page.waitForTimeout(300);
184
-
185
- // Click the SEARCH button to trigger results
186
- await clickFirst([
187
- 'text="SEARCH"',
188
- 'button:has-text("SEARCH")',
189
- 'button:has-text("Search")',
190
- ]);
191
- const resultsAppeared = await page.waitForSelector('td:has-text("' + macro.searchName + '")', { timeout: 8000 })
192
- .then(() => true).catch(() => false);
193
- if (!resultsAppeared) {
194
- return { success: false, error: 'Search results did not appear for "' + macro.searchName + '"' };
195
- }
196
-
197
- // Select the search result row (not the search input)
198
- // Target table rows/cells containing the macro name
199
- const resultSelectors = [
200
- 'td:has-text("' + macro.searchName + '")',
201
- 'tr:has-text("' + macro.searchName + '") td',
202
- '.gwt-HTML:has-text("' + macro.searchName + '")',
203
- 'div:has-text("' + macro.searchName + '"):not(:has(input))',
204
- ];
205
- let resultClicked = false;
206
- for (const sel of resultSelectors) {
207
- try {
208
- const el = page.locator(sel);
209
- if (await el.count() > 0) {
210
- await el.first().click();
211
- resultClicked = true;
212
- break;
213
- }
214
- } catch {}
215
- }
216
- if (!resultClicked) {
217
- return { success: false, error: 'No search result found for "' + macro.searchName + '"' };
218
- }
219
- await page.waitForTimeout(200);
220
-
221
- // Wait for the detail panel with serving size to appear
222
- try {
223
- await page.waitForSelector('text="Serving Size"', { timeout: 5000 });
224
- } catch {
225
- return { success: false, error: 'Serving Size panel did not appear for "' + macro.name + '"' };
226
- }
227
- await page.waitForTimeout(500);
228
-
229
- // Enter the serving size (grams)
230
- // Find the input by locating the "Serving Size" label's parent and
231
- // then finding the input within it via page.evaluate()
232
- let servingFilled = false;
233
- try {
234
- servingFilled = await page.evaluate((grams) => {
235
- // Walk all elements containing "Serving Size" text
236
- const walker = document.createTreeWalker(
237
- document.body,
238
- NodeFilter.SHOW_TEXT,
239
- { acceptNode: (node) =>
240
- node.textContent && node.textContent.trim() === 'Serving Size'
241
- ? NodeFilter.FILTER_ACCEPT
242
- : NodeFilter.FILTER_REJECT
243
- }
244
- );
245
- const textNode = walker.nextNode();
246
- if (!textNode) return false;
247
-
248
- // Walk up to find a container that also has an input
249
- let container = textNode.parentElement;
250
- for (let i = 0; i < 5 && container; i++) {
251
- const input = container.querySelector('input');
252
- if (input) {
253
- input.focus();
254
- input.select();
255
- // Use native input setter to trigger GWT's change detection
256
- const nativeSetter = Object.getOwnPropertyDescriptor(
257
- window.HTMLInputElement.prototype, 'value'
258
- ).set;
259
- nativeSetter.call(input, String(grams));
260
- input.dispatchEvent(new Event('input', { bubbles: true }));
261
- input.dispatchEvent(new Event('change', { bubbles: true }));
262
- return true;
263
- }
264
- container = container.parentElement;
265
- }
266
- return false;
267
- }, macro.grams);
268
- } catch {}
269
-
270
- if (!servingFilled) {
271
- return { success: false, error: 'Could not find serving size input for "' + macro.name + '"' };
272
- }
273
- await page.waitForTimeout(500);
274
-
275
- // Click "ADD TO DIARY"
276
- const addClicked = await clickFirst([
277
- 'button:has-text("ADD TO DIARY")',
278
- 'button:has-text("Add to Diary")',
279
- 'text="ADD TO DIARY"',
280
- 'text="Add to Diary"',
281
- 'button[type="submit"]',
282
- ]);
283
- if (!addClicked) {
284
- return { success: false, error: 'Could not find "Add to Diary" button for "' + macro.name + '"' };
285
- }
286
- const dialogDismissed = await page.waitForSelector('text="Add Food to Diary"', { state: 'hidden', timeout: 8000 })
287
- .then(() => true).catch(() => false);
288
- if (!dialogDismissed) {
289
- return { success: false, error: '"Add Food to Diary" dialog did not close after adding "' + macro.name + '"' };
290
- }
291
- await page.waitForTimeout(500);
107
+ ${buildFoodDialogCode({
108
+ foodNameVar: "macro.searchName",
109
+ itemNameVar: "macro.name",
110
+ servingCountVar: "macro.grams",
111
+ alwaysUpdateServingSize: true,
112
+ updateServingSize: true,
113
+ verifyDialogDismissed: true,
114
+ })}
292
115
  }
293
116
 
294
117
  return { success: true };
@@ -1 +1 @@
1
- {"version":3,"file":"quick-add.js","sourceRoot":"","sources":["../../src/kernel/quick-add.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2B;IACxD,OAAO,EAAE,oBAAoB;IAC7B,KAAK,EAAE,yBAAyB;IAChC,GAAG,EAAE,gBAAgB;IACrB,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAiB;IACjD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAE3D,MAAM,SAAS,GAAG,IAAI;QACpB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QAC5D,CAAC,CAAC,eAAe,CAAC;IAEpB,8BAA8B;IAC9B,MAAM,MAAM,GAA0D,EAAE,CAAC;IACzE,IAAI,OAAO,KAAK,SAAS;QACvB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,kBAAkB,CAAC,OAAO;YACtC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;IACL,IAAI,KAAK,KAAK,SAAS;QACrB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,OAAO;YACb,UAAU,EAAE,kBAAkB,CAAC,KAAK;YACpC,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,IAAI,GAAG,KAAK,SAAS;QACnB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,kBAAkB,CAAC,GAAG;YAClC,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,IAAI,OAAO,KAAK,SAAS;QACvB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,kBAAkB,CAAC,OAAO;YACtC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;IAEL,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAE1C,OAAO;qBACY,UAAU;wBACP,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;yBACxB,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2OlD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"quick-add.js","sourceRoot":"","sources":["../../src/kernel/quick-add.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2B;IACxD,OAAO,EAAE,oBAAoB;IAC7B,KAAK,EAAE,yBAAyB;IAChC,GAAG,EAAE,gBAAgB;IACrB,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAiB;IACjD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAE3D,MAAM,SAAS,GAAG,IAAI;QACpB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QAC5D,CAAC,CAAC,eAAe,CAAC;IAEpB,8BAA8B;IAC9B,MAAM,MAAM,GAA0D,EAAE,CAAC;IACzE,IAAI,OAAO,KAAK,SAAS;QACvB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,kBAAkB,CAAC,OAAO;YACtC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;IACL,IAAI,KAAK,KAAK,SAAS;QACrB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,OAAO;YACb,UAAU,EAAE,kBAAkB,CAAC,KAAK;YACpC,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,IAAI,GAAG,KAAK,SAAS;QACnB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,kBAAkB,CAAC,GAAG;YAClC,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,IAAI,OAAO,KAAK,SAAS;QACvB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,kBAAkB,CAAC,OAAO;YACtC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;IAEL,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAE1C,OAAO;qBACY,UAAU;wBACP,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;yBACxB,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CnD,mBAAmB,CAAC;QACpB,WAAW,EAAE,kBAAkB;QAC/B,WAAW,EAAE,YAAY;QACzB,eAAe,EAAE,aAAa;QAC9B,uBAAuB,EAAE,IAAI;QAC7B,iBAAiB,EAAE,IAAI;QACvB,qBAAqB,EAAE,IAAI;KAC5B,CAAC;;;;GAIC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Playwright code generator for Cronometer custom recipes scraping.
3
+ *
4
+ * Recipe names live in div.gwt-Label elements inside div[role="button"]
5
+ * rows within the scrollable list on the #custom-recipes page.
6
+ *
7
+ * Returns { success: true, recipes: [{ name }] }
8
+ */
9
+ export declare function buildRecipesCode(): string;
10
+ //# sourceMappingURL=recipes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../../src/kernel/recipes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CA4BzC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Playwright code generator for Cronometer custom recipes scraping.
3
+ *
4
+ * Recipe names live in div.gwt-Label elements inside div[role="button"]
5
+ * rows within the scrollable list on the #custom-recipes page.
6
+ *
7
+ * Returns { success: true, recipes: [{ name }] }
8
+ */
9
+ export function buildRecipesCode() {
10
+ return `
11
+ await page.goto('https://cronometer.com/#custom-recipes', { waitUntil: 'domcontentloaded', timeout: 15000 });
12
+
13
+ const url = page.url();
14
+ if (url.includes('/login') || url.includes('/signin')) {
15
+ return { success: false, error: 'Not logged in. Login may have failed.' };
16
+ }
17
+
18
+ // Wait for the GWT widget to render the recipe list.
19
+ // The search input appears before list items finish rendering, so we wait
20
+ // for an actual recipe row to be present before scraping.
21
+ await page.waitForSelector('input[placeholder*="recipes"]', { timeout: 10000 });
22
+ await page.waitForSelector('div[role="button"] .gwt-Label', { timeout: 10000 }).catch(() => {});
23
+
24
+ const recipes = await page.evaluate(() => {
25
+ // Recipe rows are div[role="button"] elements containing a div.gwt-Label child
26
+ const rows = Array.from(document.querySelectorAll('div[role="button"]'));
27
+ return rows
28
+ .map((row) => {
29
+ const label = row.querySelector('.gwt-Label');
30
+ return label ? { name: label.textContent?.trim() ?? '' } : null;
31
+ })
32
+ .filter((r) => r !== null && r.name.length > 0);
33
+ });
34
+
35
+ return { success: true, recipes };
36
+ `;
37
+ }
38
+ //# sourceMappingURL=recipes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/kernel/recipes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Local Playwright automation runtime.
3
+ */
4
+ import type { AutomationClient } from "../automation/types.js";
5
+ export declare function getPlaywrightClient(): Promise<AutomationClient>;
6
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/playwright/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,wBAAwB,CAAC;AAGhC,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAErE"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Local Playwright automation runtime.
3
+ */
4
+ import { mkdirSync } from "fs";
5
+ import { chromium } from "playwright";
6
+ import { createAutomationClient } from "../automation/runner.js";
7
+ import { getPlaywrightProfileDir, loadConfig } from "../config.js";
8
+ export async function getPlaywrightClient() {
9
+ return createAutomationClient(createPlaywrightRuntime);
10
+ }
11
+ async function createPlaywrightRuntime() {
12
+ const context = await createContext();
13
+ const page = context.pages()[0] ?? (await context.newPage());
14
+ return {
15
+ execute: (code) => executeScript(page, context, code),
16
+ close: () => context.close(),
17
+ };
18
+ }
19
+ async function createContext() {
20
+ const config = loadConfig();
21
+ const profileDir = getPlaywrightProfileDir();
22
+ mkdirSync(profileDir, { recursive: true });
23
+ return chromium.launchPersistentContext(profileDir, {
24
+ headless: config.playwrightHeadless ??
25
+ (process.platform === "linux" && !process.env["DISPLAY"]),
26
+ });
27
+ }
28
+ async function executeScript(page, context, code) {
29
+ const browser = context.browser();
30
+ const execute = new Function("page", "context", "browser", `return (async () => { ${code} })();`);
31
+ try {
32
+ return { success: true, result: await execute(page, context, browser) };
33
+ }
34
+ catch (error) {
35
+ return {
36
+ success: false,
37
+ error: error instanceof Error ? error.message : String(error),
38
+ };
39
+ }
40
+ }
41
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/playwright/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAkC,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAKjE,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEnE,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,OAAO,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;AACzD,CAAC;AAED,KAAK,UAAU,uBAAuB;IACpC,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAE7D,OAAO;QACL,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;QAC7D,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;KAC7B,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,uBAAuB,EAAE,CAAC;IAC7C,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,OAAO,QAAQ,CAAC,uBAAuB,CAAC,UAAU,EAAE;QAClD,QAAQ,EACN,MAAM,CAAC,kBAAkB;YACzB,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC5D,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,IAAU,EACV,OAAuB,EACvB,IAAY;IAEZ,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,QAAQ,CAC1B,MAAM,EACN,SAAS,EACT,SAAS,EACT,yBAAyB,IAAI,QAAQ,CAKxB,CAAC;IAEhB,IAAI,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC;IACJ,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milldr/crono",
3
- "version": "0.4.0",
3
+ "version": "0.6.1",
4
4
  "description": "CLI for Cronometer automation via Kernel.sh",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,8 @@
39
39
  "@clack/prompts": "^1.0.0",
40
40
  "@napi-rs/keyring": "^1.2.0",
41
41
  "@onkernel/sdk": "^0.33.0",
42
- "commander": "^12.0.0"
42
+ "commander": "^12.0.0",
43
+ "playwright": "^1.60.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@types/node": "^20.11.0",