@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.
- package/README.md +71 -13
- package/dist/automation/client.d.ts +5 -0
- package/dist/automation/client.d.ts.map +1 -0
- package/dist/automation/client.js +13 -0
- package/dist/automation/client.js.map +1 -0
- package/dist/automation/runner.d.ts +3 -0
- package/dist/automation/runner.d.ts.map +1 -0
- package/dist/automation/runner.js +137 -0
- package/dist/automation/runner.js.map +1 -0
- package/dist/automation/types.d.ts +66 -0
- package/dist/automation/types.d.ts.map +1 -0
- package/dist/automation/types.js +2 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/commands/add.js +3 -3
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/diary.d.ts +1 -1
- package/dist/commands/diary.d.ts.map +1 -1
- package/dist/commands/diary.js +3 -3
- package/dist/commands/diary.js.map +1 -1
- package/dist/commands/export.d.ts +1 -0
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +45 -3
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/log.js +3 -3
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +53 -26
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/quick-add.js +3 -3
- package/dist/commands/quick-add.js.map +1 -1
- package/dist/commands/recipes.d.ts +5 -0
- package/dist/commands/recipes.d.ts.map +1 -0
- package/dist/commands/recipes.js +35 -0
- package/dist/commands/recipes.js.map +1 -0
- package/dist/commands/weight.d.ts.map +1 -1
- package/dist/commands/weight.js +3 -3
- package/dist/commands/weight.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/cronometer/export.d.ts +1 -1
- package/dist/cronometer/export.d.ts.map +1 -1
- package/dist/cronometer/export.js +1 -0
- package/dist/cronometer/export.js.map +1 -1
- package/dist/cronometer/parse.d.ts +14 -0
- package/dist/cronometer/parse.d.ts.map +1 -1
- package/dist/cronometer/parse.js +62 -0
- package/dist/cronometer/parse.js.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/kernel/add-custom-food.d.ts.map +1 -1
- package/dist/kernel/add-custom-food.js +6 -127
- package/dist/kernel/add-custom-food.js.map +1 -1
- package/dist/kernel/client.d.ts +4 -56
- package/dist/kernel/client.d.ts.map +1 -1
- package/dist/kernel/client.js +29 -274
- package/dist/kernel/client.js.map +1 -1
- package/dist/kernel/food-dialog.d.ts +13 -0
- package/dist/kernel/food-dialog.d.ts.map +1 -0
- package/dist/kernel/food-dialog.js +294 -0
- package/dist/kernel/food-dialog.js.map +1 -0
- package/dist/kernel/log-food.d.ts.map +1 -1
- package/dist/kernel/log-food.js +2 -175
- package/dist/kernel/log-food.js.map +1 -1
- package/dist/kernel/login.d.ts +1 -1
- package/dist/kernel/login.d.ts.map +1 -1
- package/dist/kernel/login.js +34 -4
- package/dist/kernel/login.js.map +1 -1
- package/dist/kernel/quick-add.d.ts.map +1 -1
- package/dist/kernel/quick-add.js +9 -186
- package/dist/kernel/quick-add.js.map +1 -1
- package/dist/kernel/recipes.d.ts +10 -0
- package/dist/kernel/recipes.d.ts.map +1 -0
- package/dist/kernel/recipes.js +38 -0
- package/dist/kernel/recipes.js.map +1 -0
- package/dist/playwright/client.d.ts +6 -0
- package/dist/playwright/client.d.ts.map +1 -0
- package/dist/playwright/client.js +41 -0
- package/dist/playwright/client.js.map +1 -0
- package/package.json +3 -2
package/dist/kernel/quick-add.js
CHANGED
|
@@ -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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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;
|
|
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 @@
|
|
|
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.
|
|
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",
|