kalvium-worklog 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,66 +11,40 @@ npm install -g kalvium-worklog
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- # Full setup (login + discover position + schedule + phone webapp)
14
+ # Full setup (login + discover position + schedule)
15
15
  kalvium-worklog install
16
16
 
17
17
  # Or step by step:
18
18
  kalvium-worklog login # log in via browser (Google + 2FA)
19
19
  kalvium-worklog discover # auto-discover your position ID
20
20
  kalvium-worklog schedule 14:00 # daily auto-submit at 2 PM
21
- kalvium-worklog webapp # generate phone web app
22
21
  ```
23
22
 
24
23
  ## Commands
25
24
 
26
25
  | Command | Description |
27
26
  |---------|-------------|
28
- | `kalvium-worklog install` | Full setup: login + discover + schedule + webapp |
27
+ | `kalvium-worklog install` | Full setup: login + discover + schedule |
29
28
  | `kalvium-worklog login` | Log in to Kalvium (opens browser, waits for Google + 2FA) |
30
29
  | `kalvium-worklog discover` | Auto-discover your internship position ID |
31
- | `kalvium-worklog submit [text]` | Submit worklog (default: "working") |
30
+ | `kalvium-worklog submit [text]` | Submit worklog (default: "Working on assigned task") |
32
31
  | `kalvium-worklog submit "built a feature"` | Submit with custom text |
33
- | `kalvium-worklog submit --status remote` | Submit with custom status |
34
32
  | `kalvium-worklog submit --dry-run` | Test without submitting |
35
33
  | `kalvium-worklog status` | Check recent worklog entries |
36
34
  | `kalvium-worklog schedule 14:00` | Set up daily auto-submit at 2 PM |
37
35
  | `kalvium-worklog schedule --remove` | Remove daily auto-submit |
38
- | `kalvium-worklog webapp [path]` | Generate phone web app HTML |
39
36
  | `kalvium-worklog daily` | Run daily submit (used by scheduler) |
37
+ | `kalvium-worklog catchup` | Check if today's worklog is missing and submit if needed |
40
38
 
41
39
  ## Features
42
40
 
43
41
  - **Cross-platform**: macOS (launchd), Windows (Task Scheduler), Linux (cron)
44
42
  - **Auto re-login**: If the token expires, automatically opens browser, waits for login, and retries
45
43
  - **Token auto-refresh**: Refresh token rotates on each use, lasts 6 days
46
- - **Phone web app**: Submit from your iPhone/Android (no computer needed after setup)
47
44
  - **Auto-discovers position ID**: Works for any user, no hardcoded IDs
48
45
  - **Weekend skipping**: Auto-submit skips Saturdays and Sundays
49
-
50
- ## Work Statuses
51
-
52
- | Key | Label |
53
- |-----|-------|
54
- | `on_site` | Working on-site (Company location) |
55
- | `remote` | Working Remotely (Not in the Kalvium environment) |
56
- | `classroom` | Working out of the Kalvium environment (Classroom) |
57
- | `holiday` | Today was a company Holiday |
58
- | `leave` | Took an approved leave from work |
59
-
60
- ```bash
61
- kalvium-worklog submit "worked from home" --status remote
62
- ```
63
-
64
- ## Phone Web App
65
-
66
- After running `kalvium-worklog webapp`:
67
-
68
- 1. AirDrop `~/KalviumWorklog.html` to your phone
69
- 2. Open in **Safari** (iPhone) or **Chrome** (Android)
70
- 3. **Add to Home Screen**
71
- 4. Tap the icon to submit worklogs from your phone
72
-
73
- The phone app works independently — it refreshes its own token and auto-retries if the token expires.
46
+ - **Network retry**: Retries up to 5 times on network failure (covers laptop waking up, internet reconnecting)
47
+ - **Catch-up on wake**: If the scheduled time was missed (laptop asleep/offline), submits when the device wakes up
74
48
 
75
49
  ## How It Works
76
50
 
@@ -81,9 +55,10 @@ One-time setup:
81
55
  Daily auto-submit (scheduler):
82
56
  Refresh token → PUT worklog API → done
83
57
  If token expired → auto open browser → re-login → retry
58
+ If network fails → retry up to 5 times (1 min apart)
84
59
 
85
- Manual submit:
86
- kalvium-worklog submit "my update" refresh token PUT worklog
60
+ Catch-up (on login/wake, every 30 min):
61
+ Check if today's worklog is submittedif not, submit it
87
62
  ```
