@standards/cli 1.0.0-alpha.387 → 1.0.0-alpha.389
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 +108 -18
- package/package.json +4 -4
package/dist/bin.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { createRequire } from "node:module";
|
|
|
4
4
|
import { styleText } from "node:util";
|
|
5
5
|
import { StandardsRequestError, createTransport } from "@standards/client";
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
-
import { DEVICE_CHANNEL_WORKER_CLOSE_PROTOCOL_VIOLATION, RECORDING_OVERRIDES, RECORD_ACCESS_ROLES, RESOURCE_VISIBILITIES, SchemaError, SchemaErrorCode, ValidationError, canonicalStringify, computeReconnectDelay, computeRetryDelay, deviceChannelUrl, diffSchemaState, formatByteSize, parseRecordAccessRole, parseSchemaState, parseServerFrame, schemaStateSourcePayload } from "@standards/schema";
|
|
7
|
+
import { CATALOG_KINDS, CATALOG_LOCALES, COUNT_MODES, DEVICE_CHANNEL_WORKER_CLOSE_PROTOCOL_VIOLATION, MEETING_RESPONSES, RECORDING_OVERRIDES, RECORD_ACCESS_ROLES, RESOURCE_VISIBILITIES, SchemaError, SchemaErrorCode, ValidationError, canonicalStringify, computeReconnectDelay, computeRetryDelay, deviceChannelUrl, diffSchemaState, formatByteSize, parseRecordAccessRole, parseSchemaState, parseServerFrame, schemaStateSourcePayload } from "@standards/schema";
|
|
8
8
|
import Table from "cli-table3";
|
|
9
9
|
import { createAppClient } from "@standards/client/app";
|
|
10
10
|
import { homedir, hostname } from "node:os";
|
|
@@ -94,6 +94,15 @@ function getFormat(cmd) {
|
|
|
94
94
|
function parseCsvList(value) {
|
|
95
95
|
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* The API opts out of totals by default: an absent `countMode` yields no total.
|
|
99
|
+
* Reject unknown modes here so a typo surfaces as an error instead of a
|
|
100
|
+
* silently missing total.
|
|
101
|
+
*/
|
|
102
|
+
function parseCountFlag(count) {
|
|
103
|
+
if (COUNT_MODES.includes(count)) return count;
|
|
104
|
+
throw new ValidationError(`Invalid --count value "${count}". Expected one of: ${COUNT_MODES.join(", ")}.`, []);
|
|
105
|
+
}
|
|
97
106
|
function getClientFromCommand(cmd) {
|
|
98
107
|
const root = getGlobalOptions(cmd);
|
|
99
108
|
if (!(root.apiUrl && root.apiKey)) throw new Error("No Standards instance configured. Run \"standards login\" or pass --api-key.");
|
|
@@ -209,6 +218,77 @@ function registerBundlesCommand(program) {
|
|
|
209
218
|
});
|
|
210
219
|
}
|
|
211
220
|
//#endregion
|
|
221
|
+
//#region src/commands/catalog.ts
|
|
222
|
+
/** Reject a typo instead of letting the API answer a 400 on a bare enum. */
|
|
223
|
+
function parseChoice(flag, value, choices) {
|
|
224
|
+
if (choices.includes(value)) return value;
|
|
225
|
+
throw new ValidationError(`${flag} must be one of: ${choices.join(", ")}`, []);
|
|
226
|
+
}
|
|
227
|
+
/** An empty selection installs nothing, so it is refused before the request. */
|
|
228
|
+
function parseSelection(opts) {
|
|
229
|
+
const selection = {};
|
|
230
|
+
if (opts.entries !== void 0) selection.entries = parseCsvList(opts.entries);
|
|
231
|
+
if (opts.packs !== void 0) selection.packs = parseCsvList(opts.packs);
|
|
232
|
+
if (!(selection.entries?.length || selection.packs?.length)) throw new ValidationError("Select at least one catalog entry (--entries) or pack (--packs)", []);
|
|
233
|
+
return selection;
|
|
234
|
+
}
|
|
235
|
+
/** `<entryId>=bind`, `<entryId>=skip` or `<entryId>=rename:<name>`, one per collision. */
|
|
236
|
+
function collectDecision(value, decisions) {
|
|
237
|
+
const separator = value.indexOf("=");
|
|
238
|
+
const entryId = value.slice(0, separator);
|
|
239
|
+
const choice = value.slice(separator + 1);
|
|
240
|
+
if (separator > 0 && (choice === "bind" || choice === "skip")) return {
|
|
241
|
+
...decisions,
|
|
242
|
+
[entryId]: choice
|
|
243
|
+
};
|
|
244
|
+
if (separator > 0 && choice.startsWith("rename:") && choice.length > 7) return {
|
|
245
|
+
...decisions,
|
|
246
|
+
[entryId]: { rename: choice.slice(7) }
|
|
247
|
+
};
|
|
248
|
+
throw new ValidationError(`--decision must read <entryId>=bind, <entryId>=skip or <entryId>=rename:<name> (received: ${value})`, []);
|
|
249
|
+
}
|
|
250
|
+
/** The flags `preview` and `install` share — both take the same selection. */
|
|
251
|
+
function withSelectionOptions(command) {
|
|
252
|
+
return command.option("--entries <ids>", "comma-separated catalog entry IDs to install").option("--packs <ids>", "comma-separated catalog pack IDs to install").option("--locale <locale>", `language to compile the pieces in (${CATALOG_LOCALES.join(", ")})`, "en").option("--with-samples", "also install the packs' sample records");
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The pre-built catalog: objects, views and agents installed as copies the
|
|
256
|
+
* workspace owns. `preview` returns the plan and its fingerprint; `install`
|
|
257
|
+
* applies that same plan, refused if the workspace changed since.
|
|
258
|
+
*/
|
|
259
|
+
function registerCatalogCommand(program) {
|
|
260
|
+
const catalog = program.command("catalog").description("Browse and install pre-built objects, views and agents");
|
|
261
|
+
catalog.command("list").description("List the catalog entries and packs, and whether each is installed").option("--kind <kind>", `restrict to one kind (${CATALOG_KINDS.join(", ")})`).option("--search <text>", "match labels and descriptions").option("--locale <locale>", `language of the labels (${CATALOG_LOCALES.join(", ")})`, "en").action(async (opts, cmd) => {
|
|
262
|
+
formatOutput(await getAppClientFromCommand(cmd).catalog.list({
|
|
263
|
+
locale: parseChoice("--locale", opts.locale, CATALOG_LOCALES),
|
|
264
|
+
...opts.kind !== void 0 && { kind: parseChoice("--kind", opts.kind, CATALOG_KINDS) },
|
|
265
|
+
...opts.search !== void 0 && { q: opts.search }
|
|
266
|
+
}), getFormat(cmd));
|
|
267
|
+
});
|
|
268
|
+
catalog.command("get").description("Get one catalog entry or pack, with the IDs it pulls in").argument("<id>", "catalog entry or pack ID").option("--locale <locale>", `language of the labels (${CATALOG_LOCALES.join(", ")})`, "en").action(async (id, opts, cmd) => {
|
|
269
|
+
formatOutput(await getAppClientFromCommand(cmd).catalog.read({
|
|
270
|
+
id,
|
|
271
|
+
locale: parseChoice("--locale", opts.locale, CATALOG_LOCALES)
|
|
272
|
+
}), getFormat(cmd));
|
|
273
|
+
});
|
|
274
|
+
withSelectionOptions(catalog.command("preview").description("Plan an install: what is new, what collides, and the plan's fingerprint")).action(async (opts, cmd) => {
|
|
275
|
+
formatOutput(await getAppClientFromCommand(cmd).catalog.preview({
|
|
276
|
+
selection: parseSelection(opts),
|
|
277
|
+
locale: parseChoice("--locale", opts.locale, CATALOG_LOCALES),
|
|
278
|
+
withSamples: opts.withSamples === true
|
|
279
|
+
}), getFormat(cmd));
|
|
280
|
+
});
|
|
281
|
+
withSelectionOptions(catalog.command("install").description("Install a previewed selection as workspace-owned copies").requiredOption("--fingerprint <fingerprint>", "the fingerprint `catalog preview` returned").option("--decision <entryId=choice>", "how to settle a collision: bind, skip or rename:<name> (repeatable)", collectDecision, {})).action(async (opts, cmd) => {
|
|
282
|
+
formatOutput(await getAppClientFromCommand(cmd).catalog.install({
|
|
283
|
+
selection: parseSelection(opts),
|
|
284
|
+
locale: parseChoice("--locale", opts.locale, CATALOG_LOCALES),
|
|
285
|
+
withSamples: opts.withSamples === true,
|
|
286
|
+
decisions: opts.decision,
|
|
287
|
+
fingerprint: opts.fingerprint
|
|
288
|
+
}), getFormat(cmd));
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
212
292
|
//#region src/commands/codemod.ts
|
|
213
293
|
/** Rewrites consumer code for a breaking SDK change. */
|
|
214
294
|
function registerCodemodCommand(program) {
|
|
@@ -1428,7 +1508,7 @@ function registerDocumentsCommand(program) {
|
|
|
1428
1508
|
//#endregion
|
|
1429
1509
|
//#region src/commands/embeddings.ts
|
|
1430
1510
|
function registerEmbeddingsCommand(program) {
|
|
1431
|
-
program.command("embeddings").description("Maintain record embeddings").command("drain").description("Embed every pending
|
|
1511
|
+
program.command("embeddings").description("Maintain record and email embeddings").command("drain").description("Embed every pending record and email of the tenant").action(async (_opts, cmd) => {
|
|
1432
1512
|
const tenant = requireTenant(cmd, "An embedding drain names its tenant explicitly. Pass \"--tenant <id>\".");
|
|
1433
1513
|
await getAppClientFromCommand(cmd).embeddingsAdmin.drain({ tenantId: tenant });
|
|
1434
1514
|
process.stdout.write(`${styleText("green", "✓")} Embedding drain accepted for tenant ${tenant}.\n`);
|
|
@@ -1620,6 +1700,11 @@ function buildQuery(opts) {
|
|
|
1620
1700
|
if (opts.offset !== void 0) query.offset = parseCount("--offset", opts.offset);
|
|
1621
1701
|
return query;
|
|
1622
1702
|
}
|
|
1703
|
+
/** "needs_action" is no answer, so the API takes only the schema's answers. */
|
|
1704
|
+
function parseResponse(response) {
|
|
1705
|
+
if (MEETING_RESPONSES.includes(response)) return response;
|
|
1706
|
+
throw new ValidationError(`response must be one of: ${MEETING_RESPONSES.join(", ")}`, []);
|
|
1707
|
+
}
|
|
1623
1708
|
/** `--auto` clears the override; `--force` / `--skip` set it. Exactly one flag. */
|
|
1624
1709
|
function parseOverride(opts) {
|
|
1625
1710
|
const chosen = RECORDING_OVERRIDES.filter((override) => opts[override]);
|
|
@@ -1639,11 +1724,12 @@ function registerMeetingsCommand(program) {
|
|
|
1639
1724
|
meetings.command("get").description("Get one meeting, with its participants and linked records").argument("<id>", "meeting ID").action(async (id, _opts, cmd) => {
|
|
1640
1725
|
formatOutput(await getAppClientFromCommand(cmd).meetings.get({ meetingId: id }), getFormat(cmd));
|
|
1641
1726
|
});
|
|
1642
|
-
withQueryOptions(meetings.command("for-record").description("List the meetings a record's email addresses appear in").argument("<recordId>", "record ID").option("--email-attributes <ids>", "comma-separated email attribute IDs to match participants on")).action(async (recordId, opts, cmd) => {
|
|
1727
|
+
withQueryOptions(meetings.command("for-record").description("List the meetings a record's email addresses appear in").argument("<recordId>", "record ID").option("--email-attributes <ids>", "comma-separated email attribute IDs to match participants on").option("--count <mode>", "total count mode (estimated, exact); omit for no total")).action(async (recordId, opts, cmd) => {
|
|
1643
1728
|
formatOutput(await getAppClientFromCommand(cmd).meetings.forRecord({
|
|
1644
1729
|
recordId,
|
|
1645
1730
|
...opts.emailAttributes !== void 0 && { emailAttributeIds: parseCsvList(opts.emailAttributes) },
|
|
1646
|
-
query: buildQuery(opts)
|
|
1731
|
+
query: buildQuery(opts),
|
|
1732
|
+
...opts.count !== void 0 && { countMode: parseCountFlag(opts.count) }
|
|
1647
1733
|
}), getFormat(cmd));
|
|
1648
1734
|
});
|
|
1649
1735
|
meetings.command("transcript").description("Read a meeting's completed transcript").argument("<id>", "meeting ID").action(async (id, _opts, cmd) => {
|
|
@@ -1656,6 +1742,16 @@ function registerMeetingsCommand(program) {
|
|
|
1656
1742
|
override
|
|
1657
1743
|
}), getFormat(cmd));
|
|
1658
1744
|
});
|
|
1745
|
+
meetings.command("respond").description("Answer a meeting invitation").argument("<id>", "meeting ID").argument("<response>", `one of: ${MEETING_RESPONSES.join(", ")}`).option("--notify-organizer", "send the answer to the organizer").action(async (id, response, opts, cmd) => {
|
|
1746
|
+
formatOutput(await getAppClientFromCommand(cmd).meetings.respond({
|
|
1747
|
+
meetingId: id,
|
|
1748
|
+
response: parseResponse(response),
|
|
1749
|
+
notifyOrganizer: opts.notifyOrganizer === true
|
|
1750
|
+
}), getFormat(cmd));
|
|
1751
|
+
});
|
|
1752
|
+
meetings.command("stop-recording").description("Stop the recording of a meeting in progress").argument("<id>", "meeting ID").action(async (id, _opts, cmd) => {
|
|
1753
|
+
formatOutput(await getAppClientFromCommand(cmd).meetings.stopRecording({ meetingId: id }), getFormat(cmd));
|
|
1754
|
+
});
|
|
1659
1755
|
meetings.command("grants-recompute").description("Recompute the attendee grants of every private meeting of the tenant").action(async (_opts, cmd) => {
|
|
1660
1756
|
const tenant = requireTenant(cmd, "A meeting grants recompute names its tenant explicitly. Pass \"--tenant <id>\".");
|
|
1661
1757
|
await getAppClientFromCommand(cmd).meetingsAdmin.recomputeGrants({ tenantId: tenant });
|
|
@@ -1726,20 +1822,6 @@ function parseSortFlag(sort) {
|
|
|
1726
1822
|
direction
|
|
1727
1823
|
}];
|
|
1728
1824
|
}
|
|
1729
|
-
const COUNT_MODES = [
|
|
1730
|
-
"none",
|
|
1731
|
-
"estimated",
|
|
1732
|
-
"exact"
|
|
1733
|
-
];
|
|
1734
|
-
/**
|
|
1735
|
-
* The API opts out of totals by default: an absent or unrecognized `countMode`
|
|
1736
|
-
* yields `page.total: null`. Reject unknown modes here so a typo surfaces as an
|
|
1737
|
-
* error instead of a silently missing total.
|
|
1738
|
-
*/
|
|
1739
|
-
function parseCountFlag(count) {
|
|
1740
|
-
if (COUNT_MODES.includes(count)) return count;
|
|
1741
|
-
throw new Error(`Invalid --count value "${count}". Expected one of: ${COUNT_MODES.join(", ")}.`);
|
|
1742
|
-
}
|
|
1743
1825
|
/** Build the shared list/search request body from common query flags. */
|
|
1744
1826
|
function buildQueryBody(opts) {
|
|
1745
1827
|
const body = {};
|
|
@@ -1822,6 +1904,13 @@ function registerRecordsCommand(program) {
|
|
|
1822
1904
|
throw error;
|
|
1823
1905
|
}
|
|
1824
1906
|
});
|
|
1907
|
+
records.command("set-logo").description("Set a record's image to the logo of a company's domain").argument("<object>", "object name (e.g. companies)").argument("<recordId>", "record ID").argument("<domain>", "website or bare domain (e.g. acme.com)").action(async (objectName, recordId, domain, _opts, cmd) => {
|
|
1908
|
+
formatOutput(await getAppClientFromCommand(cmd).records.setLogo({
|
|
1909
|
+
objectName,
|
|
1910
|
+
recordId,
|
|
1911
|
+
domain
|
|
1912
|
+
}), getFormat(cmd));
|
|
1913
|
+
});
|
|
1825
1914
|
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) => {
|
|
1826
1915
|
const fileContent = await readFile(opts.file);
|
|
1827
1916
|
const fileName = basename(opts.file);
|
|
@@ -2012,6 +2101,7 @@ function createProgram() {
|
|
|
2012
2101
|
registerInvitationsCommand(program);
|
|
2013
2102
|
registerAuthCommand(program);
|
|
2014
2103
|
registerBundlesCommand(program);
|
|
2104
|
+
registerCatalogCommand(program);
|
|
2015
2105
|
registerSourcesCommand(program);
|
|
2016
2106
|
registerCodemodCommand(program);
|
|
2017
2107
|
registerConnectorsCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@standards/cli",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.389",
|
|
4
4
|
"description": "CLI tool to interact with Standards API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"commander": "^13.1.0",
|
|
20
20
|
"typescript": "^5.6.3",
|
|
21
21
|
"ws": "8.21.1",
|
|
22
|
-
"@standards/client": "1.0.0-alpha.
|
|
23
|
-
"@standards/constants": "1.0.0-alpha.
|
|
24
|
-
"@standards/schema": "1.0.0-alpha.
|
|
22
|
+
"@standards/client": "1.0.0-alpha.389",
|
|
23
|
+
"@standards/constants": "1.0.0-alpha.389",
|
|
24
|
+
"@standards/schema": "1.0.0-alpha.389"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^25.6.0",
|