@workjournal/cli 0.3.4 → 0.5.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 +56 -19
- package/dist/api-client.d.ts +23 -10
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +7 -13
- package/dist/api-client.js.map +1 -1
- package/dist/commands/entries.d.ts +6 -6
- package/dist/commands/entries.d.ts.map +1 -1
- package/dist/commands/entries.js +12 -12
- package/dist/commands/entries.js.map +1 -1
- package/dist/commands/export.d.ts +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +2 -2
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/invites.d.ts +3 -3
- package/dist/commands/invites.d.ts.map +1 -1
- package/dist/commands/invites.js +6 -8
- package/dist/commands/invites.js.map +1 -1
- package/dist/commands/journals.d.ts +7 -5
- package/dist/commands/journals.d.ts.map +1 -1
- package/dist/commands/journals.js +48 -20
- package/dist/commands/journals.js.map +1 -1
- package/dist/commands/shares.d.ts +2 -2
- package/dist/commands/shares.d.ts.map +1 -1
- package/dist/commands/shares.js +5 -5
- package/dist/commands/shares.js.map +1 -1
- package/dist/commands/workspaces.d.ts +4 -0
- package/dist/commands/workspaces.d.ts.map +1 -0
- package/dist/commands/workspaces.js +50 -0
- package/dist/commands/workspaces.js.map +1 -0
- package/dist/config.d.ts +10 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -1
- package/dist/config.js.map +1 -1
- package/dist/index.js +368 -163
- package/dist/index.js.map +1 -1
- package/dist/project-config.d.ts +34 -4
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +39 -8
- package/dist/project-config.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { API_PATHS, apiGet, requireAuth } from './api-client.js';
|
|
2
3
|
import { loginFinish, loginInteractive, loginStart } from './auth.js';
|
|
3
4
|
import { createEntry, deleteEntry, getEntry, lastEntries, listEntries, searchEntries, } from './commands/entries.js';
|
|
4
5
|
import { exportJournal } from './commands/export.js';
|
|
5
6
|
import { deleteInvite, listInvites, newInvite } from './commands/invites.js';
|
|
6
|
-
import { createJournal, deleteJournal, getJournal, listJournals, selectJournal, } from './commands/journals.js';
|
|
7
|
+
import { createJournal, deleteJournal, getJournal, listJournals, renameJournal, selectJournal, setJournalSlug, } from './commands/journals.js';
|
|
7
8
|
import { deleteShare, listShares } from './commands/shares.js';
|
|
9
|
+
import { getWorkspace, listWorkspaces, selectWorkspace } from './commands/workspaces.js';
|
|
8
10
|
import { readConfig } from './config.js';
|
|
9
11
|
import { CONFIG_DIR, clearCredentials, clearLoginState, readCredentials } from './credentials.js';
|
|
10
|
-
import { findProjectConfig, getProjectConfigPath } from './project-config.js';
|
|
12
|
+
import { findProjectConfig, getProjectConfigPath, rewriteProjectConfigAt, } from './project-config.js';
|
|
11
13
|
// ── Arg parsing ─────────────────────────────────────────────────────────────
|
|
12
14
|
const rawArgs = process.argv.slice(2);
|
|
13
15
|
const jsonOutput = rawArgs.includes('--json');
|
|
@@ -31,17 +33,64 @@ function parseFlags(argv) {
|
|
|
31
33
|
}
|
|
32
34
|
return { flags, positional };
|
|
33
35
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Migrate a legacy `{ journal_id }` project config by walking workspaces and
|
|
38
|
+
* journals to find the entry whose `id` matches. Rewrites the file in place
|
|
39
|
+
* on success. Returns `null` if migration can't run (offline, deleted journal,
|
|
40
|
+
* not authenticated).
|
|
41
|
+
*/
|
|
42
|
+
async function migrateLegacyProjectConfig(journalId, configPath) {
|
|
43
|
+
const token = await requireAuth();
|
|
44
|
+
let workspaces;
|
|
45
|
+
try {
|
|
46
|
+
const result = await apiGet(API_PATHS.workspaces, token);
|
|
47
|
+
workspaces = result.data;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
for (const ws of workspaces) {
|
|
53
|
+
try {
|
|
54
|
+
const journals = await apiGet(API_PATHS.journals(ws.slug), token);
|
|
55
|
+
const match = journals.data.find((j) => j.id === journalId);
|
|
56
|
+
if (match) {
|
|
57
|
+
rewriteProjectConfigAt(configPath, ws.slug, match.slug);
|
|
58
|
+
process.stderr.write(`Migrated .workjournal: journal_id ${journalId} → ${ws.slug}/${match.slug}\n`);
|
|
59
|
+
return { workspaceSlug: ws.slug, journalSlug: match.slug };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Skip this workspace and try the next one.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
async function resolveActiveSelection() {
|
|
69
|
+
const stored = findProjectConfig();
|
|
70
|
+
if (stored) {
|
|
71
|
+
if (stored.kind === 'v2')
|
|
72
|
+
return stored.config;
|
|
73
|
+
// Legacy v1 — try to migrate by resolving the UUID via the API.
|
|
74
|
+
process.stderr.write(`Found legacy .workjournal at ${stored.path} (journal_id: ${stored.config.journal_id}).\n` +
|
|
75
|
+
'Attempting one-shot migration to slug-based selection…\n');
|
|
76
|
+
const migrated = await migrateLegacyProjectConfig(stored.config.journal_id, stored.path);
|
|
77
|
+
if (migrated)
|
|
78
|
+
return migrated;
|
|
79
|
+
process.stderr.write('\nCould not migrate the legacy selection. The journal may have been deleted, or you are offline.\n' +
|
|
80
|
+
'Re-select with:\n' +
|
|
81
|
+
' workjournal workspaces list\n' +
|
|
82
|
+
' workjournal journals list <workspaceSlug>\n' +
|
|
83
|
+
' workjournal journals select <workspaceSlug> <journalSlug>\n');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
39
86
|
const config = readConfig();
|
|
40
|
-
if (config.
|
|
41
|
-
return config.
|
|
87
|
+
if (config.workspaceSlug && config.journalSlug) {
|
|
88
|
+
return { workspaceSlug: config.workspaceSlug, journalSlug: config.journalSlug };
|
|
89
|
+
}
|
|
42
90
|
process.stderr.write(`No journal selected. To select one:
|
|
43
|
-
workjournal
|
|
44
|
-
workjournal journals
|
|
91
|
+
workjournal workspaces list
|
|
92
|
+
workjournal journals list <workspaceSlug>
|
|
93
|
+
workjournal journals select <workspaceSlug> <journalSlug>
|
|
45
94
|
`);
|
|
46
95
|
process.exit(1);
|
|
47
96
|
}
|
|
@@ -105,207 +154,330 @@ async function routeAuth(argv) {
|
|
|
105
154
|
process.exit(1);
|
|
106
155
|
}
|
|
107
156
|
}
|
|
108
|
-
async function
|
|
157
|
+
async function routeWorkspaces(argv) {
|
|
109
158
|
const sub = argv[0];
|
|
110
159
|
switch (sub) {
|
|
111
160
|
case undefined:
|
|
112
161
|
case 'list':
|
|
113
|
-
await
|
|
162
|
+
await listWorkspaces(jsonOutput);
|
|
114
163
|
break;
|
|
115
|
-
case '
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!summary || !body) {
|
|
120
|
-
process.stderr.write('Usage: workjournal journals <id> entries write -s <summary> -b <body>\n');
|
|
164
|
+
case 'get': {
|
|
165
|
+
const slug = argv[1];
|
|
166
|
+
if (!slug) {
|
|
167
|
+
process.stderr.write('Usage: workjournal workspaces get <workspaceSlug>\n');
|
|
121
168
|
process.exit(1);
|
|
122
169
|
}
|
|
123
|
-
await
|
|
170
|
+
await getWorkspace(slug, jsonOutput);
|
|
124
171
|
break;
|
|
125
172
|
}
|
|
126
|
-
case '
|
|
127
|
-
const
|
|
128
|
-
if (
|
|
129
|
-
process.stderr.write('Usage: workjournal
|
|
173
|
+
case 'select': {
|
|
174
|
+
const slug = argv[1];
|
|
175
|
+
if (!slug) {
|
|
176
|
+
process.stderr.write('Usage: workjournal workspaces select <workspaceSlug>\n');
|
|
130
177
|
process.exit(1);
|
|
131
178
|
}
|
|
132
|
-
await
|
|
179
|
+
await selectWorkspace(slug);
|
|
133
180
|
break;
|
|
134
181
|
}
|
|
182
|
+
default:
|
|
183
|
+
process.stderr.write(`Unknown workspaces subcommand: ${sub}\n` +
|
|
184
|
+
'Usage: workjournal workspaces <list|get|select>\n');
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function routeJournals(argv) {
|
|
189
|
+
const sub = argv[0];
|
|
190
|
+
if (sub === undefined) {
|
|
191
|
+
// Shortcut: workjournal journals → show selected journal details
|
|
192
|
+
const sel = await resolveActiveSelection();
|
|
193
|
+
await getJournal(sel.workspaceSlug, sel.journalSlug, jsonOutput);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
switch (sub) {
|
|
197
|
+
case 'list': {
|
|
198
|
+
// `list [workspaceSlug]` — falls back to selected workspace if omitted.
|
|
199
|
+
const wsSlug = argv[1] ?? readConfig().workspaceSlug;
|
|
200
|
+
if (!wsSlug) {
|
|
201
|
+
process.stderr.write('Usage: workjournal journals list <workspaceSlug>\n' +
|
|
202
|
+
'(or run `workjournal workspaces select <slug>` first)\n');
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
await listJournals(wsSlug, jsonOutput);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
135
208
|
case 'get': {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
209
|
+
const ws = argv[1];
|
|
210
|
+
const j = argv[2];
|
|
211
|
+
if (!ws || !j) {
|
|
212
|
+
process.stderr.write('Usage: workjournal journals get <workspaceSlug> <journalSlug>\n');
|
|
139
213
|
process.exit(1);
|
|
140
214
|
}
|
|
141
|
-
await
|
|
142
|
-
|
|
215
|
+
await getJournal(ws, j, jsonOutput);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
case 'new':
|
|
219
|
+
case 'create': {
|
|
220
|
+
const { flags, positional } = parseFlags(argv.slice(1));
|
|
221
|
+
const ws = positional[0];
|
|
222
|
+
const name = positional[1];
|
|
223
|
+
if (!ws || !name) {
|
|
224
|
+
process.stderr.write('Usage: workjournal journals new <workspaceSlug> <name> [--slug <slug>]\n');
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
const slug = flags['--slug'];
|
|
228
|
+
await createJournal(ws, name, slug, jsonOutput);
|
|
229
|
+
return;
|
|
143
230
|
}
|
|
144
231
|
case 'delete': {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
232
|
+
const ws = argv[1];
|
|
233
|
+
const j = argv[2];
|
|
234
|
+
if (!ws || !j) {
|
|
235
|
+
process.stderr.write('Usage: workjournal journals delete <workspaceSlug> <journalSlug>\n');
|
|
148
236
|
process.exit(1);
|
|
149
237
|
}
|
|
150
|
-
await
|
|
151
|
-
|
|
238
|
+
await deleteJournal(ws, j, jsonOutput);
|
|
239
|
+
return;
|
|
152
240
|
}
|
|
153
|
-
case '
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
241
|
+
case 'rename': {
|
|
242
|
+
const ws = argv[1];
|
|
243
|
+
const j = argv[2];
|
|
244
|
+
const newName = argv[3];
|
|
245
|
+
if (!ws || !j || !newName) {
|
|
246
|
+
process.stderr.write('Usage: workjournal journals rename <workspaceSlug> <journalSlug> <newName>\n');
|
|
157
247
|
process.exit(1);
|
|
158
248
|
}
|
|
159
|
-
await
|
|
160
|
-
|
|
249
|
+
await renameJournal(ws, j, newName, jsonOutput);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
case 'set-slug': {
|
|
253
|
+
const ws = argv[1];
|
|
254
|
+
const j = argv[2];
|
|
255
|
+
const newSlug = argv[3];
|
|
256
|
+
if (!ws || !j || !newSlug) {
|
|
257
|
+
process.stderr.write('Usage: workjournal journals set-slug <workspaceSlug> <journalSlug> <newSlug>\n');
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
await setJournalSlug(ws, j, newSlug, jsonOutput);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
case 'select': {
|
|
264
|
+
const ws = argv[1];
|
|
265
|
+
const j = argv[2];
|
|
266
|
+
if (!ws || !j) {
|
|
267
|
+
process.stderr.write('Usage: workjournal journals select <workspaceSlug> <journalSlug>\n');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
await selectJournal(ws, j);
|
|
271
|
+
return;
|
|
161
272
|
}
|
|
162
273
|
default:
|
|
163
|
-
process.stderr.write(`Unknown
|
|
274
|
+
process.stderr.write(`Unknown journals subcommand: ${sub}\n` +
|
|
275
|
+
'Usage: workjournal journals <list|get|new|delete|rename|set-slug|select>\n');
|
|
164
276
|
process.exit(1);
|
|
165
277
|
}
|
|
166
278
|
}
|
|
167
|
-
async function
|
|
279
|
+
async function routeEntries(argv) {
|
|
168
280
|
const sub = argv[0];
|
|
169
281
|
switch (sub) {
|
|
170
282
|
case undefined:
|
|
171
|
-
case 'list':
|
|
172
|
-
|
|
173
|
-
|
|
283
|
+
case 'list': {
|
|
284
|
+
const ws = argv[1];
|
|
285
|
+
const j = argv[2];
|
|
286
|
+
if (!ws || !j) {
|
|
287
|
+
process.stderr.write('Usage: workjournal entries list <workspaceSlug> <journalSlug>\n');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
await listEntries(ws, j, jsonOutput);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
case 'write': {
|
|
294
|
+
const ws = argv[1];
|
|
295
|
+
const j = argv[2];
|
|
296
|
+
if (!ws || !j) {
|
|
297
|
+
process.stderr.write('Usage: workjournal entries write <workspaceSlug> <journalSlug> -s <summary> -b <body>\n');
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
const { flags } = parseFlags(argv.slice(3));
|
|
301
|
+
const summary = flags['-s'] ?? flags['--summary'];
|
|
302
|
+
const body = flags['-b'] ?? flags['--body'];
|
|
303
|
+
if (!summary || !body) {
|
|
304
|
+
process.stderr.write('Usage: workjournal entries write <workspaceSlug> <journalSlug> -s <summary> -b <body>\n');
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
await createEntry(ws, j, summary, body, jsonOutput);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
case 'last': {
|
|
311
|
+
const ws = argv[1];
|
|
312
|
+
const j = argv[2];
|
|
313
|
+
if (!ws || !j) {
|
|
314
|
+
process.stderr.write('Usage: workjournal entries last <workspaceSlug> <journalSlug> [count]\n');
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
const count = Number.parseInt(argv[3] ?? '1', 10);
|
|
318
|
+
if (Number.isNaN(count) || count < 1) {
|
|
319
|
+
process.stderr.write('Usage: workjournal entries last <workspaceSlug> <journalSlug> [count]\n');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
await lastEntries(ws, j, count, jsonOutput);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
case 'get': {
|
|
326
|
+
const ws = argv[1];
|
|
327
|
+
const j = argv[2];
|
|
328
|
+
const index = argv[3];
|
|
329
|
+
if (!ws || !j || !index) {
|
|
330
|
+
process.stderr.write('Usage: workjournal entries get <workspaceSlug> <journalSlug> <index>\n');
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
await getEntry(ws, j, index, jsonOutput);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
174
336
|
case 'delete': {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
337
|
+
const ws = argv[1];
|
|
338
|
+
const j = argv[2];
|
|
339
|
+
const index = argv[3];
|
|
340
|
+
if (!ws || !j || !index) {
|
|
341
|
+
process.stderr.write('Usage: workjournal entries delete <workspaceSlug> <journalSlug> <index>\n');
|
|
178
342
|
process.exit(1);
|
|
179
343
|
}
|
|
180
|
-
await
|
|
181
|
-
|
|
344
|
+
await deleteEntry(ws, j, index, jsonOutput);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
case 'search': {
|
|
348
|
+
const ws = argv[1];
|
|
349
|
+
const j = argv[2];
|
|
350
|
+
const query = argv[3];
|
|
351
|
+
if (!ws || !j || !query) {
|
|
352
|
+
process.stderr.write('Usage: workjournal entries search <workspaceSlug> <journalSlug> <query>\n');
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
await searchEntries(ws, j, query, jsonOutput);
|
|
356
|
+
return;
|
|
182
357
|
}
|
|
183
358
|
default:
|
|
184
|
-
process.stderr.write(`Unknown
|
|
359
|
+
process.stderr.write(`Unknown entries subcommand: ${sub}\n` +
|
|
360
|
+
'Usage: workjournal entries <list|write|last|get|delete|search>\n');
|
|
185
361
|
process.exit(1);
|
|
186
362
|
}
|
|
187
363
|
}
|
|
188
|
-
async function
|
|
364
|
+
async function routeShares(argv) {
|
|
189
365
|
const sub = argv[0];
|
|
190
366
|
switch (sub) {
|
|
191
367
|
case undefined:
|
|
192
|
-
case 'list':
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (!email) {
|
|
198
|
-
process.stderr.write('Usage: workjournal journals <id> invites new <email>\n');
|
|
368
|
+
case 'list': {
|
|
369
|
+
const ws = argv[1];
|
|
370
|
+
const j = argv[2];
|
|
371
|
+
if (!ws || !j) {
|
|
372
|
+
process.stderr.write('Usage: workjournal shares list <workspaceSlug> <journalSlug>\n');
|
|
199
373
|
process.exit(1);
|
|
200
374
|
}
|
|
201
|
-
await
|
|
202
|
-
|
|
375
|
+
await listShares(ws, j, jsonOutput);
|
|
376
|
+
return;
|
|
203
377
|
}
|
|
204
378
|
case 'delete': {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
379
|
+
const ws = argv[1];
|
|
380
|
+
const j = argv[2];
|
|
381
|
+
const email = argv[3];
|
|
382
|
+
if (!ws || !j || !email) {
|
|
383
|
+
process.stderr.write('Usage: workjournal shares delete <workspaceSlug> <journalSlug> <email>\n');
|
|
208
384
|
process.exit(1);
|
|
209
385
|
}
|
|
210
|
-
await
|
|
211
|
-
|
|
386
|
+
await deleteShare(ws, j, email, jsonOutput);
|
|
387
|
+
return;
|
|
212
388
|
}
|
|
213
389
|
default:
|
|
214
|
-
process.stderr.write(`Unknown
|
|
390
|
+
process.stderr.write(`Unknown shares subcommand: ${sub}\nUsage: workjournal shares <list|delete>\n`);
|
|
215
391
|
process.exit(1);
|
|
216
392
|
}
|
|
217
393
|
}
|
|
218
|
-
async function
|
|
394
|
+
async function routeInvites(argv) {
|
|
219
395
|
const sub = argv[0];
|
|
220
396
|
switch (sub) {
|
|
221
397
|
case undefined:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
case 'shares':
|
|
228
|
-
await routeShares(journalId, argv.slice(1));
|
|
229
|
-
break;
|
|
230
|
-
case 'invites':
|
|
231
|
-
await routeInvites(journalId, argv.slice(1));
|
|
232
|
-
break;
|
|
233
|
-
case 'export': {
|
|
234
|
-
const { flags } = parseFlags(argv.slice(1));
|
|
235
|
-
const format = flags['-f'] ?? flags['--format'] ?? 'json';
|
|
236
|
-
if (!['json', 'md', 'csv'].includes(format)) {
|
|
237
|
-
process.stderr.write('Invalid format. Use: -f json|md|csv\n');
|
|
398
|
+
case 'list': {
|
|
399
|
+
const ws = argv[1];
|
|
400
|
+
const j = argv[2];
|
|
401
|
+
if (!ws || !j) {
|
|
402
|
+
process.stderr.write('Usage: workjournal invites list <workspaceSlug> <journalSlug>\n');
|
|
238
403
|
process.exit(1);
|
|
239
404
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
405
|
+
await listInvites(ws, j, jsonOutput);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
case 'new':
|
|
409
|
+
case 'create': {
|
|
410
|
+
const ws = argv[1];
|
|
411
|
+
const j = argv[2];
|
|
412
|
+
const email = argv[3];
|
|
413
|
+
if (!ws || !j || !email) {
|
|
414
|
+
process.stderr.write('Usage: workjournal invites new <workspaceSlug> <journalSlug> <email>\n');
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
await newInvite(ws, j, email, jsonOutput);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
case 'delete': {
|
|
421
|
+
const ws = argv[1];
|
|
422
|
+
const j = argv[2];
|
|
423
|
+
const invitationId = argv[3];
|
|
424
|
+
if (!ws || !j || !invitationId) {
|
|
425
|
+
process.stderr.write('Usage: workjournal invites delete <workspaceSlug> <journalSlug> <invitationId>\n');
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
await deleteInvite(ws, j, invitationId, jsonOutput);
|
|
429
|
+
return;
|
|
243
430
|
}
|
|
244
431
|
default:
|
|
245
|
-
process.stderr.write(`Unknown subcommand: ${sub}\n`);
|
|
432
|
+
process.stderr.write(`Unknown invites subcommand: ${sub}\nUsage: workjournal invites <list|new|delete>\n`);
|
|
246
433
|
process.exit(1);
|
|
247
434
|
}
|
|
248
435
|
}
|
|
249
|
-
async function
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
if (sub === 'list') {
|
|
258
|
-
await listJournals(jsonOutput);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (sub === 'new') {
|
|
262
|
-
const name = argv[1];
|
|
263
|
-
if (!name) {
|
|
264
|
-
process.stderr.write('Usage: workjournal journals new <name>\n');
|
|
265
|
-
process.exit(1);
|
|
266
|
-
}
|
|
267
|
-
await createJournal(name, jsonOutput);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (sub === 'delete') {
|
|
271
|
-
const id = argv[1];
|
|
272
|
-
if (!id) {
|
|
273
|
-
process.stderr.write('Usage: workjournal journals delete <journal_id>\n');
|
|
274
|
-
process.exit(1);
|
|
275
|
-
}
|
|
276
|
-
await deleteJournal(id, jsonOutput);
|
|
277
|
-
return;
|
|
436
|
+
async function routeExport(argv) {
|
|
437
|
+
const ws = argv[0];
|
|
438
|
+
const j = argv[1];
|
|
439
|
+
if (!ws || !j) {
|
|
440
|
+
process.stderr.write('Usage: workjournal export <workspaceSlug> <journalSlug> [-f json|md|csv] [-p <path>]\n');
|
|
441
|
+
process.exit(1);
|
|
278
442
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
await selectJournal(id);
|
|
286
|
-
return;
|
|
443
|
+
const { flags } = parseFlags(argv.slice(2));
|
|
444
|
+
const format = flags['-f'] ?? flags['--format'] ?? 'json';
|
|
445
|
+
if (!['json', 'md', 'csv'].includes(format)) {
|
|
446
|
+
process.stderr.write('Invalid format. Use: -f json|md|csv\n');
|
|
447
|
+
process.exit(1);
|
|
287
448
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
await routeJournalResource(journalId, argv.slice(1));
|
|
449
|
+
const outputPath = flags['-p'] ?? flags['--path'];
|
|
450
|
+
await exportJournal(ws, j, format, outputPath);
|
|
291
451
|
}
|
|
292
452
|
async function routeConfig(argv) {
|
|
293
453
|
const sub = argv[0];
|
|
294
454
|
if (sub === 'show') {
|
|
295
455
|
const projectPath = getProjectConfigPath();
|
|
296
|
-
const
|
|
456
|
+
const stored = findProjectConfig();
|
|
297
457
|
const globalCfg = readConfig();
|
|
298
458
|
process.stdout.write('Project config:\n');
|
|
299
|
-
if (projectPath &&
|
|
300
|
-
process.stdout.write(` Path:
|
|
301
|
-
|
|
459
|
+
if (projectPath && stored) {
|
|
460
|
+
process.stdout.write(` Path: ${projectPath}\n`);
|
|
461
|
+
if (stored.kind === 'v2') {
|
|
462
|
+
process.stdout.write(` Workspace: ${stored.config.workspaceSlug}\n`);
|
|
463
|
+
process.stdout.write(` Journal: ${stored.config.journalSlug}\n`);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
process.stdout.write(` Legacy journal_id: ${stored.config.journal_id}\n`);
|
|
467
|
+
process.stdout.write(' (will migrate on next command that needs the selection)\n');
|
|
468
|
+
}
|
|
302
469
|
}
|
|
303
470
|
else {
|
|
304
471
|
process.stdout.write(' (none)\n');
|
|
305
472
|
}
|
|
306
473
|
process.stdout.write('Global config:\n');
|
|
307
|
-
if (globalCfg.
|
|
308
|
-
|
|
474
|
+
if (globalCfg.workspaceSlug || globalCfg.journalSlug) {
|
|
475
|
+
if (globalCfg.workspaceSlug) {
|
|
476
|
+
process.stdout.write(` Workspace: ${globalCfg.workspaceSlug}\n`);
|
|
477
|
+
}
|
|
478
|
+
if (globalCfg.journalSlug) {
|
|
479
|
+
process.stdout.write(` Journal: ${globalCfg.journalSlug}\n`);
|
|
480
|
+
}
|
|
309
481
|
}
|
|
310
482
|
else {
|
|
311
483
|
process.stdout.write(' (none)\n');
|
|
@@ -323,31 +495,42 @@ function printHelp() {
|
|
|
323
495
|
Usage:
|
|
324
496
|
workjournal List entries in selected journal
|
|
325
497
|
|
|
326
|
-
|
|
327
|
-
workjournal
|
|
328
|
-
workjournal
|
|
329
|
-
workjournal
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
workjournal
|
|
333
|
-
workjournal
|
|
334
|
-
workjournal
|
|
335
|
-
workjournal
|
|
336
|
-
workjournal
|
|
337
|
-
workjournal journal
|
|
338
|
-
workjournal
|
|
339
|
-
workjournal
|
|
340
|
-
|
|
341
|
-
|
|
498
|
+
Workspaces:
|
|
499
|
+
workjournal workspaces list List your workspaces
|
|
500
|
+
workjournal workspaces get <ws> Show workspace details
|
|
501
|
+
workjournal workspaces select <ws> Set active workspace
|
|
502
|
+
|
|
503
|
+
Journals:
|
|
504
|
+
workjournal journals list [<ws>] List journals in workspace
|
|
505
|
+
workjournal journals get <ws> <j> Show journal details
|
|
506
|
+
workjournal journals new <ws> <name> [--slug <slug>] Create a new journal
|
|
507
|
+
workjournal journals delete <ws> <j> Delete a journal
|
|
508
|
+
workjournal journals rename <ws> <j> <newName> Rename a journal
|
|
509
|
+
workjournal journals set-slug <ws> <j> <newSlug> Change a journal's slug
|
|
510
|
+
workjournal journals select <ws> <j> Set active journal
|
|
511
|
+
workjournal journals Show selected journal details
|
|
512
|
+
|
|
513
|
+
Entries:
|
|
514
|
+
workjournal entries list <ws> <j> List entries
|
|
515
|
+
workjournal entries write <ws> <j> -s <summary> -b <body>
|
|
516
|
+
Create a new entry
|
|
517
|
+
workjournal entries last <ws> <j> [count] Show most recent entries (full body)
|
|
518
|
+
workjournal entries get <ws> <j> <index> Show a single entry by index
|
|
519
|
+
workjournal entries delete <ws> <j> <index> Delete an entry by index
|
|
520
|
+
workjournal entries search <ws> <j> <query> Search entries
|
|
342
521
|
|
|
343
|
-
|
|
344
|
-
workjournal
|
|
345
|
-
workjournal
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
workjournal
|
|
349
|
-
workjournal
|
|
350
|
-
|
|
522
|
+
Shares (members):
|
|
523
|
+
workjournal shares list <ws> <j> List members
|
|
524
|
+
workjournal shares delete <ws> <j> <email> Remove a member
|
|
525
|
+
|
|
526
|
+
Invites:
|
|
527
|
+
workjournal invites list <ws> <j> List invitations
|
|
528
|
+
workjournal invites new <ws> <j> <email> Invite a collaborator
|
|
529
|
+
workjournal invites delete <ws> <j> <invitationId> Revoke an invitation
|
|
530
|
+
|
|
531
|
+
Export:
|
|
532
|
+
workjournal export <ws> <j> [-f json|md|csv] [-p <path>]
|
|
533
|
+
Export journal data
|
|
351
534
|
|
|
352
535
|
Auth:
|
|
353
536
|
workjournal auth login Interactive login
|
|
@@ -374,22 +557,44 @@ async function run() {
|
|
|
374
557
|
}
|
|
375
558
|
if (command === undefined) {
|
|
376
559
|
// Shortcut: workjournal → list entries in selected journal
|
|
377
|
-
const
|
|
378
|
-
await listEntries(
|
|
560
|
+
const sel = await resolveActiveSelection();
|
|
561
|
+
await listEntries(sel.workspaceSlug, sel.journalSlug, jsonOutput);
|
|
379
562
|
return;
|
|
380
563
|
}
|
|
381
564
|
switch (command) {
|
|
382
565
|
case 'auth':
|
|
383
566
|
await routeAuth(args.slice(1));
|
|
384
567
|
break;
|
|
568
|
+
case 'workspaces':
|
|
569
|
+
await routeWorkspaces(args.slice(1));
|
|
570
|
+
break;
|
|
385
571
|
case 'journal': {
|
|
386
|
-
|
|
387
|
-
await
|
|
572
|
+
// Shortcut: show selected journal details (matches docs/cli-commands.md)
|
|
573
|
+
const sel = await resolveActiveSelection();
|
|
574
|
+
if (args.length > 1) {
|
|
575
|
+
process.stderr.write('`workjournal journal` shows the selected journal. Use `workjournal entries`, ' +
|
|
576
|
+
'`workjournal shares`, etc. with explicit <workspaceSlug> <journalSlug> for ' +
|
|
577
|
+
'resource verbs.\n');
|
|
578
|
+
process.exit(1);
|
|
579
|
+
}
|
|
580
|
+
await getJournal(sel.workspaceSlug, sel.journalSlug, jsonOutput);
|
|
388
581
|
break;
|
|
389
582
|
}
|
|
390
583
|
case 'journals':
|
|
391
584
|
await routeJournals(args.slice(1));
|
|
392
585
|
break;
|
|
586
|
+
case 'entries':
|
|
587
|
+
await routeEntries(args.slice(1));
|
|
588
|
+
break;
|
|
589
|
+
case 'shares':
|
|
590
|
+
await routeShares(args.slice(1));
|
|
591
|
+
break;
|
|
592
|
+
case 'invites':
|
|
593
|
+
await routeInvites(args.slice(1));
|
|
594
|
+
break;
|
|
595
|
+
case 'export':
|
|
596
|
+
await routeExport(args.slice(1));
|
|
597
|
+
break;
|
|
393
598
|
case 'config':
|
|
394
599
|
await routeConfig(args.slice(1));
|
|
395
600
|
break;
|