88
63
 
89
64
  ## Token Lifecycle
@@ -100,10 +75,10 @@ Manual submit:
100
75
  ├── refresh_token.json # Current token (auto-rotates)
101
76
  ├── config.json # Position ID + API URL
102
77
  ├── daily_submit.js # Scheduler entry point
78
+ ├── catchup_submit.js # Catch-up entry point
103
79
  └── worklog.log # Log file
104
80
 
105
81
  ~/.kalvium_profile/ # Browser profile (saved login)
106
- ~/KalviumWorklog.html # Phone web app
107
82
  ```
108
83
 
109
84
  ## Uninstall
@@ -111,7 +86,7 @@ Manual submit:
111
86
  ```bash
112
87
  kalvium-worklog schedule --remove
113
88
  npm uninstall -g kalvium-worklog
114
- rm -rf ~/.kalvium ~/.kalvium_profile ~/KalviumWorklog.html
89
+ rm -rf ~/.kalvium ~/.kalvium_profile
115
90
  ```
116
91
 
117
92
  ## Requirements
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Catch-up entry point — runs on login/wake.
4
+ * Checks if today's worklog was submitted, and if not, submits it.
5
+ * Used by the login launchd job (RunAtLoad).
6
+ */
7
+ import { catchUpSubmit } from "./submit.js";
8
+ catchUpSubmit().then((ok) => {
9
+ process.exit(ok ? 0 : 1);
10
+ });
package/dist/cli.js CHANGED
@@ -2,9 +2,8 @@
2
2
  import { Command } from "commander";
3
3
  import { captureToken } from "./capture-token.js";
4
4
  import { discoverPosition } from "./discover-position.js";
5
- import { submitWorklog, checkStatus, dailySubmit } from "./submit.js";
5
+ import { submitWorklog, checkStatus, dailySubmit, catchUpSubmit } from "./submit.js";
6
6
  import { setupScheduler, removeScheduler } from "./scheduler.js";
7
- import { generateWebapp } from "./generate-webapp.js";
8
7
  import { loadToken, saveConfig, getPositionIdFromToken } from "./config.js";
9
8
  import { existsSync } from "fs";
10
9
  import { join, dirname } from "path";
@@ -42,7 +41,6 @@ program
42
41
  program
43
42
  .command("submit [text]")
44
43
  .description("Submit worklog (default text: 'Working on assigned task')")
45
- .option("--status <type>", "Work status", "on_site")
46
44
  .option("--dry-run", "Test without submitting")
47
45
  .action(async (text, opts) => {
48
46
  // Skip weekends (unless dry run)
@@ -53,7 +51,6 @@ program
53
51
  }
54
52
  const result = await submitWorklog({
55
53
  text: text ?? "Working on assigned task",
56
- status: opts.status,
57
54
  dryRun: opts.dryRun,
58
55
  });
59
56
  if (!result.success)
@@ -69,12 +66,21 @@ program
69
66
  // ─── daily ─────────────────────────────────────────────────────────────────
70
67
  program
71
68
  .command("daily")
72
- .description("Run daily auto-submit (used by scheduler — skips weekends)")
69
+ .description("Run daily auto-submit (used by scheduler — skips weekends, retries on network failure)")
73
70
  .action(async () => {
74
71
  const ok = await dailySubmit();
75
72
  if (!ok)
76
73
  process.exit(1);
77
74
  });
75
+ // ─── catchup ───────────────────────────────────────────────────────────────
76
+ program
77
+ .command("catchup")
78
+ .description("Check if today's worklog is missing and submit if needed (runs on login/wake)")
79
+ .action(async () => {
80
+ const ok = await catchUpSubmit();
81
+ if (!ok)
82
+ process.exit(1);
83
+ });
78
84
  // ─── schedule ──────────────────────────────────────────────────────────────
79
85
  program
80
86
  .command("schedule [time]")
@@ -126,19 +132,10 @@ program
126
132
  if (!ok)
127
133
  process.exit(1);
128
134
  });
129
- // ─── webapp ────────────────────────────────────────────────────────────────
130
- program
131
- .command("webapp [path]")
132
- .description("Generate phone web app HTML (default: ~/KalviumWorklog.html)")
133
- .action(async (path) => {
134
- const ok = await generateWebapp(path);
135
- if (!ok)
136
- process.exit(1);
137
- });
138
135
  // ─── install (full setup) ──────────────────────────────────────────────────
139
136
  program
140
137
  .command("install")
141
- .description("Full setup: login + discover + schedule + webapp")
138
+ .description("Full setup: login + discover + schedule")
142
139
  .action(async () => {
143
140
  const readline = await import("readline");
144
141
  const rl = readline.createInterface({
@@ -196,9 +193,6 @@ program
196
193
  }
197
194
  }
198
195
  await setupScheduler({ hour, minute });
199
- // Step 4: Webapp
200
- console.log("\n── Step 4: Generate phone web app ──");
201
- await generateWebapp();
202
196
  console.log("\n✅ Setup complete!\n");
203
197
  console.log("Commands:");
204
198
  console.log(" kalvium-worklog submit # submit with default text");
@@ -221,7 +215,7 @@ if (process.argv.length <= 2) {
221
215
  console.log(" kalvium-worklog install");
222
216
  console.log("");
223
217
  console.log(" This will log you in, discover your position ID,");
224
- console.log(" set up daily auto-submit, and generate a phone web app.");
218
+ console.log(" and set up daily auto-submit.");
225
219
  console.log("");
226
220
  console.log(" Or run kalvium-worklog --help to see all commands.");
227
221
  console.log("");
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  export { captureToken } from "./capture-token.js";
2
2
  export { discoverPosition } from "./discover-position.js";
3
- export { submitWorklog, checkStatus, dailySubmit, STATUS_OPTIONS } from "./submit.js";
3
+ export { submitWorklog, checkStatus, dailySubmit, catchUpSubmit } from "./submit.js";
4
4
  export type { SubmitOptions, SubmitResult } from "./submit.js";
5
5
  export { setupScheduler, removeScheduler } from "./scheduler.js";
6
6
  export type { ScheduleOptions } from "./scheduler.js";
7
- export { generateWebapp } from "./generate-webapp.js";
8
7
  export { refreshToken } from "./refresh.js";
9
8
  export { loadToken, saveToken, loadConfig, saveConfig, getWorklogApi, INSTALL_DIR, TOKEN_FILE, CONFIG_FILE, LOG_FILE, } from "./config.js";
10
9
  export type { TokenData, ConfigData } from "./config.js";
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  export { captureToken } from "./capture-token.js";
2
2
  export { discoverPosition } from "./discover-position.js";
3
- export { submitWorklog, checkStatus, dailySubmit, STATUS_OPTIONS } from "./submit.js";
3
+ export { submitWorklog, checkStatus, dailySubmit, catchUpSubmit } from "./submit.js";
4
4
  export { setupScheduler, removeScheduler } from "./scheduler.js";
5
- export { generateWebapp } from "./generate-webapp.js";
6
5
  export { refreshToken } from "./refresh.js";
7
6
  export { loadToken, saveToken, loadConfig, saveConfig, getWorklogApi, INSTALL_DIR, TOKEN_FILE, CONFIG_FILE, LOG_FILE, } from "./config.js";
@@ -50,7 +50,6 @@ else if (isGlobal) {
50
50
  log(" 1. Open a browser for you to log in to Kalvium");
51
51
  log(" 2. Auto-discover your internship position ID");
52
52
  log(" 3. Set up daily auto-submit at your chosen time");
53
- log(" 4. Generate a phone web app");
54
53
  log("");
55
54
  log(" Or set up step by step:");
56
55
  log(" " + BOLD + "kalvium-worklog login" + NC + " log in");
package/dist/scheduler.js CHANGED
@@ -37,30 +37,39 @@ function formatTime(hour, minute) {
37
37
  return `${hour - 12}:${minute.toString().padStart(2, "0")} PM`;
38
38
  }
39
39
  /**
40
- * Copy the daily-submit.js script to ~/.kalvium/ as a wrapper that
41
- * imports from the npm package's dist directory (using absolute paths).
42
- * This way it works even if the package is updated.
40
+ * Copy the daily-submit.js and catchup.js scripts to ~/.kalvium/ as wrappers
41
+ * that import from the npm package's dist directory (using absolute paths).
43
42
  */
44
43
  function copyDailyScript() {
45
44
  ensureInstallDir();
46
- const dst = join(INSTALL_DIR, "daily_submit.js");
47
- // Create a wrapper that imports from the package's dist directory
48
45
  const distDir = __dirname;
49
- const wrapper = `#!/usr/bin/env node
46
+ // Daily submit wrapper
47
+ const dailyDst = join(INSTALL_DIR, "daily_submit.js");
48
+ const dailyWrapper = `#!/usr/bin/env node
50
49
  // Auto-generated by kalvium-worklog. Do not edit.
51
50
  import { dailySubmit } from "${join(distDir, "submit.js")}";
52
51
  dailySubmit().then((ok) => process.exit(ok ? 0 : 1));
53
52
  `;
