@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.
Files changed (2) hide show
  1. package/dist/bin.mjs +108 -18
  2. 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 memory and skill record of the tenant").action(async (_opts, cmd) => {
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.387",
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.387",
23
- "@standards/constants": "1.0.0-alpha.387",
24
- "@standards/schema": "1.0.0-alpha.387"
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",