busabase-sdk 0.20.0 → 0.30.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/airapp-check.d.ts +117 -0
- package/dist/airapp-check.js +159 -0
- package/dist/airapp-node.d.ts +34 -1
- package/dist/airapp-node.js +22 -1
- package/dist/airapp.d.ts +1 -1
- package/dist/airapp.js +30 -5
- package/dist/{client-DF6mrd6D.d.ts → client-CVz2pqrF.d.ts} +51 -4
- package/dist/index.d.ts +3 -2
- package/dist/index.js +102 -25
- package/package.json +6 -2
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//#region src/airapp-check.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The AirApp runtime contract, as checkable rules.
|
|
4
|
+
*
|
|
5
|
+
* These live in the SDK for one reason: **a rule and the runtime it checks must
|
|
6
|
+
* ship together.** The rules below are about `BUSABASE_AIRAPP_RUNTIME`,
|
|
7
|
+
* `__airapp/runtime`, `createBusabaseClient` and `isBusabaseAirAppHosted` — all
|
|
8
|
+
* of which are defined a few files away. Keeping the rules anywhere else means
|
|
9
|
+
* the two drift, and drift here is not hypothetical: an engine rename from
|
|
10
|
+
* `local-node` to `local` broke 66 shipped apps whose runtime detection carried
|
|
11
|
+
* its own private copy of the truth.
|
|
12
|
+
*
|
|
13
|
+
* It also makes them *reachable*. Every AirApp already depends on `busabase-sdk`
|
|
14
|
+
* at an exact pin, so an app's `scripts/check.mjs` shrinks from dozens of
|
|
15
|
+
* hand-copied assertions to reading its files and calling one function. The
|
|
16
|
+
* previous delivery mechanism was "copy these into your check script", and it
|
|
17
|
+
* reached 4 apps out of 67.
|
|
18
|
+
*
|
|
19
|
+
* Pure by design: this module reads no files, spawns nothing, and touches no
|
|
20
|
+
* network. Callers hand it source text. That keeps it usable from a CLI, from an
|
|
21
|
+
* app's own check script, and from a test, without any of them agreeing on a
|
|
22
|
+
* filesystem layout.
|
|
23
|
+
*/
|
|
24
|
+
/** Severity split: an error breaks a user of the app; a warning is a default worth defending. */
|
|
25
|
+
type AirAppFindingSeverity = "error" | "warning";
|
|
26
|
+
interface AirAppFinding {
|
|
27
|
+
severity: AirAppFindingSeverity;
|
|
28
|
+
/** Stable kebab-case id, so a caller can allowlist or group without matching prose. */
|
|
29
|
+
rule: string;
|
|
30
|
+
message: string;
|
|
31
|
+
}
|
|
32
|
+
interface AirAppSources {
|
|
33
|
+
/** Raw `package.json` text. */
|
|
34
|
+
packageJson?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The host's source. **Concatenate the whole server subtree**, not just the entry
|
|
37
|
+
* file — a `server.js` that mounts `server/hono.ts` keeps the runtime route in the
|
|
38
|
+
* module, and passing only the entry reports a correct app as broken. (Observed:
|
|
39
|
+
* two shipped apps failed this way against a collector that read `server.js` alone.)
|
|
40
|
+
*/
|
|
41
|
+
server?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Which language the host is written in. Inferred from Python syntax when omitted.
|
|
44
|
+
*
|
|
45
|
+
* NOT the AirApp *runtime* — that is the engine Busabase spawned the process in
|
|
46
|
+
* (`nodepod`, `local`, `sandock`, …), which is a value the app reports at runtime,
|
|
47
|
+
* not a property of its source. Two different things called "runtime" in one
|
|
48
|
+
* domain is how the wrong one ends up being checked.
|
|
49
|
+
*/
|
|
50
|
+
serverLanguage?: "node" | "python";
|
|
51
|
+
/**
|
|
52
|
+
* The browser files that carry **logic** — app, config, client, runtime probe,
|
|
53
|
+
* the Busabase provider. The structural rules run over this corpus and no wider.
|
|
54
|
+
*
|
|
55
|
+
* Excluding string tables and demo data is deliberate rather than an oversight:
|
|
56
|
+
* an asset-path or hostname rule false-positives on UI copy that merely *talks*
|
|
57
|
+
* about localhost or shows a path in an error message.
|
|
58
|
+
*/
|
|
59
|
+
browserLogic?: string;
|
|
60
|
+
/**
|
|
61
|
+
* **Everything** the browser downloads, copy and `index.html` included.
|
|
62
|
+
*
|
|
63
|
+
* Credentials are scanned over this wider corpus, because a key pasted into a
|
|
64
|
+
* string table ships to the browser exactly like one pasted into `app.js` — and
|
|
65
|
+
* used to pass a gate that only looked at the logic files.
|
|
66
|
+
*
|
|
67
|
+
* **Exclude `app/vendor/`.** A bundled `busabase-sdk` legitimately builds an
|
|
68
|
+
* `Authorization: Bearer …` header from a resolved key, so sweeping the vendor
|
|
69
|
+
* directory in reports every app that bundles the SDK as leaking a credential.
|
|
70
|
+
* List the app's own files; never glob the whole `app/` tree.
|
|
71
|
+
*/
|
|
72
|
+
browserDownloads?: string;
|
|
73
|
+
/** `app/js/config.js`, when the app declares its own resources. */
|
|
74
|
+
config?: string;
|
|
75
|
+
/** The slug the package ships this app under, for the `resourceKey` rule. */
|
|
76
|
+
shippedSlug?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The documented interactive page budget. A higher one may be justified — say so in
|
|
80
|
+
* review — so this is a warning rather than a hard bound. A generator checking its
|
|
81
|
+
* own output is free to be stricter.
|
|
82
|
+
*/
|
|
83
|
+
declare const AIRAPP_DEFAULT_READ_LIMIT = 50;
|
|
84
|
+
/**
|
|
85
|
+
* Strip comments before pattern-matching source.
|
|
86
|
+
*
|
|
87
|
+
* Load-bearing, not tidiness. A file that explains a rule necessarily *names* the
|
|
88
|
+
* thing the rule forbids, and matching that prose let a server which had genuinely
|
|
89
|
+
* stopped reading `BUSABASE_AIRAPP_RUNTIME` pass its gate — the comment about the
|
|
90
|
+
* variable satisfied the check for the variable. Prose about a rule must never
|
|
91
|
+
* satisfy the rule.
|
|
92
|
+
*/
|
|
93
|
+
declare const stripComments: (source: string) => string;
|
|
94
|
+
/**
|
|
95
|
+
* Objects inside a `bases: [ ... ]` literal, brace-matched rather than regexed across
|
|
96
|
+
* the array, so a nested `fields:` entry is never mistaken for a Base.
|
|
97
|
+
*
|
|
98
|
+
* Reading the declaration without evaluating the module is the point: a checker must
|
|
99
|
+
* not execute the app it is checking.
|
|
100
|
+
*/
|
|
101
|
+
declare const scanAirAppConfig: (source: string) => {
|
|
102
|
+
bases: {
|
|
103
|
+
key: string | null;
|
|
104
|
+
slug: string | null;
|
|
105
|
+
}[];
|
|
106
|
+
drive: {
|
|
107
|
+
slug: string | null;
|
|
108
|
+
} | null;
|
|
109
|
+
resourceKey: string | null;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Check an AirApp against the runtime contract. Returns findings; never throws for a
|
|
113
|
+
* rule violation, so one caller can report every problem at once instead of the first.
|
|
114
|
+
*/
|
|
115
|
+
declare const checkAirApp: (sources: AirAppSources) => AirAppFinding[];
|
|
116
|
+
//#endregion
|
|
117
|
+
export { AIRAPP_DEFAULT_READ_LIMIT, AirAppFinding, AirAppFindingSeverity, AirAppSources, checkAirApp, scanAirAppConfig, stripComments };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
//#region src/airapp-check.ts
|
|
2
|
+
/** A `busabase-sdk` version must be exact. A range is not the app that was reviewed. */
|
|
3
|
+
const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
4
|
+
/** A `start` script may start the server and nothing else. */
|
|
5
|
+
const START_IS_A_BUILD = /(?:&&|\|\||;|\bnpm\s+run\s+build\b|\btsc\b|\bvite\b|\bwebpack\b|\bparcel\b)/;
|
|
6
|
+
/** An AirApp is Hono plus vanilla browser code. `esbuild` is how the SDK gets bundled. */
|
|
7
|
+
const FORBIDDEN_DEPENDENCIES = [
|
|
8
|
+
"react",
|
|
9
|
+
"react-dom",
|
|
10
|
+
"preact",
|
|
11
|
+
"vite",
|
|
12
|
+
"@vitejs/plugin-react",
|
|
13
|
+
"next",
|
|
14
|
+
"webpack",
|
|
15
|
+
"parcel"
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* The documented interactive page budget. A higher one may be justified — say so in
|
|
19
|
+
* review — so this is a warning rather than a hard bound. A generator checking its
|
|
20
|
+
* own output is free to be stricter.
|
|
21
|
+
*/
|
|
22
|
+
const AIRAPP_DEFAULT_READ_LIMIT = 50;
|
|
23
|
+
/**
|
|
24
|
+
* Strip comments before pattern-matching source.
|
|
25
|
+
*
|
|
26
|
+
* Load-bearing, not tidiness. A file that explains a rule necessarily *names* the
|
|
27
|
+
* thing the rule forbids, and matching that prose let a server which had genuinely
|
|
28
|
+
* stopped reading `BUSABASE_AIRAPP_RUNTIME` pass its gate — the comment about the
|
|
29
|
+
* variable satisfied the check for the variable. Prose about a rule must never
|
|
30
|
+
* satisfy the rule.
|
|
31
|
+
*/
|
|
32
|
+
const stripComments = (source) => source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:'"`\\])\/\/[^\n]*/g, "$1 ");
|
|
33
|
+
/**
|
|
34
|
+
* Objects inside a `bases: [ ... ]` literal, brace-matched rather than regexed across
|
|
35
|
+
* the array, so a nested `fields:` entry is never mistaken for a Base.
|
|
36
|
+
*
|
|
37
|
+
* Reading the declaration without evaluating the module is the point: a checker must
|
|
38
|
+
* not execute the app it is checking.
|
|
39
|
+
*/
|
|
40
|
+
const scanAirAppConfig = (source) => {
|
|
41
|
+
const anchor = /\bbases\s*:\s*\[/.exec(source);
|
|
42
|
+
const blocks = [];
|
|
43
|
+
if (anchor) {
|
|
44
|
+
let depth = 0;
|
|
45
|
+
let start = -1;
|
|
46
|
+
for (let index = anchor.index + anchor[0].length - 1; index < source.length; index += 1) {
|
|
47
|
+
const char = source[index];
|
|
48
|
+
if (char === "[") depth += 1;
|
|
49
|
+
else if (char === "]") {
|
|
50
|
+
depth -= 1;
|
|
51
|
+
if (depth === 0) break;
|
|
52
|
+
} else if (char === "{" && depth === 1) {
|
|
53
|
+
if (start === -1) start = index;
|
|
54
|
+
depth += 1;
|
|
55
|
+
} else if (char === "{") depth += 1;
|
|
56
|
+
else if (char === "}") {
|
|
57
|
+
depth -= 1;
|
|
58
|
+
if (depth === 1 && start !== -1) {
|
|
59
|
+
blocks.push(source.slice(start, index + 1));
|
|
60
|
+
start = -1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const literal = (block, key) => new RegExp(`\\b${key}\\s*:\\s*"([^"]*)"`).exec(block)?.[1] ?? null;
|
|
66
|
+
const drive = /\bdrive\s*:\s*\{([\s\S]*?)\}/.exec(source);
|
|
67
|
+
return {
|
|
68
|
+
bases: blocks.map((block) => ({
|
|
69
|
+
key: literal(block, "key"),
|
|
70
|
+
slug: literal(block, "slug")
|
|
71
|
+
})),
|
|
72
|
+
drive: drive ? { slug: literal(drive[1], "slug") } : null,
|
|
73
|
+
resourceKey: /\bairApp\s*:\s*\{[^}]*\bresourceKey\s*:\s*"([^"]*)"/.exec(source)?.[1] ?? null
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Check an AirApp against the runtime contract. Returns findings; never throws for a
|
|
78
|
+
* rule violation, so one caller can report every problem at once instead of the first.
|
|
79
|
+
*/
|
|
80
|
+
const checkAirApp = (sources) => {
|
|
81
|
+
const findings = [];
|
|
82
|
+
const error = (rule, message) => findings.push({
|
|
83
|
+
severity: "error",
|
|
84
|
+
rule,
|
|
85
|
+
message
|
|
86
|
+
});
|
|
87
|
+
const warn = (rule, message) => findings.push({
|
|
88
|
+
severity: "warning",
|
|
89
|
+
rule,
|
|
90
|
+
message
|
|
91
|
+
});
|
|
92
|
+
if (sources.packageJson !== void 0) {
|
|
93
|
+
let manifest;
|
|
94
|
+
try {
|
|
95
|
+
manifest = JSON.parse(sources.packageJson);
|
|
96
|
+
} catch (cause) {
|
|
97
|
+
error("airapp/package-json", `package.json is not valid JSON (${cause.message}).`);
|
|
98
|
+
}
|
|
99
|
+
if (manifest) {
|
|
100
|
+
const scripts = manifest.scripts ?? {};
|
|
101
|
+
if (typeof scripts.dev !== "string") error("airapp/dev-script", "package.json has no `dev` script. Busabase boots an app with `npm run dev`, so it installs cleanly and then never starts.");
|
|
102
|
+
if (typeof scripts.start === "string" && START_IS_A_BUILD.test(scripts.start)) error("airapp/start-pure", `\`start\` is "${scripts.start}" — a deployed start must only run the server, never build or spawn.`);
|
|
103
|
+
const dependencies = {
|
|
104
|
+
...manifest.dependencies ?? {},
|
|
105
|
+
...manifest.devDependencies ?? {}
|
|
106
|
+
};
|
|
107
|
+
const sdk = manifest.dependencies?.["busabase-sdk"];
|
|
108
|
+
if (sdk === void 0) warn("airapp/sdk", "No `busabase-sdk` dependency — the app cannot reach the workspace.");
|
|
109
|
+
else if (!EXACT_VERSION.test(String(sdk))) error("airapp/sdk-pin", `busabase-sdk is "${String(sdk)}". Pin the exact version — a range means the installed app is not the one that was reviewed.`);
|
|
110
|
+
for (const forbidden of FORBIDDEN_DEPENDENCIES) if (dependencies[forbidden] !== void 0) error("airapp/no-framework", `Depends on "${forbidden}". An AirApp is Hono plus vanilla HTML/CSS/JS; its files are reviewed and then run as-is, so nothing may need compiling first.`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (sources.browserLogic !== void 0) {
|
|
114
|
+
const logic = sources.browserLogic;
|
|
115
|
+
const code = stripComments(logic);
|
|
116
|
+
if (!logic.includes("createBusabaseClient")) warn("airapp/sdk-client", "Browser code never calls `createBusabaseClient`.");
|
|
117
|
+
if (logic.includes("__busabase_api__")) error("airapp/legacy-bridge", "The obsolete `/__busabase_api__/` bridge prefix is gone. The API is same-origin `/api/v1`.");
|
|
118
|
+
if (/baseUrl\s*:\s*["'`]https?:\/\//.test(code)) error("airapp/absolute-url", "A hard-coded absolute Busabase URL in browser code. Use `window.location.origin` — an absolute URL is right in exactly one deployment and wrong in the rest.");
|
|
119
|
+
if (/(?:src|href)="\/(?!\/)|from\s+["']\/(?!\/)/.test(code)) error("airapp/absolute-asset", "An absolute asset path. Under the Local Node engine the app is proxied onto a sub-path of Busabase's origin, so a leading slash resolves against Busabase itself and 404s.");
|
|
120
|
+
if (/while\s*\(\s*cursor\s*\)|client\.bases\.list\s*\(/.test(code) || /while\s*\(\s*true\s*\)[\s\S]{0,400}?(?:records\.list|readPage)\s*\(/.test(code)) error("airapp/unbounded-read", "Unbounded loading or runtime Base discovery. Every interactive read gets an explicit budget and fetches one page per user action.");
|
|
121
|
+
if (/\bmax\w*pages?\w*\s*=\s*\d+[\s\S]{0,400}?(?:client\.)?records\.list\s*\(/i.test(code)) error("airapp/eager-multi-page", "A capped loop fetches several pages of records in one function call. A cap bounds the damage but does not fix the shape: this still hides a multi-page scan behind one loading state instead of fetching one page per user action.");
|
|
122
|
+
const branches = /createAirAppConnectGate|getRuntime\s*\(|\bhosted\b/.test(code) || sources.server !== void 0 && /createAirAppConnectGate/.test(sources.server);
|
|
123
|
+
if (/location\s*\.\s*(?:hostname|host)\b/.test(code)) error("airapp/runtime-hostname", "Hostname-based runtime detection. Both directions are wrong: a Busabase-hosted AirApp is served from `localhost` on Desktop, and a standalone `npm run dev` is reached over a LAN IP or a signed dev tunnel. Read the runtime from `__airapp/runtime`.");
|
|
124
|
+
if (/(?:===|!==|==|!=)\s*["'`][^"'`]*(?:localhost|127\.0\.0\.1)|(?:includes|startsWith|endsWith|indexOf|search|match|test)\s*\(\s*\/?["'`]?[^"'`)]*(?:localhost|127\.0\.0\.1)/.test(code)) error("airapp/runtime-loopback", "A loopback host comparison. Runtime detection must not depend on the URL at all.");
|
|
125
|
+
if (branches && !logic.includes("__airapp/runtime")) error("airapp/runtime-probe", "This app branches on where it is running but never probes `__airapp/runtime`, so that branch is deciding on something else.");
|
|
126
|
+
if (/["'`]\/__airapp\/runtime/.test(logic)) error("airapp/runtime-probe-relative", "The runtime probe has a leading slash. It must be relative (`__airapp/runtime`) — a leading slash resolves against Busabase's root under the Local Node sub-path proxy.");
|
|
127
|
+
}
|
|
128
|
+
if (sources.server !== void 0) {
|
|
129
|
+
const server = sources.server;
|
|
130
|
+
const serverCode = stripComments(server);
|
|
131
|
+
const readsRuntimeEnv = (sources.serverLanguage ? sources.serverLanguage === "python" : /^\s*(?:import|from)\s+\w+|def\s+\w+\s*\(/m.test(server)) ? /BUSABASE_AIRAPP_RUNTIME/.test(serverCode) : /(?:read|describe)BusabaseAirAppRuntime\s*\(|process\.env\.BUSABASE_AIRAPP_RUNTIME\b/.test(serverCode);
|
|
132
|
+
const clientBranches = sources.browserLogic !== void 0 && /createAirAppConnectGate|getRuntime\s*\(|\bhosted\b/.test(stripComments(sources.browserLogic));
|
|
133
|
+
if (clientBranches && !readsRuntimeEnv) error("airapp/runtime-env", "The server never reads `BUSABASE_AIRAPP_RUNTIME`, directly or through the SDK, so it has nothing to serve at `__airapp/runtime`.");
|
|
134
|
+
if (/AIRAPP_HOSTED_RUNTIMES\s*=\s*new Set|hosted:\s*\w*RUNTIMES?\w*\.has\s*\(/.test(serverCode)) error("airapp/runtime-engine-list", "Hosting is decided from a hardcoded list of engine names. Use presence, not membership — a private list is what broke 66 apps when `local-node` was renamed `local`.");
|
|
135
|
+
if (clientBranches && !/["'`]\/__airapp\/runtime["'`]/.test(server)) error("airapp/runtime-route", "Browser code branches on the runtime but the server does not serve `/__airapp/runtime`.");
|
|
136
|
+
if (/Bearer\s+(?!\$\{)[A-Za-z0-9_-]{8,}/.test(server)) error("airapp/credential", "A literal Bearer token in the server source.");
|
|
137
|
+
}
|
|
138
|
+
if (sources.browserDownloads !== void 0) {
|
|
139
|
+
const downloads = sources.browserDownloads;
|
|
140
|
+
if (/BUSABASE_API_KEY/i.test(downloads)) error("airapp/credential", "An API key reference in browser source.");
|
|
141
|
+
if (/Bearer\s+(?!\$\{)[A-Za-z0-9_.-]{8,}/.test(downloads)) error("airapp/credential", "A literal Bearer token in browser source.");
|
|
142
|
+
if (/["'`]\s*Bearer\s*\$\{/.test(downloads)) error("airapp/credential", "Browser code builds an Authorization header. A deployed AirApp uses the viewer's ambient same-origin session and needs none.");
|
|
143
|
+
}
|
|
144
|
+
if (sources.config !== void 0) {
|
|
145
|
+
const config = scanAirAppConfig(sources.config);
|
|
146
|
+
if (sources.shippedSlug !== void 0 && config.resourceKey !== null && config.resourceKey !== sources.shippedSlug) error("airapp/resource-key", `Config declares resourceKey "${config.resourceKey}" but the package ships this app as "${sources.shippedSlug}". Install stamps nodes with the shipped slug, so the app would not recognise its own node.`);
|
|
147
|
+
for (const base of config.bases) if (base.slug === null) error("airapp/base-slug", `Config base "${base.key ?? "(unnamed)"}" has no \`slug\`. The SDK needs it to CREATE the Base, so the app's own provisioning fails even though installing from the package succeeds — install reads base.json and never opens that door.`);
|
|
148
|
+
if (config.drive !== null && config.drive.slug === null) error("airapp/base-slug", "Config `drive` has no `slug`; provisioning it will fail.");
|
|
149
|
+
if (/vaultValue|vaultSecret\s*:\s*["'`][^"'`]/.test(sources.config)) error("airapp/vault-value", "A Vault value in config. Config may reference secrets, never carry them.");
|
|
150
|
+
for (const found of sources.config.matchAll(/\breadLimit\s*:\s*(\d+)/g)) {
|
|
151
|
+
const limit = Number(found[1]);
|
|
152
|
+
if (limit < 1) error("airapp/read-budget", `readLimit is ${limit}; it must be a positive integer.`);
|
|
153
|
+
else if (limit > 50) warn("airapp/read-budget", `readLimit is ${limit}, above the 50-record default page budget. Justify it in review, or page.`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return findings;
|
|
157
|
+
};
|
|
158
|
+
//#endregion
|
|
159
|
+
export { AIRAPP_DEFAULT_READ_LIMIT, checkAirApp, scanAirAppConfig, stripComments };
|
package/dist/airapp-node.d.ts
CHANGED
|
@@ -46,6 +46,39 @@ declare const isBusabaseAirAppHosted: (runtime?: string) => boolean;
|
|
|
46
46
|
* behaviour rather than break.
|
|
47
47
|
*/
|
|
48
48
|
declare const asKnownBusabaseAirAppRuntime: (runtime: string) => BusabaseAirAppRuntime | null;
|
|
49
|
+
/** The body every AirApp serves at `__airapp/runtime`. */
|
|
50
|
+
interface BusabaseAirAppRuntimeReport {
|
|
51
|
+
/**
|
|
52
|
+
* The engine name verbatim, or `"standalone"` when nothing hosted this process.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately a plain string and not the enum: a newer Busabase naming an engine
|
|
55
|
+
* this SDK predates must still round-trip, and narrowing here would erase it.
|
|
56
|
+
*/
|
|
57
|
+
runtime: string;
|
|
58
|
+
/**
|
|
59
|
+
* The same value narrowed to what this SDK knows, or `null` for an engine it has
|
|
60
|
+
* never heard of. This is the field to branch on when behaviour genuinely differs
|
|
61
|
+
* per engine — and `null` is a normal answer, not an error.
|
|
62
|
+
*/
|
|
63
|
+
knownRuntime: BusabaseAirAppRuntime | null;
|
|
64
|
+
/** Presence of the injected variable. Never membership of {@link BUSABASE_AIRAPP_RUNTIMES}. */
|
|
65
|
+
hosted: boolean;
|
|
66
|
+
/** Whether a standalone run is proxying to a Busabase base URL. A separate axis. */
|
|
67
|
+
devProxy: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build the `__airapp/runtime` body.
|
|
71
|
+
*
|
|
72
|
+
* This exists because the one line it replaces — `hosted: …` — has now regressed
|
|
73
|
+
* twice. Both times a correct app was copied from an older source and came back
|
|
74
|
+
* deciding hosting from a hardcoded list of engine names, and both times every test
|
|
75
|
+
* stayed green, because nothing asserted the shape of that decision.
|
|
76
|
+
*
|
|
77
|
+
* An app that calls this cannot get it wrong: `hosted` is presence by construction,
|
|
78
|
+
* and the engine name is reported both verbatim (so an unknown one survives) and
|
|
79
|
+
* narrowed (so code that wants to branch on a specific engine has a type).
|
|
80
|
+
*/
|
|
81
|
+
declare const describeBusabaseAirAppRuntime: (env?: Record<string, string | undefined>) => BusabaseAirAppRuntimeReport;
|
|
49
82
|
declare const BUSABASE_AIRAPP_GATEWAY_REASONS: {
|
|
50
83
|
readonly authRequired: "AUTH_REQUIRED";
|
|
51
84
|
readonly authUnavailable: "AUTH_UNAVAILABLE";
|
|
@@ -108,4 +141,4 @@ declare class BusabaseAirAppLocalGateway {
|
|
|
108
141
|
}
|
|
109
142
|
declare const createBusabaseAirAppLocalGateway: (options: BusabaseAirAppLocalGatewayOptions) => BusabaseAirAppLocalGateway;
|
|
110
143
|
//#endregion
|
|
111
|
-
export { BUSABASE_AIRAPP_GATEWAY_REASONS, BUSABASE_AIRAPP_RUNTIMES, BUSABASE_AIRAPP_RUNTIME_ENV, BusabaseAirAppAuthStatus, BusabaseAirAppLocalGateway, BusabaseAirAppLocalGatewayOptions, BusabaseAirAppRuntime, asKnownBusabaseAirAppRuntime, createBusabaseAirAppLocalGateway, isBusabaseAirAppHosted, readBusabaseAirAppRuntime };
|
|
144
|
+
export { BUSABASE_AIRAPP_GATEWAY_REASONS, BUSABASE_AIRAPP_RUNTIMES, BUSABASE_AIRAPP_RUNTIME_ENV, BusabaseAirAppAuthStatus, BusabaseAirAppLocalGateway, BusabaseAirAppLocalGatewayOptions, BusabaseAirAppRuntime, BusabaseAirAppRuntimeReport, asKnownBusabaseAirAppRuntime, createBusabaseAirAppLocalGateway, describeBusabaseAirAppRuntime, isBusabaseAirAppHosted, readBusabaseAirAppRuntime };
|
package/dist/airapp-node.js
CHANGED
|
@@ -55,6 +55,27 @@ const isBusabaseAirAppHosted = (runtime = readBusabaseAirAppRuntime()) => runtim
|
|
|
55
55
|
* behaviour rather than break.
|
|
56
56
|
*/
|
|
57
57
|
const asKnownBusabaseAirAppRuntime = (runtime) => BUSABASE_AIRAPP_RUNTIMES.includes(runtime) ? runtime : null;
|
|
58
|
+
/**
|
|
59
|
+
* Build the `__airapp/runtime` body.
|
|
60
|
+
*
|
|
61
|
+
* This exists because the one line it replaces — `hosted: …` — has now regressed
|
|
62
|
+
* twice. Both times a correct app was copied from an older source and came back
|
|
63
|
+
* deciding hosting from a hardcoded list of engine names, and both times every test
|
|
64
|
+
* stayed green, because nothing asserted the shape of that decision.
|
|
65
|
+
*
|
|
66
|
+
* An app that calls this cannot get it wrong: `hosted` is presence by construction,
|
|
67
|
+
* and the engine name is reported both verbatim (so an unknown one survives) and
|
|
68
|
+
* narrowed (so code that wants to branch on a specific engine has a type).
|
|
69
|
+
*/
|
|
70
|
+
const describeBusabaseAirAppRuntime = (env = process.env) => {
|
|
71
|
+
const runtime = readBusabaseAirAppRuntime(env);
|
|
72
|
+
return {
|
|
73
|
+
runtime: runtime || "standalone",
|
|
74
|
+
knownRuntime: asKnownBusabaseAirAppRuntime(runtime),
|
|
75
|
+
hosted: isBusabaseAirAppHosted(runtime),
|
|
76
|
+
devProxy: (env.BUSABASE_BASE_URL || "").trim() !== ""
|
|
77
|
+
};
|
|
78
|
+
};
|
|
58
79
|
const BUSABASE_AIRAPP_GATEWAY_REASONS = {
|
|
59
80
|
authRequired: "AUTH_REQUIRED",
|
|
60
81
|
authUnavailable: "AUTH_UNAVAILABLE",
|
|
@@ -427,4 +448,4 @@ var BusabaseAirAppLocalGateway = class {
|
|
|
427
448
|
};
|
|
428
449
|
const createBusabaseAirAppLocalGateway = (options) => new BusabaseAirAppLocalGateway(options);
|
|
429
450
|
//#endregion
|
|
430
|
-
export { BUSABASE_AIRAPP_GATEWAY_REASONS, BUSABASE_AIRAPP_RUNTIMES, BUSABASE_AIRAPP_RUNTIME_ENV, BusabaseAirAppLocalGateway, asKnownBusabaseAirAppRuntime, createBusabaseAirAppLocalGateway, isBusabaseAirAppHosted, readBusabaseAirAppRuntime };
|
|
451
|
+
export { BUSABASE_AIRAPP_GATEWAY_REASONS, BUSABASE_AIRAPP_RUNTIMES, BUSABASE_AIRAPP_RUNTIME_ENV, BusabaseAirAppLocalGateway, asKnownBusabaseAirAppRuntime, createBusabaseAirAppLocalGateway, describeBusabaseAirAppRuntime, isBusabaseAirAppHosted, readBusabaseAirAppRuntime };
|
package/dist/airapp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as BusabaseClient } from "./client-
|
|
1
|
+
import { t as BusabaseClient } from "./client-CVz2pqrF.js";
|
|
2
2
|
//#region src/airapp.d.ts
|
|
3
3
|
type NodeChangeRequestInput = Parameters<BusabaseClient["nodes"]["createChangeRequest"]>[0];
|
|
4
4
|
type NodeOperationInput = NodeChangeRequestInput["operations"][number];
|
package/dist/airapp.js
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
//#region ../../packages/busabase-contract/src/domains/skill/frontmatter.ts
|
|
3
|
+
/**
|
|
4
|
+
* `SKILL.md` frontmatter — the generic shape, independent of Busabase.
|
|
5
|
+
*
|
|
6
|
+
* This used to live inside `domains/package/template.ts`, which encoded an
|
|
7
|
+
* assumption that no longer holds: that a Skill is a *part of* a template. Most
|
|
8
|
+
* Skills are not. A Skill is a directory with a `SKILL.md` an agent reads; it
|
|
9
|
+
* needs no `busabase.json`, ships no `content/`, and never installs into a
|
|
10
|
+
* workspace. Busabase's own `busabase-app-creator` is one.
|
|
11
|
+
*
|
|
12
|
+
* Modelling it only as a template component meant there was nowhere to hang
|
|
13
|
+
* checks for a plain Skill, so there were none. Hence its own file, in the
|
|
14
|
+
* domain it belongs to.
|
|
15
|
+
*
|
|
16
|
+
* `metadata` is deliberately open: a Skill carries whatever its ecosystem
|
|
17
|
+
* defines (categories, tags, risk labels, per-agent hints). The Busabase-specific
|
|
18
|
+
* block that decides template-ness is layered on in `domains/package/template.ts`
|
|
19
|
+
* — the template format knows about Skills, not the other way round.
|
|
20
|
+
*/
|
|
21
|
+
const SkillFrontmatterSchema$1 = z.object({
|
|
22
|
+
/** Identity. For a Skill inside a package this must equal the package name. */
|
|
23
|
+
name: z.string().min(1),
|
|
24
|
+
/**
|
|
25
|
+
* How an agent decides whether to reach for this Skill at all — so an empty one
|
|
26
|
+
* is not a cosmetic omission, it is a Skill that never gets picked.
|
|
27
|
+
*/
|
|
28
|
+
description: z.string().default(""),
|
|
29
|
+
metadata: z.object({}).passthrough().optional()
|
|
30
|
+
});
|
|
2
31
|
const TemplateAirAppRefSchema = z.object({
|
|
3
32
|
/** Slug of the `content/<dir>` holding the AirApp. */
|
|
4
33
|
slug: z.string().min(1),
|
|
@@ -70,11 +99,7 @@ const SkillBusabaseMetadataSchema = z.object({
|
|
|
70
99
|
resources: z.array(z.string()).default([]),
|
|
71
100
|
risk: z.string().optional()
|
|
72
101
|
});
|
|
73
|
-
z.object({
|
|
74
|
-
name: z.string().min(1),
|
|
75
|
-
description: z.string().default(""),
|
|
76
|
-
metadata: z.object({ busabase: SkillBusabaseMetadataSchema.optional() }).passthrough().optional()
|
|
77
|
-
});
|
|
102
|
+
SkillFrontmatterSchema$1.extend({ metadata: z.object({ busabase: SkillBusabaseMetadataSchema.optional() }).passthrough().optional() });
|
|
78
103
|
/** Stamp on every resource node (Base, Drive, AirApp, …) an app owns. */
|
|
79
104
|
const AppResourceOwnershipSchema = z.object({
|
|
80
105
|
appId: z.string().min(1),
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { ContractRouterClient } from "@orpc/contract";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
//#region ../../packages/busabase-contract/src/domains/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared interfaces for node-type modules. Each node type lives in its own module
|
|
6
|
+
* (./folder, ./base, ./skill, …) and exports a definition that satisfies
|
|
7
|
+
* `NodeTypeDefinition`; the registry composes + registers them.
|
|
8
|
+
*/
|
|
9
|
+
type NodePublicAccess = "detail" | "submit" | "runtime" | "no";
|
|
10
|
+
//#endregion
|
|
3
11
|
//#region ../../packages/busabase-contract/src/domains/registry.d.ts
|
|
4
12
|
declare const GENERIC_NODE_OPERATIONS: readonly [{
|
|
5
13
|
readonly kind: "node_create";
|
|
@@ -32,6 +40,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
32
40
|
readonly container: true;
|
|
33
41
|
readonly creatable: true;
|
|
34
42
|
readonly hasDetail: true;
|
|
43
|
+
readonly publicAccess: "detail";
|
|
35
44
|
};
|
|
36
45
|
readonly operations: readonly [];
|
|
37
46
|
}, {
|
|
@@ -41,6 +50,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
41
50
|
readonly capabilities: {
|
|
42
51
|
readonly hasDetail: true;
|
|
43
52
|
readonly creatable: true;
|
|
53
|
+
readonly publicAccess: "detail";
|
|
44
54
|
};
|
|
45
55
|
readonly operations: readonly [{
|
|
46
56
|
readonly kind: "record_create";
|
|
@@ -118,6 +128,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
118
128
|
readonly capabilities: {
|
|
119
129
|
readonly hasDetail: true;
|
|
120
130
|
readonly creatable: true;
|
|
131
|
+
readonly publicAccess: NodePublicAccess;
|
|
121
132
|
};
|
|
122
133
|
readonly operations: readonly [{
|
|
123
134
|
readonly kind: `${string}_file_create`;
|
|
@@ -143,6 +154,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
143
154
|
readonly capabilities: {
|
|
144
155
|
readonly hasDetail: true;
|
|
145
156
|
readonly creatable: true;
|
|
157
|
+
readonly publicAccess: NodePublicAccess;
|
|
146
158
|
};
|
|
147
159
|
readonly operations: readonly [{
|
|
148
160
|
readonly kind: `${string}_file_create`;
|
|
@@ -168,6 +180,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
168
180
|
readonly capabilities: {
|
|
169
181
|
readonly hasDetail: true;
|
|
170
182
|
readonly creatable: true;
|
|
183
|
+
readonly publicAccess: NodePublicAccess;
|
|
171
184
|
};
|
|
172
185
|
readonly operations: readonly [{
|
|
173
186
|
readonly kind: `${string}_file_create`;
|
|
@@ -193,6 +206,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
193
206
|
readonly capabilities: {
|
|
194
207
|
readonly hasDetail: true;
|
|
195
208
|
readonly creatable: true;
|
|
209
|
+
readonly publicAccess: "detail";
|
|
196
210
|
};
|
|
197
211
|
readonly operations: readonly [];
|
|
198
212
|
}, {
|
|
@@ -202,6 +216,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
202
216
|
readonly capabilities: {
|
|
203
217
|
readonly hasDetail: true;
|
|
204
218
|
readonly creatable: true;
|
|
219
|
+
readonly publicAccess: "detail";
|
|
205
220
|
};
|
|
206
221
|
readonly operations: readonly [{
|
|
207
222
|
readonly kind: "doc_update";
|
|
@@ -215,6 +230,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
215
230
|
readonly capabilities: {
|
|
216
231
|
readonly hasDetail: true;
|
|
217
232
|
readonly creatable: true;
|
|
233
|
+
readonly publicAccess: "submit";
|
|
218
234
|
};
|
|
219
235
|
readonly operations: readonly [];
|
|
220
236
|
}, {
|
|
@@ -224,6 +240,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
224
240
|
readonly capabilities: {
|
|
225
241
|
readonly hasDetail: true;
|
|
226
242
|
readonly creatable: true;
|
|
243
|
+
readonly publicAccess: "detail";
|
|
227
244
|
};
|
|
228
245
|
readonly operations: readonly [{
|
|
229
246
|
readonly kind: "whiteboard_document_update";
|
|
@@ -237,6 +254,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
237
254
|
readonly capabilities: {
|
|
238
255
|
readonly hasDetail: true;
|
|
239
256
|
readonly creatable: true;
|
|
257
|
+
readonly publicAccess: "detail";
|
|
240
258
|
};
|
|
241
259
|
readonly operations: readonly [{
|
|
242
260
|
readonly kind: "workflow_document_update";
|
|
@@ -250,6 +268,7 @@ declare const BUILTIN_NODE_TYPES: readonly [{
|
|
|
250
268
|
readonly capabilities: {
|
|
251
269
|
readonly hasDetail: true;
|
|
252
270
|
readonly creatable: true;
|
|
271
|
+
readonly publicAccess: "no";
|
|
253
272
|
};
|
|
254
273
|
readonly operations: readonly [{
|
|
255
274
|
readonly kind: "html_document_update";
|
|
@@ -420,12 +439,14 @@ declare const cloudContract: {
|
|
|
420
439
|
sources: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodArray<z.ZodEnum<{
|
|
421
440
|
files: "files";
|
|
422
441
|
names: "names";
|
|
442
|
+
nodes: "nodes";
|
|
423
443
|
records: "records";
|
|
424
444
|
}>>, z.ZodEnum<{
|
|
425
445
|
files: "files";
|
|
426
446
|
names: "names";
|
|
447
|
+
nodes: "nodes";
|
|
427
448
|
records: "records";
|
|
428
|
-
}>]>, z.ZodTransform<("files" | "names" | "records")[], "files" | "names" | "records" | ("files" | "names" | "records")[]>>>;
|
|
449
|
+
}>]>, z.ZodTransform<("files" | "names" | "nodes" | "records")[], "files" | "names" | "nodes" | "records" | ("files" | "names" | "nodes" | "records")[]>>>;
|
|
429
450
|
}, z.core.$strip>, z.ZodObject<{
|
|
430
451
|
query: z.ZodString;
|
|
431
452
|
limit: z.ZodNumber;
|
|
@@ -437,6 +458,7 @@ declare const cloudContract: {
|
|
|
437
458
|
base: "base";
|
|
438
459
|
change_request: "change_request";
|
|
439
460
|
file: "file";
|
|
461
|
+
node: "node";
|
|
440
462
|
record: "record";
|
|
441
463
|
}>;
|
|
442
464
|
title: z.ZodString;
|
|
@@ -445,6 +467,7 @@ declare const cloudContract: {
|
|
|
445
467
|
href: z.ZodString;
|
|
446
468
|
updatedAt: z.ZodNullable<z.ZodString>;
|
|
447
469
|
}, z.core.$strip>>;
|
|
470
|
+
contentTruncated: z.ZodDefault<z.ZodBoolean>;
|
|
448
471
|
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
449
472
|
grep: import("@orpc/contract").ContractProcedure<z.ZodObject<{
|
|
450
473
|
pattern: z.ZodString;
|
|
@@ -570,6 +593,14 @@ declare const cloudContract: {
|
|
|
570
593
|
}, z.core.$strip>, z.ZodObject<{
|
|
571
594
|
isDescendant: z.ZodBoolean;
|
|
572
595
|
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
596
|
+
ancestors: import("@orpc/contract").ContractProcedure<z.ZodObject<{
|
|
597
|
+
nodeId: z.ZodString;
|
|
598
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
599
|
+
[x: string]: string;
|
|
600
|
+
}>>;
|
|
601
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
602
|
+
ancestorIds: z.ZodArray<z.ZodString>;
|
|
603
|
+
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
573
604
|
createChangeRequest: import("@orpc/contract").ContractProcedure<z.ZodObject<{
|
|
574
605
|
message: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
575
606
|
submittedBy: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
@@ -14425,7 +14456,12 @@ declare const cloudContract: {
|
|
|
14425
14456
|
deletedSessionCount: z.ZodNumber;
|
|
14426
14457
|
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
14427
14458
|
connections: {
|
|
14428
|
-
list: import("@orpc/contract").ContractProcedure<
|
|
14459
|
+
list: import("@orpc/contract").ContractProcedure<z.ZodObject<{
|
|
14460
|
+
scope: z.ZodDefault<z.ZodEnum<{
|
|
14461
|
+
mine: "mine";
|
|
14462
|
+
space: "space";
|
|
14463
|
+
}>>;
|
|
14464
|
+
}, z.core.$strip>, z.ZodArray<z.ZodObject<{
|
|
14429
14465
|
slug: z.ZodString;
|
|
14430
14466
|
agentName: z.ZodString;
|
|
14431
14467
|
transport: z.ZodEnum<{
|
|
@@ -14453,6 +14489,7 @@ declare const cloudContract: {
|
|
|
14453
14489
|
lastActivityAt: z.ZodString;
|
|
14454
14490
|
error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
14455
14491
|
}, z.core.$strip>>;
|
|
14492
|
+
ownedByCurrentUser: z.ZodBoolean;
|
|
14456
14493
|
}, z.core.$strip>>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
14457
14494
|
};
|
|
14458
14495
|
sessions: {
|
|
@@ -14504,10 +14541,12 @@ declare const cloudContract: {
|
|
|
14504
14541
|
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14505
14542
|
kind: z.ZodEnum<{
|
|
14506
14543
|
audio: "audio";
|
|
14544
|
+
file: "file";
|
|
14507
14545
|
image: "image";
|
|
14508
14546
|
}>;
|
|
14509
14547
|
data: z.ZodString;
|
|
14510
14548
|
mimeType: z.ZodString;
|
|
14549
|
+
filename: z.ZodOptional<z.ZodString>;
|
|
14511
14550
|
}, z.core.$strip>>>;
|
|
14512
14551
|
}, z.core.$strip>, z.ZodObject<{
|
|
14513
14552
|
accepted: z.ZodBoolean;
|
|
@@ -15086,7 +15125,9 @@ declare const cloudContract: {
|
|
|
15086
15125
|
textContentHash: z.ZodNullable<z.ZodString>;
|
|
15087
15126
|
byteCount: z.ZodNumber;
|
|
15088
15127
|
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
15089
|
-
importBegin: import("@orpc/contract").ContractProcedure<
|
|
15128
|
+
importBegin: import("@orpc/contract").ContractProcedure<z.ZodObject<{
|
|
15129
|
+
sourceSpaceId: z.ZodString;
|
|
15130
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
15090
15131
|
sessionId: z.ZodString;
|
|
15091
15132
|
}, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
15092
15133
|
importTables: import("@orpc/contract").ContractProcedure<z.ZodObject<{
|
|
@@ -24581,7 +24622,7 @@ type OperationStatus = "pending" | "merged" | "archived" | "failed";
|
|
|
24581
24622
|
type ChangeRequestTargetType = "base" | "node";
|
|
24582
24623
|
type BusabaseSourceChannel = "web_ui" | "browser" | "openapi" | "sdk" | "cli" | "mcp" | "skill" | "webhook" | "automation" | "import";
|
|
24583
24624
|
type ReviewVerdict = "approved" | "rejected";
|
|
24584
|
-
type SearchResultKind = "record" | "change_request" | "base" | "file";
|
|
24625
|
+
type SearchResultKind = "record" | "change_request" | "base" | "file" | "node";
|
|
24585
24626
|
type CommentSubjectType = "record" | "change_request" | "operation" | "commit";
|
|
24586
24627
|
type AuditAction = "record.viewed" | "change_request.created" | "change_request.updated" | "change_request.deleted" | "change_request.reviewed" | "change_request.merged" | "base.created" | "field.created" | "doc.created" | "doc.updated" | "file.created" | "skill.created" | "drive.created" | "airapp.created" | "asset.deleted" | "asset.metadata_updated" | "asset.text_written" | "asset.text_marked_none" | "node.metadata_updated" | "node.purged";
|
|
24587
24628
|
interface UserRefVO {
|
|
@@ -24800,6 +24841,12 @@ interface SearchResponseVO {
|
|
|
24800
24841
|
offset: number;
|
|
24801
24842
|
hasMore: boolean;
|
|
24802
24843
|
results: SearchResultVO[];
|
|
24844
|
+
/**
|
|
24845
|
+
* True when some in-scope node content was indexed only up to the projection
|
|
24846
|
+
* cap, so this search could not see all of it. Lets a client report an empty
|
|
24847
|
+
* result honestly instead of implying the workspace lacks the phrase.
|
|
24848
|
+
*/
|
|
24849
|
+
contentTruncated: boolean;
|
|
24803
24850
|
}
|
|
24804
24851
|
interface AuditEventVO {
|
|
24805
24852
|
id: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as FileTreeReadFileVO, A as SearchResultKind, At as AttachmentRef, B as VaultRuntimeEnv, C as NodeSearchResultVO, Ct as VIEW_FIELD_MIN_WIDTH, D as ReviewVO, Dt as ViewSortVO, E as OperationVO, Et as ViewFilterVO, F as VaultAccessPolicy, Ft as CREATABLE_NODE_TYPES, G as AssetDetailVO, H as VaultSettingsVO, I as VaultEnvironment, It as CreatableNodeType, J as AssetVO, K as AssetTextStatus, L as VaultItemInput, Lt as NodeType, M as SourceAttributionVO, Mt as cloudContract, N as UserRefVO, Nt as NodeIcon, O as ReviewVerdict, Ot as ViewType, P as UpdateVaultSettingsDTO, Pt as NodeIconSchema, Q as FileTreeNodeVO, R as VaultItemKind, Rt as OperationKind, S as LookupRollup, St as VIEW_FIELD_MAX_WIDTH, T as OperationStatus, Tt as ViewFilterOperator, U as FileNodeMetadata, V as VaultScopeType, W as FileNodeVO, X as GrepResultVO, Y as GrepInputDTO, Z as FileTreeFileVO, _ as ChangeRequestVO, _t as GalleryCardSize, a as createBusabaseClient, at as FormPageSourceVO, b as CommitVO, bt as RecordLinkVO, c as AuditAction, ct as FormThemeVO, d as ChangeRequestBatchFailureVO, dt as ListFormsVO, et as NodeDetailVO, f as ChangeRequestCountsVO, ft as SubmitFormDTO, g as ChangeRequestTargetType, gt as BaseVO, h as ChangeRequestStatus, ht as BaseFieldVO, i as ResolvedConfig, it as FormFieldBindingVO, j as SearchResultVO, jt as CloudContract, k as SearchResponseVO, kt as ViewVO, l as AuditEventVO, lt as FormVO, m as ChangeRequestReviewBatchResultVO, mt as AssetAttachmentRef, n as BusabaseConfig, nt as CreateFormDTO, o as resolveConfig, ot as FormShareVO, p as ChangeRequestMergeBatchResultVO, pt as UpdateFormDTO, q as AssetUsageVO, r as DEFAULT_BASE_URL, rt as FormBoundFieldVO, s as AgentTaskVO, st as FormSubmitResultVO, t as BusabaseClient, tt as ActivityItemVO, u as BusabaseSourceChannel, ut as ListFormsDTO, v as CommentSubjectType, vt as GalleryCoverFit, w as NodeVO, wt as ViewConfigVO, x as FieldType, xt as RecordVO, y as CommentVO, yt as GanttScale, z as VaultItemVO } from "./client-
|
|
1
|
+
import { $ as FileTreeReadFileVO, A as SearchResultKind, At as AttachmentRef, B as VaultRuntimeEnv, C as NodeSearchResultVO, Ct as VIEW_FIELD_MIN_WIDTH, D as ReviewVO, Dt as ViewSortVO, E as OperationVO, Et as ViewFilterVO, F as VaultAccessPolicy, Ft as CREATABLE_NODE_TYPES, G as AssetDetailVO, H as VaultSettingsVO, I as VaultEnvironment, It as CreatableNodeType, J as AssetVO, K as AssetTextStatus, L as VaultItemInput, Lt as NodeType, M as SourceAttributionVO, Mt as cloudContract, N as UserRefVO, Nt as NodeIcon, O as ReviewVerdict, Ot as ViewType, P as UpdateVaultSettingsDTO, Pt as NodeIconSchema, Q as FileTreeNodeVO, R as VaultItemKind, Rt as OperationKind, S as LookupRollup, St as VIEW_FIELD_MAX_WIDTH, T as OperationStatus, Tt as ViewFilterOperator, U as FileNodeMetadata, V as VaultScopeType, W as FileNodeVO, X as GrepResultVO, Y as GrepInputDTO, Z as FileTreeFileVO, _ as ChangeRequestVO, _t as GalleryCardSize, a as createBusabaseClient, at as FormPageSourceVO, b as CommitVO, bt as RecordLinkVO, c as AuditAction, ct as FormThemeVO, d as ChangeRequestBatchFailureVO, dt as ListFormsVO, et as NodeDetailVO, f as ChangeRequestCountsVO, ft as SubmitFormDTO, g as ChangeRequestTargetType, gt as BaseVO, h as ChangeRequestStatus, ht as BaseFieldVO, i as ResolvedConfig, it as FormFieldBindingVO, j as SearchResultVO, jt as CloudContract, k as SearchResponseVO, kt as ViewVO, l as AuditEventVO, lt as FormVO, m as ChangeRequestReviewBatchResultVO, mt as AssetAttachmentRef, n as BusabaseConfig, nt as CreateFormDTO, o as resolveConfig, ot as FormShareVO, p as ChangeRequestMergeBatchResultVO, pt as UpdateFormDTO, q as AssetUsageVO, r as DEFAULT_BASE_URL, rt as FormBoundFieldVO, s as AgentTaskVO, st as FormSubmitResultVO, t as BusabaseClient, tt as ActivityItemVO, u as BusabaseSourceChannel, ut as ListFormsDTO, v as CommentSubjectType, vt as GalleryCoverFit, w as NodeVO, wt as ViewConfigVO, x as FieldType, xt as RecordVO, y as CommentVO, yt as GanttScale, z as VaultItemVO } from "./client-CVz2pqrF.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/url.d.ts
|
|
4
4
|
/**
|
|
@@ -317,13 +317,14 @@ declare class Busabase {
|
|
|
317
317
|
hasMore: boolean;
|
|
318
318
|
results: {
|
|
319
319
|
id: string;
|
|
320
|
-
kind: "base" | "change_request" | "file" | "record";
|
|
320
|
+
kind: "base" | "change_request" | "file" | "node" | "record";
|
|
321
321
|
title: string;
|
|
322
322
|
body: string;
|
|
323
323
|
eyebrow: string;
|
|
324
324
|
href: string;
|
|
325
325
|
updatedAt: string | null;
|
|
326
326
|
}[];
|
|
327
|
+
contentTruncated: boolean;
|
|
327
328
|
}, Error>;
|
|
328
329
|
/**
|
|
329
330
|
* Unified grep — one regex/literal pattern scanned across every in-scope
|
package/dist/index.js
CHANGED
|
@@ -136,13 +136,15 @@ const AgentSessionVOSchema = z.object({
|
|
|
136
136
|
/** Set when status is "failed"; surfaced verbatim to the user. */
|
|
137
137
|
error: z.string().nullable().default(null)
|
|
138
138
|
});
|
|
139
|
-
/** One connected agent backend in the
|
|
139
|
+
/** One connected agent backend visible in the requested workspace scope. */
|
|
140
140
|
const AgentConnectionVOSchema = z.object({
|
|
141
141
|
slug: z.string(),
|
|
142
142
|
agentName: z.string(),
|
|
143
143
|
transport: AgentTransportSchema,
|
|
144
144
|
sessionCount: z.number().int().nonnegative(),
|
|
145
|
-
latest: AgentSessionVOSchema.nullable()
|
|
145
|
+
latest: AgentSessionVOSchema.nullable(),
|
|
146
|
+
/** Controls owner-only actions such as deleting the saved OAuth grant. */
|
|
147
|
+
ownedByCurrentUser: z.boolean()
|
|
146
148
|
});
|
|
147
149
|
/**
|
|
148
150
|
* One streamed event from a session.
|
|
@@ -178,17 +180,34 @@ const AgentSessionEventVOSchema = z.object({
|
|
|
178
180
|
permissionOptionId: z.string().optional(),
|
|
179
181
|
at: z.string()
|
|
180
182
|
});
|
|
183
|
+
const AgentConnectionScopeSchema = z.enum(["mine", "space"]);
|
|
184
|
+
const ListAgentConnectionsInputSchema = z.object({ scope: AgentConnectionScopeSchema.default("mine") });
|
|
181
185
|
const CreateAgentSessionInputSchema = z.object({
|
|
182
186
|
/** Must name a catalog entry. Deliberately NOT a command line — see below. */
|
|
183
187
|
slug: z.string().min(1) });
|
|
184
188
|
const DisconnectAgentInputSchema = z.object({
|
|
185
189
|
/** The exact connected-agent slug shown by the sessions/catalog surfaces. */
|
|
186
190
|
slug: z.string().min(1) });
|
|
187
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* A base64 payload the browser attached.
|
|
193
|
+
*
|
|
194
|
+
* `image`/`audio` map to ACP's `ImageContent`/`AudioContent` verbatim; `file`
|
|
195
|
+
* is everything else (a PDF, a spreadsheet, a Markdown note) and becomes an
|
|
196
|
+
* ACP embedded `resource` block at send time. `data` is base64 for every kind
|
|
197
|
+
* — including textual files — so the browser never has to guess an encoding;
|
|
198
|
+
* whether a file travels to the agent as ACP text or as an ACP blob is decided
|
|
199
|
+
* server-side from the payload itself.
|
|
200
|
+
*/
|
|
188
201
|
const PromptAttachmentInputSchema = z.object({
|
|
189
|
-
kind: z.enum([
|
|
190
|
-
|
|
191
|
-
|
|
202
|
+
kind: z.enum([
|
|
203
|
+
"image",
|
|
204
|
+
"audio",
|
|
205
|
+
"file"
|
|
206
|
+
]),
|
|
207
|
+
data: z.string().min(1).max(2e7),
|
|
208
|
+
mimeType: z.string().min(1),
|
|
209
|
+
/** The original filename, carried for `file` so the agent and the transcript can name it. */
|
|
210
|
+
filename: z.string().max(255).optional()
|
|
192
211
|
});
|
|
193
212
|
const PromptAgentSessionInputSchema = z.object({
|
|
194
213
|
sessionId: z.string().min(1),
|
|
@@ -219,8 +238,8 @@ const agentsContract = {
|
|
|
219
238
|
deletedSessionCount: z.number().int().nonnegative()
|
|
220
239
|
})),
|
|
221
240
|
connections: {
|
|
222
|
-
/** Connected backends
|
|
223
|
-
list: oc.output(AgentConnectionVOSchema.array()) },
|
|
241
|
+
/** Connected backends in the current user's personal or active-space scope. */
|
|
242
|
+
list: oc.input(ListAgentConnectionsInputSchema).output(AgentConnectionVOSchema.array()) },
|
|
224
243
|
sessions: {
|
|
225
244
|
list: oc.output(AgentSessionVOSchema.array()),
|
|
226
245
|
create: oc.input(CreateAgentSessionInputSchema).output(AgentSessionVOSchema),
|
|
@@ -663,7 +682,8 @@ const makeFileTreeNodeType = (config) => ({
|
|
|
663
682
|
icon: config.icon,
|
|
664
683
|
capabilities: {
|
|
665
684
|
hasDetail: true,
|
|
666
|
-
creatable: true
|
|
685
|
+
creatable: true,
|
|
686
|
+
publicAccess: config.publicAccess
|
|
667
687
|
},
|
|
668
688
|
operations: fileTreeOperations(config.type)
|
|
669
689
|
});
|
|
@@ -681,7 +701,8 @@ const airappNodeType = makeFileTreeNodeType({
|
|
|
681
701
|
icon: "app-window",
|
|
682
702
|
routeBase: "airapps",
|
|
683
703
|
tag: "AirApps",
|
|
684
|
-
entryFile: "package.json"
|
|
704
|
+
entryFile: "package.json",
|
|
705
|
+
publicAccess: "no"
|
|
685
706
|
});
|
|
686
707
|
//#endregion
|
|
687
708
|
//#region ../../packages/busabase-contract/src/domains/base/definition.ts
|
|
@@ -692,7 +713,8 @@ const baseNodeType = {
|
|
|
692
713
|
icon: "table",
|
|
693
714
|
capabilities: {
|
|
694
715
|
hasDetail: true,
|
|
695
|
-
creatable: true
|
|
716
|
+
creatable: true,
|
|
717
|
+
publicAccess: "detail"
|
|
696
718
|
},
|
|
697
719
|
operations: [
|
|
698
720
|
{
|
|
@@ -795,7 +817,8 @@ const docNodeType = {
|
|
|
795
817
|
icon: "file-text",
|
|
796
818
|
capabilities: {
|
|
797
819
|
hasDetail: true,
|
|
798
|
-
creatable: true
|
|
820
|
+
creatable: true,
|
|
821
|
+
publicAccess: "detail"
|
|
799
822
|
},
|
|
800
823
|
operations: [{
|
|
801
824
|
kind: "doc_update",
|
|
@@ -812,7 +835,8 @@ const driveNodeType = makeFileTreeNodeType({
|
|
|
812
835
|
icon: "hard-drive",
|
|
813
836
|
routeBase: "drives",
|
|
814
837
|
tag: "Drives",
|
|
815
|
-
entryFile: "README.md"
|
|
838
|
+
entryFile: "README.md",
|
|
839
|
+
publicAccess: "no"
|
|
816
840
|
});
|
|
817
841
|
//#endregion
|
|
818
842
|
//#region ../../packages/busabase-contract/src/domains/file-node/definition.ts
|
|
@@ -824,7 +848,8 @@ const fileNodeType = {
|
|
|
824
848
|
icon: "file",
|
|
825
849
|
capabilities: {
|
|
826
850
|
hasDetail: true,
|
|
827
|
-
creatable: true
|
|
851
|
+
creatable: true,
|
|
852
|
+
publicAccess: "detail"
|
|
828
853
|
},
|
|
829
854
|
operations: []
|
|
830
855
|
};
|
|
@@ -839,7 +864,8 @@ const folderNodeType = {
|
|
|
839
864
|
capabilities: {
|
|
840
865
|
container: true,
|
|
841
866
|
creatable: true,
|
|
842
|
-
hasDetail: true
|
|
867
|
+
hasDetail: true,
|
|
868
|
+
publicAccess: "detail"
|
|
843
869
|
},
|
|
844
870
|
operations: []
|
|
845
871
|
};
|
|
@@ -860,7 +886,8 @@ const formNodeType = {
|
|
|
860
886
|
icon: "form",
|
|
861
887
|
capabilities: {
|
|
862
888
|
hasDetail: true,
|
|
863
|
-
creatable: true
|
|
889
|
+
creatable: true,
|
|
890
|
+
publicAccess: "submit"
|
|
864
891
|
},
|
|
865
892
|
operations: []
|
|
866
893
|
};
|
|
@@ -879,7 +906,8 @@ const htmlNodeType = {
|
|
|
879
906
|
icon: "code-xml",
|
|
880
907
|
capabilities: {
|
|
881
908
|
hasDetail: true,
|
|
882
|
-
creatable: true
|
|
909
|
+
creatable: true,
|
|
910
|
+
publicAccess: "no"
|
|
883
911
|
},
|
|
884
912
|
operations: [{
|
|
885
913
|
kind: "html_document_update",
|
|
@@ -896,7 +924,8 @@ const skillNodeType = makeFileTreeNodeType({
|
|
|
896
924
|
icon: "sparkles",
|
|
897
925
|
routeBase: "skills",
|
|
898
926
|
tag: "Skills",
|
|
899
|
-
entryFile: "SKILL.md"
|
|
927
|
+
entryFile: "SKILL.md",
|
|
928
|
+
publicAccess: "no"
|
|
900
929
|
});
|
|
901
930
|
//#endregion
|
|
902
931
|
//#region ../../packages/busabase-contract/src/domains/whiteboard/definition.ts
|
|
@@ -912,7 +941,8 @@ const whiteboardNodeType = {
|
|
|
912
941
|
icon: "pen-tool",
|
|
913
942
|
capabilities: {
|
|
914
943
|
hasDetail: true,
|
|
915
|
-
creatable: true
|
|
944
|
+
creatable: true,
|
|
945
|
+
publicAccess: "detail"
|
|
916
946
|
},
|
|
917
947
|
operations: [{
|
|
918
948
|
kind: "whiteboard_document_update",
|
|
@@ -935,7 +965,8 @@ const workflowNodeType = {
|
|
|
935
965
|
icon: "workflow",
|
|
936
966
|
capabilities: {
|
|
937
967
|
hasDetail: true,
|
|
938
|
-
creatable: true
|
|
968
|
+
creatable: true,
|
|
969
|
+
publicAccess: "detail"
|
|
939
970
|
},
|
|
940
971
|
operations: [{
|
|
941
972
|
kind: "workflow_document_update",
|
|
@@ -1293,11 +1324,18 @@ const agentTaskSchema = z.object({
|
|
|
1293
1324
|
});
|
|
1294
1325
|
const searchResultSchema = z.object({
|
|
1295
1326
|
id: z.string(),
|
|
1327
|
+
/**
|
|
1328
|
+
* `node` covers the CONTENT of a content-bearing node (doc / html /
|
|
1329
|
+
* whiteboard / workflow). Purely additive — callers that do not know it can
|
|
1330
|
+
* ignore the kind, and callers that never asked for it (an explicit
|
|
1331
|
+
* `sources` list without `nodes`) never receive it.
|
|
1332
|
+
*/
|
|
1296
1333
|
kind: z.enum([
|
|
1297
1334
|
"record",
|
|
1298
1335
|
"change_request",
|
|
1299
1336
|
"base",
|
|
1300
|
-
"file"
|
|
1337
|
+
"file",
|
|
1338
|
+
"node"
|
|
1301
1339
|
]),
|
|
1302
1340
|
title: z.string(),
|
|
1303
1341
|
body: z.string(),
|
|
@@ -1310,7 +1348,17 @@ const searchResponseSchema = z.object({
|
|
|
1310
1348
|
limit: z.number(),
|
|
1311
1349
|
offset: z.number(),
|
|
1312
1350
|
hasMore: z.boolean(),
|
|
1313
|
-
results: z.array(searchResultSchema)
|
|
1351
|
+
results: z.array(searchResultSchema),
|
|
1352
|
+
/**
|
|
1353
|
+
* True when at least one in-scope node's content was indexed only up to the
|
|
1354
|
+
* projection cap, so this search could not see all of it.
|
|
1355
|
+
*
|
|
1356
|
+
* Exists so an empty result can be reported honestly: without it, "no
|
|
1357
|
+
* results" is ambiguous between "the workspace does not contain this" and
|
|
1358
|
+
* "we did not look at all of it". Clients should surface it — and point at
|
|
1359
|
+
* `grep`, which has no such cap — rather than implying absence.
|
|
1360
|
+
*/
|
|
1361
|
+
contentTruncated: z.boolean().default(false)
|
|
1314
1362
|
});
|
|
1315
1363
|
const liveEventSchema = z.object({
|
|
1316
1364
|
kind: z.enum([
|
|
@@ -1523,7 +1571,8 @@ const inboxSnapshotResponseSchema = listChangeRequestsPageResponseSchema.extend(
|
|
|
1523
1571
|
const SEARCH_SOURCES = [
|
|
1524
1572
|
"records",
|
|
1525
1573
|
"files",
|
|
1526
|
-
"names"
|
|
1574
|
+
"names",
|
|
1575
|
+
"nodes"
|
|
1527
1576
|
];
|
|
1528
1577
|
const searchInputSchema = z.object({
|
|
1529
1578
|
query: z.string().default(""),
|
|
@@ -2791,6 +2840,19 @@ const ExportAssetTextVOSchema = z.object({
|
|
|
2791
2840
|
textContentHash: z.string().nullable(),
|
|
2792
2841
|
byteCount: z.number().int().nonnegative()
|
|
2793
2842
|
});
|
|
2843
|
+
const ImportBeginInputSchema = z.object({
|
|
2844
|
+
/**
|
|
2845
|
+
* The space id the archive was ORIGINALLY exported from (`manifest.spaceId`
|
|
2846
|
+
* in the `.bbdump`, already integrity-verified before this is called).
|
|
2847
|
+
* `importTableRows`'s "nodes" handling needs this to recognize the
|
|
2848
|
+
* archive's own root-node row deterministically (`rootNodeIdForSpace`) —
|
|
2849
|
+
* scanning each batch's rows for "the one with a null `parentId`" only
|
|
2850
|
+
* works when that row happens to land in the SAME batch as its children,
|
|
2851
|
+
* which cursor pagination (id-ordered, not tree-ordered) does not
|
|
2852
|
+
* guarantee once a space has more nodes than one page. See the matching
|
|
2853
|
+
* comment in `import-logic.ts`.
|
|
2854
|
+
*/
|
|
2855
|
+
sourceSpaceId: z.string() });
|
|
2794
2856
|
const ImportBeginVOSchema = z.object({ sessionId: z.string() });
|
|
2795
2857
|
/**
|
|
2796
2858
|
* `docBodies`, `attachmentBlobs` and `assetTextBlobs` are pseudo-tables: doc
|
|
@@ -2854,7 +2916,7 @@ const dumpContract = {
|
|
|
2854
2916
|
tags: ["Dump"],
|
|
2855
2917
|
summary: "Begin a full-fidelity import session",
|
|
2856
2918
|
successDescription: "Created an import session for the current space. Refused unless the space's node tree is empty (full-fidelity import preserves original ids and cannot merge into existing data)."
|
|
2857
|
-
}).output(ImportBeginVOSchema),
|
|
2919
|
+
}).input(ImportBeginInputSchema).output(ImportBeginVOSchema),
|
|
2858
2920
|
importTables: oc.route({
|
|
2859
2921
|
method: "POST",
|
|
2860
2922
|
path: "/dump/import/tables",
|
|
@@ -4233,6 +4295,14 @@ const NodeDetailVOSchema = z.discriminatedUnion("type", [
|
|
|
4233
4295
|
* hint. An ambiguous slug with no `type` is refused rather than silently
|
|
4234
4296
|
* resolved to whichever row sorts first.
|
|
4235
4297
|
*/
|
|
4298
|
+
/**
|
|
4299
|
+
* `nodes.ancestors` output — the node's ancestor ids, ROOT-FIRST, excluding
|
|
4300
|
+
* the node itself. Ids only, deliberately: the sidebar already has (or can
|
|
4301
|
+
* lazily fetch) each ancestor's own row, and it only needs to know WHICH
|
|
4302
|
+
* folders to expand. Returning full nodes here would duplicate `nodes.list`
|
|
4303
|
+
* and make a cheap navigational lookup expensive.
|
|
4304
|
+
*/
|
|
4305
|
+
const nodeAncestorsVOSchema = z.object({ ancestorIds: z.array(z.string()) });
|
|
4236
4306
|
const getNodeInputSchema = z.object({
|
|
4237
4307
|
nodeId: z.string().describe("Node id, or a slug that is unique within its type."),
|
|
4238
4308
|
type: z.enum(NODE_TYPES).optional().describe("Optional disambiguation hint, only needed when `nodeId` is a slug that exists under more than one node type.")
|
|
@@ -4355,7 +4425,7 @@ const busabaseContractRoutes = {
|
|
|
4355
4425
|
path: "/nodes/search",
|
|
4356
4426
|
tags: ["Nodes", "Search"],
|
|
4357
4427
|
summary: "Search nodes by name/slug (cheap, name-only quick-jump)",
|
|
4358
|
-
successDescription: "Plain ilike match on name/slug across every registered node type, scoped by the same node-visibility ACL as `nodes.list`. No content scan and no full-text ranking — ordered exact-slug-match first, then by name. Backs the dashboard search dialog's 'Recent' tab cache-miss path
|
|
4428
|
+
successDescription: "Plain ilike match on name/slug across every registered node type, scoped by the same node-visibility ACL as `nodes.list`. No content scan and no full-text ranking — ordered exact-slug-match first, then by name. Backs the dashboard search dialog's 'Recent' tab cache-miss path. To search what is written INSIDE nodes, use `search` with the `nodes` source (indexed, paginated) or `grep` (exhaustive, no index)."
|
|
4359
4429
|
}).input(searchNodesByNameInputSchema).output(z.array(nodeSearchResultSchema)),
|
|
4360
4430
|
isDescendant: oc.route({
|
|
4361
4431
|
method: "GET",
|
|
@@ -4364,6 +4434,13 @@ const busabaseContractRoutes = {
|
|
|
4364
4434
|
summary: "Check whether a node is a descendant of another",
|
|
4365
4435
|
successDescription: "Server-authoritative parentId-chain walk from nodeId up to potentialAncestorId. Used to gate cross-branch drag-and-drop drops in the sidebar, since the full tree is no longer guaranteed to be loaded client-side (depth-bounded lazy load) — a purely local walk could wrongly allow dropping a folder into its own unloaded descendant."
|
|
4366
4436
|
}).input(isDescendantInputSchema).output(isDescendantOutputSchema),
|
|
4437
|
+
ancestors: oc.route({
|
|
4438
|
+
method: "GET",
|
|
4439
|
+
path: "/nodes/{nodeId}/ancestors",
|
|
4440
|
+
tags: ["Nodes"],
|
|
4441
|
+
summary: "List a node's ancestor ids",
|
|
4442
|
+
successDescription: "The node's ancestor ids, root-first, excluding the node itself (`[]` directly under the workspace root). `nodeId` accepts an id or a slug, same as `nodes.get`; pass `type` when a slug exists under more than one type. Lets a depth-bounded, lazily-expanded tree open straight to a deep node on a cold load (a refresh, a bookmark, a shared link) without one round trip per level."
|
|
4443
|
+
}).input(getNodeInputSchema).output(nodeAncestorsVOSchema),
|
|
4367
4444
|
createChangeRequest: oc.route({
|
|
4368
4445
|
method: "POST",
|
|
4369
4446
|
path: "/nodes/change-requests",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "busabase-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Typed TypeScript/JavaScript SDK for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server` (or Busabase Cloud).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-sdk",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"types": "./dist/airapp-node.d.ts",
|
|
36
36
|
"default": "./dist/airapp-node.js"
|
|
37
37
|
},
|
|
38
|
+
"./airapp-check": {
|
|
39
|
+
"types": "./dist/airapp-check.d.ts",
|
|
40
|
+
"default": "./dist/airapp-check.js"
|
|
41
|
+
},
|
|
38
42
|
"./airapp": {
|
|
39
43
|
"types": "./dist/airapp.d.ts",
|
|
40
44
|
"default": "./dist/airapp.js"
|
|
@@ -63,7 +67,7 @@
|
|
|
63
67
|
"tsx": "^4.20.5",
|
|
64
68
|
"typescript": "^7.0.2",
|
|
65
69
|
"vitest": "^4.1.11",
|
|
66
|
-
"busabase-contract": "0.
|
|
70
|
+
"busabase-contract": "0.30.0",
|
|
67
71
|
"open-domains": "0.0.2",
|
|
68
72
|
"openlib": "0.1.1"
|
|
69
73
|
},
|