@agentproto/app-kit 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -1
- package/dist/define-app.d.ts.map +1 -1
- package/dist/emit.d.ts +6 -1
- package/dist/emit.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +417 -12
- package/dist/index.mjs.map +1 -1
- package/dist/load-app.d.ts +23 -1
- package/dist/load-app.d.ts.map +1 -1
- package/dist/manifest-fields.d.ts +30 -0
- package/dist/manifest-fields.d.ts.map +1 -0
- package/dist/pack.d.ts +90 -0
- package/dist/pack.d.ts.map +1 -0
- package/dist/types.d.ts +99 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -12
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { join, relative, isAbsolute, dirname, resolve } from 'path';
|
|
1
|
+
import { join, relative, isAbsolute, dirname, resolve, basename } from 'path';
|
|
2
2
|
import { defineWorkspace } from '@agentproto/workspace';
|
|
3
|
-
import { mkdir, writeFile, copyFile, cp, readFile, readdir } from 'fs/promises';
|
|
3
|
+
import { mkdir, writeFile, copyFile, cp, readFile, mkdtemp, readdir, rm, stat } from 'fs/promises';
|
|
4
4
|
import matter2 from 'gray-matter';
|
|
5
5
|
import { parseAgentManifest, agentFromManifest } from '@agentproto/agent/manifest';
|
|
6
6
|
import { loadWorkflowHandle } from '@agentproto/workflow-loader';
|
|
@@ -10,6 +10,9 @@ import { parseDriverManifest, driverDefinitionFromManifest, driverFromManifest }
|
|
|
10
10
|
import { normalizeToolId } from '@agentproto/driver';
|
|
11
11
|
import { defineCliDriver } from '@agentproto/driver-cli';
|
|
12
12
|
import { defineHttpDriver } from '@agentproto/driver-http';
|
|
13
|
+
import { createHash } from 'crypto';
|
|
14
|
+
import { tmpdir } from 'os';
|
|
15
|
+
import { spawn } from 'child_process';
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @agentproto/app-kit v0.1.0
|
|
@@ -58,7 +61,8 @@ async function emitApp(app, dir) {
|
|
|
58
61
|
...app.ui.description !== void 0 ? { description: app.ui.description } : {},
|
|
59
62
|
...app.ui.tools !== void 0 ? { tools: app.ui.tools } : {},
|
|
60
63
|
...app.ui.port !== void 0 ? { port: app.ui.port } : {},
|
|
61
|
-
...app.ui.csp !== void 0 ? { csp: app.ui.csp } : {}
|
|
64
|
+
...app.ui.csp !== void 0 ? { csp: app.ui.csp } : {},
|
|
65
|
+
...app.ui.build !== void 0 ? { build: app.ui.build } : {}
|
|
62
66
|
};
|
|
63
67
|
}
|
|
64
68
|
let artifactPath;
|
|
@@ -111,7 +115,10 @@ async function emitApp(app, dir) {
|
|
|
111
115
|
agents: agentRefs,
|
|
112
116
|
workflows: workflowRefs2,
|
|
113
117
|
...app.workspace ? { workspace: app.workspace.id } : {},
|
|
114
|
-
...app
|
|
118
|
+
...requiresFrontmatter(app),
|
|
119
|
+
...app.placement !== void 0 && app.placement !== "any" ? { placement: app.placement } : {},
|
|
120
|
+
...app.exposes && (app.exposes.agents.length > 0 || app.exposes.workflows.length > 0) ? { exposes: { agents: app.exposes.agents, workflows: app.exposes.workflows } } : {},
|
|
121
|
+
...app.accepts?.tasks ? { accepts: { tasks: true } } : {},
|
|
115
122
|
...uiFrontmatter !== void 0 ? { ui: uiFrontmatter } : {},
|
|
116
123
|
...artifactFrontmatter !== void 0 ? { artifact: artifactFrontmatter } : {},
|
|
117
124
|
...skillFrontmatter !== void 0 ? { skill: skillFrontmatter } : {},
|
|
@@ -119,6 +126,7 @@ async function emitApp(app, dir) {
|
|
|
119
126
|
...app.dev !== void 0 ? { dev: app.dev } : {},
|
|
120
127
|
...app.data !== void 0 ? { data: app.data } : {},
|
|
121
128
|
...app.externalReadRoots !== void 0 ? { externalReadRoots: app.externalReadRoots } : {},
|
|
129
|
+
...app.boundaries !== void 0 ? { boundaries: app.boundaries } : {},
|
|
122
130
|
...app.category !== void 0 ? { category: app.category } : {}
|
|
123
131
|
};
|
|
124
132
|
await writeFile(appPath, toManifest(appFrontmatter, app.description ?? ""), "utf8");
|
|
@@ -132,6 +140,15 @@ async function emitApp(app, dir) {
|
|
|
132
140
|
...skillPath ? { skillPath } : {}
|
|
133
141
|
};
|
|
134
142
|
}
|
|
143
|
+
function requiresFrontmatter(app) {
|
|
144
|
+
const r = app.requirements;
|
|
145
|
+
if (r && (r.browser || r.fs || r.gpu || r.secrets.length > 0)) {
|
|
146
|
+
return {
|
|
147
|
+
requires: { browser: r.browser, fs: r.fs, gpu: r.gpu, secrets: r.secrets, apps: r.apps }
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return app.requires !== void 0 ? { requires: app.requires } : {};
|
|
151
|
+
}
|
|
135
152
|
function toManifest(handle, body) {
|
|
136
153
|
const data = JSON.parse(JSON.stringify(handle));
|
|
137
154
|
return matter2.stringify(`
|
|
@@ -139,6 +156,86 @@ ${body}
|
|
|
139
156
|
`, data);
|
|
140
157
|
}
|
|
141
158
|
|
|
159
|
+
// src/manifest-fields.ts
|
|
160
|
+
var APP_PLACEMENTS = ["local", "box", "any", "split"];
|
|
161
|
+
function isRecord(v) {
|
|
162
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
163
|
+
}
|
|
164
|
+
function stringList(v, key, fail) {
|
|
165
|
+
if (v === void 0) return [];
|
|
166
|
+
if (!Array.isArray(v) || !v.every((e) => typeof e === "string" && e.trim() !== "")) {
|
|
167
|
+
throw fail(`'${key}' must be an array of non-empty strings.`);
|
|
168
|
+
}
|
|
169
|
+
return [...v];
|
|
170
|
+
}
|
|
171
|
+
function bool(v, key, fail) {
|
|
172
|
+
if (v === void 0) return false;
|
|
173
|
+
if (typeof v !== "boolean") throw fail(`'${key}' must be a boolean.`);
|
|
174
|
+
return v;
|
|
175
|
+
}
|
|
176
|
+
function normalizeManifestFields(input, declared, fail) {
|
|
177
|
+
let placement = "any";
|
|
178
|
+
if (input.placement !== void 0) {
|
|
179
|
+
if (typeof input.placement !== "string" || !APP_PLACEMENTS.includes(input.placement)) {
|
|
180
|
+
throw fail(
|
|
181
|
+
`'placement' must be one of ${APP_PLACEMENTS.map((p) => `"${p}"`).join(", ")}, got ${JSON.stringify(input.placement)}.`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
placement = input.placement;
|
|
185
|
+
}
|
|
186
|
+
let requires;
|
|
187
|
+
let requirements = { browser: false, fs: false, gpu: false, secrets: [], apps: [] };
|
|
188
|
+
const r = input.requires;
|
|
189
|
+
if (r !== void 0) {
|
|
190
|
+
if (Array.isArray(r)) {
|
|
191
|
+
if (!r.every((e) => typeof e === "string")) {
|
|
192
|
+
throw fail("'requires' must be an array of strings or an object { browser, fs, gpu, secrets, apps }.");
|
|
193
|
+
}
|
|
194
|
+
requires = [...r];
|
|
195
|
+
requirements = { ...requirements, apps: requires };
|
|
196
|
+
} else if (isRecord(r)) {
|
|
197
|
+
const apps = stringList(r.apps, "requires.apps", fail);
|
|
198
|
+
requirements = {
|
|
199
|
+
browser: bool(r.browser, "requires.browser", fail),
|
|
200
|
+
fs: bool(r.fs, "requires.fs", fail),
|
|
201
|
+
gpu: bool(r.gpu, "requires.gpu", fail),
|
|
202
|
+
secrets: stringList(r.secrets, "requires.secrets", fail),
|
|
203
|
+
apps
|
|
204
|
+
};
|
|
205
|
+
if (apps.length > 0) requires = apps;
|
|
206
|
+
} else {
|
|
207
|
+
throw fail("'requires' must be an array of strings or an object { browser, fs, gpu, secrets, apps }.");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
let exposes = { agents: [], workflows: [] };
|
|
211
|
+
if (input.exposes !== void 0) {
|
|
212
|
+
if (!isRecord(input.exposes)) throw fail("'exposes' must be an object { agents, workflows }.");
|
|
213
|
+
const agents = stringList(input.exposes.agents, "exposes.agents", fail);
|
|
214
|
+
const workflows = stringList(input.exposes.workflows, "exposes.workflows", fail);
|
|
215
|
+
for (const id of agents) {
|
|
216
|
+
if (!declared.agents.includes(id)) {
|
|
217
|
+
throw fail(
|
|
218
|
+
`'exposes.agents' names '${id}' but the app declares no such agent. Declared: [${declared.agents.join(", ") || "none"}].`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (const id of workflows) {
|
|
223
|
+
if (!declared.workflows.includes(id)) {
|
|
224
|
+
throw fail(
|
|
225
|
+
`'exposes.workflows' names '${id}' but the app declares no such workflow. Declared: [${declared.workflows.join(", ") || "none"}].`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exposes = { agents, workflows };
|
|
230
|
+
}
|
|
231
|
+
let accepts = { tasks: false };
|
|
232
|
+
if (input.accepts !== void 0) {
|
|
233
|
+
if (!isRecord(input.accepts)) throw fail("'accepts' must be an object { tasks }.");
|
|
234
|
+
accepts = { tasks: bool(input.accepts.tasks, "accepts.tasks", fail) };
|
|
235
|
+
}
|
|
236
|
+
return { placement, requires, requirements, exposes, accepts };
|
|
237
|
+
}
|
|
238
|
+
|
|
142
239
|
// src/define-app.ts
|
|
143
240
|
var AppDefinitionError = class extends Error {
|
|
144
241
|
constructor(message) {
|
|
@@ -193,7 +290,23 @@ function defineApp(def) {
|
|
|
193
290
|
const name = def.name;
|
|
194
291
|
const version = def.version ?? (id ? "0.1.0" : void 0);
|
|
195
292
|
const description = def.description;
|
|
196
|
-
const
|
|
293
|
+
const fields = normalizeManifestFields(
|
|
294
|
+
{ placement: def.placement, requires: def.requires, exposes: def.exposes, accepts: def.accepts },
|
|
295
|
+
{ agents: agents.map((e) => e.agent.id), workflows: workflows.map((w) => w.id) },
|
|
296
|
+
(msg) => new AppDefinitionError(msg)
|
|
297
|
+
);
|
|
298
|
+
const requires = fields.requires ? Object.freeze([...fields.requires]) : void 0;
|
|
299
|
+
const requirements = Object.freeze({
|
|
300
|
+
...fields.requirements,
|
|
301
|
+
secrets: Object.freeze([...fields.requirements.secrets]),
|
|
302
|
+
apps: Object.freeze([...fields.requirements.apps])
|
|
303
|
+
});
|
|
304
|
+
const placement = fields.placement;
|
|
305
|
+
const exposes = Object.freeze({
|
|
306
|
+
agents: Object.freeze([...fields.exposes.agents]),
|
|
307
|
+
workflows: Object.freeze([...fields.exposes.workflows])
|
|
308
|
+
});
|
|
309
|
+
const accepts = Object.freeze({ ...fields.accepts });
|
|
197
310
|
const ui = def.ui ? Object.freeze({ ...def.ui }) : void 0;
|
|
198
311
|
const artifact = def.artifact ? Object.freeze({ ...def.artifact }) : void 0;
|
|
199
312
|
const skill = def.skill ? Object.freeze({ ...def.skill }) : void 0;
|
|
@@ -201,6 +314,7 @@ function defineApp(def) {
|
|
|
201
314
|
const dev = def.dev ? Object.freeze({ launch: Object.freeze(def.dev.launch.map((l) => Object.freeze({ ...l }))) }) : void 0;
|
|
202
315
|
const externalReadRoots = def.externalReadRoots ? Object.freeze([...def.externalReadRoots]) : void 0;
|
|
203
316
|
const data = def.data ? Object.freeze({ ...def.data }) : void 0;
|
|
317
|
+
const boundaries = def.boundaries ? Object.freeze({ ...def.boundaries }) : void 0;
|
|
204
318
|
const category = def.category;
|
|
205
319
|
validateAttachment(agents, workflows);
|
|
206
320
|
validateUniqueIds(tools, "tool");
|
|
@@ -222,6 +336,10 @@ function defineApp(def) {
|
|
|
222
336
|
...version !== void 0 ? { version } : {},
|
|
223
337
|
...description !== void 0 ? { description } : {},
|
|
224
338
|
...requires !== void 0 ? { requires } : {},
|
|
339
|
+
requirements,
|
|
340
|
+
placement,
|
|
341
|
+
exposes,
|
|
342
|
+
accepts,
|
|
225
343
|
...ui !== void 0 ? { ui } : {},
|
|
226
344
|
...artifact !== void 0 ? { artifact } : {},
|
|
227
345
|
...skill !== void 0 ? { skill } : {},
|
|
@@ -229,6 +347,7 @@ function defineApp(def) {
|
|
|
229
347
|
...dev !== void 0 ? { dev } : {},
|
|
230
348
|
...data !== void 0 ? { data } : {},
|
|
231
349
|
...externalReadRoots !== void 0 ? { externalReadRoots } : {},
|
|
350
|
+
...boundaries !== void 0 ? { boundaries } : {},
|
|
232
351
|
...category !== void 0 ? { category } : {},
|
|
233
352
|
async toMastraAgents(opts, only) {
|
|
234
353
|
const targets = only ? selectAgents(frozenAgents, only) : frozenAgents;
|
|
@@ -260,6 +379,10 @@ function defineApp(def) {
|
|
|
260
379
|
...version !== void 0 ? { version } : {},
|
|
261
380
|
...description !== void 0 ? { description } : {},
|
|
262
381
|
...requires !== void 0 ? { requires } : {},
|
|
382
|
+
requirements,
|
|
383
|
+
placement,
|
|
384
|
+
exposes,
|
|
385
|
+
accepts,
|
|
263
386
|
...ui !== void 0 ? { ui } : {},
|
|
264
387
|
...artifact !== void 0 ? { artifact } : {},
|
|
265
388
|
...skill !== void 0 ? { skill } : {},
|
|
@@ -267,6 +390,7 @@ function defineApp(def) {
|
|
|
267
390
|
...dev !== void 0 ? { dev } : {},
|
|
268
391
|
...data !== void 0 ? { data } : {},
|
|
269
392
|
...externalReadRoots !== void 0 ? { externalReadRoots } : {},
|
|
393
|
+
...boundaries !== void 0 ? { boundaries } : {},
|
|
270
394
|
...category !== void 0 ? { category } : {}
|
|
271
395
|
},
|
|
272
396
|
dir
|
|
@@ -527,6 +651,54 @@ async function resolveAppUIRoot(appDir) {
|
|
|
527
651
|
}
|
|
528
652
|
return dirname(resolveRef(appDir, path));
|
|
529
653
|
}
|
|
654
|
+
function parseUiBuildFrontmatter(raw, appMdPath) {
|
|
655
|
+
if (raw === void 0) return void 0;
|
|
656
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
657
|
+
throw new AppLoadError(`'${appMdPath}': frontmatter 'ui.build' must be an object.`);
|
|
658
|
+
}
|
|
659
|
+
const command = raw.command;
|
|
660
|
+
if (typeof command !== "string" || command.trim() === "") {
|
|
661
|
+
throw new AppLoadError(`'${appMdPath}': frontmatter 'ui.build.command' must be a non-empty string.`);
|
|
662
|
+
}
|
|
663
|
+
const cwd = raw.cwd;
|
|
664
|
+
if (cwd !== void 0 && (typeof cwd !== "string" || cwd.trim() === "")) {
|
|
665
|
+
throw new AppLoadError(`'${appMdPath}': frontmatter 'ui.build.cwd' must be a non-empty string.`);
|
|
666
|
+
}
|
|
667
|
+
const sources = raw.sources;
|
|
668
|
+
if (sources !== void 0 && (!Array.isArray(sources) || !sources.every((s) => typeof s === "string" && s.trim() !== ""))) {
|
|
669
|
+
throw new AppLoadError(
|
|
670
|
+
`'${appMdPath}': frontmatter 'ui.build.sources' must be an array of non-empty strings.`
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
command,
|
|
675
|
+
...cwd !== void 0 ? { cwd } : {},
|
|
676
|
+
...sources !== void 0 ? { sources } : {}
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
async function peekAppUi(appDir) {
|
|
680
|
+
const appMdPath = join(appDir, ".agentproto", "APP.md");
|
|
681
|
+
let source;
|
|
682
|
+
try {
|
|
683
|
+
source = await readFile(appMdPath, "utf8");
|
|
684
|
+
} catch {
|
|
685
|
+
return void 0;
|
|
686
|
+
}
|
|
687
|
+
const data = matter2(source).data;
|
|
688
|
+
const ui = data.ui;
|
|
689
|
+
if (ui === void 0) return void 0;
|
|
690
|
+
if (typeof ui !== "object" || ui === null || Array.isArray(ui)) {
|
|
691
|
+
throw new AppLoadError(`'${appMdPath}': frontmatter 'ui' must be an object.`);
|
|
692
|
+
}
|
|
693
|
+
const path = ui.path;
|
|
694
|
+
if (typeof path !== "string" || path.trim() === "") {
|
|
695
|
+
throw new AppLoadError(
|
|
696
|
+
`'${appMdPath}': frontmatter 'ui.path' must be a non-empty string.`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
const build = parseUiBuildFrontmatter(ui.build, appMdPath);
|
|
700
|
+
return { path: resolveRef(appDir, path), ...build ? { build } : {} };
|
|
701
|
+
}
|
|
530
702
|
function isRefArray(v) {
|
|
531
703
|
return Array.isArray(v) && v.every(
|
|
532
704
|
(e) => typeof e === "object" && e !== null && typeof e.id === "string" && typeof e.path === "string"
|
|
@@ -546,11 +718,11 @@ function parseAppFrontmatter(data, appPath) {
|
|
|
546
718
|
`'${appPath}': frontmatter 'workflows' must be an array of { id, path }.`
|
|
547
719
|
);
|
|
548
720
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
721
|
+
normalizeManifestFields(
|
|
722
|
+
data,
|
|
723
|
+
{ agents: data.agents.map((a) => a.id), workflows: data.workflows.map((w) => w.id) },
|
|
724
|
+
(msg) => new AppLoadError(`'${appPath}': frontmatter ${msg}`)
|
|
725
|
+
);
|
|
554
726
|
if (data.data !== void 0) {
|
|
555
727
|
const d = data.data;
|
|
556
728
|
if (typeof d !== "object" || d === null || d.dir !== void 0 && (typeof d.dir !== "string" || d.dir.trim() === "")) {
|
|
@@ -566,6 +738,14 @@ function parseAppFrontmatter(data, appPath) {
|
|
|
566
738
|
);
|
|
567
739
|
}
|
|
568
740
|
}
|
|
741
|
+
if (data.boundaries !== void 0) {
|
|
742
|
+
const b = data.boundaries;
|
|
743
|
+
if (typeof b !== "object" || b === null || Array.isArray(b) || b.enforce !== void 0 && b.enforce !== "required" && b.enforce !== "best-effort") {
|
|
744
|
+
throw new AppLoadError(
|
|
745
|
+
`'${appPath}': frontmatter 'boundaries' must be an object whose optional 'enforce' is "required" or "best-effort".`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
569
749
|
if (data.category !== void 0 && (typeof data.category !== "string" || data.category.trim() === "")) {
|
|
570
750
|
throw new AppLoadError(`'${appPath}': frontmatter 'category' must be a non-empty string.`);
|
|
571
751
|
}
|
|
@@ -654,13 +834,15 @@ async function loadAppHandle(dir) {
|
|
|
654
834
|
`ui: cannot read '${uiPath}': ${err instanceof Error ? err.message : String(err)}`
|
|
655
835
|
);
|
|
656
836
|
}
|
|
837
|
+
const build = parseUiBuildFrontmatter(fm.ui.build, appPath);
|
|
657
838
|
ui = {
|
|
658
839
|
html,
|
|
659
840
|
...fm.ui.title !== void 0 ? { title: fm.ui.title } : {},
|
|
660
841
|
...fm.ui.description !== void 0 ? { description: fm.ui.description } : {},
|
|
661
842
|
...fm.ui.tools !== void 0 ? { tools: fm.ui.tools } : {},
|
|
662
843
|
...fm.ui.port !== void 0 ? { port: fm.ui.port } : {},
|
|
663
|
-
...fm.ui.csp !== void 0 ? { csp: fm.ui.csp } : {}
|
|
844
|
+
...fm.ui.csp !== void 0 ? { csp: fm.ui.csp } : {},
|
|
845
|
+
...build !== void 0 ? { build } : {}
|
|
664
846
|
};
|
|
665
847
|
}
|
|
666
848
|
return defineApp({
|
|
@@ -674,6 +856,9 @@ async function loadAppHandle(dir) {
|
|
|
674
856
|
...fm.version !== void 0 ? { version: fm.version } : {},
|
|
675
857
|
...fm.description !== void 0 ? { description: fm.description } : {},
|
|
676
858
|
...fm.requires !== void 0 ? { requires: fm.requires } : {},
|
|
859
|
+
...fm.placement !== void 0 ? { placement: fm.placement } : {},
|
|
860
|
+
...fm.exposes !== void 0 ? { exposes: fm.exposes } : {},
|
|
861
|
+
...fm.accepts !== void 0 ? { accepts: fm.accepts } : {},
|
|
677
862
|
...ui !== void 0 ? { ui } : {},
|
|
678
863
|
...fm.artifact !== void 0 ? { artifact: { path: resolveRef(dir, fm.artifact.path), ...fm.artifact.title !== void 0 ? { title: fm.artifact.title } : {}, ...fm.artifact.description !== void 0 ? { description: fm.artifact.description } : {} } } : {},
|
|
679
864
|
...fm.skill !== void 0 ? { skill: { path: resolveRef(dir, fm.skill.path), ...fm.skill.title !== void 0 ? { title: fm.skill.title } : {}, ...fm.skill.description !== void 0 ? { description: fm.skill.description } : {} } } : {},
|
|
@@ -681,10 +866,230 @@ async function loadAppHandle(dir) {
|
|
|
681
866
|
...fm.dev !== void 0 ? { dev: fm.dev } : {},
|
|
682
867
|
...fm.data !== void 0 ? { data: fm.data } : {},
|
|
683
868
|
...fm.externalReadRoots !== void 0 ? { externalReadRoots: fm.externalReadRoots } : {},
|
|
869
|
+
...fm.boundaries !== void 0 ? { boundaries: fm.boundaries } : {},
|
|
684
870
|
...fm.category !== void 0 ? { category: fm.category } : {}
|
|
685
871
|
});
|
|
686
872
|
}
|
|
873
|
+
var AGENTAPP_FORMAT = "agentapp/v1";
|
|
874
|
+
var DEFAULT_VERSION = "0.1.0";
|
|
875
|
+
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set(["node_modules", ".git"]);
|
|
876
|
+
var AgentAppPackError = class extends Error {
|
|
877
|
+
code;
|
|
878
|
+
constructor(code, message) {
|
|
879
|
+
super(message);
|
|
880
|
+
this.name = "AgentAppPackError";
|
|
881
|
+
this.code = code;
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
async function pathExists(p) {
|
|
885
|
+
try {
|
|
886
|
+
await stat(p);
|
|
887
|
+
return true;
|
|
888
|
+
} catch {
|
|
889
|
+
return false;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function safeId(id) {
|
|
893
|
+
return String(id || "app").replace(/^@/, "").replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
894
|
+
}
|
|
895
|
+
function isManifest(value) {
|
|
896
|
+
if (typeof value !== "object" || value === null) return false;
|
|
897
|
+
const m = value;
|
|
898
|
+
return typeof m.format === "string" && typeof m.id === "string" && typeof m.version === "string" && Array.isArray(m.agents) && Array.isArray(m.workflows) && Array.isArray(m.files) && typeof m.fileCount === "number" && typeof m.totalSize === "number" && typeof m.sha256 === "string";
|
|
899
|
+
}
|
|
900
|
+
async function collectFiles(root) {
|
|
901
|
+
const files = [];
|
|
902
|
+
async function walk(relDir) {
|
|
903
|
+
const absDir = join(root, relDir);
|
|
904
|
+
for (const entry of await readdir(absDir, { withFileTypes: true })) {
|
|
905
|
+
if (entry.isDirectory() && SKIP_DIR_NAMES.has(entry.name)) continue;
|
|
906
|
+
const rel = relDir ? join(relDir, entry.name) : entry.name;
|
|
907
|
+
const abs = join(absDir, entry.name);
|
|
908
|
+
if (entry.isDirectory()) {
|
|
909
|
+
await walk(rel);
|
|
910
|
+
} else if (entry.isFile()) {
|
|
911
|
+
const st = await stat(abs);
|
|
912
|
+
files.push({ path: rel, size: st.size });
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
await walk("");
|
|
917
|
+
return files;
|
|
918
|
+
}
|
|
919
|
+
async function aggregateSha256(root, files) {
|
|
920
|
+
const hash = createHash("sha256");
|
|
921
|
+
for (const file of files) {
|
|
922
|
+
hash.update(await readFile(join(root, file)));
|
|
923
|
+
}
|
|
924
|
+
return hash.digest("hex");
|
|
925
|
+
}
|
|
926
|
+
function runTar(args, opts) {
|
|
927
|
+
return new Promise((resolvePromise, reject) => {
|
|
928
|
+
const child = spawn("tar", args, { stdio: "ignore", cwd: opts?.cwd });
|
|
929
|
+
child.once("error", reject);
|
|
930
|
+
child.once("exit", (code) => resolvePromise(code ?? 0));
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
function extractIds(value) {
|
|
934
|
+
if (!Array.isArray(value)) return [];
|
|
935
|
+
const out = [];
|
|
936
|
+
for (const entry of value) {
|
|
937
|
+
if (typeof entry === "string") {
|
|
938
|
+
if (entry) out.push(entry);
|
|
939
|
+
} else if (entry && typeof entry === "object") {
|
|
940
|
+
const id = entry.id;
|
|
941
|
+
if (typeof id === "string" && id) out.push(id);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return out;
|
|
945
|
+
}
|
|
946
|
+
function extractPaths(front, key) {
|
|
947
|
+
const value = front[key];
|
|
948
|
+
if (value === void 0 || value === null) return void 0;
|
|
949
|
+
const items = Array.isArray(value) ? value : [value];
|
|
950
|
+
const out = [];
|
|
951
|
+
for (const item of items) {
|
|
952
|
+
if (typeof item === "string") {
|
|
953
|
+
if (item) out.push(basename(item));
|
|
954
|
+
} else if (item && typeof item === "object") {
|
|
955
|
+
const p = item.path;
|
|
956
|
+
if (typeof p === "string" && p) out.push(basename(p));
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return out.length > 0 ? out : void 0;
|
|
960
|
+
}
|
|
961
|
+
function extractMeta(front) {
|
|
962
|
+
const id = typeof front.id === "string" && front.id.length > 0 ? front.id : typeof front.slug === "string" && front.slug.length > 0 ? front.slug : "app";
|
|
963
|
+
const version = typeof front.version === "string" && front.version.length > 0 ? front.version : DEFAULT_VERSION;
|
|
964
|
+
const name = typeof front.name === "string" ? front.name : void 0;
|
|
965
|
+
const description = typeof front.description === "string" ? front.description : void 0;
|
|
966
|
+
const ui = extractPaths(front, "ui");
|
|
967
|
+
return {
|
|
968
|
+
id,
|
|
969
|
+
...name !== void 0 ? { name } : {},
|
|
970
|
+
version,
|
|
971
|
+
...description !== void 0 ? { description } : {},
|
|
972
|
+
agents: extractIds(front.agents),
|
|
973
|
+
workflows: extractIds(front.workflows),
|
|
974
|
+
...ui !== void 0 ? { ui } : {}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async function packApp(input) {
|
|
978
|
+
const appDirAbs = resolve(input.appDir);
|
|
979
|
+
const appMdPath = join(appDirAbs, ".agentproto", "APP.md");
|
|
980
|
+
if (!await pathExists(appMdPath)) {
|
|
981
|
+
throw new AgentAppPackError(
|
|
982
|
+
"not-an-app",
|
|
983
|
+
`${appDirAbs} is not an agentproto app (missing ${appMdPath}).`
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
const raw = await readFile(appMdPath, "utf8");
|
|
987
|
+
const front = matter2(raw).data;
|
|
988
|
+
const meta = extractMeta(front);
|
|
989
|
+
const outAbs = input.out !== void 0 ? resolve(input.out) : resolve(process.cwd(), `${safeId(meta.id)}-${meta.version}.agentapp`);
|
|
990
|
+
await mkdir(dirname(outAbs), { recursive: true });
|
|
991
|
+
const files = (await collectFiles(appDirAbs)).filter((f) => f.path !== "manifest.json").filter((f) => join(appDirAbs, f.path) !== outAbs).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
992
|
+
const fileCount = files.length;
|
|
993
|
+
const totalSize = files.reduce((sum, f) => sum + f.size, 0);
|
|
994
|
+
const sha256 = await aggregateSha256(
|
|
995
|
+
appDirAbs,
|
|
996
|
+
files.map((f) => f.path)
|
|
997
|
+
);
|
|
998
|
+
const manifest = {
|
|
999
|
+
format: AGENTAPP_FORMAT,
|
|
1000
|
+
id: meta.id,
|
|
1001
|
+
...meta.name !== void 0 ? { name: meta.name } : {},
|
|
1002
|
+
version: meta.version,
|
|
1003
|
+
...meta.description !== void 0 ? { description: meta.description } : {},
|
|
1004
|
+
agents: meta.agents,
|
|
1005
|
+
workflows: meta.workflows,
|
|
1006
|
+
...meta.ui !== void 0 ? { ui: meta.ui } : {},
|
|
1007
|
+
files: files.map((f) => f.path),
|
|
1008
|
+
fileCount,
|
|
1009
|
+
totalSize,
|
|
1010
|
+
sha256,
|
|
1011
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1012
|
+
agentprotoVersion: ">=0.1.0"
|
|
1013
|
+
};
|
|
1014
|
+
const staging = await mkdtemp(join(tmpdir(), "agentapp-"));
|
|
1015
|
+
try {
|
|
1016
|
+
await cp(appDirAbs, staging, {
|
|
1017
|
+
recursive: true,
|
|
1018
|
+
filter: (source) => !SKIP_DIR_NAMES.has(basename(source))
|
|
1019
|
+
});
|
|
1020
|
+
await writeFile(join(staging, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
1021
|
+
const entries = (await readdir(staging)).sort();
|
|
1022
|
+
const packCode = await runTar(["-czf", outAbs, ...entries], { cwd: staging });
|
|
1023
|
+
if (packCode !== 0) {
|
|
1024
|
+
throw new AgentAppPackError("tar-failed", `tar failed with exit code ${packCode}`);
|
|
1025
|
+
}
|
|
1026
|
+
} finally {
|
|
1027
|
+
await rm(staging, { recursive: true, force: true });
|
|
1028
|
+
}
|
|
1029
|
+
return { file: outAbs, manifest };
|
|
1030
|
+
}
|
|
1031
|
+
function isUnsafeBundlePath(p) {
|
|
1032
|
+
if (p === "" || isAbsolute(p)) return true;
|
|
1033
|
+
return p.split(/[\\/]/).includes("..");
|
|
1034
|
+
}
|
|
1035
|
+
async function unpackApp(input) {
|
|
1036
|
+
const bundleAbs = resolve(input.file);
|
|
1037
|
+
if (!await pathExists(bundleAbs)) {
|
|
1038
|
+
throw new AgentAppPackError("bundle-not-found", `bundle not found: ${bundleAbs}`);
|
|
1039
|
+
}
|
|
1040
|
+
const temp = await mkdtemp(join(tmpdir(), "agentapp-"));
|
|
1041
|
+
try {
|
|
1042
|
+
const unpackCode = await runTar(["-xzf", bundleAbs, "-C", temp]);
|
|
1043
|
+
if (unpackCode !== 0) {
|
|
1044
|
+
throw new AgentAppPackError("tar-failed", `tar failed with exit code ${unpackCode}`);
|
|
1045
|
+
}
|
|
1046
|
+
const manifestPath = join(temp, "manifest.json");
|
|
1047
|
+
if (!await pathExists(manifestPath)) {
|
|
1048
|
+
throw new AgentAppPackError(
|
|
1049
|
+
"missing-manifest",
|
|
1050
|
+
`${bundleAbs} is not a valid .agentapp (missing manifest.json).`
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
let parsed;
|
|
1054
|
+
try {
|
|
1055
|
+
parsed = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
1056
|
+
} catch {
|
|
1057
|
+
throw new AgentAppPackError("malformed-manifest", `${bundleAbs} has a malformed manifest.json.`);
|
|
1058
|
+
}
|
|
1059
|
+
if (!isManifest(parsed)) {
|
|
1060
|
+
throw new AgentAppPackError("malformed-manifest", `${bundleAbs} has a malformed manifest.json.`);
|
|
1061
|
+
}
|
|
1062
|
+
const manifest = parsed;
|
|
1063
|
+
if (manifest.format !== AGENTAPP_FORMAT) {
|
|
1064
|
+
throw new AgentAppPackError(
|
|
1065
|
+
"unsupported-format",
|
|
1066
|
+
`unsupported bundle format '${manifest.format}' (expected ${AGENTAPP_FORMAT}).`
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
const unsafe = manifest.files.find((f) => typeof f !== "string" || isUnsafeBundlePath(f));
|
|
1070
|
+
if (unsafe !== void 0) {
|
|
1071
|
+
throw new AgentAppPackError(
|
|
1072
|
+
"unsafe-path",
|
|
1073
|
+
`manifest.json lists a path outside the bundle root: ${JSON.stringify(unsafe)}.`
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
const actual = await aggregateSha256(temp, manifest.files);
|
|
1077
|
+
if (actual !== manifest.sha256) {
|
|
1078
|
+
throw new AgentAppPackError(
|
|
1079
|
+
"digest-mismatch",
|
|
1080
|
+
`SHA-256 mismatch (expected ${manifest.sha256}, got ${actual}). Bundle is corrupted.`
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
const outDir = input.dest !== void 0 ? resolve(input.dest) : resolve(process.cwd(), `${safeId(manifest.id)}-${manifest.version}`);
|
|
1084
|
+
await rm(manifestPath, { force: true });
|
|
1085
|
+
await mkdir(outDir, { recursive: true });
|
|
1086
|
+
await cp(temp, outDir, { recursive: true });
|
|
1087
|
+
return { dir: outDir, manifest };
|
|
1088
|
+
} finally {
|
|
1089
|
+
await rm(temp, { recursive: true, force: true });
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
687
1092
|
|
|
688
|
-
export { AppDefinitionError, AppLoadError, defineApp, emitApp, loadAppBundledTools, loadAppHandle, refKey, resolveAppUIRoot, stripOwner };
|
|
1093
|
+
export { AGENTAPP_FORMAT, AgentAppPackError, AppDefinitionError, AppLoadError, aggregateSha256, collectFiles, defineApp, emitApp, isManifest, loadAppBundledTools, loadAppHandle, packApp, peekAppUi, refKey, resolveAppUIRoot, safeId, stripOwner, unpackApp };
|
|
689
1094
|
//# sourceMappingURL=index.mjs.map
|
|
690
1095
|
//# sourceMappingURL=index.mjs.map
|