@standards/cli 1.0.0-alpha.442 → 1.0.0-alpha.444
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 +171 -36
- package/guide/core.md +2 -2
- package/guide/schema-and-bundles.md +21 -7
- package/package.json +4 -4
package/dist/bin.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { t as discoverBundles } from "./discover-bundles-DPpEZVv0.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { styleText } from "node:util";
|
|
6
|
-
import { BundleConflictError, StandardsRequestError, createTransport, syncBundle } from "@standards/client";
|
|
6
|
+
import { BundleConflictError, StandardsRequestError, createTransport, previewBundleSync, syncBundle } from "@standards/client";
|
|
7
7
|
import { Command, Option } from "commander";
|
|
8
8
|
import { BUNDLE_LOCALES, COUNT_MODES, DECISION_TYPE_AGENT_SETUP, DECISION_TYPE_BUNDLE_SETUP, ENVIRONMENT_HARNESSES, ENVIRONMENT_IMAGES, ENVIRONMENT_SIZES, LINK_MAX_MS, NotFoundError, PROVIDER_NAMES, RECORD_ACCESS_ROLES, RESOURCE_VISIBILITIES, SchemaError, SchemaErrorCode, ValidationError, canonicalStringify, diffSchemaState, environmentHarness, formatByteSize, formatDecisionLink, isEnvironmentHarnessId, isPlainRecord, isSkillRepository, parseRecordAccessRole, parseSchemaState, parseSkillSource, schemaStateSourcePayload } from "@standards/schema";
|
|
9
9
|
import { createWriteStream } from "node:fs";
|
|
@@ -287,13 +287,107 @@ async function run(body) {
|
|
|
287
287
|
process.exit(1);
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
|
-
|
|
290
|
+
/**
|
|
291
|
+
* A refused confirmation carries no counts, and a bundle sync's refusal does
|
|
292
|
+
* not even keep its details, so the plan is previewed again: when its
|
|
293
|
+
* fingerprint or its record total is no longer what was sent, the plan is
|
|
294
|
+
* stale and the message names the total to confirm. Anything else, or a
|
|
295
|
+
* caller who cannot count, gets the refusal as the server sent it.
|
|
296
|
+
*/
|
|
297
|
+
async function refuseStale(error, bundle, sent, currentPlan) {
|
|
298
|
+
if (!(error instanceof StandardsRequestError) || error.status !== 400) throw error;
|
|
299
|
+
const current = await currentPlan().catch(() => void 0);
|
|
300
|
+
if (current === void 0) throw error;
|
|
301
|
+
const { fingerprint, total } = current;
|
|
302
|
+
if (!(fingerprint !== sent.fingerprint || sent.confirmedRecords !== void 0 && total !== sent.confirmedRecords) || total === void 0) throw error;
|
|
303
|
+
throw new Error(`${messageOf(error)}\n${bundle}: ${total} records`);
|
|
304
|
+
}
|
|
305
|
+
/** Every record the removal of a bundle deletes. */
|
|
306
|
+
function removalTotal(preview) {
|
|
307
|
+
return preview.objects.reduce((total, object) => total + object.records, 0);
|
|
308
|
+
}
|
|
309
|
+
/** `<bundle>=<value>`, one per bundle, with a message naming the flag when it is malformed. */
|
|
310
|
+
function parseKeyed(flag, shape, value) {
|
|
311
|
+
const separator = value.indexOf("=");
|
|
312
|
+
if (separator > 0 && separator < value.length - 1) return [value.slice(0, separator), value.slice(separator + 1)];
|
|
313
|
+
throw new ValidationError(`${flag} must read ${shape} (received: ${value})`, []);
|
|
314
|
+
}
|
|
315
|
+
function collectFingerprint(value, fingerprints) {
|
|
316
|
+
const [bundle, fingerprint] = parseKeyed("--fingerprint", "<bundle>=<fingerprint>", value);
|
|
317
|
+
return {
|
|
318
|
+
...fingerprints,
|
|
319
|
+
[bundle]: fingerprint
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function collectConfirmRecords(value, confirmations) {
|
|
323
|
+
const [bundle, count] = parseKeyed("--confirm-records", "<bundle>=<n>", value);
|
|
324
|
+
if (!/^\d+$/.test(count)) throw new ValidationError(`--confirm-records must read <bundle>=<n> with n a whole number (received: ${value})`, []);
|
|
325
|
+
return {
|
|
326
|
+
...confirmations,
|
|
327
|
+
[bundle]: Number(count)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
/** The records confirmed for the one bundle a command acts on; another bundle's confirmation is refused. */
|
|
331
|
+
function confirmedFor(name, confirmations, { required }) {
|
|
332
|
+
const others = Object.keys(confirmations).filter((bundle) => bundle !== name);
|
|
333
|
+
const confirmed = confirmations[name];
|
|
334
|
+
if (others.length === 0 && (confirmed !== void 0 || !required)) return confirmed;
|
|
335
|
+
throw new ValidationError(`Pass --confirm-records ${name}=<n> with the records "${name}" removes: run "bundles ${required ? "remove" : "update"} ${name} --dry-run" first.`, []);
|
|
336
|
+
}
|
|
337
|
+
/** Each confirmed bundle needs the fingerprint of its own sync preview, and the bundle must be one being synced. */
|
|
338
|
+
function checkSyncConfirmations(names, opts) {
|
|
339
|
+
for (const bundle of Object.keys(opts.confirmRecords)) {
|
|
340
|
+
if (!names.includes(bundle)) throw new ValidationError(`--confirm-records names "${bundle}", which is not a discovered bundle`, []);
|
|
341
|
+
if (opts.fingerprint[bundle] === void 0) throw new ValidationError(`Pass --fingerprint ${bundle}=<fingerprint> with --confirm-records ${bundle}=<n>: run "bundles sync --dry-run" first.`, []);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function pendingRemovalsLine(bundle, removals) {
|
|
345
|
+
if (removals.length === 0) return void 0;
|
|
346
|
+
const records = removals.reduce((total, removal) => total + (removal.records ?? 0), 0);
|
|
347
|
+
const counted = removals.some((removal) => removal.records !== void 0);
|
|
348
|
+
return `${bundle}: ${removals.length} removal${removals.length === 1 ? "" : "s"} held${counted ? `, ${records} records` : ""} (run with --dry-run, then --fingerprint and --confirm-records)`;
|
|
349
|
+
}
|
|
350
|
+
async function syncDiscovered(cmd, opts) {
|
|
291
351
|
const transport = getClientFromCommand(cmd);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
352
|
+
const ordered = await orderByUses(await discoverBundles({ cwd: opts.cwd }));
|
|
353
|
+
checkSyncConfirmations(ordered.map(({ bundle }) => bundle.name), opts);
|
|
354
|
+
if (opts.dryRun) {
|
|
355
|
+
const plans = [];
|
|
356
|
+
for (const { bundle } of ordered) try {
|
|
357
|
+
plans.push({
|
|
358
|
+
bundle: bundle.name,
|
|
359
|
+
...await previewBundleSync(transport, bundle)
|
|
360
|
+
});
|
|
361
|
+
} catch (error) {
|
|
362
|
+
throw new Error(describeFailure(bundle.name, error));
|
|
363
|
+
}
|
|
364
|
+
formatOutput(plans, getFormat(cmd));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
for (const { bundle } of ordered) {
|
|
368
|
+
const confirmedRecords = opts.confirmRecords[bundle.name];
|
|
369
|
+
const fingerprint = opts.fingerprint[bundle.name];
|
|
370
|
+
try {
|
|
371
|
+
const { applied, hash, pendingRemovals } = await syncBundle(transport, bundle, { ...confirmedRecords === void 0 || fingerprint === void 0 ? {} : {
|
|
372
|
+
fingerprint,
|
|
373
|
+
confirmedRecords
|
|
374
|
+
} });
|
|
375
|
+
console.info(`${bundle.name} ${applied ? "applied" : "unchanged"} ${shortHash(hash)}`);
|
|
376
|
+
const held = pendingRemovalsLine(bundle.name, pendingRemovals);
|
|
377
|
+
if (held) console.info(held);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
const refused = confirmedRecords === void 0 || fingerprint === void 0 ? error : await refuseStale(error, bundle.name, {
|
|
380
|
+
fingerprint,
|
|
381
|
+
confirmedRecords
|
|
382
|
+
}, async () => {
|
|
383
|
+
const { fingerprint: current, totalRecords } = await previewBundleSync(transport, bundle);
|
|
384
|
+
return {
|
|
385
|
+
fingerprint: current,
|
|
386
|
+
total: totalRecords
|
|
387
|
+
};
|
|
388
|
+
}).catch((stale) => stale);
|
|
389
|
+
throw new Error(describeFailure(bundle.name, refused));
|
|
390
|
+
}
|
|
297
391
|
}
|
|
298
392
|
}
|
|
299
393
|
/** Reject a locale typo instead of letting the API answer a 400 on a bare enum. */
|
|
@@ -350,7 +444,7 @@ function withAddOptions(command) {
|
|
|
350
444
|
*/
|
|
351
445
|
function registerBundlesCommand(program) {
|
|
352
446
|
const bundles = program.command("bundles").description("Sync and manage the workspace's bundles");
|
|
353
|
-
bundles.command("sync").description("Discover the project's bundles (standards/bundles/*.ts) and sync each one").option("--cwd <dir>", "project root to discover bundles in").action((opts, cmd) => run(() => syncDiscovered(cmd, opts
|
|
447
|
+
bundles.command("sync").description("Discover the project's bundles (standards/bundles/*.ts) and sync each one; a removal is held until confirmed").option("--cwd <dir>", "project root to discover bundles in").option("--dry-run", "print each bundle's pieces, the removals it would make with their record counts, and its fingerprint").option("--fingerprint <bundle=fingerprint>", "the fingerprint `bundles sync --dry-run` printed for a bundle (repeatable)", collectFingerprint, {}).option("--confirm-records <bundle=n>", "apply the removals a bundle's sync holds, confirming the records they delete (repeatable); without it they are held", collectConfirmRecords, {}).action((opts, cmd) => run(() => syncDiscovered(cmd, opts)));
|
|
354
448
|
bundles.command("list").description("List the bundles installed in the workspace").action((_opts, cmd) => run(async () => {
|
|
355
449
|
const { installed } = await getAppClientFromCommand(cmd).bundles.list({});
|
|
356
450
|
formatOutput(installed.map((row) => ({
|
|
@@ -379,7 +473,7 @@ function registerBundlesCommand(program) {
|
|
|
379
473
|
}), getFormat(cmd));
|
|
380
474
|
});
|
|
381
475
|
});
|
|
382
|
-
bundles.command("update").description("Update an added bundle as its plan showed;
|
|
476
|
+
bundles.command("update").description("Update an added bundle as its plan showed; a piece the plan removes goes with its records only when --confirm-records matches (--dry-run prints the plan)").argument("<name>", "bundle name").option("--dry-run", "print the update plan, piece by piece, and its fingerprint").option("--fingerprint <fingerprint>", "the fingerprint `bundles update --dry-run` printed").option("--confirm-records <bundle=n>", "the records the plan's removals delete, as `--dry-run` counted them (only when it removes)", collectConfirmRecords, {}).option("--take <pieceId>", "take the new version of a piece you changed (repeatable)", collectPieceId, []).option("--skip-missing <pieceId>", "do not reinstall a missing piece (repeatable)", collectPieceId, []).option("--decision <pieceId=choice>", "how to settle a collision: bind, skip or rename:<name> (repeatable)", collectDecision, {}).action((name, opts, cmd) => {
|
|
383
477
|
const fingerprint = opts.dryRun ? void 0 : requireFingerprint(opts.fingerprint, `bundles update ${name} --dry-run`);
|
|
384
478
|
return run(async () => {
|
|
385
479
|
const client = getAppClientFromCommand(cmd);
|
|
@@ -387,20 +481,60 @@ function registerBundlesCommand(program) {
|
|
|
387
481
|
formatOutput(await client.bundles.updatePreview({ name }), getFormat(cmd));
|
|
388
482
|
return;
|
|
389
483
|
}
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
484
|
+
const confirmedRecords = confirmedFor(name, opts.confirmRecords, { required: false });
|
|
485
|
+
try {
|
|
486
|
+
const { applied } = await client.bundles.update({
|
|
487
|
+
name,
|
|
488
|
+
fingerprint,
|
|
489
|
+
take: opts.take,
|
|
490
|
+
skipMissing: opts.skipMissing,
|
|
491
|
+
decisions: opts.decision,
|
|
492
|
+
...confirmedRecords === void 0 ? {} : { confirmedRecords }
|
|
493
|
+
});
|
|
494
|
+
console.info(`${name} updated, ${applied} applied`);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
await refuseStale(error, name, {
|
|
497
|
+
fingerprint,
|
|
498
|
+
confirmedRecords
|
|
499
|
+
}, async () => {
|
|
500
|
+
const { fingerprint: current, totalRecords } = await client.bundles.updatePreview({ name });
|
|
501
|
+
return {
|
|
502
|
+
fingerprint: current,
|
|
503
|
+
total: totalRecords
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
}
|
|
398
507
|
});
|
|
399
508
|
});
|
|
400
|
-
bundles.command("remove").description("Remove a synced bundle
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
509
|
+
bundles.command("remove").description("Remove a synced bundle: its objects are deleted with their records, only when --confirm-records matches the count (--dry-run prints it). An added bundle is removed the same way").argument("<name>", "bundle name").option("--dry-run", "print what the removal deletes, its record counts and the fingerprint").option("--fingerprint <fingerprint>", "the fingerprint `bundles remove --dry-run` printed").option("--confirm-records <bundle=n>", "the records the removal deletes, as `--dry-run` counted them", collectConfirmRecords, {}).action((name, opts, cmd) => run(async () => {
|
|
510
|
+
const client = getAppClientFromCommand(cmd);
|
|
511
|
+
if (opts.dryRun) {
|
|
512
|
+
const preview = await client.bundles.removalPreview({ name });
|
|
513
|
+
formatOutput({
|
|
514
|
+
...preview,
|
|
515
|
+
totalRecords: removalTotal(preview)
|
|
516
|
+
}, getFormat(cmd));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const confirmation = {
|
|
520
|
+
confirmedRecords: confirmedFor(name, opts.confirmRecords, { required: true }),
|
|
521
|
+
fingerprint: requireFingerprint(opts.fingerprint, `bundles remove ${name} --dry-run`)
|
|
522
|
+
};
|
|
523
|
+
try {
|
|
524
|
+
await client.bundles.remove({
|
|
525
|
+
name,
|
|
526
|
+
...confirmation
|
|
527
|
+
});
|
|
528
|
+
console.info(`${name} removed`);
|
|
529
|
+
} catch (error) {
|
|
530
|
+
await refuseStale(error, name, confirmation, async () => {
|
|
531
|
+
const preview = await client.bundles.removalPreview({ name });
|
|
532
|
+
return {
|
|
533
|
+
fingerprint: preview.fingerprint,
|
|
534
|
+
total: removalTotal(preview)
|
|
535
|
+
};
|
|
536
|
+
});
|
|
537
|
+
}
|
|
404
538
|
}));
|
|
405
539
|
}
|
|
406
540
|
// src/commands/connectors.ts
|
|
@@ -412,10 +546,7 @@ const PROVIDERS = Object.keys({
|
|
|
412
546
|
"google-calendar": true,
|
|
413
547
|
"outlook-calendar": true
|
|
414
548
|
});
|
|
415
|
-
const CAPABILITIES = Object.keys({
|
|
416
|
-
modify: true,
|
|
417
|
-
send: true
|
|
418
|
-
});
|
|
549
|
+
const CAPABILITIES = Object.keys({ modify: true });
|
|
419
550
|
const MAIL_PROVIDERS = ["gmail", "outlook"];
|
|
420
551
|
const CALENDAR_PROVIDERS = ["google-calendar", "outlook-calendar"];
|
|
421
552
|
/**
|
|
@@ -424,14 +555,9 @@ const CALENDAR_PROVIDERS = ["google-calendar", "outlook-calendar"];
|
|
|
424
555
|
*/
|
|
425
556
|
function connectDisclosure(provider, capabilities = []) {
|
|
426
557
|
const modify = capabilities.includes("modify");
|
|
427
|
-
const send = capabilities.includes("send");
|
|
428
558
|
const lines = ["Standards will be able to:"];
|
|
429
|
-
if (MAIL_PROVIDERS.includes(provider))
|
|
430
|
-
|
|
431
|
-
if (modify || send && provider === "outlook") lines.push("- change your emails");
|
|
432
|
-
if (send) lines.push("- send emails from this address");
|
|
433
|
-
lines.push("It shows them in the workspace, links them to records and makes them searchable.", "AI providers process your email automatically: new emails are classified, your email history is indexed for search by meaning, and important exchanges feed memory. The default assistant can read this mailbox, and AI providers also process your email whenever an agent works on it.");
|
|
434
|
-
} else if (CALENDAR_PROVIDERS.includes(provider)) {
|
|
559
|
+
if (MAIL_PROVIDERS.includes(provider)) lines.push("- read your emails and their attachments", "- change your emails", "- send emails from this address", "It shows them in the workspace, links them to records and makes them searchable.", "AI providers process your email automatically: new emails are classified, your email history is indexed for search by meaning, and important exchanges feed memory. The default assistant can read this mailbox, and AI providers also process your email whenever an agent works on it.");
|
|
560
|
+
else if (CALENDAR_PROVIDERS.includes(provider)) {
|
|
435
561
|
lines.push("- read your events, their guests and their details");
|
|
436
562
|
if (modify) lines.push("- create, change and delete your events");
|
|
437
563
|
lines.push("It shows them in the workspace, links them to records and makes them searchable.", "The default assistant can read this calendar, and AI providers process your events whenever an agent works on them.");
|
|
@@ -458,20 +584,29 @@ function registerConnectorsCommand(program) {
|
|
|
458
584
|
connectors.command("list").description("List connector connections").option("--scope <scope>", "connection scope (tenant or actor)", "tenant").action(async (opts, cmd) => {
|
|
459
585
|
formatOutput(await getAppClientFromCommand(cmd).connectors.list({ scope: resolveScope(opts.scope) }), getFormat(cmd));
|
|
460
586
|
});
|
|
461
|
-
connectors.command("connect").description("Check a provider and capabilities; the account itself is connected from the app").requiredOption("--provider <provider>", `connector provider (${PROVIDERS.join(", ")}); run "connectors providers" for the ones this deployment configured`).option("--scope <scope>", "connection scope (tenant or actor)", "tenant").option("--capabilities <list>", `
|
|
587
|
+
connectors.command("connect").description("Check a provider and capabilities; the account itself is connected from the app").requiredOption("--provider <provider>", `connector provider (${PROVIDERS.join(", ")}); run "connectors providers" for the ones this deployment configured`).option("--scope <scope>", "connection scope (tenant or actor)", "tenant").option("--capabilities <list>", `calendar capability tiers beyond read (${CAPABILITIES.join(", ")}); a mailbox takes none`).option("--yes", "confirm the access a mailbox or calendar connection gives Standards").action((opts) => {
|
|
462
588
|
if (!PROVIDERS.includes(opts.provider)) throw new ValidationError(`--provider must be one of: ${PROVIDERS.join(", ")}`, []);
|
|
463
589
|
const capabilities = parseCapabilities(opts.capabilities);
|
|
590
|
+
if (capabilities && MAIL_PROVIDERS.includes(opts.provider)) throw new ValidationError("--capabilities applies to calendars only: a mailbox always asks for reading, changing and sending", []);
|
|
464
591
|
const disclosure = connectDisclosure(opts.provider, capabilities);
|
|
465
592
|
if (disclosure && !opts.yes) throw new Error(`${disclosure}\n\nRe-run with "--yes" to continue.`);
|
|
466
593
|
process.stdout.write(`${opts.provider} is connected from the Standards app: open Connectors in the sidebar and connect it there.\n`);
|
|
467
594
|
});
|
|
468
|
-
connectors.command("disconnect").description("Disconnect
|
|
469
|
-
|
|
595
|
+
connectors.command("disconnect").description("Disconnect a connector connection: it stops syncing, and everything it imported stays. Reconnect it from the app").argument("<id>", "connection ID").action(async (id, _opts, cmd) => {
|
|
596
|
+
await getAppClientFromCommand(cmd).connectors.disconnect({ id });
|
|
597
|
+
printDone(`Connector connection ${id} disconnected. Everything it imported stays.`);
|
|
598
|
+
});
|
|
599
|
+
connectors.command("retry").description("Retry a failing connector connection now: its backoff is lifted and one sync pass is scheduled. A connection that does not sync (disconnected, waiting for a reconnect) is left as it is").argument("<id>", "connection ID").action(async (id, _opts, cmd) => {
|
|
600
|
+
await getAppClientFromCommand(cmd).connectors.retrySync({ id });
|
|
601
|
+
printDone(`Connector connection ${id}: retry requested. A connection that syncs gets a pass now; one that does not is left as it is.`);
|
|
602
|
+
});
|
|
603
|
+
connectors.command("delete").description("Delete a connector connection. Deleting a mailbox's connection deletes its mail: every email no other connected mailbox holds").argument("<id>", "connection ID").option("--yes", "delete without confirmation").option("--remove-imported", "also remove the meetings a calendar connection imported (kept by default)").action(async (id, opts, cmd) => {
|
|
604
|
+
if (!opts.yes) throw new Error("This deletes the connection. With a mailbox it also deletes its mail: every email no other connected mailbox holds. Re-run with \"--yes\" to confirm.");
|
|
470
605
|
await getAppClientFromCommand(cmd).connectors.delete({
|
|
471
606
|
id,
|
|
472
607
|
...opts.removeImported ? { removeImported: "true" } : {}
|
|
473
608
|
});
|
|
474
|
-
printDone(`Connector connection ${id}
|
|
609
|
+
printDone(`Connector connection ${id} deleted. A mailbox's mail is removed in the background.`);
|
|
475
610
|
});
|
|
476
611
|
}
|
|
477
612
|
// src/commands/decisions.ts
|
package/guide/core.md
CHANGED
|
@@ -50,7 +50,7 @@ One exception: `standards schema diff` exits 0 when equal, 1 when there are diff
|
|
|
50
50
|
- Pipe JSON into `jq`: `standards records list contacts | jq '.data[].id'`.
|
|
51
51
|
- Collections are `{ data, page: { hasMore, total, countMode } }`. For a row table, select `.data` first.
|
|
52
52
|
- Single-quote JSON arguments: `--data '{"email":"a@b.com"}'`.
|
|
53
|
-
- Destructive commands refuse without `--yes`. Preview first with `--dry-run` where it exists.
|
|
53
|
+
- Destructive commands refuse without `--yes`. Preview first with `--dry-run` where it exists. A bundle removal confirms with `--confirm-records <bundle>=<n>`, the record count its `--dry-run` printed, instead.
|
|
54
54
|
- Never put a secret in argv. Pipe it: `printf %s "$VALUE" | standards secrets set KEY`.
|
|
55
55
|
|
|
56
56
|
## Command groups
|
|
@@ -62,7 +62,7 @@ One exception: `standards schema diff` exits 0 when equal, 1 when there are diff
|
|
|
62
62
|
| `files` | upload, list, download and delete uploaded files | `records` |
|
|
63
63
|
| `meetings` | read meetings and their transcripts | `records` |
|
|
64
64
|
| `schema` | inspect objects; `pull` and `diff` deployed source vs runtime | `schema-and-bundles` |
|
|
65
|
-
| `bundles` | sync, add and
|
|
65
|
+
| `bundles` | sync, add, update and remove bundles | `schema-and-bundles` |
|
|
66
66
|
| `skills` | pull, push, search, add workspace skills | `schema-and-bundles` |
|
|
67
67
|
| `mcp` | remote MCP servers mounted into agents | `schema-and-bundles` |
|
|
68
68
|
| `connectors` | mail and calendar connections | `schema-and-bundles` |
|
|
@@ -26,22 +26,33 @@ standards bundles status # current, update or repair, and wh
|
|
|
26
26
|
standards bundles available --locale fr # bundles the workspace may add as copies it owns
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
A sync holds its removals (an attribute, option, view or object a bundle no longer declares) and prints `<name>: <n> removals held, <r> records`. Confirm them with the dry run's fingerprint and `totalRecords`, one pair per bundle:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
standards bundles sync --dry-run # per bundle: pieces, removals with record counts, totalRecords, fingerprint
|
|
33
|
+
standards bundles sync --fingerprint crm=<fp> --confirm-records crm=<n> # applies crm's removals: values and records go
|
|
34
|
+
```
|
|
35
|
+
|
|
29
36
|
Add and update in two steps: plan with `--dry-run`, then apply with the plan's `fingerprint`.
|
|
30
37
|
|
|
31
38
|
```bash
|
|
32
39
|
standards bundles add crm --with-samples --dry-run # plan: new and colliding pieces + fingerprint
|
|
33
40
|
standards bundles add crm --with-samples --fingerprint <fp> \
|
|
34
41
|
--decision <pieceId>=bind --decision <pieceId>=rename:opportunities --decision <pieceId>=skip
|
|
35
|
-
standards bundles update crm --dry-run # pieces by group + fingerprint
|
|
42
|
+
standards bundles update crm --dry-run # pieces by group, removals, totalRecords + fingerprint
|
|
36
43
|
standards bundles update crm --fingerprint <fp> --take deals --skip-missing notes --decision companies=bind
|
|
37
|
-
standards bundles
|
|
44
|
+
standards bundles update crm --fingerprint <fp> --confirm-records crm=<n> # when the plan removes pieces
|
|
45
|
+
standards bundles remove <name> --dry-run # objects with their records, totalRecords + fingerprint
|
|
46
|
+
standards bundles remove <name> --fingerprint <fp> --confirm-records <name>=<n>
|
|
38
47
|
```
|
|
39
48
|
|
|
40
49
|
- Without `--dry-run`, `--fingerprint` is required. Pass the same names and flags as the plan.
|
|
41
50
|
- The server refuses a stale fingerprint: the workspace changed since the plan. Plan again.
|
|
42
51
|
- `--decision` settles one collision: `bind`, `skip` or `rename:<name>`.
|
|
43
|
-
- `
|
|
44
|
-
- `
|
|
52
|
+
- `--fingerprint` is `<bundle>=<fp>` on `sync` (it pushes several bundles, repeatable) and a plain value on `add`, `update` and `remove`. `--confirm-records` is always `<bundle>=<n>`.
|
|
53
|
+
- `update` keeps a piece you changed unless `--take` names it, restores a missing piece unless `--skip-missing` names it, and removes a piece the new version dropped only with `--confirm-records`.
|
|
54
|
+
- `remove` deletes the bundle's objects with every record (private and trashed included) and its views, synced or added alike. It needs `architect:delete`.
|
|
55
|
+
- A count other than the server's own recount, or a workspace changed since the dry run, is refused with `The workspace changed since the preview; preview again` and the total to confirm now. Nothing is deleted.
|
|
45
56
|
- `sync` prints `<name> applied|unchanged <hash7>` per bundle. A 409 names the owner of the conflicting piece.
|
|
46
57
|
|
|
47
58
|
## Skills
|
|
@@ -87,8 +98,11 @@ Mail and calendar connections. The account itself is connected from the app (Con
|
|
|
87
98
|
```bash
|
|
88
99
|
standards connectors providers # providers this deployment can connect
|
|
89
100
|
standards connectors list [--scope tenant|actor]
|
|
90
|
-
standards connectors connect --provider gmail [--
|
|
91
|
-
standards connectors
|
|
101
|
+
standards connectors connect --provider gmail [--yes] # checks, then points to the app
|
|
102
|
+
standards connectors connect --provider google-calendar [--capabilities modify] [--yes] # a calendar may also ask to write
|
|
103
|
+
standards connectors retry <id> # a failing connection syncs again now
|
|
104
|
+
standards connectors disconnect <id> # stops syncing, keeps everything
|
|
105
|
+
standards connectors delete <id> --yes [--remove-imported]
|
|
92
106
|
```
|
|
93
107
|
|
|
94
|
-
`disconnect` keeps
|
|
108
|
+
`retry` lifts a failing connection's backoff and schedules one sync pass; a connection that does not sync is left as it is. `disconnect` stops a connection syncing and keeps everything it imported. `delete` deletes it: deleting a mailbox's connection deletes its mail, every email no other connected mailbox holds, removed in the background once the command answers. A workspace mailbox is deleted from the app only (an API key never holds `workspace-mailboxes:delete`). A calendar keeps the meetings it imported unless `--remove-imported` is passed.
|
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.444",
|
|
4
4
|
"description": "CLI tool to interact with Standards API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"node": ">=22"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@standards/client": "1.0.0-alpha.
|
|
28
|
-
"@standards/constants": "1.0.0-alpha.
|
|
29
|
-
"@standards/schema": "1.0.0-alpha.
|
|
27
|
+
"@standards/client": "1.0.0-alpha.444",
|
|
28
|
+
"@standards/constants": "1.0.0-alpha.444",
|
|
29
|
+
"@standards/schema": "1.0.0-alpha.444",
|
|
30
30
|
"cli-table3": "^0.6.5",
|
|
31
31
|
"commander": "^13.1.0",
|
|
32
32
|
"jiti": "2.7.0",
|