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.
@@ -0,0 +1,499 @@
1
+ const AIWS_COMPONENT_FILE = "component.json";
2
+ const AIWS_MANIFEST_FILE = "aiws-manifest.json";
3
+ const AIWS_BUILD_STATE_FILE = ".aiws-build.json";
4
+ const AIWS_MANIFEST_VERSION = 1;
5
+ const AIWS_HTML_API_VERSION = 2;
6
+ const AIWS_DECLARATIVE_API_VERSION = 1;
7
+ const AIWS_MAX_SOURCE_FILES = 64;
8
+ class AiwsSourceError extends Error {
9
+ constructor(diagnostics) {
10
+ super(diagnostics.filter((d) => d.severity === "error").map((d) => `${d.file ? `${d.file}: ` : ""}${d.message}`).join("; ") || "invalid control source");
11
+ this.name = "AiwsSourceError";
12
+ this.diagnostics = diagnostics;
13
+ }
14
+ }
15
+ const encoder = new TextEncoder();
16
+ const decoder = new TextDecoder();
17
+ function joinPath(dir, name) {
18
+ return `${dir.replace(/\/+$/, "")}/${name}`;
19
+ }
20
+ function folderName(title) {
21
+ const cleaned = title.replace(/[/\\\u0000-\u001f]/g, " ").replace(/^\.+/, "").trim();
22
+ return cleaned.slice(0, 80) || "Workspace";
23
+ }
24
+ function homeControlDir(owner, workspaceTitle, name) {
25
+ return `/home/${owner}/AI Workspace/${folderName(workspaceTitle)}/controls/${name}`;
26
+ }
27
+ function homeExportDir(owner, pkg) {
28
+ return `/home/${owner}/AI Workspace/exports/${pkg}`;
29
+ }
30
+ function sourceStateKey(defEntityId) {
31
+ return `aiws:src:${defEntityId}`;
32
+ }
33
+ async function sha256Hex(bytes) {
34
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
35
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
36
+ }
37
+ async function aiwsContentDigest(files) {
38
+ const pairs = [];
39
+ for (const [name, bytes] of files)
40
+ pairs.push([name, await sha256Hex(bytes)]);
41
+ pairs.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
42
+ return `sha256:${await sha256Hex(encoder.encode(JSON.stringify(pairs)))}`;
43
+ }
44
+ function sourcesKey(files) {
45
+ return JSON.stringify([...files].sort((a, b) => a.name.localeCompare(b.name)).map((f) => [f.name, f.obj_id ?? f.etag ?? ""]));
46
+ }
47
+ const AIWS_MAX_INLINE_BYTES = 4 * 1024 * 1024;
48
+ function base64(bytes) {
49
+ let binary = "";
50
+ for (let i = 0; i < bytes.length; i += 32768)
51
+ binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
52
+ return btoa(binary);
53
+ }
54
+ async function buildFiles(nfsp, files) {
55
+ var _a;
56
+ const out = [];
57
+ let inline = 0;
58
+ for (const f of files) {
59
+ if ((_a = f.obj_id) == null ? void 0 : _a.startsWith("cyfile:")) {
60
+ out.push({ name: f.name, obj_id: f.obj_id });
61
+ continue;
62
+ }
63
+ if (!f.node_id)
64
+ throw new AiwsSourceError([{ file: f.name, severity: "error", message: "the file service did not name this file" }]);
65
+ const bytes = new Uint8Array(await (await nfsp.readFile(f.node_id)).arrayBuffer());
66
+ inline += bytes.length;
67
+ if (inline > AIWS_MAX_INLINE_BYTES)
68
+ throw new AiwsSourceError([{ file: f.name, severity: "error", message: `a control's files are limited to ${AIWS_MAX_INLINE_BYTES} bytes per build` }]);
69
+ out.push({ name: f.name, base64: base64(bytes) });
70
+ }
71
+ return out;
72
+ }
73
+ const COMPONENT_KEYS = ["manifest_version", "ident", "kind", "title", "description", "host_api", "accepts", "allow_no_source", "default_size", "config_schema", "bindings", "entry", "assets", "catalog", "agent", "deps"];
74
+ const SCHEMA_KEYWORDS = ["type", "enum", "default", "required", "title", "description", "minimum", "maximum", "minLength", "maxLength", "properties", "items"];
75
+ const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
76
+ const DID = /^did:[a-z0-9]+:[^/#\s]+$/;
77
+ const BINDING = /^[a-z][a-z0-9_]{0,31}$/;
78
+ function schemaProblems(v, path, out) {
79
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
80
+ out.push(`${path}: a schema is an object`);
81
+ return;
82
+ }
83
+ for (const [k, x] of Object.entries(v)) {
84
+ if (!SCHEMA_KEYWORDS.includes(k)) {
85
+ out.push(`${path}/${k}: keyword ${k} is outside the supported subset`);
86
+ continue;
87
+ }
88
+ if (k === "properties" && x && typeof x === "object")
89
+ for (const [n, s] of Object.entries(x))
90
+ schemaProblems(s, `${path}/properties/${n}`, out);
91
+ if (k === "items")
92
+ schemaProblems(x, `${path}/items`, out);
93
+ }
94
+ }
95
+ function checkComponent(text, fileNames) {
96
+ const out = [];
97
+ const f = AIWS_COMPONENT_FILE;
98
+ const error = (message) => out.push({ file: f, severity: "error", message });
99
+ let c;
100
+ try {
101
+ const parsed = JSON.parse(text);
102
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
103
+ error("must be a JSON object");
104
+ return out;
105
+ }
106
+ c = parsed;
107
+ } catch (e) {
108
+ error(e instanceof Error ? e.message : String(e));
109
+ return out;
110
+ }
111
+ for (const k of Object.keys(c))
112
+ if (!COMPONENT_KEYS.includes(k))
113
+ error(`unknown key ${k}`);
114
+ const mv = c.manifest_version;
115
+ if (typeof mv !== "number" || !Number.isInteger(mv) || mv < 1)
116
+ error("manifest_version (1) required");
117
+ else if (mv > AIWS_MANIFEST_VERSION)
118
+ error(`manifest_version ${mv} is newer than this client reads (${AIWS_MANIFEST_VERSION})`);
119
+ const ident = c.ident;
120
+ if (!ident || typeof ident !== "object")
121
+ error("ident { author_did, name } required");
122
+ else {
123
+ if (typeof ident.author_did !== "string" || !DID.test(ident.author_did))
124
+ error("ident.author_did must be a DID (did:<method>:<id>)");
125
+ if (typeof ident.name !== "string" || !NAME.test(ident.name))
126
+ error("ident.name must be [a-z0-9][a-z0-9._-]{0,63}");
127
+ if (typeof ident.author_did === "string" && typeof ident.name === "string" && encoder.encode(`${ident.author_did}#${ident.name}`).length > 128)
128
+ error("an identity is at most 128 bytes");
129
+ }
130
+ if (c.kind !== "declarative" && c.kind !== "html")
131
+ error("kind must be declarative or html");
132
+ if (typeof c.title !== "string" || !c.title || c.title.length > 256)
133
+ error("title: 1…256 characters");
134
+ if (c.config_schema !== void 0) {
135
+ const p = [];
136
+ schemaProblems(c.config_schema, "config_schema", p);
137
+ p.forEach(error);
138
+ }
139
+ if (c.bindings !== void 0) {
140
+ if (!c.bindings || typeof c.bindings !== "object")
141
+ error("bindings must be an object");
142
+ else
143
+ for (const n of Object.keys(c.bindings))
144
+ if (!BINDING.test(n) || n === "props")
145
+ error(`binding name ${n}: [a-z][a-z0-9_]{0,31}, not props`);
146
+ }
147
+ if (Array.isArray(c.deps) && c.deps.length > 0)
148
+ error("deps must be empty: dependencies between controls come in a later version");
149
+ const has = (n) => typeof n === "string" && fileNames.includes(n);
150
+ const named = /* @__PURE__ */ new Set([f]);
151
+ if (c.kind === "html") {
152
+ const e = c.entry;
153
+ if (!e || typeof e !== "object" || typeof e.html !== "string")
154
+ error("entry must be { html, css?, js? }");
155
+ else
156
+ for (const [k, n] of Object.entries(e)) {
157
+ if (!has(n))
158
+ error(`entry.${k} ${JSON.stringify(n)} is not in the directory`);
159
+ else
160
+ named.add(String(n));
161
+ }
162
+ if (c.host_api !== void 0 && c.host_api !== AIWS_HTML_API_VERSION)
163
+ error(`host_api: the HTML Block host speaks API ${AIWS_HTML_API_VERSION}`);
164
+ } else if (c.kind === "declarative") {
165
+ if (!has(c.entry))
166
+ error(`entry ${JSON.stringify(c.entry)} is not in the directory`);
167
+ else
168
+ named.add(String(c.entry));
169
+ }
170
+ for (const a of Array.isArray(c.assets) ? c.assets : []) {
171
+ if (!has(a))
172
+ error(`asset ${JSON.stringify(a)} is not in the directory`);
173
+ else
174
+ named.add(String(a));
175
+ }
176
+ if (c.agent !== void 0) {
177
+ if (!has(c.agent))
178
+ error(`agent ${JSON.stringify(c.agent)} is not in the directory`);
179
+ else
180
+ named.add(String(c.agent));
181
+ }
182
+ for (const n of fileNames)
183
+ if (!named.has(n))
184
+ out.push({ file: n, severity: "warning", message: "not named by component.json: kept in the source snapshot only" });
185
+ return out;
186
+ }
187
+ function compatManifest(value) {
188
+ if (!value || typeof value !== "object")
189
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "must be a JSON object" }]);
190
+ const m = value;
191
+ const version = m.manifest_version;
192
+ if (typeof version !== "number" || version < 1)
193
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "manifest_version required" }]);
194
+ if (version > AIWS_MANIFEST_VERSION)
195
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: `manifest_version ${version} needs a newer client (this one reads ${AIWS_MANIFEST_VERSION})` }]);
196
+ if (m.purpose !== "aiws")
197
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "purpose must be aiws" }]);
198
+ if (!Array.isArray(m.controls))
199
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "controls must be an array" }]);
200
+ return { manifest_version: version, purpose: "aiws", host_api: m.host_api ?? {}, controls: m.controls, workspace_templates: Array.isArray(m.workspace_templates) ? m.workspace_templates : [] };
201
+ }
202
+ async function verifyBundle(files) {
203
+ var _a, _b;
204
+ const raw = files.get(AIWS_MANIFEST_FILE);
205
+ if (!raw)
206
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "missing" }]);
207
+ let parsed;
208
+ try {
209
+ parsed = JSON.parse(decoder.decode(raw));
210
+ } catch (e) {
211
+ throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: String(e) }]);
212
+ }
213
+ const manifest = compatManifest(parsed);
214
+ const diagnostics = [];
215
+ if (manifest.workspace_templates.length > 0)
216
+ diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: "Workspace templates are not supported yet" });
217
+ if (manifest.controls.length === 0)
218
+ diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: "lists no control" });
219
+ const dirs = /* @__PURE__ */ new Set();
220
+ for (const c of manifest.controls) {
221
+ if (typeof c.dir !== "string" || !/^controls\/[a-z0-9][a-z0-9._-]{0,70}\/$/.test(c.dir) || dirs.has(c.dir)) {
222
+ diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: `control ${c.ident}: bad or repeated dir` });
223
+ continue;
224
+ }
225
+ dirs.add(c.dir);
226
+ const own = [...files].filter(([p]) => p.startsWith(c.dir)).map(([p, b]) => [p.slice(c.dir.length), b]);
227
+ if (own.some(([n]) => n.includes("/")))
228
+ diagnostics.push({ file: c.dir, severity: "error", message: "a control directory is flat" });
229
+ if (await aiwsContentDigest(own) !== c.digest)
230
+ diagnostics.push({ file: c.dir, severity: "error", message: "the files do not match the manifest digest" });
231
+ const component = own.find(([n]) => n === AIWS_COMPONENT_FILE);
232
+ if (!component) {
233
+ diagnostics.push({ file: `${c.dir}${AIWS_COMPONENT_FILE}`, severity: "error", message: "missing" });
234
+ continue;
235
+ }
236
+ for (const d of checkComponent(decoder.decode(component[1]), own.map(([n]) => n)))
237
+ diagnostics.push({ ...d, file: `${c.dir}${d.file}` });
238
+ try {
239
+ const comp = JSON.parse(decoder.decode(component[1]));
240
+ if (`${(_a = comp.ident) == null ? void 0 : _a.author_did}#${(_b = comp.ident) == null ? void 0 : _b.name}` !== c.ident || comp.kind !== c.kind)
241
+ diagnostics.push({ file: c.dir, severity: "error", message: "ident and kind must match its component.json" });
242
+ } catch {
243
+ }
244
+ }
245
+ for (const p of files.keys())
246
+ if (p !== AIWS_MANIFEST_FILE && ![...dirs].some((d) => p.startsWith(d)))
247
+ diagnostics.push({ file: p, severity: "error", message: "belongs to no control of the manifest" });
248
+ return { manifest, diagnostics };
249
+ }
250
+ async function exportBundle(client, workspaceId, entityIds, fetchAsset) {
251
+ const r = await client.extExport(workspaceId, entityIds, "package");
252
+ const files = /* @__PURE__ */ new Map();
253
+ files.set(AIWS_MANIFEST_FILE, encoder.encode(`${JSON.stringify(r.manifest, null, 2)}
254
+ `));
255
+ for (const f of r.files)
256
+ files.set(f.path, f.text !== void 0 ? encoder.encode(f.text) : await fetchAsset(workspaceId, String(f.obj_id)));
257
+ return { manifest: compatManifest(r.manifest), files, controls: r.controls };
258
+ }
259
+ async function child(nfsp, folder, name) {
260
+ const glob = /[*?[\]\\]/.test(name) ? void 0 : name;
261
+ const page = await nfsp.list(typeof folder === "string" ? folder : { ref: folder }, { limit: glob ? 10 : 1e3, ...glob ? { filter: { name_glob: glob } } : {} }, ["base", "ident"]);
262
+ return page.entries.find((e) => e.name === name);
263
+ }
264
+ async function ensureFolder(nfsp, path) {
265
+ const parts = path.split("/").filter(Boolean);
266
+ if (parts.length === 0)
267
+ throw new Error(`not a folder path: ${path}`);
268
+ let ref = (await nfsp.stat(`/${parts[0]}`)).ref;
269
+ for (const name of parts.slice(1)) {
270
+ const hit = await child(nfsp, ref, name);
271
+ if (hit && hit.target.kind !== "dir")
272
+ throw new AiwsSourceError([{ file: name, severity: "error", message: `${name} in ${path} is not a folder` }]);
273
+ ref = hit ? hit.target.ref : (await nfsp.mkdir({ ref }, name)).ref;
274
+ }
275
+ return ref;
276
+ }
277
+ async function findFolder(nfsp, path) {
278
+ const parts = path.split("/").filter(Boolean);
279
+ if (parts.length === 0)
280
+ return null;
281
+ let ref = (await nfsp.stat(`/${parts[0]}`)).ref;
282
+ for (const name of parts.slice(1)) {
283
+ const hit = await child(nfsp, ref, name);
284
+ if (!hit || hit.target.kind !== "dir")
285
+ return null;
286
+ ref = hit.target.ref;
287
+ }
288
+ return ref;
289
+ }
290
+ async function hasControlSource(nfsp, dir) {
291
+ const ref = await findFolder(nfsp, dir);
292
+ return ref !== null && await child(nfsp, ref, AIWS_COMPONENT_FILE) !== void 0;
293
+ }
294
+ async function writeHomeFile(nfsp, dir, name, bytes) {
295
+ const ref = typeof dir === "string" ? await ensureFolder(nfsp, dir) : dir;
296
+ await nfsp.uploadFile(ref, name, bytes, { overwrite: true });
297
+ }
298
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
299
+ async function collectSources(nfsp, dir) {
300
+ var _a, _b, _c;
301
+ const out = [];
302
+ const problems = [];
303
+ let cursor;
304
+ do {
305
+ const page = await nfsp.list(dir, { limit: 500, ...cursor ? { cursor } : {} }, ["base", "ident"]);
306
+ for (const e of page.entries) {
307
+ if (e.name.startsWith("."))
308
+ continue;
309
+ if (e.target.kind !== "file") {
310
+ problems.push({ file: e.name, severity: "error", message: "a control source folder is flat: move files out of sub-folders" });
311
+ continue;
312
+ }
313
+ const ref = e.target.ref;
314
+ const objId = ref.type === "object" ? ref.obj_id : typeof ((_a = e.target.attrs) == null ? void 0 : _a.obj_id) === "string" ? e.target.attrs.obj_id : void 0;
315
+ out.push({
316
+ name: e.name,
317
+ ...objId ? { obj_id: objId } : {},
318
+ ...ref.type === "live" ? { node_id: ref.node_id } : {},
319
+ ...typeof ((_b = e.target.attrs) == null ? void 0 : _b.etag) === "string" ? { etag: e.target.attrs.etag } : {},
320
+ ...typeof ((_c = e.target.attrs) == null ? void 0 : _c.size) === "number" ? { size: e.target.attrs.size } : {}
321
+ });
322
+ }
323
+ cursor = page.truncated ? page.next_cursor : void 0;
324
+ } while (cursor);
325
+ if (out.length === 0)
326
+ problems.push({ file: "", severity: "error", message: `${dir} holds no files` });
327
+ if (out.length > AIWS_MAX_SOURCE_FILES)
328
+ problems.push({ file: "", severity: "error", message: `a control holds at most ${AIWS_MAX_SOURCE_FILES} files` });
329
+ if (problems.some((p) => p.severity === "error"))
330
+ throw new AiwsSourceError(problems);
331
+ return out.sort((a, b) => a.name.localeCompare(b.name));
332
+ }
333
+ async function readBuildState(nfsp, dir) {
334
+ try {
335
+ const entry = await child(nfsp, dir, AIWS_BUILD_STATE_FILE);
336
+ const nodeId = (entry == null ? void 0 : entry.target.ref.type) === "live" ? entry.target.ref.node_id : void 0;
337
+ if (!nodeId)
338
+ return null;
339
+ const response = await nfsp.readFile(nodeId);
340
+ return await response.json();
341
+ } catch {
342
+ return null;
343
+ }
344
+ }
345
+ async function writeBuildState(nfsp, dir, state) {
346
+ await writeHomeFile(nfsp, dir, AIWS_BUILD_STATE_FILE, encoder.encode(`${JSON.stringify(state, null, 2)}
347
+ `));
348
+ }
349
+ async function buildControl(client, nfsp, workspaceId, dir, opts = {}) {
350
+ const files = await collectSources(nfsp, dir);
351
+ const state = await readBuildState(nfsp, dir);
352
+ const base = state && state.workspace_id === workspaceId ? { def_rev: state.def_rev, dir_rev: state.dir_rev } : void 0;
353
+ const result = await client.extBuild(workspaceId, {
354
+ files: await buildFiles(nfsp, files),
355
+ ...opts.folderId ? { folder_id: opts.folderId } : {},
356
+ ...base ? { expect: base } : {},
357
+ ...opts.force ? { force: true } : {},
358
+ ...opts.dryRun ? { dry_run: true } : {}
359
+ });
360
+ if (!opts.dryRun && typeof result.def_entity_id === "string" && typeof result.def_rev === "number") {
361
+ await writeBuildState(nfsp, dir, { workspace_id: workspaceId, def_entity_id: result.def_entity_id, def_rev: result.def_rev, dir_rev: Number(result.dir_rev ?? 0), ident: String(result.ident ?? ""), at: (/* @__PURE__ */ new Date()).toISOString() });
362
+ }
363
+ return { ...result, files };
364
+ }
365
+ async function pullControl(client, nfsp, workspaceId, defEntityId, outDir, fetchAsset) {
366
+ const r = await client.extExport(workspaceId, [defEntityId], "pull");
367
+ const control = r.controls[0];
368
+ const ref = await ensureFolder(nfsp, outDir);
369
+ for (const f of control.files)
370
+ await writeHomeFile(nfsp, ref, f.name, f.text !== void 0 ? encoder.encode(f.text) : await fetchAsset(workspaceId, String(f.obj_id)));
371
+ const state = { workspace_id: workspaceId, def_entity_id: control.entity_id, def_rev: control.def_rev, dir_rev: control.dir_rev, ident: control.ident, at: (/* @__PURE__ */ new Date()).toISOString() };
372
+ await writeBuildState(nfsp, outDir, state);
373
+ return { files: control.files.map((f) => f.name), state };
374
+ }
375
+ async function watchControl(nfsp, dir, onChange, opts = {}) {
376
+ var _a, _b;
377
+ let last = "";
378
+ let timer;
379
+ let running = Promise.resolve();
380
+ const check = () => {
381
+ running = running.then(async () => {
382
+ let files;
383
+ try {
384
+ files = await collectSources(nfsp, dir);
385
+ } catch {
386
+ return;
387
+ }
388
+ const key = sourcesKey(files);
389
+ if (key === last)
390
+ return;
391
+ last = key;
392
+ await onChange(files);
393
+ }).catch(() => void 0);
394
+ };
395
+ check();
396
+ while (!((_a = opts.signal) == null ? void 0 : _a.aborted)) {
397
+ const listing = await nfsp.list(dir, { limit: 1 }, ["base"]);
398
+ const stream = nfsp.watch({ tokens: listing.watch_token ? [listing.watch_token] : [], signal: opts.signal });
399
+ try {
400
+ for await (const _ of stream) {
401
+ if (timer)
402
+ clearTimeout(timer);
403
+ timer = setTimeout(check, opts.debounceMs ?? 500);
404
+ }
405
+ } catch {
406
+ if ((_b = opts.signal) == null ? void 0 : _b.aborted)
407
+ break;
408
+ await sleep(2e3);
409
+ } finally {
410
+ stream.close();
411
+ }
412
+ }
413
+ if (timer)
414
+ clearTimeout(timer);
415
+ await running;
416
+ }
417
+ function controlSkeleton(opts) {
418
+ const title = opts.title ?? opts.name;
419
+ const base = {
420
+ manifest_version: AIWS_MANIFEST_VERSION,
421
+ ident: { author_did: opts.authorDid, name: opts.name },
422
+ kind: opts.kind ?? "declarative",
423
+ title,
424
+ description: `${title}: a control of this Workspace`,
425
+ accepts: ["buckyos.table-source", "buckyos.record"],
426
+ allow_no_source: true,
427
+ default_size: { w: 260, h: 140 },
428
+ config_schema: { type: "object", properties: { label: { type: "string", title: "Label", default: title } } },
429
+ bindings: { data: { accepts: ["buckyos.table-source"], title: "Data", description: "The table this control summarizes" } },
430
+ catalog: { group: "custom", description: `${title}` },
431
+ agent: "AGENT.md"
432
+ };
433
+ const agent = `# ${title}
434
+
435
+ Bind \`data\` to a table. Config: \`label\` (text).
436
+ `;
437
+ if (opts.kind === "html") {
438
+ return {
439
+ [AIWS_COMPONENT_FILE]: `${JSON.stringify({ ...base, host_api: AIWS_HTML_API_VERSION, entry: { html: "view.html", css: "style.css", js: "behavior.js" } }, null, 2)}
440
+ `,
441
+ "view.html": '<div class="card"><div class="label" id="label"></div><div class="value" id="value">—</div></div>\n',
442
+ "style.css": ".card{font:14px system-ui;padding:12px}.label{opacity:.7}.value{font-size:28px;font-weight:600}\n",
443
+ "behavior.js": "document.getElementById('label').textContent = (aiws.context.config || {}).label || ''\nif (aiws.bindings.data) aiws.input('data').rows().then(function (rows) { document.getElementById('value').textContent = String(rows.length) })\n",
444
+ "AGENT.md": agent
445
+ };
446
+ }
447
+ return {
448
+ [AIWS_COMPONENT_FILE]: `${JSON.stringify({ ...base, entry: "declarative.json" }, null, 2)}
449
+ `,
450
+ "declarative.json": `${JSON.stringify({ layout: "stack", items: [{ kind: "metric", label: title, bind: "data.count", format: "number" }, { kind: "text", text: "Rows of the bound table" }] }, null, 2)}
451
+ `,
452
+ "AGENT.md": agent
453
+ };
454
+ }
455
+ async function createControlSource(nfsp, dir, files) {
456
+ const ref = await ensureFolder(nfsp, dir);
457
+ const existing = await nfsp.list({ ref }, { limit: 500 }, ["base"]).catch(() => null);
458
+ if (existing == null ? void 0 : existing.entries.some((e) => e.name === AIWS_COMPONENT_FILE))
459
+ throw new AiwsSourceError([{ file: AIWS_COMPONENT_FILE, severity: "error", message: `${dir} already holds a control` }]);
460
+ for (const [name, text] of Object.entries(files))
461
+ await writeHomeFile(nfsp, ref, name, encoder.encode(text));
462
+ return ref;
463
+ }
464
+ export {
465
+ AIWS_BUILD_STATE_FILE,
466
+ AIWS_COMPONENT_FILE,
467
+ AIWS_DECLARATIVE_API_VERSION,
468
+ AIWS_HTML_API_VERSION,
469
+ AIWS_MANIFEST_FILE,
470
+ AIWS_MANIFEST_VERSION,
471
+ AIWS_MAX_INLINE_BYTES,
472
+ AIWS_MAX_SOURCE_FILES,
473
+ AiwsSourceError,
474
+ aiwsContentDigest,
475
+ buildControl,
476
+ buildFiles,
477
+ checkComponent,
478
+ collectSources,
479
+ compatManifest,
480
+ controlSkeleton,
481
+ createControlSource,
482
+ ensureFolder,
483
+ exportBundle,
484
+ findFolder,
485
+ folderName,
486
+ hasControlSource,
487
+ homeControlDir,
488
+ homeExportDir,
489
+ joinPath,
490
+ pullControl,
491
+ readBuildState,
492
+ sourceStateKey,
493
+ sourcesKey,
494
+ verifyBundle,
495
+ watchControl,
496
+ writeBuildState,
497
+ writeHomeFile
498
+ };
499
+ //# sourceMappingURL=aiws_ext.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiws_ext.mjs","sources":["../src/aiws_ext.ts"],"sourcesContent":["/**\n * AI Workspace extension controls (buckyos src/frame/aiworkspace/README.md “Extension controls”), shared by the\n * Desktop and the CLI: a control's source lives in a (flat) home folder of the Zone file service; `build` lists it\n * over NFSP and hands each file's object to `ext.build`, `pull` writes a Workspace snapshot back, `watch` rebuilds on\n * change; bundles (the AIWS manifest plus `controls/<dir>/<file>`) are what exports, packages and the library carry.\n * Runs in browsers and Deno (fetch, crypto.subtle); no dependencies.\n */\nimport type { AiWorkspaceClient } from './aiworkspace_client'\nimport type { NfspClient, WireRef } from './nfsp'\n\n/** The two service calls these drivers make (an `AiWorkspaceClient`, or a host's own session with the same shape). */\nexport type AiwsExtRpc = Pick<AiWorkspaceClient, 'extBuild' | 'extExport'>\n\nexport const AIWS_COMPONENT_FILE = 'component.json'\nexport const AIWS_MANIFEST_FILE = 'aiws-manifest.json'\n/** A client's own record of the base it last built or pulled (never part of the source snapshot: dot files are not). */\nexport const AIWS_BUILD_STATE_FILE = '.aiws-build.json'\n/** The newest component.json / AIWS manifest version this module reads (older ones stay readable, Q19). */\nexport const AIWS_MANIFEST_VERSION = 1\n/** The HTML Block host API (`window.aiws` v2) and the declarative interpreter version. */\nexport const AIWS_HTML_API_VERSION = 2\nexport const AIWS_DECLARATIVE_API_VERSION = 1\nexport const AIWS_MAX_SOURCE_FILES = 64\n\n/**\n * One file of a source folder. `obj_id` is set when the file service holds it as a named-store FileObject (`cyfile:`):\n * the service then reads it by reference. Files of a native home folder have none (their `obj_id`, when the file\n * service reports one, is a `sha256:` content hash the service cannot fetch): their bytes travel with the build.\n */\nexport interface AiwsSourceFile { name: string; obj_id?: string; node_id?: string; etag?: string; size?: number }\nexport interface AiwsDiagnostic {\n file: string\n line?: number\n col?: number\n severity: 'error' | 'warning'\n message: string\n /** Cells whose contract a rebuild would break. */\n instances?: { entity_id: string; title?: string | null }[]\n}\nexport interface AiwsBuildState { workspace_id: string; def_entity_id: string; def_rev: number; dir_rev: number; ident?: string; at?: string }\nexport interface AiwsManifestControl { ident: string; name: string; kind: string; version: number; digest: string; dir: string; title?: string; description?: string; catalog?: Record<string, unknown> }\nexport interface AiwsManifest { manifest_version: number; purpose: 'aiws'; host_api?: Record<string, number>; controls: AiwsManifestControl[]; workspace_templates: unknown[] }\nexport interface AiwsBundle { manifest: AiwsManifest; files: Map<string, Uint8Array> }\n\n/** A source directory that cannot be built as it is (not a service refusal: `diagnostics` say why). */\nexport class AiwsSourceError extends Error {\n readonly diagnostics: AiwsDiagnostic[]\n constructor(diagnostics: AiwsDiagnostic[]) {\n super(diagnostics.filter((d) => d.severity === 'error').map((d) => `${d.file ? `${d.file}: ` : ''}${d.message}`).join('; ') || 'invalid control source')\n this.name = 'AiwsSourceError'\n this.diagnostics = diagnostics\n }\n}\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport function joinPath(dir: string, name: string): string {\n return `${dir.replace(/\\/+$/, '')}/${name}`\n}\n\n/** A Workspace title as one folder name (no `/`, no leading dot). */\nexport function folderName(title: string): string {\n const cleaned = title.replace(/[/\\\\\\u0000-\\u001f]/g, ' ').replace(/^\\.+/, '').trim()\n return cleaned.slice(0, 80) || 'Workspace'\n}\n\n/** The suggested home folder of a control (README: a suggestion; the definition never stores it). */\nexport function homeControlDir(owner: string, workspaceTitle: string, name: string): string {\n return `/home/${owner}/AI Workspace/${folderName(workspaceTitle)}/controls/${name}`\n}\n\n/** Where a client writes an export bundle before packing it. */\nexport function homeExportDir(owner: string, pkg: string): string {\n return `/home/${owner}/AI Workspace/exports/${pkg}`\n}\n\n/** `user_state` key under which each person remembers the home folder of a definition. */\nexport function sourceStateKey(defEntityId: string): string {\n return `aiws:src:${defEntityId}`\n}\n\n// ---- digests (same canonical forms as the service: store/src/ext.rs)\n\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', bytes as unknown as ArrayBuffer)\n return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** `sha256:` over `[[name, sha256 hex of its bytes], …]` sorted by name: a control's content digest. */\nexport async function aiwsContentDigest(files: Iterable<[string, Uint8Array]>): Promise<string> {\n const pairs: [string, string][] = []\n for (const [name, bytes] of files) pairs.push([name, await sha256Hex(bytes)])\n pairs.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))\n return `sha256:${await sha256Hex(encoder.encode(JSON.stringify(pairs)))}`\n}\n\n/** What a set of sources is, for \"nothing changed since the last build\" (names, objects or versions). */\nexport function sourcesKey(files: AiwsSourceFile[]): string {\n return JSON.stringify([...files].sort((a, b) => a.name.localeCompare(b.name)).map((f) => [f.name, f.obj_id ?? f.etag ?? '']))\n}\n\n/** Bytes a single build may carry inline (the service's limit). */\nexport const AIWS_MAX_INLINE_BYTES = 4 * 1024 * 1024\n\nfunction base64(bytes: Uint8Array): string {\n let binary = ''\n for (let i = 0; i < bytes.length; i += 0x8000) binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000))\n return btoa(binary)\n}\n\n/** The `files` of `ext.build`: FileObjects by reference, everything else read over the file service and sent inline. */\nexport async function buildFiles(nfsp: NfspClient, files: AiwsSourceFile[]): Promise<({ name: string; obj_id: string } | { name: string; base64: string })[]> {\n const out: ({ name: string; obj_id: string } | { name: string; base64: string })[] = []\n let inline = 0\n for (const f of files) {\n if (f.obj_id?.startsWith('cyfile:')) { out.push({ name: f.name, obj_id: f.obj_id }); continue }\n if (!f.node_id) throw new AiwsSourceError([{ file: f.name, severity: 'error', message: 'the file service did not name this file' }])\n const bytes = new Uint8Array(await (await nfsp.readFile(f.node_id)).arrayBuffer())\n inline += bytes.length\n if (inline > AIWS_MAX_INLINE_BYTES) throw new AiwsSourceError([{ file: f.name, severity: 'error', message: `a control's files are limited to ${AIWS_MAX_INLINE_BYTES} bytes per build` }])\n out.push({ name: f.name, base64: base64(bytes) })\n }\n return out\n}\n\n// ---- component.json pre-check (the service's composer is authoritative; this catches the usual mistakes early)\n\nconst COMPONENT_KEYS = ['manifest_version', 'ident', 'kind', 'title', 'description', 'host_api', 'accepts', 'allow_no_source', 'default_size', 'config_schema', 'bindings', 'entry', 'assets', 'catalog', 'agent', 'deps']\nconst SCHEMA_KEYWORDS = ['type', 'enum', 'default', 'required', 'title', 'description', 'minimum', 'maximum', 'minLength', 'maxLength', 'properties', 'items']\nconst NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/\nconst DID = /^did:[a-z0-9]+:[^/#\\s]+$/\nconst BINDING = /^[a-z][a-z0-9_]{0,31}$/\n\nfunction schemaProblems(v: unknown, path: string, out: string[]): void {\n if (!v || typeof v !== 'object' || Array.isArray(v)) { out.push(`${path}: a schema is an object`); return }\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) {\n if (!SCHEMA_KEYWORDS.includes(k)) { out.push(`${path}/${k}: keyword ${k} is outside the supported subset`); continue }\n if (k === 'properties' && x && typeof x === 'object') for (const [n, s] of Object.entries(x as Record<string, unknown>)) schemaProblems(s, `${path}/properties/${n}`, out)\n if (k === 'items') schemaProblems(x, `${path}/items`, out)\n }\n}\n\n/** Check a `component.json` text against the files of its directory. Errors make a build fail; warnings do not. */\nexport function checkComponent(text: string, fileNames: string[]): AiwsDiagnostic[] {\n const out: AiwsDiagnostic[] = []\n const f = AIWS_COMPONENT_FILE\n const error = (message: string) => out.push({ file: f, severity: 'error', message })\n let c: Record<string, unknown>\n try {\n const parsed = JSON.parse(text) as unknown\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { error('must be a JSON object'); return out }\n c = parsed as Record<string, unknown>\n } catch (e) {\n error(e instanceof Error ? e.message : String(e))\n return out\n }\n for (const k of Object.keys(c)) if (!COMPONENT_KEYS.includes(k)) error(`unknown key ${k}`)\n const mv = c.manifest_version\n if (typeof mv !== 'number' || !Number.isInteger(mv) || mv < 1) error('manifest_version (1) required')\n else if (mv > AIWS_MANIFEST_VERSION) error(`manifest_version ${mv} is newer than this client reads (${AIWS_MANIFEST_VERSION})`)\n const ident = c.ident as Record<string, unknown> | undefined\n if (!ident || typeof ident !== 'object') error('ident { author_did, name } required')\n else {\n if (typeof ident.author_did !== 'string' || !DID.test(ident.author_did)) error('ident.author_did must be a DID (did:<method>:<id>)')\n if (typeof ident.name !== 'string' || !NAME.test(ident.name)) error('ident.name must be [a-z0-9][a-z0-9._-]{0,63}')\n if (typeof ident.author_did === 'string' && typeof ident.name === 'string' && encoder.encode(`${ident.author_did}#${ident.name}`).length > 128) error('an identity is at most 128 bytes')\n }\n if (c.kind !== 'declarative' && c.kind !== 'html') error('kind must be declarative or html')\n if (typeof c.title !== 'string' || !c.title || c.title.length > 256) error('title: 1…256 characters')\n if (c.config_schema !== undefined) { const p: string[] = []; schemaProblems(c.config_schema, 'config_schema', p); p.forEach(error) }\n if (c.bindings !== undefined) {\n if (!c.bindings || typeof c.bindings !== 'object') error('bindings must be an object')\n else for (const n of Object.keys(c.bindings as object)) if (!BINDING.test(n) || n === 'props') error(`binding name ${n}: [a-z][a-z0-9_]{0,31}, not props`)\n }\n if (Array.isArray(c.deps) && c.deps.length > 0) error('deps must be empty: dependencies between controls come in a later version')\n const has = (n: unknown) => typeof n === 'string' && fileNames.includes(n)\n const named = new Set([f])\n if (c.kind === 'html') {\n const e = c.entry as Record<string, unknown> | undefined\n if (!e || typeof e !== 'object' || typeof e.html !== 'string') error('entry must be { html, css?, js? }')\n else for (const [k, n] of Object.entries(e)) { if (!has(n)) error(`entry.${k} ${JSON.stringify(n)} is not in the directory`); else named.add(String(n)) }\n if (c.host_api !== undefined && c.host_api !== AIWS_HTML_API_VERSION) error(`host_api: the HTML Block host speaks API ${AIWS_HTML_API_VERSION}`)\n } else if (c.kind === 'declarative') {\n if (!has(c.entry)) error(`entry ${JSON.stringify(c.entry)} is not in the directory`)\n else named.add(String(c.entry))\n }\n for (const a of Array.isArray(c.assets) ? c.assets : []) { if (!has(a)) error(`asset ${JSON.stringify(a)} is not in the directory`); else named.add(String(a)) }\n if (c.agent !== undefined) { if (!has(c.agent)) error(`agent ${JSON.stringify(c.agent)} is not in the directory`); else named.add(String(c.agent)) }\n for (const n of fileNames) if (!named.has(n)) out.push({ file: n, severity: 'warning', message: 'not named by component.json: kept in the source snapshot only' })\n return out\n}\n\n// ---- bundles (exports, the `aiws` sub-package of a PIKG)\n\n/** Read an AIWS manifest of any supported version as the current one (Q19); a newer one is refused. */\nexport function compatManifest(value: unknown): AiwsManifest {\n if (!value || typeof value !== 'object') throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'must be a JSON object' }])\n const m = value as Record<string, unknown>\n const version = m.manifest_version\n if (typeof version !== 'number' || version < 1) throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'manifest_version required' }])\n if (version > AIWS_MANIFEST_VERSION) throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: `manifest_version ${version} needs a newer client (this one reads ${AIWS_MANIFEST_VERSION})` }])\n if (m.purpose !== 'aiws') throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'purpose must be aiws' }])\n if (!Array.isArray(m.controls)) throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'controls must be an array' }])\n return { manifest_version: version, purpose: 'aiws', host_api: (m.host_api as Record<string, number>) ?? {}, controls: m.controls as AiwsManifestControl[], workspace_templates: Array.isArray(m.workspace_templates) ? m.workspace_templates : [] }\n}\n\n/** Verify a bundle the way an installer does: manifest, each control's digest and component.json, no stray files. */\nexport async function verifyBundle(files: Map<string, Uint8Array>): Promise<{ manifest: AiwsManifest; diagnostics: AiwsDiagnostic[] }> {\n const raw = files.get(AIWS_MANIFEST_FILE)\n if (!raw) throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'missing' }])\n let parsed: unknown\n try { parsed = JSON.parse(decoder.decode(raw)) } catch (e) { throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: 'error', message: String(e) }]) }\n const manifest = compatManifest(parsed)\n const diagnostics: AiwsDiagnostic[] = []\n if (manifest.workspace_templates.length > 0) diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'Workspace templates are not supported yet' })\n if (manifest.controls.length === 0) diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: 'error', message: 'lists no control' })\n const dirs = new Set<string>()\n for (const c of manifest.controls) {\n if (typeof c.dir !== 'string' || !/^controls\\/[a-z0-9][a-z0-9._-]{0,70}\\/$/.test(c.dir) || dirs.has(c.dir)) { diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: 'error', message: `control ${c.ident}: bad or repeated dir` }); continue }\n dirs.add(c.dir)\n const own: [string, Uint8Array][] = [...files].filter(([p]) => p.startsWith(c.dir)).map(([p, b]) => [p.slice(c.dir.length), b])\n if (own.some(([n]) => n.includes('/'))) diagnostics.push({ file: c.dir, severity: 'error', message: 'a control directory is flat' })\n if (await aiwsContentDigest(own) !== c.digest) diagnostics.push({ file: c.dir, severity: 'error', message: 'the files do not match the manifest digest' })\n const component = own.find(([n]) => n === AIWS_COMPONENT_FILE)\n if (!component) { diagnostics.push({ file: `${c.dir}${AIWS_COMPONENT_FILE}`, severity: 'error', message: 'missing' }); continue }\n for (const d of checkComponent(decoder.decode(component[1]), own.map(([n]) => n))) diagnostics.push({ ...d, file: `${c.dir}${d.file}` })\n try {\n const comp = JSON.parse(decoder.decode(component[1])) as { ident?: { author_did?: string; name?: string }; kind?: string }\n if (`${comp.ident?.author_did}#${comp.ident?.name}` !== c.ident || comp.kind !== c.kind) diagnostics.push({ file: c.dir, severity: 'error', message: 'ident and kind must match its component.json' })\n } catch { /* reported above */ }\n }\n for (const p of files.keys()) if (p !== AIWS_MANIFEST_FILE && ![...dirs].some((d) => p.startsWith(d))) diagnostics.push({ file: p, severity: 'error', message: 'belongs to no control of the manifest' })\n return { manifest, diagnostics }\n}\n\n/** The bundle of Workspace definitions (`ext.export`): the manifest plus every file's bytes. */\nexport async function exportBundle(client: AiwsExtRpc, workspaceId: string, entityIds: string[], fetchAsset: (workspaceId: string, objId: string) => Promise<Uint8Array>): Promise<AiwsBundle & { controls: Record<string, unknown>[] }> {\n const r = await client.extExport(workspaceId, entityIds, 'package')\n const files = new Map<string, Uint8Array>()\n files.set(AIWS_MANIFEST_FILE, encoder.encode(`${JSON.stringify(r.manifest, null, 2)}\\n`))\n for (const f of r.files) files.set(f.path, f.text !== undefined ? encoder.encode(f.text) : await fetchAsset(workspaceId, String(f.obj_id)))\n return { manifest: compatManifest(r.manifest), files, controls: r.controls }\n}\n\n// ---- the home folder over NFSP\n\n/** The entry `name` of a folder, found with a filtered listing (no request fails for a missing name). */\nasync function child(nfsp: NfspClient, folder: WireRef | string, name: string) {\n const glob = /[*?[\\]\\\\]/.test(name) ? undefined : name\n const page = await nfsp.list(typeof folder === 'string' ? folder : { ref: folder }, { limit: glob ? 10 : 1000, ...(glob ? { filter: { name_glob: glob } } : {}) }, ['base', 'ident'])\n return page.entries.find((e) => e.name === name)\n}\n\n/** Make sure a folder exists, creating each missing level below the realm root (`/home`); answers its ref. Levels\n * are looked up by listing their parent, so a missing one is not a failed request. */\nexport async function ensureFolder(nfsp: NfspClient, path: string): Promise<WireRef> {\n const parts = path.split('/').filter(Boolean)\n if (parts.length === 0) throw new Error(`not a folder path: ${path}`)\n let ref = (await nfsp.stat(`/${parts[0]}`)).ref\n for (const name of parts.slice(1)) {\n const hit = await child(nfsp, ref, name)\n if (hit && hit.target.kind !== 'dir') throw new AiwsSourceError([{ file: name, severity: 'error', message: `${name} in ${path} is not a folder` }])\n ref = hit ? hit.target.ref : (await nfsp.mkdir({ ref }, name)).ref\n }\n return ref\n}\n\n/** A folder's ref, or null when it (or a level above it) does not exist — found without failed requests. */\nexport async function findFolder(nfsp: NfspClient, path: string): Promise<WireRef | null> {\n const parts = path.split('/').filter(Boolean)\n if (parts.length === 0) return null\n let ref = (await nfsp.stat(`/${parts[0]}`)).ref\n for (const name of parts.slice(1)) {\n const hit = await child(nfsp, ref, name)\n if (!hit || hit.target.kind !== 'dir') return null\n ref = hit.target.ref\n }\n return ref\n}\n\n/** Whether a folder holds a control source (its component.json). */\nexport async function hasControlSource(nfsp: NfspClient, dir: string): Promise<boolean> {\n const ref = await findFolder(nfsp, dir)\n return ref !== null && (await child(nfsp, ref, AIWS_COMPONENT_FILE)) !== undefined\n}\n\n/** Write one file into a folder (replacing it). */\nexport async function writeHomeFile(nfsp: NfspClient, dir: string | WireRef, name: string, bytes: Uint8Array): Promise<void> {\n const ref = typeof dir === 'string' ? await ensureFolder(nfsp, dir) : dir\n await nfsp.uploadFile(ref, name, bytes, { overwrite: true })\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\n/** The files of a control's source folder (flat; dot files are the client's own and skipped). */\nexport async function collectSources(nfsp: NfspClient, dir: string): Promise<AiwsSourceFile[]> {\n const out: AiwsSourceFile[] = []\n const problems: AiwsDiagnostic[] = []\n let cursor: string | undefined\n do {\n const page = await nfsp.list(dir, { limit: 500, ...(cursor ? { cursor } : {}) }, ['base', 'ident'])\n for (const e of page.entries) {\n if (e.name.startsWith('.')) continue\n if (e.target.kind !== 'file') { problems.push({ file: e.name, severity: 'error', message: 'a control source folder is flat: move files out of sub-folders' }); continue }\n const ref = e.target.ref\n const objId = ref.type === 'object' ? ref.obj_id : typeof e.target.attrs?.obj_id === 'string' ? e.target.attrs.obj_id : undefined\n out.push({\n name: e.name,\n ...(objId ? { obj_id: objId } : {}),\n ...(ref.type === 'live' ? { node_id: ref.node_id } : {}),\n ...(typeof e.target.attrs?.etag === 'string' ? { etag: e.target.attrs.etag } : {}),\n ...(typeof e.target.attrs?.size === 'number' ? { size: e.target.attrs.size } : {}),\n })\n }\n cursor = page.truncated ? page.next_cursor : undefined\n } while (cursor)\n if (out.length === 0) problems.push({ file: '', severity: 'error', message: `${dir} holds no files` })\n if (out.length > AIWS_MAX_SOURCE_FILES) problems.push({ file: '', severity: 'error', message: `a control holds at most ${AIWS_MAX_SOURCE_FILES} files` })\n if (problems.some((p) => p.severity === 'error')) throw new AiwsSourceError(problems)\n return out.sort((a, b) => a.name.localeCompare(b.name))\n}\n\nexport async function readBuildState(nfsp: NfspClient, dir: string): Promise<AiwsBuildState | null> {\n try {\n const entry = await child(nfsp, dir, AIWS_BUILD_STATE_FILE)\n const nodeId = entry?.target.ref.type === 'live' ? entry.target.ref.node_id : undefined\n if (!nodeId) return null\n const response = await nfsp.readFile(nodeId)\n return (await response.json()) as AiwsBuildState\n } catch {\n return null\n }\n}\n\nexport async function writeBuildState(nfsp: NfspClient, dir: string, state: AiwsBuildState): Promise<void> {\n await writeHomeFile(nfsp, dir, AIWS_BUILD_STATE_FILE, encoder.encode(`${JSON.stringify(state, null, 2)}\\n`))\n}\n\nexport interface AiwsBuildOptions { folderId?: string; force?: boolean; dryRun?: boolean }\n\n/**\n * Build the control in `dir` into a Workspace: collect the files, call `ext.build` with the base recorded in\n * `.aiws-build.json` (when it is this Workspace's), and record the new base. Service refusals throw `AiwsFailure`\n * (`BUILD_FAILED` carries `data.diagnostics`); source problems throw `AiwsSourceError`.\n */\nexport async function buildControl(client: AiwsExtRpc, nfsp: NfspClient, workspaceId: string, dir: string, opts: AiwsBuildOptions = {}): Promise<Record<string, unknown> & { files: AiwsSourceFile[] }> {\n const files = await collectSources(nfsp, dir)\n const state = await readBuildState(nfsp, dir)\n const base = state && state.workspace_id === workspaceId ? { def_rev: state.def_rev, dir_rev: state.dir_rev } : undefined\n const result = await client.extBuild(workspaceId, {\n files: await buildFiles(nfsp, files),\n ...(opts.folderId ? { folder_id: opts.folderId } : {}),\n ...(base ? { expect: base } : {}),\n ...(opts.force ? { force: true } : {}),\n ...(opts.dryRun ? { dry_run: true } : {}),\n })\n if (!opts.dryRun && typeof result.def_entity_id === 'string' && typeof result.def_rev === 'number') {\n await writeBuildState(nfsp, dir, { workspace_id: workspaceId, def_entity_id: result.def_entity_id, def_rev: result.def_rev, dir_rev: Number(result.dir_rev ?? 0), ident: String(result.ident ?? ''), at: new Date().toISOString() })\n }\n return { ...result, files }\n}\n\n/**\n * Write a definition's Workspace snapshot into `outDir` (a definition never built from source gets synthesized\n * files) and record it as the base of the next build.\n */\nexport async function pullControl(client: AiwsExtRpc, nfsp: NfspClient, workspaceId: string, defEntityId: string, outDir: string, fetchAsset: (workspaceId: string, objId: string) => Promise<Uint8Array>): Promise<{ files: string[]; state: AiwsBuildState }> {\n const r = await client.extExport(workspaceId, [defEntityId], 'pull')\n const control = r.controls[0] as { entity_id: string; ident: string; def_rev: number; dir_rev: number; files: { name: string; obj_id?: string; text?: string }[] }\n const ref = await ensureFolder(nfsp, outDir)\n for (const f of control.files) await writeHomeFile(nfsp, ref, f.name, f.text !== undefined ? encoder.encode(f.text) : await fetchAsset(workspaceId, String(f.obj_id)))\n const state: AiwsBuildState = { workspace_id: workspaceId, def_entity_id: control.entity_id, def_rev: control.def_rev, dir_rev: control.dir_rev, ident: control.ident, at: new Date().toISOString() }\n await writeBuildState(nfsp, outDir, state)\n return { files: control.files.map((f) => f.name), state }\n}\n\n/**\n * Rebuild whenever the folder changes: NFSP watch events (lossy: a `resync` lists again), debounced, and only when\n * the files' objects differ from the last build. Returns when `signal` aborts.\n */\nexport async function watchControl(nfsp: NfspClient, dir: string, onChange: (files: AiwsSourceFile[]) => Promise<void>, opts: { debounceMs?: number; signal?: AbortSignal } = {}): Promise<void> {\n let last = ''\n let timer: ReturnType<typeof setTimeout> | undefined\n let running = Promise.resolve()\n const check = () => {\n running = running.then(async () => {\n let files: AiwsSourceFile[]\n try { files = await collectSources(nfsp, dir) } catch { return }\n const key = sourcesKey(files)\n if (key === last) return\n last = key\n await onChange(files)\n }).catch(() => undefined)\n }\n check()\n while (!opts.signal?.aborted) {\n const listing = await nfsp.list(dir, { limit: 1 }, ['base'])\n const stream = nfsp.watch({ tokens: listing.watch_token ? [listing.watch_token] : [], signal: opts.signal })\n try {\n for await (const _ of stream) {\n if (timer) clearTimeout(timer)\n timer = setTimeout(check, opts.debounceMs ?? 500)\n }\n } catch {\n if (opts.signal?.aborted) break\n await sleep(2_000)\n } finally {\n stream.close()\n }\n }\n if (timer) clearTimeout(timer)\n await running\n}\n\n// ---- skeletons\n\nexport interface AiwsSkeletonOptions { authorDid: string; name: string; title?: string; kind?: 'declarative' | 'html' }\n\n/** The files of a new control: a minimal working declarative or HTML control with one optional binding. */\nexport function controlSkeleton(opts: AiwsSkeletonOptions): Record<string, string> {\n const title = opts.title ?? opts.name\n const base = {\n manifest_version: AIWS_MANIFEST_VERSION,\n ident: { author_did: opts.authorDid, name: opts.name },\n kind: opts.kind ?? 'declarative',\n title,\n description: `${title}: a control of this Workspace`,\n accepts: ['buckyos.table-source', 'buckyos.record'],\n allow_no_source: true,\n default_size: { w: 260, h: 140 },\n config_schema: { type: 'object', properties: { label: { type: 'string', title: 'Label', default: title } } },\n bindings: { data: { accepts: ['buckyos.table-source'], title: 'Data', description: 'The table this control summarizes' } },\n catalog: { group: 'custom', description: `${title}` },\n agent: 'AGENT.md',\n }\n const agent = `# ${title}\\n\\nBind \\`data\\` to a table. Config: \\`label\\` (text).\\n`\n if (opts.kind === 'html') {\n return {\n [AIWS_COMPONENT_FILE]: `${JSON.stringify({ ...base, host_api: AIWS_HTML_API_VERSION, entry: { html: 'view.html', css: 'style.css', js: 'behavior.js' } }, null, 2)}\\n`,\n 'view.html': '<div class=\"card\"><div class=\"label\" id=\"label\"></div><div class=\"value\" id=\"value\">—</div></div>\\n',\n 'style.css': '.card{font:14px system-ui;padding:12px}.label{opacity:.7}.value{font-size:28px;font-weight:600}\\n',\n 'behavior.js': \"document.getElementById('label').textContent = (aiws.context.config || {}).label || ''\\nif (aiws.bindings.data) aiws.input('data').rows().then(function (rows) { document.getElementById('value').textContent = String(rows.length) })\\n\",\n 'AGENT.md': agent,\n }\n }\n return {\n [AIWS_COMPONENT_FILE]: `${JSON.stringify({ ...base, entry: 'declarative.json' }, null, 2)}\\n`,\n 'declarative.json': `${JSON.stringify({ layout: 'stack', items: [{ kind: 'metric', label: title, bind: 'data.count', format: 'number' }, { kind: 'text', text: 'Rows of the bound table' }] }, null, 2)}\\n`,\n 'AGENT.md': agent,\n }\n}\n\n/** Create a control's source folder from a skeleton (refuses to overwrite a folder that has a component.json). */\nexport async function createControlSource(nfsp: NfspClient, dir: string, files: Record<string, string>): Promise<WireRef> {\n const ref = await ensureFolder(nfsp, dir)\n const existing = await nfsp.list({ ref }, { limit: 500 }, ['base']).catch(() => null)\n if (existing?.entries.some((e) => e.name === AIWS_COMPONENT_FILE)) throw new AiwsSourceError([{ file: AIWS_COMPONENT_FILE, severity: 'error', message: `${dir} already holds a control` }])\n for (const [name, text] of Object.entries(files)) await writeHomeFile(nfsp, ref, name, encoder.encode(text))\n return ref\n}\n"],"names":[],"mappings":"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"}