busa-cli 0.97.0 → 0.98.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 +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-C7tO-SDN.js → run-BVOkW2wU.js} +283 -7
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as checkForUpdate, n as pkgVersion, r as runCli } from "./run-
|
|
2
|
+
import { i as checkForUpdate, n as pkgVersion, r as runCli } from "./run-BVOkW2wU.js";
|
|
3
3
|
//#region src/flush-stdio.ts
|
|
4
4
|
/**
|
|
5
5
|
* Wait for stdout/stderr to reach the other side before the process is torn down.
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as render, i as checkForUpdate, n as pkgVersion, r as runCli, t as HELP } from "./run-
|
|
1
|
+
import { a as render, i as checkForUpdate, n as pkgVersion, r as runCli, t as HELP } from "./run-BVOkW2wU.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 };
|
|
@@ -18,7 +18,8 @@ import { checkAirApp } from "busabase-sdk/airapp-check";
|
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
import { Uint8ArrayReader, ZipReader } from "@zip.js/zip.js";
|
|
20
20
|
import QRCode from "qrcode";
|
|
21
|
-
|
|
21
|
+
//#region ../../packages/openlib/i18n/i18n.ts
|
|
22
|
+
const i18n = {
|
|
22
23
|
defaultLocale: "en",
|
|
23
24
|
locales: [
|
|
24
25
|
"en",
|
|
@@ -46,7 +47,8 @@ const LocaleSchema = z.enum({
|
|
|
46
47
|
"vi",
|
|
47
48
|
"pt"
|
|
48
49
|
]
|
|
49
|
-
}
|
|
50
|
+
};
|
|
51
|
+
const LocaleSchema = z.enum(i18n.locales);
|
|
50
52
|
//#endregion
|
|
51
53
|
//#region ../../packages/openlib/i18n/i-string.ts
|
|
52
54
|
/**
|
|
@@ -1598,7 +1600,7 @@ const driveNodeType = makeFileTreeNodeType({
|
|
|
1598
1600
|
routeBase: "drives",
|
|
1599
1601
|
tag: "Drives",
|
|
1600
1602
|
entryFile: "README.md",
|
|
1601
|
-
publicAccess: "
|
|
1603
|
+
publicAccess: "detail"
|
|
1602
1604
|
});
|
|
1603
1605
|
//#endregion
|
|
1604
1606
|
//#region ../../packages/busabase-contract/src/domains/file-node/definition.ts
|
|
@@ -1697,7 +1699,7 @@ const htmlNodeType = {
|
|
|
1697
1699
|
capabilities: {
|
|
1698
1700
|
hasDetail: true,
|
|
1699
1701
|
creatable: true,
|
|
1700
|
-
publicAccess: "
|
|
1702
|
+
publicAccess: "detail"
|
|
1701
1703
|
},
|
|
1702
1704
|
operations: [{
|
|
1703
1705
|
kind: "html_document_update",
|
|
@@ -1715,7 +1717,7 @@ const skillNodeType = makeFileTreeNodeType({
|
|
|
1715
1717
|
routeBase: "skills",
|
|
1716
1718
|
tag: "Skills",
|
|
1717
1719
|
entryFile: "SKILL.md",
|
|
1718
|
-
publicAccess: "
|
|
1720
|
+
publicAccess: "detail"
|
|
1719
1721
|
});
|
|
1720
1722
|
//#endregion
|
|
1721
1723
|
//#region ../../packages/busabase-contract/src/domains/whiteboard/definition.ts
|
|
@@ -2817,6 +2819,162 @@ const TASK_SUPERSEDED_MCP_TOOLS = [
|
|
|
2817
2819
|
"bases_list_deleted_fields"
|
|
2818
2820
|
];
|
|
2819
2821
|
//#endregion
|
|
2822
|
+
//#region ../../packages/busabase-contract/src/contract/agent-client.ts
|
|
2823
|
+
/**
|
|
2824
|
+
* Agent client attribution — "which agent program made this change".
|
|
2825
|
+
*
|
|
2826
|
+
* The API key says WHO is accountable (its name, its owner). On one machine,
|
|
2827
|
+
* several agents often share one key (`~/.busabase/.env`), so the key alone
|
|
2828
|
+
* cannot tell "Claude Code added this" from "Codex added this". A caller names
|
|
2829
|
+
* its agent client with ONE request header; busabase-cli detects it from the
|
|
2830
|
+
* environment and sends it on every request.
|
|
2831
|
+
*
|
|
2832
|
+
* It is SELF-REPORTED: a label for people reading the history, never identity,
|
|
2833
|
+
* authorization or anything security relevant. The server normalizes it with
|
|
2834
|
+
* `normalizeAgentClient` and stores only the normalized `{ id, name }`.
|
|
2835
|
+
*/
|
|
2836
|
+
const BUSABASE_AGENT_HEADER = "x-busabase-agent";
|
|
2837
|
+
const AgentClientVOSchema = z.object({
|
|
2838
|
+
/** Normalized, lowercase `[a-z0-9-]`, at most 40 characters (e.g. `codex`). */
|
|
2839
|
+
id: z.string(),
|
|
2840
|
+
/** Display name: a known agent's product name, otherwise derived from the id. */
|
|
2841
|
+
name: z.string()
|
|
2842
|
+
});
|
|
2843
|
+
const MAX_AGENT_ID_CHARS = 40;
|
|
2844
|
+
/** Longer raw values are not an agent name; don't spend work on them. */
|
|
2845
|
+
const MAX_RAW_CHARS = 256;
|
|
2846
|
+
/**
|
|
2847
|
+
* Agents whose names we know, keyed by every id they are commonly reported
|
|
2848
|
+
* as. Only agents whose environment markers were verified (see busabase-cli
|
|
2849
|
+
* `agent-client.ts`) get a canonical entry; anything else still works through
|
|
2850
|
+
* the generic path below.
|
|
2851
|
+
*/
|
|
2852
|
+
const KNOWN_AGENTS = {
|
|
2853
|
+
"claude-code": {
|
|
2854
|
+
id: "claude-code",
|
|
2855
|
+
name: "Claude Code"
|
|
2856
|
+
},
|
|
2857
|
+
claudecode: {
|
|
2858
|
+
id: "claude-code",
|
|
2859
|
+
name: "Claude Code"
|
|
2860
|
+
},
|
|
2861
|
+
codex: {
|
|
2862
|
+
id: "codex",
|
|
2863
|
+
name: "Codex"
|
|
2864
|
+
},
|
|
2865
|
+
"codex-cli": {
|
|
2866
|
+
id: "codex",
|
|
2867
|
+
name: "Codex"
|
|
2868
|
+
},
|
|
2869
|
+
"openai-codex": {
|
|
2870
|
+
id: "codex",
|
|
2871
|
+
name: "Codex"
|
|
2872
|
+
},
|
|
2873
|
+
opencode: {
|
|
2874
|
+
id: "opencode",
|
|
2875
|
+
name: "OpenCode"
|
|
2876
|
+
},
|
|
2877
|
+
"gemini-cli": {
|
|
2878
|
+
id: "gemini-cli",
|
|
2879
|
+
name: "Gemini CLI"
|
|
2880
|
+
},
|
|
2881
|
+
gemini: {
|
|
2882
|
+
id: "gemini-cli",
|
|
2883
|
+
name: "Gemini CLI"
|
|
2884
|
+
},
|
|
2885
|
+
pi: {
|
|
2886
|
+
id: "pi",
|
|
2887
|
+
name: "Pi"
|
|
2888
|
+
},
|
|
2889
|
+
"pi-coding-agent": {
|
|
2890
|
+
id: "pi",
|
|
2891
|
+
name: "Pi"
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
/**
|
|
2895
|
+
* `AI_AGENT` is a cross-tool convention shaped `<tool>_<version>_agent`
|
|
2896
|
+
* (Claude Code: `claude-code_2-1-304_agent`); some tools set just `<tool>`
|
|
2897
|
+
* (OpenCode: `opencode`). Keep the tool part.
|
|
2898
|
+
*/
|
|
2899
|
+
const stripAiAgentConvention = (value) => {
|
|
2900
|
+
return /^([^_]+)_[^_]*_agent$/.exec(value)?.[1] ?? value;
|
|
2901
|
+
};
|
|
2902
|
+
const toDisplayName = (id) => id.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2903
|
+
/**
|
|
2904
|
+
* Normalize a self-reported agent client (the `x-busabase-agent` header, or a
|
|
2905
|
+
* raw `AI_AGENT` value) to `{ id, name }`, or null when there is nothing
|
|
2906
|
+
* usable. Known agents map to their product name; anything else is reduced to
|
|
2907
|
+
* a lowercase `[a-z0-9-]` id of at most 40 characters with a name derived from
|
|
2908
|
+
* it — so the output never carries markup, whitespace or control characters.
|
|
2909
|
+
*/
|
|
2910
|
+
const normalizeAgentClient = (raw) => {
|
|
2911
|
+
const trimmed = raw?.trim();
|
|
2912
|
+
if (!trimmed || trimmed.length > MAX_RAW_CHARS) return null;
|
|
2913
|
+
const id = stripAiAgentConvention(trimmed.toLowerCase()).replace(/[^a-z0-9]+/g, "-").slice(0, MAX_AGENT_ID_CHARS).replace(/^-+|-+$/g, "");
|
|
2914
|
+
if (!id) return null;
|
|
2915
|
+
return KNOWN_AGENTS[id] ?? {
|
|
2916
|
+
id,
|
|
2917
|
+
name: toDisplayName(id)
|
|
2918
|
+
};
|
|
2919
|
+
};
|
|
2920
|
+
//#endregion
|
|
2921
|
+
//#region src/agent-client.ts
|
|
2922
|
+
const isSet = (value) => {
|
|
2923
|
+
const trimmed = value?.trim().toLowerCase();
|
|
2924
|
+
return Boolean(trimmed) && trimmed !== "0" && trimmed !== "false";
|
|
2925
|
+
};
|
|
2926
|
+
/**
|
|
2927
|
+
* Environment markers each agent puts on the commands it runs. Checked in
|
|
2928
|
+
* order; only markers backed by evidence are listed:
|
|
2929
|
+
* - Claude Code: `CLAUDECODE=1` (measured in a command Claude Code ran).
|
|
2930
|
+
* - Codex CLI: `CODEX_SESSION_ID` / `CODEX_THREAD_ID` (measured, Codex 0.163).
|
|
2931
|
+
* - OpenCode: `OPENCODE=1` (its shell tool sets it — packages/core/src/tool/plugin/shell.ts).
|
|
2932
|
+
* - Gemini CLI: `GEMINI_CLI=1` (ShellExecutionService, @google/gemini-cli-core 0.65).
|
|
2933
|
+
* - Pi: `PI_CODING_AGENT=true` (set in its own process, inherited by its bash
|
|
2934
|
+
* tool; @mariozechner/pi-coding-agent 0.73).
|
|
2935
|
+
* Anything else is left to the generic `AI_AGENT` or the `BUSABASE_AGENT` override.
|
|
2936
|
+
*/
|
|
2937
|
+
const AGENT_MARKERS = [
|
|
2938
|
+
{
|
|
2939
|
+
id: "claude-code",
|
|
2940
|
+
vars: ["CLAUDECODE"]
|
|
2941
|
+
},
|
|
2942
|
+
{
|
|
2943
|
+
id: "codex",
|
|
2944
|
+
vars: ["CODEX_SESSION_ID", "CODEX_THREAD_ID"]
|
|
2945
|
+
},
|
|
2946
|
+
{
|
|
2947
|
+
id: "opencode",
|
|
2948
|
+
vars: ["OPENCODE"]
|
|
2949
|
+
},
|
|
2950
|
+
{
|
|
2951
|
+
id: "gemini-cli",
|
|
2952
|
+
vars: ["GEMINI_CLI"]
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
id: "pi",
|
|
2956
|
+
vars: ["PI_CODING_AGENT"]
|
|
2957
|
+
}
|
|
2958
|
+
];
|
|
2959
|
+
const normalizedId = (value) => normalizeAgentClient(value)?.id;
|
|
2960
|
+
/**
|
|
2961
|
+
* Which agent client is running this CLI, as the id sent in `x-busabase-agent`.
|
|
2962
|
+
* Precedence: an explicit `BUSABASE_AGENT` → a specific agent's marker → the
|
|
2963
|
+
* cross-tool `AI_AGENT` convention (`<tool>_<version>_agent`) → none.
|
|
2964
|
+
* Self-reported attribution only — it labels the change, it grants nothing.
|
|
2965
|
+
*/
|
|
2966
|
+
const detectAgentClient = (env) => {
|
|
2967
|
+
const explicit = normalizedId(env.BUSABASE_AGENT);
|
|
2968
|
+
if (explicit) return explicit;
|
|
2969
|
+
for (const marker of AGENT_MARKERS) if (marker.vars.some((name) => isSet(env[name]))) return marker.id;
|
|
2970
|
+
return normalizedId(env.AI_AGENT);
|
|
2971
|
+
};
|
|
2972
|
+
/** The request header for the detected agent client, or undefined when there is none. */
|
|
2973
|
+
const agentClientHeaders = (env) => {
|
|
2974
|
+
const agent = detectAgentClient(env);
|
|
2975
|
+
return agent ? { [BUSABASE_AGENT_HEADER]: agent } : void 0;
|
|
2976
|
+
};
|
|
2977
|
+
//#endregion
|
|
2820
2978
|
//#region src/config-file.ts
|
|
2821
2979
|
/**
|
|
2822
2980
|
* Credential + settings storage for every Busabase client.
|
|
@@ -6223,6 +6381,27 @@ const TemplateSecretSchema = z.object({
|
|
|
6223
6381
|
description: z.string().default(""),
|
|
6224
6382
|
required: z.boolean().default(true)
|
|
6225
6383
|
});
|
|
6384
|
+
/**
|
|
6385
|
+
* One locale's own gallery media — the value side of `template.locales`.
|
|
6386
|
+
*
|
|
6387
|
+
* Both fields optional and independent: a locale that only re-recorded the clip
|
|
6388
|
+
* declares just `video` and keeps the default screenshots. Paths are
|
|
6389
|
+
* package-relative in `busabase.json` and absolute URLs on the catalog card;
|
|
6390
|
+
* the shape is the same, which is why the card VO reuses this schema.
|
|
6391
|
+
*/
|
|
6392
|
+
const TemplateLocaleMediaSchema = z.object({
|
|
6393
|
+
/** Replaces the default list WHOLE for this locale — never merged per index. */
|
|
6394
|
+
screenshots: z.array(z.string()).optional(),
|
|
6395
|
+
video: z.string().optional()
|
|
6396
|
+
});
|
|
6397
|
+
/**
|
|
6398
|
+
* Per-locale media keyed by locale (`"zh-CN"`, `"ja"`).
|
|
6399
|
+
*
|
|
6400
|
+
* Keys are plain strings on purpose, not the supported-locale enum: a template
|
|
6401
|
+
* written for a locale this server does not know yet must still install. The
|
|
6402
|
+
* audit warns about such keys; the schema never rejects them.
|
|
6403
|
+
*/
|
|
6404
|
+
const TemplateLocalesSchema = z.record(z.string(), TemplateLocaleMediaSchema);
|
|
6226
6405
|
const TemplateManifestSchema = z.object({
|
|
6227
6406
|
/** Template Center category, e.g. `"crm"`, `"email"`, `"content"`. */
|
|
6228
6407
|
category: z.string().min(1),
|
|
@@ -6243,6 +6422,21 @@ const TemplateManifestSchema = z.object({
|
|
|
6243
6422
|
*/
|
|
6244
6423
|
video: z.string().optional(),
|
|
6245
6424
|
/**
|
|
6425
|
+
* Localized gallery media, the App Store / Google Play rule: a viewer whose
|
|
6426
|
+
* locale has an entry sees that entry's `screenshots` / `video`; anything the
|
|
6427
|
+
* entry leaves out, and every locale without an entry, falls back to the two
|
|
6428
|
+
* fields above. Those stay the English/default set — they are what every
|
|
6429
|
+
* deployed server and CLI already reads, so their meaning cannot change.
|
|
6430
|
+
*
|
|
6431
|
+
* `{ "zh-CN": { "screenshots": ["assets/screenshots/cover-zh-CN.webp"],
|
|
6432
|
+
* "video": "assets/recordings/busa-rnd-zh-CN.mp4" } }`
|
|
6433
|
+
*
|
|
6434
|
+
* Additive by construction: an older parser strips the key (see the note on
|
|
6435
|
+
* `video`), so an older server simply shows the defaults. Selection lives in
|
|
6436
|
+
* one place — `pickTemplateMedia` in `domains/templates/media`.
|
|
6437
|
+
*/
|
|
6438
|
+
locales: TemplateLocalesSchema.optional(),
|
|
6439
|
+
/**
|
|
6246
6440
|
* Ready-made prompts shown after install ("Ask agent" prefills the first).
|
|
6247
6441
|
*
|
|
6248
6442
|
* They are the difference between a folder of tables and something a user can
|
|
@@ -6957,7 +7151,12 @@ const sourceAttributionSchema = z.object({
|
|
|
6957
7151
|
ownerName: z.string().nullable(),
|
|
6958
7152
|
channel: sourceChannelSchema.nullable(),
|
|
6959
7153
|
/** Present only when the write declared a playbook the server could validate. */
|
|
6960
|
-
playbook: PlaybookAttributionVOSchema.nullable().optional()
|
|
7154
|
+
playbook: PlaybookAttributionVOSchema.nullable().optional(),
|
|
7155
|
+
/**
|
|
7156
|
+
* Present only when the write named its agent client (`x-busabase-agent`;
|
|
7157
|
+
* busabase-cli sends it automatically). Self-reported — a label, not identity.
|
|
7158
|
+
*/
|
|
7159
|
+
agent: AgentClientVOSchema.nullable().optional()
|
|
6961
7160
|
});
|
|
6962
7161
|
const commitSchema = z.object({
|
|
6963
7162
|
id: z.string(),
|
|
@@ -8274,6 +8473,25 @@ const TEXT_EXTENSIONS = /\.(?:js|mjs|cjs|ts|mts|cts|json|ndjson|md|html|css|ya?m
|
|
|
8274
8473
|
const IS_ASSERTION_CODE = /(?:^|\/)(?:scripts\/check\.mjs|tests?\/|[^/]*\.test\.[cm]?[jt]s)$/;
|
|
8275
8474
|
/** Lockfiles carry integrity hashes that are not ours to interpret. */
|
|
8276
8475
|
const NOT_SOURCE = /(?:^|\/)(?:package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb)$/;
|
|
8476
|
+
/**
|
|
8477
|
+
* The locales Busabase renders its interface in — the same list `iStringParse`'s
|
|
8478
|
+
* `LocaleSchema` is built from, never a second copy. A `template.locales` key
|
|
8479
|
+
* outside it is legal (the schema accepts any string so an install never fails
|
|
8480
|
+
* over one) but no viewer is ever served it.
|
|
8481
|
+
*/
|
|
8482
|
+
const SUPPORTED_LOCALES = i18n.locales;
|
|
8483
|
+
/**
|
|
8484
|
+
* A file name that marks itself as one non-English locale's media —
|
|
8485
|
+
* `cover-zh-CN.webp`, `busa-rnd-ja.mp4` — following the `<name>-<locale>.<ext>`
|
|
8486
|
+
* convention. Case-insensitive because file systems and authors disagree on
|
|
8487
|
+
* `zh-cn` versus `zh-CN`; the separator may be `-`, `_` or `.`.
|
|
8488
|
+
*/
|
|
8489
|
+
const LOCALIZED_FILE = new RegExp(`[-_.](${SUPPORTED_LOCALES.filter((locale) => locale !== i18n.defaultLocale).join("|")})\\.[a-z0-9]+$`, "i");
|
|
8490
|
+
const localeMarkedIn = (path) => {
|
|
8491
|
+
const match = LOCALIZED_FILE.exec(path);
|
|
8492
|
+
if (!match) return void 0;
|
|
8493
|
+
return SUPPORTED_LOCALES.find((locale) => locale.toLowerCase() === match[1].toLowerCase());
|
|
8494
|
+
};
|
|
8277
8495
|
const relativePaths = (files, root = "") => {
|
|
8278
8496
|
const prefix = root ? `${root.replace(/\/+$/, "")}/` : "";
|
|
8279
8497
|
const out = [];
|
|
@@ -8311,6 +8529,24 @@ const auditPackage = (tree, files, options = {}) => {
|
|
|
8311
8529
|
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.`);
|
|
8312
8530
|
const video = tree.manifest.template?.video;
|
|
8313
8531
|
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.`);
|
|
8532
|
+
for (const [locale, media] of Object.entries(tree.manifest.template?.locales ?? {})) {
|
|
8533
|
+
if (!SUPPORTED_LOCALES.includes(locale)) warn("package/locale-unknown", `busabase.json declares media for locale "${locale}", which is not one Busabase shows its interface in (${SUPPORTED_LOCALES.join(", ")}). It still installs, but no viewer is ever served it — check the spelling (keys are exact, e.g. "zh-CN", not "zh").`);
|
|
8534
|
+
else if (locale === i18n.defaultLocale) warn("package/locale-default-language", `busabase.json declares \`template.locales.${locale}\`, but the default \`screenshots\` / \`video\` already are the English set — and they, not \`locales.${locale}\`, are what every language without its own entry falls back to. Put English media in the defaults and drop \`locales.${locale}\`, or the two drift apart.`);
|
|
8535
|
+
for (const screenshot of media.screenshots ?? []) if (!present.has(screenshot)) error("package/screenshot-missing", `busabase.json declares ${locale} screenshot "${screenshot}" (template.locales.${locale}.screenshots), which is not in the package. A ${locale} viewer's Template Center card would render a broken image.`);
|
|
8536
|
+
if (media.video && !present.has(media.video)) error("package/video-missing", `busabase.json declares ${locale} video "${media.video}" (template.locales.${locale}.video), which is not in the package. A ${locale} viewer's detail page would show a play button that leads nowhere.`);
|
|
8537
|
+
}
|
|
8538
|
+
const defaults = [...(tree.manifest.template?.screenshots ?? []).map((path) => ({
|
|
8539
|
+
path,
|
|
8540
|
+
field: "screenshots"
|
|
8541
|
+
})), ...video ? [{
|
|
8542
|
+
path: video,
|
|
8543
|
+
field: "video"
|
|
8544
|
+
}] : []];
|
|
8545
|
+
for (const { path, field } of defaults) {
|
|
8546
|
+
const locale = localeMarkedIn(path);
|
|
8547
|
+
if (!locale) continue;
|
|
8548
|
+
warn("package/localized-default-media", `"${path}" is in the default \`template.${field}\`, but its name marks it ${locale}. The defaults are the English set every language without its own entry falls back to; move it to \`template.locales.${locale}.${field}\`.`);
|
|
8549
|
+
}
|
|
8314
8550
|
for (const relative of paths) {
|
|
8315
8551
|
const base = relative.slice(relative.lastIndexOf("/") + 1);
|
|
8316
8552
|
if (/^\.env(?!\.example$)(?:\.|$)/.test(base)) error("package/dotenv", `${relative} is part of the package. Credentials belong in Vault, never in something people install.`);
|
|
@@ -10623,6 +10859,41 @@ const assertSelfContained = (tree, options) => {
|
|
|
10623
10859
|
}
|
|
10624
10860
|
};
|
|
10625
10861
|
//#endregion
|
|
10862
|
+
//#region ../../packages/busabase-contract/src/domains/templates/media.ts
|
|
10863
|
+
const nonEmptyStrings = (value) => {
|
|
10864
|
+
if (!Array.isArray(value)) return void 0;
|
|
10865
|
+
const list = value.filter((item) => typeof item === "string" && item !== "");
|
|
10866
|
+
return list.length > 0 ? list : void 0;
|
|
10867
|
+
};
|
|
10868
|
+
const nonEmptyString = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
10869
|
+
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10870
|
+
/**
|
|
10871
|
+
* Normalize an untrusted `locales` value, or `undefined` when nothing usable is
|
|
10872
|
+
* left.
|
|
10873
|
+
*
|
|
10874
|
+
* Never throws — the catalog is fetched JSON from a repository the server does
|
|
10875
|
+
* not control, and one malformed entry must not blank the gallery for every
|
|
10876
|
+
* other card. A non-object `locales`, a non-object entry, a non-array
|
|
10877
|
+
* `screenshots` or a non-string `video` is ignored, and the viewer gets the
|
|
10878
|
+
* defaults. An empty `screenshots: []` or `video: ""` counts as "not declared":
|
|
10879
|
+
* a locale that ships no gallery should see the default one, not a placeholder.
|
|
10880
|
+
*/
|
|
10881
|
+
const parseTemplateLocales = (raw) => {
|
|
10882
|
+
if (!isPlainObject(raw)) return void 0;
|
|
10883
|
+
const locales = {};
|
|
10884
|
+
for (const [locale, entry] of Object.entries(raw)) {
|
|
10885
|
+
if (!locale || !isPlainObject(entry)) continue;
|
|
10886
|
+
const screenshots = nonEmptyStrings(entry.screenshots);
|
|
10887
|
+
const video = nonEmptyString(entry.video);
|
|
10888
|
+
if (!screenshots && !video) continue;
|
|
10889
|
+
locales[locale] = {
|
|
10890
|
+
...screenshots ? { screenshots } : {},
|
|
10891
|
+
...video ? { video } : {}
|
|
10892
|
+
};
|
|
10893
|
+
}
|
|
10894
|
+
return Object.keys(locales).length > 0 ? locales : void 0;
|
|
10895
|
+
};
|
|
10896
|
+
//#endregion
|
|
10626
10897
|
//#region ../../packages/busabase-package/src/plan.ts
|
|
10627
10898
|
/**
|
|
10628
10899
|
* Install planning: package tree + the target space's current state → a plan, a
|
|
@@ -11080,6 +11351,7 @@ const discoverPackages = (files) => {
|
|
|
11080
11351
|
}
|
|
11081
11352
|
const validation = validateTemplate(tree);
|
|
11082
11353
|
const claimsTemplate = Boolean(tree.rootSkill) || Boolean(tree.manifest.template);
|
|
11354
|
+
const locales = parseTemplateLocales(tree.manifest.template?.locales);
|
|
11083
11355
|
found.push({
|
|
11084
11356
|
subdir,
|
|
11085
11357
|
name: tree.manifest.name,
|
|
@@ -11093,6 +11365,7 @@ const discoverPackages = (files) => {
|
|
|
11093
11365
|
...validation.risk ? { risk: validation.risk } : {},
|
|
11094
11366
|
screenshots: tree.manifest.template?.screenshots ?? [],
|
|
11095
11367
|
...tree.manifest.template?.video ? { video: tree.manifest.template.video } : {},
|
|
11368
|
+
...locales ? { locales } : {},
|
|
11096
11369
|
tags: [.../* @__PURE__ */ new Set([...tree.manifest.tags, ...tree.manifest.template?.tags ?? []])],
|
|
11097
11370
|
agentPrompts: tree.manifest.template?.agentPrompts ?? [],
|
|
11098
11371
|
...tree.manifest.version ? { version: tree.manifest.version } : {},
|
|
@@ -11320,6 +11593,7 @@ const toEntry = (found, airapps) => ({
|
|
|
11320
11593
|
tags: found.tags ?? [],
|
|
11321
11594
|
screenshots: found.screenshots,
|
|
11322
11595
|
...found.video ? { video: found.video } : {},
|
|
11596
|
+
...found.locales ? { locales: found.locales } : {},
|
|
11323
11597
|
agentPrompts: found.agentPrompts ?? [],
|
|
11324
11598
|
...found.version ? { version: found.version } : {},
|
|
11325
11599
|
...found.author ? { author: found.author } : {},
|
|
@@ -14364,6 +14638,7 @@ function resolveConfig(opts, { strict = true } = {}) {
|
|
|
14364
14638
|
spaceId: opts.spaceId ?? process.env.BUSABASE_SPACE_ID ?? file.BUSABASE_SPACE_ID,
|
|
14365
14639
|
sourceChannel: "cli",
|
|
14366
14640
|
playbook: resolvePlaybook(opts.playbook, strict),
|
|
14641
|
+
headers: agentClientHeaders(process.env),
|
|
14367
14642
|
output: opts.output ?? configuredOutput() ?? "text",
|
|
14368
14643
|
fetch: withRetry(fetch, { retries })
|
|
14369
14644
|
};
|
|
@@ -14536,7 +14811,8 @@ async function rawFetch(config, method, path, body) {
|
|
|
14536
14811
|
...config.apiKey ? { authorization: `Bearer ${config.apiKey}` } : {},
|
|
14537
14812
|
...config.spaceId ? { "x-busabase-space": config.spaceId } : {},
|
|
14538
14813
|
"x-busabase-channel": "cli",
|
|
14539
|
-
...config.playbook ? { [BUSABASE_PLAYBOOK_HEADER]: config.playbook } : {}
|
|
14814
|
+
...config.playbook ? { [BUSABASE_PLAYBOOK_HEADER]: config.playbook } : {},
|
|
14815
|
+
...typeof config.headers === "object" ? config.headers : {}
|
|
14540
14816
|
},
|
|
14541
14817
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
14542
14818
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "busa-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.98.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.98.0",
|
|
37
37
|
"commander": "^14.0.0",
|
|
38
38
|
"qrcode": "^1.5.4",
|
|
39
39
|
"tar-stream": "^3.1.7",
|