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 +11 -36
- package/dist/catchup.d.ts +2 -0
- package/dist/catchup.js +10 -0
- package/dist/cli.js +13 -19
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/postinstall.js +0 -1
- package/dist/scheduler.js +67 -10
- package/dist/submit.d.ts +6 -2
- package/dist/submit.js +94 -18
- package/package.json +2 -3
- package/templates/worklog_standalone.html +0 -423
- package/templates/worklog_template.html +0 -387
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
|
|
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
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
60
|
+
Catch-up (on login/wake, every 30 min):
|
|
61
|
+
Check if today's worklog is submitted → if 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
|
|
89
|
+
rm -rf ~/.kalvium ~/.kalvium_profile
|
|
115
90
|
```
|
|
116
91
|
|
|
117
92
|
## Requirements
|
package/dist/catchup.js
ADDED
|
@@ -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
|
|
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
|
|
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,
|
|
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,
|
|
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";
|
package/dist/postinstall.js
CHANGED
|
@@ -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
|
|
41
|
-
*
|
|
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
|
-
|
|
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(
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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:
|
|
30
|
-
description:
|
|
31
|
-
category:
|
|
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=${
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
"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",
|