@wafflebase/cli 0.4.9 → 0.5.1
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/dist/bin.js +99988 -11
- package/package.json +7 -6
- package/dist/bin.d.ts +0 -2
- package/dist/client/dry-run.d.ts +0 -5
- package/dist/client/dry-run.js +0 -16
- package/dist/client/http-client.d.ts +0 -59
- package/dist/client/http-client.js +0 -178
- package/dist/commands/api-keys.d.ts +0 -2
- package/dist/commands/api-keys.js +0 -54
- package/dist/commands/cells.d.ts +0 -2
- package/dist/commands/cells.js +0 -108
- package/dist/commands/ctx.d.ts +0 -5
- package/dist/commands/ctx.js +0 -56
- package/dist/commands/docs.d.ts +0 -3
- package/dist/commands/docs.js +0 -320
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -220
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.js +0 -10
- package/dist/commands/root.d.ts +0 -18
- package/dist/commands/root.js +0 -37
- package/dist/commands/schema.d.ts +0 -2
- package/dist/commands/schema.js +0 -28
- package/dist/commands/sheets-export.d.ts +0 -2
- package/dist/commands/sheets-export.js +0 -52
- package/dist/commands/sheets-import.d.ts +0 -2
- package/dist/commands/sheets-import.js +0 -88
- package/dist/commands/sheets.d.ts +0 -2
- package/dist/commands/sheets.js +0 -17
- package/dist/commands/slides.d.ts +0 -3
- package/dist/commands/slides.js +0 -227
- package/dist/commands/status.d.ts +0 -2
- package/dist/commands/status.js +0 -22
- package/dist/commands/tabs.d.ts +0 -2
- package/dist/commands/tabs.js +0 -20
- package/dist/config/config.d.ts +0 -38
- package/dist/config/config.js +0 -135
- package/dist/config/session.d.ts +0 -49
- package/dist/config/session.js +0 -74
- package/dist/docs/content.d.ts +0 -37
- package/dist/docs/content.js +0 -90
- package/dist/docs/docx-export.d.ts +0 -23
- package/dist/docs/docx-export.js +0 -14
- package/dist/docs/docx-import.d.ts +0 -36
- package/dist/docs/docx-import.js +0 -70
- package/dist/docs/dom-polyfill.d.ts +0 -1
- package/dist/docs/dom-polyfill.js +0 -29
- package/dist/docs/fontkit-measurer.d.ts +0 -41
- package/dist/docs/fontkit-measurer.js +0 -64
- package/dist/docs/image-fetcher.d.ts +0 -37
- package/dist/docs/image-fetcher.js +0 -38
- package/dist/docs/import.d.ts +0 -72
- package/dist/docs/import.js +0 -146
- package/dist/docs/page-range.d.ts +0 -29
- package/dist/docs/page-range.js +0 -74
- package/dist/docs/page-slice.d.ts +0 -45
- package/dist/docs/page-slice.js +0 -61
- package/dist/docs/paginate.d.ts +0 -13
- package/dist/docs/paginate.js +0 -19
- package/dist/docs/pdf-export.d.ts +0 -47
- package/dist/docs/pdf-export.js +0 -95
- package/dist/output/binary.d.ts +0 -37
- package/dist/output/binary.js +0 -47
- package/dist/output/csv.d.ts +0 -4
- package/dist/output/csv.js +0 -25
- package/dist/output/formatter.d.ts +0 -4
- package/dist/output/formatter.js +0 -41
- package/dist/output/json.d.ts +0 -1
- package/dist/output/json.js +0 -3
- package/dist/output/table.d.ts +0 -4
- package/dist/output/table.js +0 -14
- package/dist/schema/registry.d.ts +0 -42
- package/dist/schema/registry.js +0 -375
- package/dist/slides/content.d.ts +0 -41
- package/dist/slides/content.js +0 -122
- package/dist/slides/import.d.ts +0 -82
- package/dist/slides/import.js +0 -170
- package/dist/slides/pptx-export.d.ts +0 -6
- package/dist/slides/pptx-export.js +0 -10
- package/dist/slides/pptx-import.d.ts +0 -35
- package/dist/slides/pptx-import.js +0 -49
- package/dist/util/csv-parse.d.ts +0 -21
- package/dist/util/csv-parse.js +0 -99
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { writeFileSync } from 'node:fs';
|
|
2
|
-
import { extname } from 'node:path';
|
|
3
|
-
import { getGlobalOpts, getClient } from './root.js';
|
|
4
|
-
import { outputError } from '../output/formatter.js';
|
|
5
|
-
import { formatCsv } from '../output/csv.js';
|
|
6
|
-
import { formatJson } from '../output/json.js';
|
|
7
|
-
const VALID_FORMATS = ['csv', 'json'];
|
|
8
|
-
function detectFormat(file, formatFlag) {
|
|
9
|
-
if (formatFlag) {
|
|
10
|
-
if (!VALID_FORMATS.includes(formatFlag)) {
|
|
11
|
-
throw new Error(`Unsupported format "${formatFlag}". Use csv or json.`);
|
|
12
|
-
}
|
|
13
|
-
return formatFlag;
|
|
14
|
-
}
|
|
15
|
-
const ext = extname(file).toLowerCase();
|
|
16
|
-
if (ext === '.json')
|
|
17
|
-
return 'json';
|
|
18
|
-
return 'csv';
|
|
19
|
-
}
|
|
20
|
-
export function registerSheetsExportCommand(parent) {
|
|
21
|
-
parent
|
|
22
|
-
.command('export <doc-id> <file>')
|
|
23
|
-
.description('Export tab data to CSV/JSON')
|
|
24
|
-
.option('--tab <tab-id>', 'Source tab', 'tab-1')
|
|
25
|
-
.option('--range <range>', 'Cell range to export (e.g. A1:D100)')
|
|
26
|
-
.option('--file-format <fmt>', 'File format (csv, json)')
|
|
27
|
-
.action(async function (docId, file) {
|
|
28
|
-
const opts = getGlobalOpts(this);
|
|
29
|
-
const localOpts = this.opts();
|
|
30
|
-
try {
|
|
31
|
-
const res = await getClient(opts).getCells(docId, localOpts.tab, localOpts.range);
|
|
32
|
-
if (!res.ok) {
|
|
33
|
-
const msg = res.data?.error?.message;
|
|
34
|
-
throw new Error(msg ?? `HTTP ${res.status}`);
|
|
35
|
-
}
|
|
36
|
-
const fmt = detectFormat(file, localOpts.fileFormat);
|
|
37
|
-
const formatted = fmt === 'csv' ? formatCsv(res.data) : formatJson(res.data);
|
|
38
|
-
if (file === '-') {
|
|
39
|
-
process.stdout.write(formatted + '\n');
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
writeFileSync(file, formatted + '\n', 'utf-8');
|
|
43
|
-
if (!opts.quiet) {
|
|
44
|
-
console.log(`Exported to ${file}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
outputError(e, opts.quiet);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { extname } from 'node:path';
|
|
3
|
-
import { getGlobalOpts, getClient, getConfig } from './root.js';
|
|
4
|
-
import { output, outputError } from '../output/formatter.js';
|
|
5
|
-
import { printDryRun } from '../client/dry-run.js';
|
|
6
|
-
import { parseCsv, parseStartRef, buildCellMap } from '../util/csv-parse.js';
|
|
7
|
-
const VALID_FORMATS = ['csv', 'json'];
|
|
8
|
-
function detectFormat(file, formatFlag) {
|
|
9
|
-
if (formatFlag) {
|
|
10
|
-
if (!VALID_FORMATS.includes(formatFlag)) {
|
|
11
|
-
throw new Error(`Unsupported format "${formatFlag}". Use csv or json.`);
|
|
12
|
-
}
|
|
13
|
-
return formatFlag;
|
|
14
|
-
}
|
|
15
|
-
const ext = extname(file).toLowerCase();
|
|
16
|
-
if (ext === '.json')
|
|
17
|
-
return 'json';
|
|
18
|
-
return 'csv';
|
|
19
|
-
}
|
|
20
|
-
function readInput(file) {
|
|
21
|
-
if (file === '-') {
|
|
22
|
-
return readFileSync(0, 'utf-8');
|
|
23
|
-
}
|
|
24
|
-
return readFileSync(file, 'utf-8');
|
|
25
|
-
}
|
|
26
|
-
export function registerSheetsImportCommand(parent) {
|
|
27
|
-
parent
|
|
28
|
-
.command('import <doc-id> <file>')
|
|
29
|
-
.description('Import CSV/JSON into a tab')
|
|
30
|
-
.option('--tab <tab-id>', 'Target tab', 'tab-1')
|
|
31
|
-
.option('--file-format <fmt>', 'File format (csv, json)')
|
|
32
|
-
.option('--start <ref>', 'Top-left cell to start import', 'A1')
|
|
33
|
-
.action(async function (docId, file) {
|
|
34
|
-
const opts = getGlobalOpts(this);
|
|
35
|
-
const localOpts = this.opts();
|
|
36
|
-
try {
|
|
37
|
-
const fmt = detectFormat(file, localOpts.fileFormat);
|
|
38
|
-
const raw = readInput(file);
|
|
39
|
-
let rows;
|
|
40
|
-
if (fmt === 'json') {
|
|
41
|
-
const parsed = JSON.parse(raw);
|
|
42
|
-
// Expect either array of arrays or array of objects
|
|
43
|
-
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
44
|
-
if (Array.isArray(parsed[0])) {
|
|
45
|
-
rows = parsed;
|
|
46
|
-
}
|
|
47
|
-
else if (typeof parsed[0] === 'object') {
|
|
48
|
-
// Array of objects → header row + value rows
|
|
49
|
-
const objs = parsed;
|
|
50
|
-
const keys = Object.keys(objs[0]);
|
|
51
|
-
rows = [keys, ...objs.map((o) => keys.map((k) => String(o[k] ?? '')))];
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
throw new Error('JSON must be an array of arrays or array of objects');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
throw new Error('JSON must be a non-empty array');
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
rows = parseCsv(raw);
|
|
63
|
-
}
|
|
64
|
-
if (rows.length === 0) {
|
|
65
|
-
throw new Error('No data to import');
|
|
66
|
-
}
|
|
67
|
-
const { row: startRow, col: startCol } = parseStartRef(localOpts.start);
|
|
68
|
-
const cells = buildCellMap(rows, startRow, startCol);
|
|
69
|
-
const cellCount = Object.keys(cells).length;
|
|
70
|
-
if (opts.dryRun) {
|
|
71
|
-
printDryRun(getConfig(opts), 'PATCH', `/documents/${docId}/tabs/${localOpts.tab}/cells`, {
|
|
72
|
-
cells,
|
|
73
|
-
});
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const res = await getClient(opts).batchCells(docId, localOpts.tab, cells);
|
|
77
|
-
if (!res.ok)
|
|
78
|
-
throw new Error(`HTTP ${res.status}`);
|
|
79
|
-
const result = typeof res.data === 'object' && res.data !== null
|
|
80
|
-
? { imported: cellCount, ...res.data }
|
|
81
|
-
: { imported: cellCount };
|
|
82
|
-
output(result, opts.format, opts.quiet);
|
|
83
|
-
}
|
|
84
|
-
catch (e) {
|
|
85
|
-
outputError(e, opts.quiet);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
package/dist/commands/sheets.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { registerCellsCommand } from './cells.js';
|
|
2
|
-
import { registerTabsCommand } from './tabs.js';
|
|
3
|
-
import { registerSheetsImportCommand } from './sheets-import.js';
|
|
4
|
-
import { registerSheetsExportCommand } from './sheets-export.js';
|
|
5
|
-
export function registerSheetsCommand(program) {
|
|
6
|
-
const sheets = program
|
|
7
|
-
.command('sheets')
|
|
8
|
-
.alias('sheet')
|
|
9
|
-
.alias('spreadsheet')
|
|
10
|
-
.alias('spreadsheets')
|
|
11
|
-
.description('Spreadsheet commands');
|
|
12
|
-
registerTabsCommand(sheets);
|
|
13
|
-
registerCellsCommand(sheets);
|
|
14
|
-
registerSheetsImportCommand(sheets);
|
|
15
|
-
registerSheetsExportCommand(sheets);
|
|
16
|
-
return sheets;
|
|
17
|
-
}
|
package/dist/commands/slides.js
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { extname } from 'node:path';
|
|
2
|
-
import { getGlobalOpts, getClient, getConfig } from './root.js';
|
|
3
|
-
import { output, outputError } from '../output/formatter.js';
|
|
4
|
-
import { printDryRun } from '../client/dry-run.js';
|
|
5
|
-
import { runSlidesImport } from '../slides/import.js';
|
|
6
|
-
import { parseSlidesContentFormat, runSlidesContent, } from '../slides/content.js';
|
|
7
|
-
import { writeBinary } from '../output/binary.js';
|
|
8
|
-
import { createImageFetcher } from '../docs/image-fetcher.js';
|
|
9
|
-
import { exportPptxCli } from '../slides/pptx-export.js';
|
|
10
|
-
export function registerSlidesCommand(program) {
|
|
11
|
-
const slides = program
|
|
12
|
-
.command('slides')
|
|
13
|
-
.alias('slide')
|
|
14
|
-
.alias('deck')
|
|
15
|
-
.description('Manage slide decks');
|
|
16
|
-
slides
|
|
17
|
-
.command('list')
|
|
18
|
-
.description('List slide decks in workspace')
|
|
19
|
-
.action(async function () {
|
|
20
|
-
const opts = getGlobalOpts(this);
|
|
21
|
-
try {
|
|
22
|
-
const res = await getClient(opts).listDocuments();
|
|
23
|
-
if (!res.ok)
|
|
24
|
-
throw new Error(`HTTP ${res.status}`);
|
|
25
|
-
let data = res.data;
|
|
26
|
-
if (Array.isArray(data)) {
|
|
27
|
-
data = data.filter((d) => d.type === 'slides');
|
|
28
|
-
}
|
|
29
|
-
output(data, opts.format, opts.quiet);
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
outputError(e, opts.quiet);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
slides
|
|
36
|
-
.command('create <title>')
|
|
37
|
-
.description('Create a new slide deck')
|
|
38
|
-
.action(async function (title) {
|
|
39
|
-
const opts = getGlobalOpts(this);
|
|
40
|
-
try {
|
|
41
|
-
if (opts.dryRun) {
|
|
42
|
-
printDryRun(getConfig(opts), 'POST', '/documents', {
|
|
43
|
-
title,
|
|
44
|
-
type: 'slides',
|
|
45
|
-
});
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const res = await getClient(opts).createDocument(title, 'slides');
|
|
49
|
-
if (!res.ok)
|
|
50
|
-
throw new Error(`HTTP ${res.status}`);
|
|
51
|
-
output(res.data, opts.format, opts.quiet);
|
|
52
|
-
}
|
|
53
|
-
catch (e) {
|
|
54
|
-
outputError(e, opts.quiet);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
slides
|
|
58
|
-
.command('get <doc-id>')
|
|
59
|
-
.description('Show slide deck metadata')
|
|
60
|
-
.action(async function (docId) {
|
|
61
|
-
const opts = getGlobalOpts(this);
|
|
62
|
-
try {
|
|
63
|
-
const res = await getClient(opts).getDocument(docId);
|
|
64
|
-
if (!res.ok)
|
|
65
|
-
throw new Error(`HTTP ${res.status}`);
|
|
66
|
-
output(res.data, opts.format, opts.quiet);
|
|
67
|
-
}
|
|
68
|
-
catch (e) {
|
|
69
|
-
outputError(e, opts.quiet);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
slides
|
|
73
|
-
.command('rename <doc-id> <title>')
|
|
74
|
-
.description('Rename a slide deck')
|
|
75
|
-
.action(async function (docId, title) {
|
|
76
|
-
const opts = getGlobalOpts(this);
|
|
77
|
-
if (opts.dryRun) {
|
|
78
|
-
printDryRun(getConfig(opts), 'PATCH', `/documents/${docId}`, { title });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const res = await getClient(opts).updateDocument(docId, title);
|
|
83
|
-
if (!res.ok)
|
|
84
|
-
throw new Error(`HTTP ${res.status}`);
|
|
85
|
-
output(res.data, opts.format, opts.quiet);
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
outputError(e, opts.quiet);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
slides
|
|
92
|
-
.command('delete <doc-id>')
|
|
93
|
-
.description('Delete a slide deck')
|
|
94
|
-
.action(async function (docId) {
|
|
95
|
-
const opts = getGlobalOpts(this);
|
|
96
|
-
if (opts.dryRun) {
|
|
97
|
-
printDryRun(getConfig(opts), 'DELETE', `/documents/${docId}`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
const res = await getClient(opts).deleteDocument(docId);
|
|
102
|
-
if (!res.ok)
|
|
103
|
-
throw new Error(`HTTP ${res.status}`);
|
|
104
|
-
output(res.data, opts.format, opts.quiet);
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
outputError(e, opts.quiet);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
slides
|
|
111
|
-
.command('content <doc-id>')
|
|
112
|
-
.description('Read deck content as JSON, Markdown, or plain text')
|
|
113
|
-
// NOTE: `--format` is intentionally not redeclared here — the global
|
|
114
|
-
// `--format` option catches the user's value (see the same comment
|
|
115
|
-
// on `docs content`). We read `opts.format` and validate it through
|
|
116
|
-
// `parseSlidesContentFormat`.
|
|
117
|
-
.option('--notes', 'Include speaker notes (md/text)', false)
|
|
118
|
-
.option('--out <file>', 'Output file (- for stdout)')
|
|
119
|
-
.option('--force', 'Overwrite existing output file', false)
|
|
120
|
-
.action(async function (docId) {
|
|
121
|
-
const opts = getGlobalOpts(this);
|
|
122
|
-
const local = this.opts();
|
|
123
|
-
try {
|
|
124
|
-
const format = parseSlidesContentFormat(opts.format);
|
|
125
|
-
if (opts.dryRun) {
|
|
126
|
-
printDryRun(getConfig(opts), 'GET', `/documents/${docId}/content`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const res = await getClient(opts).getSlidesContent(docId);
|
|
130
|
-
if (!res.ok) {
|
|
131
|
-
const body = res.data;
|
|
132
|
-
if (body?.error) {
|
|
133
|
-
// Surface backend-shaped errors (e.g., TYPE_MISMATCH) verbatim
|
|
134
|
-
// so agents reading stderr can act on the `code` field.
|
|
135
|
-
console.error(JSON.stringify(body, null, 2));
|
|
136
|
-
process.exitCode = 1;
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
throw new Error(`HTTP ${res.status}`);
|
|
140
|
-
}
|
|
141
|
-
runSlidesContent({
|
|
142
|
-
deck: res.data,
|
|
143
|
-
format,
|
|
144
|
-
notes: local.notes,
|
|
145
|
-
out: local.out,
|
|
146
|
-
force: local.force,
|
|
147
|
-
quiet: opts.quiet,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
catch (e) {
|
|
151
|
-
outputError(e, opts.quiet);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
slides
|
|
155
|
-
.command('export <doc-id> <file>')
|
|
156
|
-
.description('Export a slide deck to PPTX')
|
|
157
|
-
// NOTE: `--format` is intentionally not redeclared here — the global
|
|
158
|
-
// `--format` option catches the user's value (see the same comment on
|
|
159
|
-
// `docs export`). We read `opts.format` via `getOptionValueSourceWithGlobals`
|
|
160
|
-
// to distinguish an explicit CLI flag from the default, then validate
|
|
161
|
-
// that only "pptx" is accepted.
|
|
162
|
-
.option('--force', 'Overwrite existing output file', false)
|
|
163
|
-
.action(async function (docId, file) {
|
|
164
|
-
const opts = getGlobalOpts(this);
|
|
165
|
-
const local = this.opts();
|
|
166
|
-
try {
|
|
167
|
-
const formatSource = this.getOptionValueSourceWithGlobals('format');
|
|
168
|
-
// Widen to `string` — `opts.format` is the global `OutputFormat`
|
|
169
|
-
// union (json|table|csv|yaml), which has no overlap with the
|
|
170
|
-
// export-only `pptx` value, so a direct comparison is a tsc error.
|
|
171
|
-
const fmt = formatSource === 'cli' ? opts.format : undefined;
|
|
172
|
-
if (fmt && fmt !== 'pptx')
|
|
173
|
-
throw new Error(`Invalid --format "${fmt}". Only "pptx" is supported.`);
|
|
174
|
-
if (!fmt && extname(file).toLowerCase() !== '.pptx') {
|
|
175
|
-
throw new Error(`Cannot infer format from "${file}". Use a .pptx extension or --format pptx.`);
|
|
176
|
-
}
|
|
177
|
-
if (opts.dryRun) {
|
|
178
|
-
printDryRun(getConfig(opts), 'GET', `/documents/${docId}/content`);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const res = await getClient(opts).getSlidesContent(docId);
|
|
182
|
-
if (!res.ok) {
|
|
183
|
-
const body = res.data;
|
|
184
|
-
if (body?.error) {
|
|
185
|
-
console.error(JSON.stringify(body, null, 2));
|
|
186
|
-
process.exitCode = 1;
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
throw new Error(`HTTP ${res.status}`);
|
|
190
|
-
}
|
|
191
|
-
const imageFetcher = createImageFetcher({ serverBase: getConfig(opts).server });
|
|
192
|
-
const bytes = await exportPptxCli(res.data, { imageFetcher });
|
|
193
|
-
writeBinary(bytes, file, { force: local.force, quiet: opts.quiet });
|
|
194
|
-
}
|
|
195
|
-
catch (e) {
|
|
196
|
-
outputError(e, opts.quiet);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
registerSlidesImportCommand(slides);
|
|
200
|
-
}
|
|
201
|
-
export function registerSlidesImportCommand(slides) {
|
|
202
|
-
slides
|
|
203
|
-
.command('import <file>')
|
|
204
|
-
.description('Import a .pptx file as a new (or replacement) slides deck')
|
|
205
|
-
.option('--title <title>', 'Deck title (default: file basename)')
|
|
206
|
-
.option('--replace <doc-id>', 'Replace content of an existing deck')
|
|
207
|
-
.option('--yes', 'Skip --replace confirmation prompt', false)
|
|
208
|
-
.action(async function (file) {
|
|
209
|
-
const opts = getGlobalOpts(this);
|
|
210
|
-
const local = this.opts();
|
|
211
|
-
try {
|
|
212
|
-
const result = await runSlidesImport({
|
|
213
|
-
file,
|
|
214
|
-
title: local.title,
|
|
215
|
-
replace: local.replace,
|
|
216
|
-
yes: local.yes,
|
|
217
|
-
quiet: opts.quiet,
|
|
218
|
-
dryRun: opts.dryRun,
|
|
219
|
-
}, getClient(opts));
|
|
220
|
-
if (result.exitCode !== 0)
|
|
221
|
-
process.exitCode = result.exitCode;
|
|
222
|
-
}
|
|
223
|
-
catch (e) {
|
|
224
|
-
outputError(e, opts.quiet);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
}
|
package/dist/commands/status.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { loadSession, isSessionExpired } from '../config/session.js';
|
|
2
|
-
export function registerStatusCommand(program) {
|
|
3
|
-
program
|
|
4
|
-
.command('status')
|
|
5
|
-
.description('Show current auth state')
|
|
6
|
-
.action(() => {
|
|
7
|
-
const session = loadSession();
|
|
8
|
-
if (!session) {
|
|
9
|
-
console.log('Not logged in. Run `wafflebase login`.');
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
console.log(`Logged in as ${session.user.username} (${session.user.email})`);
|
|
13
|
-
console.log(`Server: ${session.server}`);
|
|
14
|
-
const ws = session.workspaces.find((w) => w.id === session.activeWorkspace);
|
|
15
|
-
const wsLabel = ws
|
|
16
|
-
? `${ws.name} (${session.activeWorkspace.slice(0, 8)}...)`
|
|
17
|
-
: session.activeWorkspace;
|
|
18
|
-
console.log(`Workspace: ${wsLabel}`);
|
|
19
|
-
const expired = isSessionExpired(session);
|
|
20
|
-
console.log(`Session: ${expired ? 'expired' : 'valid'} (expires ${session.expiresAt})`);
|
|
21
|
-
});
|
|
22
|
-
}
|
package/dist/commands/tabs.d.ts
DELETED
package/dist/commands/tabs.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { getGlobalOpts, getClient } from './root.js';
|
|
2
|
-
import { output, outputError } from '../output/formatter.js';
|
|
3
|
-
export function registerTabsCommand(parent) {
|
|
4
|
-
const tab = parent.command('tabs').alias('tab').description('Manage tabs');
|
|
5
|
-
tab
|
|
6
|
-
.command('list <doc-id>')
|
|
7
|
-
.description('List tabs in a document')
|
|
8
|
-
.action(async function (docId) {
|
|
9
|
-
const opts = getGlobalOpts(this);
|
|
10
|
-
try {
|
|
11
|
-
const res = await getClient(opts).listTabs(docId);
|
|
12
|
-
if (!res.ok)
|
|
13
|
-
throw new Error(`HTTP ${res.status}`);
|
|
14
|
-
output(res.data, opts.format, opts.quiet);
|
|
15
|
-
}
|
|
16
|
-
catch (e) {
|
|
17
|
-
outputError(e, opts.quiet);
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
}
|
package/dist/config/config.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export declare const DEFAULT_SERVER = "https://api.wafflebase.io";
|
|
2
|
-
export interface CliConfig {
|
|
3
|
-
server: string;
|
|
4
|
-
apiKey: string;
|
|
5
|
-
workspace: string;
|
|
6
|
-
authMode: 'api-key' | 'jwt' | 'none';
|
|
7
|
-
accessToken?: string;
|
|
8
|
-
refreshToken?: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Migrate config from old path (~/.config/wafflebase/config.yaml) to new path
|
|
12
|
-
* (~/.wafflebase/config.yaml) if needed.
|
|
13
|
-
*/
|
|
14
|
-
export declare function migrateConfigIfNeeded(newPath: string, oldPath?: string): void;
|
|
15
|
-
/**
|
|
16
|
-
* Return the resolved config file path.
|
|
17
|
-
* WAFFLEBASE_CONFIG env var overrides everything; otherwise defaults to
|
|
18
|
-
* ~/.wafflebase/config.yaml (with migration from old ~/.config/wafflebase path).
|
|
19
|
-
*/
|
|
20
|
-
export declare function getConfigPath(): string;
|
|
21
|
-
/**
|
|
22
|
-
* Resolve CLI configuration from flags > env > session > config file.
|
|
23
|
-
*
|
|
24
|
-
* Auth resolution order:
|
|
25
|
-
* 1. Flag/env `--api-key` or `WAFFLEBASE_API_KEY` → API key auth
|
|
26
|
-
* 2. Session `~/.wafflebase/session.json` with both tokens → JWT auth
|
|
27
|
-
* (the access token may already be past its `expiresAt`; the HTTP
|
|
28
|
-
* client refreshes it on the first 401 and falls back to a clear
|
|
29
|
-
* SESSION_EXPIRED error when the refresh token is also dead)
|
|
30
|
-
* 3. Config profile `api-key` → API key auth
|
|
31
|
-
* 4. None → empty (commands will fail)
|
|
32
|
-
*/
|
|
33
|
-
export declare function resolveConfig(flags: {
|
|
34
|
-
server?: string;
|
|
35
|
-
apiKey?: string;
|
|
36
|
-
workspace?: string;
|
|
37
|
-
profile?: string;
|
|
38
|
-
}): CliConfig;
|
package/dist/config/config.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
2
|
-
import { join, dirname } from 'node:path';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { parse as parseYaml } from 'yaml';
|
|
5
|
-
import { loadSession } from './session.js';
|
|
6
|
-
export const DEFAULT_SERVER = 'https://api.wafflebase.io';
|
|
7
|
-
/**
|
|
8
|
-
* Migrate config from old path (~/.config/wafflebase/config.yaml) to new path
|
|
9
|
-
* (~/.wafflebase/config.yaml) if needed.
|
|
10
|
-
*/
|
|
11
|
-
export function migrateConfigIfNeeded(newPath, oldPath = join(homedir(), '.config', 'wafflebase', 'config.yaml')) {
|
|
12
|
-
if (existsSync(newPath))
|
|
13
|
-
return;
|
|
14
|
-
if (!existsSync(oldPath))
|
|
15
|
-
return;
|
|
16
|
-
try {
|
|
17
|
-
mkdirSync(dirname(newPath), { recursive: true });
|
|
18
|
-
copyFileSync(oldPath, newPath);
|
|
19
|
-
process.stderr.write(`[wafflebase] Config migrated from ${oldPath} to ${newPath}\n`);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// Silent failure — migration is best-effort
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Return the resolved config file path.
|
|
27
|
-
* WAFFLEBASE_CONFIG env var overrides everything; otherwise defaults to
|
|
28
|
-
* ~/.wafflebase/config.yaml (with migration from old ~/.config/wafflebase path).
|
|
29
|
-
*/
|
|
30
|
-
export function getConfigPath() {
|
|
31
|
-
if (process.env.WAFFLEBASE_CONFIG) {
|
|
32
|
-
return process.env.WAFFLEBASE_CONFIG;
|
|
33
|
-
}
|
|
34
|
-
const newPath = join(homedir(), '.wafflebase', 'config.yaml');
|
|
35
|
-
migrateConfigIfNeeded(newPath);
|
|
36
|
-
return newPath;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Resolve CLI configuration from flags > env > session > config file.
|
|
40
|
-
*
|
|
41
|
-
* Auth resolution order:
|
|
42
|
-
* 1. Flag/env `--api-key` or `WAFFLEBASE_API_KEY` → API key auth
|
|
43
|
-
* 2. Session `~/.wafflebase/session.json` with both tokens → JWT auth
|
|
44
|
-
* (the access token may already be past its `expiresAt`; the HTTP
|
|
45
|
-
* client refreshes it on the first 401 and falls back to a clear
|
|
46
|
-
* SESSION_EXPIRED error when the refresh token is also dead)
|
|
47
|
-
* 3. Config profile `api-key` → API key auth
|
|
48
|
-
* 4. None → empty (commands will fail)
|
|
49
|
-
*/
|
|
50
|
-
export function resolveConfig(flags) {
|
|
51
|
-
const profile = loadProfile(flags.profile ?? 'default');
|
|
52
|
-
// Step 1: Check flags/env for API key
|
|
53
|
-
const flagOrEnvApiKey = flags.apiKey ?? process.env.WAFFLEBASE_API_KEY ?? undefined;
|
|
54
|
-
if (flagOrEnvApiKey) {
|
|
55
|
-
return {
|
|
56
|
-
server: flags.server ??
|
|
57
|
-
process.env.WAFFLEBASE_SERVER ??
|
|
58
|
-
profile.server ??
|
|
59
|
-
DEFAULT_SERVER,
|
|
60
|
-
apiKey: flagOrEnvApiKey,
|
|
61
|
-
workspace: flags.workspace ??
|
|
62
|
-
process.env.WAFFLEBASE_WORKSPACE ??
|
|
63
|
-
profile.workspace ??
|
|
64
|
-
'',
|
|
65
|
-
authMode: 'api-key',
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
// Step 2: Try session — accept it whenever both tokens are present.
|
|
69
|
-
// Gating on `isSessionExpired` here would silently drop the session
|
|
70
|
-
// (and its `activeWorkspace`) the moment the access token expired,
|
|
71
|
-
// turning every command into a `/workspaces//...` 404. Letting the
|
|
72
|
-
// HTTP client see the expired access token + refresh token lets its
|
|
73
|
-
// 401 → refresh → retry path do its job.
|
|
74
|
-
const session = loadSession();
|
|
75
|
-
if (session && session.accessToken && session.refreshToken) {
|
|
76
|
-
return {
|
|
77
|
-
server: flags.server ??
|
|
78
|
-
process.env.WAFFLEBASE_SERVER ??
|
|
79
|
-
session.server ??
|
|
80
|
-
profile.server ??
|
|
81
|
-
DEFAULT_SERVER,
|
|
82
|
-
apiKey: '',
|
|
83
|
-
workspace: flags.workspace ??
|
|
84
|
-
process.env.WAFFLEBASE_WORKSPACE ??
|
|
85
|
-
session.activeWorkspace ??
|
|
86
|
-
profile.workspace ??
|
|
87
|
-
'',
|
|
88
|
-
authMode: 'jwt',
|
|
89
|
-
accessToken: session.accessToken,
|
|
90
|
-
refreshToken: session.refreshToken,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
// Step 3: Fallback to config profile API key
|
|
94
|
-
const profileApiKey = profile['api-key'];
|
|
95
|
-
if (profileApiKey) {
|
|
96
|
-
return {
|
|
97
|
-
server: flags.server ??
|
|
98
|
-
process.env.WAFFLEBASE_SERVER ??
|
|
99
|
-
profile.server ??
|
|
100
|
-
DEFAULT_SERVER,
|
|
101
|
-
apiKey: profileApiKey,
|
|
102
|
-
workspace: flags.workspace ??
|
|
103
|
-
process.env.WAFFLEBASE_WORKSPACE ??
|
|
104
|
-
profile.workspace ??
|
|
105
|
-
'',
|
|
106
|
-
authMode: 'api-key',
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
// Step 4: Nothing available
|
|
110
|
-
return {
|
|
111
|
-
server: flags.server ??
|
|
112
|
-
process.env.WAFFLEBASE_SERVER ??
|
|
113
|
-
profile.server ??
|
|
114
|
-
DEFAULT_SERVER,
|
|
115
|
-
apiKey: '',
|
|
116
|
-
workspace: flags.workspace ??
|
|
117
|
-
process.env.WAFFLEBASE_WORKSPACE ??
|
|
118
|
-
profile.workspace ??
|
|
119
|
-
'',
|
|
120
|
-
authMode: 'none',
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function loadProfile(name) {
|
|
124
|
-
const configPath = getConfigPath();
|
|
125
|
-
if (!existsSync(configPath))
|
|
126
|
-
return {};
|
|
127
|
-
try {
|
|
128
|
-
const raw = readFileSync(configPath, 'utf-8');
|
|
129
|
-
const config = parseYaml(raw);
|
|
130
|
-
return config?.profiles?.[name] ?? {};
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
return {};
|
|
134
|
-
}
|
|
135
|
-
}
|
package/dist/config/session.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export interface SessionUser {
|
|
2
|
-
id: number;
|
|
3
|
-
username: string;
|
|
4
|
-
email: string;
|
|
5
|
-
photo: string | null;
|
|
6
|
-
}
|
|
7
|
-
export interface WorkspaceInfo {
|
|
8
|
-
id: string;
|
|
9
|
-
name: string;
|
|
10
|
-
}
|
|
11
|
-
export interface Session {
|
|
12
|
-
server: string;
|
|
13
|
-
user: SessionUser;
|
|
14
|
-
accessToken: string;
|
|
15
|
-
refreshToken: string;
|
|
16
|
-
expiresAt: string;
|
|
17
|
-
activeWorkspace: string;
|
|
18
|
-
workspaces: WorkspaceInfo[];
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Returns the default session file path: ~/.wafflebase/session.json
|
|
22
|
-
*/
|
|
23
|
-
export declare function getSessionPath(): string;
|
|
24
|
-
/**
|
|
25
|
-
* Loads the session from disk. Returns null if the file does not exist or
|
|
26
|
-
* contains invalid JSON.
|
|
27
|
-
*/
|
|
28
|
-
export declare function loadSession(path?: string): Session | null;
|
|
29
|
-
/**
|
|
30
|
-
* Writes the session to disk with owner-only (0600) permissions.
|
|
31
|
-
* Creates the parent directory if it does not exist.
|
|
32
|
-
*/
|
|
33
|
-
export declare function saveSession(session: Session, path?: string): void;
|
|
34
|
-
/**
|
|
35
|
-
* Deletes the session file if it exists. No-op otherwise.
|
|
36
|
-
*/
|
|
37
|
-
export declare function clearSession(path?: string): void;
|
|
38
|
-
/**
|
|
39
|
-
* Returns true when the session's expiresAt timestamp is in the past.
|
|
40
|
-
*/
|
|
41
|
-
export declare function isSessionExpired(session: Session): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Decodes the `exp` claim from a JWT's payload segment and returns an ISO
|
|
44
|
-
* 8601 string.
|
|
45
|
-
*
|
|
46
|
-
* JWT format: <header>.<payload>.<signature>
|
|
47
|
-
* The payload is base64url-encoded JSON.
|
|
48
|
-
*/
|
|
49
|
-
export declare function decodeJwtExpiry(token: string): string;
|