antpath 0.4.1 → 0.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.
@@ -291,6 +291,13 @@ export function parseMcpServerRef(input, path) {
291
291
  if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
292
292
  throw new Error(`${path}.url must use http or https (got ${parsed.protocol})`);
293
293
  }
294
+ // Auth belongs in `secrets.mcpServers[i].headers`, never in the URL
295
+ // itself. A `https://user:pass@host` style URL would be persisted in
296
+ // the non-secret run snapshot and hashed into the idempotency key —
297
+ // both unacceptable for credential material.
298
+ if (parsed.username !== "" || parsed.password !== "") {
299
+ throw new Error(`${path}.url must not contain userinfo (username/password); use secrets.mcpServers[].headers for auth`);
300
+ }
294
301
  }
295
302
  catch (cause) {
296
303
  if (cause instanceof Error && cause.message.startsWith(path)) {
@@ -63,10 +63,11 @@ export async function createSkillBundle(http, args) {
63
63
  form.append("name", args.name);
64
64
  const blobBody = toBlob(args.body, args.contentType ?? "application/zip");
65
65
  form.append("bundle", blobBody, args.filename ?? `${args.name}.zip`);
66
- return http.request("/api/skills", {
66
+ const result = await http.request("/api/skills", {
67
67
  method: "POST",
68
68
  body: form
69
69
  });
70
+ return unwrapSkill(result);
70
71
  }
71
72
  export async function listSkills(http) {
72
73
  const result = await http.request("/api/skills");
@@ -76,13 +77,20 @@ export async function listSkills(http) {
76
77
  return result.skills;
77
78
  }
78
79
  export async function getSkill(http, skillId) {
79
- return http.request(`/api/skills/${encodeURIComponent(skillId)}`);
80
+ const result = await http.request(`/api/skills/${encodeURIComponent(skillId)}`);
81
+ return unwrapSkill(result);
80
82
  }
81
83
  export async function deleteSkill(http, skillId) {
82
84
  await http.request(`/api/skills/${encodeURIComponent(skillId)}`, {
83
85
  method: "DELETE"
84
86
  });
85
87
  }
88
+ function unwrapSkill(result) {
89
+ if (result && typeof result === "object" && "skill" in result) {
90
+ return result.skill;
91
+ }
92
+ return result;
93
+ }
86
94
  function toBlob(input, contentType) {
87
95
  if (input instanceof Blob) {
88
96
  return input;
@@ -793,7 +793,25 @@ function parseFlatSkills(input) {
793
793
  if (!Array.isArray(input)) {
794
794
  throw new Error("submission.skills must be an array of SkillRef objects");
795
795
  }
796
- return input.map((item, index) => parseSkillRef(item, `submission.skills[${index}]`));
796
+ const seenWorkspace = new Set();
797
+ const seenProvider = new Set();
798
+ return input.map((item, index) => {
799
+ const ref = parseSkillRef(item, `submission.skills[${index}]`);
800
+ if (ref.kind === "workspace") {
801
+ if (seenWorkspace.has(ref.id)) {
802
+ throw new Error(`submission.skills duplicate workspace skill id: ${ref.id}`);
803
+ }
804
+ seenWorkspace.add(ref.id);
805
+ }
806
+ else {
807
+ const key = `${ref.vendor}:${ref.skillId}:${ref.version ?? ""}`;
808
+ if (seenProvider.has(key)) {
809
+ throw new Error(`submission.skills duplicate provider skill: ${ref.vendor}:${ref.skillId}${ref.version ? `:${ref.version}` : ""}`);
810
+ }
811
+ seenProvider.add(key);
812
+ }
813
+ return ref;
814
+ });
797
815
  }
798
816
  function parseFlatMcpServers(input) {
799
817
  if (input === undefined) {
@@ -0,0 +1,33 @@
1
+ import type { JsonValue, PlatformCleanupPolicy, PlatformProxyEndpoint, PlatformTemplateEnvironment } from "./_shared/index.js";
2
+ import type { McpServer } from "./mcp-server.js";
3
+ import type { Skill } from "./skill.js";
4
+ /**
5
+ * SDK-side blueprint. Mirrors the shared `Blueprint` shape but uses the
6
+ * SDK's `Skill` and `McpServer` classes so call sites can write
7
+ * `skills: [rules]` and `mcpServers: [gh]` instead of repeating the
8
+ * underlying wire types. The `submitRun` method normalises these into
9
+ * the wire submission + secrets bag before sending.
10
+ *
11
+ * `Blueprint` is intentionally `Omit<SubmitRunOptions, "secrets" |
12
+ * "idempotencyKey" | "signal">` so it can be embedded as a reusable,
13
+ * credential-free fragment via `defineRun`.
14
+ */
15
+ export interface Blueprint {
16
+ readonly model: string;
17
+ readonly system?: string;
18
+ readonly prompt: string | readonly string[];
19
+ readonly skills?: readonly Skill[];
20
+ readonly mcpServers?: readonly McpServer[];
21
+ readonly environment?: PlatformTemplateEnvironment;
22
+ readonly cleanup?: PlatformCleanupPolicy;
23
+ readonly proxyEndpoints?: readonly PlatformProxyEndpoint[];
24
+ readonly metadata?: Record<string, JsonValue>;
25
+ }
26
+ /**
27
+ * Wrap a `(params) => Blueprint` factory. The wrapper is identity at
28
+ * runtime; the value of `defineRun` is purely the type boundary it
29
+ * imposes — it forces the producer to return a `Blueprint` (no
30
+ * `secrets`, no `idempotencyKey`, no `signal`) so callers cannot
31
+ * accidentally bake credentials into a reusable artifact.
32
+ */
33
+ export declare function defineRun<TParams>(producer: (params: TParams) => Blueprint): (params: TParams) => Blueprint;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Wrap a `(params) => Blueprint` factory. The wrapper is identity at
3
+ * runtime; the value of `defineRun` is purely the type boundary it
4
+ * imposes — it forces the producer to return a `Blueprint` (no
5
+ * `secrets`, no `idempotencyKey`, no `signal`) so callers cannot
6
+ * accidentally bake credentials into a reusable artifact.
7
+ */
8
+ export function defineRun(producer) {
9
+ if (typeof producer !== "function") {
10
+ throw new TypeError("defineRun expects a function");
11
+ }
12
+ return (params) => producer(params);
13
+ }
14
+ //# sourceMappingURL=blueprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAU,QAAwC;IACzE,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,MAAe,EAAa,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * In-memory skill bundle: a flat path -> bytes map and the
3
+ * deterministically-zipped representation.
4
+ *
5
+ * The SDK runs only the cheap, safety-critical checks here
6
+ * (`validateSkillBundleEntry`: no `..`, no absolute paths, no Windows
7
+ * backslashes, depth/length limits). The BFF re-canonicalises and
8
+ * recomputes the canonical hash on receipt — SDK-side hashing is NOT
9
+ * part of any contract, so we don't expose one.
10
+ */
11
+ export interface BundledSkill {
12
+ readonly zip: Uint8Array;
13
+ readonly fileCount: number;
14
+ readonly compressedSize: number;
15
+ }
16
+ /** Inline files map: path -> contents (UTF-8 string or raw bytes). */
17
+ export type SkillFiles = Readonly<Record<string, string | Uint8Array>>;
18
+ export declare function bundleSkillFiles(files: SkillFiles): BundledSkill;
package/dist/bundle.js ADDED
@@ -0,0 +1,55 @@
1
+ import { zipSync } from "fflate";
2
+ import { SKILL_BUNDLE_LIMITS, validateSkillBundleEntry } from "./_shared/index.js";
3
+ const TEXT = new TextEncoder();
4
+ export function bundleSkillFiles(files) {
5
+ if (!files || typeof files !== "object") {
6
+ throw new Error("Skill files map is required");
7
+ }
8
+ const entries = Object.entries(files);
9
+ if (entries.length === 0) {
10
+ throw new Error("Skill files map cannot be empty");
11
+ }
12
+ if (entries.length > SKILL_BUNDLE_LIMITS.maxFiles) {
13
+ throw new Error(`Skill bundle exceeds ${SKILL_BUNDLE_LIMITS.maxFiles} file limit (got ${entries.length})`);
14
+ }
15
+ const collected = new Map();
16
+ let hasSkillMd = false;
17
+ let totalDecompressed = 0;
18
+ for (const [rawPath, contents] of entries) {
19
+ const bytes = typeof contents === "string" ? TEXT.encode(contents) : contents;
20
+ if (!(bytes instanceof Uint8Array)) {
21
+ throw new Error(`Skill file "${rawPath}" must be a string or Uint8Array`);
22
+ }
23
+ const entry = validateSkillBundleEntry({ path: rawPath, size: bytes.byteLength });
24
+ if (entry.path === "SKILL.md") {
25
+ hasSkillMd = true;
26
+ }
27
+ totalDecompressed += bytes.byteLength;
28
+ if (totalDecompressed > SKILL_BUNDLE_LIMITS.maxDecompressedBytes) {
29
+ throw new Error(`Skill bundle exceeds decompressed cap of ${SKILL_BUNDLE_LIMITS.maxDecompressedBytes} bytes`);
30
+ }
31
+ if (collected.has(entry.path)) {
32
+ throw new Error(`Skill bundle contains duplicate path: ${entry.path}`);
33
+ }
34
+ collected.set(entry.path, bytes);
35
+ }
36
+ if (!hasSkillMd) {
37
+ throw new Error('Skill bundle must contain a "SKILL.md" file at the root');
38
+ }
39
+ // Sort entries and pin every mtime to the epoch so the byte output is
40
+ // identical across machines and re-runs (the BFF re-canonicalises and
41
+ // recomputes the canonical hash, so this is for retry-safety / debug
42
+ // reproducibility rather than a wire-shape contract).
43
+ const sorted = [...collected.entries()].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
44
+ const zippable = {};
45
+ for (const [path, bytes] of sorted) {
46
+ zippable[path] = [bytes, { mtime: ZIP_EPOCH }];
47
+ }
48
+ const zip = zipSync(zippable, { level: 6 });
49
+ if (zip.byteLength > SKILL_BUNDLE_LIMITS.maxCompressedBytes) {
50
+ throw new Error(`Skill bundle exceeds compressed cap of ${SKILL_BUNDLE_LIMITS.maxCompressedBytes} bytes (got ${zip.byteLength})`);
51
+ }
52
+ return { zip, fileCount: entries.length, compressedSize: zip.byteLength };
53
+ }
54
+ const ZIP_EPOCH = new Date(Date.UTC(1980, 0, 1));
55
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}