@stndrds/cli 1.0.0-alpha.226 → 1.0.0-alpha.228
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.mjs +136 -39
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/program.ts
|
|
4
|
-
import
|
|
4
|
+
import chalk7 from "chalk";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/client.ts
|
|
@@ -32,16 +32,10 @@ function createClient(config) {
|
|
|
32
32
|
"Content-Type": "application/json",
|
|
33
33
|
...tenantId ? { "X-Tenant-ID": tenantId } : {}
|
|
34
34
|
};
|
|
35
|
-
async function
|
|
36
|
-
const url = buildUrl(apiUrl, path, options?.params);
|
|
35
|
+
async function sendRequest(url, init) {
|
|
37
36
|
let response;
|
|
38
37
|
try {
|
|
39
|
-
response = await fetch(url, {
|
|
40
|
-
method,
|
|
41
|
-
headers,
|
|
42
|
-
body: options?.body ? JSON.stringify(options.body) : void 0,
|
|
43
|
-
signal: AbortSignal.timeout(3e4)
|
|
44
|
-
});
|
|
38
|
+
response = await fetch(url, { ...init, signal: AbortSignal.timeout(3e4) });
|
|
45
39
|
} catch (error) {
|
|
46
40
|
if (error instanceof TypeError || error instanceof DOMException && error.name === "TimeoutError") {
|
|
47
41
|
throw new ApiClientError(0, `Could not connect to ${apiUrl}. Is the server running?`);
|
|
@@ -58,6 +52,19 @@ function createClient(config) {
|
|
|
58
52
|
}
|
|
59
53
|
return data;
|
|
60
54
|
}
|
|
55
|
+
function request(method, path, options) {
|
|
56
|
+
const url = buildUrl(apiUrl, path, options?.params);
|
|
57
|
+
return sendRequest(url, {
|
|
58
|
+
method,
|
|
59
|
+
headers,
|
|
60
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function postMultipart(path, form) {
|
|
64
|
+
const url = buildUrl(apiUrl, path);
|
|
65
|
+
const { "Content-Type": _ct, ...multipartHeaders } = headers;
|
|
66
|
+
return sendRequest(url, { method: "POST", headers: multipartHeaders, body: form });
|
|
67
|
+
}
|
|
61
68
|
return {
|
|
62
69
|
get(path, params) {
|
|
63
70
|
return request("GET", path, { params });
|
|
@@ -73,7 +80,8 @@ function createClient(config) {
|
|
|
73
80
|
},
|
|
74
81
|
delete(path) {
|
|
75
82
|
return request("DELETE", path);
|
|
76
|
-
}
|
|
83
|
+
},
|
|
84
|
+
postMultipart
|
|
77
85
|
};
|
|
78
86
|
}
|
|
79
87
|
|
|
@@ -200,6 +208,43 @@ function registerAutofillCommand(program) {
|
|
|
200
208
|
);
|
|
201
209
|
}
|
|
202
210
|
|
|
211
|
+
// src/commands/connectors.ts
|
|
212
|
+
import chalk3 from "chalk";
|
|
213
|
+
var PROVIDERS = ["gmail", "outlook"];
|
|
214
|
+
function resolveScope(scope) {
|
|
215
|
+
return scope === "actor" ? "actor" : "tenant";
|
|
216
|
+
}
|
|
217
|
+
function registerConnectorsCommand(program) {
|
|
218
|
+
const connectors = program.command("connectors").description("Manage email connectors (Gmail / Outlook OAuth connections)");
|
|
219
|
+
connectors.command("list").description("List connector connections").option("--scope <scope>", "connection scope (tenant or actor)", "tenant").action(async (opts, cmd) => {
|
|
220
|
+
const client = getClientFromCommand(cmd);
|
|
221
|
+
const result = await client.get("/connectors/connections", {
|
|
222
|
+
scope: resolveScope(opts.scope)
|
|
223
|
+
});
|
|
224
|
+
formatOutput(result, getFormat(cmd));
|
|
225
|
+
});
|
|
226
|
+
connectors.command("connect").description("Start an OAuth flow and return the authorization URL to open").requiredOption("--provider <provider>", "connector provider (gmail or outlook)").option("--scope <scope>", "connection scope (tenant or actor)", "tenant").action(async (opts, cmd) => {
|
|
227
|
+
if (!PROVIDERS.includes(opts.provider)) {
|
|
228
|
+
throw new Error(`--provider must be one of: ${PROVIDERS.join(", ")}`);
|
|
229
|
+
}
|
|
230
|
+
const client = getClientFromCommand(cmd);
|
|
231
|
+
const result = await client.post("/connectors/auth/start", {
|
|
232
|
+
provider: opts.provider,
|
|
233
|
+
scope: resolveScope(opts.scope)
|
|
234
|
+
});
|
|
235
|
+
formatOutput(result, getFormat(cmd));
|
|
236
|
+
});
|
|
237
|
+
connectors.command("disconnect").description("Disconnect (delete) a connector connection").argument("<id>", "connection ID").option("--yes", "disconnect without confirmation").action(async (id, opts, cmd) => {
|
|
238
|
+
if (!opts.yes) {
|
|
239
|
+
throw new Error('Disconnecting a connector is explicit. Re-run with "--yes" to confirm.');
|
|
240
|
+
}
|
|
241
|
+
const client = getClientFromCommand(cmd);
|
|
242
|
+
await client.delete(`/connectors/connections/${id}`);
|
|
243
|
+
process.stdout.write(`${chalk3.green("\u2713")} Connector connection ${id} disconnected.
|
|
244
|
+
`);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
203
248
|
// src/commands/documents.ts
|
|
204
249
|
function registerDocumentsCommand(program) {
|
|
205
250
|
const documents = program.command("documents").description("Manage documents");
|
|
@@ -263,8 +308,40 @@ function registerDocumentsCommand(program) {
|
|
|
263
308
|
});
|
|
264
309
|
}
|
|
265
310
|
|
|
311
|
+
// src/commands/folders.ts
|
|
312
|
+
import { readFile } from "fs/promises";
|
|
313
|
+
function registerFoldersCommand(program) {
|
|
314
|
+
const folders = program.command("folders").description("Manage record drive folders");
|
|
315
|
+
folders.command("reconcile-paths").description("Create a folder tree on a record drive from paths").argument("<object>", "object name (e.g. contacts)").argument("<recordId>", "record ID owning the drive").option(
|
|
316
|
+
"--path <path>",
|
|
317
|
+
"folder path to create (repeatable)",
|
|
318
|
+
(val, acc) => {
|
|
319
|
+
acc.push(val);
|
|
320
|
+
return acc;
|
|
321
|
+
},
|
|
322
|
+
[]
|
|
323
|
+
).option("--paths-file <file>", "file containing one path per line").option("--mode <mode>", "reconcile mode: repair or dryRun", "repair").action(async (objectName, recordId, opts, cmd) => {
|
|
324
|
+
const paths = [...opts.path];
|
|
325
|
+
if (opts.pathsFile) {
|
|
326
|
+
const content = await readFile(opts.pathsFile, "utf-8");
|
|
327
|
+
const filePaths = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
328
|
+
paths.push(...filePaths);
|
|
329
|
+
}
|
|
330
|
+
if (paths.length === 0) {
|
|
331
|
+
throw new Error("No paths provided. Pass at least one --path or a non-empty --paths-file.");
|
|
332
|
+
}
|
|
333
|
+
const client = getClientFromCommand(cmd);
|
|
334
|
+
const result = await client.post(`/folders/record-drive/${recordId}/reconcile-paths`, {
|
|
335
|
+
objectName,
|
|
336
|
+
paths,
|
|
337
|
+
mode: opts.mode
|
|
338
|
+
});
|
|
339
|
+
formatOutput(result, getFormat(cmd));
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
266
343
|
// src/commands/keys.ts
|
|
267
|
-
import
|
|
344
|
+
import chalk4 from "chalk";
|
|
268
345
|
function registerKeysCommand(program) {
|
|
269
346
|
const keys = program.command("keys").description("Manage Standards API keys");
|
|
270
347
|
keys.command("list").description("List API keys").action(async (_opts, cmd) => {
|
|
@@ -291,12 +368,14 @@ function registerKeysCommand(program) {
|
|
|
291
368
|
}
|
|
292
369
|
const client = getClientFromCommand(cmd);
|
|
293
370
|
await client.delete(`/api-keys/${id}`);
|
|
294
|
-
process.stdout.write(`${
|
|
371
|
+
process.stdout.write(`${chalk4.green("\u2713")} API key ${id} revoked.
|
|
295
372
|
`);
|
|
296
373
|
});
|
|
297
374
|
}
|
|
298
375
|
|
|
299
376
|
// src/commands/records.ts
|
|
377
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
378
|
+
import { basename } from "path";
|
|
300
379
|
function parseSortFlag(sort) {
|
|
301
380
|
const [attribute, direction = "asc"] = sort.split(":");
|
|
302
381
|
return [{ attribute, direction }];
|
|
@@ -353,15 +432,31 @@ function registerRecordsCommand(program) {
|
|
|
353
432
|
const result = await client.post(`/records/${objectName}/search`, body);
|
|
354
433
|
formatOutput(result, getFormat(cmd));
|
|
355
434
|
});
|
|
435
|
+
records.command("attach-document").description("Upload and attach a local file to a record").argument("<object>", "object name (e.g. contacts)").argument("<recordId>", "record ID").requiredOption("--file <path>", "local file path to upload").option("--attribute <attr>", "attribute name to attach the document to").option("--parent-id <folderId>", "parent folder ID").option("--title <title>", "document title (defaults to filename)").action(async (objectName, recordId, opts, cmd) => {
|
|
436
|
+
const fileContent = await readFile2(opts.file);
|
|
437
|
+
const fileName = basename(opts.file);
|
|
438
|
+
const title = opts.title ?? fileName;
|
|
439
|
+
const form = new FormData();
|
|
440
|
+
form.append("files", new Blob([fileContent]), fileName);
|
|
441
|
+
form.append("title", title);
|
|
442
|
+
if (opts.attribute) form.append("attributeName", opts.attribute);
|
|
443
|
+
if (opts.parentId) form.append("parentId", opts.parentId);
|
|
444
|
+
const client = getClientFromCommand(cmd);
|
|
445
|
+
const result = await client.postMultipart(
|
|
446
|
+
`/records/${objectName}/${recordId}/documents/attach`,
|
|
447
|
+
form
|
|
448
|
+
);
|
|
449
|
+
formatOutput(result, getFormat(cmd));
|
|
450
|
+
});
|
|
356
451
|
}
|
|
357
452
|
|
|
358
453
|
// src/commands/root.ts
|
|
359
454
|
import { stdin as input, stdout as output } from "process";
|
|
360
455
|
import { createInterface } from "readline/promises";
|
|
361
|
-
import
|
|
456
|
+
import chalk5 from "chalk";
|
|
362
457
|
|
|
363
458
|
// src/config.ts
|
|
364
|
-
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
459
|
+
import { mkdir, readFile as readFile3, rm, writeFile } from "fs/promises";
|
|
365
460
|
import { homedir } from "os";
|
|
366
461
|
import { dirname, join } from "path";
|
|
367
462
|
var DEFAULT_API_URL = "http://localhost:4100/v1";
|
|
@@ -375,7 +470,7 @@ function getConfigPath() {
|
|
|
375
470
|
}
|
|
376
471
|
async function readConfig() {
|
|
377
472
|
try {
|
|
378
|
-
const raw = await
|
|
473
|
+
const raw = await readFile3(getConfigPath(), "utf8");
|
|
379
474
|
const parsed = JSON.parse(raw);
|
|
380
475
|
return {
|
|
381
476
|
currentProfile: parsed.currentProfile,
|
|
@@ -498,7 +593,7 @@ function registerRootCommands(program) {
|
|
|
498
593
|
await client.get("/api-keys");
|
|
499
594
|
await upsertProfile({ name: opts.name, apiUrl: opts.url, apiKey });
|
|
500
595
|
process.stdout.write(
|
|
501
|
-
`${
|
|
596
|
+
`${chalk5.green("\u2713")} Standards instance "${opts.name}" saved and selected.
|
|
502
597
|
`
|
|
503
598
|
);
|
|
504
599
|
process.stdout.write(` API URL: ${opts.url}
|
|
@@ -510,7 +605,7 @@ function registerRootCommands(program) {
|
|
|
510
605
|
});
|
|
511
606
|
program.command("use").description("Select the active Standards instance").argument("<name>", "instance name").action(async (name) => {
|
|
512
607
|
await setCurrentProfile(name);
|
|
513
|
-
process.stdout.write(`${
|
|
608
|
+
process.stdout.write(`${chalk5.green("\u2713")} Standards instance "${name}" selected.
|
|
514
609
|
`);
|
|
515
610
|
});
|
|
516
611
|
program.command("instances").description("List configured Standards instances").action(async (_opts, cmd) => {
|
|
@@ -529,7 +624,7 @@ function registerRootCommands(program) {
|
|
|
529
624
|
});
|
|
530
625
|
program.command("logout").description("Remove a Standards instance from local CLI config").argument("[name]", "instance name, defaults to current").action(async (name) => {
|
|
531
626
|
await removeProfile(name);
|
|
532
|
-
process.stdout.write(`${
|
|
627
|
+
process.stdout.write(`${chalk5.green("\u2713")} Standards instance removed.
|
|
533
628
|
`);
|
|
534
629
|
});
|
|
535
630
|
}
|
|
@@ -539,7 +634,7 @@ import { createHash } from "crypto";
|
|
|
539
634
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
540
635
|
import { join as join2 } from "path";
|
|
541
636
|
import { fileURLToPath } from "url";
|
|
542
|
-
import
|
|
637
|
+
import chalk6 from "chalk";
|
|
543
638
|
function registerSchemaCommand(program) {
|
|
544
639
|
const schema = program.command("schema").description("Inspect schema objects and attributes");
|
|
545
640
|
schema.command("list").description("List all schema objects").action(async (_opts, cmd) => {
|
|
@@ -566,7 +661,7 @@ function registerSchemaCommand(program) {
|
|
|
566
661
|
const result = await fetchDrift(getClientFromCommand(cmd), opts.object);
|
|
567
662
|
const totalDrift = result.summary.totalCustomObjects + result.summary.totalCustomAttributes;
|
|
568
663
|
if (totalDrift === 0) {
|
|
569
|
-
console.info(
|
|
664
|
+
console.info(chalk6.green("\u2713 No drift detected \u2014 nothing to pull."));
|
|
570
665
|
process.exit(0);
|
|
571
666
|
}
|
|
572
667
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -574,7 +669,7 @@ function registerSchemaCommand(program) {
|
|
|
574
669
|
const shouldSave = opts.save !== false;
|
|
575
670
|
if (opts.output) {
|
|
576
671
|
writeFileSync(opts.output, prompt, "utf-8");
|
|
577
|
-
console.info(
|
|
672
|
+
console.info(chalk6.green(`\u2713 Prompt written to ${opts.output}`));
|
|
578
673
|
return;
|
|
579
674
|
}
|
|
580
675
|
if (shouldSave) {
|
|
@@ -588,8 +683,8 @@ function registerSchemaCommand(program) {
|
|
|
588
683
|
const filename = join2(pullsDir, `${date}-${hash}.md`);
|
|
589
684
|
writeFileSync(filename, prompt, "utf-8");
|
|
590
685
|
writeFileSync(join2(diffDir, "latest.json"), JSON.stringify(result, null, 2), "utf-8");
|
|
591
|
-
console.info(
|
|
592
|
-
console.info(
|
|
686
|
+
console.info(chalk6.green(`\u2713 Pull prompt written to ${filename}`));
|
|
687
|
+
console.info(chalk6.dim(" Copy-paste its contents into your LLM to resolve the drift."));
|
|
593
688
|
} else {
|
|
594
689
|
process.stdout.write(prompt);
|
|
595
690
|
}
|
|
@@ -604,46 +699,46 @@ var attrInline = (attr) => {
|
|
|
604
699
|
function printDiffOutput(result) {
|
|
605
700
|
const lines = [];
|
|
606
701
|
for (const obj of result.customObjects) {
|
|
607
|
-
lines.push(
|
|
608
|
-
lines.push(
|
|
702
|
+
lines.push(chalk6.yellow(`\u26A0 ${obj.name} (${obj.label}) \u2014 custom object, not in code`));
|
|
703
|
+
lines.push(chalk6.dim(' \u2192 Run "standards pull" to promote or leave.'));
|
|
609
704
|
}
|
|
610
705
|
for (const entry of result.systemObjectDrift) {
|
|
611
706
|
const unexpected = entry.sealed ? entry.customAttributes.filter((a) => !a.tolerated) : [];
|
|
612
707
|
const tolerated = entry.customAttributes.filter((a) => a.tolerated);
|
|
613
708
|
if (entry.sealed && unexpected.length > 0) {
|
|
614
709
|
lines.push(
|
|
615
|
-
|
|
710
|
+
chalk6.red(
|
|
616
711
|
`\u2717 ${entry.objectName} \u2014 ${unexpected.length} unexpected custom attribute(s) (sealed object)`
|
|
617
712
|
)
|
|
618
713
|
);
|
|
619
|
-
for (const attr of unexpected) lines.push(
|
|
620
|
-
lines.push(
|
|
714
|
+
for (const attr of unexpected) lines.push(chalk6.red(attrInline(attr)));
|
|
715
|
+
lines.push(chalk6.dim(' \u2192 Run "standards pull" to promote or tolerate these attributes.'));
|
|
621
716
|
if (tolerated.length > 0) {
|
|
622
|
-
lines.push(
|
|
623
|
-
for (const attr of tolerated) lines.push(
|
|
717
|
+
lines.push(chalk6.dim(` Also tolerated: ${tolerated.length} attribute(s)`));
|
|
718
|
+
for (const attr of tolerated) lines.push(chalk6.dim(attrInline(attr)));
|
|
624
719
|
}
|
|
625
720
|
continue;
|
|
626
721
|
}
|
|
627
722
|
const okAttrs = entry.sealed ? tolerated : entry.customAttributes;
|
|
628
723
|
if (okAttrs.length === 0) continue;
|
|
629
724
|
const okSuffix = entry.sealed ? "tolerated custom attribute(s) (sealed)" : "custom attribute(s) (extensible)";
|
|
630
|
-
lines.push(
|
|
631
|
-
for (const attr of okAttrs) lines.push(
|
|
725
|
+
lines.push(chalk6.green(`\u2713 ${entry.objectName} \u2014 ${okAttrs.length} ${okSuffix}`));
|
|
726
|
+
for (const attr of okAttrs) lines.push(chalk6.dim(attrInline(attr)));
|
|
632
727
|
}
|
|
633
728
|
if (lines.length === 0) {
|
|
634
|
-
console.info(
|
|
729
|
+
console.info(chalk6.green("\u2713 No drift detected."));
|
|
635
730
|
return;
|
|
636
731
|
}
|
|
637
732
|
console.info(lines.join("\n"));
|
|
638
733
|
if (result.hasUnexpectedDrift) {
|
|
639
734
|
console.info(
|
|
640
|
-
|
|
735
|
+
chalk6.red(
|
|
641
736
|
`
|
|
642
737
|
\u2717 Unexpected drift: ${result.summary.totalUnexpected} attribute(s) on sealed object(s).`
|
|
643
738
|
)
|
|
644
739
|
);
|
|
645
740
|
} else {
|
|
646
|
-
console.info(
|
|
741
|
+
console.info(chalk6.green("\n\u2713 No unexpected drift."));
|
|
647
742
|
}
|
|
648
743
|
}
|
|
649
744
|
function resolveStandardsDir() {
|
|
@@ -758,9 +853,11 @@ function createProgram() {
|
|
|
758
853
|
registerRecordsCommand(program);
|
|
759
854
|
registerSchemaCommand(program);
|
|
760
855
|
registerDocumentsCommand(program);
|
|
856
|
+
registerFoldersCommand(program);
|
|
761
857
|
registerKeysCommand(program);
|
|
762
858
|
registerAuthCommand(program);
|
|
763
859
|
registerAutofillCommand(program);
|
|
860
|
+
registerConnectorsCommand(program);
|
|
764
861
|
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
765
862
|
const raw = program.opts();
|
|
766
863
|
const resolved = await resolveCliConfig({
|
|
@@ -774,7 +871,7 @@ function createProgram() {
|
|
|
774
871
|
program.setOptionValue("tenant", raw.tenant);
|
|
775
872
|
if (!(resolved.apiKey || isPublicCommand(actionCommand))) {
|
|
776
873
|
console.error(
|
|
777
|
-
|
|
874
|
+
chalk7.red(
|
|
778
875
|
`\u2717 Error: No Standards instance configured. Run "standards login" or pass --api-key.`
|
|
779
876
|
)
|
|
780
877
|
);
|
|
@@ -788,12 +885,12 @@ async function runProgram(argv = process.argv) {
|
|
|
788
885
|
await program.parseAsync(argv).catch((error) => {
|
|
789
886
|
if (error instanceof ApiClientError) {
|
|
790
887
|
if (error.statusCode > 0) {
|
|
791
|
-
console.error(
|
|
888
|
+
console.error(chalk7.red(`\u2717 Error (${error.statusCode}): ${error.message}`));
|
|
792
889
|
} else {
|
|
793
|
-
console.error(
|
|
890
|
+
console.error(chalk7.red(`\u2717 Error: ${error.message}`));
|
|
794
891
|
}
|
|
795
892
|
} else if (error instanceof Error) {
|
|
796
|
-
console.error(
|
|
893
|
+
console.error(chalk7.red(`\u2717 Error: ${error.message}`));
|
|
797
894
|
}
|
|
798
895
|
process.exit(1);
|
|
799
896
|
});
|