@workjournal/cli 0.1.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 ADDED
@@ -0,0 +1,93 @@
1
+ # @workjournal/cli
2
+
3
+ Command-line tool for authenticating with [Workjournal](https://workjournal.pro).
4
+
5
+ ## Installation
6
+
7
+ Run on demand without installing:
8
+
9
+ ```bash
10
+ npx @workjournal/cli login
11
+ ```
12
+
13
+ Or install globally:
14
+
15
+ ```bash
16
+ npm install -g @workjournal/cli
17
+ workjournal login
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ | Command | Description |
23
+ |---------|-------------|
24
+ | `workjournal login` | Interactive paste-back login (terminal only) |
25
+ | `workjournal login start` | Print authorize URL and store PKCE state |
26
+ | `workjournal login finish <CODE>` | Exchange the pasted code for credentials |
27
+ | `workjournal logout` | Remove stored credentials |
28
+ | `workjournal whoami` | Check if authenticated |
29
+ | `workjournal status` | Show token expiry details |
30
+
31
+ ## How login works
32
+
33
+ The CLI uses OAuth out-of-band redirect (`urn:ietf:wg:oauth:2.0:oob`) with PKCE. There is no port binding, no auto-opened browser, and no callback server. The flow works equally well in local terminals, SSH sessions, dev containers, and CI.
34
+
35
+ ### Interactive (terminal)
36
+
37
+ ```bash
38
+ $ workjournal login
39
+ Open this URL in any browser:
40
+
41
+ https://app.workjournal.pro/authorize?...
42
+
43
+ Code: ABC123DE
44
+ Authenticated successfully!
45
+ Credentials saved to ~/.workjournal/credentials.json
46
+ ```
47
+
48
+ The CLI prints an authorize URL, you open it in any browser, log in, click Approve, copy the displayed 8-character code, paste it into the terminal. Done.
49
+
50
+ ### Two-phase (assistant-driven)
51
+
52
+ When invoked by an AI assistant via a one-shot Bash call (e.g. the [Workjournal journal skill](https://github.com/workjournal-pro/skill)), the same flow runs as two independent commands with PKCE state passed via a temp file on disk:
53
+
54
+ ```bash
55
+ workjournal login start # prints URL, stores PKCE state in ~/.workjournal/cli-login-state.json
56
+ # user opens URL, approves, copies the displayed code
57
+ workjournal login finish ABC123DE # reads state, exchanges code, writes credentials
58
+ ```
59
+
60
+ This is what makes the same flow drivable from a skill — the assistant doesn't need an interactive stdin to a still-running process.
61
+
62
+ ## Credential storage
63
+
64
+ Credentials are written to `~/.workjournal/credentials.json` with mode `0600`, in a directory created with mode `0700`.
65
+
66
+ ```
67
+ ~/.workjournal/
68
+ ├── credentials.json # { access_token, refresh_token, expires_at }
69
+ └── cli-login-state.json # ephemeral PKCE state, deleted after `login finish`
70
+ ```
71
+
72
+ The CLI never stores passwords. The MCP server (`@workjournal/mcp-server`) reads `credentials.json` automatically — there's no separate setup.
73
+
74
+ ## Headless environments
75
+
76
+ The CLI's normal flow already works headless — you do not need a browser on the same machine as the CLI. You only need a browser somewhere (your laptop, your phone) to load the authorize URL.
77
+
78
+ If you need to skip interactive auth entirely (for example in CI), set `WORKJOURNAL_API_KEY` to a valid Supabase JWT and the MCP server will use that instead of `credentials.json`.
79
+
80
+ ## Security
81
+
82
+ - **PKCE binds the URL to the redemption.** Even if someone intercepts the displayed code, they can't redeem it without the verifier sitting on the local disk.
83
+ - **Codes are single-use** and expire 5 minutes after `login start`.
84
+ - **No tokens in URLs** — the access token never appears in any redirect.
85
+ - **Constant-time comparison** of the PKCE verifier on the API side prevents timing attacks.
86
+ - **No long-lived listener** on the user's machine — the CLI binds no sockets.
87
+
88
+ ## Links
89
+
90
+ - [Workjournal](https://workjournal.pro)
91
+ - [Get Started](https://workjournal.pro/docs/get-started)
92
+ - [GitHub](https://github.com/workjournal-pro/workjournal)
93
+ - [CLI auth workflow reference](https://github.com/workjournal-pro/workjournal/blob/main/docs/cli-tool-auth-workflow.md)
package/dist/auth.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Phase 1: generate PKCE, write the in-progress state file, print the authorize URL.
3
+ *
4
+ * The state file holds the verifier between this command and `loginFinish`. The
5
+ * assistant (or the user) is expected to open the URL, approve the request, and
6
+ * paste the displayed code back into a `workjournal login finish <CODE>` call.
7
+ */
8
+ export declare function loginStart(): void;
9
+ /**
10
+ * Phase 2: read the verifier from the state file, exchange the pasted code for
11
+ * Supabase session tokens, persist them.
12
+ *
13
+ * On API failure the state file is left in place so the user can retry `finish`
14
+ * with a corrected code (subject to the server-side 5-minute TTL on the code).
15
+ * The state file is deleted only on a fully successful exchange.
16
+ */
17
+ export declare function loginFinish(rawCode: string): Promise<void>;
18
+ /**
19
+ * Convenience wrapper for direct terminal use: runs `start`, prompts for the
20
+ * code via readline, then runs `finish` in the same process. Same on-disk
21
+ * shape as the two-command flow — the state file is written and deleted as
22
+ * a side effect.
23
+ *
24
+ * Refuses to run when stdin is not a TTY (e.g. when invoked by an assistant
25
+ * via Bash) and points the caller at the two-phase commands instead.
26
+ */
27
+ export declare function loginInteractive(): Promise<void>;
28
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAgDA;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAcjC;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyCtD"}
package/dist/auth.js ADDED
@@ -0,0 +1,164 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { createInterface } from 'node:readline/promises';
3
+ import { clearLoginState, isLoginStateExpired, readLoginState, writeCredentials, writeLoginState, } from './credentials.js';
4
+ const APP_URL = process.env['WORKJOURNAL_APP_URL'] ?? 'https://app.workjournal.pro';
5
+ const API_URL = process.env['WORKJOURNAL_API_URL'] ?? 'https://api.workjournal.pro';
6
+ const OOB_REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob';
7
+ function generatePkce() {
8
+ const verifier = randomBytes(32).toString('base64url');
9
+ const challenge = createHash('sha256').update(verifier).digest('base64url');
10
+ return { verifier, challenge };
11
+ }
12
+ function generateState() {
13
+ return randomBytes(16).toString('hex');
14
+ }
15
+ function buildAuthorizeUrl(challenge) {
16
+ const state = generateState();
17
+ return (`${APP_URL}/authorize?client_id=workjournal-cli` +
18
+ `&redirect_uri=${encodeURIComponent(OOB_REDIRECT_URI)}` +
19
+ `&state=${state}` +
20
+ `&code_challenge=${challenge}` +
21
+ `&code_challenge_method=S256`);
22
+ }
23
+ /**
24
+ * Phase 1: generate PKCE, write the in-progress state file, print the authorize URL.
25
+ *
26
+ * The state file holds the verifier between this command and `loginFinish`. The
27
+ * assistant (or the user) is expected to open the URL, approve the request, and
28
+ * paste the displayed code back into a `workjournal login finish <CODE>` call.
29
+ */
30
+ export function loginStart() {
31
+ const { verifier, challenge } = generatePkce();
32
+ const state = {
33
+ verifier,
34
+ challenge,
35
+ created_at: new Date().toISOString(),
36
+ };
37
+ writeLoginState(state);
38
+ const url = buildAuthorizeUrl(challenge);
39
+ process.stdout.write('Open this URL in any browser:\n\n');
40
+ process.stdout.write(` ${url}\n\n`);
41
+ process.stdout.write('After approving, run:\n\n');
42
+ process.stdout.write(' workjournal login finish <CODE>\n');
43
+ }
44
+ /**
45
+ * Phase 2: read the verifier from the state file, exchange the pasted code for
46
+ * Supabase session tokens, persist them.
47
+ *
48
+ * On API failure the state file is left in place so the user can retry `finish`
49
+ * with a corrected code (subject to the server-side 5-minute TTL on the code).
50
+ * The state file is deleted only on a fully successful exchange.
51
+ */
52
+ export async function loginFinish(rawCode) {
53
+ const code = rawCode.trim().toUpperCase();
54
+ if (!code) {
55
+ process.stderr.write('Error: missing code argument.\nUsage: workjournal login finish <CODE>\n');
56
+ process.exit(1);
57
+ }
58
+ const state = readLoginState();
59
+ if (!state) {
60
+ process.stderr.write('Error: no login in progress. Run `workjournal login start` first.\n');
61
+ process.exit(1);
62
+ }
63
+ if (isLoginStateExpired(state)) {
64
+ clearLoginState();
65
+ process.stderr.write('Error: login state expired. Run `workjournal login start` again.\n');
66
+ process.exit(1);
67
+ }
68
+ await exchangeAndPersist(code, state.verifier);
69
+ clearLoginState();
70
+ }
71
+ /**
72
+ * Convenience wrapper for direct terminal use: runs `start`, prompts for the
73
+ * code via readline, then runs `finish` in the same process. Same on-disk
74
+ * shape as the two-command flow — the state file is written and deleted as
75
+ * a side effect.
76
+ *
77
+ * Refuses to run when stdin is not a TTY (e.g. when invoked by an assistant
78
+ * via Bash) and points the caller at the two-phase commands instead.
79
+ */
80
+ export async function loginInteractive() {
81
+ if (!process.stdin.isTTY) {
82
+ process.stderr.write('Error: `workjournal login` with no subcommand requires an interactive terminal.\n' +
83
+ 'For non-interactive use, run:\n' +
84
+ ' workjournal login start\n' +
85
+ ' workjournal login finish <CODE>\n');
86
+ process.exit(1);
87
+ }
88
+ const { verifier, challenge } = generatePkce();
89
+ const state = {
90
+ verifier,
91
+ challenge,
92
+ created_at: new Date().toISOString(),
93
+ };
94
+ writeLoginState(state);
95
+ const url = buildAuthorizeUrl(challenge);
96
+ process.stdout.write('Open this URL in any browser:\n\n');
97
+ process.stdout.write(` ${url}\n\n`);
98
+ process.stdout.write('After approving, paste the code shown on the page below.\n\n');
99
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
100
+ let code;
101
+ try {
102
+ const answer = await rl.question('Code: ');
103
+ code = answer.trim().toUpperCase();
104
+ }
105
+ finally {
106
+ rl.close();
107
+ }
108
+ if (!code) {
109
+ clearLoginState();
110
+ process.stderr.write('No code entered. Aborting.\n');
111
+ process.exit(1);
112
+ }
113
+ await exchangeAndPersist(code, verifier);
114
+ clearLoginState();
115
+ }
116
+ async function exchangeAndPersist(code, verifier) {
117
+ const controller = new AbortController();
118
+ const timeout = setTimeout(() => controller.abort(), 15_000);
119
+ let res;
120
+ try {
121
+ res = await fetch(`${API_URL}/v1/auth/cli/exchange`, {
122
+ method: 'POST',
123
+ headers: { 'Content-Type': 'application/json' },
124
+ body: JSON.stringify({ code, code_verifier: verifier }),
125
+ signal: controller.signal,
126
+ });
127
+ }
128
+ catch (error) {
129
+ clearTimeout(timeout);
130
+ const message = error instanceof Error && error.name === 'AbortError'
131
+ ? 'Request timed out after 15s'
132
+ : error instanceof Error
133
+ ? error.message
134
+ : String(error);
135
+ process.stderr.write(`Failed to exchange code: ${message}\n`);
136
+ process.exit(1);
137
+ }
138
+ clearTimeout(timeout);
139
+ if (!res.ok) {
140
+ const text = await res.text();
141
+ let message = text;
142
+ try {
143
+ const parsed = JSON.parse(text);
144
+ if (parsed.error?.message)
145
+ message = parsed.error.message;
146
+ }
147
+ catch {
148
+ // keep raw text
149
+ }
150
+ process.stderr.write(`Failed to exchange code (${res.status}): ${message}\n`);
151
+ process.exit(1);
152
+ }
153
+ const data = (await res.json());
154
+ const expiresAt = new Date(Date.now() + data.expires_in * 1000).toISOString();
155
+ const creds = {
156
+ access_token: data.access_token,
157
+ expires_at: expiresAt,
158
+ ...(data.refresh_token ? { refresh_token: data.refresh_token } : {}),
159
+ };
160
+ writeCredentials(creds);
161
+ process.stdout.write('\nAuthenticated successfully!\n');
162
+ process.stdout.write('Credentials saved to ~/.workjournal/credentials.json\n');
163
+ }
164
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export interface StoredCredentials {
2
+ access_token: string;
3
+ refresh_token?: string;
4
+ expires_at: string;
5
+ }
6
+ export interface LoginState {
7
+ verifier: string;
8
+ challenge: string;
9
+ created_at: string;
10
+ }
11
+ export declare function readCredentials(): StoredCredentials | null;
12
+ export declare function writeCredentials(creds: StoredCredentials): void;
13
+ export declare function clearCredentials(): void;
14
+ export declare function writeLoginState(state: LoginState): void;
15
+ /**
16
+ * Read the in-progress login state file. Returns null if missing or unreadable.
17
+ * Does NOT enforce TTL — callers should check `created_at` against `LOGIN_STATE_TTL_MS`.
18
+ */
19
+ export declare function readLoginState(): LoginState | null;
20
+ export declare function clearLoginState(): void;
21
+ export declare function isLoginStateExpired(state: LoginState): boolean;
22
+ export declare function isExpired(creds: StoredCredentials): boolean;
23
+ export declare function refreshAccessToken(refreshToken: string): Promise<StoredCredentials>;
24
+ /**
25
+ * Get a valid access token, refreshing if expired.
26
+ * Returns null if no credentials are stored.
27
+ */
28
+ export declare function getAccessToken(): Promise<string | null>;
29
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,iBAAiB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AASD,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACnB;AAQD,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CAU1D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAI/D;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAIvD;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,UAAU,GAAG,IAAI,CA0BlD;AAED,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAI9D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAE3D;AAID,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0BzF;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiB7D"}
@@ -0,0 +1,125 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const CONFIG_DIR = join(homedir(), '.workjournal');
5
+ const CREDENTIALS_PATH = join(CONFIG_DIR, 'credentials.json');
6
+ const CREDENTIALS_TMP_PATH = `${CREDENTIALS_PATH}.tmp`;
7
+ const LOGIN_STATE_PATH = join(CONFIG_DIR, 'cli-login-state.json');
8
+ const LOGIN_STATE_TMP_PATH = `${LOGIN_STATE_PATH}.tmp`;
9
+ const LOGIN_STATE_TTL_MS = 10 * 60 * 1000;
10
+ function ensureConfigDir() {
11
+ if (!existsSync(CONFIG_DIR)) {
12
+ mkdirSync(CONFIG_DIR, { mode: 0o700, recursive: true });
13
+ }
14
+ }
15
+ export function readCredentials() {
16
+ if (!existsSync(CREDENTIALS_PATH)) {
17
+ return null;
18
+ }
19
+ try {
20
+ const raw = readFileSync(CREDENTIALS_PATH, 'utf-8');
21
+ return JSON.parse(raw);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ export function writeCredentials(creds) {
28
+ ensureConfigDir();
29
+ writeFileSync(CREDENTIALS_TMP_PATH, `${JSON.stringify(creds, null, '\t')}\n`, { mode: 0o600 });
30
+ renameSync(CREDENTIALS_TMP_PATH, CREDENTIALS_PATH);
31
+ }
32
+ export function clearCredentials() {
33
+ if (existsSync(CREDENTIALS_PATH)) {
34
+ unlinkSync(CREDENTIALS_PATH);
35
+ }
36
+ }
37
+ export function writeLoginState(state) {
38
+ ensureConfigDir();
39
+ writeFileSync(LOGIN_STATE_TMP_PATH, `${JSON.stringify(state, null, '\t')}\n`, { mode: 0o600 });
40
+ renameSync(LOGIN_STATE_TMP_PATH, LOGIN_STATE_PATH);
41
+ }
42
+ /**
43
+ * Read the in-progress login state file. Returns null if missing or unreadable.
44
+ * Does NOT enforce TTL — callers should check `created_at` against `LOGIN_STATE_TTL_MS`.
45
+ */
46
+ export function readLoginState() {
47
+ if (!existsSync(LOGIN_STATE_PATH)) {
48
+ return null;
49
+ }
50
+ try {
51
+ const raw = readFileSync(LOGIN_STATE_PATH, 'utf-8');
52
+ const parsed = JSON.parse(raw);
53
+ if (!parsed ||
54
+ typeof parsed !== 'object' ||
55
+ typeof parsed.verifier !== 'string' ||
56
+ parsed.verifier.length === 0 ||
57
+ typeof parsed.challenge !== 'string' ||
58
+ parsed.challenge.length === 0 ||
59
+ typeof parsed.created_at !== 'string') {
60
+ return null;
61
+ }
62
+ return {
63
+ verifier: parsed.verifier,
64
+ challenge: parsed.challenge,
65
+ created_at: parsed.created_at,
66
+ };
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ export function clearLoginState() {
73
+ if (existsSync(LOGIN_STATE_PATH)) {
74
+ unlinkSync(LOGIN_STATE_PATH);
75
+ }
76
+ }
77
+ export function isLoginStateExpired(state) {
78
+ const created = Date.parse(state.created_at);
79
+ if (!Number.isFinite(created))
80
+ return true;
81
+ return Date.now() - created > LOGIN_STATE_TTL_MS;
82
+ }
83
+ export function isExpired(creds) {
84
+ return new Date(creds.expires_at) < new Date();
85
+ }
86
+ const API_URL = process.env['WORKJOURNAL_API_URL'] ?? 'https://api.workjournal.pro';
87
+ export async function refreshAccessToken(refreshToken) {
88
+ const res = await fetch(`${API_URL}/v1/auth/refresh`, {
89
+ method: 'POST',
90
+ headers: { 'Content-Type': 'application/json' },
91
+ body: JSON.stringify({ refresh_token: refreshToken }),
92
+ });
93
+ if (!res.ok) {
94
+ const text = await res.text();
95
+ throw new Error(`Token refresh failed (${res.status}): ${text}`);
96
+ }
97
+ const data = (await res.json());
98
+ const expiresAt = new Date(Date.now() + data.expires_in * 1000).toISOString();
99
+ const creds = {
100
+ access_token: data.access_token,
101
+ refresh_token: data.refresh_token,
102
+ expires_at: expiresAt,
103
+ };
104
+ writeCredentials(creds);
105
+ return creds;
106
+ }
107
+ /**
108
+ * Get a valid access token, refreshing if expired.
109
+ * Returns null if no credentials are stored.
110
+ */
111
+ export async function getAccessToken() {
112
+ const creds = readCredentials();
113
+ if (!creds) {
114
+ return null;
115
+ }
116
+ if (isExpired(creds)) {
117
+ if (!creds.refresh_token) {
118
+ throw new Error('Access token expired and no refresh token available. Please run `workjournal login` again.');
119
+ }
120
+ const refreshed = await refreshAccessToken(creds.refresh_token);
121
+ return refreshed.access_token;
122
+ }
123
+ return creds.access_token;
124
+ }
125
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AACnD,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAC9D,MAAM,oBAAoB,GAAG,GAAG,gBAAgB,MAAM,CAAC;AACvD,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;AAClE,MAAM,oBAAoB,GAAG,GAAG,gBAAgB,MAAM,CAAC;AACvD,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAQ1C,SAAS,eAAe;IACvB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;AACF,CAAC;AAED,MAAM,UAAU,eAAe;IAC9B,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAwB;IACxD,eAAe,EAAE,CAAC;IAClB,aAAa,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/F,UAAU,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC/B,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC9B,CAAC;AACF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAiB;IAChD,eAAe,EAAE,CAAC;IAClB,aAAa,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/F,UAAU,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC7B,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QACtD,IACC,CAAC,MAAM;YACP,OAAO,MAAM,KAAK,QAAQ;YAC1B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YACnC,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAC5B,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;YACpC,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;YAC7B,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EACpC,CAAC;YACF,OAAO,IAAI,CAAC;QACb,CAAC;QACD,OAAO;YACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;SAC7B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,eAAe;IAC9B,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC9B,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,kBAAkB,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAwB;IACjD,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,6BAA6B,CAAC;AAEpF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,YAAoB;IAC5D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,kBAAkB,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;KACrD,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAI7B,CAAC;IACF,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAE9E,MAAM,KAAK,GAAsB;QAChC,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,UAAU,EAAE,SAAS;KACrB,CAAC;IACF,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IACnC,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACb,CAAC;IAED,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACd,4FAA4F,CAC5F,CAAC;QACH,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAChE,OAAO,SAAS,CAAC,YAAY,CAAC;IAC/B,CAAC;IAED,OAAO,KAAK,CAAC,YAAY,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { loginFinish, loginInteractive, loginStart } from './auth.js';
3
+ import { clearCredentials, clearLoginState, readCredentials } from './credentials.js';
4
+ const command = process.argv[2];
5
+ switch (command) {
6
+ case 'login': {
7
+ const sub = process.argv[3];
8
+ if (sub === 'start') {
9
+ loginStart();
10
+ }
11
+ else if (sub === 'finish') {
12
+ const code = process.argv[4];
13
+ if (!code) {
14
+ process.stderr.write('Usage: workjournal login finish <CODE>\n');
15
+ process.exit(1);
16
+ }
17
+ await loginFinish(code);
18
+ }
19
+ else if (sub === undefined) {
20
+ await loginInteractive();
21
+ }
22
+ else {
23
+ process.stderr.write(`Unknown login subcommand: ${sub}\n`);
24
+ process.stderr.write('Usage: workjournal login [start | finish <CODE>]\n');
25
+ process.exit(1);
26
+ }
27
+ break;
28
+ }
29
+ case 'logout':
30
+ clearCredentials();
31
+ clearLoginState();
32
+ process.stdout.write('Logged out. Credentials removed.\n');
33
+ break;
34
+ case 'whoami': {
35
+ const creds = readCredentials();
36
+ if (!creds) {
37
+ process.stderr.write('Not logged in. Run `workjournal login` to authenticate.\n');
38
+ process.exit(1);
39
+ }
40
+ process.stdout.write('Authenticated. Credentials stored at ~/.workjournal/credentials.json\n');
41
+ break;
42
+ }
43
+ case 'status': {
44
+ const creds = readCredentials();
45
+ if (!creds) {
46
+ process.stdout.write('Status: not authenticated\n');
47
+ process.stdout.write('Run `workjournal login` to authenticate.\n');
48
+ }
49
+ else {
50
+ const expiresAt = new Date(creds.expires_at);
51
+ const isExpired = expiresAt < new Date();
52
+ process.stdout.write('Status: authenticated\n');
53
+ process.stdout.write(`Token expires: ${expiresAt.toISOString()}${isExpired ? ' (expired, will auto-refresh)' : ''}\n`);
54
+ }
55
+ break;
56
+ }
57
+ default:
58
+ process.stdout.write(`workjournal — CLI for Workjournal (https://workjournal.pro)
59
+
60
+ Usage:
61
+ workjournal login Interactive login (terminal only)
62
+ workjournal login start Print authorize URL and store PKCE state
63
+ workjournal login finish <CODE> Exchange the pasted code for credentials
64
+ workjournal logout Remove stored credentials
65
+ workjournal whoami Show current auth status
66
+ workjournal status Show detailed auth status\n`);
67
+ if (command && command !== 'help' && command !== '--help') {
68
+ process.stderr.write(`Unknown command: ${command}\n`);
69
+ process.exit(1);
70
+ }
71
+ }
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@workjournal/cli",
3
+ "version": "0.1.0",
4
+ "description": "Command-line tool for authenticating with Workjournal — the journaling service for AI agents and developers.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "workjournal": "./dist/index.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/workjournal-pro/workjournal.git",
26
+ "directory": "packages/cli"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/workjournal-pro/workjournal/issues"
30
+ },
31
+ "homepage": "https://workjournal.pro",
32
+ "keywords": [
33
+ "workjournal",
34
+ "cli",
35
+ "oauth",
36
+ "authentication",
37
+ "ai-agents",
38
+ "journaling"
39
+ ],
40
+ "dependencies": {},
41
+ "devDependencies": {
42
+ "@types/node": "22.15.29",
43
+ "typescript": "5.8.3",
44
+ "vitest": "4.1.2"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc --project tsconfig.json",
48
+ "test": "vitest run"
49
+ }
50
+ }