@deckflow/deckuse 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,122 +1,59 @@
1
1
  #!/usr/bin/env node
2
- import { readFile } from 'node:fs/promises';
3
- import { extname, resolve } from 'node:path';
2
+ import { access, readFile } from 'node:fs/promises';
3
+ import { dirname, extname, join, resolve } from 'node:path';
4
+ import { PROTOCOL_VERSION, commandJsonSchema, measureText, parseLength, revisionAsNumber, } from '@deckflow/deckuse-core';
5
+ import { resolveCliText } from './cli-text.js';
6
+ import { helpTopicFromArgs, resolveHelp } from './help.js';
4
7
  import { runCommand } from './index.js';
8
+ import { monitorStart, monitorStatus, monitorStop, runMonitorForeground, } from './monitor-daemon.js';
9
+ import { renderPage } from './render.js';
10
+ import { EDITION } from './edition.js';
5
11
  import { version } from './version.js';
6
12
  const args = process.argv.slice(2);
7
- const json = args.includes('--json');
8
- const clean = args.filter((arg) => arg !== '--json');
9
- const HELP = {
10
- main: `usage: deckuse <command> [<args>] [--json]
11
-
12
- Inspect, modify, validate, and export Office document workspaces.
13
-
14
- Common commands:
15
- init <input> <workspace> Create a workspace from an Office document
16
- inspect <workspace> Inspect the document structure
17
- query <workspace> [selector] Query document elements
18
- apply <workspace> Apply JSON command objects
19
- validate <workspace> Validate workspace changes
20
- commit <workspace> Export the workspace to a document
21
-
22
- Run 'deckuse <command> --help' for details about a command.
23
- Run 'deckuse help <command>' for the same command reference.
24
-
25
- Options:
26
- -h, --help Show this help message
27
- -v, -V, --version
28
- Show the deckuse version
29
- --json Format command results as compact JSON
30
- `,
31
- init: `usage: deckuse init <input> <workspace> [--json]
32
-
33
- Create a deckuse workspace from an Office document.
34
-
35
- Arguments:
36
- <input> Source document (.pptx, .docx, .xlsx, .key, or .numbers)
37
- <workspace> Directory in which to create the workspace
38
-
39
- Example:
40
- deckuse init presentation.pptx ./presentation
41
- `,
42
- inspect: `usage: deckuse inspect <workspace> [--depth <n>] [--json]
43
-
44
- Inspect the indexed structure of a workspace.
45
-
46
- Arguments:
47
- <workspace> Existing deckuse workspace
48
-
49
- Options:
50
- --depth <n> Traversal depth (default: 2)
51
-
52
- Example:
53
- deckuse inspect ./presentation --depth 3
54
- `,
55
- query: `usage: deckuse query <workspace> [selector] [--limit <n>] [--json]
56
-
57
- Query indexed elements in a workspace.
58
-
59
- Arguments:
60
- <workspace> Existing deckuse workspace
61
- [selector] Element selector (default: *)
62
-
63
- Options:
64
- --limit <n> Maximum results to return (default: 100)
65
-
66
- Example:
67
- deckuse query ./presentation 'slide[*] text' --limit 20
68
- `,
69
- apply: `usage: deckuse apply <workspace> [--input <file|->] [--json]
70
-
71
- Apply one or more JSON command objects to a workspace. Input may be a JSON
72
- object, a JSON array, or newline-delimited JSON. Commands read from standard
73
- input by default.
74
-
75
- Arguments:
76
- <workspace> Existing deckuse workspace
77
-
78
- Options:
79
- --input <file|-> Command input file, or - for standard input (default: -)
80
-
81
- Examples:
82
- deckuse apply ./presentation --input changes.json
83
- printf '%s\n' '{"type":"add", ...}' | deckuse apply ./presentation
84
- `,
85
- validate: `usage: deckuse validate <workspace> [--level <level>] [--json]
86
-
87
- Validate workspace changes before export.
88
-
89
- Arguments:
90
- <workspace> Existing deckuse workspace
91
-
92
- Options:
93
- --level <level> Validation level (default: full)
94
-
95
- Example:
96
- deckuse validate ./presentation --level full
97
- `,
98
- commit: `usage: deckuse commit <workspace> [-o <output>] [--force] [--json]
99
-
100
- Export workspace changes to an Office document.
101
-
102
- Arguments:
103
- <workspace> Existing deckuse workspace
104
-
105
- Options:
106
- -o, --output <output>
107
- Destination document path
108
- -f, --force Overwrite an existing destination
109
-
110
- Example:
111
- deckuse commit ./presentation -o updated.pptx
112
- `,
13
+ const takeFlag = (list, name) => {
14
+ const i = list.indexOf(name);
15
+ if (i < 0)
16
+ return false;
17
+ list.splice(i, 1);
18
+ return true;
113
19
  };
114
- const writeHelp = (command = 'main') => {
115
- process.stdout.write(HELP[command]);
20
+ const takeOption = (list, name) => {
21
+ const i = list.indexOf(name);
22
+ if (i < 0)
23
+ return undefined;
24
+ const value = list[i + 1];
25
+ list.splice(i, 2);
26
+ return value;
27
+ };
28
+ const json = takeFlag(args, '--json');
29
+ const quiet = takeFlag(args, '--quiet');
30
+ const dryRun = takeFlag(args, '--dry-run');
31
+ const workspaceOpt = takeOption(args, '--workspace');
32
+ const revisionOpt = takeOption(args, '--revision');
33
+ const expectRevisionOpt = takeOption(args, '--expect-revision');
34
+ const reasonOpt = takeOption(args, '--reason');
35
+ const clean = args;
36
+ const findWorkspace = async (start) => {
37
+ if (start)
38
+ return resolve(start);
39
+ let dir = resolve(process.cwd());
40
+ for (;;) {
41
+ try {
42
+ await access(join(dir, '.deckuse', 'manifest.json'));
43
+ return dir;
44
+ }
45
+ catch {
46
+ const parent = dirname(dir);
47
+ if (parent === dir)
48
+ throw new Error('No deckuse workspace found; pass --workspace <path>');
49
+ dir = parent;
50
+ }
51
+ }
52
+ };
53
+ const revisionFor = async (workspace) => {
54
+ const manifest = JSON.parse(await readFile(resolve(workspace, '.deckuse', 'manifest.json'), 'utf8'));
55
+ return manifest.revision;
116
56
  };
117
- const isHelpFlag = (value) => value === '--help' || value === '-h';
118
- const isVersionFlag = (value) => value === '--version' || value === '-v' || value === '-V';
119
- const isHelpCommand = (value) => value in HELP && value !== 'main';
120
57
  const readStdin = async () => {
121
58
  let value = '';
122
59
  process.stdin.setEncoding('utf8');
@@ -124,137 +61,913 @@ const readStdin = async () => {
124
61
  value += String(chunk);
125
62
  return value;
126
63
  };
127
- const option = (name) => {
128
- const i = clean.indexOf(name);
129
- return i >= 0 ? clean[i + 1] : undefined;
64
+ const mutationExtras = async (workspace) => {
65
+ const transactionId = expectRevisionOpt ?? (await revisionFor(workspace));
66
+ return {
67
+ version: PROTOCOL_VERSION,
68
+ workspaceId: workspace,
69
+ transactionId,
70
+ ...(dryRun ? { dryRun: true } : {}),
71
+ ...(expectRevisionOpt ? { expectRevision: expectRevisionOpt } : {}),
72
+ ...(reasonOpt ? { reason: reasonOpt } : {}),
73
+ };
74
+ };
75
+ const toEnvelope = (commandLabel, result, extras = {}) => {
76
+ if (!result.ok) {
77
+ return {
78
+ ok: false,
79
+ command: commandLabel,
80
+ warnings: result.diagnostics
81
+ .filter((d) => d.severity === 'warning')
82
+ .map((d) => d.message),
83
+ error: {
84
+ code: result.error.code,
85
+ message: result.error.message,
86
+ ...(result.error.target ? { target: result.error.target } : {}),
87
+ ...(result.error.hint ? { hint: result.error.hint } : {}),
88
+ ...(result.error.diagnostics?.length
89
+ ? { diagnostics: result.error.diagnostics }
90
+ : result.diagnostics.length
91
+ ? { diagnostics: result.diagnostics }
92
+ : {}),
93
+ },
94
+ ...extras,
95
+ };
96
+ }
97
+ const value = result.value;
98
+ const revision = revisionAsNumber(value['revision']) ??
99
+ (typeof value['revision'] === 'number' ? value['revision'] : undefined);
100
+ return {
101
+ ok: true,
102
+ command: commandLabel,
103
+ ...(revision !== undefined ? { revision } : {}),
104
+ branch: typeof value['branch'] === 'string' ? value['branch'] : 'main',
105
+ ...(Array.isArray(value['affectedSlides'])
106
+ ? { affectedSlides: value['affectedSlides'] }
107
+ : {}),
108
+ ...(Array.isArray(value['changedTargets'])
109
+ ? { changedTargets: value['changedTargets'] }
110
+ : {}),
111
+ ...(Array.isArray(value['changedParts'])
112
+ ? { changedParts: value['changedParts'] }
113
+ : {}),
114
+ warnings: [
115
+ ...result.diagnostics.filter((d) => d.severity === 'warning').map((d) => d.message),
116
+ ...(Array.isArray(value['warnings']) ? value['warnings'] : []),
117
+ ],
118
+ data: value,
119
+ ...extras,
120
+ };
130
121
  };
131
- const output = (result) => {
132
- process.stdout.write(`${JSON.stringify(result, null, json ? 0 : 2)}\n`);
122
+ const outputEnvelope = (envelope) => {
123
+ if (json) {
124
+ process.stdout.write(`${JSON.stringify(envelope)}\n`);
125
+ return;
126
+ }
127
+ if (quiet)
128
+ return;
129
+ if (!envelope.ok) {
130
+ process.stderr.write(`deckuse: ${envelope.error?.code ?? 'ERROR'}: ${envelope.error?.message ?? 'failed'}\n`);
131
+ if (envelope.error?.hint)
132
+ process.stderr.write(`hint: ${envelope.error.hint}\n`);
133
+ const diagnostics = envelope.error?.diagnostics;
134
+ if (Array.isArray(diagnostics)) {
135
+ for (const d of diagnostics) {
136
+ const path = Array.isArray(d.path) && d.path.length > 0 ? `${d.path.map(String).join('.')}: ` : '';
137
+ process.stderr.write(` - ${path}${d.message}\n`);
138
+ }
139
+ }
140
+ return;
141
+ }
142
+ process.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
133
143
  };
134
- const execute = async (command) => {
144
+ const execute = async (commandLabel, command) => {
145
+ if (revisionOpt && command && typeof command === 'object' && command !== null) {
146
+ const type = command.type;
147
+ if (type &&
148
+ ![
149
+ 'status',
150
+ 'list',
151
+ 'get',
152
+ 'inspect',
153
+ 'search',
154
+ 'query',
155
+ 'getText',
156
+ 'validate',
157
+ 'history',
158
+ 'export',
159
+ ].includes(type)) {
160
+ outputEnvelope({
161
+ ok: false,
162
+ command: commandLabel,
163
+ error: {
164
+ code: 'INVALID_COMMAND',
165
+ message: 'Write commands reject --revision; omit it or use a read command',
166
+ },
167
+ });
168
+ return false;
169
+ }
170
+ }
135
171
  const result = await runCommand(command);
136
- output(result);
172
+ outputEnvelope(toEnvelope(commandLabel, result));
137
173
  return result.ok;
138
174
  };
139
- const revisionFor = async (workspace) => {
140
- const manifest = JSON.parse(await readFile(resolve(workspace, '.deckuse', 'manifest.json'), 'utf8'));
141
- return manifest.revision;
142
- };
143
- try {
144
- if (clean.length === 0) {
145
- const payload = await readStdin();
146
- const ok = await execute(JSON.parse(payload));
147
- if (!ok)
148
- process.exitCode = 1;
149
- }
150
- else if (isHelpFlag(clean[0])) {
151
- writeHelp();
175
+ const parseProps = (list) => {
176
+ const props = {};
177
+ for (let i = 0; i < list.length;) {
178
+ const token = list[i];
179
+ if (!token.startsWith('--')) {
180
+ i += 1;
181
+ continue;
182
+ }
183
+ const key = token.slice(2);
184
+ const next = list[i + 1];
185
+ if (next === undefined || next.startsWith('--')) {
186
+ props[key] = true;
187
+ i += 1;
188
+ continue;
189
+ }
190
+ let value = next;
191
+ if (next === 'true')
192
+ value = true;
193
+ else if (next === 'false')
194
+ value = false;
195
+ else if (/^-?\d+(\.\d+)?$/.test(next))
196
+ value = Number(next);
197
+ else if ((next.startsWith('{') && next.endsWith('}')) ||
198
+ (next.startsWith('[') && next.endsWith(']'))) {
199
+ try {
200
+ value = JSON.parse(next);
201
+ }
202
+ catch {
203
+ value = next;
204
+ }
205
+ }
206
+ props[key] = value;
207
+ i += 2;
152
208
  }
153
- else if (isVersionFlag(clean[0])) {
154
- process.stdout.write(`deckuse ${version}\n`);
209
+ return props;
210
+ };
211
+ const optionFrom = (list, name) => {
212
+ const i = list.indexOf(name);
213
+ return i >= 0 ? list[i + 1] : undefined;
214
+ };
215
+ /** Pass through bare numbers as number; keep unit strings for protocol parseLength. */
216
+ const lengthArg = (raw) => {
217
+ if (raw === undefined)
218
+ return undefined;
219
+ if (raw === 'auto')
220
+ return 'auto';
221
+ if (/^-?\d+(\.\d+)?$/.test(raw))
222
+ return Number(raw);
223
+ return raw;
224
+ };
225
+ const APPLY_WRITE_TYPES = new Set([
226
+ 'setText',
227
+ 'replaceText',
228
+ 'setTransform',
229
+ 'setProperties',
230
+ 'set',
231
+ 'xfrmSet',
232
+ 'zMove',
233
+ 'add',
234
+ 'addSlide',
235
+ 'addShape',
236
+ 'remove',
237
+ 'replacePicture',
238
+ 'duplicate',
239
+ 'alignElements',
240
+ 'batch',
241
+ ]);
242
+ const isRecord = (value) => typeof value === 'object' && value !== null;
243
+ const isTransactionOp = (value) => isRecord(value) && 'op' in value;
244
+ const isHighLevelWrite = (value) => isRecord(value) && APPLY_WRITE_TYPES.has(String(value['type']));
245
+ /** Classify apply payload items: high-level batch vs low-level transaction. */
246
+ const classifyApplyOps = (ops) => {
247
+ if (ops.length === 0)
248
+ return { kind: 'invalid', message: 'apply input operations array is empty' };
249
+ if (ops.every(isTransactionOp))
250
+ return { kind: 'transaction' };
251
+ if (ops.every(isHighLevelWrite))
252
+ return { kind: 'batch' };
253
+ if (ops.some(isHighLevelWrite) && ops.some(isTransactionOp)) {
254
+ return {
255
+ kind: 'invalid',
256
+ message: 'apply input mixes high-level write commands (type) with transaction ops (op); use one format',
257
+ };
155
258
  }
156
- else if (clean[0] === 'help') {
157
- const command = clean[1];
158
- if (!command)
159
- writeHelp();
160
- else if (isHelpCommand(command))
161
- writeHelp(command);
162
- else
163
- throw new Error(`Unknown command: ${command}`);
259
+ if (ops.some(isHighLevelWrite))
260
+ return { kind: 'batch' };
261
+ if (ops.some(isTransactionOp)) {
262
+ return {
263
+ kind: 'invalid',
264
+ message: 'apply transaction operations must all include "op"; high-level writes must use "type"',
265
+ };
164
266
  }
165
- else {
166
- const action = clean[0];
167
- let ok = true;
168
- if (!action || !isHelpCommand(action))
169
- throw new Error(`Unknown command: ${action ?? '(missing)'}`);
170
- if (isHelpFlag(clean[1])) {
171
- writeHelp(action);
267
+ return {
268
+ kind: 'invalid',
269
+ message: 'apply input must be high-level write commands ({ "type": "addShape"|... }) or transaction ops ({ "op": ... })',
270
+ };
271
+ };
272
+ const main = async () => {
273
+ try {
274
+ if (clean.length === 0) {
275
+ const payload = await readStdin();
276
+ const parsed = JSON.parse(payload);
277
+ const ok = await execute('stdin', parsed);
278
+ if (!ok)
279
+ process.exitCode = 1;
172
280
  }
173
- else if (action === 'init') {
174
- const source = clean[1], workspace = clean[2];
175
- if (!source || !workspace)
176
- throw new Error('Usage: deckuse init input.pptx workspace/');
177
- ok = await execute({
178
- version: '1.0',
179
- type: 'init',
180
- workspaceId: workspace,
181
- format: extname(source).slice(1).toLowerCase(),
182
- source,
183
- });
281
+ else if (clean[0] === '--version' || clean[0] === '-v' || clean[0] === '-V') {
282
+ process.stdout.write(`deckuse ${version} (edition=${EDITION})\n`);
184
283
  }
185
- else if (action === 'inspect' || action === 'query' || action === 'validate') {
186
- const workspace = clean[1];
187
- if (!workspace)
188
- throw new Error(`Usage: deckuse ${action} workspace/`);
189
- ok = await execute(action === 'query'
190
- ? {
191
- version: '1.0',
192
- type: 'query',
284
+ else {
285
+ const helpTopic = helpTopicFromArgs(clean);
286
+ if (helpTopic !== null) {
287
+ process.stdout.write(resolveHelp(helpTopic));
288
+ return;
289
+ }
290
+ const action = clean[0];
291
+ let ok = true;
292
+ if (action === 'schema') {
293
+ const typeOpt = optionFrom(clean, '--type') ??
294
+ (clean[1] && !clean[1].startsWith('--') ? clean[1] : undefined);
295
+ const full = commandJsonSchema;
296
+ const defs = full.$defs ?? {};
297
+ const resolveRef = (entry) => {
298
+ if (!entry || typeof entry !== 'object')
299
+ return undefined;
300
+ const record = entry;
301
+ if (typeof record['$ref'] === 'string') {
302
+ const ref = record['$ref'];
303
+ const name = ref.replace(/^#\/\$defs\//, '');
304
+ const resolved = defs[name];
305
+ return resolved && typeof resolved === 'object'
306
+ ? resolved
307
+ : undefined;
308
+ }
309
+ return record;
310
+ };
311
+ const typeOf = (entry) => {
312
+ const resolved = resolveRef(entry);
313
+ const typeProp = resolved?.['properties']
314
+ ? resolved['properties']['type']
315
+ : undefined;
316
+ if (!typeProp || typeof typeProp !== 'object')
317
+ return undefined;
318
+ const typeRecord = typeProp;
319
+ if (typeof typeRecord['const'] === 'string')
320
+ return typeRecord['const'];
321
+ const nested = resolveRef(typeProp);
322
+ return typeof nested?.['const'] === 'string' ? nested['const'] : undefined;
323
+ };
324
+ let schema = full;
325
+ if (typeOpt) {
326
+ const variants = full.oneOf ?? full.anyOf ?? [];
327
+ const match = variants.find((entry) => typeOf(entry) === typeOpt);
328
+ if (!match) {
329
+ const known = variants.map(typeOf).filter(Boolean).join(', ');
330
+ throw new Error(`Unknown command type for schema: ${typeOpt}. Known: ${known || '(none)'}`);
331
+ }
332
+ schema = { ...(resolveRef(match) ?? match), $defs: defs };
333
+ }
334
+ const payload = {
335
+ ok: true,
336
+ command: 'deckuse schema',
337
+ data: {
338
+ cliVersion: version,
339
+ protocolVersion: PROTOCOL_VERSION,
340
+ edition: EDITION,
341
+ ...(typeOpt ? { type: typeOpt } : {}),
342
+ schema,
343
+ },
344
+ };
345
+ process.stdout.write(`${JSON.stringify(payload, null, json ? undefined : 2)}\n`);
346
+ return;
347
+ }
348
+ if (action === 'measure') {
349
+ const text = optionFrom(clean, '--text');
350
+ if (text === undefined)
351
+ throw new Error('Usage: deckuse measure --text <string> --font-size <pt>');
352
+ const fontSize = Number(optionFrom(clean, '--font-size') ?? optionFrom(clean, '--fontSize') ?? 12);
353
+ if (!(fontSize > 0))
354
+ throw new Error('--font-size must be a positive number (pt)');
355
+ const bold = clean.includes('--bold');
356
+ const fontFamily = optionFrom(clean, '--font-family') ?? optionFrom(clean, '--fontFamily');
357
+ const maxWidthRaw = optionFrom(clean, '--max-width') ?? optionFrom(clean, '--maxWidth');
358
+ const maxWidthEmu = maxWidthRaw !== undefined
359
+ ? parseLength(lengthArg(maxWidthRaw) ?? maxWidthRaw, { axis: 'x' })
360
+ : undefined;
361
+ const measured = measureText({
362
+ text,
363
+ fontSize,
364
+ ...(bold ? { bold: true } : {}),
365
+ ...(fontFamily ? { fontFamily } : {}),
366
+ ...(maxWidthEmu !== undefined ? { maxWidthEmu } : {}),
367
+ });
368
+ outputEnvelope({
369
+ ok: true,
370
+ command: 'deckuse measure',
371
+ data: measured,
372
+ });
373
+ return;
374
+ }
375
+ if (action === 'init') {
376
+ const source = clean[1];
377
+ const workspace = workspaceOpt ?? clean[2];
378
+ if (!source || !workspace)
379
+ throw new Error('Usage: deckuse init <input.pptx> <workspace/>');
380
+ ok = await execute('deckuse init', {
381
+ version: PROTOCOL_VERSION,
382
+ type: 'init',
383
+ workspaceId: resolve(workspace),
384
+ format: extname(source).slice(1).toLowerCase(),
385
+ source: resolve(source),
386
+ });
387
+ }
388
+ else if (action === 'status') {
389
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
390
+ ok = await execute('deckuse status', {
391
+ version: PROTOCOL_VERSION,
392
+ type: 'status',
193
393
  workspaceId: workspace,
194
- selector: clean[2] ?? '*',
195
- limit: Number(option('--limit') ?? 100),
394
+ });
395
+ }
396
+ else if (action === 'list') {
397
+ const resource = clean[1];
398
+ if (!resource)
399
+ throw new Error('Usage: deckuse list <slides|shapes|layouts|masters|theme>');
400
+ const workspace = await findWorkspace(workspaceOpt);
401
+ const slide = optionFrom(clean, '--slide');
402
+ ok = await execute(`deckuse list ${resource}`, {
403
+ version: PROTOCOL_VERSION,
404
+ type: 'list',
405
+ workspaceId: workspace,
406
+ resource,
407
+ ...(slide ? { slide: Number(slide) } : {}),
408
+ });
409
+ }
410
+ else if (action === 'get') {
411
+ const target = clean[1];
412
+ if (!target)
413
+ throw new Error('Usage: deckuse get <target> [--resolve both] [--json]');
414
+ const workspace = await findWorkspace(workspaceOpt);
415
+ const resolveMode = optionFrom(clean, '--resolve') ?? 'both';
416
+ const propsRaw = optionFrom(clean, '--props');
417
+ ok = await execute('deckuse get', {
418
+ version: PROTOCOL_VERSION,
419
+ type: 'get',
420
+ workspaceId: workspace,
421
+ target,
422
+ resolve: resolveMode,
423
+ provenance: !clean.includes('--no-provenance'),
424
+ ...(propsRaw ? { props: propsRaw.split(',').map((p) => p.trim()) } : {}),
425
+ });
426
+ }
427
+ else if (action === 'inspect') {
428
+ const target = clean[1];
429
+ const workspace = await findWorkspace(workspaceOpt);
430
+ ok = await execute('deckuse inspect', {
431
+ version: PROTOCOL_VERSION,
432
+ type: 'inspect',
433
+ workspaceId: workspace,
434
+ ...(target && !target.startsWith('--') ? { target } : {}),
435
+ visualTree: clean.includes('--visual-tree'),
436
+ depth: Number(optionFrom(clean, '--depth') ?? 2),
437
+ });
438
+ }
439
+ else if (action === 'search') {
440
+ const kind = clean[1];
441
+ if (kind !== 'text' && kind !== 'shape')
442
+ throw new Error('Usage: deckuse search <text|shape> ...');
443
+ const workspace = await findWorkspace(workspaceOpt);
444
+ const query = kind === 'text' ? clean[2] : optionFrom(clean, '--query');
445
+ ok = await execute('deckuse search', {
446
+ version: PROTOCOL_VERSION,
447
+ type: 'search',
448
+ workspaceId: workspace,
449
+ kind,
450
+ ...(query ? { query } : {}),
451
+ ...(optionFrom(clean, '--name') ? { name: optionFrom(clean, '--name') } : {}),
452
+ limit: Number(optionFrom(clean, '--limit') ?? 100),
453
+ });
454
+ }
455
+ else if (action === 'add') {
456
+ const what = clean[1];
457
+ const workspace = await findWorkspace(workspaceOpt);
458
+ const base = await mutationExtras(workspace);
459
+ if (what === 'slide') {
460
+ ok = await execute('deckuse add slide', {
461
+ ...base,
462
+ type: 'addSlide',
463
+ ...(optionFrom(clean, '--after') ? { after: Number(optionFrom(clean, '--after')) } : {}),
464
+ ...(optionFrom(clean, '--layout') ? { layout: optionFrom(clean, '--layout') } : {}),
465
+ ...(optionFrom(clean, '--name') ? { name: optionFrom(clean, '--name') } : {}),
466
+ });
467
+ }
468
+ else if (what === 'shape') {
469
+ const slide = optionFrom(clean, '--slide');
470
+ const shapeType = optionFrom(clean, '--type');
471
+ if (!slide || !shapeType)
472
+ throw new Error('Usage: deckuse add shape --slide <n> --type <text|rect|rounded-rect|ellipse|line|connector|elbow|arrow|chevron|circular-arrow|image|group|table|chart|…>');
473
+ const rowsRaw = optionFrom(clean, '--rows');
474
+ let rows;
475
+ if (rowsRaw !== undefined) {
476
+ try {
477
+ const parsed = JSON.parse(rowsRaw);
478
+ if (!Array.isArray(parsed) ||
479
+ !parsed.every((row) => Array.isArray(row) && row.every((cell) => typeof cell === 'string'))) {
480
+ throw new Error('expected string[][]');
481
+ }
482
+ rows = parsed;
483
+ }
484
+ catch {
485
+ throw new Error(`--rows must be JSON string[][] (example: --rows '[["A","B"],["1","2"]]')`);
486
+ }
487
+ }
488
+ const dataRaw = optionFrom(clean, '--data');
489
+ let data;
490
+ if (dataRaw !== undefined) {
491
+ try {
492
+ data = JSON.parse(dataRaw);
493
+ }
494
+ catch {
495
+ throw new Error(`--data must be JSON (example: --data '{"categories":["Q1"],"series":[{"name":"S1","values":[1]}]}')`);
496
+ }
497
+ }
498
+ const chartType = optionFrom(clean, '--chart-type');
499
+ const textRaw = takeFlag(clean, '--text-raw');
500
+ const textFile = optionFrom(clean, '--text-file');
501
+ const textOpt = optionFrom(clean, '--text');
502
+ const initialText = await resolveCliText({
503
+ ...(textOpt !== undefined ? { value: textOpt } : {}),
504
+ ...(textFile !== undefined ? { textFile } : {}),
505
+ raw: textRaw,
506
+ });
507
+ const theme = optionFrom(clean, '--theme');
508
+ const alignColumnsRaw = optionFrom(clean, '--align-columns');
509
+ let alignColumns;
510
+ if (alignColumnsRaw) {
511
+ try {
512
+ alignColumns = JSON.parse(alignColumnsRaw);
513
+ }
514
+ catch {
515
+ alignColumns = alignColumnsRaw.split(',').map((s) => s.trim());
516
+ }
517
+ }
518
+ ok = await execute('deckuse add shape', {
519
+ ...base,
520
+ type: 'addShape',
521
+ slide: Number(slide),
522
+ shapeType,
523
+ ...(optionFrom(clean, '--name') ? { name: optionFrom(clean, '--name') } : {}),
524
+ ...(optionFrom(clean, '--role') ? { role: optionFrom(clean, '--role') } : {}),
525
+ ...(lengthArg(optionFrom(clean, '--x')) !== undefined
526
+ ? { x: lengthArg(optionFrom(clean, '--x')) }
527
+ : {}),
528
+ ...(lengthArg(optionFrom(clean, '--y')) !== undefined
529
+ ? { y: lengthArg(optionFrom(clean, '--y')) }
530
+ : {}),
531
+ ...(lengthArg(optionFrom(clean, '--width')) !== undefined
532
+ ? { width: lengthArg(optionFrom(clean, '--width')) }
533
+ : {}),
534
+ ...(lengthArg(optionFrom(clean, '--height')) !== undefined
535
+ ? { height: lengthArg(optionFrom(clean, '--height')) }
536
+ : {}),
537
+ ...(optionFrom(clean, '--file') ? { file: optionFrom(clean, '--file') } : {}),
538
+ ...(initialText !== undefined ? { text: initialText } : {}),
539
+ ...(rows !== undefined ? { rows } : {}),
540
+ ...(theme ? { theme } : {}),
541
+ ...(alignColumns ? { alignColumns } : {}),
542
+ ...(chartType ? { chartType } : {}),
543
+ ...(data !== undefined ? { data } : {}),
544
+ ...(takeFlag(clean, '--show-data-labels') ? { showDataLabels: true } : {}),
545
+ });
546
+ }
547
+ else
548
+ throw new Error('Usage: deckuse add <slide|shape> ...');
549
+ }
550
+ else if (action === 'remove') {
551
+ const target = clean[1];
552
+ if (!target)
553
+ throw new Error('Usage: deckuse remove <target>');
554
+ const workspace = await findWorkspace(workspaceOpt);
555
+ ok = await execute('deckuse remove', {
556
+ ...(await mutationExtras(workspace)),
557
+ type: 'remove',
558
+ target,
559
+ });
560
+ }
561
+ else if (action === 'set') {
562
+ const workspace = await findWorkspace(workspaceOpt);
563
+ const base = await mutationExtras(workspace);
564
+ if (clean[1] === 'text') {
565
+ const target = clean[2];
566
+ const textRaw = takeFlag(clean, '--text-raw');
567
+ const textFile = optionFrom(clean, '--text-file');
568
+ const valueOpt = optionFrom(clean, '--value');
569
+ const blocksRaw = optionFrom(clean, '--blocks');
570
+ if (!target)
571
+ throw new Error('Usage: deckuse set text <target> --value <text> | --text-file <path> | --blocks <json>');
572
+ if (blocksRaw !== undefined) {
573
+ let blocks;
574
+ try {
575
+ blocks = JSON.parse(blocksRaw);
576
+ }
577
+ catch {
578
+ throw new Error('--blocks must be JSON array of {text, fontSize?, textColor?, ...}');
579
+ }
580
+ ok = await execute('deckuse set text', {
581
+ ...base,
582
+ type: 'setText',
583
+ target,
584
+ blocks,
585
+ });
586
+ }
587
+ else {
588
+ const value = await resolveCliText({
589
+ ...(valueOpt !== undefined ? { value: valueOpt } : {}),
590
+ ...(textFile !== undefined ? { textFile } : {}),
591
+ raw: textRaw,
592
+ });
593
+ if (value === undefined)
594
+ throw new Error('Usage: deckuse set text <target> --value <text> | --text-file <path> | --blocks <json>');
595
+ ok = await execute('deckuse set text', {
596
+ ...base,
597
+ type: 'setText',
598
+ target,
599
+ value,
600
+ });
601
+ }
602
+ }
603
+ else {
604
+ const target = clean[1];
605
+ if (!target)
606
+ throw new Error('Usage: deckuse set <target> --font.size 42 ...');
607
+ const props = parseProps(clean.slice(2));
608
+ delete props['scope'];
609
+ const scope = optionFrom(clean, '--scope') ?? 'local';
610
+ // remove flag keys that are not properties
611
+ for (const key of Object.keys(props)) {
612
+ if (['expect-revision', 'reason', 'dry-run', 'workspace', 'json', 'quiet'].includes(key))
613
+ delete props[key];
614
+ }
615
+ ok = await execute('deckuse set', {
616
+ ...base,
617
+ type: 'set',
618
+ target,
619
+ properties: props,
620
+ scope,
621
+ });
622
+ }
623
+ }
624
+ else if (action === 'replace-text') {
625
+ const source = optionFrom(clean, '--source');
626
+ const target = optionFrom(clean, '--target');
627
+ if (source === undefined || target === undefined)
628
+ throw new Error('Usage: deckuse replace-text --source <text> --target <text> [--regex] [--limit <n>] [--selector <sel>]');
629
+ if (!source)
630
+ throw new Error('replace-text --source must be a non-empty string');
631
+ const workspace = await findWorkspace(workspaceOpt);
632
+ ok = await execute('deckuse replace-text', {
633
+ ...(await mutationExtras(workspace)),
634
+ type: 'replaceText',
635
+ find: source,
636
+ replace: target,
637
+ ...(clean.includes('--regex') ? { regex: true } : {}),
638
+ ...(optionFrom(clean, '--limit')
639
+ ? { limit: Number(optionFrom(clean, '--limit')) }
640
+ : {}),
641
+ ...(optionFrom(clean, '--selector')
642
+ ? { selector: optionFrom(clean, '--selector') }
643
+ : {}),
644
+ });
645
+ }
646
+ else if (action === 'xfrm') {
647
+ if (clean[1] !== 'set')
648
+ throw new Error('Usage: deckuse xfrm set --slide <n> --shape <id> ...');
649
+ const workspace = await findWorkspace(workspaceOpt);
650
+ const slide = optionFrom(clean, '--slide');
651
+ const shape = optionFrom(clean, '--shape');
652
+ const target = optionFrom(clean, '--target') ??
653
+ (slide && shape ? `slide:${slide}/shape:${shape}` : undefined);
654
+ if (!target)
655
+ throw new Error('xfrm set requires --target or --slide and --shape');
656
+ ok = await execute('deckuse xfrm set', {
657
+ ...(await mutationExtras(workspace)),
658
+ type: 'xfrmSet',
659
+ target,
660
+ ...(lengthArg(optionFrom(clean, '--x')) !== undefined
661
+ ? { x: lengthArg(optionFrom(clean, '--x')) }
662
+ : {}),
663
+ ...(lengthArg(optionFrom(clean, '--y')) !== undefined
664
+ ? { y: lengthArg(optionFrom(clean, '--y')) }
665
+ : {}),
666
+ ...(lengthArg(optionFrom(clean, '--width') ?? optionFrom(clean, '--cx')) !== undefined
667
+ ? { width: lengthArg(optionFrom(clean, '--width') ?? optionFrom(clean, '--cx')) }
668
+ : {}),
669
+ ...(lengthArg(optionFrom(clean, '--height') ?? optionFrom(clean, '--cy')) !== undefined
670
+ ? { height: lengthArg(optionFrom(clean, '--height') ?? optionFrom(clean, '--cy')) }
671
+ : {}),
672
+ ...(optionFrom(clean, '--rotation')
673
+ ? { rotation: Number(optionFrom(clean, '--rotation')) }
674
+ : {}),
675
+ });
676
+ }
677
+ else if (action === 'align') {
678
+ const workspace = await findWorkspace(workspaceOpt);
679
+ const slide = optionFrom(clean, '--slide');
680
+ const targetsRaw = optionFrom(clean, '--targets');
681
+ const mode = optionFrom(clean, '--mode');
682
+ if (!slide || !targetsRaw || !mode)
683
+ throw new Error('Usage: deckuse align --slide <n> --targets <t1,t2,...> --mode <left|right|top|bottom|center-h|center-v|distribute-h|distribute-v> [--gap <length>]');
684
+ const targets = targetsRaw.split(',').map((t) => t.trim()).filter(Boolean);
685
+ ok = await execute('deckuse align', {
686
+ ...(await mutationExtras(workspace)),
687
+ type: 'alignElements',
688
+ slide: Number(slide),
689
+ targets,
690
+ mode,
691
+ ...(lengthArg(optionFrom(clean, '--gap')) !== undefined
692
+ ? { gap: lengthArg(optionFrom(clean, '--gap')) }
693
+ : {}),
694
+ });
695
+ }
696
+ else if (action === 'z') {
697
+ if (clean[1] !== 'move')
698
+ throw new Error('Usage: deckuse z move <target> --above <target>');
699
+ const target = clean[2];
700
+ if (!target)
701
+ throw new Error('Usage: deckuse z move <target> ...');
702
+ const workspace = await findWorkspace(workspaceOpt);
703
+ ok = await execute('deckuse z move', {
704
+ ...(await mutationExtras(workspace)),
705
+ type: 'zMove',
706
+ target,
707
+ ...(optionFrom(clean, '--above') ? { above: optionFrom(clean, '--above') } : {}),
708
+ ...(optionFrom(clean, '--below') ? { below: optionFrom(clean, '--below') } : {}),
709
+ ...(clean.includes('--to-front') ? { toFront: true } : {}),
710
+ ...(clean.includes('--to-back') ? { toBack: true } : {}),
711
+ });
712
+ }
713
+ else if (action === 'apply') {
714
+ const input = optionFrom(clean, '--input') ?? '-';
715
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
716
+ const raw = input === '-' ? await readStdin() : await readFile(resolve(input), 'utf8');
717
+ let values;
718
+ let wrappedOperations;
719
+ try {
720
+ const parsed = JSON.parse(raw);
721
+ if (typeof parsed === 'object' &&
722
+ parsed !== null &&
723
+ Array.isArray(parsed.operations)) {
724
+ wrappedOperations = parsed.operations;
725
+ }
726
+ else {
727
+ values = Array.isArray(parsed) ? parsed : [parsed];
728
+ }
196
729
  }
197
- : {
198
- version: '1.0',
199
- type: action,
730
+ catch {
731
+ values = raw
732
+ .split(/\r?\n/)
733
+ .filter(Boolean)
734
+ .map((line) => JSON.parse(line));
735
+ }
736
+ const extras = await mutationExtras(workspace);
737
+ const runHighLevelBatch = async (list) => {
738
+ const commands = list.map((value) => {
739
+ if (!isRecord(value))
740
+ throw new Error('apply input must contain command objects');
741
+ if (value['type'] === 'batch')
742
+ return value;
743
+ if (!APPLY_WRITE_TYPES.has(String(value['type'])))
744
+ throw new Error(`apply input contains non-write command: ${String(value['type'])}`);
745
+ return { ...extras, ...value };
746
+ });
747
+ const payload = commands.length === 1 && commands[0]?.['type'] !== 'batch'
748
+ ? commands[0]
749
+ : {
750
+ ...extras,
751
+ type: 'batch',
752
+ atomic: true,
753
+ commands: commands.flatMap((command) => command['type'] === 'batch'
754
+ ? (command.commands ?? [])
755
+ : [command]),
756
+ };
757
+ return execute('deckuse apply', payload);
758
+ };
759
+ if (wrappedOperations) {
760
+ const classified = classifyApplyOps(wrappedOperations);
761
+ if (classified.kind === 'invalid')
762
+ throw new Error(classified.message);
763
+ if (classified.kind === 'transaction') {
764
+ ok = await execute('deckuse apply', {
765
+ ...extras,
766
+ type: 'applyTransaction',
767
+ operations: wrappedOperations,
768
+ });
769
+ }
770
+ else {
771
+ ok = await runHighLevelBatch(wrappedOperations);
772
+ }
773
+ }
774
+ else {
775
+ const list = values ?? [];
776
+ const classified = classifyApplyOps(list);
777
+ if (classified.kind === 'invalid')
778
+ throw new Error(classified.message);
779
+ if (classified.kind === 'transaction') {
780
+ ok = await execute('deckuse apply', {
781
+ ...extras,
782
+ type: 'applyTransaction',
783
+ operations: list,
784
+ });
785
+ }
786
+ else {
787
+ ok = await runHighLevelBatch(list);
788
+ }
789
+ }
790
+ }
791
+ else if (action === 'validate') {
792
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
793
+ ok = await execute('deckuse validate', {
794
+ version: PROTOCOL_VERSION,
795
+ type: 'validate',
200
796
  workspaceId: workspace,
201
- ...(action === 'inspect'
202
- ? { depth: Number(option('--depth') ?? 2) }
203
- : { level: option('--level') ?? 'full' }),
797
+ level: optionFrom(clean, '--level') ?? 'full',
798
+ package: clean.includes('--package'),
799
+ relationships: clean.includes('--relationships'),
800
+ ...(optionFrom(clean, '--slide') ? { slide: Number(optionFrom(clean, '--slide')) } : {}),
204
801
  });
205
- }
206
- else if (action === 'commit') {
207
- const workspace = clean[1];
208
- if (!workspace)
209
- throw new Error('Usage: deckuse commit workspace/ -o output.pptx [--force]');
210
- const force = clean.includes('--force') || clean.includes('-f');
211
- ok = await execute({
212
- version: '1.0',
213
- type: 'commit',
214
- workspaceId: workspace,
215
- transactionId: await revisionFor(workspace),
216
- ...(option('-o') || option('--output')
217
- ? { destination: option('-o') ?? option('--output') }
218
- : {}),
219
- ...(force ? { overwrite: true } : {}),
220
- });
221
- }
222
- else {
223
- const workspace = clean[1], input = option('--input') ?? '-';
224
- if (!workspace)
225
- throw new Error('Usage: deckuse apply workspace/ --input file|-');
226
- const raw = input === '-' ? await readStdin() : await readFile(resolve(input), 'utf8');
227
- let values;
228
- try {
229
- const parsed = JSON.parse(raw);
230
- values = Array.isArray(parsed) ? parsed : [parsed];
231
802
  }
232
- catch {
233
- values = raw
234
- .split(/\r?\n/)
235
- .filter(Boolean)
236
- .map((line) => JSON.parse(line));
803
+ else if (action === 'history') {
804
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
805
+ ok = await execute('deckuse history', {
806
+ version: PROTOCOL_VERSION,
807
+ type: 'history',
808
+ workspaceId: workspace,
809
+ limit: Number(optionFrom(clean, '--limit') ?? 100),
810
+ offset: Number(optionFrom(clean, '--offset') ?? 0),
811
+ ...(optionFrom(clean, '--slide') ? { slide: Number(optionFrom(clean, '--slide')) } : {}),
812
+ });
813
+ }
814
+ else if (action === 'undo') {
815
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
816
+ ok = await execute('deckuse undo', {
817
+ version: PROTOCOL_VERSION,
818
+ type: 'undo',
819
+ workspaceId: workspace,
820
+ steps: Number(optionFrom(clean, '--steps') ?? 1),
821
+ });
237
822
  }
238
- for (const value of values) {
239
- if (typeof value !== 'object' || value === null)
240
- throw new Error('apply input must contain command objects');
241
- const command = {
242
- version: '1.0',
823
+ else if (action === 'export') {
824
+ const output = clean[1];
825
+ if (!output)
826
+ throw new Error('Usage: deckuse export <output.pptx>');
827
+ const workspace = await findWorkspace(workspaceOpt);
828
+ const fromPackage = takeFlag(clean, '--from-package');
829
+ ok = await execute('deckuse export', {
830
+ version: PROTOCOL_VERSION,
831
+ type: 'export',
243
832
  workspaceId: workspace,
244
- transactionId: await revisionFor(workspace),
245
- ...value,
833
+ output: resolve(output),
834
+ ...(revisionOpt ? { revision: revisionOpt } : {}),
835
+ ...(fromPackage ? { fromPackage: true } : {}),
836
+ });
837
+ }
838
+ else if (action === 'monitor') {
839
+ const sub = clean[1] === 'start' || clean[1] === 'stop' || clean[1] === 'status' ? clean[1] : undefined;
840
+ const daemonWorker = takeFlag(clean, '--daemon-worker');
841
+ const positionalWorkspace = sub
842
+ ? clean[2] && !clean[2].startsWith('--')
843
+ ? clean[2]
844
+ : undefined
845
+ : clean[1] && !clean[1].startsWith('--')
846
+ ? clean[1]
847
+ : undefined;
848
+ const workspace = await findWorkspace(workspaceOpt ?? positionalWorkspace);
849
+ const port = Number(optionFrom(clean, '--port') ?? 4173);
850
+ if (!Number.isInteger(port) || port < 0 || port > 65535)
851
+ throw new Error('--port must be an integer between 0 and 65535');
852
+ const host = optionFrom(clean, '--host') ?? '0.0.0.0';
853
+ if (sub === 'start') {
854
+ try {
855
+ const meta = await monitorStart(workspace, { host, port });
856
+ outputEnvelope({
857
+ ok: true,
858
+ command: 'deckuse monitor start',
859
+ data: meta,
860
+ });
861
+ }
862
+ catch (error) {
863
+ outputEnvelope({
864
+ ok: false,
865
+ command: 'deckuse monitor start',
866
+ error: {
867
+ code: 'IO_ERROR',
868
+ message: error instanceof Error ? error.message : 'Monitor start failed',
869
+ },
870
+ });
871
+ process.exitCode = 1;
872
+ }
873
+ return;
874
+ }
875
+ if (sub === 'stop') {
876
+ const result = await monitorStop(workspace);
877
+ outputEnvelope({
878
+ ok: true,
879
+ command: 'deckuse monitor stop',
880
+ data: result,
881
+ });
882
+ return;
883
+ }
884
+ if (sub === 'status') {
885
+ const result = await monitorStatus(workspace);
886
+ outputEnvelope({
887
+ ok: true,
888
+ command: 'deckuse monitor status',
889
+ data: result,
890
+ });
891
+ return;
892
+ }
893
+ const monitor = await runMonitorForeground(workspace, {
894
+ host,
895
+ port,
896
+ asDaemonWorker: daemonWorker,
897
+ });
898
+ if (!daemonWorker)
899
+ process.stdout.write(`Deckuse monitor: ${monitor.url}\n`);
900
+ const shutdown = async (signal) => {
901
+ process.stderr.write(`deckuse monitor: received ${signal}, shutting down\n`);
902
+ await monitor.close();
903
+ process.exit(0);
246
904
  };
247
- ok = (await execute(command)) && ok;
248
- if (!ok)
249
- break;
905
+ process.once('SIGINT', () => void shutdown('SIGINT'));
906
+ process.once('SIGTERM', () => void shutdown('SIGTERM'));
907
+ return;
908
+ }
909
+ else if (action === 'render') {
910
+ const pageRaw = optionFrom(clean, '--page');
911
+ if (!pageRaw)
912
+ throw new Error('Usage: deckuse render --page <n> [--output <file.png>] [--scale <n>]');
913
+ if (/\s|,|-/.test(pageRaw))
914
+ throw new Error('--page accepts exactly one positive integer (not a range or list)');
915
+ const page = Number(pageRaw);
916
+ if (!Number.isInteger(page) || page < 1)
917
+ throw new Error('--page must be a positive integer');
918
+ const scaleRaw = optionFrom(clean, '--scale');
919
+ const scale = scaleRaw !== undefined ? Number(scaleRaw) : undefined;
920
+ if (scale !== undefined && !(scale > 0))
921
+ throw new Error('--scale must be a positive number');
922
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
923
+ const outputOpt = optionFrom(clean, '--output');
924
+ try {
925
+ const rendered = await renderPage(workspace, {
926
+ page,
927
+ ...(outputOpt ? { output: resolve(outputOpt) } : {}),
928
+ ...(scale !== undefined ? { scale } : {}),
929
+ });
930
+ outputEnvelope({
931
+ ok: true,
932
+ command: 'deckuse render',
933
+ warnings: [...rendered.warnings],
934
+ data: { page: rendered.page, output: rendered.output, warnings: rendered.warnings },
935
+ });
936
+ }
937
+ catch (error) {
938
+ outputEnvelope({
939
+ ok: false,
940
+ command: 'deckuse render',
941
+ error: {
942
+ code: 'RENDER_FAILED',
943
+ message: error instanceof Error ? error.message : 'Render failed',
944
+ },
945
+ });
946
+ ok = false;
947
+ }
948
+ }
949
+ else if (action === 'query') {
950
+ // Back-compat shim
951
+ const workspace = await findWorkspace(workspaceOpt ?? clean[1]);
952
+ ok = await execute('deckuse query', {
953
+ version: PROTOCOL_VERSION,
954
+ type: 'query',
955
+ workspaceId: workspace,
956
+ selector: clean[2] ?? '*',
957
+ limit: Number(optionFrom(clean, '--limit') ?? 100),
958
+ });
250
959
  }
960
+ else {
961
+ throw new Error(`Unknown command: ${action}`);
962
+ }
963
+ if (!ok)
964
+ process.exitCode = 1;
251
965
  }
252
- if (!ok)
253
- process.exitCode = 1;
254
966
  }
255
- }
256
- catch (cause) {
257
- process.stderr.write(`deckuse: ${cause instanceof Error ? cause.message : 'Invalid input'}\n`);
258
- process.exitCode = 2;
259
- }
967
+ catch (cause) {
968
+ process.stderr.write(`deckuse: ${cause instanceof Error ? cause.message : 'Invalid input'}\n`);
969
+ process.exitCode = 2;
970
+ }
971
+ };
972
+ void main();
260
973
  //# sourceMappingURL=bin.js.map