busa-cli 0.16.1 → 0.16.2
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/{chunk-FCF6L4Y2.js → chunk-AHIRHKUB.js} +118 -64
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -1049,7 +1049,13 @@ var htmlNodeType = {
|
|
|
1049
1049
|
label: "HTML",
|
|
1050
1050
|
icon: "code-xml",
|
|
1051
1051
|
capabilities: { hasDetail: true, creatable: true },
|
|
1052
|
-
operations: [
|
|
1052
|
+
operations: [
|
|
1053
|
+
{
|
|
1054
|
+
kind: "html_document_update",
|
|
1055
|
+
label: "Update HTML",
|
|
1056
|
+
tone: "border-blue-200 bg-blue-50 text-blue-800"
|
|
1057
|
+
}
|
|
1058
|
+
]
|
|
1053
1059
|
};
|
|
1054
1060
|
|
|
1055
1061
|
// ../../packages/busabase-contract/src/domains/skill/definition.ts
|
|
@@ -1068,7 +1074,13 @@ var whiteboardNodeType = {
|
|
|
1068
1074
|
label: "Whiteboard",
|
|
1069
1075
|
icon: "pen-tool",
|
|
1070
1076
|
capabilities: { hasDetail: true, creatable: true },
|
|
1071
|
-
operations: [
|
|
1077
|
+
operations: [
|
|
1078
|
+
{
|
|
1079
|
+
kind: "whiteboard_document_update",
|
|
1080
|
+
label: "Update whiteboard",
|
|
1081
|
+
tone: "border-blue-200 bg-blue-50 text-blue-800"
|
|
1082
|
+
}
|
|
1083
|
+
]
|
|
1072
1084
|
};
|
|
1073
1085
|
|
|
1074
1086
|
// ../../packages/busabase-contract/src/domains/workflow/definition.ts
|
|
@@ -1077,7 +1089,13 @@ var workflowNodeType = {
|
|
|
1077
1089
|
label: "Workflow",
|
|
1078
1090
|
icon: "workflow",
|
|
1079
1091
|
capabilities: { hasDetail: true, creatable: true },
|
|
1080
|
-
operations: [
|
|
1092
|
+
operations: [
|
|
1093
|
+
{
|
|
1094
|
+
kind: "workflow_document_update",
|
|
1095
|
+
label: "Update workflow",
|
|
1096
|
+
tone: "border-blue-200 bg-blue-50 text-blue-800"
|
|
1097
|
+
}
|
|
1098
|
+
]
|
|
1081
1099
|
};
|
|
1082
1100
|
|
|
1083
1101
|
// ../../packages/busabase-contract/src/domains/registry.ts
|
|
@@ -3564,12 +3582,8 @@ var nodeSchema = z8.lazy(
|
|
|
3564
3582
|
slug: z8.string(),
|
|
3565
3583
|
name: z8.string(),
|
|
3566
3584
|
description: z8.string(),
|
|
3567
|
-
metadata: z8.object({
|
|
3568
|
-
|
|
3569
|
-
visibility: z8.enum(["private", "workspace", "public"]).optional(),
|
|
3570
|
-
version: z8.string().optional(),
|
|
3571
|
-
assetId: z8.string().optional()
|
|
3572
|
-
}).catchall(z8.unknown()).default({}),
|
|
3585
|
+
metadata: z8.object({ version: z8.string().optional() }).catchall(z8.unknown()).default({}),
|
|
3586
|
+
explicitVisibility: z8.enum(["private", "workspace", "public"]).nullable().default(null),
|
|
3573
3587
|
position: z8.number(),
|
|
3574
3588
|
createdAt: z8.string(),
|
|
3575
3589
|
updatedAt: z8.string(),
|
|
@@ -3676,7 +3690,17 @@ var commitSchema = z8.object({
|
|
|
3676
3690
|
nodeId: z8.string().nullable(),
|
|
3677
3691
|
operationId: z8.string().nullable(),
|
|
3678
3692
|
parentCommitId: z8.string().nullable(),
|
|
3679
|
-
|
|
3693
|
+
// Deliberately loose (`z.record`), NOT a discriminated union keyed on `operation`.
|
|
3694
|
+
//
|
|
3695
|
+
// Do not "tighten" this. Commits already in the database were written before
|
|
3696
|
+
// per-operation payload validation existed, so their shapes carry no guarantee.
|
|
3697
|
+
// The write path (`insertCommit`) and the merge path (`parseCommitPayload`) are
|
|
3698
|
+
// strict precisely because they only ever touch freshly-written payloads; this VO
|
|
3699
|
+
// is also used to render *history* and approval detail pages, which read arbitrarily
|
|
3700
|
+
// old commits. Making it strict would turn any legacy-shaped row into a 500 on a
|
|
3701
|
+
// read-only screen. This is not a compatibility shim — it is the requirement not to
|
|
3702
|
+
// break reading data that already exists.
|
|
3703
|
+
payload: z8.record(z8.string(), z8.unknown()),
|
|
3680
3704
|
operation: z8.enum(OPERATION_KINDS),
|
|
3681
3705
|
message: z8.string(),
|
|
3682
3706
|
author: z8.string(),
|
|
@@ -4216,7 +4240,7 @@ var PACKAGE_COMPUTED_FIELD_TYPES = [
|
|
|
4216
4240
|
"auto_number",
|
|
4217
4241
|
"formula",
|
|
4218
4242
|
// `lookup` is derived from OTHER records and resolved at read time, so it
|
|
4219
|
-
// shows up in `headCommit.
|
|
4243
|
+
// shows up in `headCommit.payload` like any other value — but exporting it
|
|
4220
4244
|
// would ship derived data as if it were authored, and install would strip it
|
|
4221
4245
|
// server-side anyway.
|
|
4222
4246
|
"lookup",
|
|
@@ -4542,14 +4566,18 @@ var applyInstall = async (client, plan, options) => {
|
|
|
4542
4566
|
options.serverUrl
|
|
4543
4567
|
);
|
|
4544
4568
|
progress("Pass 1/5: creating the node tree\u2026");
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4569
|
+
if (plan.existingTargetFolderNodeId) {
|
|
4570
|
+
state.targetFolderNodeId = plan.existingTargetFolderNodeId;
|
|
4571
|
+
} else {
|
|
4572
|
+
state.targetFolderNodeId = await createFolderNode(client, {
|
|
4573
|
+
parentNodeId: void 0,
|
|
4574
|
+
slug: plan.targetFolderSlug,
|
|
4575
|
+
name: plan.tree.manifest.name,
|
|
4576
|
+
description: plan.tree.manifest.description,
|
|
4577
|
+
submittedBy: options.submittedBy
|
|
4578
|
+
});
|
|
4579
|
+
state.created.folders++;
|
|
4580
|
+
}
|
|
4553
4581
|
await createStructure(
|
|
4554
4582
|
client,
|
|
4555
4583
|
plan.tree.nodes,
|
|
@@ -5293,7 +5321,7 @@ var collectRecords = async (client, baseId, fields, options) => {
|
|
|
5293
5321
|
for (const record of page.records) {
|
|
5294
5322
|
if (record.status !== "active") continue;
|
|
5295
5323
|
const values = {};
|
|
5296
|
-
for (const [slug, value] of Object.entries(record.headCommit.
|
|
5324
|
+
for (const [slug, value] of Object.entries(record.headCommit.payload ?? {})) {
|
|
5297
5325
|
const type = typeBySlug.get(slug);
|
|
5298
5326
|
if (!type) continue;
|
|
5299
5327
|
if (PACKAGE_COMPUTED_FIELD_TYPES.includes(type)) continue;
|
|
@@ -6059,27 +6087,39 @@ var writePackageFiles = async (files, outDir, options = {}) => {
|
|
|
6059
6087
|
};
|
|
6060
6088
|
|
|
6061
6089
|
// ../../packages/busabase-package/src/plan.ts
|
|
6090
|
+
var flattenExistingNodes = (nodes) => {
|
|
6091
|
+
const result = [];
|
|
6092
|
+
const visit = (node) => {
|
|
6093
|
+
result.push(node);
|
|
6094
|
+
for (const child of node.children ?? []) visit(child);
|
|
6095
|
+
};
|
|
6096
|
+
for (const node of nodes) visit(node);
|
|
6097
|
+
return result;
|
|
6098
|
+
};
|
|
6062
6099
|
var findTargetFolder = (nodes, slug) => {
|
|
6063
|
-
|
|
6064
|
-
return roots.find((node) => node.slug === slug && node.type === "folder");
|
|
6100
|
+
return flattenExistingNodes(nodes).find((node) => node.slug === slug && node.type === "folder");
|
|
6065
6101
|
};
|
|
6066
6102
|
var resolveTargetState = async (client, targetFolderSlug) => {
|
|
6067
|
-
const
|
|
6103
|
+
const listedNodes = await client.nodes.list({});
|
|
6104
|
+
const slugsByType = /* @__PURE__ */ new Map();
|
|
6105
|
+
for (const node of flattenExistingNodes(listedNodes)) {
|
|
6106
|
+
const slugs = slugsByType.get(node.type) ?? /* @__PURE__ */ new Set();
|
|
6107
|
+
slugs.add(node.slug);
|
|
6108
|
+
slugsByType.set(node.type, slugs);
|
|
6109
|
+
}
|
|
6068
6110
|
return {
|
|
6069
|
-
targetFolder: findTargetFolder(
|
|
6070
|
-
|
|
6111
|
+
targetFolder: findTargetFolder(listedNodes, targetFolderSlug),
|
|
6112
|
+
existingNodeSlugsByType: slugsByType
|
|
6071
6113
|
};
|
|
6072
6114
|
};
|
|
6073
6115
|
var buildInstallPlan = (tree, target, options = {}) => {
|
|
6074
6116
|
const targetFolderSlug = options.intoFolder ?? suggestSlug(tree.manifest.name);
|
|
6075
6117
|
const warnings = [];
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
);
|
|
6079
|
-
const collisions = findCollisions(tree, targetFolderSlug, existingChildSlugs, target);
|
|
6118
|
+
assertPackageNodeIdentitiesUnique(tree);
|
|
6119
|
+
const collisions = findCollisions(tree, targetFolderSlug, target);
|
|
6080
6120
|
let planned = tree;
|
|
6081
6121
|
if (options.rename && collisions.length > 0) {
|
|
6082
|
-
planned = applyRenames(tree, collisions,
|
|
6122
|
+
planned = applyRenames(tree, collisions, target.existingNodeSlugsByType);
|
|
6083
6123
|
}
|
|
6084
6124
|
collectWarnings(planned, warnings);
|
|
6085
6125
|
const counts = countTree(planned);
|
|
@@ -6090,6 +6130,7 @@ var buildInstallPlan = (tree, target, options = {}) => {
|
|
|
6090
6130
|
}
|
|
6091
6131
|
return {
|
|
6092
6132
|
targetFolderSlug,
|
|
6133
|
+
existingTargetFolderNodeId: target.targetFolder?.id,
|
|
6093
6134
|
tree: planned,
|
|
6094
6135
|
collisions,
|
|
6095
6136
|
warnings,
|
|
@@ -6107,24 +6148,33 @@ var hasUnlinkableRelationValues = (node) => {
|
|
|
6107
6148
|
})
|
|
6108
6149
|
);
|
|
6109
6150
|
};
|
|
6110
|
-
var findCollisions = (tree, targetFolderSlug,
|
|
6151
|
+
var findCollisions = (tree, targetFolderSlug, target) => {
|
|
6111
6152
|
const collisions = [];
|
|
6112
|
-
for (const node of tree.nodes) {
|
|
6113
|
-
if (
|
|
6114
|
-
collisions.push({ kind: "node", slug: node.slug, path: `${targetFolderSlug}/${node.slug}` });
|
|
6115
|
-
}
|
|
6116
|
-
}
|
|
6117
|
-
for (const baseNode of collectBaseNodes(tree.nodes)) {
|
|
6118
|
-
if (target.existingBaseSlugs.has(baseNode.slug)) {
|
|
6153
|
+
for (const node of walkNodes(tree.nodes)) {
|
|
6154
|
+
if (target.existingNodeSlugsByType.get(node.type)?.has(node.slug)) {
|
|
6119
6155
|
collisions.push({
|
|
6120
|
-
kind: "base",
|
|
6121
|
-
|
|
6122
|
-
|
|
6156
|
+
kind: node.type === "base" ? "base" : "node",
|
|
6157
|
+
nodeType: node.type,
|
|
6158
|
+
slug: node.slug,
|
|
6159
|
+
path: `${targetFolderSlug}/\u2026/${node.slug} (${node.type} slugs are unique per space)`
|
|
6123
6160
|
});
|
|
6124
6161
|
}
|
|
6125
6162
|
}
|
|
6126
6163
|
return collisions;
|
|
6127
6164
|
};
|
|
6165
|
+
var nodeIdentityKey = (node) => `${node.type}\0${node.slug}`;
|
|
6166
|
+
var assertPackageNodeIdentitiesUnique = (tree) => {
|
|
6167
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6168
|
+
for (const node of walkNodes(tree.nodes)) {
|
|
6169
|
+
const key = nodeIdentityKey(node);
|
|
6170
|
+
if (seen.has(key)) {
|
|
6171
|
+
throw new Error(
|
|
6172
|
+
`Package contains duplicate ${node.type} slug "${node.slug}". Node slugs must be unique by type within a space.`
|
|
6173
|
+
);
|
|
6174
|
+
}
|
|
6175
|
+
seen.add(key);
|
|
6176
|
+
}
|
|
6177
|
+
};
|
|
6128
6178
|
var nextFreeSlug = (slug, taken) => {
|
|
6129
6179
|
for (let suffix = 2; suffix < 1e3; suffix++) {
|
|
6130
6180
|
const candidate = `${slug}-${suffix}`;
|
|
@@ -6132,38 +6182,42 @@ var nextFreeSlug = (slug, taken) => {
|
|
|
6132
6182
|
}
|
|
6133
6183
|
throw new Error(`Could not find a free slug for "${slug}" after 999 attempts.`);
|
|
6134
6184
|
};
|
|
6135
|
-
var applyRenames = (tree, collisions,
|
|
6136
|
-
const
|
|
6137
|
-
const
|
|
6138
|
-
|
|
6185
|
+
var applyRenames = (tree, collisions, existingNodeSlugsByType) => {
|
|
6186
|
+
const takenByType = /* @__PURE__ */ new Map();
|
|
6187
|
+
for (const [type, slugs] of existingNodeSlugsByType) {
|
|
6188
|
+
takenByType.set(type, new Set(slugs));
|
|
6189
|
+
}
|
|
6190
|
+
for (const node of walkNodes(tree.nodes)) {
|
|
6191
|
+
const taken = takenByType.get(node.type) ?? /* @__PURE__ */ new Set();
|
|
6192
|
+
taken.add(node.slug);
|
|
6193
|
+
takenByType.set(node.type, taken);
|
|
6194
|
+
}
|
|
6139
6195
|
const nodeRenames = /* @__PURE__ */ new Map();
|
|
6140
6196
|
const baseRenames = /* @__PURE__ */ new Map();
|
|
6141
6197
|
for (const collision of collisions) {
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
}
|
|
6154
|
-
const rewriteNode = (node
|
|
6198
|
+
const node = [...walkNodes(tree.nodes)].find(
|
|
6199
|
+
(candidate) => candidate.type === collision.nodeType && candidate.slug === collision.slug
|
|
6200
|
+
);
|
|
6201
|
+
if (!node) continue;
|
|
6202
|
+
const taken = takenByType.get(node.type) ?? /* @__PURE__ */ new Set();
|
|
6203
|
+
const renamed = nextFreeSlug(collision.slug, taken);
|
|
6204
|
+
taken.add(renamed);
|
|
6205
|
+
takenByType.set(node.type, taken);
|
|
6206
|
+
nodeRenames.set(nodeIdentityKey(node), renamed);
|
|
6207
|
+
if (node.type === "base") baseRenames.set(node.slug, renamed);
|
|
6208
|
+
collision.renamedTo = renamed;
|
|
6209
|
+
}
|
|
6210
|
+
const rewriteNode = (node) => {
|
|
6211
|
+
const slug = nodeRenames.get(nodeIdentityKey(node)) ?? node.slug;
|
|
6155
6212
|
if (node.type === "base") {
|
|
6156
|
-
const renamed = baseRenames.get(node.slug) ?? (isTopLevel ? nodeRenames.get(node.slug) : void 0);
|
|
6157
|
-
const slug2 = renamed ?? node.slug;
|
|
6158
6213
|
return {
|
|
6159
6214
|
...node,
|
|
6160
|
-
slug
|
|
6215
|
+
slug,
|
|
6161
6216
|
base: { ...node.base, fields: node.base.fields.map(rewriteFieldTarget) }
|
|
6162
6217
|
};
|
|
6163
6218
|
}
|
|
6164
|
-
const slug = (isTopLevel ? nodeRenames.get(node.slug) : void 0) ?? node.slug;
|
|
6165
6219
|
if (node.type === "folder") {
|
|
6166
|
-
return { ...node, slug, children: node.children.map(
|
|
6220
|
+
return { ...node, slug, children: node.children.map(rewriteNode) };
|
|
6167
6221
|
}
|
|
6168
6222
|
return { ...node, slug };
|
|
6169
6223
|
};
|
|
@@ -6174,7 +6228,7 @@ var applyRenames = (tree, collisions, existingChildSlugs, existingBaseSlugs) =>
|
|
|
6174
6228
|
if (!renamed) return field;
|
|
6175
6229
|
return { ...field, options: { ...field.options, targetBaseSlug: renamed } };
|
|
6176
6230
|
};
|
|
6177
|
-
return { ...tree, nodes: tree.nodes.map(
|
|
6231
|
+
return { ...tree, nodes: tree.nodes.map(rewriteNode) };
|
|
6178
6232
|
};
|
|
6179
6233
|
var collectWarnings = (tree, warnings) => {
|
|
6180
6234
|
for (const baseNode of collectBaseNodes(tree.nodes)) {
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "busa-cli",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
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.16.
|
|
36
|
+
"busabase-sdk": "0.16.2",
|
|
37
37
|
"commander": "^14.0.0",
|
|
38
38
|
"tar-stream": "^3.1.7",
|
|
39
39
|
"yaml": "^2.9.0",
|