kalvium-worklog 1.0.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.
@@ -0,0 +1,121 @@
1
+ import { chromium } from "playwright";
2
+ import { PROFILE_DIR, CONFIG_FILE, ensureInstallDir, saveConfig, } from "./config.js";
3
+ /**
4
+ * Discover the user's internship position ID by capturing API calls.
5
+ * Tries headless first, falls back to headed browser for manual login.
6
+ */
7
+ export async function discoverPosition() {
8
+ ensureInstallDir();
9
+ console.log("=== Discovering internship position ID ===\n");
10
+ // Step 1: Try headless first
11
+ console.log("1. Trying headless mode (existing session)...");
12
+ let captured = await discoverViaBrowser(true);
13
+ if (!captured.token) {
14
+ console.log(" No valid session. Opening browser for manual login...");
15
+ captured = await discoverViaBrowser(false);
16
+ }
17
+ if (!captured.token) {
18
+ console.log("\nERROR: Could not capture token. Please log in and try again.");
19
+ return false;
20
+ }
21
+ console.log(` Token captured: ${captured.token.slice(0, 40)}...`);
22
+ // Step 2: Extract position ID from captured API URLs
23
+ const positionId = extractPositionId(captured.api_url ?? "");
24
+ if (!positionId) {
25
+ console.log("\nERROR: Could not determine position ID from API calls");
26
+ return false;
27
+ }
28
+ // Step 3: Save config
29
+ const worklogUrl = `https://student-api.kalvium.community/api/internships/worklogs/${positionId}`;
30
+ saveConfig({ position_id: positionId, worklog_api_url: worklogUrl });
31
+ console.log(`\n=== SUCCESS ===`);
32
+ console.log(`Position ID: ${positionId}`);
33
+ console.log(`Worklog API: ${worklogUrl}`);
34
+ console.log(`Saved to: ${CONFIG_FILE}`);
35
+ return true;
36
+ }
37
+ async function discoverViaBrowser(headless) {
38
+ const captured = {};
39
+ const context = await chromium.launchPersistentContext(PROFILE_DIR, {
40
+ headless: false,
41
+ args: [
42
+ "--disable-blink-features=AutomationControlled",
43
+ ...(headless ? ["--headless=new"] : []),
44
+ ],
45
+ });
46
+ const page = context.pages()[0] ?? (await context.newPage());
47
+ page.on("request", (req) => {
48
+ const url = req.url();
49
+ if (url.includes("student-api.kalvium")) {
50
+ const auth = req.headers()["authorization"] ?? "";
51
+ if (auth.startsWith("Bearer ")) {
52
+ captured.token = auth;
53
+ }
54
+ if (url.includes("internships")) {
55
+ captured.api_url = url;
56
+ console.log(` Found API call: ${url}`);
57
+ }
58
+ }
59
+ });
60
+ if (headless) {
61
+ console.log("Loading kalvium.community/internships (headless)...");
62
+ try {
63
+ await page.goto("https://kalvium.community/internships", {
64
+ waitUntil: "domcontentloaded",
65
+ timeout: 30000,
66
+ });
67
+ await page
68
+ .waitForLoadState("networkidle", { timeout: 20000 })
69
+ .catch(() => { });
70
+ await page.waitForTimeout(8000);
71
+ }
72
+ catch {
73
+ // ignore
74
+ }
75
+ }
76
+ else {
77
+ console.log("\n ========================================");
78
+ console.log(" Browser opened. Please log in now:");
79
+ console.log(" 1. Click 'Continue with Google'");
80
+ console.log(" 2. Sign in with your Kalvium account");
81
+ console.log(" 3. Complete 2FA if prompted");
82
+ console.log(" 4. Wait — the script continues automatically");
83
+ console.log(" ========================================\n");
84
+ await page.goto("https://kalvium.community/internships", {
85
+ waitUntil: "domcontentloaded",
86
+ timeout: 120000,
87
+ });
88
+ console.log(" Waiting for login to complete (up to 5 minutes)...");
89
+ for (let i = 0; i < 300; i++) {
90
+ if (captured.token && captured.api_url)
91
+ break;
92
+ await page.waitForTimeout(1000);
93
+ if (i > 0 && i % 30 === 0) {
94
+ const url = page.url();
95
+ if (url.includes("kalvium.community/internships") &&
96
+ !url.includes("error")) {
97
+ await page
98
+ .waitForLoadState("networkidle", { timeout: 5000 })
99
+ .catch(() => { });
100
+ }
101
+ }
102
+ }
103
+ }
104
+ await context.close();
105
+ return captured;
106
+ }
107
+ function extractPositionId(apiUrl) {
108
+ if (!apiUrl)
109
+ return null;
110
+ const parts = apiUrl.split("/");
111
+ const internshipsIdx = parts.indexOf("internships");
112
+ if (internshipsIdx === -1)
113
+ return null;
114
+ for (const part of parts.slice(internshipsIdx + 1)) {
115
+ // Position IDs are UUIDs (36 chars, 4 dashes)
116
+ if (part.length === 36 && part.split("-").length === 5) {
117
+ return part;
118
+ }
119
+ }
120
+ return null;
121
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Generate a phone-friendly web app HTML file with the refresh token embedded.
3
+ */
4
+ export declare function generateWebapp(outputPath?: string): Promise<boolean>;
@@ -0,0 +1,61 @@
1
+ import { readFileSync, writeFileSync, existsSync } from "fs";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { homedir } from "os";
5
+ import { loadToken, ensureInstallDir, getPositionIdFromToken } from "./config.js";
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ /**
8
+ * Generate a phone-friendly web app HTML file with the refresh token embedded.
9
+ */
10
+ export async function generateWebapp(outputPath) {
11
+ ensureInstallDir();
12
+ const token = loadToken();
13
+ if (!token) {
14
+ console.log("ERROR: No token found. Run `kalvium-worklog login` first.");
15
+ return false;
16
+ }
17
+ // Find template — check dist/templates (published), then ../templates (dev)
18
+ const templateCandidates = [
19
+ join(__dirname, "..", "templates", "worklog_template.html"),
20
+ join(__dirname, "templates", "worklog_template.html"),
21
+ ];
22
+ let templateHtml = null;
23
+ for (const path of templateCandidates) {
24
+ if (existsSync(path)) {
25
+ templateHtml = readFileSync(path, "utf-8");
26
+ break;
27
+ }
28
+ }
29
+ if (!templateHtml) {
30
+ console.log("ERROR: Template not found.");
31
+ return false;
32
+ }
33
+ // Inject token into HTML
34
+ const tokenJson = JSON.stringify(token);
35
+ const injectScript = `
36
+ <script>
37
+ const EMBEDDED_TOKEN = ${tokenJson};
38
+ if (!localStorage.getItem("kalvium_tokens")) {
39
+ localStorage.setItem("kalvium_tokens", JSON.stringify(EMBEDDED_TOKEN));
40
+ }
41
+ </script>
42
+ `;
43
+ const html = templateHtml.replace("</body>", injectScript + "\n</body>");
44
+ // Write output
45
+ const outPath = outputPath ?? join(homedir(), "KalviumWorklog.html");
46
+ writeFileSync(outPath, html);
47
+ console.log(` Web app generated: ${outPath}`);
48
+ console.log(` Size: ${html.length} bytes`);
49
+ // Show position ID from JWT
50
+ const posId = getPositionIdFromToken(token);
51
+ if (posId) {
52
+ console.log(` Position ID (from JWT): ${posId}`);
53
+ }
54
+ console.log(`\n To set up on phone:`);
55
+ console.log(` 1. AirDrop '${outPath}' to your phone, OR`);
56
+ console.log(` 2. Open the file in Safari → Share → Add to Home Screen`);
57
+ console.log(`\n Or manually transfer the token:`);
58
+ console.log(` - Open the web app on your phone`);
59
+ console.log(` - Tap 'Setup / Token' → paste token JSON → Save`);
60
+ return true;
61
+ }
@@ -0,0 +1,10 @@
1
+ export { captureToken } from "./capture-token.js";
2
+ export { discoverPosition } from "./discover-position.js";
3
+ export { submitWorklog, checkStatus, dailySubmit, STATUS_OPTIONS } from "./submit.js";
4
+ export type { SubmitOptions, SubmitResult } from "./submit.js";
5
+ export { setupScheduler, removeScheduler } from "./scheduler.js";
6
+ export type { ScheduleOptions } from "./scheduler.js";
7
+ export { generateWebapp } from "./generate-webapp.js";
8
+ export { refreshToken } from "./refresh.js";
9
+ export { loadToken, saveToken, loadConfig, saveConfig, getWorklogApi, INSTALL_DIR, TOKEN_FILE, CONFIG_FILE, LOG_FILE, } from "./config.js";
10
+ export type { TokenData, ConfigData } from "./config.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { captureToken } from "./capture-token.js";
2
+ export { discoverPosition } from "./discover-position.js";
3
+ export { submitWorklog, checkStatus, dailySubmit, STATUS_OPTIONS } from "./submit.js";
4
+ export { setupScheduler, removeScheduler } from "./scheduler.js";
5
+ export { generateWebapp } from "./generate-webapp.js";
6
+ export { refreshToken } from "./refresh.js";
7
+ export { loadToken, saveToken, loadConfig, saveConfig, getWorklogApi, INSTALL_DIR, TOKEN_FILE, CONFIG_FILE, LOG_FILE, } from "./config.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Postinstall script — runs automatically after `npm install kalvium-worklog`.
3
+ *
4
+ * If the package is installed globally, prompts the user to run setup.
5
+ * If installed locally (as a dependency), just prints info.
6
+ *
7
+ * IMPORTANT: This must not block or require user input during npm install
8
+ * in CI environments. It only prints guidance.
9
+ */
10
+ export {};
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Postinstall script — runs automatically after `npm install kalvium-worklog`.
3
+ *
4
+ * If the package is installed globally, prompts the user to run setup.
5
+ * If installed locally (as a dependency), just prints info.
6
+ *
7
+ * IMPORTANT: This must not block or require user input during npm install
8
+ * in CI environments. It only prints guidance.
9
+ */
10
+ import { existsSync } from "fs";
11
+ import { join } from "path";
12
+ import { homedir } from "os";
13
+ const TOKEN_FILE = join(homedir(), ".kalvium", "refresh_token.json");
14
+ const CONFIG_FILE = join(homedir(), ".kalvium", "config.json");
15
+ // Detect if this is a global install
16
+ const isGlobal = process.env.npm_config_global === "true";
17
+ // Don't run postinstall in CI or during npm publish
18
+ const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
19
+ if (isCI) {
20
+ process.exit(0);
21
+ }
22
+ // Use stderr for output — npm suppresses stdout from lifecycle scripts
23
+ // but stderr is always shown
24
+ const log = (msg) => console.error(msg);
25
+ const GREEN = "\x1b[0;32m";
26
+ const CYAN = "\x1b[0;36m";
27
+ const YELLOW = "\x1b[1;33m";
28
+ const BOLD = "\x1b[1m";
29
+ const NC = "\x1b[0m";
30
+ log("");
31
+ log(CYAN + "==".repeat(25) + NC);
32
+ log(CYAN + " " + BOLD + "Kalvium Worklog" + NC + CYAN + " installed successfully!" + " ".repeat(7) + NC);
33
+ log(CYAN + "==".repeat(25) + NC);
34
+ log("");
35
+ const alreadySetup = existsSync(TOKEN_FILE) && existsSync(CONFIG_FILE);
36
+ if (alreadySetup) {
37
+ log(GREEN + "OK" + NC + " You are already set up!");
38
+ log("");
39
+ log(" Quick commands:");
40
+ log(" " + BOLD + "kalvium-worklog submit" + NC + " submit worklog");
41
+ log(" " + BOLD + "kalvium-worklog status" + NC + " check status");
42
+ log("");
43
+ }
44
+ else if (isGlobal) {
45
+ log(" To get started, run:");
46
+ log("");
47
+ log(" " + BOLD + "kalvium-worklog install" + NC);
48
+ log("");
49
+ log(" This will:");
50
+ log(" 1. Open a browser for you to log in to Kalvium");
51
+ log(" 2. Auto-discover your internship position ID");
52
+ log(" 3. Set up daily auto-submit at your chosen time");
53
+ log(" 4. Generate a phone web app");
54
+ log("");
55
+ log(" Or set up step by step:");
56
+ log(" " + BOLD + "kalvium-worklog login" + NC + " log in");
57
+ log(" " + BOLD + "kalvium-worklog discover" + NC + " find position ID");
58
+ log(" " + BOLD + "kalvium-worklog schedule 14:00" + NC + " daily at 2 PM");
59
+ log("");
60
+ }
61
+ else {
62
+ // Local install — use npx
63
+ log(" To get started, run:");
64
+ log("");
65
+ log(" " + BOLD + "npx kalvium-worklog install" + NC);
66
+ log("");
67
+ log(" Or install globally first:");
68
+ log(" " + BOLD + "npm install -g kalvium-worklog" + NC);
69
+ log(" " + BOLD + "kalvium-worklog install" + NC);
70
+ log("");
71
+ }
72
+ log(" Docs: " + YELLOW + "https://github.com/kp/kalvium-worklog" + NC);
73
+ log("");
@@ -0,0 +1,7 @@
1
+ export declare function log(message: string): void;
2
+ export interface RefreshResult {
3
+ success: boolean;
4
+ accessToken?: string;
5
+ error?: string;
6
+ }
7
+ export declare function refreshToken(): Promise<RefreshResult>;
@@ -0,0 +1,42 @@
1
+ import { appendFileSync } from "fs";
2
+ import { LOG_FILE, ensureInstallDir } from "./config.js";
3
+ export function log(message) {
4
+ ensureInstallDir();
5
+ const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
6
+ const line = `${timestamp} ${message}\n`;
7
+ try {
8
+ appendFileSync(LOG_FILE, line);
9
+ }
10
+ catch {
11
+ // ignore
12
+ }
13
+ console.log(message);
14
+ }
15
+ export async function refreshToken() {
16
+ const { loadToken, saveToken, KEYCLOAK_URL, CLIENT_ID } = await import("./config.js");
17
+ const token = loadToken();
18
+ if (!token?.refresh_token) {
19
+ return { success: false, error: "No refresh token found" };
20
+ }
21
+ const body = new URLSearchParams({
22
+ grant_type: "refresh_token",
23
+ client_id: CLIENT_ID,
24
+ refresh_token: token.refresh_token,
25
+ });
26
+ try {
27
+ const res = await fetch(KEYCLOAK_URL, {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
30
+ body,
31
+ });
32
+ const data = await res.json();
33
+ if (data.error) {
34
+ return { success: false, error: data.error };
35
+ }
36
+ saveToken(data);
37
+ return { success: true, accessToken: data.access_token };
38
+ }
39
+ catch (e) {
40
+ return { success: false, error: String(e) };
41
+ }
42
+ }
@@ -0,0 +1,15 @@
1
+ export interface ScheduleOptions {
2
+ hour: number;
3
+ minute: number;
4
+ }
5
+ /**
6
+ * Set up daily auto-submit scheduler.
7
+ * macOS: launchd
8
+ * Windows: Task Scheduler
9
+ * Linux: cron
10
+ */
11
+ export declare function setupScheduler(options: ScheduleOptions): Promise<boolean>;
12
+ /**
13
+ * Remove the scheduled task.
14
+ */
15
+ export declare function removeScheduler(): Promise<boolean>;
@@ -0,0 +1,200 @@
1
+ import { homedir, platform } from "os";
2
+ import { join } from "path";
3
+ import { writeFileSync, existsSync, mkdirSync } from "fs";
4
+ import { execSync } from "child_process";
5
+ import { INSTALL_DIR, LOG_FILE, ensureInstallDir } from "./config.js";
6
+ import { dirname } from "path";
7
+ import { fileURLToPath } from "url";
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const IS_MAC = platform() === "darwin";
10
+ const IS_WINDOWS = platform() === "win32";
11
+ const IS_LINUX = platform() === "linux";
12
+ /**
13
+ * Set up daily auto-submit scheduler.
14
+ * macOS: launchd
15
+ * Windows: Task Scheduler
16
+ * Linux: cron
17
+ */
18
+ export async function setupScheduler(options) {
19
+ const { hour, minute } = options;
20
+ if (IS_MAC) {
21
+ return setupLaunchd(hour, minute);
22
+ }
23
+ else if (IS_WINDOWS) {
24
+ return setupTaskScheduler(hour, minute);
25
+ }
26
+ else if (IS_LINUX) {
27
+ return setupCron(hour, minute);
28
+ }
29
+ console.log("Unknown OS — cannot set up scheduler. Use `kalvium-worklog submit` manually.");
30
+ return false;
31
+ }
32
+ function formatTime(hour, minute) {
33
+ if (hour < 12)
34
+ return `${hour}:${minute.toString().padStart(2, "0")} AM`;
35
+ if (hour === 12)
36
+ return `12:${minute.toString().padStart(2, "0")} PM`;
37
+ return `${hour - 12}:${minute.toString().padStart(2, "0")} PM`;
38
+ }
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.
43
+ */
44
+ function copyDailyScript() {
45
+ ensureInstallDir();
46
+ const dst = join(INSTALL_DIR, "daily_submit.js");
47
+ // Create a wrapper that imports from the package's dist directory
48
+ const distDir = __dirname;
49
+ const wrapper = `#!/usr/bin/env node
50
+ // Auto-generated by kalvium-worklog. Do not edit.
51
+ import { dailySubmit } from "${join(distDir, "submit.js")}";
52
+ dailySubmit().then((ok) => process.exit(ok ? 0 : 1));
53
+ `;
54
+ writeFileSync(dst, wrapper);
55
+ return dst;
56
+ }
57
+ function setupLaunchd(hour, minute) {
58
+ const launchAgentsDir = join(homedir(), "Library", "LaunchAgents");
59
+ if (!existsSync(launchAgentsDir)) {
60
+ mkdirSync(launchAgentsDir, { recursive: true });
61
+ }
62
+ const plistPath = join(launchAgentsDir, "com.kalvium.worklog.plist");
63
+ const dailyScript = copyDailyScript();
64
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
65
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
66
+ <plist version="1.0">
67
+ <dict>
68
+ <key>Label</key>
69
+ <string>com.kalvium.worklog</string>
70
+ <key>ProgramArguments</key>
71
+ <array>
72
+ <string>${process.execPath}</string>
73
+ <string>${dailyScript}</string>
74
+ </array>
75
+ <key>StartCalendarInterval</key>
76
+ <dict>
77
+ <key>Hour</key>
78
+ <integer>${hour}</integer>
79
+ <key>Minute</key>
80
+ <integer>${minute}</integer>
81
+ <key>Weekday</key>
82
+ <array>
83
+ <integer>1</integer>
84
+ <integer>2</integer>
85
+ <integer>3</integer>
86
+ <integer>4</integer>
87
+ <integer>5</integer>
88
+ </array>
89
+ </dict>
90
+ <key>StandardOutPath</key>
91
+ <string>${LOG_FILE}</string>
92
+ <key>StandardErrorPath</key>
93
+ <string>${LOG_FILE}</string>
94
+ <key>EnvironmentVariables</key>
95
+ <dict>
96
+ <key>HOME</key>
97
+ <string>${homedir()}</string>
98
+ <key>PATH</key>
99
+ <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin</string>
100
+ </dict>
101
+ </dict>
102
+ </plist>`;
103
+ writeFileSync(plistPath, plist);
104
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
105
+ execSync(`launchctl load "${plistPath}"`, { stdio: "ignore" });
106
+ console.log(`Daily auto-submit installed (${formatTime(hour, minute)} every weekday)`);
107
+ return true;
108
+ }
109
+ function setupTaskScheduler(hour, minute) {
110
+ const dailyScript = copyDailyScript();
111
+ const batPath = join(INSTALL_DIR, "daily_submit.bat");
112
+ // Create batch wrapper
113
+ writeFileSync(batPath, `@echo off\r\n"${process.execPath}" "${dailyScript}"\r\n`);
114
+ // Remove existing task
115
+ try {
116
+ execSync('schtasks /Delete /TN "KalviumWorklog" /F', { stdio: "ignore" });
117
+ }
118
+ catch {
119
+ // task doesn't exist yet
120
+ }
121
+ const timeStr = `${hour.toString().padStart(2, "0")}:${minute
122
+ .toString()
123
+ .padStart(2, "0")}`;
124
+ try {
125
+ execSync(`schtasks /Create /TN "KalviumWorklog" /TR "${batPath}" /SC WEEKLY /D MON,TUE,WED,THU,FRI /ST ${timeStr} /F`, { stdio: "pipe" });
126
+ console.log(`Daily auto-submit installed (${formatTime(hour, minute)} every weekday)`);
127
+ return true;
128
+ }
129
+ catch (e) {
130
+ console.log(`Failed to create scheduled task: ${e}`);
131
+ return false;
132
+ }
133
+ }
134
+ function setupCron(hour, minute) {
135
+ const dailyScript = copyDailyScript();
136
+ const cronLine = `${minute} ${hour} * * 1-5 ${process.execPath} ${dailyScript} >> ${LOG_FILE} 2>&1`;
137
+ // Read existing crontab
138
+ let crontab = "";
139
+ try {
140
+ crontab = execSync("crontab -l", { encoding: "utf-8" });
141
+ }
142
+ catch {
143
+ // no crontab yet
144
+ }
145
+ // Remove existing kalvium entry
146
+ const lines = crontab
147
+ .split("\n")
148
+ .filter((line) => !line.includes("kalvium") && !line.includes("KalviumWorklog"));
149
+ // Add new entry
150
+ lines.push(cronLine);
151
+ // Write back
152
+ const newCrontab = lines.join("\n") + "\n";
153
+ execSync(`echo "${newCrontab.replace(/"/g, '\\"')}" | crontab -`);
154
+ console.log(`Daily auto-submit installed (${formatTime(hour, minute)} every weekday via cron)`);
155
+ return true;
156
+ }
157
+ /**
158
+ * Remove the scheduled task.
159
+ */
160
+ export async function removeScheduler() {
161
+ if (IS_MAC) {
162
+ const plistPath = join(homedir(), "Library/LaunchAgents/com.kalvium.worklog.plist");
163
+ try {
164
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
165
+ }
166
+ catch {
167
+ // ignore
168
+ }
169
+ if (existsSync(plistPath)) {
170
+ execSync(`rm "${plistPath}"`);
171
+ }
172
+ console.log("Daily auto-submit removed.");
173
+ return true;
174
+ }
175
+ else if (IS_WINDOWS) {
176
+ try {
177
+ execSync('schtasks /Delete /TN "KalviumWorklog" /F', { stdio: "ignore" });
178
+ }
179
+ catch {
180
+ // ignore
181
+ }
182
+ console.log("Daily auto-submit removed.");
183
+ return true;
184
+ }
185
+ else if (IS_LINUX) {
186
+ try {
187
+ const crontab = execSync("crontab -l", { encoding: "utf-8" });
188
+ const lines = crontab
189
+ .split("\n")
190
+ .filter((line) => !line.includes("kalvium") && !line.includes("KalviumWorklog"));
191
+ execSync(`echo "${lines.join("\n")}" | crontab -`);
192
+ }
193
+ catch {
194
+ // ignore
195
+ }
196
+ console.log("Daily auto-submit removed.");
197
+ return true;
198
+ }
199
+ return false;
200
+ }
@@ -0,0 +1,26 @@
1
+ export interface SubmitOptions {
2
+ text?: string;
3
+ status?: string;
4
+ dryRun?: boolean;
5
+ }
6
+ export interface SubmitResult {
7
+ success: boolean;
8
+ message: string;
9
+ worklogId?: string;
10
+ }
11
+ export declare const STATUS_OPTIONS: Record<string, string>;
12
+ /**
13
+ * Submit worklog with auto-relogin retry.
14
+ * If the refresh token has expired, automatically opens a browser
15
+ * for re-login and retries the submission.
16
+ */
17
+ export declare function submitWorklog(options?: SubmitOptions): Promise<SubmitResult>;
18
+ /**
19
+ * Check recent worklog status with auto-relogin retry.
20
+ */
21
+ export declare function checkStatus(): Promise<void>;
22
+ /**
23
+ * Daily auto-submit (used by scheduler).
24
+ * Skips weekends. Logs to file.
25
+ */
26
+ export declare function dailySubmit(): Promise<boolean>;