busa-cli 0.52.0 → 0.53.0
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/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as runCli, r as pkgVersion, t as checkForUpdate } from "./update-check-
|
|
2
|
+
import { i as runCli, r as pkgVersion, t as checkForUpdate } from "./update-check-Rqylvhw0.js";
|
|
3
3
|
//#region src/cli.ts
|
|
4
4
|
const exitCode = await runCli(process.argv.slice(2));
|
|
5
5
|
const notice = await checkForUpdate("busabase-cli", pkgVersion());
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as render, i as runCli, n as HELP, r as pkgVersion, t as checkForUpdate } from "./update-check-
|
|
1
|
+
import { a as render, i as runCli, n as HELP, r as pkgVersion, t as checkForUpdate } from "./update-check-Rqylvhw0.js";
|
|
2
2
|
import { DEFAULT_BASE_URL, createBusabaseClient, normalizeBaseUrl } from "busabase-sdk";
|
|
3
3
|
export { DEFAULT_BASE_URL, HELP, checkForUpdate, createBusabaseClient, normalizeBaseUrl, pkgVersion, render, runCli };
|
|
@@ -160,12 +160,20 @@ const customPromptBodySchema = iStringSchema.refine((value) => iStringLocaleValu
|
|
|
160
160
|
* One custom scenario prompt. `body`'s `{target}` placeholder is substituted at
|
|
161
161
|
* render time with the same target string `PromptDef.body(target)` receives
|
|
162
162
|
* today (see `node-agent-prompts.ts`) — this schema does not interpolate it.
|
|
163
|
+
*
|
|
164
|
+
* The placeholder is OPTIONAL and chooses placement only: a `body` that never
|
|
165
|
+
* mentions `{target}` gets the target line prepended as its first paragraph, so
|
|
166
|
+
* a custom prompt can never reach an agent without naming the node it acts on.
|
|
167
|
+
* That is why the schema does not require it — forgetting it is not an error to
|
|
168
|
+
* reject, it is a default to supply.
|
|
163
169
|
*/
|
|
164
170
|
const customPromptDefSchema = z.object({
|
|
165
171
|
/** Stable id, unique within this node's custom list. */
|
|
166
172
|
key: z.string().trim().min(1, { message: "key must not be empty" }),
|
|
167
173
|
/** Defaults to `change` (same default the curated prompts use) so a prompt
|
|
168
|
-
* cannot silently
|
|
174
|
+
* cannot silently opt out of the change-request path by omission — whether that
|
|
175
|
+
* path then merges immediately or waits for review is the permission layer's
|
|
176
|
+
* call, not the prompt's. */
|
|
169
177
|
intent: customPromptIntentSchema.optional(),
|
|
170
178
|
/** Short title shown in the dialog's left list. */
|
|
171
179
|
label: customPromptLabelSchema,
|
|
@@ -1236,6 +1244,7 @@ const baseNodeType = {
|
|
|
1236
1244
|
capabilities: {
|
|
1237
1245
|
hasDetail: true,
|
|
1238
1246
|
creatable: true,
|
|
1247
|
+
commonlyCreated: true,
|
|
1239
1248
|
publicAccess: "detail"
|
|
1240
1249
|
},
|
|
1241
1250
|
operations: [
|
|
@@ -1340,6 +1349,7 @@ const docNodeType = {
|
|
|
1340
1349
|
capabilities: {
|
|
1341
1350
|
hasDetail: true,
|
|
1342
1351
|
creatable: true,
|
|
1352
|
+
commonlyCreated: true,
|
|
1343
1353
|
publicAccess: "detail"
|
|
1344
1354
|
},
|
|
1345
1355
|
operations: [{
|
|
@@ -1371,6 +1381,7 @@ const fileNodeType = {
|
|
|
1371
1381
|
capabilities: {
|
|
1372
1382
|
hasDetail: true,
|
|
1373
1383
|
creatable: true,
|
|
1384
|
+
commonlyCreated: true,
|
|
1374
1385
|
publicAccess: "detail"
|
|
1375
1386
|
},
|
|
1376
1387
|
operations: []
|
|
@@ -1386,6 +1397,7 @@ const folderNodeType = {
|
|
|
1386
1397
|
capabilities: {
|
|
1387
1398
|
container: true,
|
|
1388
1399
|
creatable: true,
|
|
1400
|
+
commonlyCreated: true,
|
|
1389
1401
|
hasDetail: true,
|
|
1390
1402
|
publicAccess: "detail"
|
|
1391
1403
|
},
|
|
@@ -1396,9 +1408,10 @@ const folderNodeType = {
|
|
|
1396
1408
|
/**
|
|
1397
1409
|
* Form node: an agent-authored, sandboxed web page bound to a Base via an
|
|
1398
1410
|
* explicit field-binding contract. A submission does NOT write a record
|
|
1399
|
-
* directly — it produces a
|
|
1400
|
-
*
|
|
1401
|
-
*
|
|
1411
|
+
* directly — it produces a record-create ChangeRequest on the target Base (so
|
|
1412
|
+
* the submitting act reuses the base's `record_create` op, not a form-specific
|
|
1413
|
+
* one), which then merges immediately or waits for review according to the
|
|
1414
|
+
* submitter's permission on that Base. The form's own config (bindings/page/share) is owner-
|
|
1402
1415
|
* managed and edited directly, so this node contributes no CR operations of its
|
|
1403
1416
|
* own for now.
|
|
1404
1417
|
*/
|
|
@@ -1406,10 +1419,23 @@ const formNodeType = {
|
|
|
1406
1419
|
type: "form",
|
|
1407
1420
|
label: "Form",
|
|
1408
1421
|
icon: "form",
|
|
1422
|
+
/**
|
|
1423
|
+
* `hidden` until a Form can be created from a create surface at all.
|
|
1424
|
+
*
|
|
1425
|
+
* `busabase_forms.target_base_id` is NOT NULL and `form` registers no
|
|
1426
|
+
* `node_create` materializer, so a Form built through the generic New-item
|
|
1427
|
+
* flow (which only collects name/slug/description) is a node row with no form
|
|
1428
|
+
* config behind it — it opens to a dead end, every time, for everyone. The
|
|
1429
|
+
* type stays fully `creatable` so `forms.create` (which does take a target
|
|
1430
|
+
* Base) and the REST/MCP surface are untouched; it just no longer offers an
|
|
1431
|
+
* entry point that cannot succeed. Drop this once the New-item flow asks for
|
|
1432
|
+
* the target Base and a materializer writes the config row.
|
|
1433
|
+
*/
|
|
1409
1434
|
capabilities: {
|
|
1410
1435
|
hasDetail: true,
|
|
1411
1436
|
creatable: true,
|
|
1412
|
-
publicAccess: "submit"
|
|
1437
|
+
publicAccess: "submit",
|
|
1438
|
+
hidden: true
|
|
1413
1439
|
},
|
|
1414
1440
|
operations: []
|
|
1415
1441
|
};
|
|
@@ -5703,6 +5729,19 @@ const TemplateManifestSchema = z.object({
|
|
|
5703
5729
|
/** Card/detail screenshots, package-relative (`assets/screenshots/overview.webp`). */
|
|
5704
5730
|
screenshots: z.array(z.string()).default([]),
|
|
5705
5731
|
/**
|
|
5732
|
+
* Optional demo clip, package-relative (`assets/recordings/busa-crm.mp4`).
|
|
5733
|
+
*
|
|
5734
|
+
* No companion poster field on purpose: the detail page uses
|
|
5735
|
+
* `screenshots[0]`, which the catalog already requires to be the cover. One
|
|
5736
|
+
* declared path instead of two that can disagree with each other.
|
|
5737
|
+
*
|
|
5738
|
+
* Note for anyone adding a sibling field here: this is a plain `z.object`, so
|
|
5739
|
+
* an unrecognized key in `busabase.json` is silently stripped rather than
|
|
5740
|
+
* rejected. A template cannot declare a field ahead of the schema landing —
|
|
5741
|
+
* it just vanishes, with no error from `busabase-cli check`.
|
|
5742
|
+
*/
|
|
5743
|
+
video: z.string().optional(),
|
|
5744
|
+
/**
|
|
5706
5745
|
* Ready-made prompts shown after install ("Ask agent" prefills the first).
|
|
5707
5746
|
*
|
|
5708
5747
|
* They are the difference between a folder of tables and something a user can
|
|
@@ -6047,7 +6086,26 @@ const updateFieldChangeRequestInputSchema = z.object({
|
|
|
6047
6086
|
patch: z.object({
|
|
6048
6087
|
name: fieldNameSchema.optional(),
|
|
6049
6088
|
required: z.boolean().optional(),
|
|
6050
|
-
options: fieldOptionsSchema.optional()
|
|
6089
|
+
options: fieldOptionsSchema.optional(),
|
|
6090
|
+
/**
|
|
6091
|
+
* Not a patch key — rejected on purpose, and the only key here that is.
|
|
6092
|
+
*
|
|
6093
|
+
* `update` cannot change a field's type; `convert` does, after
|
|
6094
|
+
* `previewFieldConversion` has shown what happens to the stored values.
|
|
6095
|
+
* But `patch` is a plain (non-strict) object, so `{ type: "markdown" }`
|
|
6096
|
+
* used to be stripped silently: the request validated, the change request
|
|
6097
|
+
* merged, `ok: true` came back, and the field was still whatever it was.
|
|
6098
|
+
* A caller reaching for the obvious-but-wrong shape got a successful
|
|
6099
|
+
* no-op, which reads exactly like a successful conversion.
|
|
6100
|
+
*
|
|
6101
|
+
* Blanket `.strict()` is not the fix here — see `contract/auto-merge.ts`
|
|
6102
|
+
* on why these schemas stay open: the SDK ships on its own cadence
|
|
6103
|
+
* against self-hosted servers, so a newer client sending a newer optional
|
|
6104
|
+
* key is normal traffic, and strictness would 400 all of them to catch
|
|
6105
|
+
* this one. Naming the single key that will never be legitimate keeps
|
|
6106
|
+
* that forward compatibility intact.
|
|
6107
|
+
*/
|
|
6108
|
+
type: z.never({ message: "A field type cannot be changed with `update`. Use operation: \"convert\" with `newType`, and call `previewFieldConversion` first to see how many stored values survive it." }).optional()
|
|
6051
6109
|
}),
|
|
6052
6110
|
message: z.string().optional(),
|
|
6053
6111
|
submittedBy: z.string().optional().default("local-editor"),
|
|
@@ -6321,7 +6379,14 @@ z.object({
|
|
|
6321
6379
|
name: z.string(),
|
|
6322
6380
|
slug: z.string(),
|
|
6323
6381
|
path: z.string(),
|
|
6324
|
-
updatedAt: z.string()
|
|
6382
|
+
updatedAt: z.string(),
|
|
6383
|
+
/**
|
|
6384
|
+
* The node's own custom avatar, same shape as `NodeVO.icon`. Optional so an
|
|
6385
|
+
* older server that predates this field is still a valid response — a
|
|
6386
|
+
* caller that doesn't know it falls back to the type icon exactly as it
|
|
6387
|
+
* always has.
|
|
6388
|
+
*/
|
|
6389
|
+
icon: NodeIconSchema.nullable().optional()
|
|
6325
6390
|
});
|
|
6326
6391
|
const userRefSchema = z.object({
|
|
6327
6392
|
id: z.string(),
|
|
@@ -7584,6 +7649,8 @@ const auditPackage = (tree, files, options = {}) => {
|
|
|
7584
7649
|
const present = new Set(paths);
|
|
7585
7650
|
if (options.directoryName !== void 0 && options.directoryName !== tree.manifest.name) error("package/identity", `Directory is "${options.directoryName}" but busabase.json declares "${tree.manifest.name}". The directory name is the package's identity, not a label beside it.`);
|
|
7586
7651
|
for (const screenshot of tree.manifest.template?.screenshots ?? []) if (!present.has(screenshot)) error("package/screenshot-missing", `busabase.json declares screenshot "${screenshot}", which is not in the package. The Template Center card would render a broken image.`);
|
|
7652
|
+
const video = tree.manifest.template?.video;
|
|
7653
|
+
if (video && !present.has(video)) error("package/video-missing", `busabase.json declares video "${video}", which is not in the package. The Template Center detail page would show a play button that leads nowhere.`);
|
|
7587
7654
|
for (const relative of paths) {
|
|
7588
7655
|
const base = relative.slice(relative.lastIndexOf("/") + 1);
|
|
7589
7656
|
if (/^\.env(?!\.example$)(?:\.|$)/.test(base)) error("package/dotenv", `${relative} is part of the package. Credentials belong in Vault, never in something people install.`);
|
|
@@ -9317,7 +9384,7 @@ const toCreatableFields = (node, fields, recordIdsByKey) => {
|
|
|
9317
9384
|
return result;
|
|
9318
9385
|
};
|
|
9319
9386
|
const createRecords = async (client, base, recordsToCreate, plan, options, state, recordIdsByKey, app) => {
|
|
9320
|
-
const mergeRecords =
|
|
9387
|
+
const mergeRecords = app ? app.mergeSampleRecords : options.autoMerge;
|
|
9321
9388
|
const records = [...recordsToCreate].sort((a, b) => a.key.localeCompare(b.key, "en"));
|
|
9322
9389
|
for (let offset = 0; offset < records.length; offset += 200) {
|
|
9323
9390
|
const batch = records.slice(offset, offset + 200);
|
|
@@ -10058,12 +10125,17 @@ const hasUnlinkableRelationValues = (node) => {
|
|
|
10058
10125
|
};
|
|
10059
10126
|
const findCollisions = (tree, targetFolderSlug, target) => {
|
|
10060
10127
|
const collisions = [];
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10128
|
+
const check = (type, slug) => {
|
|
10129
|
+
if (!target.existingNodeSlugsByType.get(type)?.has(slug)) return;
|
|
10130
|
+
collisions.push({
|
|
10131
|
+
kind: type === "base" ? "base" : "node",
|
|
10132
|
+
nodeType: type,
|
|
10133
|
+
slug,
|
|
10134
|
+
path: `${targetFolderSlug}/…/${slug} (${type} slugs are unique per space)`
|
|
10135
|
+
});
|
|
10136
|
+
};
|
|
10137
|
+
for (const node of walkNodes(tree.nodes)) check(node.type, node.slug);
|
|
10138
|
+
if (tree.rootSkill) check("skill", tree.rootSkill.slug);
|
|
10067
10139
|
return collisions;
|
|
10068
10140
|
};
|
|
10069
10141
|
const nodeIdentityKey = (node) => `${node.type}\0${node.slug}`;
|
|
@@ -10102,7 +10174,22 @@ const applyRenames = (tree, collisions, existingNodeSlugsByType) => {
|
|
|
10102
10174
|
}
|
|
10103
10175
|
const nodeRenames = /* @__PURE__ */ new Map();
|
|
10104
10176
|
const baseRenames = /* @__PURE__ */ new Map();
|
|
10177
|
+
if (tree.rootSkill) {
|
|
10178
|
+
const taken = takenByType.get("skill") ?? /* @__PURE__ */ new Set();
|
|
10179
|
+
taken.add(tree.rootSkill.slug);
|
|
10180
|
+
takenByType.set("skill", taken);
|
|
10181
|
+
}
|
|
10182
|
+
let rootSkillRename;
|
|
10105
10183
|
for (const collision of collisions) {
|
|
10184
|
+
if (tree.rootSkill && collision.nodeType === "skill" && collision.slug === tree.rootSkill.slug) {
|
|
10185
|
+
const taken = takenByType.get("skill") ?? /* @__PURE__ */ new Set();
|
|
10186
|
+
const renamed = nextFreeSlug(collision.slug, taken);
|
|
10187
|
+
taken.add(renamed);
|
|
10188
|
+
takenByType.set("skill", taken);
|
|
10189
|
+
rootSkillRename = renamed;
|
|
10190
|
+
collision.renamedTo = renamed;
|
|
10191
|
+
continue;
|
|
10192
|
+
}
|
|
10106
10193
|
const node = [...walkNodes(tree.nodes)].find((candidate) => candidate.type === collision.nodeType && candidate.slug === collision.slug);
|
|
10107
10194
|
if (!node) continue;
|
|
10108
10195
|
const taken = takenByType.get(node.type) ?? /* @__PURE__ */ new Set();
|
|
@@ -10148,7 +10235,11 @@ const applyRenames = (tree, collisions, existingNodeSlugsByType) => {
|
|
|
10148
10235
|
};
|
|
10149
10236
|
return {
|
|
10150
10237
|
...tree,
|
|
10151
|
-
nodes: tree.nodes.map(rewriteNode)
|
|
10238
|
+
nodes: tree.nodes.map(rewriteNode),
|
|
10239
|
+
...tree.rootSkill && rootSkillRename ? { rootSkill: {
|
|
10240
|
+
...tree.rootSkill,
|
|
10241
|
+
slug: rootSkillRename
|
|
10242
|
+
} } : {}
|
|
10152
10243
|
};
|
|
10153
10244
|
};
|
|
10154
10245
|
const collectWarnings = (tree, warnings) => {
|
|
@@ -10337,6 +10428,7 @@ const discoverPackages = (files) => {
|
|
|
10337
10428
|
...tree.manifest.template?.category ? { category: tree.manifest.template.category } : {},
|
|
10338
10429
|
...validation.risk ? { risk: validation.risk } : {},
|
|
10339
10430
|
screenshots: tree.manifest.template?.screenshots ?? [],
|
|
10431
|
+
...tree.manifest.template?.video ? { video: tree.manifest.template.video } : {},
|
|
10340
10432
|
tags: [.../* @__PURE__ */ new Set([...tree.manifest.tags, ...tree.manifest.template?.tags ?? []])],
|
|
10341
10433
|
agentPrompts: tree.manifest.template?.agentPrompts ?? [],
|
|
10342
10434
|
...tree.manifest.version ? { version: tree.manifest.version } : {},
|
|
@@ -10563,6 +10655,7 @@ const toEntry = (found, airapps) => ({
|
|
|
10563
10655
|
...found.risk ? { risk: found.risk } : {},
|
|
10564
10656
|
tags: found.tags ?? [],
|
|
10565
10657
|
screenshots: found.screenshots,
|
|
10658
|
+
...found.video ? { video: found.video } : {},
|
|
10566
10659
|
agentPrompts: found.agentPrompts ?? [],
|
|
10567
10660
|
...found.version ? { version: found.version } : {},
|
|
10568
10661
|
...found.author ? { author: found.author } : {},
|
|
@@ -11371,6 +11464,7 @@ const PROCEDURE_PERMISSION_POLICY = {
|
|
|
11371
11464
|
"webhooks.testFire": workspace("manage"),
|
|
11372
11465
|
"vault.get": workspace("manage"),
|
|
11373
11466
|
"vault.update": workspace("manage"),
|
|
11467
|
+
"vault.updatePreviewFileCredential": workspace("manage"),
|
|
11374
11468
|
"vault.clear": workspace("manage"),
|
|
11375
11469
|
"dump.exportTables": workspace("manage"),
|
|
11376
11470
|
"dump.exportAssetText": workspace("manage"),
|
|
@@ -11407,6 +11501,8 @@ const PROCEDURE_PERMISSION_POLICY = {
|
|
|
11407
11501
|
"bases.previewFieldConversion": node("read"),
|
|
11408
11502
|
"fileTrees.listFiles": node("read"),
|
|
11409
11503
|
"fileTrees.readFile": node("read"),
|
|
11504
|
+
"fileTrees.previewConfig": workspace("read"),
|
|
11505
|
+
"fileTrees.preparePreview": node("read"),
|
|
11410
11506
|
"airapps.runLocal": node("write"),
|
|
11411
11507
|
"airapps.stopLocal": node("write"),
|
|
11412
11508
|
"nodes.readLines": node("read"),
|
|
@@ -11421,6 +11517,7 @@ const PROCEDURE_PERMISSION_POLICY = {
|
|
|
11421
11517
|
"assets.readTextLines": node("read"),
|
|
11422
11518
|
"install.planFromGithub": workspace("read"),
|
|
11423
11519
|
"install.fromGithub": workspace("manage"),
|
|
11520
|
+
"install.fromGithubStream": workspace("manage"),
|
|
11424
11521
|
"templates.list": workspace("read"),
|
|
11425
11522
|
"guides.list": workspace("read"),
|
|
11426
11523
|
"guides.read": workspace("read"),
|
|
@@ -11430,6 +11527,7 @@ const PROCEDURE_PERMISSION_POLICY = {
|
|
|
11430
11527
|
"agents.sessions.list": workspace("manage"),
|
|
11431
11528
|
"agents.sessions.create": workspace("manage"),
|
|
11432
11529
|
"agents.sessions.prompt": workspace("manage"),
|
|
11530
|
+
"agents.sessions.setConfigOption": workspace("manage"),
|
|
11433
11531
|
"agents.sessions.cancel": workspace("manage"),
|
|
11434
11532
|
"agents.sessions.close": workspace("manage"),
|
|
11435
11533
|
"agents.sessions.subscribe": workspace("manage"),
|
|
@@ -11732,9 +11830,10 @@ const wrapGuidance = (text, width = 78) => {
|
|
|
11732
11830
|
/**
|
|
11733
11831
|
* `--wait`: block until a submitted proposal is actually decided.
|
|
11734
11832
|
*
|
|
11735
|
-
*
|
|
11736
|
-
*
|
|
11737
|
-
*
|
|
11833
|
+
* A write can still land as a pending proposal — a proposal-only credential, no
|
|
11834
|
+
* write access on the target, or an explicit `--require-review` — so the useful
|
|
11835
|
+
* unit of work for a caller is not "the change request was filed" but "the
|
|
11836
|
+
* change request was merged, or it wasn't". Without this every caller writes the same poll loop
|
|
11738
11837
|
* against `change-requests get`, and an agent that skips it reports a proposal
|
|
11739
11838
|
* nobody has looked at as a completed task.
|
|
11740
11839
|
*/
|
|
@@ -12831,7 +12930,7 @@ Examples:
|
|
|
12831
12930
|
submittedBy: opts.submittedBy
|
|
12832
12931
|
});
|
|
12833
12932
|
}));
|
|
12834
|
-
addGlobalFlags(bases.command("create-change-request")).description("
|
|
12933
|
+
addGlobalFlags(bases.command("create-change-request")).description("Create a record via a Change Request — merged immediately if you have write access, otherwise proposed for review").requiredOption("--base-id <id>", "target Base id").requiredOption("--fields-json <json|@file>", "record fields as JSON, or @file.json").option("--message <text>", "reviewer-facing Change Request message").option("--submitted-by <name>", "producer label").option("--auto-merge", "skip review and create the record immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)").option("--require-review", "always propose a pending Change Request, even if you have write access").addHelpText("after", `
|
|
12835
12934
|
Examples:
|
|
12836
12935
|
busabase-cli bases create-change-request --base-id bse_123 --fields-json '{"title":"Hello","status":"draft"}'
|
|
12837
12936
|
busabase-cli bases create-change-request --base-id bse_123 --fields-json @record.json
|
|
@@ -12918,7 +13017,7 @@ Examples:
|
|
|
12918
13017
|
startLine: opts.startLine,
|
|
12919
13018
|
endLine: opts.endLine
|
|
12920
13019
|
})));
|
|
12921
|
-
addGlobalFlags(program.command("install")).description("Install a Busabase package from a GitHub repo or a local directory into this space").argument("<source>", "GitHub repo URL (optionally /tree/<ref>[/<subdir>]), or a local directory / file:// URL written by `busabase-cli export`").option("--into-folder <name>", "target folder slug (default: the package's manifest name)").option("--dry-run", "print the plan (tree, record counts, collisions) and create nothing").option("--auto-merge", "merge the package's records and docs on the spot
|
|
13020
|
+
addGlobalFlags(program.command("install")).description("Install a Busabase package from a GitHub repo or a local directory into this space").argument("<source>", "GitHub repo URL (optionally /tree/<ref>[/<subdir>]), or a local directory / file:// URL written by `busabase-cli export`").option("--into-folder <name>", "target folder slug (default: the package's manifest name)").option("--dry-run", "print the plan (tree, record counts, collisions) and create nothing").option("--auto-merge", "merge the package's records and docs on the spot (the default; the flag is kept so scripts that pass it keep working)").option("--require-review", "leave the package's records and docs as change requests for you to approve, instead of merging them").option("--rename", "install colliding items under suffixed slugs (-2, -3, …) instead of failing").option("--skill <name>", "which package to install when the URL is a repository holding several (e.g. busabase/skills)").option("--no-sample-records", "propose a template's sample rows for review instead of merging them (default: merged, so the app is not empty on first open)").option("--no-rollback", "keep whatever a failed install created (default: it is moved to Trash)").addHelpText("after", `
|
|
12922
13021
|
The URL's git ref is the version pin — a tag installs that tag's content forever,
|
|
12923
13022
|
even after the branch moves on. GITHUB_TOKEN is honored for private repos.
|
|
12924
13023
|
|
|
@@ -12931,12 +13030,15 @@ Examples:
|
|
|
12931
13030
|
busabase-cli install https://github.com/acme/support-kb-template
|
|
12932
13031
|
busabase-cli install https://github.com/acme/packages/tree/v1.2.0/skills/pdf-summarizer
|
|
12933
13032
|
busabase-cli install https://github.com/acme/support-kb-template --dry-run
|
|
12934
|
-
busabase-cli install https://github.com/acme/support-kb-template --into-folder support --
|
|
13033
|
+
busabase-cli install https://github.com/acme/support-kb-template --into-folder support --require-review
|
|
12935
13034
|
busabase-cli install ./support-kb-template --dry-run
|
|
12936
13035
|
|
|
12937
13036
|
Folders, Bases, their fields and their views are structure and are always created
|
|
12938
|
-
immediately. Records are content:
|
|
12939
|
-
to
|
|
13037
|
+
immediately. Records, docs and app code are content: they merge on the spot too if
|
|
13038
|
+
your credential can write to the space, and --require-review leaves them as change
|
|
13039
|
+
requests for you to approve instead. Installing is already restricted to a space
|
|
13040
|
+
owner/admin, so choosing to install IS the trust decision — remember that a package
|
|
13041
|
+
can carry skills and AirApps, i.e. code your agents will run.
|
|
12940
13042
|
|
|
12941
13043
|
A TEMPLATE (a package that also carries a SKILL.md) installs as an app: its
|
|
12942
13044
|
manual lands as a Skill node, its nodes are stamped as belonging to that app, its
|
|
@@ -12948,13 +13050,13 @@ still wait for your review.
|
|
|
12948
13050
|
If the URL is a repository that holds several packages rather than being one,
|
|
12949
13051
|
install lists them and you pick with --skill <name>.
|
|
12950
13052
|
|
|
12951
|
-
A package whose records carry relation values
|
|
12952
|
-
stores the ids of the records it points at, and those
|
|
12953
|
-
are merged, so review
|
|
12954
|
-
field with nothing linked yet does not trigger this.`).action(runArgAction(state, (repoUrl, client, opts, config) => runInstall(client, repoUrl, {
|
|
13053
|
+
A package whose records carry relation values cannot be installed with
|
|
13054
|
+
--require-review — a relation stores the ids of the records it points at, and those
|
|
13055
|
+
exist only once the records are merged, so review would install every relation
|
|
13056
|
+
empty. Defining a relation field with nothing linked yet does not trigger this.`).action(runArgAction(state, (repoUrl, client, opts, config) => runInstall(client, repoUrl, {
|
|
12955
13057
|
intoFolder: opts.intoFolder,
|
|
12956
13058
|
dryRun: Boolean(opts.dryRun),
|
|
12957
|
-
autoMerge:
|
|
13059
|
+
autoMerge: opts.requireReview !== true,
|
|
12958
13060
|
rename: Boolean(opts.rename),
|
|
12959
13061
|
skill: opts.skill,
|
|
12960
13062
|
noSampleRecords: opts.sampleRecords === false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "busa-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "Command-line client for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server`. Short-name alias for busabase-cli.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-cli",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@zip.js/zip.js": "^2.8.20",
|
|
35
35
|
"busabase-package": "workspace:*",
|
|
36
|
-
"busabase-sdk": "0.
|
|
36
|
+
"busabase-sdk": "0.53.0",
|
|
37
37
|
"commander": "^14.0.0",
|
|
38
38
|
"qrcode": "^1.5.4",
|
|
39
39
|
"tar-stream": "^3.1.7",
|