54
- writeFileSync(dst, wrapper);
55
- return dst;
53
+ writeFileSync(dailyDst, dailyWrapper);
54
+ // Catch-up wrapper (runs on login/wake)
55
+ const catchupDst = join(INSTALL_DIR, "catchup_submit.js");
56
+ const catchupWrapper = `#!/usr/bin/env node
57
+ // Auto-generated by kalvium-worklog. Do not edit.
58
+ import { catchUpSubmit } from "${join(distDir, "submit.js")}";
59
+ catchUpSubmit().then((ok) => process.exit(ok ? 0 : 1));
60
+ `;
61
+ writeFileSync(catchupDst, catchupWrapper);
62
+ return dailyDst;
56
63
  }
57
64
  function setupLaunchd(hour, minute) {
58
65
  const launchAgentsDir = join(homedir(), "Library", "LaunchAgents");
59
66
  if (!existsSync(launchAgentsDir)) {
60
67
  mkdirSync(launchAgentsDir, { recursive: true });
61
68
  }
62
- const plistPath = join(launchAgentsDir, "com.kalvium.worklog.plist");
63
69
  const dailyScript = copyDailyScript();
70
+ const catchupScript = join(INSTALL_DIR, "catchup_submit.js");
71
+ // ─── 1. Scheduled daily plist (fires at chosen time) ────────────────────
72
+ const plistPath = join(launchAgentsDir, "com.kalvium.worklog.plist");
64
73
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
65
74
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
66
75
  <plist version="1.0">
@@ -103,7 +112,43 @@ function setupLaunchd(hour, minute) {
103
112
  writeFileSync(plistPath, plist);
104
113
  execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
105
114
  execSync(`launchctl load "${plistPath}"`, { stdio: "ignore" });
115
+ // ─── 2. Catch-up plist (fires on login/wake — RunAtLoad) ────────────────
116
+ // This checks if today's worklog was already submitted. If not, submits it.
117
+ // Handles the case where the scheduled time was missed (laptop asleep/offline).
118
+ const catchupPlistPath = join(launchAgentsDir, "com.kalvium.worklog.catchup.plist");
119
+ const catchupPlist = `<?xml version="1.0" encoding="UTF-8"?>
120
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
121
+ <plist version="1.0">
122
+ <dict>
123
+ <key>Label</key>
124
+ <string>com.kalvium.worklog.catchup</string>
125
+ <key>ProgramArguments</key>
126
+ <array>
127
+ <string>${process.execPath}</string>
128
+ <string>${catchupScript}</string>
129
+ </array>
130
+ <key>RunAtLoad</key>
131
+ <true/>
132
+ <key>StartInterval</key>
133
+ <integer>1800</integer>
134
+ <key>StandardOutPath</key>
135
+ <string>${LOG_FILE}</string>
136
+ <key>StandardErrorPath</key>
137
+ <string>${LOG_FILE}</string>
138
+ <key>EnvironmentVariables</key>
139
+ <dict>
140
+ <key>HOME</key>
141
+ <string>${homedir()}</string>
142
+ <key>PATH</key>
143
+ <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin</string>
144
+ </dict>
145
+ </dict>
146
+ </plist>`;
147
+ writeFileSync(catchupPlistPath, catchupPlist);
148
+ execSync(`launchctl unload "${catchupPlistPath}"`, { stdio: "ignore" });
149
+ execSync(`launchctl load "${catchupPlistPath}"`, { stdio: "ignore" });
106
150
  console.log(`Daily auto-submit installed (${formatTime(hour, minute)} every weekday)`);
151
+ console.log(" + Catch-up check on login/wake (every 30 min)");
107
152
  return true;
108
153
  }
109
154
  function setupTaskScheduler(hour, minute) {
@@ -159,6 +204,7 @@ function setupCron(hour, minute) {
159
204
  */
160
205
  export async function removeScheduler() {
161
206
  if (IS_MAC) {
207
+ // Remove daily plist
162
208
  const plistPath = join(homedir(), "Library/LaunchAgents/com.kalvium.worklog.plist");
163
209
  try {
164
210
  execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
@@ -169,7 +215,18 @@ export async function removeScheduler() {
169
215
  if (existsSync(plistPath)) {
170
216
  execSync(`rm "${plistPath}"`);
171
217
  }
172
- console.log("Daily auto-submit removed.");
218
+ // Remove catch-up plist
219
+ const catchupPlistPath = join(homedir(), "Library/LaunchAgents/com.kalvium.worklog.catchup.plist");
220
+ try {
221
+ execSync(`launchctl unload "${catchupPlistPath}"`, { stdio: "ignore" });
222
+ }
223
+ catch {
224
+ // ignore
225
+ }
226
+ if (existsSync(catchupPlistPath)) {
227
+ execSync(`rm "${catchupPlistPath}"`);
228
+ }
229
+ console.log("Daily auto-submit removed (including catch-up).");
173
230
  return true;
174
231
  }
175
232
  else if (IS_WINDOWS) {
package/dist/submit.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export interface SubmitOptions {
2
2
  text?: string;
3
- status?: string;
4
3
  dryRun?: boolean;
5
4
  }
6
5
  export interface SubmitResult {
@@ -8,7 +7,6 @@ export interface SubmitResult {
8
7
  message: string;
9
8
  worklogId?: string;
10
9
  }
11
- export declare const STATUS_OPTIONS: Record<string, string>;
12
10
  /**
13
11
  * Submit worklog with auto-relogin retry.
14
12
  * If the refresh token has expired, automatically opens a browser
@@ -22,5 +20,11 @@ export declare function checkStatus(): Promise<void>;
22
20
  /**
23
21
  * Daily auto-submit (used by scheduler).
24
22
  * Skips weekends. Logs to file.
23
+ * Retries on network failure (up to 5 times with 60s delay).
25
24
  */
26
25
  export declare function dailySubmit(): Promise<boolean>;
26
+ /**
27
+ * Catch-up check: if it's a weekday and today's worklog hasn't been
28
+ * submitted yet, submit it. Used on login/wake to handle missed runs.
29
+ */
30
+ export declare function catchUpSubmit(): Promise<boolean>;
package/dist/submit.js CHANGED
@@ -1,13 +1,8 @@
1
1
  import { getWorklogApi, log } from "./config.js";
2
2
  import { refreshToken } from "./refresh.js";
3
3
  import { captureToken } from "./capture-token.js";
4
- export const STATUS_OPTIONS = {
5
- on_site: "Working on-site (Company location)",
6
- remote: "Working Remotely (Not in the Kalvium environment)",
7
- classroom: "Working out of the Kalvium environment (Classroom)",
8
- holiday: "Today was a company Holiday",
9
- leave: "Took an approved leave from work",
10
- };
4
+ const STATUS_KEY = "on_site";
5
+ const STATUS_LABEL = "Working on-site (Company location)";
11
6
  /**
12
7
  * Submit worklog with auto-relogin retry.
13
8
  * If the refresh token has expired, automatically opens a browser
@@ -15,7 +10,6 @@ export const STATUS_OPTIONS = {
15
10
  */
16
11
  export async function submitWorklog(options = {}) {
17
12
  const text = options.text ?? "Working on assigned task";
18
- const statusKey = options.status ?? "on_site";
19
13
  const dryRun = options.dryRun ?? false;
20
14
  const worklogApi = getWorklogApi();
21
15
  if (!worklogApi) {
@@ -24,11 +18,10 @@ export async function submitWorklog(options = {}) {
24
18
  message: "No config found. Run `kalvium-worklog discover` first.",
25
19
  };
26
20
  }
27
- const statusLabel = STATUS_OPTIONS[statusKey] ?? STATUS_OPTIONS.on_site;
28
21
  const body = JSON.stringify({
29
- title: statusLabel,
30
- description: statusLabel,
31
- category: statusKey,
22
+ title: STATUS_LABEL,
23
+ description: STATUS_LABEL,
24
+ category: STATUS_KEY,
32
25
  timeSpent: 0,
33
26
  priorityLevel: "medium",
34
27
  blockers: "",
@@ -36,7 +29,7 @@ export async function submitWorklog(options = {}) {
36
29
  });
37
30
  if (dryRun) {
38
31
  console.log(`[DRY RUN] Would submit to ${worklogApi}`);
39
- console.log(` text='${text}', status=${statusKey}`);
32
+ console.log(` text='${text}', status=${STATUS_KEY}`);
40
33
  console.log(` Body: ${body}`);
41
34
  return { success: true, message: "Dry run" };
42
35
  }
@@ -177,6 +170,7 @@ export async function checkStatus() {
177
170
  /**
178
171
  * Daily auto-submit (used by scheduler).
179
172
  * Skips weekends. Logs to file.
173
+ * Retries on network failure (up to 5 times with 60s delay).
180
174
  */
181
175
  export async function dailySubmit() {
182
176
  const day = new Date().getDay();
@@ -185,12 +179,94 @@ export async function dailySubmit() {
185
179
  return true;
186
180
  }
187
181
  log("[INFO] Starting daily worklog submission...");
188
- const result = await submitWorklog({ text: "Working on assigned task", status: "on_site" });
189
- if (result.success) {
190
- log(`[INFO] SUCCESS: ${result.message}`);
182
+ // Retry up to 5 times in case of network failure
183
+ // (covers: laptop waking up, internet reconnecting, etc.)
184
+ const MAX_RETRIES = 5;
185
+ const RETRY_DELAY_MS = 60000; // 1 minute between retries
186
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
187
+ if (attempt > 1) {
188
+ log(`[INFO] Retry ${attempt}/${MAX_RETRIES} (waiting for network...)`);
189
+ }
190
+ const result = await submitWorklog({
191
+ text: "Working on assigned task",
192
+ });
193
+ if (result.success) {
194
+ log(`[INFO] SUCCESS: ${result.message}`);
195
+ return true;
196
+ }
197
+ // Check if it's a network error (retry) vs a real error (give up)
198
+ const isNetworkError = result.message.includes("fetch") ||
199
+ result.message.includes("ECONNREFUSED") ||
200
+ result.message.includes("ENOTFOUND") ||
201
+ result.message.includes("ETIMEDOUT") ||
202
+ result.message.includes("network") ||
203
+ result.message.includes("Failed to fetch") ||
204
+ result.message.includes("refresh failed");
205
+ // "Already submitted" is a success, not an error
206
+ if (result.message.includes("Already submitted")) {
207
+ log(`[INFO] ${result.message}`);
208
+ return true;
209
+ }
210
+ if (isNetworkError && attempt < MAX_RETRIES) {
211
+ log(`[WARN] Network error: ${result.message}. Retrying in 60s...`);
212
+ await sleep(RETRY_DELAY_MS);
213
+ continue;
214
+ }
215
+ // Non-network error or max retries reached
216
+ log(`[ERROR] ${result.message}`);
217
+ return false;
218
+ }
219
+ return false;
220
+ }
221
+ function sleep(ms) {
222
+ return new Promise((resolve) => setTimeout(resolve, ms));
223
+ }
224
+ /**
225
+ * Catch-up check: if it's a weekday and today's worklog hasn't been
226
+ * submitted yet, submit it. Used on login/wake to handle missed runs.
227
+ */
228
+ export async function catchUpSubmit() {
229
+ const day = new Date().getDay();
230
+ if (day === 0 || day === 6) {
231
+ return true; // weekend, nothing to do
232
+ }
233
+ log("[INFO] Catch-up check: verifying today's worklog...");
234
+ // Check if today's worklog was already submitted
235
+ const worklogApi = getWorklogApi();
236
+ if (!worklogApi) {
237
+ log("[ERROR] No config found. Run `kalvium-worklog discover` first.");
238
+ return false;
239
+ }
240
+ // Try to refresh token and check status
241
+ const refreshResult = await refreshToken();
242
+ if (!refreshResult.success || !refreshResult.accessToken) {
243
+ log("[WARN] Could not refresh token for catch-up check. Will try submit directly.");
244
+ // Fall through to submit attempt — it has its own retry/relogin logic
191
245
  }
192
246
  else {
193
- log(`[ERROR] ${result.message}`);
247
+ try {
248
+ const res = await fetch(worklogApi, {
249
+ headers: { authorization: `Bearer ${refreshResult.accessToken}` },
250
+ });
251
+ if (res.ok) {
252
+ const worklogs = await res.json();
253
+ if (Array.isArray(worklogs)) {
254
+ const today = new Date().toISOString().slice(0, 10);
255
+ const todayEntry = worklogs.find((w) => (w.worklogDate ?? "").slice(0, 10) === today &&
256
+ w.status === "complete");
257
+ if (todayEntry) {
258
+ log("[INFO] Today's worklog already submitted. No catch-up needed.");
259
+ return true;
260
+ }
261
+ }
262
+ }
263
+ }
264
+ catch {
265
+ // Network error — fall through to submit attempt
266
+ log("[WARN] Could not check status for catch-up. Will try submit directly.");
267
+ }
194
268
  }
195
- return result.success;
269
+ // Today's worklog is missing — submit it
270
+ log("[INFO] Today's worklog is missing. Running catch-up submit...");
271
+ return dailySubmit();
196
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kalvium-worklog",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "Auto-submit your daily Kalvium worklog without opening the website",
5
5
  "keywords": [
6
6
  "kalvium",
@@ -27,8 +27,7 @@
27
27
  "kworklog": "dist/cli.js"
28
28
  },
29
29
  "files": [
30
- "dist",
31
- "templates"
30
+ "dist"
32
31
  ],
33
32
  "scripts": {
34
33
  "build": "tsc",