buckyos 0.7.159 → 0.7.160
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 +396 -18
- package/cli/dist/cli.mjs.map +1 -1
- package/cli/modules/aiws.ts +327 -4
- package/cli/modules/pikg.ts +29 -3
- package/cli/modules/pikg_protocol.ts +42 -3
- package/cli/modules/share.ts +2 -1
- package/cli/version.ts +1 -1
- package/dist/aiworkspace_client.d.ts +143 -0
- package/dist/aiworkspace_client.d.ts.map +1 -1
- package/dist/aiws_ext.cjs +2 -0
- package/dist/aiws_ext.cjs.map +1 -0
- package/dist/aiws_ext.d.ts +169 -0
- package/dist/aiws_ext.d.ts.map +1 -0
- package/dist/aiws_ext.mjs +499 -0
- package/dist/aiws_ext.mjs.map +1 -0
- package/dist/browser.cjs +1 -1
- package/dist/browser.mjs +2 -2
- package/dist/{executor_client-24abb248.js → executor_client-47edd42d.js} +2 -2
- package/dist/executor_client-47edd42d.js.map +1 -0
- package/dist/{executor_client-24076936.mjs → executor_client-50023bcc.mjs} +58 -1
- package/dist/executor_client-50023bcc.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +36 -2
- package/dist/index.mjs.map +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.mjs +38 -2
- package/dist/node.mjs.map +1 -1
- package/package.json +6 -1
- package/dist/executor_client-24076936.mjs.map +0 -1
- package/dist/executor_client-24abb248.js.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, taskMgrTaskEventPath, ndm_proxy, objectUrl as objectUrl$1, ExecutorCommandStream } 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 } 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";
|
|
@@ -866,8 +866,8 @@ async function resolveConfig(explicit, environment2 = readEnvironment(), options
|
|
|
866
866
|
const timeoutMs = explicit.timeout ? parseDuration(explicit.timeout) : DEFAULT_TIMEOUT_MS;
|
|
867
867
|
if (endpoint)
|
|
868
868
|
validateEndpoint(endpoint);
|
|
869
|
-
const
|
|
870
|
-
if (
|
|
869
|
+
const sessionToken2 = explicit.sessionToken ?? environment2.BUCKYOS_APPCLIENT_SESSION_TOKEN;
|
|
870
|
+
if (sessionToken2)
|
|
871
871
|
sources.session_token = explicit.sessionToken ? "argument" : "environment";
|
|
872
872
|
if (profileName) {
|
|
873
873
|
sources.profile = explicit.profile ? "argument" : environment2.BUCKYOS_TOOL_PROFILE ? "environment" : "config";
|
|
@@ -880,7 +880,7 @@ async function resolveConfig(explicit, environment2 = readEnvironment(), options
|
|
|
880
880
|
zone,
|
|
881
881
|
endpoint,
|
|
882
882
|
identity,
|
|
883
|
-
sessionToken,
|
|
883
|
+
sessionToken: sessionToken2,
|
|
884
884
|
sessionTokenFile: explicit.sessionTokenFile,
|
|
885
885
|
output,
|
|
886
886
|
defaultProtocol,
|
|
@@ -4164,6 +4164,7 @@ function derivedSelector(key) {
|
|
|
4164
4164
|
agent: {},
|
|
4165
4165
|
agent_skills: {},
|
|
4166
4166
|
agent_tools: {},
|
|
4167
|
+
aiws: {},
|
|
4167
4168
|
script: {}
|
|
4168
4169
|
};
|
|
4169
4170
|
return selectors[key];
|
|
@@ -4482,6 +4483,24 @@ function validateServiceConfigTips(value, label) {
|
|
|
4482
4483
|
function appDocObjectId(value) {
|
|
4483
4484
|
return ndn.buildNamedObjectByJson("appdoc", value)[0].toString();
|
|
4484
4485
|
}
|
|
4486
|
+
const AIWS_SUB_PACKAGE = "aiws";
|
|
4487
|
+
const MAX_AIWS_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
4488
|
+
async function verifyAiwsPayload(tarGz) {
|
|
4489
|
+
const files = /* @__PURE__ */ new Map();
|
|
4490
|
+
for (const entry of await readTarGz(tarGz, MAX_AIWS_PAYLOAD_BYTES))
|
|
4491
|
+
files.set(entry.path, entry.bytes);
|
|
4492
|
+
let checked;
|
|
4493
|
+
try {
|
|
4494
|
+
checked = await verifyBundle(files);
|
|
4495
|
+
} catch (error) {
|
|
4496
|
+
throw invalid("aiws", `aiws bundle: ${error instanceof Error ? error.message : String(error)}`);
|
|
4497
|
+
}
|
|
4498
|
+
const errors = checked.diagnostics.filter((d) => d.severity === "error");
|
|
4499
|
+
if (errors.length > 0) {
|
|
4500
|
+
throw invalid("aiws", `aiws bundle: ${errors.map((d) => `${d.file}: ${d.message}`).join("; ")}`);
|
|
4501
|
+
}
|
|
4502
|
+
return { manifest_version: checked.manifest.manifest_version, controls: checked.manifest.controls.map((c) => ({ ident: c.ident, kind: c.kind, version: c.version, digest: c.digest })) };
|
|
4503
|
+
}
|
|
4485
4504
|
async function validateSnapshot(distDir, appDoc, packageMeta) {
|
|
4486
4505
|
const payloads = /* @__PURE__ */ new Map();
|
|
4487
4506
|
for (const entryValue of Object.values(packageMeta.content_index)) {
|
|
@@ -4490,7 +4509,12 @@ async function validateSnapshot(distDir, appDoc, packageMeta) {
|
|
|
4490
4509
|
const digest = await digestFile(path);
|
|
4491
4510
|
payloads.set(entry.path, digest);
|
|
4492
4511
|
}
|
|
4493
|
-
|
|
4512
|
+
const validated = validateObjectGraph(appDoc, packageMeta, payloads, true, false, true);
|
|
4513
|
+
if (appDoc.app_type === "aiws") {
|
|
4514
|
+
for (const sub of validated.subpackages)
|
|
4515
|
+
await verifyAiwsPayload(await getHost().readFile(join$1(distDir, sub.payload_path)));
|
|
4516
|
+
}
|
|
4517
|
+
return validated;
|
|
4494
4518
|
}
|
|
4495
4519
|
async function packSnapshot(distDir, destination, appDoc, packageMeta) {
|
|
4496
4520
|
const validated = await validateSnapshot(distDir, appDoc, packageMeta);
|
|
@@ -4595,9 +4619,15 @@ async function inspectPikg(path) {
|
|
|
4595
4619
|
Boolean(jwtEntry),
|
|
4596
4620
|
!jsonDoc || !jwtDoc || appDocObjectId(jsonDoc) === appDocObjectId(jwtDoc)
|
|
4597
4621
|
);
|
|
4622
|
+
let aiws2;
|
|
4623
|
+
if (appDoc.app_type === "aiws") {
|
|
4624
|
+
const sub = validated.subpackages[0];
|
|
4625
|
+
aiws2 = await verifyAiwsPayload(await readZipEntry(archive, archive.byName.get(sub.payload_path), MAX_AIWS_PAYLOAD_BYTES));
|
|
4626
|
+
}
|
|
4598
4627
|
const pikgDigest = await digestFile(path);
|
|
4599
4628
|
return {
|
|
4600
4629
|
schema_version: 1,
|
|
4630
|
+
...aiws2 ? { aiws: aiws2 } : {},
|
|
4601
4631
|
protocol: PACKAGE_META_SCHEMA,
|
|
4602
4632
|
pikg_path: resolve$2(path),
|
|
4603
4633
|
size: pikgDigest.size,
|
|
@@ -4894,7 +4924,7 @@ function validateAppDocShape(appDoc) {
|
|
|
4894
4924
|
}
|
|
4895
4925
|
if (appDoc.doc_type !== "app")
|
|
4896
4926
|
throw invalid("appdoc", "APPDOC.doc_type must be app");
|
|
4897
|
-
if (!["service", "dapp", "web", "agent", "agent_skills"].includes(String(appDoc.app_type))) {
|
|
4927
|
+
if (!["service", "dapp", "web", "agent", "agent_skills", "aiws"].includes(String(appDoc.app_type))) {
|
|
4898
4928
|
throw invalid("appdoc", "APPDOC.app_type is invalid");
|
|
4899
4929
|
}
|
|
4900
4930
|
if (appDoc.name !== void 0)
|
|
@@ -4952,6 +4982,9 @@ function validateAppDocShape(appDoc) {
|
|
|
4952
4982
|
throw invalid("appdoc", "an agent_skills App must contain only the agent_skills sub package");
|
|
4953
4983
|
}
|
|
4954
4984
|
}
|
|
4985
|
+
if (appDoc.app_type === "aiws" && Object.keys(pkgList).join() !== AIWS_SUB_PACKAGE) {
|
|
4986
|
+
throw invalid("appdoc", "an aiws package must contain only the aiws sub package");
|
|
4987
|
+
}
|
|
4955
4988
|
deriveNamespaceForPackage(appDoc);
|
|
4956
4989
|
}
|
|
4957
4990
|
function deriveNamespaceForPackage(appDoc) {
|
|
@@ -5205,7 +5238,7 @@ function validateContentHandlers(value, permissions) {
|
|
|
5205
5238
|
}
|
|
5206
5239
|
return value;
|
|
5207
5240
|
}
|
|
5208
|
-
const PACKAGE_VERSION = "0.7.
|
|
5241
|
+
const PACKAGE_VERSION = "0.7.160";
|
|
5209
5242
|
const TOOL_VERSION = PACKAGE_VERSION;
|
|
5210
5243
|
const SDK_VERSION = PACKAGE_VERSION;
|
|
5211
5244
|
const PROTOCOL_VERSION = "1";
|
|
@@ -5223,11 +5256,13 @@ const SHA256_ID = /^sha256:[0-9a-f]{64}$/;
|
|
|
5223
5256
|
const OBJECT_ID = /^(?:appdoc|pkg):[0-9a-f]{64}$/;
|
|
5224
5257
|
const SAFE_GENERATED_FILE = /^[A-Za-z0-9._-]+$/;
|
|
5225
5258
|
const OBJECT_OUTPUT$c = { type: "object", additionalProperties: true };
|
|
5226
|
-
const APP_KINDS = ["static-web", "script", "docker", "agent-skills"];
|
|
5259
|
+
const APP_KINDS = ["static-web", "script", "docker", "agent-skills", "aiws-controls"];
|
|
5227
5260
|
const PATH_KINDS = {
|
|
5228
5261
|
"static-web": { key: "web", category: "web", selectorType: "static" },
|
|
5229
5262
|
script: { key: "script", category: "dapp", selectorType: "single" },
|
|
5230
|
-
"agent-skills": { key: "agent_skills", category: "agent_skills", selectorType: "single" }
|
|
5263
|
+
"agent-skills": { key: "agent_skills", category: "agent_skills", selectorType: "single" },
|
|
5264
|
+
// AI Workspace controls: one bundle (aiws-manifest.json + controls/<dir>/), installed into the AI Workspace library
|
|
5265
|
+
"aiws-controls": { key: "aiws", category: "aiws", selectorType: "single" }
|
|
5231
5266
|
};
|
|
5232
5267
|
function packageEnvironmentQualifier(key, selector) {
|
|
5233
5268
|
const effective = selector ?? derivedSelector(key);
|
|
@@ -5322,7 +5357,8 @@ function createPikgModule(dependencies = {}) {
|
|
|
5322
5357
|
examples: [
|
|
5323
5358
|
"buckyos pikg init .",
|
|
5324
5359
|
"buckyos --non-interactive pikg init . --owner did:bns:root --kind static-web --source ./web/dist",
|
|
5325
|
-
"buckyos --non-interactive pikg init . --owner did:bns:root --kind agent-skills --source ./skills"
|
|
5360
|
+
"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"
|
|
5326
5362
|
],
|
|
5327
5363
|
handler: async (ctx, input) => await initCommand(ctx, input, docker)
|
|
5328
5364
|
},
|
|
@@ -5345,7 +5381,7 @@ function createPikgModule(dependencies = {}) {
|
|
|
5345
5381
|
execution: "local",
|
|
5346
5382
|
networkAccess: false,
|
|
5347
5383
|
examples: ["buckyos pikg build ./dapp_meta"],
|
|
5348
|
-
handler: async (ctx, input) => await buildCommand(ctx, input, docker, now)
|
|
5384
|
+
handler: async (ctx, input) => await buildCommand$1(ctx, input, docker, now)
|
|
5349
5385
|
},
|
|
5350
5386
|
{
|
|
5351
5387
|
verb: "pack",
|
|
@@ -5562,11 +5598,15 @@ async function initCommand(ctx, input, docker) {
|
|
|
5562
5598
|
try {
|
|
5563
5599
|
await getHost().lstat(absoluteSource);
|
|
5564
5600
|
} catch (error) {
|
|
5565
|
-
if (isHostError(error, "NotFound"))
|
|
5601
|
+
if (!isHostError(error, "NotFound"))
|
|
5602
|
+
throw error;
|
|
5603
|
+
if (kind === "aiws-controls") {
|
|
5604
|
+
await writeAiwsSkeleton(absoluteSource, owner, name);
|
|
5605
|
+
sourceSummary2.skeleton = true;
|
|
5606
|
+
} else {
|
|
5566
5607
|
await ctx.io.stderr(`warning: source does not exist yet: ${sourceSummary2.path}
|
|
5567
5608
|
`);
|
|
5568
|
-
}
|
|
5569
|
-
throw error;
|
|
5609
|
+
}
|
|
5570
5610
|
}
|
|
5571
5611
|
}
|
|
5572
5612
|
const appMeta = {
|
|
@@ -5626,7 +5666,25 @@ async function initCommand(ctx, input, docker) {
|
|
|
5626
5666
|
next_command: `buckyos pikg build ${metaDir}`
|
|
5627
5667
|
};
|
|
5628
5668
|
}
|
|
5629
|
-
async function
|
|
5669
|
+
async function writeAiwsSkeleton(dir, ownerDid, name) {
|
|
5670
|
+
const files = controlSkeleton({ authorDid: ownerDid, name, title: name });
|
|
5671
|
+
const encoder = new TextEncoder();
|
|
5672
|
+
const host2 = getHost();
|
|
5673
|
+
await host2.mkdir(join(dir, "controls", name), { recursive: true });
|
|
5674
|
+
for (const [file, text2] of Object.entries(files))
|
|
5675
|
+
await host2.writeTextFile(join(dir, "controls", name, file), text2);
|
|
5676
|
+
const digest = await aiwsContentDigest(Object.entries(files).map(([file, text2]) => [file, encoder.encode(text2)]));
|
|
5677
|
+
const manifest = {
|
|
5678
|
+
manifest_version: AIWS_MANIFEST_VERSION,
|
|
5679
|
+
purpose: "aiws",
|
|
5680
|
+
host_api: { declarative: 1 },
|
|
5681
|
+
controls: [{ ident: `${ownerDid}#${name}`, name, kind: "declarative", version: 1, digest, dir: `controls/${name}/`, title: name }],
|
|
5682
|
+
workspace_templates: []
|
|
5683
|
+
};
|
|
5684
|
+
await host2.writeTextFile(join(dir, AIWS_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
5685
|
+
`);
|
|
5686
|
+
}
|
|
5687
|
+
async function buildCommand$1(ctx, input, docker, now) {
|
|
5630
5688
|
const metaInput = optionalInputString(input, "meta_dir") ?? "./dapp_meta";
|
|
5631
5689
|
const metaDir = await realDirectory(resolveFromCwd$1(ctx, metaInput), "dapp_meta");
|
|
5632
5690
|
const appMeta = parseAppMeta(await readJson(join(metaDir, "app.json"), "app.json"));
|
|
@@ -11098,7 +11156,12 @@ async function aiws(ctx, method, params) {
|
|
|
11098
11156
|
const error = expectObject$4(result.error, `${method}.error`);
|
|
11099
11157
|
const code = typeof error.code === "string" ? error.code : "AIWS_ERROR";
|
|
11100
11158
|
const detail = typeof error.detail === "string" ? error.detail : code;
|
|
11101
|
-
|
|
11159
|
+
const details = {};
|
|
11160
|
+
if (typeof error.sub_code === "string")
|
|
11161
|
+
details.sub_code = error.sub_code;
|
|
11162
|
+
if (error.data !== void 0)
|
|
11163
|
+
details.data = error.data;
|
|
11164
|
+
throw new ToolError(code, `${code}${typeof error.sub_code === "string" ? `/${error.sub_code}` : ""}: ${detail}`, void 0, false, details);
|
|
11102
11165
|
}
|
|
11103
11166
|
return result;
|
|
11104
11167
|
}
|
|
@@ -11471,11 +11534,326 @@ const exportCommand = {
|
|
|
11471
11534
|
return { ok: true, format: result.format, kind: result.kind, path: out };
|
|
11472
11535
|
}
|
|
11473
11536
|
};
|
|
11537
|
+
async function sessionToken(ctx) {
|
|
11538
|
+
const session = ctx.session;
|
|
11539
|
+
if (!session)
|
|
11540
|
+
throw new ToolError("AUTH_REQUIRED", "authenticated session is required", EXIT_AUTH);
|
|
11541
|
+
return (await session.ensureValid()).token;
|
|
11542
|
+
}
|
|
11543
|
+
async function openHome(ctx) {
|
|
11544
|
+
const session = ctx.session;
|
|
11545
|
+
if (!session)
|
|
11546
|
+
throw new ToolError("AUTH_REQUIRED", "authenticated session is required", EXIT_AUTH);
|
|
11547
|
+
const nfsp = new NfspClient({ baseUrl: gatewayOrigin$2(ctx.connection.endpoint), sessionToken: async () => (await session.ensureValid()).token });
|
|
11548
|
+
await nfsp.hello(["cli"]);
|
|
11549
|
+
return nfsp;
|
|
11550
|
+
}
|
|
11551
|
+
function extRpc(ctx) {
|
|
11552
|
+
return {
|
|
11553
|
+
extBuild: (workspace_id, params) => aiws(ctx, "ext.build", { workspace_id, ...params }),
|
|
11554
|
+
extExport: (workspace_id, entity_ids, purpose = "package") => aiws(ctx, "ext.export", { workspace_id, entity_ids, purpose })
|
|
11555
|
+
};
|
|
11556
|
+
}
|
|
11557
|
+
function assetFetcher(ctx) {
|
|
11558
|
+
return async (workspaceId, objId) => {
|
|
11559
|
+
const response = await fetch(`${gatewayOrigin$2(ctx.connection.endpoint)}/kapi/aiworkspace/asset/${encodeURIComponent(workspaceId)}/${encodeURIComponent(objId)}`, {
|
|
11560
|
+
headers: { Authorization: `Bearer ${await sessionToken(ctx)}` },
|
|
11561
|
+
signal: ctx.signal
|
|
11562
|
+
});
|
|
11563
|
+
if (!response.ok)
|
|
11564
|
+
throw new ToolError("AIWS_ASSET_UNAVAILABLE", `asset ${objId}: HTTP ${response.status}`);
|
|
11565
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
11566
|
+
};
|
|
11567
|
+
}
|
|
11568
|
+
async function sourceErrors(run) {
|
|
11569
|
+
try {
|
|
11570
|
+
return await run();
|
|
11571
|
+
} catch (error) {
|
|
11572
|
+
if (error instanceof AiwsSourceError)
|
|
11573
|
+
throw new ToolError("BUILD_FAILED", `BUILD_FAILED: ${error.message}`, void 0, false, { data: { diagnostics: error.diagnostics } });
|
|
11574
|
+
throw error;
|
|
11575
|
+
}
|
|
11576
|
+
}
|
|
11577
|
+
function list(input, key) {
|
|
11578
|
+
return requiredInputString(input, key).split(",").map((s) => s.trim()).filter(Boolean);
|
|
11579
|
+
}
|
|
11580
|
+
const LOCAL_STATE = ".aiws-build.json";
|
|
11581
|
+
async function isLocalDir(path) {
|
|
11582
|
+
try {
|
|
11583
|
+
return (await getHost().stat(path)).isDirectory;
|
|
11584
|
+
} catch {
|
|
11585
|
+
return false;
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
async function localFiles(dir) {
|
|
11589
|
+
const host2 = getHost();
|
|
11590
|
+
const files = [];
|
|
11591
|
+
const problems = [];
|
|
11592
|
+
let total = 0;
|
|
11593
|
+
for (const entry of await host2.readDir(dir)) {
|
|
11594
|
+
if (entry.name.startsWith("."))
|
|
11595
|
+
continue;
|
|
11596
|
+
if (!entry.isFile) {
|
|
11597
|
+
problems.push({ file: entry.name, severity: "error", message: "a control source folder is flat: move files out of sub-folders" });
|
|
11598
|
+
continue;
|
|
11599
|
+
}
|
|
11600
|
+
const bytes = await host2.readFile(host2.path.join(dir, entry.name));
|
|
11601
|
+
total += bytes.length;
|
|
11602
|
+
files.push({ name: entry.name, base64: btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join("")) });
|
|
11603
|
+
}
|
|
11604
|
+
if (files.length === 0)
|
|
11605
|
+
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
|
+
if (problems.length > 0)
|
|
11609
|
+
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.localeCompare(b.name));
|
|
11611
|
+
}
|
|
11612
|
+
async function localState(dir) {
|
|
11613
|
+
try {
|
|
11614
|
+
return JSON.parse(await getHost().readTextFile(getHost().path.join(dir, LOCAL_STATE)));
|
|
11615
|
+
} catch {
|
|
11616
|
+
return null;
|
|
11617
|
+
}
|
|
11618
|
+
}
|
|
11619
|
+
async function buildLocal(ctx, ws, dir, opts) {
|
|
11620
|
+
const state = await localState(dir);
|
|
11621
|
+
const result = await aiws(ctx, "ext.build", {
|
|
11622
|
+
workspace_id: ws,
|
|
11623
|
+
files: await localFiles(dir),
|
|
11624
|
+
...opts.folderId ? { folder_id: opts.folderId } : {},
|
|
11625
|
+
...state && state.workspace_id === ws ? { expect: { def_rev: state.def_rev, dir_rev: state.dir_rev } } : {},
|
|
11626
|
+
...opts.force ? { force: true } : {},
|
|
11627
|
+
...opts.dryRun ? { dry_run: true } : {}
|
|
11628
|
+
});
|
|
11629
|
+
if (!opts.dryRun && typeof result.def_entity_id === "string" && typeof result.def_rev === "number") {
|
|
11630
|
+
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)}
|
|
11631
|
+
`);
|
|
11632
|
+
}
|
|
11633
|
+
return result;
|
|
11634
|
+
}
|
|
11635
|
+
const buildCommand = {
|
|
11636
|
+
verb: "build",
|
|
11637
|
+
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: "The folder is a local folder (when it exists here; its files are sent inline, ≤ 4 MiB) or a Zone path (e.g. /home/alice/AI Workspace/Demo/controls/kpi-card; --zone-path forces it), flat. 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.",
|
|
11639
|
+
positionals: [{ name: "dir", description: "Local folder, or Zone path of the control source folder", required: true }],
|
|
11640
|
+
options: [
|
|
11641
|
+
WORKSPACE_OPTION,
|
|
11642
|
+
{ name: "folder", property: "folder_id", description: "Data folder for the definition (default: data/extensions)", type: "string" },
|
|
11643
|
+
{ name: "watch", property: "watch", description: "Rebuild whenever the folder changes", type: "boolean" },
|
|
11644
|
+
{ name: "dry-run", property: "dry_run", description: "Check only: diagnostics, nothing committed", type: "boolean" },
|
|
11645
|
+
{ 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" }
|
|
11647
|
+
],
|
|
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"]),
|
|
11649
|
+
outputSchema: OBJECT_OUTPUT,
|
|
11650
|
+
resultSchemaVersion: 1,
|
|
11651
|
+
access: { mode: "fixed", level: "write" },
|
|
11652
|
+
asyncMode: "sync",
|
|
11653
|
+
requiresSession: true,
|
|
11654
|
+
examples: ['buckyos aiws build "/home/alice/AI Workspace/Demo/controls/kpi-card" --workspace ws_abc', 'buckyos aiws build "/home/alice/AI Workspace/Demo/controls/kpi-card" --workspace ws_abc --watch'],
|
|
11655
|
+
handler: async (ctx, input) => {
|
|
11656
|
+
const ws = requiredInputString(input, "workspace_id");
|
|
11657
|
+
const dir = requiredInputString(input, "dir");
|
|
11658
|
+
const opts = { folderId: inputString(input, "folder_id"), force: input.force === true, dryRun: input.dry_run === true };
|
|
11659
|
+
if (input.zone_path !== true && await isLocalDir(getHost().path.resolve(ctx.cwd, dir))) {
|
|
11660
|
+
const local = getHost().path.resolve(ctx.cwd, dir);
|
|
11661
|
+
if (input.watch === true)
|
|
11662
|
+
throw new UsageError("AIWS_USAGE", "--watch follows a Zone folder; rebuild a local folder yourself");
|
|
11663
|
+
return buildLocal(ctx, ws, local, opts);
|
|
11664
|
+
}
|
|
11665
|
+
const nfsp = await openHome(ctx);
|
|
11666
|
+
if (input.watch !== true)
|
|
11667
|
+
return sourceErrors(() => buildControl(extRpc(ctx), nfsp, ws, dir, opts));
|
|
11668
|
+
let builds = 0;
|
|
11669
|
+
let failures = 0;
|
|
11670
|
+
await watchControl(nfsp, dir, async () => {
|
|
11671
|
+
try {
|
|
11672
|
+
const r = await sourceErrors(() => buildControl(extRpc(ctx), nfsp, ws, dir, opts));
|
|
11673
|
+
builds++;
|
|
11674
|
+
await ctx.io.stderr(`built ${r.ident} v${r.version ?? "?"} (${r.unchanged ? "unchanged" : r.status})
|
|
11675
|
+
`);
|
|
11676
|
+
} catch (error) {
|
|
11677
|
+
failures++;
|
|
11678
|
+
const diagnostics = error instanceof ToolError ? error.details.data?.diagnostics : void 0;
|
|
11679
|
+
await ctx.io.stderr(`build failed: ${error instanceof Error ? error.message : String(error)}
|
|
11680
|
+
${diagnostics ? `${JSON.stringify(diagnostics, null, 2)}
|
|
11681
|
+
` : ""}`);
|
|
11682
|
+
}
|
|
11683
|
+
}, { signal: ctx.signal });
|
|
11684
|
+
return { ok: true, watched: dir, builds, failures };
|
|
11685
|
+
}
|
|
11686
|
+
};
|
|
11687
|
+
const pullCommand = {
|
|
11688
|
+
verb: "pull",
|
|
11689
|
+
summary: "Write a control definition’s Workspace snapshot into a folder — a Zone path (default) or a local one (--local) — as the base of the next build",
|
|
11690
|
+
positionals: [{ name: "def", description: "Definition (buckyos.block-def) id", required: true }],
|
|
11691
|
+
options: [WORKSPACE_OPTION, { name: "out", property: "out", description: "Folder to write (created)", type: "string", required: true }, { name: "local", property: "local", description: "Write into a local folder instead of the Zone", type: "boolean" }],
|
|
11692
|
+
inputSchema: workspaceInput({ def: { type: "string", minLength: 1 }, out: { type: "string", minLength: 1 }, local: { type: "boolean" } }, ["def", "out"]),
|
|
11693
|
+
outputSchema: OBJECT_OUTPUT,
|
|
11694
|
+
resultSchemaVersion: 1,
|
|
11695
|
+
access: { mode: "fixed", level: "write" },
|
|
11696
|
+
asyncMode: "sync",
|
|
11697
|
+
requiresSession: true,
|
|
11698
|
+
examples: ['buckyos aiws pull def_abc --workspace ws_abc --out "/home/alice/AI Workspace/Demo/controls/kpi-card"'],
|
|
11699
|
+
handler: async (ctx, input) => {
|
|
11700
|
+
if (input.local === true) {
|
|
11701
|
+
const ws = requiredInputString(input, "workspace_id");
|
|
11702
|
+
const host2 = getHost();
|
|
11703
|
+
const out = host2.path.resolve(ctx.cwd, requiredInputString(input, "out"));
|
|
11704
|
+
const r2 = await aiws(ctx, "ext.export", { workspace_id: ws, entity_ids: [requiredInputString(input, "def")], purpose: "pull" });
|
|
11705
|
+
const control = expectObject$4(r2.controls[0], "ext.export.controls[0]");
|
|
11706
|
+
await host2.mkdir(out, { recursive: true });
|
|
11707
|
+
const fetchAsset = assetFetcher(ctx);
|
|
11708
|
+
const files = [];
|
|
11709
|
+
for (const f of control.files) {
|
|
11710
|
+
const target = host2.path.join(out, f.name);
|
|
11711
|
+
if (f.text !== void 0)
|
|
11712
|
+
await host2.writeTextFile(target, f.text);
|
|
11713
|
+
else
|
|
11714
|
+
await host2.writeFile(target, await fetchAsset(ws, String(f.obj_id)));
|
|
11715
|
+
files.push(f.name);
|
|
11716
|
+
}
|
|
11717
|
+
await host2.writeTextFile(host2.path.join(out, LOCAL_STATE), `${JSON.stringify({ workspace_id: ws, def_entity_id: control.entity_id, def_rev: control.def_rev, dir_rev: control.dir_rev, ident: control.ident, at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
11718
|
+
`);
|
|
11719
|
+
return { ok: true, out, files };
|
|
11720
|
+
}
|
|
11721
|
+
const nfsp = await openHome(ctx);
|
|
11722
|
+
const r = await pullControl(extRpc(ctx), nfsp, requiredInputString(input, "workspace_id"), requiredInputString(input, "def"), requiredInputString(input, "out"), assetFetcher(ctx));
|
|
11723
|
+
return { ok: true, out: requiredInputString(input, "out"), ...r };
|
|
11724
|
+
}
|
|
11725
|
+
};
|
|
11726
|
+
async function pikgVerb(ctx, verb, input) {
|
|
11727
|
+
const command = createPikgModule().commands.find((c) => c.verb === verb);
|
|
11728
|
+
if (!command)
|
|
11729
|
+
throw new ToolError("INTERNAL", `pikg ${verb} is missing`);
|
|
11730
|
+
return expectObject$4(await command.handler(ctx, input), `pikg ${verb}`);
|
|
11731
|
+
}
|
|
11732
|
+
async function uploadPackage(ctx, path) {
|
|
11733
|
+
const bucket_id = `user/aiworkspace/${ctx.principal.id}/exports`;
|
|
11734
|
+
const key = `aiws/${Date.now()}-${getHost().path.basename(path)}`;
|
|
11735
|
+
const bytes = await getHost().readFile(path);
|
|
11736
|
+
if (bytes.byteLength > 32 * 1024 * 1024)
|
|
11737
|
+
throw new UsageError("FILE_TOO_LARGE", "a control package installed from the CLI is limited to 32 MiB");
|
|
11738
|
+
const url = `${gatewayOrigin$2(ctx.connection.endpoint)}/bucket/v1/${bucket_id}/${key.split("/").map(encodeURIComponent).join("/")}`;
|
|
11739
|
+
const response = await fetch(url, { method: "PUT", headers: { Authorization: `Bearer ${await sessionToken(ctx)}` }, body: new Blob([bytes]), signal: ctx.signal });
|
|
11740
|
+
if (!response.ok)
|
|
11741
|
+
throw new ToolError("AIWS_UPLOAD_FAILED", `upload of ${path}: HTTP ${response.status} ${await response.text().catch(() => "")}`);
|
|
11742
|
+
return { bucket_id, key };
|
|
11743
|
+
}
|
|
11744
|
+
const extExportCommand = {
|
|
11745
|
+
verb: "ext-export",
|
|
11746
|
+
summary: "Export Workspace controls as a bundle in a local folder; optionally pack it as an aiws PIKG and install it into your library",
|
|
11747
|
+
description: "Writes <out>/aiws/ (aiws-manifest.json + controls/<dir>/). --pikg (or --install) also runs pikg init/build/pack in <out> (kind aiws-controls); --install binds the .pikg into your aiworkspace exports bucket and calls ext.install.",
|
|
11748
|
+
positionals: [{ name: "defs", description: "Definition (or instance) ids, comma-separated", required: true }],
|
|
11749
|
+
options: [
|
|
11750
|
+
WORKSPACE_OPTION,
|
|
11751
|
+
{ name: "out", property: "out", description: "Local folder to write into (created)", type: "string", required: true },
|
|
11752
|
+
{ name: "name", property: "name", description: "Package name (default: the first control’s)", type: "string" },
|
|
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" }
|
|
11757
|
+
],
|
|
11758
|
+
inputSchema: workspaceInput({ defs: { type: "string", minLength: 1 }, out: { type: "string", minLength: 1 }, name: { type: "string" }, version: { type: "string" }, owner: { type: "string" }, pikg: { type: "boolean" }, install: { type: "boolean" } }, ["defs", "out"]),
|
|
11759
|
+
outputSchema: OBJECT_OUTPUT,
|
|
11760
|
+
resultSchemaVersion: 1,
|
|
11761
|
+
access: { mode: "fixed", level: "write" },
|
|
11762
|
+
asyncMode: "sync",
|
|
11763
|
+
requiresSession: true,
|
|
11764
|
+
examples: ["buckyos aiws ext-export def_abc,def_xyz --workspace ws_abc --out ./kpi-pack --install"],
|
|
11765
|
+
handler: async (ctx, input) => {
|
|
11766
|
+
const ws = requiredInputString(input, "workspace_id");
|
|
11767
|
+
const host2 = getHost();
|
|
11768
|
+
const out = host2.path.resolve(ctx.cwd, requiredInputString(input, "out"));
|
|
11769
|
+
const bundle = await exportBundle(extRpc(ctx), ws, list(input, "defs"), assetFetcher(ctx));
|
|
11770
|
+
const root = host2.path.join(out, "aiws");
|
|
11771
|
+
await host2.remove(root, { recursive: true }).catch(() => void 0);
|
|
11772
|
+
for (const [path, bytes] of bundle.files) {
|
|
11773
|
+
const target = host2.path.join(root, path);
|
|
11774
|
+
await host2.mkdir(host2.path.dirname(target), { recursive: true });
|
|
11775
|
+
await host2.writeFile(target, bytes);
|
|
11776
|
+
}
|
|
11777
|
+
const result = { ok: true, bundle: root, controls: bundle.manifest.controls.map((c) => ({ ident: c.ident, kind: c.kind, version: c.version, digest: c.digest })) };
|
|
11778
|
+
if (input.pikg !== true && input.install !== true)
|
|
11779
|
+
return result;
|
|
11780
|
+
const name = inputString(input, "name") ?? bundle.manifest.controls[0]?.name.replace(/[._]/g, "-") ?? "controls";
|
|
11781
|
+
const owner = inputString(input, "owner") ?? `did:bns:${ctx.principal.id}`;
|
|
11782
|
+
const version = inputString(input, "version") ?? "0.1.0";
|
|
11783
|
+
const appJson = host2.path.join(out, "dapp_meta", "app.json");
|
|
11784
|
+
const existing = await host2.readTextFile(appJson).then((t) => JSON.parse(t)).catch(() => null);
|
|
11785
|
+
if (!existing) {
|
|
11786
|
+
await pikgVerb(ctx, "init", { project_dir: out, name, owner, kind: "aiws-controls", source: "./aiws", version });
|
|
11787
|
+
} else if (inputString(input, "version") && existing.version !== version) {
|
|
11788
|
+
await host2.writeTextFile(appJson, `${JSON.stringify({ ...existing, version }, null, 2)}
|
|
11789
|
+
`);
|
|
11790
|
+
const pikgJson = host2.path.join(out, "dapp_meta", "pikg.json");
|
|
11791
|
+
const meta = JSON.parse(await host2.readTextFile(pikgJson));
|
|
11792
|
+
await host2.writeTextFile(pikgJson, `${JSON.stringify({ ...meta, pikg_file: `${existing.name}-${version}.pikg` }, null, 2)}
|
|
11793
|
+
`);
|
|
11794
|
+
}
|
|
11795
|
+
await pikgVerb(ctx, "build", { meta_dir: host2.path.join(out, "dapp_meta") });
|
|
11796
|
+
const packed = await pikgVerb(ctx, "pack", { dist_dir: host2.path.join(out, "dapp_dist") });
|
|
11797
|
+
const pikgPath = String(packed.pikg_path);
|
|
11798
|
+
result.pikg = pikgPath;
|
|
11799
|
+
if (input.install !== true)
|
|
11800
|
+
return result;
|
|
11801
|
+
result.install = await aiws(ctx, "ext.install", { pikg: await uploadPackage(ctx, pikgPath), mode: "dev" });
|
|
11802
|
+
return result;
|
|
11803
|
+
}
|
|
11804
|
+
};
|
|
11805
|
+
const libraryCommand = {
|
|
11806
|
+
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
|
+
inputSchema: { type: "object", properties: { action: { type: "string", enum: ["list", "install", "remove"] }, target: { type: "string" } }, required: ["action"], additionalProperties: false },
|
|
11811
|
+
outputSchema: OBJECT_OUTPUT,
|
|
11812
|
+
resultSchemaVersion: 1,
|
|
11813
|
+
access: { mode: "fixed", level: "write" },
|
|
11814
|
+
asyncMode: "sync",
|
|
11815
|
+
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"],
|
|
11817
|
+
handler: async (ctx, input) => {
|
|
11818
|
+
const action = requiredInputString(input, "action");
|
|
11819
|
+
if (action === "list")
|
|
11820
|
+
return aiws(ctx, "ext.list_library", {});
|
|
11821
|
+
if (action === "remove")
|
|
11822
|
+
return aiws(ctx, "ext.uninstall", { pkg_id: requiredInputString(input, "target") });
|
|
11823
|
+
if (action !== "install")
|
|
11824
|
+
throw new UsageError("AIWS_USAGE", "library: list, install <file.pikg> or remove <pkg_id>");
|
|
11825
|
+
const path = getHost().path.resolve(ctx.cwd, requiredInputString(input, "target"));
|
|
11826
|
+
return aiws(ctx, "ext.install", { pikg: await uploadPackage(ctx, path), mode: "dev" });
|
|
11827
|
+
}
|
|
11828
|
+
};
|
|
11829
|
+
const extImportCommand = {
|
|
11830
|
+
verb: "ext-import",
|
|
11831
|
+
summary: "Bring controls of your library into a Workspace (one Commit): <pkg_id>#<ident>, comma-separated",
|
|
11832
|
+
positionals: [{ name: "items", description: "<pkg_id>#<author_did>#<name>[,…] (ext.list_library names them)", required: true }],
|
|
11833
|
+
options: [WORKSPACE_OPTION, { name: "folder", property: "folder_id", description: "Data folder for the definitions (default: data/extensions)", type: "string" }],
|
|
11834
|
+
inputSchema: workspaceInput({ items: { type: "string", minLength: 1 }, folder_id: { type: "string" } }, ["items"]),
|
|
11835
|
+
outputSchema: OBJECT_OUTPUT,
|
|
11836
|
+
resultSchemaVersion: 1,
|
|
11837
|
+
access: { mode: "fixed", level: "write" },
|
|
11838
|
+
asyncMode: "sync",
|
|
11839
|
+
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 items = list(input, "items").map((item) => {
|
|
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
|
+
});
|
|
11848
|
+
const folder = inputString(input, "folder_id");
|
|
11849
|
+
return aiws(ctx, "ext.import", { workspace_id: requiredInputString(input, "workspace_id"), items, ...folder ? { folder_id: folder } : {} });
|
|
11850
|
+
}
|
|
11851
|
+
};
|
|
11474
11852
|
function createAiwsModule() {
|
|
11475
11853
|
return {
|
|
11476
11854
|
name: "aiws",
|
|
11477
|
-
summary: "AI Workspace: read a workspace, Block-controls (catalogue, import, export), and execute wish runs as their executor (claim, heartbeat, report)",
|
|
11478
|
-
commands: [infoCommand, readCommand, queryCommand, controlsCommand, importCommand, exportCommand, runGetCommand, runClaimCommand, runHeartbeatCommand, runFilesCommand, runToolCommand, runProgramCommand, runReportCommand]
|
|
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]
|
|
11479
11857
|
};
|
|
11480
11858
|
}
|
|
11481
11859
|
const VERSION = TOOL_VERSION;
|