@milldr/crono 0.1.0 → 0.3.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.
Files changed (61) hide show
  1. package/README.md +128 -0
  2. package/dist/commands/add.d.ts +9 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +65 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/export.d.ts +8 -0
  7. package/dist/commands/export.d.ts.map +1 -0
  8. package/dist/commands/export.js +142 -0
  9. package/dist/commands/export.js.map +1 -0
  10. package/dist/commands/log.d.ts +6 -0
  11. package/dist/commands/log.d.ts.map +1 -0
  12. package/dist/commands/log.js +40 -0
  13. package/dist/commands/log.js.map +1 -0
  14. package/dist/commands/quick-add.d.ts +2 -0
  15. package/dist/commands/quick-add.d.ts.map +1 -1
  16. package/dist/commands/quick-add.js +20 -3
  17. package/dist/commands/quick-add.js.map +1 -1
  18. package/dist/config.d.ts +2 -0
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js.map +1 -1
  21. package/dist/cronometer/auth.d.ts +31 -0
  22. package/dist/cronometer/auth.d.ts.map +1 -0
  23. package/dist/cronometer/auth.js +151 -0
  24. package/dist/cronometer/auth.js.map +1 -0
  25. package/dist/cronometer/export.d.ts +22 -0
  26. package/dist/cronometer/export.d.ts.map +1 -0
  27. package/dist/cronometer/export.js +83 -0
  28. package/dist/cronometer/export.js.map +1 -0
  29. package/dist/cronometer/parse.d.ts +35 -0
  30. package/dist/cronometer/parse.d.ts.map +1 -0
  31. package/dist/cronometer/parse.js +158 -0
  32. package/dist/cronometer/parse.js.map +1 -0
  33. package/dist/index.js +35 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/kernel/add-custom-food.d.ts +22 -0
  36. package/dist/kernel/add-custom-food.d.ts.map +1 -0
  37. package/dist/kernel/add-custom-food.js +314 -0
  38. package/dist/kernel/add-custom-food.js.map +1 -0
  39. package/dist/kernel/client.d.ts +17 -0
  40. package/dist/kernel/client.d.ts.map +1 -1
  41. package/dist/kernel/client.js +92 -1
  42. package/dist/kernel/client.js.map +1 -1
  43. package/dist/kernel/log-food.d.ts +17 -0
  44. package/dist/kernel/log-food.d.ts.map +1 -0
  45. package/dist/kernel/log-food.js +230 -0
  46. package/dist/kernel/log-food.js.map +1 -0
  47. package/dist/kernel/login.d.ts.map +1 -1
  48. package/dist/kernel/login.js +24 -1
  49. package/dist/kernel/login.js.map +1 -1
  50. package/dist/kernel/quick-add.d.ts.map +1 -1
  51. package/dist/kernel/quick-add.js +27 -1
  52. package/dist/kernel/quick-add.js.map +1 -1
  53. package/dist/utils/date.d.ts +9 -0
  54. package/dist/utils/date.d.ts.map +1 -1
  55. package/dist/utils/date.js +22 -0
  56. package/dist/utils/date.js.map +1 -1
  57. package/package.json +5 -1
  58. package/dist/debug-nav.d.ts +0 -2
  59. package/dist/debug-nav.d.ts.map +0 -1
  60. package/dist/debug-nav.js +0 -99
  61. package/dist/debug-nav.js.map +0 -1
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Playwright code generator for Cronometer food logging.
3
+ *
4
+ * Returns a code string that executes remotely via
5
+ * kernel.browsers.playwright.execute(). The code has access to
6
+ * `page`, `context`, and `browser` from the Playwright environment.
7
+ */
8
+ /**
9
+ * Generate Playwright code for logging a food to the Cronometer diary.
10
+ *
11
+ * Flow:
12
+ * navigate to #diary → right-click meal → "Add Food" → search food name →
13
+ * select result → set servings → "Add to Diary"
14
+ */
15
+ export function buildLogFoodCode(entry) {
16
+ const { name, meal, servings } = entry;
17
+ const mealLabel = meal
18
+ ? meal.charAt(0).toUpperCase() + meal.slice(1).toLowerCase()
19
+ : "Uncategorized";
20
+ const foodName = JSON.stringify(name);
21
+ const servingCount = servings ?? 1;
22
+ return `
23
+ const foodName = ${foodName};
24
+ const mealLabel = ${JSON.stringify(mealLabel)};
25
+ const servingCount = ${servingCount};
26
+
27
+ // Navigate to diary
28
+ await page.goto('https://cronometer.com/#diary', { waitUntil: 'domcontentloaded', timeout: 15000 });
29
+ await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
30
+
31
+ // Verify we're logged in
32
+ const url = page.url();
33
+ if (url.includes('/login') || url.includes('/signin')) {
34
+ return { success: false, error: 'Not logged in. Login may have failed.' };
35
+ }
36
+
37
+ // Helper: find and click an element from a list of selectors
38
+ async function clickFirst(selectors, description) {
39
+ for (const sel of selectors) {
40
+ try {
41
+ const el = page.locator(sel);
42
+ if (await el.count() > 0) {
43
+ await el.first().click();
44
+ return true;
45
+ }
46
+ } catch {}
47
+ }
48
+ return false;
49
+ }
50
+
51
+ // Helper: right-click an element from a list of selectors
52
+ async function rightClickFirst(selectors, description) {
53
+ for (const sel of selectors) {
54
+ try {
55
+ const el = page.locator(sel);
56
+ if (await el.count() > 0) {
57
+ await el.first().click({ button: 'right' });
58
+ return true;
59
+ }
60
+ } catch {}
61
+ }
62
+ return false;
63
+ }
64
+
65
+ // Right-click the meal category
66
+ const clicked = await rightClickFirst([
67
+ 'text="' + mealLabel + '"',
68
+ ':has-text("' + mealLabel + '")',
69
+ ], 'meal category');
70
+ if (!clicked) {
71
+ return { success: false, error: 'Could not find meal category "' + mealLabel + '" in diary' };
72
+ }
73
+ await page.waitForSelector('text="Add Food..."', { timeout: 3000 }).catch(() =>
74
+ page.waitForSelector('text="Add Food"', { timeout: 2000 }).catch(() => {})
75
+ );
76
+
77
+ // Click "Add Food..." in context menu
78
+ const addFoodClicked = await clickFirst([
79
+ 'text="Add Food..."',
80
+ 'text="Add Food\u2026"',
81
+ 'text="Add Food"',
82
+ '[role="menuitem"]:has-text("Add Food")',
83
+ ], 'Add Food menu item');
84
+ if (!addFoodClicked) {
85
+ return { success: false, error: 'Could not find "Add Food" in context menu' };
86
+ }
87
+ await page.waitForTimeout(200);
88
+
89
+ // Wait for "Add Food to Diary" dialog
90
+ try {
91
+ await page.waitForSelector('text="Add Food to Diary"', { timeout: 5000 });
92
+ } catch {
93
+ return { success: false, error: 'Add Food to Diary dialog did not appear' };
94
+ }
95
+ await page.waitForTimeout(300);
96
+
97
+ // Search for the food
98
+ const searchSelectors = [
99
+ 'input[placeholder*="Search all foods" i]',
100
+ 'input[placeholder*="Search" i]',
101
+ 'input[placeholder*="food" i]',
102
+ 'input.gwt-TextBox',
103
+ 'input[type="text"]',
104
+ 'input[type="search"]',
105
+ ];
106
+ let searched = false;
107
+ for (const sel of searchSelectors) {
108
+ try {
109
+ const el = page.locator(sel);
110
+ if (await el.count() > 0) {
111
+ await el.first().click();
112
+ await page.waitForTimeout(200);
113
+ await el.first().fill('');
114
+ await page.keyboard.type(foodName, { delay: 50 });
115
+ searched = true;
116
+ break;
117
+ }
118
+ } catch {}
119
+ }
120
+ if (!searched) {
121
+ return { success: false, error: 'Could not find food search bar in Add Food dialog' };
122
+ }
123
+ await page.waitForTimeout(300);
124
+
125
+ // Click SEARCH
126
+ await clickFirst([
127
+ 'text="SEARCH"',
128
+ 'button:has-text("SEARCH")',
129
+ 'button:has-text("Search")',
130
+ ], 'SEARCH button');
131
+
132
+ // Wait for results
133
+ try {
134
+ await page.waitForSelector('td:has-text("' + foodName + '")', { timeout: 8000 });
135
+ } catch {
136
+ return { success: false, error: 'No food found matching "' + foodName + '"' };
137
+ }
138
+
139
+ // Select the search result
140
+ const resultSelectors = [
141
+ 'td:has-text("' + foodName + '")',
142
+ 'tr:has-text("' + foodName + '") td',
143
+ '.gwt-HTML:has-text("' + foodName + '")',
144
+ 'div:has-text("' + foodName + '"):not(:has(input))',
145
+ ];
146
+ let resultClicked = false;
147
+ for (const sel of resultSelectors) {
148
+ try {
149
+ const el = page.locator(sel);
150
+ if (await el.count() > 0) {
151
+ await el.first().click();
152
+ resultClicked = true;
153
+ break;
154
+ }
155
+ } catch {}
156
+ }
157
+ if (!resultClicked) {
158
+ return { success: false, error: 'No food found matching "' + foodName + '"' };
159
+ }
160
+ await page.waitForTimeout(200);
161
+
162
+ // Wait for the detail panel with serving size
163
+ try {
164
+ await page.waitForSelector('text="Serving Size"', { timeout: 5000 });
165
+ } catch {
166
+ return { success: false, error: 'Serving Size panel did not appear for "' + foodName + '"' };
167
+ }
168
+ await page.waitForTimeout(500);
169
+
170
+ // If servings != 1, update the serving size input
171
+ if (servingCount !== 1) {
172
+ let servingFilled = false;
173
+ try {
174
+ servingFilled = await page.evaluate((count) => {
175
+ const walker = document.createTreeWalker(
176
+ document.body,
177
+ NodeFilter.SHOW_TEXT,
178
+ { acceptNode: (node) =>
179
+ node.textContent && node.textContent.trim() === 'Serving Size'
180
+ ? NodeFilter.FILTER_ACCEPT
181
+ : NodeFilter.FILTER_REJECT
182
+ }
183
+ );
184
+ const textNode = walker.nextNode();
185
+ if (!textNode) return false;
186
+
187
+ let container = textNode.parentElement;
188
+ for (let i = 0; i < 5 && container; i++) {
189
+ const input = container.querySelector('input');
190
+ if (input) {
191
+ input.focus();
192
+ input.select();
193
+ const nativeSetter = Object.getOwnPropertyDescriptor(
194
+ window.HTMLInputElement.prototype, 'value'
195
+ ).set;
196
+ nativeSetter.call(input, String(count));
197
+ input.dispatchEvent(new Event('input', { bubbles: true }));
198
+ input.dispatchEvent(new Event('change', { bubbles: true }));
199
+ return true;
200
+ }
201
+ container = container.parentElement;
202
+ }
203
+ return false;
204
+ }, servingCount);
205
+ } catch {}
206
+
207
+ if (!servingFilled) {
208
+ return { success: false, error: 'Could not update serving size for "' + foodName + '"' };
209
+ }
210
+ await page.waitForTimeout(500);
211
+ }
212
+
213
+ // Click "ADD TO DIARY"
214
+ const addClicked = await clickFirst([
215
+ 'button:has-text("ADD TO DIARY")',
216
+ 'button:has-text("Add to Diary")',
217
+ 'text="ADD TO DIARY"',
218
+ 'text="Add to Diary"',
219
+ 'button[type="submit"]',
220
+ ], 'ADD TO DIARY button');
221
+ if (!addClicked) {
222
+ return { success: false, error: 'Could not find "Add to Diary" button' };
223
+ }
224
+ await page.waitForSelector('text="Add Food to Diary"', { state: 'hidden', timeout: 8000 }).catch(() => {});
225
+ await page.waitForTimeout(300);
226
+
227
+ return { success: true };
228
+ `;
229
+ }
230
+ //# sourceMappingURL=log-food.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-food.js","sourceRoot":"","sources":["../../src/kernel/log-food.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAmB;IAClD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAEvC,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,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,QAAQ,IAAI,CAAC,CAAC;IAEnC,OAAO;uBACc,QAAQ;wBACP,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;2BACtB,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2MpC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/kernel/login.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAKjD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAyF7E"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/kernel/login.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAKjD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgH7E"}
@@ -119,7 +119,30 @@ export function buildAutoLoginCode(username, password) {
119
119
 
120
120
  const url = page.url();
121
121
  const loggedIn = !url.includes('/login') && !url.includes('/signin');
122
- return { success: true, loggedIn, url };
122
+
123
+ // If still on login page, check for error messages (rate limit, wrong creds, etc.)
124
+ let loginError = null;
125
+ if (!loggedIn) {
126
+ loginError = await page.evaluate(() => {
127
+ const selectors = [
128
+ '.error-message', '.alert', '.notification',
129
+ '[class*="error"]', '[class*="alert"]',
130
+ '.gwt-HTML',
131
+ ];
132
+ for (const sel of selectors) {
133
+ const els = document.querySelectorAll(sel);
134
+ for (const el of els) {
135
+ const text = el.textContent?.trim();
136
+ if (text && text.length > 5 && text.length < 300 && el.offsetParent !== null) {
137
+ return text;
138
+ }
139
+ }
140
+ }
141
+ return null;
142
+ });
143
+ }
144
+
145
+ return { success: true, loggedIn, url, loginError };
123
146
  `;
124
147
  }
125
148
  //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/kernel/login.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;GAMN,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;;;GAGN,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,QAAgB;IACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAE1C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAiCyB,QAAQ;;;;;;;;;;;;;;;;;kCAiBR,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCvC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/kernel/login.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;GAMN,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;;;GAGN,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,QAAgB;IACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAE1C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAiCyB,QAAQ;;;;;;;;;;;;;;;;;kCAiBR,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDvC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"quick-add.d.ts","sourceRoot":"","sources":["../../src/kernel/quick-add.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIrD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CA+O3D"}
1
+ {"version":3,"file":"quick-add.d.ts","sourceRoot":"","sources":["../../src/kernel/quick-add.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKrD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAwQ3D"}
@@ -13,6 +13,7 @@ export const MACRO_SEARCH_NAMES = {
13
13
  protein: "Quick Add, Protein",
14
14
  carbs: "Quick Add, Carbohydrate",
15
15
  fat: "Quick Add, Fat",
16
+ alcohol: "Quick Add, Alcohol",
16
17
  };
17
18
  /**
18
19
  * Generate Playwright code for adding a quick entry to Cronometer.
@@ -22,7 +23,7 @@ export const MACRO_SEARCH_NAMES = {
22
23
  * select result → enter serving size (grams) → "Add to Diary"
23
24
  */
24
25
  export function buildQuickAddCode(entry) {
25
- const { protein, carbs, fat, meal } = entry;
26
+ const { protein, carbs, fat, alcohol, meal, date } = entry;
26
27
  const mealLabel = meal
27
28
  ? meal.charAt(0).toUpperCase() + meal.slice(1).toLowerCase()
28
29
  : "Uncategorized";
@@ -46,21 +47,46 @@ export function buildQuickAddCode(entry) {
46
47
  searchName: MACRO_SEARCH_NAMES.fat,
47
48
  grams: fat,
48
49
  });
50
+ if (alcohol !== undefined)
51
+ macros.push({
52
+ name: "alcohol",
53
+ searchName: MACRO_SEARCH_NAMES.alcohol,
54
+ grams: alcohol,
55
+ });
49
56
  const macrosJson = JSON.stringify(macros);
50
57
  return `
51
58
  const macros = ${macrosJson};
52
59
  const mealLabel = ${JSON.stringify(mealLabel)};
60
+ const targetDate = ${JSON.stringify(date ?? null)};
53
61
 
54
62
  // Navigate to diary — we're already logged in from the same session
55
63
  await page.goto('https://cronometer.com/#diary', { waitUntil: 'domcontentloaded', timeout: 15000 });
56
64
  await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
57
65
 
66
+ // Wait for the diary to fully render
67
+ await page.waitForTimeout(2000);
68
+
58
69
  // Verify we're logged in
59
70
  const url = page.url();
60
71
  if (url.includes('/login') || url.includes('/signin')) {
61
72
  return { success: false, error: 'Not logged in. Login may have failed.' };
62
73
  }
63
74
 
75
+ // Navigate to the target date using prev-day arrows (same approach as diary/weight)
76
+ if (targetDate) {
77
+ const today = new Date();
78
+ today.setHours(0, 0, 0, 0);
79
+ const target = new Date(targetDate + 'T00:00:00');
80
+ const daysBack = Math.round((today - target) / (1000 * 60 * 60 * 24));
81
+ for (let s = 0; s < daysBack && s < 90; s++) {
82
+ const prev = page.locator('i.diary-date-previous').filter({ visible: true });
83
+ if (await prev.count() > 0) {
84
+ await prev.first().click();
85
+ await page.waitForTimeout(2000);
86
+ }
87
+ }
88
+ }
89
+
64
90
  // Helper: find and click an element from a list of selectors
65
91
  async function clickFirst(selectors, description) {
66
92
  for (const sel of selectors) {
@@ -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;CACtB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAiB;IACjD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAE5C,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;IAEL,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAE1C,OAAO;qBACY,UAAU;wBACP,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8M9C,CAAC;AACJ,CAAC"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgOlD,CAAC;AACJ,CAAC"}
@@ -12,6 +12,15 @@ export declare function parseDate(str: string): string;
12
12
  export declare function formatDate(date: Date): string;
13
13
  /** Return today's date as YYYY-MM-DD. */
14
14
  export declare function todayStr(): string;
15
+ /**
16
+ * Resolve a date input string to YYYY-MM-DD.
17
+ *
18
+ * Supports:
19
+ * - "yesterday"
20
+ * - Relative: "-1d", "-7d" (N days ago)
21
+ * - Absolute: "YYYY-MM-DD"
22
+ */
23
+ export declare function resolveDate(input: string): string;
15
24
  /**
16
25
  * Parse a range spec into start/end date strings.
17
26
  *
@@ -1 +1 @@
1
- {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAa7C;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAK7C;AAED,yCAAyC;AACzC,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CA0BvE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAW9D"}
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAa7C;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAK7C;AAED,yCAAyC;AACzC,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAajD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CA0BvE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAW9D"}
@@ -35,6 +35,28 @@ export function formatDate(date) {
35
35
  export function todayStr() {
36
36
  return formatDate(new Date());
37
37
  }
38
+ /**
39
+ * Resolve a date input string to YYYY-MM-DD.
40
+ *
41
+ * Supports:
42
+ * - "yesterday"
43
+ * - Relative: "-1d", "-7d" (N days ago)
44
+ * - Absolute: "YYYY-MM-DD"
45
+ */
46
+ export function resolveDate(input) {
47
+ if (input === "yesterday") {
48
+ const d = new Date();
49
+ d.setDate(d.getDate() - 1);
50
+ return formatDate(d);
51
+ }
52
+ const relMatch = input.match(/^-(\d+)d$/);
53
+ if (relMatch) {
54
+ const d = new Date();
55
+ d.setDate(d.getDate() - parseInt(relMatch[1], 10));
56
+ return formatDate(d);
57
+ }
58
+ return parseDate(input);
59
+ }
38
60
  /**
39
61
  * Parse a range spec into start/end date strings.
40
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milldr/crono",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for Cronometer automation via Kernel.sh",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,6 +26,10 @@
26
26
  "automation",
27
27
  "kernel"
28
28
  ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/milldr/crono"
32
+ },
29
33
  "author": "Daniel Miller",
30
34
  "license": "MIT",
31
35
  "engines": {
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=debug-nav.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"debug-nav.d.ts","sourceRoot":"","sources":["../src/debug-nav.ts"],"names":[],"mappings":""}
package/dist/debug-nav.js DELETED
@@ -1,99 +0,0 @@
1
- /**
2
- * Debug script: dump date navigation elements from Cronometer diary.
3
- * Run with: npx tsx src/debug-nav.ts
4
- */
5
- import Kernel from "@onkernel/sdk";
6
- import { getCredential } from "./credentials.js";
7
- import { buildAutoLoginCode } from "./kernel/login.js";
8
- async function main() {
9
- const apiKey = process.env["KERNEL_API_KEY"] ?? getCredential("kernel-api-key");
10
- process.env["KERNEL_API_KEY"] = apiKey;
11
- const kernel = new Kernel();
12
- const browser = await kernel.browsers.create({
13
- headless: true,
14
- stealth: true,
15
- timeout_seconds: 120,
16
- });
17
- try {
18
- const username = getCredential("cronometer-username");
19
- const password = getCredential("cronometer-password");
20
- // Login
21
- await kernel.browsers.playwright.execute(browser.session_id, {
22
- code: buildAutoLoginCode(username, password),
23
- timeout_sec: 60,
24
- });
25
- // Navigate to diary and dump nav info
26
- const result = await kernel.browsers.playwright.execute(browser.session_id, {
27
- code: `
28
- await page.goto('https://cronometer.com/#diary', { waitUntil: 'domcontentloaded', timeout: 15000 });
29
- await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
30
- await page.waitForTimeout(3000);
31
-
32
- const debug = await page.evaluate(() => {
33
- // 1. All images and their src attributes
34
- const imgs = Array.from(document.querySelectorAll('img')).map(img => ({
35
- src: img.getAttribute('src'),
36
- alt: img.getAttribute('alt'),
37
- visible: img.offsetParent !== null,
38
- width: img.offsetWidth,
39
- height: img.offsetHeight,
40
- classes: img.className,
41
- }));
42
-
43
- // 2. All buttons/clickable elements near the top of the page
44
- const buttons = Array.from(document.querySelectorAll('button, [role="button"], .gwt-PushButton')).map(el => ({
45
- text: (el.textContent || '').trim().substring(0, 80),
46
- classes: el.className,
47
- visible: el.offsetParent !== null,
48
- tag: el.tagName,
49
- }));
50
-
51
- // 3. Elements that might be date-related
52
- const dateEls = Array.from(document.querySelectorAll('[class*="date" i], [class*="calendar" i], [class*="nav" i], [class*="arrow" i], [class*="picker" i]')).map(el => ({
53
- tag: el.tagName,
54
- classes: el.className,
55
- text: (el.textContent || '').trim().substring(0, 80),
56
- visible: el.offsetParent !== null,
57
- html: el.outerHTML.substring(0, 200),
58
- }));
59
-
60
- // 4. Look for any element containing a date-like text (month name)
61
- const dateTexts = [];
62
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
63
- let node;
64
- while (node = walker.nextNode()) {
65
- const t = (node.textContent || '').trim();
66
- if (/(?:January|February|March|April|May|June|July|August|September|October|November|December)/i.test(t)) {
67
- dateTexts.push({
68
- text: t.substring(0, 100),
69
- parentTag: node.parentElement?.tagName,
70
- parentClasses: node.parentElement?.className,
71
- });
72
- }
73
- }
74
-
75
- // 5. GWT PushButton elements (Cronometer uses GWT)
76
- const pushButtons = Array.from(document.querySelectorAll('.gwt-PushButton, .gwt-PushButton-up, .gwt-PushButton-down')).map(el => ({
77
- classes: el.className,
78
- html: el.outerHTML.substring(0, 300),
79
- visible: el.offsetParent !== null,
80
- }));
81
-
82
- return { imgs, buttons, dateEls, dateTexts, pushButtons };
83
- });
84
-
85
- return debug;
86
- `,
87
- timeout_sec: 60,
88
- });
89
- console.log(JSON.stringify(result.result, null, 2));
90
- }
91
- finally {
92
- try {
93
- await kernel.browsers.deleteByID(browser.session_id);
94
- }
95
- catch { }
96
- }
97
- }
98
- main().catch(console.error);
99
- //# sourceMappingURL=debug-nav.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"debug-nav.js","sourceRoot":"","sources":["../src/debug-nav.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,MAAM,MAAM,eAAe,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,MAAO,CAAC;IAExC,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC3C,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,IAAI;QACb,eAAe,EAAE,GAAG;KACrB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,aAAa,CAAC,qBAAqB,CAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,aAAa,CAAC,qBAAqB,CAAE,CAAC;QAEvD,QAAQ;QACR,MAAM,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE;YAC3D,IAAI,EAAE,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAC5C,WAAW,EAAE,EAAE;SAChB,CAAC,CAAC;QAEH,sCAAsC;QACtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CACrD,OAAO,CAAC,UAAU,EAClB;YACE,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA2DL;YACD,WAAW,EAAE,EAAE;SAChB,CACF,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}