buckyos 0.7.160 → 0.7.162
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/cli/dist/cli.mjs +412 -96
- package/cli/dist/cli.mjs.map +1 -1
- package/cli/modules/aiws.ts +385 -81
- package/cli/modules/pikg.ts +15 -6
- package/cli/modules/pikg_protocol.ts +9 -3
- package/cli/version.ts +1 -1
- package/dist/aiworkspace_client.d.ts +165 -1
- package/dist/aiworkspace_client.d.ts.map +1 -1
- package/dist/aiws_ext.cjs +1 -1
- package/dist/aiws_ext.cjs.map +1 -1
- package/dist/aiws_ext.d.ts +160 -19
- package/dist/aiws_ext.d.ts.map +1 -1
- package/dist/aiws_ext.mjs +817 -68
- package/dist/aiws_ext.mjs.map +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.mjs +2 -2
- package/dist/{executor_client-50023bcc.mjs → executor_client-0856c3fc.mjs} +22 -2
- package/dist/executor_client-0856c3fc.mjs.map +1 -0
- package/dist/{executor_client-47edd42d.js → executor_client-3f7679e3.js} +9 -9
- package/dist/executor_client-3f7679e3.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +15 -3
- package/dist/node.cjs +1 -1
- package/dist/node.mjs +15 -3
- package/package.json +1 -1
- package/dist/executor_client-47edd42d.js.map +0 -1
- package/dist/executor_client-50023bcc.mjs.map +0 -1
package/cli/dist/cli.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import process from "node:process";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import * as nodePath from "node:path";
|
|
4
4
|
import { resolve as resolve$5, dirname as dirname$3 } from "node:path";
|
|
5
|
-
import { BuckyOSSDK, RuntimeType, buckyos, namelib, VerifyHubClient, createAppInstanceId, parseSessionTokenClaims, ndn, verifyBundle, controlSkeleton, aiwsContentDigest, AIWS_MANIFEST_VERSION, AIWS_MANIFEST_FILE, taskMgrTaskEventPath, ndm_proxy, objectUrl as objectUrl$1, ExecutorCommandStream, watchControl, buildControl, pullControl, exportBundle, NfspClient, AiwsSourceError } from "buckyos/node";
|
|
5
|
+
import { BuckyOSSDK, RuntimeType, buckyos, namelib, VerifyHubClient, createAppInstanceId, parseSessionTokenClaims, ndn, verifyBundle, controlSkeleton, aiwsContentDigest, AIWS_MANIFEST_VERSION, AIWS_MANIFEST_FILE, taskMgrTaskEventPath, ndm_proxy, objectUrl as objectUrl$1, ExecutorCommandStream, watchControl, buildControl, pullControl, exportBundle, NfspClient, AiwsSourceError, packBuildFiles, templateDir, isSourcePath } from "buckyos/node";
|
|
6
6
|
import * as provisionSdk from "buckyos/provision";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { readFile, writeFile, readdir, stat, lstat, realpath, mkdtemp, rename, rm, mkdir, chmod, symlink, copyFile, open } from "node:fs/promises";
|
|
@@ -4499,7 +4499,11 @@ async function verifyAiwsPayload(tarGz) {
|
|
|
4499
4499
|
if (errors.length > 0) {
|
|
4500
4500
|
throw invalid("aiws", `aiws bundle: ${errors.map((d) => `${d.file}: ${d.message}`).join("; ")}`);
|
|
4501
4501
|
}
|
|
4502
|
-
return {
|
|
4502
|
+
return {
|
|
4503
|
+
manifest_version: checked.manifest.manifest_version,
|
|
4504
|
+
controls: checked.manifest.controls.map((c) => ({ ident: c.ident, kind: c.kind, version: c.version, digest: c.digest })),
|
|
4505
|
+
templates: checked.manifest.workspace_templates.map((t) => ({ id: t.id, title: t.title, digest: t.digest, requires: t.requires }))
|
|
4506
|
+
};
|
|
4503
4507
|
}
|
|
4504
4508
|
async function validateSnapshot(distDir, appDoc, packageMeta) {
|
|
4505
4509
|
const payloads = /* @__PURE__ */ new Map();
|
|
@@ -5238,7 +5242,7 @@ function validateContentHandlers(value, permissions) {
|
|
|
5238
5242
|
}
|
|
5239
5243
|
return value;
|
|
5240
5244
|
}
|
|
5241
|
-
const PACKAGE_VERSION = "0.7.
|
|
5245
|
+
const PACKAGE_VERSION = "0.7.162";
|
|
5242
5246
|
const TOOL_VERSION = PACKAGE_VERSION;
|
|
5243
5247
|
const SDK_VERSION = PACKAGE_VERSION;
|
|
5244
5248
|
const PROTOCOL_VERSION = "1";
|
|
@@ -5332,7 +5336,12 @@ function createPikgModule(dependencies = {}) {
|
|
|
5332
5336
|
type: "string"
|
|
5333
5337
|
},
|
|
5334
5338
|
{ name: "version", description: "Initial App version", type: "string" },
|
|
5335
|
-
{ name: "app-did", property: "app_did", description: "Explicit App DID", type: "string" }
|
|
5339
|
+
{ name: "app-did", property: "app_did", description: "Explicit App DID", type: "string" },
|
|
5340
|
+
{
|
|
5341
|
+
name: "composite",
|
|
5342
|
+
description: "aiws-controls: start the package with a composite control (a panel of Blocks) instead of a declarative one",
|
|
5343
|
+
type: "boolean"
|
|
5344
|
+
}
|
|
5336
5345
|
],
|
|
5337
5346
|
inputSchema: {
|
|
5338
5347
|
type: "object",
|
|
@@ -5343,7 +5352,8 @@ function createPikgModule(dependencies = {}) {
|
|
|
5343
5352
|
kind: { type: "string", enum: APP_KINDS },
|
|
5344
5353
|
source: { type: "string", minLength: 1 },
|
|
5345
5354
|
version: { type: "string", minLength: 1 },
|
|
5346
|
-
app_did: { type: "string", minLength: 1 }
|
|
5355
|
+
app_did: { type: "string", minLength: 1 },
|
|
5356
|
+
composite: { type: "boolean" }
|
|
5347
5357
|
},
|
|
5348
5358
|
additionalProperties: false
|
|
5349
5359
|
},
|
|
@@ -5358,7 +5368,8 @@ function createPikgModule(dependencies = {}) {
|
|
|
5358
5368
|
"buckyos pikg init .",
|
|
5359
5369
|
"buckyos --non-interactive pikg init . --owner did:bns:root --kind static-web --source ./web/dist",
|
|
5360
5370
|
"buckyos --non-interactive pikg init . --owner did:bns:root --kind agent-skills --source ./skills",
|
|
5361
|
-
"buckyos --non-interactive pikg init . --owner did:bns:alice --kind aiws-controls --source ./aiws"
|
|
5371
|
+
"buckyos --non-interactive pikg init . --owner did:bns:alice --kind aiws-controls --source ./aiws",
|
|
5372
|
+
"buckyos --non-interactive pikg init . --owner did:bns:alice --kind aiws-controls --source ./aiws --composite"
|
|
5362
5373
|
],
|
|
5363
5374
|
handler: async (ctx, input) => await initCommand(ctx, input, docker)
|
|
5364
5375
|
},
|
|
@@ -5601,7 +5612,7 @@ async function initCommand(ctx, input, docker) {
|
|
|
5601
5612
|
if (!isHostError(error, "NotFound"))
|
|
5602
5613
|
throw error;
|
|
5603
5614
|
if (kind === "aiws-controls") {
|
|
5604
|
-
await writeAiwsSkeleton(absoluteSource, owner, name);
|
|
5615
|
+
await writeAiwsSkeleton(absoluteSource, owner, name, input.composite === true);
|
|
5605
5616
|
sourceSummary2.skeleton = true;
|
|
5606
5617
|
} else {
|
|
5607
5618
|
await ctx.io.stderr(`warning: source does not exist yet: ${sourceSummary2.path}
|
|
@@ -5666,8 +5677,9 @@ async function initCommand(ctx, input, docker) {
|
|
|
5666
5677
|
next_command: `buckyos pikg build ${metaDir}`
|
|
5667
5678
|
};
|
|
5668
5679
|
}
|
|
5669
|
-
async function writeAiwsSkeleton(dir, ownerDid, name) {
|
|
5670
|
-
const
|
|
5680
|
+
async function writeAiwsSkeleton(dir, ownerDid, name, composite) {
|
|
5681
|
+
const kind = composite ? "composite" : "declarative";
|
|
5682
|
+
const files = controlSkeleton({ authorDid: ownerDid, name, title: name, kind });
|
|
5671
5683
|
const encoder = new TextEncoder();
|
|
5672
5684
|
const host2 = getHost();
|
|
5673
5685
|
await host2.mkdir(join(dir, "controls", name), { recursive: true });
|
|
@@ -5677,8 +5689,8 @@ async function writeAiwsSkeleton(dir, ownerDid, name) {
|
|
|
5677
5689
|
const manifest = {
|
|
5678
5690
|
manifest_version: AIWS_MANIFEST_VERSION,
|
|
5679
5691
|
purpose: "aiws",
|
|
5680
|
-
host_api: { declarative: 1 },
|
|
5681
|
-
controls: [{ ident: `${ownerDid}#${name}`, name, kind
|
|
5692
|
+
host_api: composite ? {} : { declarative: 1 },
|
|
5693
|
+
controls: [{ ident: `${ownerDid}#${name}`, name, kind, version: 1, digest, dir: `controls/${name}/`, title: name }],
|
|
5682
5694
|
workspace_templates: []
|
|
5683
5695
|
};
|
|
5684
5696
|
await host2.writeTextFile(join(dir, AIWS_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
@@ -11577,6 +11589,25 @@ async function sourceErrors(run) {
|
|
|
11577
11589
|
function list(input, key) {
|
|
11578
11590
|
return requiredInputString(input, key).split(",").map((s) => s.trim()).filter(Boolean);
|
|
11579
11591
|
}
|
|
11592
|
+
const IDENT = /^did:[a-z0-9]+:[^/#\s]+#[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
11593
|
+
async function migrationFile(ctx, path) {
|
|
11594
|
+
const host2 = getHost();
|
|
11595
|
+
let value;
|
|
11596
|
+
try {
|
|
11597
|
+
value = JSON.parse(await host2.readTextFile(host2.path.resolve(ctx.cwd, path)));
|
|
11598
|
+
} catch (error) {
|
|
11599
|
+
throw new UsageError("AIWS_USAGE", `--migration ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
11600
|
+
}
|
|
11601
|
+
const shape = `--migration ${path}: { "<author_did>#<name>": { config?, bindings?, slots?, events? } }`;
|
|
11602
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11603
|
+
throw new UsageError("AIWS_USAGE", shape);
|
|
11604
|
+
for (const [ident, m] of Object.entries(value)) {
|
|
11605
|
+
if (!IDENT.test(ident) || !m || typeof m !== "object" || Array.isArray(m) || Object.keys(m).some((k) => !["config", "bindings", "slots", "events"].includes(k))) {
|
|
11606
|
+
throw new UsageError("AIWS_USAGE", `${shape} (at ${ident})`);
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
return value;
|
|
11610
|
+
}
|
|
11580
11611
|
const LOCAL_STATE = ".aiws-build.json";
|
|
11581
11612
|
async function isLocalDir(path) {
|
|
11582
11613
|
try {
|
|
@@ -11589,25 +11620,42 @@ async function localFiles(dir) {
|
|
|
11589
11620
|
const host2 = getHost();
|
|
11590
11621
|
const files = [];
|
|
11591
11622
|
const problems = [];
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11623
|
+
const walk = async (folder, prefix, depth) => {
|
|
11624
|
+
for (const entry of await host2.readDir(folder)) {
|
|
11625
|
+
if (entry.name.startsWith("."))
|
|
11626
|
+
continue;
|
|
11627
|
+
const name = `${prefix}${entry.name}`;
|
|
11628
|
+
if (entry.isDirectory) {
|
|
11629
|
+
if (depth >= 8)
|
|
11630
|
+
problems.push({ file: name, severity: "error", message: "sub-folders nest at most 8 levels" });
|
|
11631
|
+
else
|
|
11632
|
+
await walk(host2.path.join(folder, entry.name), `${name}/`, depth + 1);
|
|
11633
|
+
} else if (entry.isFile)
|
|
11634
|
+
files.push({ name, bytes: await host2.readFile(host2.path.join(folder, entry.name)) });
|
|
11635
|
+
else
|
|
11636
|
+
problems.push({ file: name, severity: "error", message: "only files and folders belong to a control source" });
|
|
11599
11637
|
}
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
files.push({ name: entry.name, base64: btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join("")) });
|
|
11603
|
-
}
|
|
11638
|
+
};
|
|
11639
|
+
await walk(dir, "", 0);
|
|
11604
11640
|
if (files.length === 0)
|
|
11605
11641
|
problems.push({ file: "", severity: "error", message: `${dir} holds no files` });
|
|
11606
|
-
if (total > 4 * 1024 * 1024)
|
|
11607
|
-
problems.push({ file: "", severity: "error", message: "a control is limited to 4 MiB per build from a local folder" });
|
|
11608
11642
|
if (problems.length > 0)
|
|
11609
11643
|
throw new ToolError("BUILD_FAILED", `BUILD_FAILED: ${problems.map((p) => `${p.file}: ${p.message}`).join("; ")}`, void 0, false, { data: { diagnostics: problems } });
|
|
11610
|
-
return files.sort((a, b) => a.name.
|
|
11644
|
+
return files.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
11645
|
+
}
|
|
11646
|
+
function sourceUploader(ctx) {
|
|
11647
|
+
return async (name, bytes) => {
|
|
11648
|
+
const bucket = `user/aiworkspace/${ctx.principal.id}/exports`;
|
|
11649
|
+
const key = `aiws-src/${Date.now()}-${name.replaceAll("/", "_")}`;
|
|
11650
|
+
const url = `${gatewayOrigin$2(ctx.connection.endpoint)}/bucket/v1/${bucket}/${key.split("/").map(encodeURIComponent).join("/")}`;
|
|
11651
|
+
const response = await fetch(url, { method: "PUT", headers: { Authorization: `Bearer ${await sessionToken(ctx)}` }, body: new Blob([bytes]), signal: ctx.signal });
|
|
11652
|
+
if (!response.ok)
|
|
11653
|
+
throw new ToolError("AIWS_UPLOAD_FAILED", `upload of ${name}: HTTP ${response.status} ${await response.text().catch(() => "")}`);
|
|
11654
|
+
const put = await response.json();
|
|
11655
|
+
if (!put.obj_id)
|
|
11656
|
+
throw new ToolError("AIWS_UPLOAD_FAILED", `upload of ${name}: the bucket answered no obj_id`);
|
|
11657
|
+
return put.obj_id;
|
|
11658
|
+
};
|
|
11611
11659
|
}
|
|
11612
11660
|
async function localState(dir) {
|
|
11613
11661
|
try {
|
|
@@ -11620,11 +11668,12 @@ async function buildLocal(ctx, ws, dir, opts) {
|
|
|
11620
11668
|
const state = await localState(dir);
|
|
11621
11669
|
const result = await aiws(ctx, "ext.build", {
|
|
11622
11670
|
workspace_id: ws,
|
|
11623
|
-
files: await localFiles(dir),
|
|
11671
|
+
files: await sourceErrors(async () => packBuildFiles(await localFiles(dir), sourceUploader(ctx))),
|
|
11624
11672
|
...opts.folderId ? { folder_id: opts.folderId } : {},
|
|
11625
11673
|
...state && state.workspace_id === ws ? { expect: { def_rev: state.def_rev, dir_rev: state.dir_rev } } : {},
|
|
11626
11674
|
...opts.force ? { force: true } : {},
|
|
11627
|
-
...opts.dryRun ? { dry_run: true } : {}
|
|
11675
|
+
...opts.dryRun ? { dry_run: true } : {},
|
|
11676
|
+
...opts.migration ? { migration: opts.migration } : {}
|
|
11628
11677
|
});
|
|
11629
11678
|
if (!opts.dryRun && typeof result.def_entity_id === "string" && typeof result.def_rev === "number") {
|
|
11630
11679
|
await getHost().writeTextFile(getHost().path.join(dir, LOCAL_STATE), `${JSON.stringify({ workspace_id: ws, def_entity_id: result.def_entity_id, def_rev: result.def_rev, dir_rev: result.dir_rev ?? 0, ident: result.ident, at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
@@ -11635,7 +11684,7 @@ async function buildLocal(ctx, ws, dir, opts) {
|
|
|
11635
11684
|
const buildCommand = {
|
|
11636
11685
|
verb: "build",
|
|
11637
11686
|
summary: "Build a control from its source folder (local, or in the Zone; component.json + files) into a Workspace; instances update, a failure commits nothing",
|
|
11638
|
-
description:
|
|
11687
|
+
description: 'The folder is a local folder (when it exists here; its files are sent inline, and past 4 MiB per build the largest are uploaded to your exports bucket and sent by reference) or a Zone path (e.g. /home/alice/AI Workspace/Demo/controls/kpi-card; --zone-path forces it); sub-folders are part of the source (file names are relative paths). The base of the last build or pull is kept in its .aiws-build.json; building over a newer Workspace version is refused (pull first, or --force). --watch rebuilds on every change until interrupted. A build that narrows what instances rely on (a binding renamed, a slot moved, an event dropped) takes --migration, a JSON file { "<author_did>#<name>": Migration } (as ext-upgrade).',
|
|
11639
11688
|
positionals: [{ name: "dir", description: "Local folder, or Zone path of the control source folder", required: true }],
|
|
11640
11689
|
options: [
|
|
11641
11690
|
WORKSPACE_OPTION,
|
|
@@ -11643,9 +11692,10 @@ const buildCommand = {
|
|
|
11643
11692
|
{ name: "watch", property: "watch", description: "Rebuild whenever the folder changes", type: "boolean" },
|
|
11644
11693
|
{ name: "dry-run", property: "dry_run", description: "Check only: diagnostics, nothing committed", type: "boolean" },
|
|
11645
11694
|
{ name: "force", property: "force", description: "Build over whatever the Workspace holds now", type: "boolean" },
|
|
11646
|
-
{ name: "zone-path", property: "zone_path", description: "Treat the folder as a Zone path even if a local folder has that name", type: "boolean" }
|
|
11695
|
+
{ name: "zone-path", property: "zone_path", description: "Treat the folder as a Zone path even if a local folder has that name", type: "boolean" },
|
|
11696
|
+
{ name: "migration", property: "migration", description: 'JSON file { "<author_did>#<name>": Migration } rewriting the instances of a build that narrows the contract', type: "string" }
|
|
11647
11697
|
],
|
|
11648
|
-
inputSchema: workspaceInput({ dir: { type: "string", minLength: 1 }, folder_id: { type: "string" }, watch: { type: "boolean" }, dry_run: { type: "boolean" }, force: { type: "boolean" }, zone_path: { type: "boolean" } }, ["dir"]),
|
|
11698
|
+
inputSchema: workspaceInput({ dir: { type: "string", minLength: 1 }, folder_id: { type: "string" }, watch: { type: "boolean" }, dry_run: { type: "boolean" }, force: { type: "boolean" }, zone_path: { type: "boolean" }, migration: { type: "string" } }, ["dir"]),
|
|
11649
11699
|
outputSchema: OBJECT_OUTPUT,
|
|
11650
11700
|
resultSchemaVersion: 1,
|
|
11651
11701
|
access: { mode: "fixed", level: "write" },
|
|
@@ -11655,7 +11705,8 @@ const buildCommand = {
|
|
|
11655
11705
|
handler: async (ctx, input) => {
|
|
11656
11706
|
const ws = requiredInputString(input, "workspace_id");
|
|
11657
11707
|
const dir = requiredInputString(input, "dir");
|
|
11658
|
-
const
|
|
11708
|
+
const migration = inputString(input, "migration");
|
|
11709
|
+
const opts = { folderId: inputString(input, "folder_id"), force: input.force === true, dryRun: input.dry_run === true, upload: sourceUploader(ctx), ...migration ? { migration: await migrationFile(ctx, migration) } : {} };
|
|
11659
11710
|
if (input.zone_path !== true && await isLocalDir(getHost().path.resolve(ctx.cwd, dir))) {
|
|
11660
11711
|
const local = getHost().path.resolve(ctx.cwd, dir);
|
|
11661
11712
|
if (input.watch === true)
|
|
@@ -11707,7 +11758,8 @@ const pullCommand = {
|
|
|
11707
11758
|
const fetchAsset = assetFetcher(ctx);
|
|
11708
11759
|
const files = [];
|
|
11709
11760
|
for (const f of control.files) {
|
|
11710
|
-
const target = host2.path.join(out, f.name);
|
|
11761
|
+
const target = host2.path.join(out, ...f.name.split("/"));
|
|
11762
|
+
await host2.mkdir(host2.path.dirname(target), { recursive: true });
|
|
11711
11763
|
if (f.text !== void 0)
|
|
11712
11764
|
await host2.writeTextFile(target, f.text);
|
|
11713
11765
|
else
|
|
@@ -11741,6 +11793,57 @@ async function uploadPackage(ctx, path) {
|
|
|
11741
11793
|
throw new ToolError("AIWS_UPLOAD_FAILED", `upload of ${path}: HTTP ${response.status} ${await response.text().catch(() => "")}`);
|
|
11742
11794
|
return { bucket_id, key };
|
|
11743
11795
|
}
|
|
11796
|
+
const PACKAGE_INPUT = { name: { type: "string" }, version: { type: "string" }, owner: { type: "string" }, pikg: { type: "boolean" }, install: { type: "boolean" } };
|
|
11797
|
+
function packageOptions(nameDefault) {
|
|
11798
|
+
return [
|
|
11799
|
+
{ name: "name", property: "name", description: `Package name (default: ${nameDefault})`, type: "string" },
|
|
11800
|
+
{ name: "version", property: "version", description: "Package version (default 0.1.0)", type: "string" },
|
|
11801
|
+
{ name: "owner", property: "owner", description: "Owner DID of the package (default did:bns:<you>)", type: "string" },
|
|
11802
|
+
{ name: "pikg", property: "pikg", description: "Also pack the bundle as an aiws PIKG", type: "boolean" },
|
|
11803
|
+
{ name: "install", property: "install", description: "Pack, then install into your AI Workspace library (dev mode)", type: "boolean" }
|
|
11804
|
+
];
|
|
11805
|
+
}
|
|
11806
|
+
const fromBase64 = (text2) => Uint8Array.from(atob(text2), (c) => c.charCodeAt(0));
|
|
11807
|
+
async function writeBundle(out, files) {
|
|
11808
|
+
const host2 = getHost();
|
|
11809
|
+
const root = host2.path.join(out, "aiws");
|
|
11810
|
+
await host2.remove(root, { recursive: true }).catch(() => void 0);
|
|
11811
|
+
for (const [path, bytes] of files) {
|
|
11812
|
+
const target = host2.path.join(root, path);
|
|
11813
|
+
await host2.mkdir(host2.path.dirname(target), { recursive: true });
|
|
11814
|
+
await host2.writeFile(target, bytes);
|
|
11815
|
+
}
|
|
11816
|
+
return root;
|
|
11817
|
+
}
|
|
11818
|
+
async function packBundle(ctx, input, out, defaultName, result) {
|
|
11819
|
+
if (input.pikg !== true && input.install !== true)
|
|
11820
|
+
return result;
|
|
11821
|
+
const host2 = getHost();
|
|
11822
|
+
const name = inputString(input, "name") ?? defaultName;
|
|
11823
|
+
const owner = inputString(input, "owner") ?? `did:bns:${ctx.principal.id}`;
|
|
11824
|
+
const version = inputString(input, "version") ?? "0.1.0";
|
|
11825
|
+
const appJson = host2.path.join(out, "dapp_meta", "app.json");
|
|
11826
|
+
const existing = await host2.readTextFile(appJson).then((t) => JSON.parse(t)).catch(() => null);
|
|
11827
|
+
if (!existing) {
|
|
11828
|
+
await pikgVerb(ctx, "init", { project_dir: out, name, owner, kind: "aiws-controls", source: "./aiws", version });
|
|
11829
|
+
} else if (inputString(input, "version") && existing.version !== version) {
|
|
11830
|
+
await host2.writeTextFile(appJson, `${JSON.stringify({ ...existing, version }, null, 2)}
|
|
11831
|
+
`);
|
|
11832
|
+
const pikgJson = host2.path.join(out, "dapp_meta", "pikg.json");
|
|
11833
|
+
const meta = JSON.parse(await host2.readTextFile(pikgJson));
|
|
11834
|
+
await host2.writeTextFile(pikgJson, `${JSON.stringify({ ...meta, pikg_file: `${existing.name}-${version}.pikg` }, null, 2)}
|
|
11835
|
+
`);
|
|
11836
|
+
}
|
|
11837
|
+
await pikgVerb(ctx, "build", { meta_dir: host2.path.join(out, "dapp_meta") });
|
|
11838
|
+
const packed = await pikgVerb(ctx, "pack", { dist_dir: host2.path.join(out, "dapp_dist") });
|
|
11839
|
+
const pikgPath = String(packed.pikg_path);
|
|
11840
|
+
result.pikg = pikgPath;
|
|
11841
|
+
if (input.install !== true)
|
|
11842
|
+
return result;
|
|
11843
|
+
result.install = await aiws(ctx, "ext.install", { pikg: await uploadPackage(ctx, pikgPath), mode: "dev" });
|
|
11844
|
+
return result;
|
|
11845
|
+
}
|
|
11846
|
+
const controlSummary = (controls) => controls.map((c) => ({ ident: c.ident, kind: c.kind, version: c.version, digest: c.digest }));
|
|
11744
11847
|
const extExportCommand = {
|
|
11745
11848
|
verb: "ext-export",
|
|
11746
11849
|
summary: "Export Workspace controls as a bundle in a local folder; optionally pack it as an aiws PIKG and install it into your library",
|
|
@@ -11749,13 +11852,9 @@ const extExportCommand = {
|
|
|
11749
11852
|
options: [
|
|
11750
11853
|
WORKSPACE_OPTION,
|
|
11751
11854
|
{ name: "out", property: "out", description: "Local folder to write into (created)", type: "string", required: true },
|
|
11752
|
-
|
|
11753
|
-
{ name: "version", property: "version", description: "Package version (default 0.1.0)", type: "string" },
|
|
11754
|
-
{ name: "owner", property: "owner", description: "Owner DID of the package (default did:bns:<you>)", type: "string" },
|
|
11755
|
-
{ name: "pikg", property: "pikg", description: "Also pack the bundle as an aiws PIKG", type: "boolean" },
|
|
11756
|
-
{ name: "install", property: "install", description: "Pack, then install into your AI Workspace library (dev mode)", type: "boolean" }
|
|
11855
|
+
...packageOptions("the first control’s")
|
|
11757
11856
|
],
|
|
11758
|
-
inputSchema: workspaceInput({ defs: { type: "string", minLength: 1 }, out: { type: "string", minLength: 1 },
|
|
11857
|
+
inputSchema: workspaceInput({ defs: { type: "string", minLength: 1 }, out: { type: "string", minLength: 1 }, ...PACKAGE_INPUT }, ["defs", "out"]),
|
|
11759
11858
|
outputSchema: OBJECT_OUTPUT,
|
|
11760
11859
|
resultSchemaVersion: 1,
|
|
11761
11860
|
access: { mode: "fixed", level: "write" },
|
|
@@ -11763,97 +11862,314 @@ const extExportCommand = {
|
|
|
11763
11862
|
requiresSession: true,
|
|
11764
11863
|
examples: ["buckyos aiws ext-export def_abc,def_xyz --workspace ws_abc --out ./kpi-pack --install"],
|
|
11765
11864
|
handler: async (ctx, input) => {
|
|
11766
|
-
const
|
|
11767
|
-
const
|
|
11768
|
-
const
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
return result;
|
|
11865
|
+
const out = getHost().path.resolve(ctx.cwd, requiredInputString(input, "out"));
|
|
11866
|
+
const bundle = await exportBundle(extRpc(ctx), requiredInputString(input, "workspace_id"), list(input, "defs"), assetFetcher(ctx));
|
|
11867
|
+
const result = { ok: true, bundle: await writeBundle(out, bundle.files), controls: controlSummary(bundle.manifest.controls) };
|
|
11868
|
+
return packBundle(ctx, input, out, bundle.manifest.controls[0]?.name.replace(/[._]/g, "-") ?? "controls", result);
|
|
11869
|
+
}
|
|
11870
|
+
};
|
|
11871
|
+
const extUpgradeCommand = {
|
|
11872
|
+
verb: "ext-upgrade",
|
|
11873
|
+
summary: "Upgrade controls a Workspace imported from your library to the version your library holds now (one Commit; instances keep their overrides and content)",
|
|
11874
|
+
description: 'Each definition is rebuilt from its package in your library (--pkg, or the package it was imported from), with what it builds on from the same package, dependencies first. A definition built locally since its import is refused (EXT_LOCAL_CHANGES; the error data lists the changed files) unless --force. A version that narrows what instances rely on needs --migration, a JSON file { "<author_did>#<name>": Migration } keyed by identity (config / bindings { rename, drop, fill }, slots { rename, move }, events { rename, drop }); --dry-run answers what it would write on each instance. Across Zones: `aiws library bundle <pkg_id> --out <file>` in the Zone of the library, then `aiws ext-import --bundle <file> --upgrade` in the Zone of the Workspace.',
|
|
11875
|
+
positionals: [{ name: "defs", description: "Definition (buckyos.block-def) ids, comma-separated", required: true }],
|
|
11876
|
+
options: [
|
|
11877
|
+
WORKSPACE_OPTION,
|
|
11878
|
+
{ name: "pkg", property: "pkg_id", description: "Library package to upgrade from (default: the one each definition was imported from)", type: "string" },
|
|
11879
|
+
{ name: "migration", property: "migration", description: 'JSON file { "<author_did>#<name>": Migration } rewriting the instances of a narrowing version', type: "string" },
|
|
11880
|
+
{ name: "dry-run", property: "dry_run", description: "Check only: the plan per instance, nothing committed", type: "boolean" },
|
|
11881
|
+
{ name: "force", property: "force", description: "Upgrade over local changes (they are replaced)", type: "boolean" }
|
|
11882
|
+
],
|
|
11883
|
+
inputSchema: workspaceInput({ defs: { type: "string", minLength: 1 }, pkg_id: { type: "string" }, migration: { type: "string" }, dry_run: { type: "boolean" }, force: { type: "boolean" } }, ["defs"]),
|
|
11884
|
+
outputSchema: OBJECT_OUTPUT,
|
|
11885
|
+
resultSchemaVersion: 1,
|
|
11886
|
+
access: { mode: "fixed", level: "write" },
|
|
11887
|
+
asyncMode: "sync",
|
|
11888
|
+
requiresSession: true,
|
|
11889
|
+
examples: ["buckyos aiws ext-upgrade def_abc --workspace ws_abc --dry-run", "buckyos aiws ext-upgrade def_abc,def_xyz --workspace ws_abc --migration ./migration.json"],
|
|
11890
|
+
handler: async (ctx, input) => {
|
|
11891
|
+
const pkg = inputString(input, "pkg_id");
|
|
11892
|
+
const migration = inputString(input, "migration");
|
|
11893
|
+
return aiws(ctx, "ext.upgrade", {
|
|
11894
|
+
workspace_id: requiredInputString(input, "workspace_id"),
|
|
11895
|
+
items: list(input, "defs").map((def_entity_id) => ({ def_entity_id, ...pkg ? { pkg_id: pkg } : {} })),
|
|
11896
|
+
...migration ? { migration: await migrationFile(ctx, migration) } : {},
|
|
11897
|
+
...input.force === true ? { force: true } : {},
|
|
11898
|
+
...input.dry_run === true ? { dry_run: true } : {},
|
|
11899
|
+
...ctx.idempotencyKey ? { idempotency_key: ctx.idempotencyKey } : {}
|
|
11900
|
+
});
|
|
11803
11901
|
}
|
|
11804
11902
|
};
|
|
11805
11903
|
const libraryCommand = {
|
|
11806
11904
|
verb: "library",
|
|
11807
|
-
summary: "Your AI Workspace control library: `list`, `install <file.pikg>` (an aiws package, dev mode), `remove <pkg_id>`",
|
|
11808
|
-
positionals: [{ name: "action", description: "list | install | remove", required: true }, { name: "target", description: "install: local .pikg path; remove: package id", required: false }],
|
|
11809
|
-
options: [
|
|
11810
|
-
|
|
11905
|
+
summary: "Your AI Workspace control library: `list` (each control with the identities it depends on, `deps`, and the package’s templates), `install <file.pikg>` (an aiws package, dev mode), `remove <pkg_id>`, `bundle <pkg_id> --out <file.json>` (the controls with their bytes, for `ext-import --bundle` in another Zone)",
|
|
11906
|
+
positionals: [{ name: "action", description: "list | install | remove | bundle", required: true }, { name: "target", description: "install: local .pikg path; remove, bundle: package id", required: false }],
|
|
11907
|
+
options: [
|
|
11908
|
+
{ name: "out", property: "out", description: "bundle: the JSON file to write", type: "string" },
|
|
11909
|
+
{ name: "idents", property: "idents", description: "bundle: only these controls (and what they build on), comma-separated identities", type: "string" }
|
|
11910
|
+
],
|
|
11911
|
+
inputSchema: { type: "object", properties: { action: { type: "string", enum: ["list", "install", "remove", "bundle"] }, target: { type: "string" }, out: { type: "string" }, idents: { type: "string" } }, required: ["action"], additionalProperties: false },
|
|
11811
11912
|
outputSchema: OBJECT_OUTPUT,
|
|
11812
11913
|
resultSchemaVersion: 1,
|
|
11813
11914
|
access: { mode: "fixed", level: "write" },
|
|
11814
11915
|
asyncMode: "sync",
|
|
11815
11916
|
requiresSession: true,
|
|
11816
|
-
examples: ["buckyos aiws library list", "buckyos aiws library install ./kpi-pack-0.1.0.pikg", "buckyos aiws library remove kpi-pack.alice.bns.did"],
|
|
11917
|
+
examples: ["buckyos aiws library list", "buckyos aiws library install ./kpi-pack-0.1.0.pikg", "buckyos aiws library remove kpi-pack.alice.bns.did", "buckyos aiws library bundle kpi-pack.alice.bns.did --out ./kpi-pack.bundle.json"],
|
|
11817
11918
|
handler: async (ctx, input) => {
|
|
11818
11919
|
const action = requiredInputString(input, "action");
|
|
11819
11920
|
if (action === "list")
|
|
11820
11921
|
return aiws(ctx, "ext.list_library", {});
|
|
11821
11922
|
if (action === "remove")
|
|
11822
11923
|
return aiws(ctx, "ext.uninstall", { pkg_id: requiredInputString(input, "target") });
|
|
11924
|
+
if (action === "bundle") {
|
|
11925
|
+
const host2 = getHost();
|
|
11926
|
+
const out = host2.path.resolve(ctx.cwd, requiredInputString(input, "out"));
|
|
11927
|
+
const idents = inputString(input, "idents");
|
|
11928
|
+
const r = await aiws(ctx, "ext.library_bundle", { pkg_id: requiredInputString(input, "target"), ...idents ? { idents: list(input, "idents") } : {} });
|
|
11929
|
+
const controls = Array.isArray(r.controls) ? r.controls : [];
|
|
11930
|
+
await host2.writeTextFile(out, `${JSON.stringify({ pkg_id: r.pkg_id, controls }, null, 2)}
|
|
11931
|
+
`);
|
|
11932
|
+
return { ok: true, out, pkg_id: r.pkg_id, controls: controls.map((c) => ({ ident: c.ident, version: c.version, digest: c.digest, deps: c.deps })) };
|
|
11933
|
+
}
|
|
11823
11934
|
if (action !== "install")
|
|
11824
|
-
throw new UsageError("AIWS_USAGE", "library: list, install <file.pikg> or
|
|
11935
|
+
throw new UsageError("AIWS_USAGE", "library: list, install <file.pikg>, remove <pkg_id> or bundle <pkg_id> --out <file.json>");
|
|
11825
11936
|
const path = getHost().path.resolve(ctx.cwd, requiredInputString(input, "target"));
|
|
11826
11937
|
return aiws(ctx, "ext.install", { pikg: await uploadPackage(ctx, path), mode: "dev" });
|
|
11827
11938
|
}
|
|
11828
11939
|
};
|
|
11940
|
+
async function relayBundle(ctx, ws, controls, opts) {
|
|
11941
|
+
const pending = [...controls];
|
|
11942
|
+
const needed = new Set(controls.flatMap((c) => c.deps ?? []));
|
|
11943
|
+
const out = [];
|
|
11944
|
+
while (pending.length > 0) {
|
|
11945
|
+
const left = new Set(pending.map((c2) => c2.ident));
|
|
11946
|
+
const c = pending.splice(Math.max(0, pending.findIndex((p) => (p.deps ?? []).every((d) => !left.has(d) || d === p.ident))), 1)[0];
|
|
11947
|
+
try {
|
|
11948
|
+
const r = await aiws(ctx, "ext.build", {
|
|
11949
|
+
workspace_id: ws,
|
|
11950
|
+
files: await sourceErrors(() => packBuildFiles(c.files.map((f) => ({ name: f.name, bytes: fromBase64(f.base64) })), sourceUploader(ctx))),
|
|
11951
|
+
provenance: c.provenance,
|
|
11952
|
+
...opts.folderId ? { folder_id: opts.folderId } : {},
|
|
11953
|
+
...opts.upgrade ? { upgrade: true } : {},
|
|
11954
|
+
...opts.migration?.[c.ident] ? { migration: { [c.ident]: opts.migration[c.ident] } } : {}
|
|
11955
|
+
});
|
|
11956
|
+
out.push({ ident: c.ident, def_entity_id: r.def_entity_id, action: r.action ?? r.status, version: r.version });
|
|
11957
|
+
} catch (error) {
|
|
11958
|
+
if (opts.upgrade || !needed.has(c.ident) || !(error instanceof ToolError) || error.details.sub_code !== "EXT_IDENT_EXISTS")
|
|
11959
|
+
throw error;
|
|
11960
|
+
out.push({ ident: c.ident, def_entity_id: error.details.data?.def_entity_id, action: "satisfied" });
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11963
|
+
return out;
|
|
11964
|
+
}
|
|
11829
11965
|
const extImportCommand = {
|
|
11830
11966
|
verb: "ext-import",
|
|
11831
|
-
summary: "Bring controls of your library into a Workspace (one Commit): <pkg_id>#<ident>, comma-separated",
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
|
|
11967
|
+
summary: "Bring controls of your library into a Workspace (one Commit): <pkg_id>#<ident>, comma-separated; what they build on (deps) comes along, dependencies first, and an identity the Workspace already has satisfies it. --bundle builds a library bundle of another Zone instead",
|
|
11968
|
+
description: 'Across Zones: `aiws library bundle <pkg_id> --out <file>` in the Zone of the library, then `aiws ext-import --bundle <file> --workspace <ws>` in the Zone of the Workspace: each control is built with its package provenance, dependencies first; a dependency the Workspace already holds is met by that definition. --upgrade (with --bundle) upgrades the identities the Workspace holds from another version, --migration naming a JSON file { "<author_did>#<name>": Migration } for a version that narrows what instances rely on. Upgrades from your library in the same Zone are `aiws ext-upgrade`.',
|
|
11969
|
+
positionals: [{ name: "items", description: "<pkg_id>#<author_did>#<name>[,…] (ext.list_library names them); not with --bundle", required: false }],
|
|
11970
|
+
options: [
|
|
11971
|
+
WORKSPACE_OPTION,
|
|
11972
|
+
{ name: "folder", property: "folder_id", description: "Data folder for the definitions (default: data/extensions)", type: "string" },
|
|
11973
|
+
{ name: "bundle", property: "bundle", description: "A library bundle file (`aiws library bundle`, any Zone) to build instead of items", type: "string" },
|
|
11974
|
+
{ name: "upgrade", property: "upgrade", description: "With --bundle: upgrade identities the Workspace holds from another version", type: "boolean" },
|
|
11975
|
+
{ name: "migration", property: "migration", description: 'With --upgrade: JSON file { "<author_did>#<name>": Migration }', type: "string" }
|
|
11976
|
+
],
|
|
11977
|
+
inputSchema: workspaceInput({ items: { type: "string", minLength: 1 }, folder_id: { type: "string" }, bundle: { type: "string" }, upgrade: { type: "boolean" }, migration: { type: "string" } }),
|
|
11835
11978
|
outputSchema: OBJECT_OUTPUT,
|
|
11836
11979
|
resultSchemaVersion: 1,
|
|
11837
11980
|
access: { mode: "fixed", level: "write" },
|
|
11838
11981
|
asyncMode: "sync",
|
|
11839
11982
|
requiresSession: true,
|
|
11840
|
-
examples: ["buckyos aiws ext-import kpi-pack.alice.bns.did#did:bns:alice#kpi-card --workspace ws_abc"],
|
|
11841
|
-
handler: (ctx, input) => {
|
|
11842
|
-
const
|
|
11843
|
-
const cut = item.indexOf("#");
|
|
11844
|
-
if (cut <= 0)
|
|
11845
|
-
throw new UsageError("AIWS_USAGE", `${item}: <pkg_id>#<ident>`);
|
|
11846
|
-
return { pkg_id: item.slice(0, cut), ident: item.slice(cut + 1) };
|
|
11847
|
-
});
|
|
11983
|
+
examples: ["buckyos aiws ext-import kpi-pack.alice.bns.did#did:bns:alice#kpi-card --workspace ws_abc", "buckyos aiws ext-import --bundle ./kpi-pack.bundle.json --workspace ws_abc --upgrade"],
|
|
11984
|
+
handler: async (ctx, input) => {
|
|
11985
|
+
const ws = requiredInputString(input, "workspace_id");
|
|
11848
11986
|
const folder = inputString(input, "folder_id");
|
|
11849
|
-
|
|
11987
|
+
const bundle = inputString(input, "bundle");
|
|
11988
|
+
const migration = inputString(input, "migration");
|
|
11989
|
+
if (migration && input.upgrade !== true)
|
|
11990
|
+
throw new UsageError("AIWS_USAGE", "--migration goes with --upgrade");
|
|
11991
|
+
if (!bundle) {
|
|
11992
|
+
if (input.upgrade === true)
|
|
11993
|
+
throw new UsageError("AIWS_USAGE", "--upgrade goes with --bundle (a library bundle of another Zone); upgrade what came from your own library with `aiws ext-upgrade`");
|
|
11994
|
+
const items = list(input, "items").map((item) => {
|
|
11995
|
+
const cut = item.indexOf("#");
|
|
11996
|
+
if (cut <= 0)
|
|
11997
|
+
throw new UsageError("AIWS_USAGE", `${item}: <pkg_id>#<ident>`);
|
|
11998
|
+
return { pkg_id: item.slice(0, cut), ident: item.slice(cut + 1) };
|
|
11999
|
+
});
|
|
12000
|
+
return aiws(ctx, "ext.import", { workspace_id: ws, items, ...folder ? { folder_id: folder } : {} });
|
|
12001
|
+
}
|
|
12002
|
+
if (inputString(input, "items"))
|
|
12003
|
+
throw new UsageError("AIWS_USAGE", "pass items or --bundle, not both");
|
|
12004
|
+
const host2 = getHost();
|
|
12005
|
+
const file = JSON.parse(await host2.readTextFile(host2.path.resolve(ctx.cwd, bundle)));
|
|
12006
|
+
if (!Array.isArray(file.controls) || file.controls.some((c) => !c || typeof c.ident !== "string" || !Array.isArray(c.files))) {
|
|
12007
|
+
throw new UsageError("AIWS_USAGE", `--bundle ${bundle}: not a library bundle ({ pkg_id, controls: [{ ident, provenance, files }] }, from \`aiws library bundle\`)`);
|
|
12008
|
+
}
|
|
12009
|
+
const controls = await relayBundle(ctx, ws, file.controls, { folderId: folder, upgrade: input.upgrade === true, ...migration ? { migration: await migrationFile(ctx, migration) } : {} });
|
|
12010
|
+
return { ok: true, controls };
|
|
12011
|
+
}
|
|
12012
|
+
};
|
|
12013
|
+
function createParams(text2, declared) {
|
|
12014
|
+
const out = {};
|
|
12015
|
+
for (const pair of text2.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
12016
|
+
const cut = pair.indexOf("=");
|
|
12017
|
+
const name = cut > 0 ? pair.slice(0, cut) : "";
|
|
12018
|
+
if (!Object.hasOwn(declared, name))
|
|
12019
|
+
throw new UsageError("AIWS_USAGE", `--param ${pair}: the template's params are ${Object.keys(declared).join(", ") || "(none)"}`);
|
|
12020
|
+
out[name] = typedValue(declared[name].type, pair.slice(cut + 1), `--param ${pair}`);
|
|
12021
|
+
}
|
|
12022
|
+
return out;
|
|
12023
|
+
}
|
|
12024
|
+
function exportParams(text2) {
|
|
12025
|
+
const out = {};
|
|
12026
|
+
for (const item of text2.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
12027
|
+
const [name, type, ...rest] = item.split(":");
|
|
12028
|
+
if (!name || !["string", "number", "boolean"].includes(type))
|
|
12029
|
+
throw new UsageError("AIWS_USAGE", `--param ${item}: <name>:<string | number | boolean>[:<default>]`);
|
|
12030
|
+
out[name] = { type, ...rest.length > 0 ? { default: typedValue(type, rest.join(":"), `--param ${item}`) } : {} };
|
|
12031
|
+
}
|
|
12032
|
+
return out;
|
|
12033
|
+
}
|
|
12034
|
+
function typedValue(type, raw, label) {
|
|
12035
|
+
if (type === "number") {
|
|
12036
|
+
const n = Number(raw);
|
|
12037
|
+
if (!raw.trim() || !Number.isFinite(n))
|
|
12038
|
+
throw new UsageError("AIWS_USAGE", `${label}: a number`);
|
|
12039
|
+
return n;
|
|
12040
|
+
}
|
|
12041
|
+
if (type === "boolean") {
|
|
12042
|
+
if (raw !== "true" && raw !== "false")
|
|
12043
|
+
throw new UsageError("AIWS_USAGE", `${label}: true or false`);
|
|
12044
|
+
return raw === "true";
|
|
12045
|
+
}
|
|
12046
|
+
return raw;
|
|
12047
|
+
}
|
|
12048
|
+
async function exportTemplate(ctx, input) {
|
|
12049
|
+
const ws = requiredInputString(input, "workspace_id");
|
|
12050
|
+
const id = requiredInputString(input, "id");
|
|
12051
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id))
|
|
12052
|
+
throw new UsageError("AIWS_USAGE", `--id ${id}: [a-z0-9][a-z0-9-]{0,63}`);
|
|
12053
|
+
const description = inputString(input, "description");
|
|
12054
|
+
const param = inputString(input, "param");
|
|
12055
|
+
const template = {
|
|
12056
|
+
id,
|
|
12057
|
+
title: requiredInputString(input, "title"),
|
|
12058
|
+
...description ? { description } : {},
|
|
12059
|
+
...param ? { params: exportParams(param) } : {},
|
|
12060
|
+
...inputString(input, "reset") ? { reset: { tables: list(input, "reset") } } : {}
|
|
12061
|
+
};
|
|
12062
|
+
const out = getHost().path.resolve(ctx.cwd, requiredInputString(input, "out"));
|
|
12063
|
+
const r = await aiws(ctx, "ext.export_template", { workspace_id: ws, surfaces: list(input, "surfaces"), ...inputString(input, "data") ? { data: list(input, "data") } : {}, template });
|
|
12064
|
+
const item = expectObject$4(r.template, "ext.export_template.template");
|
|
12065
|
+
const defs = (Array.isArray(r.controls) ? r.controls : []).map((c) => typeof c === "string" ? c : String(expectObject$4(c, "ext.export_template.controls[]").entity_id));
|
|
12066
|
+
const files = /* @__PURE__ */ new Map();
|
|
12067
|
+
let manifest = { manifest_version: AIWS_MANIFEST_VERSION, purpose: "aiws", host_api: {}, controls: [] };
|
|
12068
|
+
if (defs.length > 0) {
|
|
12069
|
+
const bundle = await exportBundle(extRpc(ctx), ws, defs, assetFetcher(ctx));
|
|
12070
|
+
for (const [path, bytes] of bundle.files)
|
|
12071
|
+
files.set(path, bytes);
|
|
12072
|
+
manifest = bundle.manifest;
|
|
12073
|
+
}
|
|
12074
|
+
const dir = templateDir(id);
|
|
12075
|
+
for (const f of Array.isArray(r.files) ? r.files : []) {
|
|
12076
|
+
const path = f.path.startsWith(dir) ? f.path : `${dir}${f.path}`;
|
|
12077
|
+
if (!isSourcePath(path))
|
|
12078
|
+
throw new ToolError("INVALID_SERVICE_RESPONSE", `ext.export_template: ${f.path} is not a file of the template`);
|
|
12079
|
+
files.set(path, fromBase64(f.base64));
|
|
12080
|
+
}
|
|
12081
|
+
manifest = { ...manifest, manifest_version: AIWS_MANIFEST_VERSION, workspace_templates: [item] };
|
|
12082
|
+
files.set(AIWS_MANIFEST_FILE, new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}
|
|
12083
|
+
`));
|
|
12084
|
+
const result = {
|
|
12085
|
+
ok: true,
|
|
12086
|
+
bundle: await writeBundle(out, files),
|
|
12087
|
+
template: { id: item.id, title: item.title, digest: item.digest, requires: item.requires },
|
|
12088
|
+
controls: controlSummary(manifest.controls)
|
|
12089
|
+
};
|
|
12090
|
+
return packBundle(ctx, input, out, id, result);
|
|
12091
|
+
}
|
|
12092
|
+
const templateCommand = {
|
|
12093
|
+
verb: "template",
|
|
12094
|
+
summary: "Workspace templates (snapshots carried in control packages): `list` (the templates of your library), `create <pkg_id>#<template_id>` (a new Workspace from one: answers its workspace_id), `export` (Surfaces of a Workspace as a template bundle; --pikg / --install pack it with the controls it needs and install it)",
|
|
12095
|
+
description: "create: --title names the new Workspace; --param k=v[,k=v…] sets the template’s params (typed as the template declares them; `template list` shows them). Installing a template never creates a Workspace; each create makes an independent one. export: --workspace, --surfaces s1,s2, --id and --title are required; --data e1,e2 carries data entities along, --param name:type[:default][,…] (type string | number | boolean) declares what a creator fills into {{params.<name>}}, --reset t1,t2 names tables a new Workspace starts without rows. It writes <out>/aiws/ (aiws-manifest.json at manifest_version 3 with the template in workspace_templates, controls/<dir>/ of the controls it needs, templates/<id>/), then packs and installs it as `ext-export` does.",
|
|
12096
|
+
positionals: [{ name: "action", description: "list | create | export", required: true }, { name: "target", description: "create: <pkg_id>#<template_id>", required: false }],
|
|
12097
|
+
options: [
|
|
12098
|
+
{ name: "workspace", property: "workspace_id", description: "export: Workspace id (ws_…)", type: "string" },
|
|
12099
|
+
{ name: "surfaces", property: "surfaces", description: "export: Surface ids, comma-separated", type: "string" },
|
|
12100
|
+
{ name: "id", property: "id", description: "export: template id ([a-z0-9][a-z0-9-]{0,63})", type: "string" },
|
|
12101
|
+
{ name: "title", property: "title", description: "create: title of the new Workspace; export: title of the template", type: "string" },
|
|
12102
|
+
{ name: "description", property: "description", description: "export: what the template is for", type: "string" },
|
|
12103
|
+
{ name: "data", property: "data", description: "export: data entity ids to carry along, comma-separated", type: "string" },
|
|
12104
|
+
{ name: "param", property: "param", description: "create: k=v[,k=v…]; export: name:type[:default][,…]", type: "string" },
|
|
12105
|
+
{ name: "reset", property: "reset", description: "export: tables a new Workspace starts without rows, comma-separated", type: "string" },
|
|
12106
|
+
{ name: "out", property: "out", description: "export: local folder to write into (created)", type: "string" },
|
|
12107
|
+
...packageOptions("the template id")
|
|
12108
|
+
],
|
|
12109
|
+
inputSchema: {
|
|
12110
|
+
type: "object",
|
|
12111
|
+
properties: {
|
|
12112
|
+
action: { type: "string", enum: ["list", "create", "export"] },
|
|
12113
|
+
target: { type: "string" },
|
|
12114
|
+
workspace_id: { type: "string" },
|
|
12115
|
+
surfaces: { type: "string" },
|
|
12116
|
+
id: { type: "string" },
|
|
12117
|
+
title: { type: "string" },
|
|
12118
|
+
description: { type: "string" },
|
|
12119
|
+
data: { type: "string" },
|
|
12120
|
+
param: { type: "string" },
|
|
12121
|
+
reset: { type: "string" },
|
|
12122
|
+
out: { type: "string" },
|
|
12123
|
+
...PACKAGE_INPUT
|
|
12124
|
+
},
|
|
12125
|
+
required: ["action"],
|
|
12126
|
+
additionalProperties: false
|
|
12127
|
+
},
|
|
12128
|
+
outputSchema: OBJECT_OUTPUT,
|
|
12129
|
+
resultSchemaVersion: 1,
|
|
12130
|
+
access: { mode: "fixed", level: "write" },
|
|
12131
|
+
asyncMode: "sync",
|
|
12132
|
+
requiresSession: true,
|
|
12133
|
+
examples: [
|
|
12134
|
+
"buckyos aiws template list",
|
|
12135
|
+
'buckyos aiws template create team-pack.alice.bns.did#team-board --title "Ops board" --param team_name=Ops',
|
|
12136
|
+
'buckyos aiws template export --workspace ws_abc --surfaces sf_board --id team-board --title "Team task board" --param team_name:string:Team --reset tasks --out ./team-pack --install'
|
|
12137
|
+
],
|
|
12138
|
+
handler: async (ctx, input) => {
|
|
12139
|
+
const action = requiredInputString(input, "action");
|
|
12140
|
+
if (action === "list") {
|
|
12141
|
+
const library = await aiws(ctx, "ext.list_library", {});
|
|
12142
|
+
const packages = Array.isArray(library.packages) ? library.packages : [];
|
|
12143
|
+
return { ok: true, templates: packages.flatMap((p) => (p.templates ?? []).map((t) => ({ ref: `${p.pkg_id}#${t.id}`, pkg_id: p.pkg_id, version: p.version, ...t }))) };
|
|
12144
|
+
}
|
|
12145
|
+
if (action === "export")
|
|
12146
|
+
return exportTemplate(ctx, input);
|
|
12147
|
+
if (action !== "create")
|
|
12148
|
+
throw new UsageError("AIWS_USAGE", "template: list, create <pkg_id>#<template_id> or export --workspace <ws> --surfaces <ids> --id <id> --title <title> --out <dir>");
|
|
12149
|
+
const target = requiredInputString(input, "target");
|
|
12150
|
+
const cut = target.indexOf("#");
|
|
12151
|
+
if (cut <= 0 || cut === target.length - 1)
|
|
12152
|
+
throw new UsageError("AIWS_USAGE", `${target}: <pkg_id>#<template_id>`);
|
|
12153
|
+
const [pkg_id, template_id] = [target.slice(0, cut), target.slice(cut + 1)];
|
|
12154
|
+
const param = inputString(input, "param");
|
|
12155
|
+
let params;
|
|
12156
|
+
if (param) {
|
|
12157
|
+
const library = await aiws(ctx, "ext.list_library", {});
|
|
12158
|
+
const packages = Array.isArray(library.packages) ? library.packages : [];
|
|
12159
|
+
const found = packages.find((p) => p.pkg_id === pkg_id)?.templates?.find((t) => t.id === template_id);
|
|
12160
|
+
if (!found)
|
|
12161
|
+
throw new UsageError("AIWS_USAGE", `${target} is not a template of your library (aiws template list)`);
|
|
12162
|
+
params = createParams(param, found.params ?? {});
|
|
12163
|
+
}
|
|
12164
|
+
const title = inputString(input, "title");
|
|
12165
|
+
return aiws(ctx, "ext.create_from_template", { pkg_id, template_id, ...title ? { title } : {}, ...params ? { params } : {} });
|
|
11850
12166
|
}
|
|
11851
12167
|
};
|
|
11852
12168
|
function createAiwsModule() {
|
|
11853
12169
|
return {
|
|
11854
12170
|
name: "aiws",
|
|
11855
|
-
summary: "AI Workspace: read a workspace, Block-controls (catalogue, import, export), extension controls (build, pull, export, library, import), and execute wish runs as their executor (claim, heartbeat, report)",
|
|
11856
|
-
commands: [infoCommand, readCommand, queryCommand, controlsCommand, importCommand, exportCommand, buildCommand, pullCommand, extExportCommand, libraryCommand, extImportCommand, runGetCommand, runClaimCommand, runHeartbeatCommand, runFilesCommand, runToolCommand, runProgramCommand, runReportCommand]
|
|
12171
|
+
summary: "AI Workspace: read a workspace, Block-controls (catalogue, import, export), extension controls (build, pull, export, library, import, upgrade), Workspace templates (list, create, export), and execute wish runs as their executor (claim, heartbeat, report)",
|
|
12172
|
+
commands: [infoCommand, readCommand, queryCommand, controlsCommand, importCommand, exportCommand, buildCommand, pullCommand, extExportCommand, libraryCommand, extImportCommand, extUpgradeCommand, templateCommand, runGetCommand, runClaimCommand, runHeartbeatCommand, runFilesCommand, runToolCommand, runProgramCommand, runReportCommand]
|
|
11857
12173
|
};
|
|
11858
12174
|
}
|
|
11859
12175
|
const VERSION = TOOL_VERSION;
|