@01.software/cli 0.2.0 → 0.3.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/index.js +543 -145
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -5,11 +5,24 @@ import { createRequire } from "module";
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/lib/client.ts
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
CollectionClient,
|
|
10
|
+
OrderApi,
|
|
11
|
+
CartApi,
|
|
12
|
+
ProductApi,
|
|
13
|
+
parseApiKey
|
|
14
|
+
} from "@01.software/sdk";
|
|
9
15
|
import pc from "picocolors";
|
|
10
16
|
|
|
11
17
|
// src/lib/credentials.ts
|
|
12
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
existsSync,
|
|
20
|
+
mkdirSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
writeFileSync,
|
|
23
|
+
unlinkSync,
|
|
24
|
+
appendFileSync
|
|
25
|
+
} from "fs";
|
|
13
26
|
import { join } from "path";
|
|
14
27
|
import { homedir } from "os";
|
|
15
28
|
var DIR_NAME = ".01software";
|
|
@@ -27,8 +40,14 @@ function saveCredentials(creds) {
|
|
|
27
40
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
28
41
|
}
|
|
29
42
|
const filePath = getCredentialsPath();
|
|
30
|
-
const data = {
|
|
31
|
-
|
|
43
|
+
const data = {
|
|
44
|
+
...creds,
|
|
45
|
+
storedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
46
|
+
};
|
|
47
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2), {
|
|
48
|
+
encoding: "utf-8",
|
|
49
|
+
mode: 384
|
|
50
|
+
});
|
|
32
51
|
}
|
|
33
52
|
function deleteCredentials() {
|
|
34
53
|
const filePath = getCredentialsPath();
|
|
@@ -48,8 +67,14 @@ function saveLocalCredentials(creds) {
|
|
|
48
67
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
49
68
|
}
|
|
50
69
|
const filePath = getLocalCredentialsPath();
|
|
51
|
-
const data = {
|
|
52
|
-
|
|
70
|
+
const data = {
|
|
71
|
+
...creds,
|
|
72
|
+
storedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
73
|
+
};
|
|
74
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2), {
|
|
75
|
+
encoding: "utf-8",
|
|
76
|
+
mode: 384
|
|
77
|
+
});
|
|
53
78
|
ensureGitignore();
|
|
54
79
|
}
|
|
55
80
|
function saveTenantList(tenants) {
|
|
@@ -58,7 +83,10 @@ function saveTenantList(tenants) {
|
|
|
58
83
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
59
84
|
}
|
|
60
85
|
const filePath = join(homedir(), DIR_NAME, TENANTS_FILE);
|
|
61
|
-
writeFileSync(filePath, JSON.stringify(tenants, null, 2), {
|
|
86
|
+
writeFileSync(filePath, JSON.stringify(tenants, null, 2), {
|
|
87
|
+
encoding: "utf-8",
|
|
88
|
+
mode: 384
|
|
89
|
+
});
|
|
62
90
|
}
|
|
63
91
|
function loadTenantList() {
|
|
64
92
|
const filePath = join(homedir(), DIR_NAME, TENANTS_FILE);
|
|
@@ -111,8 +139,12 @@ function resolveClient(apiKeyFlag) {
|
|
|
111
139
|
clientKey = parsed.clientKey;
|
|
112
140
|
secretKey = parsed.secretKey;
|
|
113
141
|
} catch {
|
|
114
|
-
console.error(
|
|
115
|
-
|
|
142
|
+
console.error(
|
|
143
|
+
pc.red(
|
|
144
|
+
'Invalid --api-key value. Expected Base64-encoded "clientKey:secretKey".'
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
process.exit(2);
|
|
116
148
|
}
|
|
117
149
|
} else {
|
|
118
150
|
clientKey = process.env.SOFTWARE_CLIENT_KEY;
|
|
@@ -139,14 +171,16 @@ function resolveClient(apiKeyFlag) {
|
|
|
139
171
|
'Run "01 login" to authenticate via browser,\nor set SOFTWARE_CLIENT_KEY and SOFTWARE_SECRET_KEY environment variables,\nor use the --api-key <base64> flag.'
|
|
140
172
|
)
|
|
141
173
|
);
|
|
142
|
-
process.exit(
|
|
174
|
+
process.exit(2);
|
|
143
175
|
}
|
|
144
176
|
const apiOpts = { clientKey, secretKey };
|
|
145
177
|
return {
|
|
146
178
|
collections: new CollectionClient(clientKey, secretKey),
|
|
147
179
|
api: new OrderApi(apiOpts),
|
|
148
180
|
cart: new CartApi(apiOpts),
|
|
149
|
-
product: new ProductApi(apiOpts)
|
|
181
|
+
product: new ProductApi(apiOpts),
|
|
182
|
+
clientKey,
|
|
183
|
+
secretKey
|
|
150
184
|
};
|
|
151
185
|
}
|
|
152
186
|
|
|
@@ -163,13 +197,22 @@ function printTable(data) {
|
|
|
163
197
|
}
|
|
164
198
|
const keys = Object.keys(data[0]);
|
|
165
199
|
const widths = keys.map(
|
|
166
|
-
(k) => Math.max(
|
|
200
|
+
(k) => Math.max(
|
|
201
|
+
k.length,
|
|
202
|
+
...data.map(
|
|
203
|
+
(row) => String(row[k] ?? "").length
|
|
204
|
+
)
|
|
205
|
+
)
|
|
167
206
|
);
|
|
168
207
|
console.log(keys.map((k, i) => k.padEnd(widths[i])).join(" "));
|
|
169
208
|
console.log(keys.map((_, i) => "-".repeat(widths[i])).join(" "));
|
|
170
209
|
for (const row of data) {
|
|
171
210
|
console.log(
|
|
172
|
-
keys.map(
|
|
211
|
+
keys.map(
|
|
212
|
+
(k, i) => String(row[k] ?? "").padEnd(
|
|
213
|
+
widths[i]
|
|
214
|
+
)
|
|
215
|
+
).join(" ")
|
|
173
216
|
);
|
|
174
217
|
}
|
|
175
218
|
} else if (data && typeof data === "object") {
|
|
@@ -183,13 +226,31 @@ function printTable(data) {
|
|
|
183
226
|
console.log(String(data));
|
|
184
227
|
}
|
|
185
228
|
}
|
|
229
|
+
function printNdjson(data) {
|
|
230
|
+
if (Array.isArray(data)) {
|
|
231
|
+
data.forEach((item) => console.log(JSON.stringify(item)));
|
|
232
|
+
} else if (data && typeof data === "object" && "docs" in data) {
|
|
233
|
+
const { docs, ...meta } = data;
|
|
234
|
+
docs.forEach((doc) => console.log(JSON.stringify(doc)));
|
|
235
|
+
if (Object.keys(meta).length > 0)
|
|
236
|
+
console.log(JSON.stringify({ _meta: meta }));
|
|
237
|
+
} else {
|
|
238
|
+
console.log(JSON.stringify(data));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
186
241
|
function printResult(data, format) {
|
|
187
|
-
if (format === "
|
|
242
|
+
if (format === "ndjson") {
|
|
243
|
+
printNdjson(data);
|
|
244
|
+
} else if (format === "table") {
|
|
188
245
|
if (data && typeof data === "object" && "docs" in data) {
|
|
189
246
|
const resp = data;
|
|
190
247
|
printTable(resp.docs);
|
|
191
|
-
console.log(
|
|
192
|
-
|
|
248
|
+
console.log(
|
|
249
|
+
pc2.dim(
|
|
250
|
+
`
|
|
251
|
+
${resp.totalDocs} total | page ${resp.page}/${resp.totalPages}`
|
|
252
|
+
)
|
|
253
|
+
);
|
|
193
254
|
return;
|
|
194
255
|
}
|
|
195
256
|
printTable(data);
|
|
@@ -197,6 +258,26 @@ ${resp.totalDocs} total | page ${resp.page}/${resp.totalPages}`));
|
|
|
197
258
|
printJson(data);
|
|
198
259
|
}
|
|
199
260
|
}
|
|
261
|
+
function getExitCode(error) {
|
|
262
|
+
if (!error || typeof error !== "object") return 1;
|
|
263
|
+
const err = error;
|
|
264
|
+
if (err.name === "ConfigError") return 2;
|
|
265
|
+
if (err.name === "ValidationError") return 3;
|
|
266
|
+
if (err.name === "NetworkError" || err.name === "TimeoutError") return 4;
|
|
267
|
+
if (err.name === "GoneError") return 5;
|
|
268
|
+
if (err.name === "UsageLimitError") return 6;
|
|
269
|
+
const s = err.status;
|
|
270
|
+
if (s === 401) return 2;
|
|
271
|
+
if (s === 400 || s === 422) return 3;
|
|
272
|
+
if (s === 408 || s === 503) return 4;
|
|
273
|
+
if (s === 404) return 5;
|
|
274
|
+
if (s === 429) return 6;
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
function exitWithError(error) {
|
|
278
|
+
printError(error);
|
|
279
|
+
process.exit(getExitCode(error));
|
|
280
|
+
}
|
|
200
281
|
function printError(error) {
|
|
201
282
|
if (error && typeof error === "object" && "message" in error) {
|
|
202
283
|
const err = error;
|
|
@@ -221,12 +302,12 @@ function parseJson(value, label) {
|
|
|
221
302
|
const parsed = JSON.parse(value);
|
|
222
303
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
223
304
|
console.error(pc3.red(`--${label} must be a JSON object.`));
|
|
224
|
-
process.exit(
|
|
305
|
+
process.exit(3);
|
|
225
306
|
}
|
|
226
307
|
return parsed;
|
|
227
308
|
} catch {
|
|
228
309
|
console.error(pc3.red(`Invalid JSON for --${label}: ${value}`));
|
|
229
|
-
process.exit(
|
|
310
|
+
process.exit(3);
|
|
230
311
|
}
|
|
231
312
|
}
|
|
232
313
|
function parseJsonArray(value, label) {
|
|
@@ -234,12 +315,12 @@ function parseJsonArray(value, label) {
|
|
|
234
315
|
const parsed = JSON.parse(value);
|
|
235
316
|
if (!Array.isArray(parsed)) {
|
|
236
317
|
console.error(pc3.red(`--${label} must be a JSON array.`));
|
|
237
|
-
process.exit(
|
|
318
|
+
process.exit(3);
|
|
238
319
|
}
|
|
239
320
|
return parsed;
|
|
240
321
|
} catch {
|
|
241
322
|
console.error(pc3.red(`Invalid JSON for --${label}: ${value}`));
|
|
242
|
-
process.exit(
|
|
323
|
+
process.exit(3);
|
|
243
324
|
}
|
|
244
325
|
}
|
|
245
326
|
|
|
@@ -248,15 +329,27 @@ function readFileAsBlob(filePath) {
|
|
|
248
329
|
const buffer = readFileSync2(filePath);
|
|
249
330
|
return { blob: new Blob([buffer]), filename: basename(filePath) };
|
|
250
331
|
}
|
|
251
|
-
var collectionList = COLLECTIONS.join(", ");
|
|
252
332
|
function validateCollection(name) {
|
|
253
333
|
if (!COLLECTIONS.includes(name)) {
|
|
254
|
-
|
|
334
|
+
const normalized = name.replace(/-/g, "").toLowerCase();
|
|
335
|
+
const suggestions = COLLECTIONS.filter((c) => {
|
|
336
|
+
const cn = c.replace(/-/g, "").toLowerCase();
|
|
337
|
+
return cn.startsWith(normalized) || normalized.startsWith(cn) || normalized.length >= 3 && cn.includes(normalized);
|
|
338
|
+
}).slice(0, 5);
|
|
339
|
+
const hint = suggestions.length > 0 ? `Did you mean: ${suggestions.join(", ")}?` : 'Run "01 --help" for available collections.';
|
|
340
|
+
throw new Error(`Unknown collection "${name}". ${hint}`);
|
|
255
341
|
}
|
|
256
342
|
return name;
|
|
257
343
|
}
|
|
344
|
+
function parseSelect(input) {
|
|
345
|
+
return Object.fromEntries(input.split(",").map((f) => [f.trim(), true]));
|
|
346
|
+
}
|
|
258
347
|
function registerCrudCommands(program2, getClient2, getFormat2) {
|
|
259
|
-
program2.command("query <collection>").description("Query documents from a collection").option("--where <json>", "Filter conditions (JSON)").option("--limit <n>", "Max results", (v) => parseInt(v, 10)).option("--page <n>", "Page number", (v) => parseInt(v, 10)).option("--sort <field>", "Sort field (prefix with - for descending)").
|
|
348
|
+
program2.command("query <collection>").description("Query documents from a collection").option("--where <json>", "Filter conditions (JSON)").option("--limit <n>", "Max results", (v) => parseInt(v, 10)).option("--page <n>", "Page number", (v) => parseInt(v, 10)).option("--sort <field>", "Sort field (prefix with - for descending)").option(
|
|
349
|
+
"--depth <n>",
|
|
350
|
+
"Relationship depth (0 = no population)",
|
|
351
|
+
(v) => parseInt(v, 10)
|
|
352
|
+
).option("--select <fields>", "Comma-separated fields to select").action(async (collection, opts) => {
|
|
260
353
|
try {
|
|
261
354
|
const col = validateCollection(collection);
|
|
262
355
|
const client = getClient2();
|
|
@@ -265,29 +358,49 @@ function registerCrudCommands(program2, getClient2, getFormat2) {
|
|
|
265
358
|
if (opts.limit) options.limit = opts.limit;
|
|
266
359
|
if (opts.page) options.page = opts.page;
|
|
267
360
|
if (opts.sort) options.sort = opts.sort;
|
|
361
|
+
if (opts.depth != null) options.depth = opts.depth;
|
|
362
|
+
if (opts.select) options.select = parseSelect(opts.select);
|
|
268
363
|
const result = await client.collections.from(col).find(options);
|
|
269
364
|
printResult(result, getFormat2());
|
|
270
365
|
} catch (e) {
|
|
271
|
-
|
|
272
|
-
process.exit(1);
|
|
366
|
+
exitWithError(e);
|
|
273
367
|
}
|
|
274
368
|
});
|
|
275
|
-
program2.command("get <collection> <id>").description("Get a document by ID").
|
|
369
|
+
program2.command("get <collection> <id>").description("Get a document by ID").option(
|
|
370
|
+
"--depth <n>",
|
|
371
|
+
"Relationship depth (0 = no population)",
|
|
372
|
+
(v) => parseInt(v, 10)
|
|
373
|
+
).option("--select <fields>", "Comma-separated fields to select").action(async (collection, id, opts) => {
|
|
276
374
|
try {
|
|
277
375
|
const col = validateCollection(collection);
|
|
278
376
|
const client = getClient2();
|
|
279
|
-
const
|
|
377
|
+
const options = {};
|
|
378
|
+
if (opts.depth != null) options.depth = opts.depth;
|
|
379
|
+
if (opts.select) options.select = parseSelect(opts.select);
|
|
380
|
+
const result = await client.collections.from(col).findById(id, options);
|
|
280
381
|
printResult(result, getFormat2());
|
|
281
382
|
} catch (e) {
|
|
282
|
-
|
|
283
|
-
process.exit(1);
|
|
383
|
+
exitWithError(e);
|
|
284
384
|
}
|
|
285
385
|
});
|
|
286
|
-
program2.command("create <collection>").description("Create a new document").requiredOption("--data <json>", "Document data (JSON)").option("--file <path>", "File to upload (for upload collections)").action(async (collection, opts) => {
|
|
386
|
+
program2.command("create <collection>").description("Create a new document").requiredOption("--data <json>", "Document data (JSON)").option("--file <path>", "File to upload (for upload collections)").option("--dry-run", "Validate inputs without executing").action(async (collection, opts) => {
|
|
287
387
|
try {
|
|
288
388
|
const col = validateCollection(collection);
|
|
289
|
-
const client = getClient2();
|
|
290
389
|
const data = parseJson(opts.data, "data");
|
|
390
|
+
if (opts.dryRun) {
|
|
391
|
+
printResult(
|
|
392
|
+
{
|
|
393
|
+
dryRun: true,
|
|
394
|
+
valid: true,
|
|
395
|
+
action: "create",
|
|
396
|
+
collection: col,
|
|
397
|
+
data
|
|
398
|
+
},
|
|
399
|
+
getFormat2()
|
|
400
|
+
);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const client = getClient2();
|
|
291
404
|
let fileOpts;
|
|
292
405
|
if (opts.file) {
|
|
293
406
|
const { blob, filename } = readFileAsBlob(opts.file);
|
|
@@ -296,15 +409,28 @@ function registerCrudCommands(program2, getClient2, getFormat2) {
|
|
|
296
409
|
const result = await client.collections.from(col).create(data, fileOpts);
|
|
297
410
|
printResult(result, getFormat2());
|
|
298
411
|
} catch (e) {
|
|
299
|
-
|
|
300
|
-
process.exit(1);
|
|
412
|
+
exitWithError(e);
|
|
301
413
|
}
|
|
302
414
|
});
|
|
303
|
-
program2.command("update <collection> <id>").description("Update a document by ID").requiredOption("--data <json>", "Document data (JSON)").option("--file <path>", "File to upload (for upload collections)").action(async (collection, id, opts) => {
|
|
415
|
+
program2.command("update <collection> <id>").description("Update a document by ID").requiredOption("--data <json>", "Document data (JSON)").option("--file <path>", "File to upload (for upload collections)").option("--dry-run", "Validate inputs without executing").action(async (collection, id, opts) => {
|
|
304
416
|
try {
|
|
305
417
|
const col = validateCollection(collection);
|
|
306
|
-
const client = getClient2();
|
|
307
418
|
const data = parseJson(opts.data, "data");
|
|
419
|
+
if (opts.dryRun) {
|
|
420
|
+
printResult(
|
|
421
|
+
{
|
|
422
|
+
dryRun: true,
|
|
423
|
+
valid: true,
|
|
424
|
+
action: "update",
|
|
425
|
+
collection: col,
|
|
426
|
+
id,
|
|
427
|
+
data
|
|
428
|
+
},
|
|
429
|
+
getFormat2()
|
|
430
|
+
);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const client = getClient2();
|
|
308
434
|
let fileOpts;
|
|
309
435
|
if (opts.file) {
|
|
310
436
|
const { blob, filename } = readFileAsBlob(opts.file);
|
|
@@ -313,44 +439,80 @@ function registerCrudCommands(program2, getClient2, getFormat2) {
|
|
|
313
439
|
const result = await client.collections.from(col).update(id, data, fileOpts);
|
|
314
440
|
printResult(result, getFormat2());
|
|
315
441
|
} catch (e) {
|
|
316
|
-
|
|
317
|
-
process.exit(1);
|
|
442
|
+
exitWithError(e);
|
|
318
443
|
}
|
|
319
444
|
});
|
|
320
|
-
program2.command("delete <collection> <id>").description("Delete a document by ID").action(async (collection, id) => {
|
|
445
|
+
program2.command("delete <collection> <id>").description("Delete a document by ID").option("--dry-run", "Validate inputs without executing").action(async (collection, id, opts) => {
|
|
321
446
|
try {
|
|
322
447
|
const col = validateCollection(collection);
|
|
448
|
+
if (opts.dryRun) {
|
|
449
|
+
printResult(
|
|
450
|
+
{
|
|
451
|
+
dryRun: true,
|
|
452
|
+
valid: true,
|
|
453
|
+
action: "delete",
|
|
454
|
+
collection: col,
|
|
455
|
+
id
|
|
456
|
+
},
|
|
457
|
+
getFormat2()
|
|
458
|
+
);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
323
461
|
const client = getClient2();
|
|
324
462
|
const result = await client.collections.from(col).remove(id);
|
|
325
463
|
printResult(result, getFormat2());
|
|
326
464
|
} catch (e) {
|
|
327
|
-
|
|
328
|
-
process.exit(1);
|
|
465
|
+
exitWithError(e);
|
|
329
466
|
}
|
|
330
467
|
});
|
|
331
|
-
program2.command("update-many <collection>").description("Update multiple documents matching a filter").requiredOption("--where <json>", "Filter conditions (JSON)").requiredOption("--data <json>", "Update data (JSON)").action(async (collection, opts) => {
|
|
468
|
+
program2.command("update-many <collection>").description("Update multiple documents matching a filter").requiredOption("--where <json>", "Filter conditions (JSON)").requiredOption("--data <json>", "Update data (JSON)").option("--dry-run", "Validate inputs without executing").action(async (collection, opts) => {
|
|
332
469
|
try {
|
|
333
470
|
const col = validateCollection(collection);
|
|
334
|
-
const client = getClient2();
|
|
335
471
|
const where = parseJson(opts.where, "where");
|
|
336
472
|
const data = parseJson(opts.data, "data");
|
|
473
|
+
if (opts.dryRun) {
|
|
474
|
+
printResult(
|
|
475
|
+
{
|
|
476
|
+
dryRun: true,
|
|
477
|
+
valid: true,
|
|
478
|
+
action: "update-many",
|
|
479
|
+
collection: col,
|
|
480
|
+
where,
|
|
481
|
+
data
|
|
482
|
+
},
|
|
483
|
+
getFormat2()
|
|
484
|
+
);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const client = getClient2();
|
|
337
488
|
const result = await client.collections.from(col).updateMany(where, data);
|
|
338
489
|
printResult(result, getFormat2());
|
|
339
490
|
} catch (e) {
|
|
340
|
-
|
|
341
|
-
process.exit(1);
|
|
491
|
+
exitWithError(e);
|
|
342
492
|
}
|
|
343
493
|
});
|
|
344
|
-
program2.command("delete-many <collection>").description("Delete multiple documents matching a filter").requiredOption("--where <json>", "Filter conditions (JSON)").action(async (collection, opts) => {
|
|
494
|
+
program2.command("delete-many <collection>").description("Delete multiple documents matching a filter").requiredOption("--where <json>", "Filter conditions (JSON)").option("--dry-run", "Validate inputs without executing").action(async (collection, opts) => {
|
|
345
495
|
try {
|
|
346
496
|
const col = validateCollection(collection);
|
|
347
|
-
const client = getClient2();
|
|
348
497
|
const where = parseJson(opts.where, "where");
|
|
498
|
+
if (opts.dryRun) {
|
|
499
|
+
printResult(
|
|
500
|
+
{
|
|
501
|
+
dryRun: true,
|
|
502
|
+
valid: true,
|
|
503
|
+
action: "delete-many",
|
|
504
|
+
collection: col,
|
|
505
|
+
where
|
|
506
|
+
},
|
|
507
|
+
getFormat2()
|
|
508
|
+
);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const client = getClient2();
|
|
349
512
|
const result = await client.collections.from(col).removeMany(where);
|
|
350
513
|
printResult(result, getFormat2());
|
|
351
514
|
} catch (e) {
|
|
352
|
-
|
|
353
|
-
process.exit(1);
|
|
515
|
+
exitWithError(e);
|
|
354
516
|
}
|
|
355
517
|
});
|
|
356
518
|
}
|
|
@@ -358,21 +520,41 @@ function registerCrudCommands(program2, getClient2, getFormat2) {
|
|
|
358
520
|
// src/commands/order.ts
|
|
359
521
|
function registerOrderCommands(program2, getClient2, getFormat2) {
|
|
360
522
|
const order = program2.command("order").description("Order management");
|
|
361
|
-
order.command("create").description("Create a new order").
|
|
523
|
+
order.command("create").description("Create a new order").option("--payment-id <id>", "Payment ID").requiredOption("--order-number <num>", "Order number").requiredOption("--email <email>", "Customer email").option("--customer <id>", "Customer ID").option("--name <name>", "Customer name").option("--phone <phone>", "Customer phone").requiredOption("--shipping-address <json>", "Shipping address (JSON)").requiredOption("--products <json>", "Order products array (JSON)").requiredOption("--total-amount <n>", "Total amount", parseFloat).option("--dry-run", "Validate inputs without executing").action(async (opts) => {
|
|
362
524
|
try {
|
|
363
|
-
const
|
|
364
|
-
|
|
525
|
+
const shippingAddress = parseJson(
|
|
526
|
+
opts.shippingAddress,
|
|
527
|
+
"shipping-address"
|
|
528
|
+
);
|
|
529
|
+
const orderProducts = parseJsonArray(opts.products, "products");
|
|
530
|
+
const data = {
|
|
365
531
|
paymentId: opts.paymentId,
|
|
366
532
|
orderNumber: opts.orderNumber,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
533
|
+
customerSnapshot: {
|
|
534
|
+
email: opts.email,
|
|
535
|
+
name: opts.name,
|
|
536
|
+
phone: opts.phone
|
|
537
|
+
},
|
|
538
|
+
customer: opts.customer,
|
|
539
|
+
shippingAddress,
|
|
540
|
+
orderProducts,
|
|
370
541
|
totalAmount: opts.totalAmount
|
|
542
|
+
};
|
|
543
|
+
if (opts.dryRun) {
|
|
544
|
+
printResult(
|
|
545
|
+
{ dryRun: true, valid: true, action: "order create", data },
|
|
546
|
+
getFormat2()
|
|
547
|
+
);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const client = getClient2();
|
|
551
|
+
const result = await client.api.createOrder({
|
|
552
|
+
...data,
|
|
553
|
+
orderProducts
|
|
371
554
|
});
|
|
372
555
|
printResult(result, getFormat2());
|
|
373
556
|
} catch (e) {
|
|
374
|
-
|
|
375
|
-
process.exit(1);
|
|
557
|
+
exitWithError(e);
|
|
376
558
|
}
|
|
377
559
|
});
|
|
378
560
|
order.command("get <orderNumber>").description("Get an order by order number").action(async (orderNumber) => {
|
|
@@ -381,51 +563,76 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
|
|
|
381
563
|
const result = await client.api.getOrder({ orderNumber });
|
|
382
564
|
printResult(result, getFormat2());
|
|
383
565
|
} catch (e) {
|
|
384
|
-
|
|
385
|
-
process.exit(1);
|
|
566
|
+
exitWithError(e);
|
|
386
567
|
}
|
|
387
568
|
});
|
|
388
|
-
order.command("update <orderNumber>").description("Update order status").requiredOption("--status <status>", "New status").action(async (orderNumber, opts) => {
|
|
569
|
+
order.command("update <orderNumber>").description("Update order status").requiredOption("--status <status>", "New status").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
|
|
389
570
|
try {
|
|
571
|
+
const data = { orderNumber, status: opts.status };
|
|
572
|
+
if (opts.dryRun) {
|
|
573
|
+
printResult(
|
|
574
|
+
{ dryRun: true, valid: true, action: "order update", data },
|
|
575
|
+
getFormat2()
|
|
576
|
+
);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
390
579
|
const client = getClient2();
|
|
391
|
-
const result = await client.api.updateOrder(
|
|
392
|
-
orderNumber,
|
|
393
|
-
status: opts.status
|
|
394
|
-
});
|
|
580
|
+
const result = await client.api.updateOrder(data);
|
|
395
581
|
printResult(result, getFormat2());
|
|
396
582
|
} catch (e) {
|
|
397
|
-
|
|
398
|
-
process.exit(1);
|
|
583
|
+
exitWithError(e);
|
|
399
584
|
}
|
|
400
585
|
});
|
|
401
|
-
order.command("checkout").description("Convert a cart to an order").requiredOption("--cart-id <id>", "Cart ID").
|
|
586
|
+
order.command("checkout").description("Convert a cart to an order").requiredOption("--cart-id <id>", "Cart ID").option("--payment-id <id>", "Payment ID (optional for free orders)").requiredOption("--order-number <num>", "Order number").requiredOption("--customer <json>", "Customer snapshot (JSON)").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
|
|
402
587
|
try {
|
|
403
|
-
const
|
|
404
|
-
const
|
|
588
|
+
const customerSnapshot = parseJson(opts.customer, "customer");
|
|
589
|
+
const data = {
|
|
405
590
|
cartId: opts.cartId,
|
|
406
591
|
paymentId: opts.paymentId,
|
|
407
592
|
orderNumber: opts.orderNumber,
|
|
408
|
-
customerSnapshot
|
|
593
|
+
customerSnapshot
|
|
594
|
+
};
|
|
595
|
+
if (opts.dryRun) {
|
|
596
|
+
printResult(
|
|
597
|
+
{ dryRun: true, valid: true, action: "order checkout", data },
|
|
598
|
+
getFormat2()
|
|
599
|
+
);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const client = getClient2();
|
|
603
|
+
const result = await client.api.checkout({
|
|
604
|
+
...data,
|
|
605
|
+
customerSnapshot
|
|
409
606
|
});
|
|
410
607
|
printResult(result, getFormat2());
|
|
411
608
|
} catch (e) {
|
|
412
|
-
|
|
413
|
-
process.exit(1);
|
|
609
|
+
exitWithError(e);
|
|
414
610
|
}
|
|
415
611
|
});
|
|
416
|
-
order.command("fulfill <orderNumber>").description("Create a fulfillment for an order").requiredOption("--items <json>", "Fulfillment items array (JSON)").option("--carrier <name>", "Shipping carrier").option("--tracking-number <num>", "Tracking number").action(async (orderNumber, opts) => {
|
|
612
|
+
order.command("fulfill <orderNumber>").description("Create a fulfillment for an order").requiredOption("--items <json>", "Fulfillment items array (JSON)").option("--carrier <name>", "Shipping carrier").option("--tracking-number <num>", "Tracking number").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
|
|
417
613
|
try {
|
|
418
|
-
const
|
|
419
|
-
const
|
|
614
|
+
const items = parseJsonArray(opts.items, "items");
|
|
615
|
+
const data = {
|
|
420
616
|
orderNumber,
|
|
421
|
-
items
|
|
617
|
+
items,
|
|
422
618
|
carrier: opts.carrier,
|
|
423
619
|
trackingNumber: opts.trackingNumber
|
|
620
|
+
};
|
|
621
|
+
if (opts.dryRun) {
|
|
622
|
+
printResult(
|
|
623
|
+
{ dryRun: true, valid: true, action: "order fulfill", data },
|
|
624
|
+
getFormat2()
|
|
625
|
+
);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const client = getClient2();
|
|
629
|
+
const result = await client.api.createFulfillment({
|
|
630
|
+
...data,
|
|
631
|
+
items
|
|
424
632
|
});
|
|
425
633
|
printResult(result, getFormat2());
|
|
426
634
|
} catch (e) {
|
|
427
|
-
|
|
428
|
-
process.exit(1);
|
|
635
|
+
exitWithError(e);
|
|
429
636
|
}
|
|
430
637
|
});
|
|
431
638
|
}
|
|
@@ -433,51 +640,83 @@ function registerOrderCommands(program2, getClient2, getFormat2) {
|
|
|
433
640
|
// src/commands/return.ts
|
|
434
641
|
function registerReturnCommands(program2, getClient2, getFormat2) {
|
|
435
642
|
const ret = program2.command("return").description("Return management");
|
|
436
|
-
ret.command("create <orderNumber>").description("Create a return request").requiredOption("--products <json>", "Return products array (JSON)").requiredOption("--refund-amount <n>", "Refund amount", parseFloat).option(
|
|
643
|
+
ret.command("create <orderNumber>").description("Create a return request").requiredOption("--products <json>", "Return products array (JSON)").requiredOption("--refund-amount <n>", "Refund amount", parseFloat).option(
|
|
644
|
+
"--reason <reason>",
|
|
645
|
+
"Return reason (change_of_mind, defective, wrong_delivery, damaged, other)"
|
|
646
|
+
).option("--reason-detail <text>", "Detailed reason").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
|
|
437
647
|
try {
|
|
438
|
-
const
|
|
439
|
-
const
|
|
648
|
+
const returnProducts = parseJsonArray(opts.products, "products");
|
|
649
|
+
const data = {
|
|
440
650
|
orderNumber,
|
|
441
|
-
returnProducts
|
|
651
|
+
returnProducts,
|
|
442
652
|
refundAmount: opts.refundAmount,
|
|
443
653
|
reason: opts.reason,
|
|
444
654
|
reasonDetail: opts.reasonDetail
|
|
655
|
+
};
|
|
656
|
+
if (opts.dryRun) {
|
|
657
|
+
printResult(
|
|
658
|
+
{ dryRun: true, valid: true, action: "return create", data },
|
|
659
|
+
getFormat2()
|
|
660
|
+
);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const client = getClient2();
|
|
664
|
+
const result = await client.api.createReturn({
|
|
665
|
+
...data,
|
|
666
|
+
returnProducts
|
|
445
667
|
});
|
|
446
668
|
printResult(result, getFormat2());
|
|
447
669
|
} catch (e) {
|
|
448
|
-
|
|
449
|
-
process.exit(1);
|
|
670
|
+
exitWithError(e);
|
|
450
671
|
}
|
|
451
672
|
});
|
|
452
|
-
ret.command("update <returnId>").description("Update return status").requiredOption(
|
|
673
|
+
ret.command("update <returnId>").description("Update return status").requiredOption(
|
|
674
|
+
"--status <status>",
|
|
675
|
+
"New status (processing, approved, rejected, completed)"
|
|
676
|
+
).option("--dry-run", "Validate inputs without executing").action(async (returnId, opts) => {
|
|
453
677
|
try {
|
|
678
|
+
const data = { returnId, status: opts.status };
|
|
679
|
+
if (opts.dryRun) {
|
|
680
|
+
printResult(
|
|
681
|
+
{ dryRun: true, valid: true, action: "return update", data },
|
|
682
|
+
getFormat2()
|
|
683
|
+
);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
454
686
|
const client = getClient2();
|
|
455
|
-
const result = await client.api.updateReturn(
|
|
456
|
-
returnId,
|
|
457
|
-
status: opts.status
|
|
458
|
-
});
|
|
687
|
+
const result = await client.api.updateReturn(data);
|
|
459
688
|
printResult(result, getFormat2());
|
|
460
689
|
} catch (e) {
|
|
461
|
-
|
|
462
|
-
process.exit(1);
|
|
690
|
+
exitWithError(e);
|
|
463
691
|
}
|
|
464
692
|
});
|
|
465
|
-
ret.command("refund <orderNumber>").description("Return with refund").requiredOption("--products <json>", "Return products array (JSON)").requiredOption("--refund-amount <n>", "Refund amount", parseFloat).requiredOption("--payment-id <id>", "Payment ID").option("--reason <reason>", "Return reason").option("--reason-detail <text>", "Detailed reason").option("--refund-receipt-url <url>", "Refund receipt URL").action(async (orderNumber, opts) => {
|
|
693
|
+
ret.command("refund <orderNumber>").description("Return with refund").requiredOption("--products <json>", "Return products array (JSON)").requiredOption("--refund-amount <n>", "Refund amount", parseFloat).requiredOption("--payment-id <id>", "Payment ID").option("--reason <reason>", "Return reason").option("--reason-detail <text>", "Detailed reason").option("--refund-receipt-url <url>", "Refund receipt URL").option("--dry-run", "Validate inputs without executing").action(async (orderNumber, opts) => {
|
|
466
694
|
try {
|
|
467
|
-
const
|
|
468
|
-
const
|
|
695
|
+
const returnProducts = parseJsonArray(opts.products, "products");
|
|
696
|
+
const data = {
|
|
469
697
|
orderNumber,
|
|
470
|
-
returnProducts
|
|
698
|
+
returnProducts,
|
|
471
699
|
refundAmount: opts.refundAmount,
|
|
472
700
|
paymentId: opts.paymentId,
|
|
473
701
|
reason: opts.reason,
|
|
474
702
|
reasonDetail: opts.reasonDetail,
|
|
475
703
|
refundReceiptUrl: opts.refundReceiptUrl
|
|
704
|
+
};
|
|
705
|
+
if (opts.dryRun) {
|
|
706
|
+
printResult(
|
|
707
|
+
{ dryRun: true, valid: true, action: "return refund", data },
|
|
708
|
+
getFormat2()
|
|
709
|
+
);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const client = getClient2();
|
|
713
|
+
const result = await client.api.returnWithRefund({
|
|
714
|
+
...data,
|
|
715
|
+
returnProducts
|
|
476
716
|
});
|
|
477
717
|
printResult(result, getFormat2());
|
|
478
718
|
} catch (e) {
|
|
479
|
-
|
|
480
|
-
process.exit(1);
|
|
719
|
+
exitWithError(e);
|
|
481
720
|
}
|
|
482
721
|
});
|
|
483
722
|
}
|
|
@@ -485,43 +724,73 @@ function registerReturnCommands(program2, getClient2, getFormat2) {
|
|
|
485
724
|
// src/commands/cart.ts
|
|
486
725
|
function registerCartCommands(program2, getClient2, getFormat2) {
|
|
487
726
|
const cart = program2.command("cart").description("Cart management");
|
|
488
|
-
cart.command("add <cartId>").description("Add an item to cart").requiredOption("--product <id>", "Product ID").requiredOption("--variant <id>", "Variant ID").requiredOption("--option <id>", "Option ID").requiredOption(
|
|
727
|
+
cart.command("add <cartId>").description("Add an item to cart").requiredOption("--product <id>", "Product ID").requiredOption("--variant <id>", "Variant ID").requiredOption("--option <id>", "Option ID").requiredOption(
|
|
728
|
+
"--quantity <n>",
|
|
729
|
+
"Quantity",
|
|
730
|
+
(v) => parseInt(v, 10)
|
|
731
|
+
).option("--dry-run", "Validate inputs without executing").action(async (cartId, opts) => {
|
|
489
732
|
try {
|
|
490
|
-
const
|
|
491
|
-
const result = await client.cart.addItem({
|
|
733
|
+
const data = {
|
|
492
734
|
cartId,
|
|
493
735
|
product: opts.product,
|
|
494
736
|
variant: opts.variant,
|
|
495
737
|
option: opts.option,
|
|
496
738
|
quantity: opts.quantity
|
|
497
|
-
}
|
|
739
|
+
};
|
|
740
|
+
if (opts.dryRun) {
|
|
741
|
+
printResult(
|
|
742
|
+
{ dryRun: true, valid: true, action: "cart add", data },
|
|
743
|
+
getFormat2()
|
|
744
|
+
);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const client = getClient2();
|
|
748
|
+
const result = await client.cart.addItem(data);
|
|
498
749
|
printResult(result, getFormat2());
|
|
499
750
|
} catch (e) {
|
|
500
|
-
|
|
501
|
-
process.exit(1);
|
|
751
|
+
exitWithError(e);
|
|
502
752
|
}
|
|
503
753
|
});
|
|
504
|
-
cart.command("update <cartItemId>").description("Update cart item quantity").requiredOption(
|
|
754
|
+
cart.command("update <cartItemId>").description("Update cart item quantity").requiredOption(
|
|
755
|
+
"--quantity <n>",
|
|
756
|
+
"New quantity",
|
|
757
|
+
(v) => parseInt(v, 10)
|
|
758
|
+
).option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
|
|
505
759
|
try {
|
|
760
|
+
const data = { cartItemId, quantity: opts.quantity };
|
|
761
|
+
if (opts.dryRun) {
|
|
762
|
+
printResult(
|
|
763
|
+
{ dryRun: true, valid: true, action: "cart update", data },
|
|
764
|
+
getFormat2()
|
|
765
|
+
);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
506
768
|
const client = getClient2();
|
|
507
|
-
const result = await client.cart.updateItem(
|
|
508
|
-
cartItemId,
|
|
509
|
-
quantity: opts.quantity
|
|
510
|
-
});
|
|
769
|
+
const result = await client.cart.updateItem(data);
|
|
511
770
|
printResult(result, getFormat2());
|
|
512
771
|
} catch (e) {
|
|
513
|
-
|
|
514
|
-
process.exit(1);
|
|
772
|
+
exitWithError(e);
|
|
515
773
|
}
|
|
516
774
|
});
|
|
517
|
-
cart.command("remove <cartItemId>").description("Remove an item from cart").action(async (cartItemId) => {
|
|
775
|
+
cart.command("remove <cartItemId>").description("Remove an item from cart").option("--dry-run", "Validate inputs without executing").action(async (cartItemId, opts) => {
|
|
518
776
|
try {
|
|
777
|
+
if (opts.dryRun) {
|
|
778
|
+
printResult(
|
|
779
|
+
{
|
|
780
|
+
dryRun: true,
|
|
781
|
+
valid: true,
|
|
782
|
+
action: "cart remove",
|
|
783
|
+
data: { cartItemId }
|
|
784
|
+
},
|
|
785
|
+
getFormat2()
|
|
786
|
+
);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
519
789
|
const client = getClient2();
|
|
520
790
|
const result = await client.cart.removeItem({ cartItemId });
|
|
521
791
|
printResult(result, getFormat2());
|
|
522
792
|
} catch (e) {
|
|
523
|
-
|
|
524
|
-
process.exit(1);
|
|
793
|
+
exitWithError(e);
|
|
525
794
|
}
|
|
526
795
|
});
|
|
527
796
|
}
|
|
@@ -529,15 +798,17 @@ function registerCartCommands(program2, getClient2, getFormat2) {
|
|
|
529
798
|
// src/commands/stock.ts
|
|
530
799
|
function registerStockCommands(program2, getClient2, getFormat2) {
|
|
531
800
|
const stock = program2.command("stock").description("Stock management");
|
|
532
|
-
stock.command("check").description("Check stock availability").requiredOption(
|
|
801
|
+
stock.command("check").description("Check stock availability").requiredOption(
|
|
802
|
+
"--items <json>",
|
|
803
|
+
"Items to check (JSON array of { optionId, quantity })"
|
|
804
|
+
).action(async (opts) => {
|
|
533
805
|
try {
|
|
534
806
|
const client = getClient2();
|
|
535
807
|
const items = parseJsonArray(opts.items, "items");
|
|
536
808
|
const result = await client.product.stockCheck({ items });
|
|
537
809
|
printResult(result, getFormat2());
|
|
538
810
|
} catch (e) {
|
|
539
|
-
|
|
540
|
-
process.exit(1);
|
|
811
|
+
exitWithError(e);
|
|
541
812
|
}
|
|
542
813
|
});
|
|
543
814
|
}
|
|
@@ -545,19 +816,29 @@ function registerStockCommands(program2, getClient2, getFormat2) {
|
|
|
545
816
|
// src/commands/transaction.ts
|
|
546
817
|
function registerTransactionCommands(program2, getClient2, getFormat2) {
|
|
547
818
|
const tx = program2.command("transaction").description("Transaction management");
|
|
548
|
-
tx.command("update").description("Update transaction status").requiredOption("--payment-id <id>", "Payment ID").requiredOption(
|
|
819
|
+
tx.command("update").description("Update transaction status").requiredOption("--payment-id <id>", "Payment ID").requiredOption(
|
|
820
|
+
"--status <status>",
|
|
821
|
+
"New status (pending, paid, failed, canceled)"
|
|
822
|
+
).requiredOption("--payment-method <method>", "Payment method").requiredOption("--receipt-url <url>", "Receipt URL").option("--dry-run", "Validate inputs without executing").action(async (opts) => {
|
|
549
823
|
try {
|
|
550
|
-
const
|
|
551
|
-
const result = await client.api.updateTransaction({
|
|
824
|
+
const data = {
|
|
552
825
|
paymentId: opts.paymentId,
|
|
553
826
|
status: opts.status,
|
|
554
827
|
paymentMethod: opts.paymentMethod,
|
|
555
828
|
receiptUrl: opts.receiptUrl
|
|
556
|
-
}
|
|
829
|
+
};
|
|
830
|
+
if (opts.dryRun) {
|
|
831
|
+
printResult(
|
|
832
|
+
{ dryRun: true, valid: true, action: "transaction update", data },
|
|
833
|
+
getFormat2()
|
|
834
|
+
);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const client = getClient2();
|
|
838
|
+
const result = await client.api.updateTransaction(data);
|
|
557
839
|
printResult(result, getFormat2());
|
|
558
840
|
} catch (e) {
|
|
559
|
-
|
|
560
|
-
process.exit(1);
|
|
841
|
+
exitWithError(e);
|
|
561
842
|
}
|
|
562
843
|
});
|
|
563
844
|
}
|
|
@@ -577,8 +858,12 @@ function escapeHtml(s) {
|
|
|
577
858
|
function openBrowser(url) {
|
|
578
859
|
const os = platform();
|
|
579
860
|
const onError = () => {
|
|
580
|
-
console.log(
|
|
581
|
-
|
|
861
|
+
console.log(
|
|
862
|
+
pc4.yellow(
|
|
863
|
+
`Could not open browser automatically. Open this URL manually:
|
|
864
|
+
${url}`
|
|
865
|
+
)
|
|
866
|
+
);
|
|
582
867
|
};
|
|
583
868
|
if (os === "win32") {
|
|
584
869
|
exec(`start "" "${url}"`, (err) => {
|
|
@@ -610,7 +895,7 @@ var ERROR_HTML = (msg) => `<!DOCTYPE html>
|
|
|
610
895
|
<style>${PAGE_STYLE}</style>
|
|
611
896
|
</head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`;
|
|
612
897
|
function startAuthServer(options) {
|
|
613
|
-
return new Promise((
|
|
898
|
+
return new Promise((resolve2, reject) => {
|
|
614
899
|
const server = createServer((req, res) => {
|
|
615
900
|
if (!req.url) {
|
|
616
901
|
res.writeHead(400).end();
|
|
@@ -631,19 +916,19 @@ function startAuthServer(options) {
|
|
|
631
916
|
if (error) {
|
|
632
917
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(ERROR_HTML(error));
|
|
633
918
|
console.error(pc4.red(`Login failed: ${error}`));
|
|
634
|
-
cleanup(
|
|
919
|
+
cleanup(2);
|
|
635
920
|
return;
|
|
636
921
|
}
|
|
637
922
|
if (receivedState !== options.state) {
|
|
638
923
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(ERROR_HTML("State mismatch \u2014 possible CSRF attack."));
|
|
639
924
|
console.error(pc4.red("Login failed: state mismatch."));
|
|
640
|
-
cleanup(
|
|
925
|
+
cleanup(2);
|
|
641
926
|
return;
|
|
642
927
|
}
|
|
643
928
|
if (!clientKey || !secretKey || !tenant) {
|
|
644
929
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(ERROR_HTML("Missing credentials in callback."));
|
|
645
930
|
console.error(pc4.red("Login failed: missing credentials."));
|
|
646
|
-
cleanup(
|
|
931
|
+
cleanup(2);
|
|
647
932
|
return;
|
|
648
933
|
}
|
|
649
934
|
options.saveFn({
|
|
@@ -685,10 +970,12 @@ Logged in successfully!`));
|
|
|
685
970
|
return;
|
|
686
971
|
}
|
|
687
972
|
timeout = setTimeout(() => {
|
|
688
|
-
console.error(
|
|
689
|
-
|
|
973
|
+
console.error(
|
|
974
|
+
pc4.red("\nLogin timed out (3 minutes). Please try again.")
|
|
975
|
+
);
|
|
976
|
+
cleanup(4);
|
|
690
977
|
}, TIMEOUT_MS);
|
|
691
|
-
|
|
978
|
+
resolve2({ port: addr.port, cleanup });
|
|
692
979
|
});
|
|
693
980
|
server.on("error", (err) => {
|
|
694
981
|
reject(err);
|
|
@@ -713,8 +1000,12 @@ function registerAuthCommands(program2) {
|
|
|
713
1000
|
${loginUrl}`));
|
|
714
1001
|
openBrowser(loginUrl);
|
|
715
1002
|
} catch (err) {
|
|
716
|
-
console.error(
|
|
717
|
-
|
|
1003
|
+
console.error(
|
|
1004
|
+
pc4.red(
|
|
1005
|
+
`Server error: ${err instanceof Error ? err.message : String(err)}`
|
|
1006
|
+
)
|
|
1007
|
+
);
|
|
1008
|
+
process.exit(4);
|
|
718
1009
|
}
|
|
719
1010
|
});
|
|
720
1011
|
program2.command("logout").description("Remove stored credentials").action(() => {
|
|
@@ -739,7 +1030,9 @@ ${loginUrl}`));
|
|
|
739
1030
|
console.log(`Tenant: ${pc4.bold(creds.tenantName)}${scope}`);
|
|
740
1031
|
console.log(`Client Key: ${pc4.dim(masked)}`);
|
|
741
1032
|
console.log(`Stored at: ${pc4.dim(creds.storedAt)}`);
|
|
742
|
-
console.log(
|
|
1033
|
+
console.log(
|
|
1034
|
+
`File: ${pc4.dim(isLocal ? getLocalCredentialsPath() : getCredentialsPath())}`
|
|
1035
|
+
);
|
|
743
1036
|
});
|
|
744
1037
|
const tenant = program2.command("tenant").description("Manage tenant switching");
|
|
745
1038
|
tenant.command("list").description("Show cached tenant list").action(() => {
|
|
@@ -768,13 +1061,17 @@ ${loginUrl}`));
|
|
|
768
1061
|
const tenants = loadTenantList();
|
|
769
1062
|
if (!tenants || tenants.length === 0) {
|
|
770
1063
|
console.error(pc4.red('No cached tenants. Run "01 login" first.'));
|
|
771
|
-
process.exit(
|
|
1064
|
+
process.exit(2);
|
|
772
1065
|
}
|
|
773
|
-
const match = tenants.find(
|
|
1066
|
+
const match = tenants.find(
|
|
1067
|
+
(t) => t.name.toLowerCase() === name.toLowerCase()
|
|
1068
|
+
);
|
|
774
1069
|
if (!match) {
|
|
775
1070
|
console.error(pc4.red(`Tenant "${name}" not found in cache.`));
|
|
776
|
-
console.error(
|
|
777
|
-
|
|
1071
|
+
console.error(
|
|
1072
|
+
pc4.dim(`Available: ${tenants.map((t) => t.name).join(", ")}`)
|
|
1073
|
+
);
|
|
1074
|
+
process.exit(3);
|
|
778
1075
|
}
|
|
779
1076
|
const state = randomBytes(32).toString("hex");
|
|
780
1077
|
const isLocal = !!opts.local;
|
|
@@ -784,10 +1081,14 @@ ${loginUrl}`));
|
|
|
784
1081
|
saveFn: (creds) => {
|
|
785
1082
|
if (isLocal) {
|
|
786
1083
|
saveLocalCredentials(creds);
|
|
787
|
-
console.log(
|
|
1084
|
+
console.log(
|
|
1085
|
+
pc4.dim(`Credentials saved to ${getLocalCredentialsPath()}`)
|
|
1086
|
+
);
|
|
788
1087
|
} else {
|
|
789
1088
|
saveCredentials(creds);
|
|
790
|
-
console.log(
|
|
1089
|
+
console.log(
|
|
1090
|
+
pc4.dim(`Credentials saved to ${getCredentialsPath()}`)
|
|
1091
|
+
);
|
|
791
1092
|
}
|
|
792
1093
|
}
|
|
793
1094
|
});
|
|
@@ -802,22 +1103,117 @@ ${loginUrl}`));
|
|
|
802
1103
|
${loginUrl}`));
|
|
803
1104
|
openBrowser(loginUrl);
|
|
804
1105
|
} catch (err) {
|
|
805
|
-
console.error(
|
|
806
|
-
|
|
1106
|
+
console.error(
|
|
1107
|
+
pc4.red(
|
|
1108
|
+
`Server error: ${err instanceof Error ? err.message : String(err)}`
|
|
1109
|
+
)
|
|
1110
|
+
);
|
|
1111
|
+
process.exit(4);
|
|
807
1112
|
}
|
|
808
1113
|
});
|
|
809
1114
|
}
|
|
810
1115
|
|
|
1116
|
+
// src/commands/schema.ts
|
|
1117
|
+
import { COLLECTIONS as COLLECTIONS2 } from "@01.software/sdk";
|
|
1118
|
+
function getApiUrl() {
|
|
1119
|
+
return (process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL || "https://api.01.software").replace(/\/$/, "");
|
|
1120
|
+
}
|
|
1121
|
+
function registerSchemaCommands(program2, getClient2, getFormat2) {
|
|
1122
|
+
const schema = program2.command("schema").description("Collection schema introspection");
|
|
1123
|
+
schema.command("list").description("List available collections").action(() => {
|
|
1124
|
+
printResult(COLLECTIONS2, getFormat2());
|
|
1125
|
+
});
|
|
1126
|
+
schema.command("show <collection>").description("Show collection field schema").action(async (collection) => {
|
|
1127
|
+
try {
|
|
1128
|
+
if (!COLLECTIONS2.includes(collection)) {
|
|
1129
|
+
const normalized = collection.replace(/-/g, "").toLowerCase();
|
|
1130
|
+
const suggestions = COLLECTIONS2.filter((c) => {
|
|
1131
|
+
const cn = c.replace(/-/g, "").toLowerCase();
|
|
1132
|
+
return cn.startsWith(normalized) || normalized.startsWith(cn) || normalized.length >= 3 && cn.includes(normalized);
|
|
1133
|
+
}).slice(0, 5);
|
|
1134
|
+
const hint = suggestions.length > 0 ? `Did you mean: ${suggestions.join(", ")}?` : 'Run "01 schema list" for available collections.';
|
|
1135
|
+
throw new Error(`Unknown collection "${collection}". ${hint}`);
|
|
1136
|
+
}
|
|
1137
|
+
const client = getClient2();
|
|
1138
|
+
const { createServerToken } = await import("@01.software/sdk/auth");
|
|
1139
|
+
const token = await createServerToken(
|
|
1140
|
+
client.clientKey,
|
|
1141
|
+
client.secretKey
|
|
1142
|
+
);
|
|
1143
|
+
const baseUrl = getApiUrl();
|
|
1144
|
+
const url = `${baseUrl}/api/tenants/schema/${encodeURIComponent(collection)}`;
|
|
1145
|
+
const response = await fetch(url, {
|
|
1146
|
+
headers: {
|
|
1147
|
+
"X-Client-Key": client.clientKey,
|
|
1148
|
+
Authorization: `Bearer ${token}`
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
if (!response.ok) {
|
|
1152
|
+
const body = await response.json().catch(() => ({ error: response.statusText }));
|
|
1153
|
+
const err = new Error(
|
|
1154
|
+
body.error || `HTTP ${response.status}`
|
|
1155
|
+
);
|
|
1156
|
+
Object.assign(err, { status: response.status });
|
|
1157
|
+
throw err;
|
|
1158
|
+
}
|
|
1159
|
+
const result = await response.json();
|
|
1160
|
+
printResult(result, getFormat2());
|
|
1161
|
+
} catch (e) {
|
|
1162
|
+
exitWithError(e);
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/commands/mcp.ts
|
|
1168
|
+
import { resolve, dirname } from "path";
|
|
1169
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1170
|
+
import { spawn } from "child_process";
|
|
1171
|
+
import { fileURLToPath } from "url";
|
|
1172
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1173
|
+
function findStdioEntry() {
|
|
1174
|
+
for (const depth of ["../../../..", "../../.."]) {
|
|
1175
|
+
const candidate = resolve(__dirname, depth, "apps/mcp/.xmcp/stdio.js");
|
|
1176
|
+
if (existsSync2(candidate)) return candidate;
|
|
1177
|
+
}
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
function registerMcpCommands(program2) {
|
|
1181
|
+
program2.command("mcp").description("Start MCP server over stdio").action(() => {
|
|
1182
|
+
const client = resolveClient(program2.opts().apiKey);
|
|
1183
|
+
const stdioEntry = findStdioEntry();
|
|
1184
|
+
if (!stdioEntry) {
|
|
1185
|
+
exitWithError(
|
|
1186
|
+
new Error(
|
|
1187
|
+
"MCP server not found. Ensure apps/mcp is built (pnpm --filter mcp build)."
|
|
1188
|
+
)
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
const child = spawn(process.execPath, [stdioEntry], {
|
|
1192
|
+
env: {
|
|
1193
|
+
...process.env,
|
|
1194
|
+
SOFTWARE_CLIENT_KEY: client.clientKey,
|
|
1195
|
+
SOFTWARE_SECRET_KEY: client.secretKey
|
|
1196
|
+
},
|
|
1197
|
+
stdio: ["inherit", "inherit", "inherit"]
|
|
1198
|
+
});
|
|
1199
|
+
child.on("error", (err) => {
|
|
1200
|
+
exitWithError(new Error(`Failed to start MCP server: ${err.message}`));
|
|
1201
|
+
});
|
|
1202
|
+
child.on("exit", (code) => {
|
|
1203
|
+
process.exit(code ?? 0);
|
|
1204
|
+
});
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
|
|
811
1208
|
// src/index.ts
|
|
812
1209
|
var require2 = createRequire(import.meta.url);
|
|
813
1210
|
var { version } = require2("../package.json");
|
|
814
1211
|
process.on("unhandledRejection", (err) => {
|
|
815
|
-
|
|
816
|
-
process.exit(1);
|
|
1212
|
+
exitWithError(err);
|
|
817
1213
|
});
|
|
818
1214
|
var program = new Command();
|
|
819
|
-
program.name("01").description("CLI for the 01.software platform").version(version).option("--api-key <base64>", "Base64-encoded API key (clientKey:secretKey)").option("--format <format>", "Output format: json
|
|
820
|
-
var getFormat = () => program.opts().format;
|
|
1215
|
+
program.name("01").description("CLI for the 01.software platform").version(version).option("--api-key <base64>", "Base64-encoded API key (clientKey:secretKey)").option("--format <format>", "Output format: json, table, or ndjson");
|
|
1216
|
+
var getFormat = () => program.opts().format ?? process.env.OUTPUT_FORMAT ?? "json";
|
|
821
1217
|
var getClient = () => resolveClient(program.opts().apiKey);
|
|
822
1218
|
registerCrudCommands(program, getClient, getFormat);
|
|
823
1219
|
registerOrderCommands(program, getClient, getFormat);
|
|
@@ -825,6 +1221,8 @@ registerReturnCommands(program, getClient, getFormat);
|
|
|
825
1221
|
registerCartCommands(program, getClient, getFormat);
|
|
826
1222
|
registerStockCommands(program, getClient, getFormat);
|
|
827
1223
|
registerTransactionCommands(program, getClient, getFormat);
|
|
1224
|
+
registerSchemaCommands(program, getClient, getFormat);
|
|
1225
|
+
registerMcpCommands(program);
|
|
828
1226
|
registerAuthCommands(program);
|
|
829
1227
|
program.parse();
|
|
830
1228
|
//# sourceMappingURL=index.js.map
|