@agentproto/app-kit 1.5.0 → 1.6.1

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 CHANGED
@@ -140,7 +140,7 @@ runtime app registry:
140
140
  /apps/:appId/ui`, the MCP panel cache, and `agentproto app serve` all call
141
141
  the same `ensureAppUiBuilt` (`@agentproto/runtime/app-ui-build`) before
142
142
  serving, single-flight per bundle path so concurrent first requests only
143
- build once. Output is captured to `<appDir>/.agentproto/ui-build.log`; a
143
+ build once. Output is captured to `~/.agentproto/logs/app-ui-build/<app>-<hash>.log`; a
144
144
  failing build surfaces that log's tail as the tool/route error instead of
145
145
  a bare 404. No `ui.build` declared ⇒ today's behavior: the bundle must
146
146
  already exist on disk.
@@ -394,7 +394,7 @@ ui:
394
394
  `agentproto app serve` all resolve this the same way (`peekAppUi` +
395
395
  `ensureAppUiBuilt` from `@agentproto/runtime/app-ui-build`): missing or
396
396
  older than the newest matching source → run `command` once (single-flight
397
- per bundle path, output captured to `.agentproto/ui-build.log`) → serve.
397
+ per bundle path, output captured to `~/.agentproto/logs/app-ui-build/`) → serve.
398
398
  Omit `build` to keep committing the bundle, unchanged from before this
399
399
  existed.
400
400
 
@@ -1 +1 @@
1
- {"version":3,"file":"define-app.d.ts","sourceRoot":"","sources":["../src/define-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAEV,aAAa,EACb,SAAS,EACT,aAAa,EACb,oBAAoB,EAKrB,MAAM,YAAY,CAAA;AAKnB,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CA0LvD;AA8JD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACpC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,GAC/B,oBAAoB,GAAG,SAAS,CAsBlC;AAmPD,YAAY,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"define-app.d.ts","sourceRoot":"","sources":["../src/define-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAEV,aAAa,EACb,SAAS,EAET,aAAa,EACb,oBAAoB,EAKrB,MAAM,YAAY,CAAA;AAKnB,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CA+LvD;AA8JD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACpC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,GAC/B,oBAAoB,GAAG,SAAS,CAsBlC;AAmPD,YAAY,EAAE,aAAa,EAAE,CAAA"}
package/dist/emit.d.ts CHANGED
@@ -32,7 +32,7 @@
32
32
  */
33
33
  import type { WorkflowHandle } from "@agentproto/workflow";
34
34
  import type { WorkspaceHandle } from "@agentproto/workspace";
35
- import type { AgentEntry, AppAccepts, AppArtifactDecl, AppArtifactSurface, AppBoundariesDefinition, AppDataDefinition, AppDevDefinition, AppExposes, AppPlacement, AppRequirements, AppSkillSurface, AppUiDefinition, EmittedApp } from "./types.js";
35
+ import type { AgentEntry, AppAccepts, AppArtifactDecl, AppArtifactSurface, AppBoundariesDefinition, AppDataDefinition, AppDevDefinition, AppExposes, AppPlacement, AppRequirement, AppRequirements, AppSkillSurface, AppUiDefinition, EmittedApp } from "./types.js";
36
36
  interface EmitInput {
37
37
  readonly agents: readonly AgentEntry[];
38
38
  readonly workflows: readonly WorkflowHandle[];
@@ -43,6 +43,7 @@ interface EmitInput {
43
43
  readonly description?: string;
44
44
  readonly requires?: readonly string[];
45
45
  readonly requirements?: AppRequirements;
46
+ readonly appRequirements?: readonly AppRequirement[];
46
47
  readonly placement?: AppPlacement;
47
48
  readonly exposes?: AppExposes;
48
49
  readonly accepts?: AppAccepts;
@@ -1 +1 @@
1
- {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGpP,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAA;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAA;IACpC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAA;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAA;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAA;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAwH9E"}
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGpQ,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAA;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAA;IACpC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAA;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,cAAc,EAAE,CAAA;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAA;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAA;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAwH9E"}
package/dist/index.d.ts CHANGED
@@ -41,8 +41,9 @@ export { emitApp } from "./emit.js";
41
41
  export { loadAppHandle, AppLoadError, resolveAppUIRoot, peekAppUi } from "./load-app.js";
42
42
  export type { AppUiPeek } from "./load-app.js";
43
43
  export { loadAppBundledTools } from "./load-app-tools.js";
44
- export { packApp, unpackApp, aggregateSha256, collectFiles, isManifest, safeId, AGENTAPP_FORMAT, AgentAppPackError, } from "./pack.js";
45
- export type { AgentAppManifest, BundleFile, AgentAppPackErrorCode } from "./pack.js";
44
+ export { packApp, unpackApp, aggregateSha256, collectFiles, isManifest, safeId, AGENTAPP_FORMAT, AgentAppPackError, RELEASE_DEFAULT_EXCLUDE, globToRegExp, readPackageRules, } from "./pack.js";
45
+ export type { AgentAppManifest, BundleFile, AgentAppPackErrorCode, AppPackageRules } from "./pack.js";
46
46
  export { refKey, stripOwner } from "./refs.js";
47
- export type { AppDefinition, AppHandle, AgentEntry, DoctypeHandle, WorkspaceShorthand, WorkspaceInput, AppUiDefinition, AppUiBuildConfig, OpenAIAppUiExtension, OpenAIEntrypoint, OpenAIIcon, AppArtifactSurface, AppSkillSurface, AppArtifactDecl, AppDevLaunchConfig, AppBoundariesDefinition, AppDataDefinition, AppDevDefinition, AppPlacement, AppRequirements, AppExposes, AppAccepts, ToMastraAgentOptions, EmittedApp, } from "./types.js";
47
+ export { versionSatisfies } from "./version-range.js";
48
+ export type { AppDefinition, AppHandle, AgentEntry, DoctypeHandle, WorkspaceShorthand, WorkspaceInput, AppUiDefinition, AppUiBuildConfig, OpenAIAppUiExtension, OpenAIEntrypoint, OpenAIIcon, AppArtifactSurface, AppSkillSurface, AppArtifactDecl, AppDevLaunchConfig, AppBoundariesDefinition, AppDataDefinition, AppDevDefinition, AppPlacement, AppRequirements, AppRequirement, AppRequirementEntry, AppExposes, AppAccepts, ToMastraAgentOptions, EmittedApp, } from "./types.js";
48
49
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACxF,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EACL,OAAO,EACP,SAAS,EACT,eAAe,EACf,YAAY,EACZ,UAAU,EACV,MAAM,EACN,eAAe,EACf,iBAAiB,GAClB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AACpF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC9C,YAAY,EACV,aAAa,EACb,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,UAAU,GACX,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACxF,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EACL,OAAO,EACP,SAAS,EACT,eAAe,EACf,YAAY,EACZ,UAAU,EACV,MAAM,EACN,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACrG,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EACV,aAAa,EACb,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,UAAU,GACX,MAAM,YAAY,CAAA"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { join, relative, isAbsolute, dirname, resolve, basename } from 'path';
1
+ import { join, relative, isAbsolute, dirname, resolve, posix, basename } from 'path';
2
2
  import { defineWorkspace } from '@agentproto/workspace';
3
- import { mkdir, writeFile, copyFile, cp, readFile, mkdtemp, readdir, rm, stat } from 'fs/promises';
3
+ import { mkdir, writeFile, copyFile, cp, readFile, mkdtemp, stat, readdir, rm } 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';
@@ -143,13 +143,28 @@ async function emitApp(app, dir) {
143
143
  }
144
144
  function requiresFrontmatter(app) {
145
145
  const r = app.requirements;
146
+ const apps = appsFrontmatter(app);
147
+ const hasAppDetail = (app.appRequirements ?? []).some(
148
+ (e) => e.version !== void 0 || e.workflows !== void 0 && e.workflows.length > 0
149
+ );
146
150
  if (r && (r.browser || r.fs || r.gpu || r.secrets.length > 0)) {
147
151
  return {
148
- requires: { browser: r.browser, fs: r.fs, gpu: r.gpu, secrets: r.secrets, apps: r.apps }
152
+ requires: { browser: r.browser, fs: r.fs, gpu: r.gpu, secrets: r.secrets, apps }
149
153
  };
150
154
  }
155
+ if (hasAppDetail) {
156
+ return { requires: { apps } };
157
+ }
151
158
  return app.requires !== void 0 ? { requires: app.requires } : {};
152
159
  }
160
+ function appsFrontmatter(app) {
161
+ const entries = app.appRequirements ?? app.requires ?? [];
162
+ return entries.map((e) => {
163
+ if (typeof e === "string") return e;
164
+ if (e.version === void 0 && (e.workflows === void 0 || e.workflows.length === 0)) return e.id;
165
+ return e;
166
+ });
167
+ }
153
168
  function toManifest(handle, body) {
154
169
  const data = JSON.parse(JSON.stringify(handle));
155
170
  return matter2.stringify(`
