labelinn 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/bin/cli.js +793 -0
- package/lib/index.d.ts +327 -0
- package/lib/index.js +556 -0
- package/package.json +61 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* LabelInn CLI — quick testing tool for the LabelInn API
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx labelinn test # verify your API key
|
|
9
|
+
* npx labelinn printers # list printers
|
|
10
|
+
* npx labelinn printers <id> --status # get printer status
|
|
11
|
+
* npx labelinn print <printer_id> --zpl "^XA^FO50,50^ADN,36,20^FDHello^FS^XZ"
|
|
12
|
+
* npx labelinn print <printer_id> --image-url https://...
|
|
13
|
+
* npx labelinn print <printer_id> --design <design_id> --data '{"sku":"A1"}'
|
|
14
|
+
* npx labelinn jobs # list recent jobs
|
|
15
|
+
* npx labelinn jobs <id> # get job detail
|
|
16
|
+
* npx labelinn designs # list designs
|
|
17
|
+
* npx labelinn webhooks # list webhooks
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const LabelInn = require('../lib/index.js');
|
|
21
|
+
|
|
22
|
+
const HELP = `
|
|
23
|
+
LabelInn CLI v1.1.0 — Developer testing tool
|
|
24
|
+
|
|
25
|
+
SETUP:
|
|
26
|
+
Set your API key as an environment variable:
|
|
27
|
+
export LABELINN_API_KEY=sk_test_xxxxx (Linux/Mac)
|
|
28
|
+
$env:LABELINN_API_KEY="sk_test_xxxxx" (PowerShell)
|
|
29
|
+
|
|
30
|
+
COMMANDS:
|
|
31
|
+
test Verify API key & connectivity
|
|
32
|
+
|
|
33
|
+
login Save API key for future commands
|
|
34
|
+
login --key <key> Save key non-interactively
|
|
35
|
+
login status Check saved credentials
|
|
36
|
+
login logout Remove saved credentials
|
|
37
|
+
|
|
38
|
+
printers List all printers
|
|
39
|
+
printers <id> Get printer details
|
|
40
|
+
printers <id> --status Get printer status only
|
|
41
|
+
printers <id> --identify Flash/beep the printer
|
|
42
|
+
printers <id> --update --name <n> Update printer name
|
|
43
|
+
printers <id> --media <json> Set media definition (JSON)
|
|
44
|
+
|
|
45
|
+
groups List printer groups
|
|
46
|
+
groups <id> Get group details
|
|
47
|
+
groups add --name <n> [--printers id1,id2] Create a group
|
|
48
|
+
groups rm <id> Delete a group
|
|
49
|
+
|
|
50
|
+
print <printer_id> [options] Submit a print job
|
|
51
|
+
--zpl <string> Raw ZPL payload
|
|
52
|
+
--image-url <url> HTTPS image URL
|
|
53
|
+
--design <design_id> Template design ID
|
|
54
|
+
--data <json> Template variables (JSON)
|
|
55
|
+
--copies <n> Number of copies
|
|
56
|
+
--name <name> Job name
|
|
57
|
+
--priority <p> low, normal, high, urgent
|
|
58
|
+
--schedule <iso> Schedule for later (ISO 8601)
|
|
59
|
+
|
|
60
|
+
jobs List recent print jobs
|
|
61
|
+
jobs <id> Get job details
|
|
62
|
+
jobs <id> --cancel Cancel a queued job
|
|
63
|
+
jobs <id> --retry Retry a failed job
|
|
64
|
+
jobs <id> --reprint Reprint a completed job
|
|
65
|
+
batch <file.json> Batch print from JSON file
|
|
66
|
+
|
|
67
|
+
designs List designs
|
|
68
|
+
designs <id> Get design details
|
|
69
|
+
designs <id> --revisions List revision history
|
|
70
|
+
|
|
71
|
+
webhooks List webhooks
|
|
72
|
+
webhooks add <url> [events...] Subscribe to events
|
|
73
|
+
webhooks rm <id> Delete a webhook
|
|
74
|
+
webhooks test <id> Send test ping
|
|
75
|
+
webhooks update <id> [--events e1,e2] [--enabled true|false]
|
|
76
|
+
webhooks deliveries <id> List delivery history
|
|
77
|
+
|
|
78
|
+
connect Data Connect commands
|
|
79
|
+
connect sources List connector sources
|
|
80
|
+
connect sources <id> Get source details
|
|
81
|
+
connect sources create --name <n> [--format csv|json|xml]
|
|
82
|
+
connect sources rm <id> Delete a source
|
|
83
|
+
connect ingest <source_id> --file <path> Ingest data from file
|
|
84
|
+
connect ingest <source_id> --data <json> Ingest inline JSON data
|
|
85
|
+
connect test-parse --file <path> Parse without storing
|
|
86
|
+
connect schema <source_id> Get detected schema
|
|
87
|
+
connect records <source_id> List ingested records
|
|
88
|
+
connect mappings <source_id> Get field mappings
|
|
89
|
+
connect print <source_id> --design <id> --printer <id> Print from connector
|
|
90
|
+
|
|
91
|
+
OPTIONS:
|
|
92
|
+
--key <key> API key (overrides LABELINN_API_KEY)
|
|
93
|
+
--base-url <url> Override base URL
|
|
94
|
+
--json Output raw JSON
|
|
95
|
+
-h, --help Show this help
|
|
96
|
+
|
|
97
|
+
EXAMPLES:
|
|
98
|
+
npx labelinn test
|
|
99
|
+
npx labelinn print prt_abc123 --zpl "^XA^FO50,50^ADN,36,20^FDHello^FS^XZ"
|
|
100
|
+
npx labelinn print prt_abc123 --design dsg_ship --data '{"name":"Ali","barcode":"TR12345"}' --priority high
|
|
101
|
+
npx labelinn printers prt_abc123 --media '{"width_mm":100,"height_mm":50}'
|
|
102
|
+
npx labelinn printers prt_abc123 --identify
|
|
103
|
+
npx labelinn groups add --name "Warehouse" --printers prt_1,prt_2
|
|
104
|
+
npx labelinn jobs --json
|
|
105
|
+
`.trim();
|
|
106
|
+
|
|
107
|
+
// ── Arg Parsing ──
|
|
108
|
+
|
|
109
|
+
function parseArgs(argv) {
|
|
110
|
+
const args = { _: [], flags: {} };
|
|
111
|
+
let i = 0;
|
|
112
|
+
while (i < argv.length) {
|
|
113
|
+
const a = argv[i];
|
|
114
|
+
if (a === '-h' || a === '--help') {
|
|
115
|
+
args.flags.help = true;
|
|
116
|
+
} else if (a === '--json') {
|
|
117
|
+
args.flags.json = true;
|
|
118
|
+
} else if (a === '--status') {
|
|
119
|
+
args.flags.status = true;
|
|
120
|
+
} else if (a === '--cancel') {
|
|
121
|
+
args.flags.cancel = true;
|
|
122
|
+
} else if (a === '--identify') {
|
|
123
|
+
args.flags.identify = true;
|
|
124
|
+
} else if (a === '--update') {
|
|
125
|
+
args.flags.update = true;
|
|
126
|
+
} else if (a === '--retry') {
|
|
127
|
+
args.flags.retry = true;
|
|
128
|
+
} else if (a === '--reprint') {
|
|
129
|
+
args.flags.reprint = true;
|
|
130
|
+
} else if (a === '--revisions') {
|
|
131
|
+
args.flags.revisions = true;
|
|
132
|
+
} else if (a.startsWith('--') && i + 1 < argv.length) {
|
|
133
|
+
const key = a.slice(2);
|
|
134
|
+
args.flags[key] = argv[++i];
|
|
135
|
+
} else {
|
|
136
|
+
args._.push(a);
|
|
137
|
+
}
|
|
138
|
+
i++;
|
|
139
|
+
}
|
|
140
|
+
return args;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getClient(flags) {
|
|
144
|
+
const apiKey = flags.key || process.env.LABELINN_API_KEY || getStoredKey();
|
|
145
|
+
if (!apiKey) {
|
|
146
|
+
console.error('Error: No API key. Run "labelinn login", set LABELINN_API_KEY, or use --key <key>');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const opts = {};
|
|
150
|
+
if (flags['base-url']) opts.baseUrl = flags['base-url'];
|
|
151
|
+
return new LabelInn(apiKey, opts);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function output(data, flags) {
|
|
155
|
+
if (flags.json) {
|
|
156
|
+
console.log(JSON.stringify(data, null, 2));
|
|
157
|
+
} else {
|
|
158
|
+
// Clean internal keys for display
|
|
159
|
+
const clean = { ...data };
|
|
160
|
+
delete clean._rateLimit;
|
|
161
|
+
delete clean._idempotent;
|
|
162
|
+
console.log(JSON.stringify(clean, null, 2));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getCredentialsPath() {
|
|
167
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
168
|
+
const path = require('path');
|
|
169
|
+
return path.join(home, '.labelinn', 'credentials.json');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function getStoredKey() {
|
|
173
|
+
const fs = require('fs');
|
|
174
|
+
const credPath = getCredentialsPath();
|
|
175
|
+
try {
|
|
176
|
+
const config = JSON.parse(fs.readFileSync(credPath, 'utf8'));
|
|
177
|
+
return config.api_key || '';
|
|
178
|
+
} catch {
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function cmdLogin(args, flags) {
|
|
184
|
+
const fs = require('fs');
|
|
185
|
+
const path = require('path');
|
|
186
|
+
const readline = require('readline');
|
|
187
|
+
const credPath = getCredentialsPath();
|
|
188
|
+
const credDir = path.dirname(credPath);
|
|
189
|
+
|
|
190
|
+
const subCmd = args._[1];
|
|
191
|
+
|
|
192
|
+
if (subCmd === 'status') {
|
|
193
|
+
try {
|
|
194
|
+
const config = JSON.parse(fs.readFileSync(credPath, 'utf8'));
|
|
195
|
+
const key = config.api_key || '';
|
|
196
|
+
const prefix = key.length > 15 ? key.substring(0, 15) + '...' : key;
|
|
197
|
+
const mode = key.startsWith('sk_test_') ? 'TEST' : 'LIVE';
|
|
198
|
+
console.log(`Logged in: ${prefix}`);
|
|
199
|
+
console.log(` Mode: ${mode}`);
|
|
200
|
+
console.log(` Config: ${credPath}`);
|
|
201
|
+
} catch {
|
|
202
|
+
console.log('Not logged in. Run: labelinn login');
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (subCmd === 'logout') {
|
|
208
|
+
try {
|
|
209
|
+
fs.unlinkSync(credPath);
|
|
210
|
+
console.log('Logged out. Credentials removed.');
|
|
211
|
+
} catch {
|
|
212
|
+
console.log('Not logged in.');
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let apiKey = flags.key;
|
|
218
|
+
if (!apiKey) {
|
|
219
|
+
console.log('Enter your LabelInn API key.');
|
|
220
|
+
console.log(' Get one at: https://labelinn.com → Settings → API Keys');
|
|
221
|
+
console.log();
|
|
222
|
+
|
|
223
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
224
|
+
apiKey = await new Promise((resolve) => {
|
|
225
|
+
rl.question('API Key: ', (answer) => {
|
|
226
|
+
rl.close();
|
|
227
|
+
resolve(answer.trim());
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!apiKey) {
|
|
233
|
+
console.error('Error: No API key provided.');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
if (!apiKey.startsWith('sk_live_') && !apiKey.startsWith('sk_test_')) {
|
|
237
|
+
console.error('Error: API key must start with sk_live_ or sk_test_');
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Test the key
|
|
242
|
+
console.log('Verifying key...');
|
|
243
|
+
const client = new LabelInn(apiKey);
|
|
244
|
+
const result = await client.fleet.list();
|
|
245
|
+
const printers = result.printers || result;
|
|
246
|
+
const count = Array.isArray(printers) ? printers.length : 0;
|
|
247
|
+
|
|
248
|
+
// Save credentials
|
|
249
|
+
fs.mkdirSync(credDir, { recursive: true });
|
|
250
|
+
fs.writeFileSync(credPath, JSON.stringify({ api_key: apiKey }, null, 2), 'utf8');
|
|
251
|
+
|
|
252
|
+
const mode = apiKey.startsWith('sk_test_') ? 'TEST (sandbox)' : 'LIVE';
|
|
253
|
+
console.log(`✓ Logged in! Mode: ${mode}, ${count} printer(s) found.`);
|
|
254
|
+
console.log(` Credentials saved to ${credPath}`);
|
|
255
|
+
console.log(' You can now run commands without --key flag.');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function table(rows, columns) {
|
|
259
|
+
if (!rows || rows.length === 0) {
|
|
260
|
+
console.log('(none)');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// Simple table output
|
|
264
|
+
const widths = {};
|
|
265
|
+
for (const col of columns) {
|
|
266
|
+
widths[col] = col.length;
|
|
267
|
+
for (const row of rows) {
|
|
268
|
+
const val = String(row[col] ?? '');
|
|
269
|
+
widths[col] = Math.max(widths[col], val.length);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const header = columns.map(c => c.toUpperCase().padEnd(widths[c])).join(' ');
|
|
273
|
+
console.log(header);
|
|
274
|
+
console.log(columns.map(c => '─'.repeat(widths[c])).join(' '));
|
|
275
|
+
for (const row of rows) {
|
|
276
|
+
console.log(columns.map(c => String(row[c] ?? '').padEnd(widths[c])).join(' '));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ── Commands ──
|
|
281
|
+
|
|
282
|
+
async function cmdTest(client, flags) {
|
|
283
|
+
console.log(`Mode: ${client.isTestMode ? 'TEST (sandbox)' : 'LIVE'}`);
|
|
284
|
+
console.log('Checking connectivity...');
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const result = await client.fleet.list();
|
|
288
|
+
const printers = result.printers || result;
|
|
289
|
+
const count = Array.isArray(printers) ? printers.length : 0;
|
|
290
|
+
console.log(`✓ Connected! Found ${count} printer(s).`);
|
|
291
|
+
if (result._rateLimit) {
|
|
292
|
+
console.log(` Rate limit: ${result._rateLimit.remaining}/${result._rateLimit.limit} remaining`);
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
console.error(`✗ Failed: ${err.message}`);
|
|
296
|
+
if (err.code) console.error(` Code: ${err.code}`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function cmdPrinters(client, args, flags) {
|
|
302
|
+
const printerId = args._[1];
|
|
303
|
+
if (printerId) {
|
|
304
|
+
if (flags.status) {
|
|
305
|
+
const result = await client.fleet.status(printerId);
|
|
306
|
+
output(result, flags);
|
|
307
|
+
} else if (flags.identify) {
|
|
308
|
+
await client.fleet.identify(printerId);
|
|
309
|
+
console.log(`✓ Identify command sent to printer ${printerId}`);
|
|
310
|
+
} else if (flags.media) {
|
|
311
|
+
let mediaParams;
|
|
312
|
+
try {
|
|
313
|
+
mediaParams = JSON.parse(flags.media);
|
|
314
|
+
} catch {
|
|
315
|
+
console.error('Error: --media must be valid JSON, e.g. \'{"width_mm":100,"height_mm":50}\'');
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
const result = await client.fleet.setMedia(printerId, mediaParams);
|
|
319
|
+
if (flags.json) {
|
|
320
|
+
output(result, flags);
|
|
321
|
+
} else {
|
|
322
|
+
console.log(`✓ Media definition updated on ${printerId}`);
|
|
323
|
+
}
|
|
324
|
+
} else if (flags.update) {
|
|
325
|
+
const params = {};
|
|
326
|
+
if (flags.name) params.name = flags.name;
|
|
327
|
+
const result = await client.fleet.update(printerId, params);
|
|
328
|
+
if (flags.json) {
|
|
329
|
+
output(result, flags);
|
|
330
|
+
} else {
|
|
331
|
+
console.log(`✓ Printer ${printerId} updated`);
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
const result = await client.fleet.get(printerId);
|
|
335
|
+
output(result, flags);
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
const result = await client.fleet.list();
|
|
339
|
+
const printers = result.printers || result;
|
|
340
|
+
if (flags.json) {
|
|
341
|
+
output(result, flags);
|
|
342
|
+
} else {
|
|
343
|
+
table(Array.isArray(printers) ? printers : [], ['id', 'name', 'status', 'model', 'connection']);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function cmdGroups(client, args, flags) {
|
|
349
|
+
const subCmd = args._[1];
|
|
350
|
+
if (subCmd === 'add') {
|
|
351
|
+
const params = {};
|
|
352
|
+
if (flags.name) params.name = flags.name;
|
|
353
|
+
if (flags.printers) params.printer_ids = flags.printers.split(',');
|
|
354
|
+
if (flags.description) params.description = flags.description;
|
|
355
|
+
const result = await client.fleet.groups.create(params);
|
|
356
|
+
if (flags.json) {
|
|
357
|
+
output(result, flags);
|
|
358
|
+
} else {
|
|
359
|
+
console.log(`✓ Group created: ${result.id}`);
|
|
360
|
+
}
|
|
361
|
+
} else if (subCmd === 'rm') {
|
|
362
|
+
const id = args._[2];
|
|
363
|
+
if (!id) { console.error('Usage: labelinn groups rm <id>'); process.exit(1); }
|
|
364
|
+
await client.fleet.groups.delete(id);
|
|
365
|
+
console.log(`✓ Group ${id} deleted`);
|
|
366
|
+
} else if (subCmd) {
|
|
367
|
+
const result = await client.fleet.groups.get(subCmd);
|
|
368
|
+
output(result, flags);
|
|
369
|
+
} else {
|
|
370
|
+
const result = await client.fleet.groups.list();
|
|
371
|
+
const groups = result.groups || result;
|
|
372
|
+
if (flags.json) {
|
|
373
|
+
output(result, flags);
|
|
374
|
+
} else {
|
|
375
|
+
table(Array.isArray(groups) ? groups : [], ['id', 'name', 'printer_count']);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function cmdPrint(client, args, flags) {
|
|
381
|
+
const printerId = args._[1];
|
|
382
|
+
if (!printerId) {
|
|
383
|
+
console.error('Usage: labelinn print <printer_id> [--zpl|--image-url|--design] ...');
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const params = { printer_id: printerId };
|
|
388
|
+
|
|
389
|
+
if (flags.zpl) {
|
|
390
|
+
params.payload_type = 'zpl';
|
|
391
|
+
params.payload_data = flags.zpl;
|
|
392
|
+
} else if (flags['image-url']) {
|
|
393
|
+
params.payload_type = 'image';
|
|
394
|
+
params.image_url = flags['image-url'];
|
|
395
|
+
} else if (flags.design) {
|
|
396
|
+
params.payload_type = 'template';
|
|
397
|
+
params.design_id = flags.design;
|
|
398
|
+
if (flags.data) {
|
|
399
|
+
try {
|
|
400
|
+
params.data = JSON.parse(flags.data);
|
|
401
|
+
} catch {
|
|
402
|
+
console.error('Error: --data must be valid JSON');
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
console.error('Error: Specify --zpl, --image-url, or --design');
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (flags.copies) params.copies = parseInt(flags.copies, 10);
|
|
412
|
+
if (flags.name) params.job_name = flags.name;
|
|
413
|
+
if (flags.priority) params.priority = flags.priority;
|
|
414
|
+
if (flags.schedule) params.print_at = flags.schedule;
|
|
415
|
+
|
|
416
|
+
const opts = {};
|
|
417
|
+
if (flags['idempotency-key']) opts.idempotencyKey = flags['idempotency-key'];
|
|
418
|
+
|
|
419
|
+
const job = await client.print.create(params, opts);
|
|
420
|
+
if (flags.json) {
|
|
421
|
+
output(job, flags);
|
|
422
|
+
} else {
|
|
423
|
+
console.log(`✓ Print job submitted`);
|
|
424
|
+
console.log(` Job ID: ${job.id}`);
|
|
425
|
+
console.log(` Status: ${job.status}`);
|
|
426
|
+
if (job._idempotent) console.log(' (Idempotent — duplicate request)');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function cmdJobs(client, args, flags) {
|
|
431
|
+
const jobId = args._[1];
|
|
432
|
+
if (jobId) {
|
|
433
|
+
if (flags.cancel) {
|
|
434
|
+
await client.print.cancel(jobId);
|
|
435
|
+
console.log(`✓ Job ${jobId} cancelled`);
|
|
436
|
+
} else if (flags.retry) {
|
|
437
|
+
const result = await client.print.retry(jobId);
|
|
438
|
+
if (flags.json) {
|
|
439
|
+
output(result, flags);
|
|
440
|
+
} else {
|
|
441
|
+
console.log(`✓ Retry job created: ${result.id}`);
|
|
442
|
+
}
|
|
443
|
+
} else if (flags.reprint) {
|
|
444
|
+
const overrides = {};
|
|
445
|
+
if (flags.copies) overrides.copies = parseInt(flags.copies, 10);
|
|
446
|
+
const result = await client.print.reprint(jobId, overrides);
|
|
447
|
+
if (flags.json) {
|
|
448
|
+
output(result, flags);
|
|
449
|
+
} else {
|
|
450
|
+
console.log(`✓ Reprint job created: ${result.id}`);
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
const result = await client.print.get(jobId);
|
|
454
|
+
output(result, flags);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
const query = {};
|
|
458
|
+
if (flags.status) query.status = flags.status;
|
|
459
|
+
if (flags.printer) query.printer_id = flags.printer;
|
|
460
|
+
if (flags.limit) query.limit = parseInt(flags.limit, 10);
|
|
461
|
+
|
|
462
|
+
const result = await client.print.list(query);
|
|
463
|
+
const jobs = result.jobs || result;
|
|
464
|
+
if (flags.json) {
|
|
465
|
+
output(result, flags);
|
|
466
|
+
} else {
|
|
467
|
+
table(Array.isArray(jobs) ? jobs : [], ['id', 'status', 'printer_id', 'payload_type', 'created_at']);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function cmdDesigns(client, args, flags) {
|
|
473
|
+
const designId = args._[1];
|
|
474
|
+
if (designId) {
|
|
475
|
+
if (flags.revisions) {
|
|
476
|
+
const result = await client.designs.revisions(designId);
|
|
477
|
+
output(result, flags);
|
|
478
|
+
} else {
|
|
479
|
+
const result = await client.designs.get(designId);
|
|
480
|
+
output(result, flags);
|
|
481
|
+
}
|
|
482
|
+
} else {
|
|
483
|
+
const result = await client.designs.list();
|
|
484
|
+
const designs = result.designs || result;
|
|
485
|
+
if (flags.json) {
|
|
486
|
+
output(result, flags);
|
|
487
|
+
} else {
|
|
488
|
+
table(Array.isArray(designs) ? designs : [], ['id', 'name', 'width_mm', 'height_mm']);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async function cmdBatch(client, args, flags) {
|
|
494
|
+
const filePath = args._[1];
|
|
495
|
+
if (!filePath) {
|
|
496
|
+
console.error('Usage: labelinn batch <file.json>');
|
|
497
|
+
console.error(' File should contain a JSON array of job objects, or { "jobs": [...] }');
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const fs = require('fs');
|
|
502
|
+
let raw;
|
|
503
|
+
try {
|
|
504
|
+
raw = fs.readFileSync(filePath, 'utf8');
|
|
505
|
+
} catch (err) {
|
|
506
|
+
console.error(`Error: Cannot read file "${filePath}": ${err.message}`);
|
|
507
|
+
process.exit(1);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
let jobs;
|
|
511
|
+
try {
|
|
512
|
+
const parsed = JSON.parse(raw);
|
|
513
|
+
jobs = Array.isArray(parsed) ? parsed : (parsed.jobs || parsed);
|
|
514
|
+
if (!Array.isArray(jobs)) throw new Error('Not an array');
|
|
515
|
+
} catch (err) {
|
|
516
|
+
console.error(`Error: File must contain a JSON array of jobs, or { "jobs": [...] }`);
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (jobs.length === 0) {
|
|
521
|
+
console.error('Error: Jobs array is empty');
|
|
522
|
+
process.exit(1);
|
|
523
|
+
}
|
|
524
|
+
if (jobs.length > 100) {
|
|
525
|
+
console.error(`Error: Max 100 jobs per batch (got ${jobs.length})`);
|
|
526
|
+
process.exit(1);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const result = await client.print.batch(jobs);
|
|
530
|
+
if (flags.json) {
|
|
531
|
+
output(result, flags);
|
|
532
|
+
} else {
|
|
533
|
+
console.log(`✓ Batch submitted: ${result.total_jobs || jobs.length} jobs`);
|
|
534
|
+
console.log(` Successful: ${result.successful ?? '?'}`);
|
|
535
|
+
console.log(` Failed: ${result.failed ?? '?'}`);
|
|
536
|
+
if (result.batch_id) console.log(` Batch ID: ${result.batch_id}`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async function cmdConnect(client, args, flags) {
|
|
541
|
+
const subCmd = args._[1];
|
|
542
|
+
|
|
543
|
+
if (subCmd === 'sources') {
|
|
544
|
+
const sourceArg = args._[2];
|
|
545
|
+
if (sourceArg === 'create') {
|
|
546
|
+
if (!flags.name) { console.error('Usage: labelinn connect sources create --name <n> [--format csv|json|xml]'); process.exit(1); }
|
|
547
|
+
const params = { name: flags.name };
|
|
548
|
+
if (flags.format) params.format = flags.format;
|
|
549
|
+
if (flags.description) params.description = flags.description;
|
|
550
|
+
const result = await client.connect.createSource(params);
|
|
551
|
+
if (flags.json) { output(result, flags); }
|
|
552
|
+
else {
|
|
553
|
+
console.log(`✓ Source created`);
|
|
554
|
+
console.log(` ID: ${result.id}`);
|
|
555
|
+
console.log(` Name: ${result.name}`);
|
|
556
|
+
console.log(` Format: ${result.format || 'auto'}`);
|
|
557
|
+
}
|
|
558
|
+
} else if (sourceArg === 'rm') {
|
|
559
|
+
const id = args._[3];
|
|
560
|
+
if (!id) { console.error('Usage: labelinn connect sources rm <id>'); process.exit(1); }
|
|
561
|
+
await client.connect.deleteSource(id);
|
|
562
|
+
console.log(`✓ Source ${id} deleted`);
|
|
563
|
+
} else if (sourceArg) {
|
|
564
|
+
// Get specific source
|
|
565
|
+
const result = await client.connect.getSource(sourceArg);
|
|
566
|
+
if (flags.json) { output(result, flags); }
|
|
567
|
+
else {
|
|
568
|
+
console.log(`Source: ${result.name || result.id}`);
|
|
569
|
+
console.log(` ID: ${result.id}`);
|
|
570
|
+
console.log(` Format: ${result.format || '?'}`);
|
|
571
|
+
console.log(` Records: ${result.record_count ?? '?'}`);
|
|
572
|
+
console.log(` Active: ${result.is_active}`);
|
|
573
|
+
if (result.created_at) console.log(` Created: ${result.created_at}`);
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
// List sources
|
|
577
|
+
const result = await client.connect.listSources();
|
|
578
|
+
const sources = Array.isArray(result) ? result : (result.sources || []);
|
|
579
|
+
if (flags.json) { output(result, flags); }
|
|
580
|
+
else { table(sources, ['id', 'name', 'format', 'record_count', 'is_active']); }
|
|
581
|
+
}
|
|
582
|
+
} else if (subCmd === 'ingest') {
|
|
583
|
+
const sourceId = args._[2];
|
|
584
|
+
if (!sourceId) { console.error('Usage: labelinn connect ingest <source_id> --file <path> | --data <json>'); process.exit(1); }
|
|
585
|
+
let payload;
|
|
586
|
+
if (flags.file) {
|
|
587
|
+
const fs = require('fs');
|
|
588
|
+
payload = fs.readFileSync(flags.file, 'utf-8');
|
|
589
|
+
} else if (flags.data) {
|
|
590
|
+
payload = flags.data;
|
|
591
|
+
} else {
|
|
592
|
+
console.error('Provide --file <path> or --data <json>'); process.exit(1);
|
|
593
|
+
}
|
|
594
|
+
const params = { source_id: sourceId, payload };
|
|
595
|
+
if (flags.format) params.format = flags.format;
|
|
596
|
+
const result = await client.connect.ingest(params);
|
|
597
|
+
if (flags.json) { output(result, flags); }
|
|
598
|
+
else {
|
|
599
|
+
console.log(`✓ Ingested ${result.records_count ?? '?'} records into ${result.source_id}`);
|
|
600
|
+
console.log(` Format: ${result.format || '?'}`);
|
|
601
|
+
}
|
|
602
|
+
} else if (subCmd === 'test-parse') {
|
|
603
|
+
let payload;
|
|
604
|
+
if (flags.file) {
|
|
605
|
+
const fs = require('fs');
|
|
606
|
+
payload = fs.readFileSync(flags.file, 'utf-8');
|
|
607
|
+
} else if (flags.data) {
|
|
608
|
+
payload = flags.data;
|
|
609
|
+
} else {
|
|
610
|
+
console.error('Provide --file <path> or --data <json>'); process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
const params = { payload };
|
|
613
|
+
if (flags.format) params.format = flags.format;
|
|
614
|
+
const result = await client.connect.testParse(params);
|
|
615
|
+
if (flags.json) { output(result, flags); }
|
|
616
|
+
else {
|
|
617
|
+
console.log(`Format: ${result.format}`);
|
|
618
|
+
console.log(`Records: ${(result.records || []).length}`);
|
|
619
|
+
if (result.schema) {
|
|
620
|
+
console.log('Schema:');
|
|
621
|
+
table(result.schema, ['path', 'type', 'sample']);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
} else if (subCmd === 'schema') {
|
|
625
|
+
const sourceId = args._[2];
|
|
626
|
+
if (!sourceId) { console.error('Usage: labelinn connect schema <source_id>'); process.exit(1); }
|
|
627
|
+
const result = await client.connect.getSchema(sourceId);
|
|
628
|
+
if (flags.json) { output(result, flags); }
|
|
629
|
+
else {
|
|
630
|
+
const fields = result.fields || result.schema || [];
|
|
631
|
+
table(fields, ['path', 'type', 'sample']);
|
|
632
|
+
}
|
|
633
|
+
} else if (subCmd === 'records') {
|
|
634
|
+
const sourceId = args._[2];
|
|
635
|
+
if (!sourceId) { console.error('Usage: labelinn connect records <source_id>'); process.exit(1); }
|
|
636
|
+
const result = await client.connect.listRecords(sourceId, flags.limit ? parseInt(flags.limit) : undefined);
|
|
637
|
+
output(result, flags);
|
|
638
|
+
} else if (subCmd === 'mappings') {
|
|
639
|
+
const sourceId = args._[2];
|
|
640
|
+
if (!sourceId) { console.error('Usage: labelinn connect mappings <source_id>'); process.exit(1); }
|
|
641
|
+
const result = await client.connect.getMappings(sourceId);
|
|
642
|
+
output(result, flags);
|
|
643
|
+
} else if (subCmd === 'print') {
|
|
644
|
+
const sourceId = args._[2];
|
|
645
|
+
if (!sourceId || !flags.design || !flags.printer) {
|
|
646
|
+
console.error('Usage: labelinn connect print <source_id> --design <id> --printer <id> [--copies n]');
|
|
647
|
+
process.exit(1);
|
|
648
|
+
}
|
|
649
|
+
const params = { source_id: sourceId, design_id: flags.design, printer_id: flags.printer };
|
|
650
|
+
if (flags.copies) params.copies = parseInt(flags.copies);
|
|
651
|
+
const result = await client.connect.print(params);
|
|
652
|
+
if (flags.json) { output(result, flags); }
|
|
653
|
+
else {
|
|
654
|
+
console.log(`✓ Print jobs created: ${result.count ?? (result.job_ids || []).length}`);
|
|
655
|
+
if (result.job_ids) console.log(` Job IDs: ${result.job_ids.join(', ')}`);
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
console.log('Data Connect commands:');
|
|
659
|
+
console.log(' connect sources List sources');
|
|
660
|
+
console.log(' connect sources <id> Get source details');
|
|
661
|
+
console.log(' connect sources create --name <n> Create a source');
|
|
662
|
+
console.log(' connect sources rm <id> Delete a source');
|
|
663
|
+
console.log(' connect ingest <id> --file <path> Ingest data');
|
|
664
|
+
console.log(' connect test-parse --file <path> Parse without storing');
|
|
665
|
+
console.log(' connect schema <id> Get schema');
|
|
666
|
+
console.log(' connect records <id> List records');
|
|
667
|
+
console.log(' connect mappings <id> Get mappings');
|
|
668
|
+
console.log(' connect print <id> --design <d> --printer <p> Print');
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
async function cmdWebhooks(client, args, flags) {
|
|
673
|
+
const subCmd = args._[1];
|
|
674
|
+
if (subCmd === 'add') {
|
|
675
|
+
const url = args._[2];
|
|
676
|
+
if (!url) { console.error('Usage: labelinn webhooks add <url> [events...]'); process.exit(1); }
|
|
677
|
+
const events = args._.slice(3);
|
|
678
|
+
if (events.length === 0) events.push('print_job.completed', 'print_job.failed');
|
|
679
|
+
const result = await client.webhooks.create({ url, events });
|
|
680
|
+
if (flags.json) {
|
|
681
|
+
output(result, flags);
|
|
682
|
+
} else {
|
|
683
|
+
console.log(`✓ Webhook created`);
|
|
684
|
+
console.log(` ID: ${result.id}`);
|
|
685
|
+
console.log(` URL: ${result.url}`);
|
|
686
|
+
console.log(` Events: ${(result.events || events).join(', ')}`);
|
|
687
|
+
if (result.signing_secret) {
|
|
688
|
+
console.log(` Secret: ${result.signing_secret}`);
|
|
689
|
+
console.log(' (Save this — it won\'t be shown again)');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
} else if (subCmd === 'rm') {
|
|
693
|
+
const id = args._[2];
|
|
694
|
+
if (!id) { console.error('Usage: labelinn webhooks rm <id>'); process.exit(1); }
|
|
695
|
+
await client.webhooks.delete(id);
|
|
696
|
+
console.log(`✓ Webhook ${id} deleted`);
|
|
697
|
+
} else if (subCmd === 'test') {
|
|
698
|
+
const id = args._[2];
|
|
699
|
+
if (!id) { console.error('Usage: labelinn webhooks test <id>'); process.exit(1); }
|
|
700
|
+
const result = await client.webhooks.test(id);
|
|
701
|
+
if (flags.json) {
|
|
702
|
+
output(result, flags);
|
|
703
|
+
} else {
|
|
704
|
+
console.log(`✓ Test ping sent to webhook ${id}`);
|
|
705
|
+
}
|
|
706
|
+
} else if (subCmd === 'update') {
|
|
707
|
+
const id = args._[2];
|
|
708
|
+
if (!id) { console.error('Usage: labelinn webhooks update <id> [--events e1,e2] [--enabled true|false]'); process.exit(1); }
|
|
709
|
+
const params = {};
|
|
710
|
+
if (flags.events) params.events = flags.events.split(',');
|
|
711
|
+
if (flags.enabled !== undefined) params.enabled = flags.enabled === 'true';
|
|
712
|
+
if (flags.url) params.url = flags.url;
|
|
713
|
+
if (flags.description) params.description = flags.description;
|
|
714
|
+
const result = await client.webhooks.update(id, params);
|
|
715
|
+
if (flags.json) {
|
|
716
|
+
output(result, flags);
|
|
717
|
+
} else {
|
|
718
|
+
console.log(`✓ Webhook ${id} updated`);
|
|
719
|
+
}
|
|
720
|
+
} else if (subCmd === 'deliveries') {
|
|
721
|
+
const id = args._[2];
|
|
722
|
+
if (!id) { console.error('Usage: labelinn webhooks deliveries <id>'); process.exit(1); }
|
|
723
|
+
const result = await client.webhooks.deliveries(id);
|
|
724
|
+
if (flags.json) {
|
|
725
|
+
output(result, flags);
|
|
726
|
+
} else {
|
|
727
|
+
const deliveries = Array.isArray(result) ? result : (result.deliveries || result);
|
|
728
|
+
table(Array.isArray(deliveries) ? deliveries : [], ['id', 'event', 'status', 'http_status', 'timestamp']);
|
|
729
|
+
}
|
|
730
|
+
} else {
|
|
731
|
+
const result = await client.webhooks.list();
|
|
732
|
+
const webhooks = result.webhooks || result;
|
|
733
|
+
if (flags.json) {
|
|
734
|
+
output(result, flags);
|
|
735
|
+
} else {
|
|
736
|
+
table(Array.isArray(webhooks) ? webhooks : [], ['id', 'url', 'events']);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// ── Main ──
|
|
742
|
+
|
|
743
|
+
async function main() {
|
|
744
|
+
const args = parseArgs(process.argv.slice(2));
|
|
745
|
+
|
|
746
|
+
if (args.flags.help || args._.length === 0) {
|
|
747
|
+
console.log(HELP);
|
|
748
|
+
process.exit(0);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const command = args._[0];
|
|
752
|
+
let client;
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
// Login command doesn't need existing auth
|
|
756
|
+
if (command === 'login') {
|
|
757
|
+
await cmdLogin(args, args.flags);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
client = getClient(args.flags);
|
|
762
|
+
} catch (err) {
|
|
763
|
+
console.error(`Error: ${err.message}`);
|
|
764
|
+
process.exit(1);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
try {
|
|
768
|
+
switch (command) {
|
|
769
|
+
case 'test': await cmdTest(client, args.flags); break;
|
|
770
|
+
case 'printers': await cmdPrinters(client, args, args.flags); break;
|
|
771
|
+
case 'groups': await cmdGroups(client, args, args.flags); break;
|
|
772
|
+
case 'print': await cmdPrint(client, args, args.flags); break;
|
|
773
|
+
case 'jobs': await cmdJobs(client, args, args.flags); break;
|
|
774
|
+
case 'batch': await cmdBatch(client, args, args.flags); break;
|
|
775
|
+
case 'designs': await cmdDesigns(client, args, args.flags); break;
|
|
776
|
+
case 'webhooks': await cmdWebhooks(client, args, args.flags); break;
|
|
777
|
+
case 'connect': await cmdConnect(client, args, args.flags); break;
|
|
778
|
+
default:
|
|
779
|
+
console.error(`Unknown command: ${command}`);
|
|
780
|
+
console.error('Run "labelinn --help" for usage');
|
|
781
|
+
process.exit(1);
|
|
782
|
+
}
|
|
783
|
+
} catch (err) {
|
|
784
|
+
if (err instanceof LabelInn.LabelInnError) {
|
|
785
|
+
console.error(`API Error [${err.status}] ${err.code}: ${err.message}`);
|
|
786
|
+
} else {
|
|
787
|
+
console.error(`Error: ${err.message}`);
|
|
788
|
+
}
|
|
789
|
+
process.exit(1);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
main();
|