primitive-admin 1.1.0-alpha.84 → 1.1.0-alpha.86
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/README.md +77 -1
- package/assets/skill/skills/primitive-platform/SKILL.md +8 -0
- package/dist/src/commands/admins.js +29 -32
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.js +15 -2
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.js +12 -5
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +2 -1
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/catalog.js +26 -10
- package/dist/src/commands/catalog.js.map +1 -1
- package/dist/src/commands/collection-type-configs.js +2 -1
- package/dist/src/commands/collection-type-configs.js.map +1 -1
- package/dist/src/commands/collections.js +675 -19
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/connections.js +8 -9
- package/dist/src/commands/connections.js.map +1 -1
- package/dist/src/commands/cron-triggers.js +2 -1
- package/dist/src/commands/cron-triggers.js.map +1 -1
- package/dist/src/commands/database-type-configs.js +4 -3
- package/dist/src/commands/database-type-configs.js.map +1 -1
- package/dist/src/commands/databases.js +27 -10
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +20 -0
- package/dist/src/commands/documents.js +236 -28
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.js +2 -1
- package/dist/src/commands/email-templates.js.map +1 -1
- package/dist/src/commands/env.js +11 -1
- package/dist/src/commands/env.js.map +1 -1
- package/dist/src/commands/feature-flags.js +2 -1
- package/dist/src/commands/feature-flags.js.map +1 -1
- package/dist/src/commands/functions.js +58 -6
- package/dist/src/commands/functions.js.map +1 -1
- package/dist/src/commands/group-type-configs.js +2 -1
- package/dist/src/commands/group-type-configs.js.map +1 -1
- package/dist/src/commands/groups.js +29 -8
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.js +17 -11
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/integrations.js +30 -9
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/locks.js +2 -1
- package/dist/src/commands/locks.js.map +1 -1
- package/dist/src/commands/metadata-category-configs.js +2 -1
- package/dist/src/commands/metadata-category-configs.js.map +1 -1
- package/dist/src/commands/metadata.js +8 -1
- package/dist/src/commands/metadata.js.map +1 -1
- package/dist/src/commands/prompts.js +28 -11
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.js +2 -1
- package/dist/src/commands/rule-sets.js.map +1 -1
- package/dist/src/commands/scripts.js +15 -6
- package/dist/src/commands/scripts.js.map +1 -1
- package/dist/src/commands/secrets.js +2 -1
- package/dist/src/commands/secrets.js.map +1 -1
- package/dist/src/commands/sessions.js +10 -9
- package/dist/src/commands/sessions.js.map +1 -1
- package/dist/src/commands/sync.d.ts +125 -29
- package/dist/src/commands/sync.js +865 -88
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.js +2 -1
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.js +19 -10
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/vars.js +2 -1
- package/dist/src/commands/vars.js.map +1 -1
- package/dist/src/commands/waitlist.js +2 -1
- package/dist/src/commands/waitlist.js.map +1 -1
- package/dist/src/commands/webhooks.js +17 -10
- package/dist/src/commands/webhooks.js.map +1 -1
- package/dist/src/commands/workflows.js +36 -18
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +176 -8
- package/dist/src/lib/api-client.js +205 -68
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/collection-export.d.ts +184 -0
- package/dist/src/lib/collection-export.js +252 -0
- package/dist/src/lib/collection-export.js.map +1 -0
- package/dist/src/lib/config-object-descriptor.js +44 -8
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/config-payload.d.ts +8 -1
- package/dist/src/lib/config-payload.js +49 -4
- package/dist/src/lib/config-payload.js.map +1 -1
- package/dist/src/lib/config-surface.d.ts +2 -1
- package/dist/src/lib/config-surface.js +54 -3
- package/dist/src/lib/config-surface.js.map +1 -1
- package/dist/src/lib/deprecation.d.ts +22 -0
- package/dist/src/lib/deprecation.js +43 -0
- package/dist/src/lib/deprecation.js.map +1 -0
- package/dist/src/lib/function-db-types.d.ts +14 -3
- package/dist/src/lib/function-db-types.js +55 -11
- package/dist/src/lib/function-db-types.js.map +1 -1
- package/dist/src/lib/function-trigger-listing.d.ts +27 -0
- package/dist/src/lib/function-trigger-listing.js +70 -0
- package/dist/src/lib/function-trigger-listing.js.map +1 -0
- package/dist/src/lib/generated-config-surfaces.d.ts +715 -4
- package/dist/src/lib/generated-config-surfaces.js +2424 -226
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +1 -1
- package/dist/src/lib/generated-sdk-types.js +1 -1
- package/dist/src/lib/generated-sdk-types.js.map +1 -1
- package/dist/src/lib/list-options.d.ts +68 -0
- package/dist/src/lib/list-options.js +89 -0
- package/dist/src/lib/list-options.js.map +1 -0
- package/dist/src/lib/log-inspection.d.ts +34 -1
- package/dist/src/lib/log-inspection.js +28 -0
- package/dist/src/lib/log-inspection.js.map +1 -1
- package/dist/src/lib/paginate.d.ts +15 -0
- package/dist/src/lib/paginate.js +17 -0
- package/dist/src/lib/paginate.js.map +1 -1
- package/dist/src/lib/prompt-cost-format.d.ts +11 -0
- package/dist/src/lib/prompt-cost-format.js +41 -0
- package/dist/src/lib/prompt-cost-format.js.map +1 -0
- package/dist/src/lib/prompt-schema-codegen.d.ts +53 -0
- package/dist/src/lib/prompt-schema-codegen.js +253 -3
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -1
- package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/banners.d.ts +6 -0
- package/dist/src/lib/swift-codegen/banners.js +6 -0
- package/dist/src/lib/swift-codegen/banners.js.map +1 -1
- package/dist/src/lib/swift-codegen/dbGenerator.js +11 -3
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/functionGenerator.d.ts +7 -0
- package/dist/src/lib/swift-codegen/functionGenerator.js +41 -3
- package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/generator.js +8 -2
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/package.json +2 -2
|
@@ -6,6 +6,8 @@ import { resolveAppId } from "../lib/config.js";
|
|
|
6
6
|
import { success, error, info, formatTable, formatId, formatDate, json, keyValue, result as printResult, warn, flushOutput, } from "../lib/output.js";
|
|
7
7
|
import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
8
8
|
import { resolveOwnerUserId } from "../lib/resolve-owner.js";
|
|
9
|
+
import { pageCursorOption, pageLimitOption, parsePageLimit, printEmptyPage, printPageHint, } from "../lib/list-options.js";
|
|
10
|
+
import { normalizeCliListEnvelope, wholeListEnvelope, } from "../lib/paginate.js";
|
|
9
11
|
import { parseDataOption } from "../lib/data-input.js";
|
|
10
12
|
import { parseFilterOptions } from "../lib/record-filter.js";
|
|
11
13
|
import { buildPermissionsExport } from "../lib/document-export-permissions.js";
|
|
@@ -33,6 +35,23 @@ function printIngestSession(session) {
|
|
|
33
35
|
printResult(label, value);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* One refused records call, as one line for an operator (#3764).
|
|
40
|
+
*
|
|
41
|
+
* The same composition `describeBulkFailure` makes for the atomic blob, on the
|
|
42
|
+
* verbs that have no `--json` envelope to fill: the server's sentence, the
|
|
43
|
+
* stable code a script branches on, and the status. `statusCode` FIRST —
|
|
44
|
+
* `cli/src/lib/api-client.ts`'s `ApiError` is where the status lives, and a
|
|
45
|
+
* reader that asks for `status` alone prints nothing at all (#3597 paid for
|
|
46
|
+
* that once already).
|
|
47
|
+
*/
|
|
48
|
+
export function describeRecordsFailure(err) {
|
|
49
|
+
const message = (typeof err?.message === "string" && err.message) || String(err);
|
|
50
|
+
const code = typeof err?.code === "string" && err.code ? err.code : undefined;
|
|
51
|
+
const raw = err?.statusCode ?? err?.status;
|
|
52
|
+
const status = Number.isFinite(Number(raw)) ? Number(raw) : undefined;
|
|
53
|
+
return `${message}${code ? ` [${code}]` : ""}${status !== undefined ? ` (${status})` : ""}`;
|
|
54
|
+
}
|
|
36
55
|
/**
|
|
37
56
|
* Report a document's format (#2816) — but only when it is a large document.
|
|
38
57
|
*
|
|
@@ -47,6 +66,30 @@ function printDocumentFormat(doc) {
|
|
|
47
66
|
keyValue("Format", "2 (large document)");
|
|
48
67
|
}
|
|
49
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The `--document-format` an operator stated, or the refusal (#3764, E10).
|
|
71
|
+
*
|
|
72
|
+
* Refused HERE rather than by the server, because an operator CAN act on it
|
|
73
|
+
* (principle 6) and a round trip to be told what they typed wrong is a round
|
|
74
|
+
* trip they do not need to spend. Commander hands the raw string over, so the
|
|
75
|
+
* digits are what is read; an unset flag states nothing.
|
|
76
|
+
*/
|
|
77
|
+
export function parseDocumentFormatOption(value) {
|
|
78
|
+
if (value === undefined || value === null)
|
|
79
|
+
return undefined;
|
|
80
|
+
const text = String(value).trim();
|
|
81
|
+
if (text === "1")
|
|
82
|
+
return 1;
|
|
83
|
+
if (text === "2")
|
|
84
|
+
return 2;
|
|
85
|
+
throw new Error(`Invalid --document-format "${String(value)}". Expected 1 (an ordinary ` +
|
|
86
|
+
`document) or 2 (a large document).`);
|
|
87
|
+
}
|
|
88
|
+
/** The `--document-format <1|2>` flag, said once for every records verb. */
|
|
89
|
+
const DOCUMENT_FORMAT_FLAG = "--document-format <1|2>";
|
|
90
|
+
const DOCUMENT_FORMAT_HELP = "The document format you expect: 1 (ordinary) or 2 (large). A disagreement " +
|
|
91
|
+
"with the platform's own answer is refused with DOCUMENT_FORMAT_MISMATCH " +
|
|
92
|
+
"rather than answered out of the wrong tables";
|
|
50
93
|
/**
|
|
51
94
|
* What `documents records bulk` reports when the write is refused (#3619).
|
|
52
95
|
*
|
|
@@ -164,18 +207,25 @@ Examples:
|
|
|
164
207
|
.description("List a user's documents")
|
|
165
208
|
.requiredOption("--user-id <id>", "User ID whose documents to list")
|
|
166
209
|
.option("--app <app-id>", "App ID")
|
|
210
|
+
.addOption(pageLimitOption())
|
|
211
|
+
.addOption(pageCursorOption())
|
|
167
212
|
.option("--json", "Output as JSON")
|
|
168
213
|
.action(async (options) => {
|
|
214
|
+
const limit = parsePageLimit(options.limit);
|
|
169
215
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
170
216
|
const client = new ApiClient();
|
|
171
217
|
try {
|
|
172
|
-
const
|
|
218
|
+
const page = normalizeCliListEnvelope(await client.listAdminDocumentsPage(resolvedAppId, options.userId, {
|
|
219
|
+
limit,
|
|
220
|
+
cursor: options.cursor,
|
|
221
|
+
}));
|
|
222
|
+
const list = page.items;
|
|
173
223
|
if (options.json) {
|
|
174
|
-
json(
|
|
224
|
+
json(page);
|
|
175
225
|
return;
|
|
176
226
|
}
|
|
177
227
|
if (list.length === 0) {
|
|
178
|
-
|
|
228
|
+
printEmptyPage(page, "documents");
|
|
179
229
|
return;
|
|
180
230
|
}
|
|
181
231
|
console.log(formatTable(list, [
|
|
@@ -188,6 +238,7 @@ Examples:
|
|
|
188
238
|
{ header: "PERMISSION", key: "permission" },
|
|
189
239
|
{ header: "GRANTED", key: "grantedAt", format: formatDate },
|
|
190
240
|
]));
|
|
241
|
+
printPageHint(page);
|
|
191
242
|
}
|
|
192
243
|
catch (err) {
|
|
193
244
|
error(err.message);
|
|
@@ -378,7 +429,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
378
429
|
const result = await client.listDocumentPermissions(resolvedAppId, documentId);
|
|
379
430
|
const list = Array.isArray(result) ? result : result?.permissions ?? [];
|
|
380
431
|
if (options.json) {
|
|
381
|
-
json(list);
|
|
432
|
+
json(wholeListEnvelope(list));
|
|
382
433
|
return;
|
|
383
434
|
}
|
|
384
435
|
if (list.length === 0) {
|
|
@@ -516,7 +567,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
516
567
|
}
|
|
517
568
|
}
|
|
518
569
|
catch (err) {
|
|
519
|
-
|
|
570
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
571
|
+
// refused records call is scriptable rather than only readable.
|
|
572
|
+
error(describeRecordsFailure(err));
|
|
520
573
|
process.exit(1);
|
|
521
574
|
}
|
|
522
575
|
});
|
|
@@ -567,7 +620,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
567
620
|
]));
|
|
568
621
|
}
|
|
569
622
|
catch (err) {
|
|
570
|
-
|
|
623
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
624
|
+
// refused records call is scriptable rather than only readable.
|
|
625
|
+
error(describeRecordsFailure(err));
|
|
571
626
|
process.exit(1);
|
|
572
627
|
}
|
|
573
628
|
});
|
|
@@ -582,13 +637,24 @@ Permissions (enforced by the server, not the CLI):
|
|
|
582
637
|
.option("--filter-file <path>", "Read filter from a JSON or TOML file")
|
|
583
638
|
.option("--limit <n>", "Maximum number of records to return (max 100)", parseInt)
|
|
584
639
|
.option("--cursor <cursor>", "Pagination cursor from a previous query")
|
|
640
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
585
641
|
.option("--json", "Output as JSON")
|
|
586
642
|
.action(async (documentId, modelName, options) => {
|
|
587
643
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
644
|
+
let documentFormat;
|
|
645
|
+
try {
|
|
646
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
647
|
+
}
|
|
648
|
+
catch (err) {
|
|
649
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
650
|
+
// refused records call is scriptable rather than only readable.
|
|
651
|
+
error(describeRecordsFailure(err));
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
588
654
|
const filter = parseFilterOptions(options);
|
|
589
655
|
const client = new ApiClient();
|
|
590
656
|
try {
|
|
591
|
-
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter, limit: options.limit, cursor: options.cursor });
|
|
657
|
+
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter, limit: options.limit, cursor: options.cursor, documentFormat });
|
|
592
658
|
if (options.json) {
|
|
593
659
|
json(result);
|
|
594
660
|
return;
|
|
@@ -626,7 +692,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
626
692
|
}
|
|
627
693
|
}
|
|
628
694
|
catch (err) {
|
|
629
|
-
|
|
695
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
696
|
+
// refused records call is scriptable rather than only readable.
|
|
697
|
+
error(describeRecordsFailure(err));
|
|
630
698
|
process.exit(1);
|
|
631
699
|
}
|
|
632
700
|
});
|
|
@@ -638,15 +706,26 @@ Permissions (enforced by the server, not the CLI):
|
|
|
638
706
|
.argument("<model-name>", "Model name")
|
|
639
707
|
.argument("<record-id>", "Record ID")
|
|
640
708
|
.option("--app <app-id>", "App ID")
|
|
709
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
641
710
|
.option("--json", "Output as JSON")
|
|
642
711
|
.action(async (documentId, modelName, recordId, options) => {
|
|
643
712
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
713
|
+
let documentFormat;
|
|
714
|
+
try {
|
|
715
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
716
|
+
}
|
|
717
|
+
catch (err) {
|
|
718
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
719
|
+
// refused records call is scriptable rather than only readable.
|
|
720
|
+
error(describeRecordsFailure(err));
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
644
723
|
const client = new ApiClient();
|
|
645
724
|
try {
|
|
646
725
|
// A single-record fetch is a query filtered by primary-key id (there is
|
|
647
726
|
// no dedicated get endpoint — reuse queryDocumentRecords, exactly as
|
|
648
727
|
// the `databases records get` twin reuses queryDatabaseRecords).
|
|
649
|
-
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter: { id: recordId }, limit: 1 });
|
|
728
|
+
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter: { id: recordId }, limit: 1, documentFormat });
|
|
650
729
|
const record = result.items[0];
|
|
651
730
|
if (options.json) {
|
|
652
731
|
json(record ?? null);
|
|
@@ -670,7 +749,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
670
749
|
}
|
|
671
750
|
}
|
|
672
751
|
catch (err) {
|
|
673
|
-
|
|
752
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
753
|
+
// refused records call is scriptable rather than only readable.
|
|
754
|
+
error(describeRecordsFailure(err));
|
|
674
755
|
process.exit(1);
|
|
675
756
|
}
|
|
676
757
|
});
|
|
@@ -682,14 +763,25 @@ Permissions (enforced by the server, not the CLI):
|
|
|
682
763
|
.argument("<model-name>", "Model name to count")
|
|
683
764
|
.option("--app <app-id>", "App ID")
|
|
684
765
|
.option("--filter <json>", "Filter as JSON (e.g. '{\"status\":\"open\"}')")
|
|
766
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
685
767
|
.option("--filter-file <path>", "Read filter from a JSON or TOML file")
|
|
686
768
|
.option("--json", "Output as JSON")
|
|
687
769
|
.action(async (documentId, modelName, options) => {
|
|
688
770
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
771
|
+
let documentFormat;
|
|
772
|
+
try {
|
|
773
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
777
|
+
// refused records call is scriptable rather than only readable.
|
|
778
|
+
error(describeRecordsFailure(err));
|
|
779
|
+
process.exit(1);
|
|
780
|
+
}
|
|
689
781
|
const filter = parseFilterOptions(options);
|
|
690
782
|
const client = new ApiClient();
|
|
691
783
|
try {
|
|
692
|
-
const result = await client.countDocumentRecords(resolvedAppId, documentId, modelName, { filter });
|
|
784
|
+
const result = await client.countDocumentRecords(resolvedAppId, documentId, modelName, { filter, documentFormat });
|
|
693
785
|
if (options.json) {
|
|
694
786
|
json(result);
|
|
695
787
|
return;
|
|
@@ -697,7 +789,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
697
789
|
console.log(` ${result.count}`);
|
|
698
790
|
}
|
|
699
791
|
catch (err) {
|
|
700
|
-
|
|
792
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
793
|
+
// refused records call is scriptable rather than only readable.
|
|
794
|
+
error(describeRecordsFailure(err));
|
|
701
795
|
process.exit(1);
|
|
702
796
|
}
|
|
703
797
|
});
|
|
@@ -717,9 +811,20 @@ Permissions (enforced by the server, not the CLI):
|
|
|
717
811
|
.option("--group-by <field>", "Group results by this plain field (repeatable; StringSet fields are not supported)", (value, previous = []) => previous.concat(value), [])
|
|
718
812
|
.option("--filter <json>", "Filter as JSON")
|
|
719
813
|
.option("--filter-file <path>", "Read filter from a JSON or TOML file")
|
|
814
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
720
815
|
.option("--json", "Output as JSON")
|
|
721
816
|
.action(async (documentId, modelName, options) => {
|
|
722
817
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
818
|
+
let documentFormat;
|
|
819
|
+
try {
|
|
820
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
821
|
+
}
|
|
822
|
+
catch (err) {
|
|
823
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
824
|
+
// refused records call is scriptable rather than only readable.
|
|
825
|
+
error(describeRecordsFailure(err));
|
|
826
|
+
process.exit(1);
|
|
827
|
+
}
|
|
723
828
|
if (!AGGREGATE_OPS.includes(options.op)) {
|
|
724
829
|
error(`Invalid --op "${options.op}". Expected one of: ${AGGREGATE_OPS.join(", ")}.`);
|
|
725
830
|
process.exit(1);
|
|
@@ -742,7 +847,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
742
847
|
const groupBy = options.groupBy || [];
|
|
743
848
|
const client = new ApiClient();
|
|
744
849
|
try {
|
|
745
|
-
const response = await client.aggregateDocumentRecords(resolvedAppId, documentId, modelName, { operations: [operation], groupBy, filter });
|
|
850
|
+
const response = await client.aggregateDocumentRecords(resolvedAppId, documentId, modelName, { operations: [operation], groupBy, filter }, { documentFormat });
|
|
746
851
|
if (options.json) {
|
|
747
852
|
json(response);
|
|
748
853
|
return;
|
|
@@ -779,7 +884,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
779
884
|
}
|
|
780
885
|
}
|
|
781
886
|
catch (err) {
|
|
782
|
-
|
|
887
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
888
|
+
// refused records call is scriptable rather than only readable.
|
|
889
|
+
error(describeRecordsFailure(err));
|
|
783
890
|
process.exit(1);
|
|
784
891
|
}
|
|
785
892
|
});
|
|
@@ -802,15 +909,26 @@ Permissions (enforced by the server, not the CLI):
|
|
|
802
909
|
.option("--data <json>", "Record fields as JSON (e.g. '{\"qty\":10}')")
|
|
803
910
|
.option("--data-file <file>", "Read record fields from a JSON file")
|
|
804
911
|
.option("--upsert-on <field>", "Update the record whose <field> matches instead of creating")
|
|
912
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
805
913
|
.option("--json", "Output as JSON")
|
|
806
914
|
.action(async (documentId, modelName, options) => {
|
|
807
915
|
const data = parseDataOption(options, "record fields");
|
|
808
916
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
917
|
+
let documentFormat;
|
|
918
|
+
try {
|
|
919
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
920
|
+
}
|
|
921
|
+
catch (err) {
|
|
922
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
923
|
+
// refused records call is scriptable rather than only readable.
|
|
924
|
+
error(describeRecordsFailure(err));
|
|
925
|
+
process.exit(1);
|
|
926
|
+
}
|
|
809
927
|
const recordId = options.id || ulid();
|
|
810
928
|
const writeOptions = options.upsertOn ? { upsertOn: options.upsertOn } : undefined;
|
|
811
929
|
const client = new ApiClient();
|
|
812
930
|
try {
|
|
813
|
-
const result = await client.saveDocumentRecord(resolvedAppId, documentId, modelName, { id: recordId, data, ...(writeOptions ? { options: writeOptions } : {}) });
|
|
931
|
+
const result = await client.saveDocumentRecord(resolvedAppId, documentId, modelName, { id: recordId, data, ...(writeOptions ? { options: writeOptions } : {}) }, { documentFormat });
|
|
814
932
|
if (options.json) {
|
|
815
933
|
json(result);
|
|
816
934
|
return;
|
|
@@ -818,7 +936,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
818
936
|
success(`Record saved: ${result.record?.id ?? recordId}`);
|
|
819
937
|
}
|
|
820
938
|
catch (err) {
|
|
821
|
-
|
|
939
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
940
|
+
// refused records call is scriptable rather than only readable.
|
|
941
|
+
error(describeRecordsFailure(err));
|
|
822
942
|
process.exit(1);
|
|
823
943
|
}
|
|
824
944
|
});
|
|
@@ -831,14 +951,25 @@ Permissions (enforced by the server, not the CLI):
|
|
|
831
951
|
.argument("<record-id>", "Record ID to patch")
|
|
832
952
|
.option("--app <app-id>", "App ID")
|
|
833
953
|
.option("--data <json>", "Fields to merge as JSON (e.g. '{\"status\":\"closed\"}')")
|
|
954
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
834
955
|
.option("--data-file <file>", "Read fields from a JSON file")
|
|
835
956
|
.option("--json", "Output as JSON")
|
|
836
957
|
.action(async (documentId, modelName, recordId, options) => {
|
|
837
958
|
const data = parseDataOption(options, "fields to merge");
|
|
838
959
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
960
|
+
let documentFormat;
|
|
961
|
+
try {
|
|
962
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
963
|
+
}
|
|
964
|
+
catch (err) {
|
|
965
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
966
|
+
// refused records call is scriptable rather than only readable.
|
|
967
|
+
error(describeRecordsFailure(err));
|
|
968
|
+
process.exit(1);
|
|
969
|
+
}
|
|
839
970
|
const client = new ApiClient();
|
|
840
971
|
try {
|
|
841
|
-
const result = await client.patchDocumentRecord(resolvedAppId, documentId, modelName, recordId, { data });
|
|
972
|
+
const result = await client.patchDocumentRecord(resolvedAppId, documentId, modelName, recordId, { data }, { documentFormat });
|
|
842
973
|
if (options.json) {
|
|
843
974
|
json(result);
|
|
844
975
|
return;
|
|
@@ -846,7 +977,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
846
977
|
success(`Record patched: ${recordId}`);
|
|
847
978
|
}
|
|
848
979
|
catch (err) {
|
|
849
|
-
|
|
980
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
981
|
+
// refused records call is scriptable rather than only readable.
|
|
982
|
+
error(describeRecordsFailure(err));
|
|
850
983
|
process.exit(1);
|
|
851
984
|
}
|
|
852
985
|
});
|
|
@@ -858,10 +991,21 @@ Permissions (enforced by the server, not the CLI):
|
|
|
858
991
|
.argument("<model-name>", "Model name")
|
|
859
992
|
.argument("<record-id>", "Record ID to delete")
|
|
860
993
|
.option("--app <app-id>", "App ID")
|
|
994
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
861
995
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
862
996
|
.option("--json", "Output as JSON")
|
|
863
997
|
.action(async (documentId, modelName, recordId, options) => {
|
|
864
998
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
999
|
+
let documentFormat;
|
|
1000
|
+
try {
|
|
1001
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
1002
|
+
}
|
|
1003
|
+
catch (err) {
|
|
1004
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
1005
|
+
// refused records call is scriptable rather than only readable.
|
|
1006
|
+
error(describeRecordsFailure(err));
|
|
1007
|
+
process.exit(1);
|
|
1008
|
+
}
|
|
865
1009
|
if (!options.yes) {
|
|
866
1010
|
const confirmed = await confirmPrompt(`Delete record ${recordId} from ${modelName} in document ${documentId}?`);
|
|
867
1011
|
if (!confirmed) {
|
|
@@ -871,7 +1015,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
871
1015
|
}
|
|
872
1016
|
const client = new ApiClient();
|
|
873
1017
|
try {
|
|
874
|
-
const result = await client.deleteDocumentRecord(resolvedAppId, documentId, modelName, recordId);
|
|
1018
|
+
const result = await client.deleteDocumentRecord(resolvedAppId, documentId, modelName, recordId, { documentFormat });
|
|
875
1019
|
if (options.json) {
|
|
876
1020
|
json(result);
|
|
877
1021
|
return;
|
|
@@ -881,7 +1025,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
881
1025
|
success(`Record deleted: ${recordId}`);
|
|
882
1026
|
}
|
|
883
1027
|
catch (err) {
|
|
884
|
-
|
|
1028
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
1029
|
+
// refused records call is scriptable rather than only readable.
|
|
1030
|
+
error(describeRecordsFailure(err));
|
|
885
1031
|
process.exit(1);
|
|
886
1032
|
}
|
|
887
1033
|
});
|
|
@@ -896,9 +1042,20 @@ Permissions (enforced by the server, not the CLI):
|
|
|
896
1042
|
"create/patch, not allowed on delete; a create `id` must be a 26-char " +
|
|
897
1043
|
"uppercase Crockford ULID")
|
|
898
1044
|
.option("--app <app-id>", "App ID")
|
|
1045
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
899
1046
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
900
1047
|
.option("--json", "Output as JSON")
|
|
901
1048
|
.action(async (documentId, options) => {
|
|
1049
|
+
let documentFormat;
|
|
1050
|
+
try {
|
|
1051
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
1052
|
+
}
|
|
1053
|
+
catch (err) {
|
|
1054
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
1055
|
+
// refused records call is scriptable rather than only readable.
|
|
1056
|
+
error(describeRecordsFailure(err));
|
|
1057
|
+
process.exit(1);
|
|
1058
|
+
}
|
|
902
1059
|
const parsed = parseDataOption(options, "operations blob", {
|
|
903
1060
|
allowArray: true,
|
|
904
1061
|
});
|
|
@@ -917,7 +1074,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
917
1074
|
}
|
|
918
1075
|
const client = new ApiClient();
|
|
919
1076
|
try {
|
|
920
|
-
const result = await client.bulkDocumentRecords(resolvedAppId, documentId, operations);
|
|
1077
|
+
const result = await client.bulkDocumentRecords(resolvedAppId, documentId, operations, { documentFormat });
|
|
921
1078
|
if (options.json) {
|
|
922
1079
|
json(result);
|
|
923
1080
|
return;
|
|
@@ -1152,13 +1309,14 @@ Permissions (enforced by the server, not the CLI):
|
|
|
1152
1309
|
.option("--cursor <cursor>", "Continue from a previous page's nextCursor")
|
|
1153
1310
|
.option("--json", "Output as JSON")
|
|
1154
1311
|
.action(async (documentId, options) => {
|
|
1312
|
+
const limit = parsePageLimit(options.limit);
|
|
1155
1313
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1156
1314
|
const client = new ApiClient();
|
|
1157
1315
|
try {
|
|
1158
|
-
const page = await client.listDocumentIngests(resolvedAppId, documentId, {
|
|
1159
|
-
limit
|
|
1316
|
+
const page = normalizeCliListEnvelope(await client.listDocumentIngests(resolvedAppId, documentId, {
|
|
1317
|
+
limit,
|
|
1160
1318
|
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
1161
|
-
});
|
|
1319
|
+
}));
|
|
1162
1320
|
if (options.json) {
|
|
1163
1321
|
json(page);
|
|
1164
1322
|
return;
|
|
@@ -1226,13 +1384,14 @@ Permissions (enforced by the server, not the CLI):
|
|
|
1226
1384
|
.option("--cursor <cursor>", "Continue from a previous page's nextCursor")
|
|
1227
1385
|
.option("--json", "Output as JSON")
|
|
1228
1386
|
.action(async (documentId, options) => {
|
|
1387
|
+
const limit = parsePageLimit(options.limit);
|
|
1229
1388
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1230
1389
|
const client = new ApiClient();
|
|
1231
1390
|
try {
|
|
1232
|
-
const page = await client.listDocumentSnapshots(resolvedAppId, documentId, {
|
|
1233
|
-
limit
|
|
1391
|
+
const page = normalizeCliListEnvelope(await client.listDocumentSnapshots(resolvedAppId, documentId, {
|
|
1392
|
+
limit,
|
|
1234
1393
|
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
1235
|
-
});
|
|
1394
|
+
}));
|
|
1236
1395
|
if (options.json) {
|
|
1237
1396
|
json(page);
|
|
1238
1397
|
return;
|
|
@@ -1540,7 +1699,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
1540
1699
|
const result = await client.listDocumentGroupPermissions(resolvedAppId, documentId);
|
|
1541
1700
|
const list = Array.isArray(result) ? result : result?.permissions ?? [];
|
|
1542
1701
|
if (options.json) {
|
|
1543
|
-
json(list);
|
|
1702
|
+
json(wholeListEnvelope(list));
|
|
1544
1703
|
return;
|
|
1545
1704
|
}
|
|
1546
1705
|
if (list.length === 0) {
|
|
@@ -1589,6 +1748,55 @@ Permissions (enforced by the server, not the CLI):
|
|
|
1589
1748
|
process.exit(1);
|
|
1590
1749
|
}
|
|
1591
1750
|
});
|
|
1751
|
+
// ---- Effective access for a named user (#3658) ----
|
|
1752
|
+
const access = documents
|
|
1753
|
+
.command("access")
|
|
1754
|
+
.description("Read a user's effective access to a document");
|
|
1755
|
+
access
|
|
1756
|
+
.command("get")
|
|
1757
|
+
.description("Show what a user may do with a document, across direct, group, " +
|
|
1758
|
+
"collection and link access")
|
|
1759
|
+
.argument("<document-id>", "Document ID")
|
|
1760
|
+
.requiredOption("--user <user-id>", "The user whose access to resolve")
|
|
1761
|
+
.option("--app <app-id>", "App ID")
|
|
1762
|
+
.option("--json", "Output as JSON")
|
|
1763
|
+
.action(async (documentId, options) => {
|
|
1764
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1765
|
+
const client = new ApiClient();
|
|
1766
|
+
try {
|
|
1767
|
+
const result = await client.validateDocumentAccessForUser(resolvedAppId, documentId, options.user);
|
|
1768
|
+
const row = {
|
|
1769
|
+
documentId,
|
|
1770
|
+
userId: options.user,
|
|
1771
|
+
hasAccess: result?.hasAccess === true,
|
|
1772
|
+
permission: result?.permission ?? null,
|
|
1773
|
+
accessSource: result?.accessSource ?? null,
|
|
1774
|
+
appRole: result?.appRole ?? null,
|
|
1775
|
+
};
|
|
1776
|
+
if (options.json) {
|
|
1777
|
+
json(row);
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
console.log(formatTable([row], [
|
|
1781
|
+
{ header: "DOCUMENT", key: "documentId" },
|
|
1782
|
+
{ header: "USER", key: "userId" },
|
|
1783
|
+
{ header: "ACCESS", key: "hasAccess" },
|
|
1784
|
+
{ header: "PERMISSION", key: "permission" },
|
|
1785
|
+
{ header: "SOURCE", key: "accessSource" },
|
|
1786
|
+
{ header: "APP_ROLE", key: "appRole" },
|
|
1787
|
+
]));
|
|
1788
|
+
if (!row.hasAccess) {
|
|
1789
|
+
// The role is not a grant: only the tag routes admit an
|
|
1790
|
+
// administrator without one, so an operator reading "admin" here
|
|
1791
|
+
// should not conclude the user may write content.
|
|
1792
|
+
info("No document grant. An app role admits tag changes only, never a content write.");
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
catch (err) {
|
|
1796
|
+
error(err.message);
|
|
1797
|
+
process.exit(1);
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1592
1800
|
// ---- Link access ("anyone with the link") ----
|
|
1593
1801
|
const linkAccess = documents
|
|
1594
1802
|
.command("link-access")
|