@@ -157,6 +172,52 @@ ${body}
157
172
  `, data);
158
173
  }
159
174
 
175
+ // src/version-range.ts
176
+ var VERSION_RE = /^(\d+)\.(\d+)\.(\d+)(?:[-+][\w.\-]+)?$/;
177
+ function parseVersion(v) {
178
+ const m = VERSION_RE.exec(v);
179
+ if (!m) {
180
+ throw new Error(`invalid version "${v}" \u2014 expected major.minor.patch (e.g. "1.2.3").`);
181
+ }
182
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
183
+ }
184
+ function compare(a, b) {
185
+ return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
186
+ }
187
+ function parseVersionRange(range) {
188
+ let m = /^\^(\d+)(?:\.(\d+))?(?:\.(\d+))?$/.exec(range);
189
+ if (m !== null) {
190
+ return {
191
+ kind: "caret",
192
+ version: [Number(m[1]), m[2] !== void 0 ? Number(m[2]) : 0, m[3] !== void 0 ? Number(m[3]) : 0]
193
+ };
194
+ }
195
+ m = /^~(\d+)(?:\.(\d+))?(?:\.(\d+))?$/.exec(range);
196
+ if (m !== null) {
197
+ return {
198
+ kind: "tilde",
199
+ version: [Number(m[1]), m[2] !== void 0 ? Number(m[2]) : 0, m[3] !== void 0 ? Number(m[3]) : 0],
200
+ minorGiven: m[2] !== void 0
201
+ };
202
+ }
203
+ if (VERSION_RE.test(range)) return { kind: "exact", version: parseVersion(range) };
204
+ throw new Error(
205
+ `invalid version range "${range}" \u2014 expected an exact version ("1.2.3"), a caret ("^1", "^1.2", "^1.2.3"), or a tilde ("~1", "~1.2", "~1.2.3").`
206
+ );
207
+ }
208
+ function versionSatisfies(version, range) {
209
+ const v = parseVersion(version);
210
+ const r = parseVersionRange(range);
211
+ if (r.kind === "exact") return compare(v, r.version) === 0;
212
+ if (compare(v, r.version) < 0) return false;
213
+ if (r.kind === "caret") {
214
+ if (r.version[0] > 0) return v[0] === r.version[0];
215
+ if (r.version[1] > 0) return v[0] === 0 && v[1] === r.version[1];
216
+ return v[0] === 0 && v[1] === 0 && v[2] === r.version[2];
217
+ }
218
+ return v[0] === r.version[0] && (!r.minorGiven || v[1] === r.version[1]);
219
+ }
220
+
160
221
  // src/manifest-fields.ts
161
222
  var APP_PLACEMENTS = ["local", "box", "any", "split"];
162
223
  function isRecord(v) {
@@ -174,6 +235,52 @@ function bool(v, key, fail) {
174
235
  if (typeof v !== "boolean") throw fail(`'${key}' must be a boolean.`);
175
236
  return v;
176
237
  }
238
+ function appRequirementEntry(entry, key, fail) {
239
+ if (typeof entry === "string") {
240
+ if (entry.trim() === "") throw fail(`'${key}' entries must be non-empty strings or objects.`);
241
+ return { id: entry };
242
+ }
243
+ if (!isRecord(entry)) {
244
+ throw fail(`'${key}' entries must be non-empty strings or objects, got ${JSON.stringify(entry)}.`);
245
+ }
246
+ const id = entry.id;
247
+ if (typeof id !== "string" || id.trim() === "") {
248
+ throw fail(`'${key}.id' must be a non-empty string.`);
249
+ }
250
+ const version = entry.version;
251
+ if (version !== void 0) {
252
+ if (typeof version !== "string" || version.trim() === "") {
253
+ throw fail(`'${key}.version' must be a non-empty string.`);
254
+ }
255
+ try {
256
+ parseVersionRange(version);
257
+ } catch (err) {
258
+ throw fail(`'${key}.version' ${err instanceof Error ? err.message : String(err)}`);
259
+ }
260
+ }
261
+ const workflows = entry.workflows;
262
+ if (workflows !== void 0) {
263
+ if (!Array.isArray(workflows) || !workflows.every((w) => typeof w === "string" && w.trim() !== "")) {
264
+ throw fail(`'${key}.workflows' must be an array of non-empty strings.`);
265
+ }
266
+ }
267
+ const extra = Object.keys(entry).filter((k) => k !== "id" && k !== "version" && k !== "workflows");
268
+ if (extra.length > 0) {
269
+ throw fail(`'${key}.${extra[0]}' is not a supported requires.apps entry field (expected id, version, workflows).`);
270
+ }
271
+ return {
272
+ id,
273
+ ...version !== void 0 ? { version } : {},
274
+ ...workflows !== void 0 ? { workflows: [...workflows] } : {}
275
+ };
276
+ }
277
+ function appRequirementList(v, key, fail) {
278
+ if (v === void 0) return [];
279
+ if (!Array.isArray(v)) {
280
+ throw fail(`'${key}' must be an array of non-empty strings or { id, version?, workflows? } objects.`);
281
+ }
282
+ return v.map((e, i) => appRequirementEntry(e, `${key}[${i}]`, fail));
283
+ }
177
284
  function normalizeManifestFields(input, declared, fail) {
178
285
  let placement = "any";
179
286
  if (input.placement !== void 0) {
@@ -186,6 +293,7 @@ function normalizeManifestFields(input, declared, fail) {
186
293
  }
187
294
  let requires;
188
295
  let requirements = { browser: false, fs: false, gpu: false, secrets: [], apps: [] };
296
+ let appRequirements = [];
189
297
  const r = input.requires;
190
298
  if (r !== void 0) {
191
299
  if (Array.isArray(r)) {
@@ -194,16 +302,18 @@ function normalizeManifestFields(input, declared, fail) {
194
302
  }
195
303
  requires = [...r];
196
304
  requirements = { ...requirements, apps: requires };
305
+ appRequirements = appRequirementList(r, "requires", fail);
197
306
  } else if (isRecord(r)) {
198
- const apps = stringList(r.apps, "requires.apps", fail);
307
+ const apps = appRequirementList(r.apps, "requires.apps", fail);
199
308
  requirements = {
200
309
  browser: bool(r.browser, "requires.browser", fail),
201
310
  fs: bool(r.fs, "requires.fs", fail),
202
311
  gpu: bool(r.gpu, "requires.gpu", fail),
203
312
  secrets: stringList(r.secrets, "requires.secrets", fail),
204
- apps
313
+ apps: apps.map((e) => e.id)
205
314
  };
206
- if (apps.length > 0) requires = apps;
315
+ appRequirements = apps;
316
+ if (apps.length > 0) requires = apps.map((e) => e.id);
207
317
  } else {
208
318
  throw fail("'requires' must be an array of strings or an object { browser, fs, gpu, secrets, apps }.");
209
319
  }
@@ -234,7 +344,7 @@ function normalizeManifestFields(input, declared, fail) {
234
344
  if (!isRecord(input.accepts)) throw fail("'accepts' must be an object { tasks }.");
235
345
  accepts = { tasks: bool(input.accepts.tasks, "accepts.tasks", fail) };
236
346
  }
237
- return { placement, requires, requirements, exposes, accepts };
347
+ return { placement, requires, requirements, appRequirements, exposes, accepts };
238
348
  }
239
349
 
240
350
  // src/define-app.ts
@@ -302,6 +412,9 @@ function defineApp(def) {
302
412
  secrets: Object.freeze([...fields.requirements.secrets]),
303
413
  apps: Object.freeze([...fields.requirements.apps])
304
414
  });
415
+ const appRequirements = Object.freeze(
416
+ fields.appRequirements.map((e) => Object.freeze({ ...e, ...e.workflows !== void 0 ? { workflows: Object.freeze([...e.workflows]) } : {} }))
417
+ );
305
418
  const placement = fields.placement;
306
419
  const exposes = Object.freeze({
307
420
  agents: Object.freeze([...fields.exposes.agents]),
@@ -350,6 +463,7 @@ function defineApp(def) {
350
463
  ...description !== void 0 ? { description } : {},
351
464
  ...requires !== void 0 ? { requires } : {},
352
465
  requirements,
466
+ appRequirements,
353
467
  placement,
354
468
  exposes,
355
469
  accepts,
@@ -393,6 +507,7 @@ function defineApp(def) {
393
507
  ...description !== void 0 ? { description } : {},
394
508
  ...requires !== void 0 ? { requires } : {},
395
509
  requirements,
510
+ appRequirements,
396
511
  placement,
397
512
  exposes,
398
513
  accepts,
@@ -1227,8 +1342,116 @@ function extractMeta(front) {
1227
1342
  ...ui !== void 0 ? { ui } : {}
1228
1343
  };
1229
1344
  }
1345
+ var RELEASE_DEFAULT_EXCLUDE = [
1346
+ "ui/**",
1347
+ "docs/**",
1348
+ "data/**",
1349
+ "scripts/**",
1350
+ // Root-level only: `**/dev/**` would also drop a shipped agent named
1351
+ // `dev` (`.agentproto/agents/dev/`); `ui/dev/` is covered by `ui/**`.
1352
+ // Same for `test/` and `tests/`.
1353
+ "dev/**",
1354
+ "test/**",
1355
+ "tests/**",
1356
+ // Test files and folders anywhere (a workflow's `foo.test.mjs` next to
1357
+ // its `entry.mjs`); these names are never a runtime import.
1358
+ "**/__tests__/**",
1359
+ "**/*.test.*",
1360
+ "**/*.spec.*",
1361
+ // Repo docs at the app root (the catalog entry carries the description).
1362
+ "README.md",
1363
+ "CHANGELOG.md",
1364
+ "CONTRIBUTING.md",
1365
+ // Repo and editor tooling.
1366
+ ".github/**",
1367
+ ".vscode/**",
1368
+ ".idea/**",
1369
+ ".gitignore",
1370
+ ".editorconfig",
1371
+ "tsconfig*.json",
1372
+ "vitest.config.*",
1373
+ "jest.config.*",
1374
+ "eslint.config.*",
1375
+ ".eslintrc*",
1376
+ ".prettierrc*",
1377
+ "**/*.log",
1378
+ "**/*.map",
1379
+ "**/.DS_Store",
1380
+ "**/.env",
1381
+ "**/.env.*"
1382
+ ];
1383
+ function globToRegExp(glob) {
1384
+ const g = glob.replace(/^\.\//, "");
1385
+ let re = "";
1386
+ for (let i = 0; i < g.length; i++) {
1387
+ const c = g[i];
1388
+ if (c === "*") {
1389
+ if (g[i + 1] === "*") {
1390
+ i++;
1391
+ if (g[i + 1] === "/") {
1392
+ i++;
1393
+ re += "(?:.*/)?";
1394
+ } else {
1395
+ re += ".*";
1396
+ }
1397
+ } else {
1398
+ re += "[^/]*";
1399
+ }
1400
+ } else if ("\\^$+?.()|{}[]".includes(c)) {
1401
+ re += "\\" + c;
1402
+ } else {
1403
+ re += c;
1404
+ }
1405
+ }
1406
+ return new RegExp(`^${re}$`);
1407
+ }
1408
+ function toPosix(p) {
1409
+ return p.replace(/\\/g, "/");
1410
+ }
1411
+ function matchesAny(path, globs) {
1412
+ const p = toPosix(path);
1413
+ return globs.some((re) => re.test(p));
1414
+ }
1415
+ function readPackageRules(front) {
1416
+ const raw = front.package;
1417
+ if (raw === void 0 || raw === null) return {};
1418
+ if (typeof raw !== "object" || Array.isArray(raw)) {
1419
+ throw new AgentAppPackError("invalid-package", "APP.md `package` must be an object.");
1420
+ }
1421
+ const r = raw;
1422
+ const list = (key) => {
1423
+ const v = r[key];
1424
+ if (v === void 0) return void 0;
1425
+ if (!Array.isArray(v) || v.some((s) => typeof s !== "string" || s.trim() === "")) {
1426
+ throw new AgentAppPackError(
1427
+ "invalid-package",
1428
+ `APP.md \`package.${key}\` must be an array of non-empty glob strings.`
1429
+ );
1430
+ }
1431
+ return v;
1432
+ };
1433
+ if (r.stripBuild !== void 0 && typeof r.stripBuild !== "boolean") {
1434
+ throw new AgentAppPackError("invalid-package", "APP.md `package.stripBuild` must be a boolean.");
1435
+ }
1436
+ const include = list("include");
1437
+ const exclude = list("exclude");
1438
+ return {
1439
+ ...include !== void 0 ? { include } : {},
1440
+ ...exclude !== void 0 ? { exclude } : {},
1441
+ ...typeof r.stripBuild === "boolean" ? { stripBuild: r.stripBuild } : {}
1442
+ };
1443
+ }
1444
+ function uiRelPath(front) {
1445
+ const ui = front.ui;
1446
+ const raw = typeof ui === "string" ? ui : ui && typeof ui === "object" && !Array.isArray(ui) && typeof ui.path === "string" ? ui.path : void 0;
1447
+ if (raw === void 0 || raw === "" || isAbsolute(raw)) return void 0;
1448
+ const rel = posix.normalize(toPosix(raw)).replace(/^\.\//, "");
1449
+ if (rel.startsWith("../") || rel === "..") return void 0;
1450
+ return rel;
1451
+ }
1230
1452
  async function packApp(input) {
1231
1453
  const appDirAbs = resolve(input.appDir);
1454
+ const release = input.release === true;
1232
1455
  const appMdPath = join(appDirAbs, ".agentproto", "APP.md");
1233
1456
  if (!await pathExists(appMdPath)) {
1234
1457
  throw new AgentAppPackError(
@@ -1237,49 +1460,82 @@ async function packApp(input) {
1237
1460
  );
1238
1461
  }
1239
1462
  const raw = await readFile(appMdPath, "utf8");
1240
- const front = matter2(raw).data;
1463
+ const parsedAppMd = matter2(raw);
1464
+ const front = parsedAppMd.data;
1241
1465
  const meta = extractMeta(front);
1466
+ const rules = readPackageRules(front);
1242
1467
  const outAbs = input.out !== void 0 ? resolve(input.out) : resolve(process.cwd(), `${safeId(meta.id)}-${meta.version}.agentapp`);
1243
1468
  await mkdir(dirname(outAbs), { recursive: true });
1244
- 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);
1245
- const fileCount = files.length;
1246
- const totalSize = files.reduce((sum, f) => sum + f.size, 0);
1247
- const sha256 = await aggregateSha256(
1248
- appDirAbs,
1249
- files.map((f) => f.path)
1250
- );
1251
- const manifest = {
1252
- format: AGENTAPP_FORMAT,
1253
- id: meta.id,
1254
- ...meta.name !== void 0 ? { name: meta.name } : {},
1255
- version: meta.version,
1256
- ...meta.description !== void 0 ? { description: meta.description } : {},
1257
- agents: meta.agents,
1258
- workflows: meta.workflows,
1259
- ...meta.ui !== void 0 ? { ui: meta.ui } : {},
1260
- files: files.map((f) => f.path),
1261
- fileCount,
1262
- totalSize,
1263
- sha256,
1264
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1265
- agentprotoVersion: ">=0.1.0"
1266
- };
1469
+ const appMdRel = ".agentproto/APP.md";
1470
+ const uiRel = uiRelPath(front);
1471
+ const uiAssetsPrefix = uiRel !== void 0 ? `${posix.dirname(uiRel)}/assets/`.replace(/^\.\//, "") : void 0;
1472
+ const includeRes = rules.include?.map(globToRegExp);
1473
+ const excludeRes = [...release ? RELEASE_DEFAULT_EXCLUDE : [], ...rules.exclude ?? []].map(globToRegExp);
1474
+ const alwaysShip = (p) => p === appMdRel || p === uiRel || uiAssetsPrefix !== void 0 && p.startsWith(uiAssetsPrefix);
1475
+ const selected = (await collectFiles(appDirAbs)).filter((f) => f.path !== "manifest.json").filter((f) => join(appDirAbs, f.path) !== outAbs).filter((f) => {
1476
+ const p = toPosix(f.path);
1477
+ if (alwaysShip(p)) return true;
1478
+ if (includeRes !== void 0 && !matchesAny(p, includeRes)) return false;
1479
+ return !matchesAny(p, excludeRes);
1480
+ }).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
1481
+ if (release && uiRel !== void 0 && !selected.some((f) => toPosix(f.path) === uiRel)) {
1482
+ throw new AgentAppPackError(
1483
+ "missing-ui",
1484
+ `ui.path "${uiRel}" does not exist in ${appDirAbs} \u2014 build the UI first (\`agentproto app build <appDir>\`, or \`agentproto app pack --release\`, which runs \`ui.build\`).`
1485
+ );
1486
+ }
1487
+ const stripBuild = rules.stripBuild ?? release;
1267
1488
  const staging = await mkdtemp(join(tmpdir(), "agentapp-"));
1268
1489
  try {
1269
- await cp(appDirAbs, staging, {
1270
- recursive: true,
1271
- filter: (source) => !SKIP_DIR_NAMES.has(basename(source))
1272
- });
1490
+ for (const f of selected) {
1491
+ const dest = join(staging, f.path);
1492
+ await mkdir(dirname(dest), { recursive: true });
1493
+ await copyFile(join(appDirAbs, f.path), dest);
1494
+ }
1495
+ if (stripBuild) {
1496
+ const ui = front.ui;
1497
+ if (ui && typeof ui === "object" && !Array.isArray(ui) && "build" in ui) {
1498
+ const data = structuredClone(front);
1499
+ delete data.ui.build;
1500
+ await writeFile(join(staging, appMdRel), matter2.stringify(parsedAppMd.content, data), "utf8");
1501
+ }
1502
+ }
1503
+ const files = [];
1504
+ for (const f of selected) {
1505
+ files.push({ path: f.path, size: (await stat(join(staging, f.path))).size });
1506
+ }
1507
+ const fileCount = files.length;
1508
+ const totalSize = files.reduce((sum, f) => sum + f.size, 0);
1509
+ const sha256 = await aggregateSha256(
1510
+ staging,
1511
+ files.map((f) => f.path)
1512
+ );
1513
+ const manifest = {
1514
+ format: AGENTAPP_FORMAT,
1515
+ id: meta.id,
1516
+ ...meta.name !== void 0 ? { name: meta.name } : {},
1517
+ version: meta.version,
1518
+ ...meta.description !== void 0 ? { description: meta.description } : {},
1519
+ agents: meta.agents,
1520
+ workflows: meta.workflows,
1521
+ ...meta.ui !== void 0 ? { ui: meta.ui } : {},
1522
+ files: files.map((f) => f.path),
1523
+ fileCount,
1524
+ totalSize,
1525
+ sha256,
1526
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1527
+ agentprotoVersion: ">=0.1.0"
1528
+ };
1273
1529
  await writeFile(join(staging, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
1274
1530
  const entries = (await readdir(staging)).sort();
1275
1531
  const packCode = await runTar(["-czf", outAbs, ...entries], { cwd: staging });
1276
1532
  if (packCode !== 0) {
1277
1533
  throw new AgentAppPackError("tar-failed", `tar failed with exit code ${packCode}`);
1278
1534
  }
1535
+ return { file: outAbs, manifest };
1279
1536
  } finally {
1280
1537
  await rm(staging, { recursive: true, force: true });
1281
1538
  }
1282
- return { file: outAbs, manifest };
1283
1539
  }
1284
1540
  function isUnsafeBundlePath(p) {
1285
1541
  if (p === "" || isAbsolute(p)) return true;
@@ -1343,6 +1599,6 @@ async function unpackApp(input) {
1343
1599
  }
1344
1600
  }
1345
1601
 
1346
- export { AGENTAPP_FORMAT, AgentAppPackError, AppDefinitionError, AppLoadError, aggregateSha256, collectFiles, defineApp, emitApp, isManifest, loadAppBundledTools, loadAppHandle, packApp, peekAppUi, refKey, resolveAppUIRoot, safeId, stripOwner, unpackApp };
1602
+ export { AGENTAPP_FORMAT, AgentAppPackError, AppDefinitionError, AppLoadError, RELEASE_DEFAULT_EXCLUDE, aggregateSha256, collectFiles, defineApp, emitApp, globToRegExp, isManifest, loadAppBundledTools, loadAppHandle, packApp, peekAppUi, readPackageRules, refKey, resolveAppUIRoot, safeId, stripOwner, unpackApp, versionSatisfies };
1347
1603
  //# sourceMappingURL=index.mjs.map
1348
1604
  //# sourceMappingURL=index.mjs.map