@hasna/skills 0.5.4 → 0.5.5

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/dist/index.js CHANGED
@@ -19,14 +19,12 @@ var __toESM = (mod, isNodeMode, target) => {
19
19
  }
20
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
21
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
- if (mod && typeof mod === "object" || typeof mod === "function") {
23
- for (let key of __getOwnPropNames(mod))
24
- if (!__hasOwnProp.call(to, key))
25
- __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
27
- enumerable: true
28
- });
29
- }
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
30
28
  if (canCache)
31
29
  cache.set(mod, to);
32
30
  return to;
@@ -4731,113 +4729,113 @@ function readIfExists(path) {
4731
4729
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
4732
4730
  var exports_external = {};
4733
4731
  __export(exports_external, {
4734
- BRAND: () => BRAND,
4735
- DIRTY: () => DIRTY,
4736
- EMPTY_PATH: () => EMPTY_PATH,
4737
- INVALID: () => INVALID,
4738
- NEVER: () => NEVER,
4739
- OK: () => OK,
4740
- ParseStatus: () => ParseStatus,
4741
- Schema: () => ZodType,
4742
- ZodAny: () => ZodAny,
4743
- ZodArray: () => ZodArray,
4744
- ZodBigInt: () => ZodBigInt,
4745
- ZodBoolean: () => ZodBoolean,
4746
- ZodBranded: () => ZodBranded,
4747
- ZodCatch: () => ZodCatch,
4748
- ZodDate: () => ZodDate,
4749
- ZodDefault: () => ZodDefault,
4750
- ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
4751
- ZodEffects: () => ZodEffects,
4752
- ZodEnum: () => ZodEnum,
4753
- ZodError: () => ZodError,
4754
- ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
4755
- ZodFunction: () => ZodFunction,
4756
- ZodIntersection: () => ZodIntersection,
4757
- ZodIssueCode: () => ZodIssueCode,
4758
- ZodLazy: () => ZodLazy,
4759
- ZodLiteral: () => ZodLiteral,
4760
- ZodMap: () => ZodMap,
4761
- ZodNaN: () => ZodNaN,
4762
- ZodNativeEnum: () => ZodNativeEnum,
4763
- ZodNever: () => ZodNever,
4764
- ZodNull: () => ZodNull,
4765
- ZodNullable: () => ZodNullable,
4766
- ZodNumber: () => ZodNumber,
4767
- ZodObject: () => ZodObject,
4768
- ZodOptional: () => ZodOptional,
4769
- ZodParsedType: () => ZodParsedType,
4770
- ZodPipeline: () => ZodPipeline,
4771
- ZodPromise: () => ZodPromise,
4772
- ZodReadonly: () => ZodReadonly,
4773
- ZodRecord: () => ZodRecord,
4774
- ZodSchema: () => ZodType,
4775
- ZodSet: () => ZodSet,
4776
- ZodString: () => ZodString,
4777
- ZodSymbol: () => ZodSymbol,
4778
- ZodTransformer: () => ZodEffects,
4779
- ZodTuple: () => ZodTuple,
4780
- ZodType: () => ZodType,
4781
- ZodUndefined: () => ZodUndefined,
4782
- ZodUnion: () => ZodUnion,
4783
- ZodUnknown: () => ZodUnknown,
4784
- ZodVoid: () => ZodVoid,
4785
- addIssueToContext: () => addIssueToContext,
4786
- any: () => anyType,
4787
- array: () => arrayType,
4788
- bigint: () => bigIntType,
4789
- boolean: () => booleanType,
4790
- coerce: () => coerce,
4791
- custom: () => custom,
4792
- date: () => dateType,
4793
- datetimeRegex: () => datetimeRegex,
4794
- defaultErrorMap: () => en_default,
4795
- discriminatedUnion: () => discriminatedUnionType,
4796
- effect: () => effectsType,
4797
- enum: () => enumType,
4798
- function: () => functionType,
4799
- getErrorMap: () => getErrorMap,
4800
- getParsedType: () => getParsedType,
4801
- instanceof: () => instanceOfType,
4802
- intersection: () => intersectionType,
4803
- isAborted: () => isAborted,
4804
- isAsync: () => isAsync,
4805
- isDirty: () => isDirty,
4806
- isValid: () => isValid,
4807
- late: () => late,
4808
- lazy: () => lazyType,
4809
- literal: () => literalType,
4810
- makeIssue: () => makeIssue,
4811
- map: () => mapType,
4812
- nan: () => nanType,
4813
- nativeEnum: () => nativeEnumType,
4814
- never: () => neverType,
4815
- null: () => nullType,
4816
- nullable: () => nullableType,
4817
- number: () => numberType,
4818
- object: () => objectType,
4819
- objectUtil: () => objectUtil,
4820
- oboolean: () => oboolean,
4821
- onumber: () => onumber,
4822
- optional: () => optionalType,
4823
- ostring: () => ostring,
4824
- pipeline: () => pipelineType,
4825
- preprocess: () => preprocessType,
4826
- promise: () => promiseType,
4827
- quotelessJson: () => quotelessJson,
4828
- record: () => recordType,
4829
- set: () => setType,
4830
- setErrorMap: () => setErrorMap,
4831
- strictObject: () => strictObjectType,
4832
- string: () => stringType,
4833
- symbol: () => symbolType,
4834
- transformer: () => effectsType,
4835
- tuple: () => tupleType,
4836
- undefined: () => undefinedType,
4837
- union: () => unionType,
4838
- unknown: () => unknownType,
4732
+ void: () => voidType,
4839
4733
  util: () => util,
4840
- void: () => voidType
4734
+ unknown: () => unknownType,
4735
+ union: () => unionType,
4736
+ undefined: () => undefinedType,
4737
+ tuple: () => tupleType,
4738
+ transformer: () => effectsType,
4739
+ symbol: () => symbolType,
4740
+ string: () => stringType,
4741
+ strictObject: () => strictObjectType,
4742
+ setErrorMap: () => setErrorMap,
4743
+ set: () => setType,
4744
+ record: () => recordType,
4745
+ quotelessJson: () => quotelessJson,
4746
+ promise: () => promiseType,
4747
+ preprocess: () => preprocessType,
4748
+ pipeline: () => pipelineType,
4749
+ ostring: () => ostring,
4750
+ optional: () => optionalType,
4751
+ onumber: () => onumber,
4752
+ oboolean: () => oboolean,
4753
+ objectUtil: () => objectUtil,
4754
+ object: () => objectType,
4755
+ number: () => numberType,
4756
+ nullable: () => nullableType,
4757
+ null: () => nullType,
4758
+ never: () => neverType,
4759
+ nativeEnum: () => nativeEnumType,
4760
+ nan: () => nanType,
4761
+ map: () => mapType,
4762
+ makeIssue: () => makeIssue,
4763
+ literal: () => literalType,
4764
+ lazy: () => lazyType,
4765
+ late: () => late,
4766
+ isValid: () => isValid,
4767
+ isDirty: () => isDirty,
4768
+ isAsync: () => isAsync,
4769
+ isAborted: () => isAborted,
4770
+ intersection: () => intersectionType,
4771
+ instanceof: () => instanceOfType,
4772
+ getParsedType: () => getParsedType,
4773
+ getErrorMap: () => getErrorMap,
4774
+ function: () => functionType,
4775
+ enum: () => enumType,
4776
+ effect: () => effectsType,
4777
+ discriminatedUnion: () => discriminatedUnionType,
4778
+ defaultErrorMap: () => en_default,
4779
+ datetimeRegex: () => datetimeRegex,
4780
+ date: () => dateType,
4781
+ custom: () => custom,
4782
+ coerce: () => coerce,
4783
+ boolean: () => booleanType,
4784
+ bigint: () => bigIntType,
4785
+ array: () => arrayType,
4786
+ any: () => anyType,
4787
+ addIssueToContext: () => addIssueToContext,
4788
+ ZodVoid: () => ZodVoid,
4789
+ ZodUnknown: () => ZodUnknown,
4790
+ ZodUnion: () => ZodUnion,
4791
+ ZodUndefined: () => ZodUndefined,
4792
+ ZodType: () => ZodType,
4793
+ ZodTuple: () => ZodTuple,
4794
+ ZodTransformer: () => ZodEffects,
4795
+ ZodSymbol: () => ZodSymbol,
4796
+ ZodString: () => ZodString,
4797
+ ZodSet: () => ZodSet,
4798
+ ZodSchema: () => ZodType,
4799
+ ZodRecord: () => ZodRecord,
4800
+ ZodReadonly: () => ZodReadonly,
4801
+ ZodPromise: () => ZodPromise,
4802
+ ZodPipeline: () => ZodPipeline,
4803
+ ZodParsedType: () => ZodParsedType,
4804
+ ZodOptional: () => ZodOptional,
4805
+ ZodObject: () => ZodObject,
4806
+ ZodNumber: () => ZodNumber,
4807
+ ZodNullable: () => ZodNullable,
4808
+ ZodNull: () => ZodNull,
4809
+ ZodNever: () => ZodNever,
4810
+ ZodNativeEnum: () => ZodNativeEnum,
4811
+ ZodNaN: () => ZodNaN,
4812
+ ZodMap: () => ZodMap,
4813
+ ZodLiteral: () => ZodLiteral,
4814
+ ZodLazy: () => ZodLazy,
4815
+ ZodIssueCode: () => ZodIssueCode,
4816
+ ZodIntersection: () => ZodIntersection,
4817
+ ZodFunction: () => ZodFunction,
4818
+ ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind,
4819
+ ZodError: () => ZodError,
4820
+ ZodEnum: () => ZodEnum,
4821
+ ZodEffects: () => ZodEffects,
4822
+ ZodDiscriminatedUnion: () => ZodDiscriminatedUnion,
4823
+ ZodDefault: () => ZodDefault,
4824
+ ZodDate: () => ZodDate,
4825
+ ZodCatch: () => ZodCatch,
4826
+ ZodBranded: () => ZodBranded,
4827
+ ZodBoolean: () => ZodBoolean,
4828
+ ZodBigInt: () => ZodBigInt,
4829
+ ZodArray: () => ZodArray,
4830
+ ZodAny: () => ZodAny,
4831
+ Schema: () => ZodType,
4832
+ ParseStatus: () => ParseStatus,
4833
+ OK: () => OK,
4834
+ NEVER: () => NEVER,
4835
+ INVALID: () => INVALID,
4836
+ EMPTY_PATH: () => EMPTY_PATH,
4837
+ DIRTY: () => DIRTY,
4838
+ BRAND: () => BRAND
4841
4839
  });
4842
4840
 
4843
4841
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
@@ -9530,7 +9528,9 @@ function normalizeSkillsApiOrigin(apiUrl) {
9530
9528
  throw new SkillsFleetCredentialError("A Skills API URL must use HTTPS (or loopback HTTP), without credentials, query or fragment", "INVALID_API_URL");
9531
9529
  }
9532
9530
  const pathname = url.pathname.replace(/\/+$/, "");
9533
- if (pathname === "/api" || pathname === "/api/v1") {
9531
+ if (url.origin === "https://api.hasna.com" && pathname === "/skills/v1") {
9532
+ url.pathname = "/skills";
9533
+ } else if (pathname === "/api" || pathname === "/api/v1") {
9534
9534
  url.pathname = "/";
9535
9535
  } else if (pathname.endsWith("/api/v1")) {
9536
9536
  url.pathname = pathname.slice(0, -"/api/v1".length) || "/";
@@ -9539,6 +9539,21 @@ function normalizeSkillsApiOrigin(apiUrl) {
9539
9539
  }
9540
9540
  return url.toString().replace(/\/+$/, "");
9541
9541
  }
9542
+ function skillsApiRequestUrl(apiUrl, route) {
9543
+ const origin = normalizeSkillsApiOrigin(apiUrl);
9544
+ if (!route.startsWith("/api/") || route.includes("#") || route.includes("\\")) {
9545
+ throw new SkillsFleetCredentialError("Invalid Skills API route", "INVALID_API_URL");
9546
+ }
9547
+ if (origin === "https://api.hasna.com/skills") {
9548
+ if (route === "/api/auth/whoami")
9549
+ return `${origin}/v1/auth/whoami`;
9550
+ if (!route.startsWith("/api/v1/")) {
9551
+ throw new SkillsFleetCredentialError("The internal Skills gateway has no established login contract yet. Select an explicitly configured instance with supported authentication.", "GATEWAY_AUTH_UNAVAILABLE");
9552
+ }
9553
+ return `${origin}${route.slice("/api".length)}`;
9554
+ }
9555
+ return `${origin}${route}`;
9556
+ }
9542
9557
  function configuredSkillsApiUrl(env = process.env, keychain, profile) {
9543
9558
  const declared = SKILLS_API_URL_ENV_KEYS.filter((key) => env[key] !== undefined).map((key) => ({ key, value: env[key] }));
9544
9559
  for (const entry of declared) {
@@ -9891,7 +9906,14 @@ function getConfiguredApiUrl(env = process.env) {
9891
9906
  }
9892
9907
  function buildSkillsApiUrl(apiUrl, endpoint = "/skills") {
9893
9908
  const url = new URL(apiUrl);
9909
+ if (url.origin === "https://api.hasna.com" && /^\/skills\/(?:api\/)?v1\/skills\/?$/.test(url.pathname)) {
9910
+ url.pathname = "/skills";
9911
+ apiUrl = url.toString();
9912
+ }
9894
9913
  const cleanEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
9914
+ if (normalizeSkillsApiOrigin(apiUrl) === "https://api.hasna.com/skills") {
9915
+ return skillsApiRequestUrl(apiUrl, `/api/v1${cleanEndpoint}`);
9916
+ }
9895
9917
  const pathname = url.pathname.replace(/\/+$/, "");
9896
9918
  const apiBase = /\/api(?:\/v1)?\/skills$/.test(pathname) ? pathname.slice(0, -"/skills".length) : pathname;
9897
9919
  if (/\/api(?:\/v1)?$/.test(apiBase)) {
@@ -10992,8 +11014,17 @@ function creditCount(value) {
10992
11014
  }
10993
11015
  return value;
10994
11016
  }
11017
+ function runQuoteReceipt(value) {
11018
+ if (value === undefined)
11019
+ return;
11020
+ if (typeof value !== "string" || !value.length || Buffer.byteLength(value, "utf8") > 4096) {
11021
+ throw new Error("Invalid quote receipt");
11022
+ }
11023
+ return value;
11024
+ }
10995
11025
  function parseRemoteRunQuote(value) {
10996
11026
  const quote = object(value);
11027
+ runQuoteReceipt(quote.quoteReceipt);
10997
11028
  const pricing = object(quote.pricing);
10998
11029
  if (typeof quote.skill !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(quote.skill))
10999
11030
  throw new Error("Invalid quoted skill");
@@ -11278,7 +11309,7 @@ class RemoteSkillsClient {
11278
11309
  this.apiUrl = normalizeSkillsApiOrigin(apiUrl);
11279
11310
  }
11280
11311
  async request(path, options) {
11281
- return fetch(`${this.apiUrl}${path}`, {
11312
+ return fetch(skillsApiRequestUrl(this.apiUrl, path), {
11282
11313
  ...options,
11283
11314
  redirect: "error",
11284
11315
  credentials: "omit",
@@ -11338,6 +11369,7 @@ class RemoteSkillsClient {
11338
11369
  return { status: res.status, body };
11339
11370
  }
11340
11371
  async submitRun(slug, input, args, approval = {}) {
11372
+ const quoteReceipt = runQuoteReceipt(approval.quoteReceipt);
11341
11373
  if (approval.idempotencyKey !== undefined && !/^[A-Za-z0-9._:-]{1,128}$/.test(approval.idempotencyKey))
11342
11374
  throw new Error("Idempotency key must be 1-128 URL-safe characters");
11343
11375
  if (approval.maxCostCents !== undefined)
@@ -11354,15 +11386,20 @@ class RemoteSkillsClient {
11354
11386
  ...approval.maxCredits !== undefined ? { maxCredits: approval.maxCredits } : {},
11355
11387
  ...approval.maxCostCents !== undefined ? { maxCostCents: approval.maxCostCents } : {},
11356
11388
  ...approval.idempotencyKey !== undefined ? { idempotencyKey: approval.idempotencyKey } : {},
11357
- ...approval.inputFiles !== undefined ? { files: approval.inputFiles } : {}
11389
+ ...approval.inputFiles !== undefined ? { files: approval.inputFiles } : {},
11390
+ ...quoteReceipt !== undefined ? { quoteReceipt } : {}
11358
11391
  })
11359
11392
  });
11393
+ if (!res.ok) {
11394
+ res.body?.cancel().catch(() => {});
11395
+ throw new RemoteRequestError(`/api/v1/runs/${encodeURIComponent(slug)}`, res.status);
11396
+ }
11360
11397
  return normalizeRemoteSkillRunContract(await res.json(), slug);
11361
11398
  }
11362
- async quoteRun(slug, input = {}, args = []) {
11399
+ async quoteRun(slug, input = {}, args = [], files) {
11363
11400
  const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/quote`, {
11364
11401
  method: "POST",
11365
- body: JSON.stringify({ input, args })
11402
+ body: JSON.stringify({ input, args, ...files === undefined ? {} : { files } })
11366
11403
  }, { quoteRefusal: true });
11367
11404
  return parseRemoteRunQuote(await response.json());
11368
11405
  }
@@ -11378,17 +11415,24 @@ class RemoteSkillsClient {
11378
11415
  return this.capabilities;
11379
11416
  }
11380
11417
  async submitQuotedRun(slug, input = {}, args = [], approval = {}) {
11418
+ runQuoteReceipt(approval.quoteReceipt);
11419
+ ({ input, args, approval } = JSON.parse(JSON.stringify({ input, args, approval })));
11381
11420
  const maximum = creditCount(approval.maxCredits ?? approval.maxCostCents ?? 0);
11382
11421
  if (approval.maxCostCents !== undefined && approval.maxCostCents !== maximum)
11383
11422
  throw new Error("Credit approval fields disagree");
11384
- const quote = await this.quoteRun(slug, input, args);
11385
- if (quote.pricing.costCents > maximum)
11423
+ const quote = approval.quoteReceipt === undefined ? await this.quoteRun(slug, input, args, approval.inputFiles?.length ? approval.inputFiles : undefined) : undefined;
11424
+ if (quote && quote.pricing.costCents > maximum)
11386
11425
  throw new RemoteCreditApprovalError(quote.pricing.costCents, maximum);
11387
11426
  const capabilities = await this.getCapabilities();
11388
11427
  if (!capabilities.capabilities.includes("runs.submit") || capabilities.billing?.boundedRunApproval !== true || capabilities.billing.unit !== "credits") {
11389
11428
  throw new Error("The configured server does not support bounded credit approval; refusing remote submission");
11390
11429
  }
11391
- return this.submitRun(quote.skill, input, args, { ...approval, maxCredits: maximum, maxCostCents: maximum });
11430
+ return this.submitRun(quote?.skill ?? slug, input, args, {
11431
+ ...approval,
11432
+ maxCredits: maximum,
11433
+ maxCostCents: maximum,
11434
+ ...(quote?.quoteReceipt ?? approval.quoteReceipt) === undefined ? {} : { quoteReceipt: quote?.quoteReceipt ?? approval.quoteReceipt }
11435
+ });
11392
11436
  }
11393
11437
  async getIdentity() {
11394
11438
  return (await this.requestNewRoute("/api/auth/whoami")).json();
@@ -11676,6 +11720,10 @@ class RemoteSkillsClient {
11676
11720
  return { id: artifactId, fileName: String(artifact.fileName ?? artifactId), bytes, byteSize: bytes.byteLength, sha256: artifact.sha256 };
11677
11721
  }
11678
11722
  async submitQuotedRunWithFiles(slug, input, args, files, approval = {}) {
11723
+ runQuoteReceipt(approval.quoteReceipt);
11724
+ ({ input, args, approval } = JSON.parse(JSON.stringify({ input, args, approval })));
11725
+ describeRemoteFiles(files);
11726
+ files = files.map((file) => ({ name: file.name, contentType: file.contentType, bytes: new Uint8Array(file.bytes) }));
11679
11727
  const inputFiles = describeRemoteFiles(files);
11680
11728
  if (files.length && !(await this.getCapabilities()).capabilities.includes("runs.uploads"))
11681
11729
  throw new Error("The configured server does not support input uploads");
@@ -11739,7 +11787,7 @@ class RemoteSkillsClient {
11739
11787
  const headers = { Authorization: `Bearer ${this.apiKey}` };
11740
11788
  if (ifMatch)
11741
11789
  headers["If-Match"] = ifMatch;
11742
- return fetch(`${this.apiUrl}/api/v1/skills`, {
11790
+ return fetch(skillsApiRequestUrl(this.apiUrl, "/api/v1/skills"), {
11743
11791
  method: "POST",
11744
11792
  headers,
11745
11793
  body: form,
@@ -13288,7 +13336,7 @@ import { dirname as dirname7, relative as relative4 } from "path";
13288
13336
  // package.json
13289
13337
  var package_default = {
13290
13338
  name: "@hasna/skills",
13291
- version: "0.5.4",
13339
+ version: "0.5.5",
13292
13340
  description: "Skills library for AI coding agents",
13293
13341
  type: "module",
13294
13342
  bin: {
@@ -14022,7 +14070,7 @@ var toolContracts = [
14022
14070
  name: "run_skill",
14023
14071
  title: "Run Skill",
14024
14072
  description: "Run a skill locally or through a configured remote runner. Returns compact stdout/stderr previews and run summaries by default; pass detail:true for full records.",
14025
- params: ["name", "input?", "args?", "detail?", "remote?", "maxCredits?", "maxCostCents?", "idempotency_key?", "files?"],
14073
+ params: ["name", "input?", "args?", "detail?", "remote?", "maxCredits?", "maxCostCents?", "quoteReceipt?", "idempotency_key?", "files?"],
14026
14074
  category: "execution",
14027
14075
  sideEffects: "local-process-or-remote-run",
14028
14076
  stable: true,
@@ -14034,6 +14082,7 @@ var toolContracts = [
14034
14082
  remote: { type: "boolean", description: "Use the configured server catalog." },
14035
14083
  maxCredits: { type: "integer", minimum: 0, description: "Maximum explicitly approved integer credits; omitted permits only free remote runs." },
14036
14084
  maxCostCents: { type: "integer", minimum: 0, description: "Legacy alias for maxCredits; both must agree." },
14085
+ quoteReceipt: { type: "string", minLength: 1, maxLength: 4096, description: "Opaque approved quote receipt, at most 4096 UTF-8 bytes. Preserve it and the quoted input/args unchanged; never refresh after confirmation." },
14037
14086
  idempotency_key: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Stable retry key for the same remote submission." },
14038
14087
  files: { type: "array", maxItems: 10, items: objectSchema({ name: stringSchema("Safe basename"), base64: { type: "string", maxLength: 1398104 }, contentType: stringSchema("MIME type") }, ["name", "base64"]), description: "Inline remote inputs, at most 1 MiB combined." }
14039
14088
  }, ["name"]),
@@ -14317,12 +14366,17 @@ remoteCustomerContracts.push({
14317
14366
  name: "quote_skill",
14318
14367
  title: "Quote Remote Skill",
14319
14368
  description: "Get a server credit quote without submitting a run.",
14320
- params: ["name", "input?", "args?"],
14369
+ params: ["name", "input?", "args?", "files?"],
14321
14370
  category: "execution",
14322
14371
  sideEffects: "none",
14323
14372
  stable: true,
14324
- inputSchema: objectSchema({ name: skillNameInput, input: runInputSchema, args: runArgsSchema }, ["name"]),
14325
- outputSchema: objectSchema({ skill: stringSchema("Canonical server skill."), pricing: objectSchema({}, [], "Quoted integer credits.", true) }, ["skill", "pricing"], undefined, true)
14373
+ inputSchema: objectSchema({
14374
+ name: skillNameInput,
14375
+ input: runInputSchema,
14376
+ args: runArgsSchema,
14377
+ files: { type: "array", maxItems: 10, items: objectSchema({ name: stringSchema("Safe basename"), base64: { type: "string", maxLength: 1398104 }, contentType: stringSchema("MIME type") }, ["name", "base64"]), description: "Same inline files to submit after approval, at most 1 MiB combined." }
14378
+ }, ["name"]),
14379
+ outputSchema: objectSchema({ skill: stringSchema("Canonical server skill."), pricing: objectSchema({}, [], "Quoted integer credits.", true), quoteReceipt: { type: "string", minLength: 1, maxLength: 4096, description: "Opaque server quote binding, at most 4096 UTF-8 bytes; preserve verbatim for approval." } }, ["skill", "pricing"], undefined, true)
14326
14380
  });
14327
14381
  remoteCustomerContracts.push({
14328
14382
  name: "download_run_artifact",
@@ -14335,7 +14389,47 @@ remoteCustomerContracts.push({
14335
14389
  inputSchema: objectSchema({ run_id: stringSchema("Run identifier."), artifact_id: stringSchema("Artifact identifier.") }, ["run_id", "artifact_id"]),
14336
14390
  outputSchema: objectSchema({ id: stringSchema("Artifact identifier."), fileName: stringSchema("Artifact file name."), base64: stringSchema("Verified bytes."), sha256: stringSchema("SHA256 digest."), byteSize: { type: "integer", minimum: 0 } }, ["id", "fileName", "base64", "sha256", "byteSize"])
14337
14391
  });
14338
- var contracts = [...toolContracts, ...remoteCustomerContracts].sort((a, b) => a.name.localeCompare(b.name));
14392
+ var publicationUuidSchema = { type: "string", pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" };
14393
+ var publicationVerification = {
14394
+ email: { type: "string", format: "email", maxLength: 254 },
14395
+ code: { type: "string", pattern: "^\\d{6}$" },
14396
+ userId: publicationUuidSchema,
14397
+ membershipId: publicationUuidSchema,
14398
+ recoveryDirectory: { type: "string", maxLength: 4096, description: "Absolute host-local recovery directory without symbolic links." }
14399
+ };
14400
+ var privatePublicationContracts = [
14401
+ { name: "publish_private_skill", title: "Publish private skill", extras: {
14402
+ directory: { type: "string", maxLength: 4096, description: "Absolute local skill source directory." },
14403
+ skillId: publicationUuidSchema,
14404
+ expectedCurrentVersionId: { oneOf: [publicationUuidSchema, { type: "null" }] },
14405
+ idempotencyKey: publicationUuidSchema,
14406
+ confirm: { const: true },
14407
+ waitMs: { type: "integer", minimum: 0, maximum: 300000 }
14408
+ }, required: ["directory", "skillId", "expectedCurrentVersionId", "confirm"] },
14409
+ { name: "get_private_publication", title: "Get private publication", extras: {}, required: [] },
14410
+ { name: "resume_private_publication", title: "Resume private publication", extras: { confirm: { const: true }, waitMs: { type: "integer", minimum: 0, maximum: 300000 } }, required: ["confirm"] },
14411
+ { name: "cancel_private_publication", title: "Cancel private publication", extras: { confirm: { const: true } }, required: ["confirm"] }
14412
+ ].map((operation) => ({
14413
+ name: operation.name,
14414
+ title: operation.title,
14415
+ description: "Manage private source publication with fresh workspace verification and durable host-local recovery. Upload consent and current version comparison are explicit; private execution remains unavailable.",
14416
+ params: [...Object.keys(publicationVerification), ...Object.keys(operation.extras)],
14417
+ category: "storage",
14418
+ sideEffects: "filesystem",
14419
+ stable: true,
14420
+ inputSchema: objectSchema({ ...publicationVerification, ...operation.extras }, [...Object.keys(publicationVerification), ...operation.required]),
14421
+ outputSchema: objectSchema({
14422
+ recoveryDirectory: { type: "string" },
14423
+ skillId: publicationUuidSchema,
14424
+ intentId: { oneOf: [publicationUuidSchema, { type: "null" }] },
14425
+ state: { type: "string" },
14426
+ versionId: { oneOf: [publicationUuidSchema, { type: "null" }] },
14427
+ committed: { type: "boolean" },
14428
+ executionEnabled: { const: false },
14429
+ nextAction: { type: "string" }
14430
+ }, ["recoveryDirectory", "skillId", "intentId", "state", "versionId", "committed", "executionEnabled", "nextAction"])
14431
+ }));
14432
+ var contracts = [...toolContracts, ...remoteCustomerContracts, ...privatePublicationContracts].sort((a, b) => a.name.localeCompare(b.name));
14339
14433
  var resourceContracts = [
14340
14434
  {
14341
14435
  uri: "skills://mcp/contracts",
@@ -15822,7 +15916,7 @@ async function requestInvitationEmail(origin, action, input) {
15822
15916
  }
15823
15917
  const body = JSON.stringify({ invitationId: value.invitationId, token: value.token, challengeId: value.challengeId, ...action === "accept" ? { code: value.code } : {} });
15824
15918
  try {
15825
- const response = await fetch(`${target}/api/v1/account/invitations/email-${action}`, {
15919
+ const response = await fetch(skillsApiRequestUrl(target, `/api/v1/account/invitations/email-${action}`), {
15826
15920
  method: "POST",
15827
15921
  headers: { "Content-Type": "application/json" },
15828
15922
  body,
@@ -15853,35 +15947,342 @@ async function requestInvitationEmail(origin, action, input) {
15853
15947
  throw new RemoteInvitationEmailUnconfirmedError(action);
15854
15948
  }
15855
15949
 
15856
- // src/lib/remote-auth.ts
15857
- var MAX_ERROR_DETAIL_LENGTH = 200;
15950
+ // src/lib/remote-private-publications.ts
15951
+ import { createHash as createHash9 } from "crypto";
15952
+ var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
15953
+ var failures = {
15954
+ INVALID_REQUEST: [400, "The publication request is invalid."],
15955
+ SESSION_EXPIRED: [401, "Sign in again to manage this publication."],
15956
+ ACCOUNT_UNAVAILABLE: [403, "The account is unavailable."],
15957
+ INTERACTIVE_SESSION_REQUIRED: [403, "An interactive account session is required."],
15958
+ PUBLICATION_FORBIDDEN: [403, "This session cannot manage the publication."],
15959
+ PUBLICATION_ENTITLEMENT_REQUIRED: [403, "The workspace is not entitled to publish private skills."],
15960
+ PUBLICATION_UNAVAILABLE: [404, "The publication is unavailable to this session."],
15961
+ MANIFEST_NAME_MISMATCH: [409, "The manifest name does not match the selected skill."],
15962
+ IDEMPOTENCY_CONFLICT: [409, "This request key already identifies different publication bytes. Use the saved recovery directory."],
15963
+ CURRENT_VERSION_CHANGED: [409, "The current version changed. Inspect it before explicitly starting another publication."],
15964
+ VERSION_EXISTS: [409, "This version already exists."],
15965
+ VERSION_RESERVED: [409, "This version is reserved by another publication."],
15966
+ PUBLICATION_COMMITTED: [409, "The publication is already committed."],
15967
+ PUBLICATION_UPLOAD_UNAVAILABLE: [409, "This publication cannot receive another upload."],
15968
+ PUBLICATION_LIMIT: [429, "The workspace publication limit has been reached."],
15969
+ PUBLICATION_BUSY: [503, "The publication is busy. Reconcile the saved intent before retrying."],
15970
+ PUBLICATION_UNCERTAIN: [503, "The publication outcome is uncertain. Reconcile the saved intent."],
15971
+ PUBLICATION_CAPABILITY_UNAVAILABLE: [503, "Private publishing is not enabled on this server."],
15972
+ PUBLICATION_SIGNING_UNAVAILABLE: [503, "Upload authorization is temporarily unavailable. Keep the same intent."]
15973
+ };
15858
15974
 
15859
- class HostedApiError extends Error {
15860
- status;
15975
+ class PrivatePublicationError extends Error {
15861
15976
  code;
15862
- detail;
15863
- endpoint;
15864
- apiUrl;
15865
- constructor(message, options = {}) {
15977
+ uncertain;
15978
+ status;
15979
+ constructor(code, message, uncertain = false, status) {
15866
15980
  super(message);
15867
- this.name = "HostedApiError";
15868
- this.status = options.status;
15869
- this.code = options.code;
15870
- this.detail = options.detail;
15871
- this.endpoint = options.endpoint;
15872
- this.apiUrl = options.apiUrl;
15981
+ this.code = code;
15982
+ this.uncertain = uncertain;
15983
+ this.status = status;
15984
+ this.name = "PrivatePublicationError";
15873
15985
  }
15874
15986
  }
15875
- async function requestAuthApi(instance, path, options) {
15876
- const url = normalizeSkillsApiOrigin(instance);
15877
- const safeUrl = url;
15878
- const endpoint = `${(options?.method || "GET").toUpperCase()} ${safeUrl}${path}`;
15879
- let res;
15987
+ var bad = () => {
15988
+ throw new PrivatePublicationError("INVALID_PUBLICATION_INPUT", "Invalid publication input or recovery data.");
15989
+ };
15990
+ var invalid3 = () => {
15991
+ throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
15992
+ };
15993
+ var publicationUuid = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v);
15994
+ var hash = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
15995
+ var record5 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
15996
+ var exact = (v, keys) => record5(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
15997
+ var date = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
15998
+ var publicationSha256 = (bytes) => createHash9("sha256").update(bytes).digest("hex");
15999
+ function checkedPublicationDeclaration(value) {
16000
+ if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
16001
+ return bad();
15880
16002
  try {
15881
- res = await fetch(`${url}${path}`, {
15882
- ...options,
15883
- redirect: "error",
15884
- signal: options?.signal ?? AbortSignal.timeout(15000),
16003
+ const manifest = JSON.parse(value.manifestText);
16004
+ if (!record5(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !hash(manifest.provenance?.content_hash))
16005
+ return bad();
16006
+ } catch {
16007
+ return bad();
16008
+ }
16009
+ return Object.freeze({ ...value });
16010
+ }
16011
+ function checkedPublicationView(value, skillId, intentId, declaration) {
16012
+ if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !hash(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES || typeof value.state !== "string" || !["awaiting_upload", "queued", "verifying", "needs_attention", "committed", "rejected", "cancelled", "expired"].includes(value.state) || !date(value.expiresAt) || !date(value.createdAt) || !(value.versionId === null || publicationUuid(value.versionId)) || value.state === "committed" && value.versionId === null)
16013
+ return invalid3();
16014
+ if (declaration && ["version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize"].some((k) => value[k] !== declaration[k]))
16015
+ return invalid3();
16016
+ return Object.freeze({ ...value });
16017
+ }
16018
+ async function boundedJson(url, init, token, mutation, budget = 15000, signal) {
16019
+ const controller = new AbortController;
16020
+ let reader;
16021
+ let response;
16022
+ const timeout = new PrivatePublicationError("PUBLICATION_UNCONFIRMED", "No confirmed publication result. Inspect the saved intent before retrying.", mutation);
16023
+ let timer;
16024
+ let abort;
16025
+ try {
16026
+ return await Promise.race([(async () => {
16027
+ if (signal?.aborted)
16028
+ throw timeout;
16029
+ response = await fetch(url, {
16030
+ ...init,
16031
+ redirect: "error",
16032
+ credentials: "omit",
16033
+ signal: controller.signal,
16034
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }
16035
+ });
16036
+ const length = response.headers.get("content-length");
16037
+ if (length !== null && (!/^\d+$/.test(length) || Number(length) > 65536))
16038
+ return invalid3();
16039
+ reader = response.body?.getReader();
16040
+ const chunks = [];
16041
+ let size = 0;
16042
+ if (reader)
16043
+ while (true) {
16044
+ const part = await reader.read();
16045
+ if (part.done)
16046
+ break;
16047
+ size += part.value.byteLength;
16048
+ if (size > 65536)
16049
+ return invalid3();
16050
+ chunks.push(part.value);
16051
+ }
16052
+ if (controller.signal.aborted)
16053
+ throw timeout;
16054
+ const bytes = Buffer.concat(chunks);
16055
+ let body;
16056
+ try {
16057
+ body = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
16058
+ } catch {
16059
+ return invalid3();
16060
+ }
16061
+ if (!response.ok) {
16062
+ const code = record5(body) && typeof body.code === "string" && Object.hasOwn(failures, body.code) ? body.code : null;
16063
+ if (code && failures[code][0] === response.status)
16064
+ throw new PrivatePublicationError(code, failures[code][1], mutation && code === "PUBLICATION_UNCERTAIN", response.status);
16065
+ throw new PrivatePublicationError("PUBLICATION_REQUEST_FAILED", "The publication request was refused. Inspect its status before retrying.", mutation && response.status >= 500, response.status);
16066
+ }
16067
+ return body;
16068
+ })(), new Promise((_, reject) => {
16069
+ abort = () => {
16070
+ controller.abort();
16071
+ reject(timeout);
16072
+ };
16073
+ timer = setTimeout(abort, Math.max(1, Math.min(15000, budget)));
16074
+ signal?.addEventListener("abort", abort, { once: true });
16075
+ })]);
16076
+ } catch (error) {
16077
+ if (error instanceof PrivatePublicationError) {
16078
+ if (mutation && error.code === "INVALID_PUBLICATION_RESPONSE")
16079
+ throw timeout;
16080
+ throw error;
16081
+ }
16082
+ throw timeout;
16083
+ } finally {
16084
+ if (timer)
16085
+ clearTimeout(timer);
16086
+ if (abort)
16087
+ signal?.removeEventListener("abort", abort);
16088
+ controller.abort();
16089
+ if (reader)
16090
+ reader.cancel().catch(() => {});
16091
+ else
16092
+ response?.body?.cancel().catch(() => {});
16093
+ }
16094
+ }
16095
+
16096
+ class RemotePrivatePublicationsClient {
16097
+ apiOrigin;
16098
+ organizationId;
16099
+ userId;
16100
+ membershipId;
16101
+ #token;
16102
+ constructor(apiUrl, session) {
16103
+ const checked = parseWorkspaceSession(session, { userId: session?.user?.id, membershipId: session?.user?.membershipId });
16104
+ if (checked.user.role === "viewer")
16105
+ throw new PrivatePublicationError("PUBLICATION_FORBIDDEN", failures.PUBLICATION_FORBIDDEN[1]);
16106
+ this.apiOrigin = normalizeSkillsApiOrigin(apiUrl);
16107
+ this.#token = checked.token;
16108
+ this.organizationId = checked.organization.id;
16109
+ this.userId = checked.user.id;
16110
+ this.membershipId = checked.user.membershipId;
16111
+ Object.freeze(this);
16112
+ }
16113
+ async getCapability(options = {}) {
16114
+ if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
16115
+ return bad();
16116
+ const response = await boundedJson(skillsApiRequestUrl(this.apiOrigin, "/api/v1/capabilities"), {}, this.#token, false, options.timeoutMs, options.signal);
16117
+ const p = record5(response) && response.privatePublishing;
16118
+ if (!record5(response) || response.contractVersion !== 1 || response.apiVersion !== 1 || !exact(p, ["contractVersion", "enabled", "authentication", "maxArchiveBytes", "uploadMaxTtlSeconds", "executionEnabled"]) || p.contractVersion !== 1 || typeof p.enabled !== "boolean" || p.authentication !== "interactive-session" || p.maxArchiveBytes !== PRIVATE_PUBLICATION_MAX_BYTES || p.uploadMaxTtlSeconds !== 300 || p.executionEnabled !== false)
16119
+ throw new PrivatePublicationError("PUBLICATION_CONTRACT_UNAVAILABLE", "This server does not support the hosted private publication contract.");
16120
+ return Object.freeze({ ...p });
16121
+ }
16122
+ async#gate(enabled, options = {}) {
16123
+ if (!(await this.getCapability(options)).enabled && enabled)
16124
+ throw new PrivatePublicationError("PUBLICATION_CAPABILITY_UNAVAILABLE", failures.PUBLICATION_CAPABILITY_UNAVAILABLE[1]);
16125
+ }
16126
+ #path(skillId, intentId) {
16127
+ if (!publicationUuid(skillId) || intentId !== undefined && !publicationUuid(intentId))
16128
+ return bad();
16129
+ return skillsApiRequestUrl(this.apiOrigin, `/api/v1/skills/${skillId}/publication-uploads${intentId ? `/${intentId}` : ""}`);
16130
+ }
16131
+ async#view(path, method, skillId, intentId, declaration, options = {}) {
16132
+ const value = await boundedJson(path, { method, ...method === "GET" ? {} : { body: JSON.stringify(declaration ?? {}) } }, this.#token, method !== "GET", options.timeoutMs, options.signal);
16133
+ try {
16134
+ if (!record5(value) || !Object.keys(value).every((k) => k === "upload" || k === "changed") || value.changed !== undefined && typeof value.changed !== "boolean")
16135
+ return invalid3();
16136
+ return checkedPublicationView(value.upload, skillId, intentId, declaration);
16137
+ } catch (error) {
16138
+ if (method !== "GET")
16139
+ throw new PrivatePublicationError("PUBLICATION_UNCONFIRMED", "The publication result could not be confirmed. Reconcile the saved intent before another action.", true);
16140
+ throw error;
16141
+ }
16142
+ }
16143
+ async begin(skillId, input) {
16144
+ const path = this.#path(skillId), declaration = checkedPublicationDeclaration(input);
16145
+ await this.#gate(true);
16146
+ return this.#view(path, "POST", skillId, undefined, declaration);
16147
+ }
16148
+ async get(skillId, intentId, options = {}) {
16149
+ const path = this.#path(skillId, intentId), until = Date.now() + (options.timeoutMs ?? 15000);
16150
+ await this.#gate(false, options);
16151
+ return this.#view(path, "GET", skillId, intentId, undefined, { ...options, timeoutMs: Math.max(1, until - Date.now()) });
16152
+ }
16153
+ async finalize(skillId, intentId) {
16154
+ const path = this.#path(skillId, intentId);
16155
+ await this.#gate(true);
16156
+ return this.#view(`${path}/finalize`, "POST", skillId, intentId);
16157
+ }
16158
+ async cancel(skillId, intentId) {
16159
+ const path = this.#path(skillId, intentId);
16160
+ await this.#gate(false);
16161
+ return this.#view(path, "DELETE", skillId, intentId);
16162
+ }
16163
+ async upload(skillId, intent, bytes) {
16164
+ const captured = checkedPublicationView(intent, skillId), path = this.#path(skillId, captured.id);
16165
+ if (!(bytes instanceof Uint8Array) || bytes.byteLength !== captured.archiveByteSize)
16166
+ return bad();
16167
+ const owned = Buffer.from(bytes);
16168
+ if (captured.state !== "awaiting_upload" || owned.byteLength !== captured.archiveByteSize || publicationSha256(owned) !== captured.archiveSha256)
16169
+ return bad();
16170
+ await this.#gate(true);
16171
+ const value = await boundedJson(`${path}/upload-url`, { method: "POST", body: "{}" }, this.#token, false);
16172
+ const upload = this.#upload(value, captured);
16173
+ const controller = new AbortController;
16174
+ let timer;
16175
+ const uncertain = () => new PrivatePublicationError("PUBLICATION_UPLOAD_UNCONFIRMED", "Upload acceptance is uncertain. Resume this saved intent to finalize and inspect it; do not upload again.", true);
16176
+ try {
16177
+ await Promise.race([(async () => {
16178
+ const response = await fetch(upload.uploadUrl, { method: "PUT", headers: upload.headers, body: owned, redirect: "error", credentials: "omit", signal: controller.signal });
16179
+ response.body?.cancel().catch(() => {});
16180
+ if (response.status !== 200 || controller.signal.aborted)
16181
+ throw uncertain();
16182
+ })(), new Promise((_, reject) => {
16183
+ timer = setTimeout(() => {
16184
+ controller.abort();
16185
+ reject(uncertain());
16186
+ }, 30000);
16187
+ })]);
16188
+ } catch {
16189
+ throw uncertain();
16190
+ } finally {
16191
+ if (timer)
16192
+ clearTimeout(timer);
16193
+ controller.abort();
16194
+ }
16195
+ }
16196
+ #upload(value, intent) {
16197
+ if (!exact(value, ["upload"]) || !exact(value.upload, ["method", "uploadUrl", "headers", "expiresAt"]))
16198
+ return invalid3();
16199
+ const p = value.upload;
16200
+ if (p.method !== "PUT" || typeof p.uploadUrl !== "string" || p.uploadUrl.length > 8192 || /[\x00-\x20\x7f]/.test(p.uploadUrl) || !date(p.expiresAt) || Date.parse(p.expiresAt) - Date.now() < 1000 || Date.parse(p.expiresAt) - Date.now() > 300000 || Date.parse(p.expiresAt) > Date.parse(intent.expiresAt) || !exact(p.headers, ["content-type", "content-length", "x-amz-checksum-sha256", "x-amz-expected-bucket-owner"]) || p.headers["content-type"] !== "application/gzip" || p.headers["content-length"] !== String(intent.archiveByteSize) || p.headers["x-amz-checksum-sha256"] !== Buffer.from(intent.archiveSha256, "hex").toString("base64") || typeof p.headers["x-amz-expected-bucket-owner"] !== "string" || !/^\d{12}$/.test(p.headers["x-amz-expected-bucket-owner"]))
16201
+ return invalid3();
16202
+ let url;
16203
+ try {
16204
+ url = new URL(p.uploadUrl);
16205
+ } catch {
16206
+ return invalid3();
16207
+ }
16208
+ if (url.protocol !== "https:" || url.port || url.username || url.password || url.hash || !/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]\.s3\.[a-z]{2}(?:-[a-z]+)+-[1-9]\.amazonaws\.com$/.test(url.hostname) || url.pathname !== `/private-publication-staging/${this.organizationId}/${intent.id}/bundle.tgz` || !url.search || url.searchParams.get("X-Amz-Algorithm") !== "AWS4-HMAC-SHA256" || url.searchParams.get("X-Amz-SignedHeaders") !== "content-length;content-type;host;x-amz-checksum-sha256;x-amz-expected-bucket-owner" || !/^[a-f0-9]{64}$/.test(url.searchParams.get("X-Amz-Signature") ?? ""))
16209
+ return invalid3();
16210
+ const queryKeys = ["X-Amz-Algorithm", "X-Amz-Credential", "X-Amz-Date", "X-Amz-Expires", "X-Amz-Security-Token", "X-Amz-Signature", "X-Amz-SignedHeaders"];
16211
+ if ([...url.searchParams.keys()].sort().join(",") !== queryKeys.sort().join(","))
16212
+ return invalid3();
16213
+ const issued = url.searchParams.get("X-Amz-Date"), ttl = url.searchParams.get("X-Amz-Expires"), credential = url.searchParams.get("X-Amz-Credential");
16214
+ const timestamp3 = /^(\d{4})(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)Z$/.exec(issued);
16215
+ const region = url.hostname.split(".s3.")[1].split(".amazonaws.com")[0];
16216
+ if (!timestamp3 || !/^[1-9]\d{0,2}$/.test(ttl) || Number(ttl) > 300 || !/^[A-Z0-9]{16,128}\//.test(credential) || credential.split("/").slice(1).join("/") !== `${issued.slice(0, 8)}/${region}/s3/aws4_request` || !/^[\x21-\x7e]{1,4096}$/.test(url.searchParams.get("X-Amz-Security-Token")))
16217
+ return invalid3();
16218
+ const issuedAt = Date.parse(`${timestamp3[1]}-${timestamp3[2]}-${timestamp3[3]}T${timestamp3[4]}:${timestamp3[5]}:${timestamp3[6]}Z`);
16219
+ if (!Number.isFinite(issuedAt) || issuedAt > Date.now() + 1000 || issuedAt + Number(ttl) * 1000 !== Date.parse(p.expiresAt))
16220
+ return invalid3();
16221
+ return { method: "PUT", uploadUrl: url.href, expiresAt: p.expiresAt, headers: Object.freeze({ ...p.headers }) };
16222
+ }
16223
+ async wait(skillId, intentId, options = {}) {
16224
+ const timeout = options.timeoutMs ?? 60000;
16225
+ if (!Number.isSafeInteger(timeout) || timeout < 0 || timeout > 300000)
16226
+ return bad();
16227
+ const until = Date.now() + timeout;
16228
+ let previous;
16229
+ while (true) {
16230
+ if (options.signal?.aborted)
16231
+ throw new PrivatePublicationError("PUBLICATION_WAIT_ABORTED", "Stopped waiting. The server publication continues; inspect the saved intent.");
16232
+ let view;
16233
+ try {
16234
+ view = await this.get(skillId, intentId, { timeoutMs: timeout === 0 ? 15000 : Math.max(1, Math.min(15000, until - Date.now())), signal: options.signal });
16235
+ } catch (error) {
16236
+ if (previous && Date.now() >= until && !options.signal?.aborted)
16237
+ return previous;
16238
+ throw error;
16239
+ }
16240
+ previous = view;
16241
+ if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
16242
+ return view;
16243
+ await new Promise((resolve4) => {
16244
+ const timer = setTimeout(done, Math.min(1000, until - Date.now()));
16245
+ function done() {
16246
+ clearTimeout(timer);
16247
+ options.signal?.removeEventListener("abort", done);
16248
+ resolve4();
16249
+ }
16250
+ options.signal?.addEventListener("abort", done, { once: true });
16251
+ });
16252
+ }
16253
+ }
16254
+ }
16255
+
16256
+ // src/lib/remote-auth.ts
16257
+ var MAX_ERROR_DETAIL_LENGTH = 200;
16258
+
16259
+ class HostedApiError extends Error {
16260
+ status;
16261
+ code;
16262
+ detail;
16263
+ endpoint;
16264
+ apiUrl;
16265
+ constructor(message, options = {}) {
16266
+ super(message);
16267
+ this.name = "HostedApiError";
16268
+ this.status = options.status;
16269
+ this.code = options.code;
16270
+ this.detail = options.detail;
16271
+ this.endpoint = options.endpoint;
16272
+ this.apiUrl = options.apiUrl;
16273
+ }
16274
+ }
16275
+ async function requestAuthApi(instance, path, options) {
16276
+ const url = normalizeSkillsApiOrigin(instance);
16277
+ const safeUrl = url;
16278
+ const requestUrl = skillsApiRequestUrl(url, path);
16279
+ const endpoint = `${(options?.method || "GET").toUpperCase()} ${requestUrl}`;
16280
+ let res;
16281
+ try {
16282
+ res = await fetch(requestUrl, {
16283
+ ...options,
16284
+ redirect: "error",
16285
+ signal: options?.signal ?? AbortSignal.timeout(15000),
15885
16286
  headers: { "Content-Type": "application/json", ...options?.headers }
15886
16287
  });
15887
16288
  } catch (err) {
@@ -15893,10 +16294,10 @@ async function requestAuthApi(instance, path, options) {
15893
16294
  const text2 = await res.text();
15894
16295
  const body = text2 ? parseJsonBody(text2) : {};
15895
16296
  if (!res.ok) {
15896
- const record5 = isRecord5(body) ? body : {};
15897
- const detail = typeof record5.detail === "string" ? record5.detail : undefined;
15898
- const error = typeof record5.error === "string" ? record5.error : undefined;
15899
- const code = typeof record5.code === "string" ? record5.code : undefined;
16297
+ const record6 = isRecord5(body) ? body : {};
16298
+ const detail = typeof record6.detail === "string" ? record6.detail : undefined;
16299
+ const error = typeof record6.error === "string" ? record6.error : undefined;
16300
+ const code = typeof record6.code === "string" ? record6.code : undefined;
15900
16301
  throw new HostedApiError(detail || error || `${res.status} ${res.statusText}`, {
15901
16302
  status: res.status,
15902
16303
  code,
@@ -15930,6 +16331,10 @@ class RemoteSkillsAuthClient {
15930
16331
  constructor(apiUrl) {
15931
16332
  this.apiOrigin = normalizeSkillsApiOrigin(apiUrl);
15932
16333
  }
16334
+ async openPrivatePublications(email2, code, context) {
16335
+ const origin = this.apiOrigin, captured = workspaceContext(context);
16336
+ return new RemotePrivatePublicationsClient(origin, await this.switchWorkspace(email2, code, captured));
16337
+ }
15933
16338
  requestInvitationEmailChallenge(input) {
15934
16339
  return requestInvitationEmail(this.apiOrigin, "challenge", input);
15935
16340
  }
@@ -15977,9 +16382,10 @@ class RemoteSkillsAuthClient {
15977
16382
  const apiOrigin = this.apiOrigin;
15978
16383
  if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
15979
16384
  throw new Error("Fresh email and six-digit verification code are required to manage this account");
16385
+ const requestUrl = skillsApiRequestUrl(apiOrigin, "/api/auth/verify");
15980
16386
  let response;
15981
16387
  try {
15982
- response = await fetch(`${apiOrigin}/api/auth/verify`, {
16388
+ response = await fetch(requestUrl, {
15983
16389
  method: "POST",
15984
16390
  redirect: "error",
15985
16391
  credentials: "omit",
@@ -16070,258 +16476,501 @@ class RemoteSkillsAuthClient {
16070
16476
  return requestAuthApi(this.apiOrigin, path, options);
16071
16477
  }
16072
16478
  }
16479
+ // src/lib/private-publication-recovery.ts
16480
+ import { constants as constants2, closeSync as closeSync3, fsyncSync, fstatSync as fstatSync3, lstatSync as lstatSync5, mkdirSync as mkdirSync14, openSync as openSync3, readSync as readSync2, realpathSync as realpathSync2, renameSync as renameSync4, unlinkSync, writeFileSync as writeFileSync13 } from "fs";
16481
+ import { dirname as dirname12, isAbsolute as isAbsolute5, join as join22, resolve as resolve4 } from "path";
16482
+ import { randomUUID } from "crypto";
16483
+ var fail2 = () => {
16484
+ throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
16485
+ };
16486
+ function safeDirectory(directory) {
16487
+ if (!isAbsolute5(directory) || directory !== resolve4(directory) || realpathSync2(directory) !== directory)
16488
+ return fail2();
16489
+ for (let path = directory;; path = dirname12(path)) {
16490
+ const stat = lstatSync5(path);
16491
+ if (!stat.isDirectory() || stat.isSymbolicLink())
16492
+ return fail2();
16493
+ if (path === dirname12(path))
16494
+ break;
16495
+ }
16496
+ const own = lstatSync5(directory);
16497
+ if ((own.mode & 63) !== 0 || process.getuid && own.uid !== process.getuid())
16498
+ return fail2();
16499
+ return { dev: own.dev, ino: own.ino };
16500
+ }
16501
+ function unchangedDirectory(directory, identity) {
16502
+ const now = safeDirectory(directory);
16503
+ if (now.dev !== identity.dev || now.ino !== identity.ino)
16504
+ return fail2();
16505
+ }
16506
+ function readOwned(directory, name, max) {
16507
+ const identity = safeDirectory(directory), file = join22(directory, name);
16508
+ const fd = openSync3(file, constants2.O_RDONLY | constants2.O_NOFOLLOW);
16509
+ try {
16510
+ const stat = fstatSync3(fd);
16511
+ if (!stat.isFile() || stat.nlink !== 1 || stat.size > max || stat.size < 1 || (stat.mode & 63) !== 0 || process.getuid && stat.uid !== process.getuid())
16512
+ return fail2();
16513
+ const buffer = Buffer.alloc(Math.min(max, stat.size) + 1);
16514
+ let length = 0;
16515
+ while (length < buffer.length) {
16516
+ const count = readSync2(fd, buffer, length, buffer.length - length, length);
16517
+ if (count === 0)
16518
+ break;
16519
+ length += count;
16520
+ }
16521
+ const bytes = buffer.subarray(0, length), after = fstatSync3(fd);
16522
+ unchangedDirectory(directory, identity);
16523
+ if (bytes.length !== stat.size || stat.size !== after.size || stat.mtimeMs !== after.mtimeMs || stat.ctimeMs !== after.ctimeMs)
16524
+ return fail2();
16525
+ return bytes;
16526
+ } finally {
16527
+ closeSync3(fd);
16528
+ }
16529
+ }
16530
+ function writeOwned(directory, name, bytes) {
16531
+ const fd = openSync3(join22(directory, name), constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
16532
+ try {
16533
+ writeFileSync13(fd, bytes);
16534
+ fsyncSync(fd);
16535
+ } finally {
16536
+ closeSync3(fd);
16537
+ }
16538
+ }
16539
+ function save(directory, value) {
16540
+ const identity = safeDirectory(directory), temporary = `.receipt-${randomUUID()}.json`;
16541
+ writeOwned(directory, temporary, JSON.stringify(value) + `
16542
+ `);
16543
+ unchangedDirectory(directory, identity);
16544
+ renameSync4(join22(directory, temporary), join22(directory, "receipt.json"));
16545
+ const fd = openSync3(directory, constants2.O_RDONLY | constants2.O_NOFOLLOW);
16546
+ try {
16547
+ fsyncSync(fd);
16548
+ } finally {
16549
+ closeSync3(fd);
16550
+ }
16551
+ }
16552
+ function bind(client, receipt) {
16553
+ if (["apiOrigin", "organizationId", "userId", "membershipId"].some((k) => client[k] !== receipt[k]))
16554
+ throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
16555
+ }
16556
+ async function locked(directory, action) {
16557
+ const identity = safeDirectory(directory), file = join22(directory, "operation.lock");
16558
+ let fd;
16559
+ try {
16560
+ fd = openSync3(file, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
16561
+ } catch {
16562
+ throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
16563
+ }
16564
+ const lock = fstatSync3(fd);
16565
+ try {
16566
+ writeFileSync13(fd, JSON.stringify({ pid: process.pid }) + `
16567
+ `);
16568
+ fsyncSync(fd);
16569
+ return await action();
16570
+ } finally {
16571
+ closeSync3(fd);
16572
+ unchangedDirectory(directory, identity);
16573
+ const now = lstatSync5(file);
16574
+ if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
16575
+ fail2();
16576
+ unlinkSync(file);
16577
+ }
16578
+ }
16579
+ function readPrivatePublicationRecovery(directory) {
16580
+ try {
16581
+ const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(readOwned(directory, "receipt.json", 65536)));
16582
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== ["contractVersion", "apiOrigin", "organizationId", "userId", "membershipId", "skillId", "declaration", "phase", "intent"].sort().join(",") || value.contractVersion !== 1 || ![value.organizationId, value.userId, value.membershipId, value.skillId].every(publicationUuid) || typeof value.apiOrigin !== "string" || normalizeSkillsApiOrigin(value.apiOrigin) !== value.apiOrigin || !["prepared", "begin_uncertain", "awaiting_upload", "upload_uncertain", "uploaded", "finalize_uncertain", "observed"].includes(value.phase))
16583
+ return fail2();
16584
+ value.declaration = checkedPublicationDeclaration(value.declaration);
16585
+ if (value.intent !== null)
16586
+ value.intent = checkedPublicationView(value.intent, value.skillId, undefined, value.declaration);
16587
+ if (value.intent === null && !["prepared", "begin_uncertain"].includes(value.phase))
16588
+ return fail2();
16589
+ const bytes = readOwned(directory, "bundle.tgz", PRIVATE_PUBLICATION_MAX_BYTES);
16590
+ if (bytes.length !== value.declaration.archiveByteSize || publicationSha256(bytes) !== value.declaration.archiveSha256)
16591
+ return fail2();
16592
+ return { receipt: value, bytes };
16593
+ } catch {
16594
+ return fail2();
16595
+ }
16596
+ }
16597
+ async function preparePrivatePublication(client, sourceDirectory, recoveryDirectory, input) {
16598
+ if (!publicationUuid(input.skillId) || !(input.expectedCurrentVersionId === null || publicationUuid(input.expectedCurrentVersionId)) || input.idempotencyKey !== undefined && !publicationUuid(input.idempotencyKey))
16599
+ return fail2();
16600
+ const packed = packSkillBundle(sourceDirectory, { maxUnpackedBytes: 32 * 1024 * 1024 });
16601
+ const inspected = await inspectSkillBundle(packed.bytes, { limits: { compressedBytes: PRIVATE_PUBLICATION_MAX_BYTES } });
16602
+ const manifest = inspected.entries.find((entry) => entry.path === "skill.json");
16603
+ if (!manifest || manifest.bytes.length > 16384 || !(await verifyContentHashFromEntries(inspected.entries)).valid)
16604
+ throw new PrivatePublicationError("PUBLICATION_BUNDLE_INVALID", "The skill must have a valid skill.json with its current content hash. Validate the skill before publishing.");
16605
+ const manifestText = new TextDecoder("utf-8", { fatal: true }).decode(manifest.bytes);
16606
+ const declaration = checkedPublicationDeclaration({
16607
+ idempotencyKey: input.idempotencyKey ?? randomUUID(),
16608
+ version: JSON.parse(manifestText).version,
16609
+ expectedCurrentVersionId: input.expectedCurrentVersionId,
16610
+ manifestText,
16611
+ archiveSha256: inspected.sha256,
16612
+ archiveByteSize: packed.bytes.length
16613
+ });
16614
+ const receipt = {
16615
+ contractVersion: 1,
16616
+ apiOrigin: client.apiOrigin,
16617
+ organizationId: client.organizationId,
16618
+ userId: client.userId,
16619
+ membershipId: client.membershipId,
16620
+ skillId: input.skillId,
16621
+ declaration,
16622
+ phase: "prepared",
16623
+ intent: null
16624
+ };
16625
+ if (!isAbsolute5(recoveryDirectory) || resolve4(recoveryDirectory) !== recoveryDirectory || realpathSync2(dirname12(recoveryDirectory)) !== dirname12(recoveryDirectory))
16626
+ return fail2();
16627
+ mkdirSync14(recoveryDirectory, { mode: 448 });
16628
+ safeDirectory(recoveryDirectory);
16629
+ const parent = openSync3(dirname12(recoveryDirectory), constants2.O_RDONLY | constants2.O_NOFOLLOW);
16630
+ try {
16631
+ fsyncSync(parent);
16632
+ } finally {
16633
+ closeSync3(parent);
16634
+ }
16635
+ writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
16636
+ save(recoveryDirectory, receipt);
16637
+ return receipt;
16638
+ }
16639
+ function privatePublicationResult(directory, receipt) {
16640
+ const state = receipt.intent?.state ?? receipt.phase, committed = state === "committed";
16641
+ const nextAction = committed ? "The version is published. Private execution remains unavailable." : ["rejected", "cancelled", "expired"].includes(state) ? "This intent is terminal. Inspect the result before explicitly preparing another version." : state === "needs_attention" ? "Keep this intent and contact the service operator; do not create a replacement or upload again." : receipt.phase === "upload_uncertain" ? "Run publication resume with this recovery directory to finalize the same intent without another upload." : receipt.intent ? "Run publication status or resume with this recovery directory; cancel explicitly if you want to stop." : "Run publication resume with this recovery directory to reconcile the identical request key and declaration.";
16642
+ return {
16643
+ recoveryDirectory: directory,
16644
+ skillId: receipt.skillId,
16645
+ intentId: receipt.intent?.id ?? null,
16646
+ state,
16647
+ versionId: receipt.intent?.versionId ?? null,
16648
+ committed,
16649
+ executionEnabled: false,
16650
+ nextAction
16651
+ };
16652
+ }
16653
+ async function continuePrivatePublication(client, directory, options) {
16654
+ if (options.confirm !== true)
16655
+ throw new PrivatePublicationError("PUBLICATION_CONFIRM_REQUIRED", "Explicit upload confirmation is required.");
16656
+ if (options.waitMs !== undefined && (!Number.isSafeInteger(options.waitMs) || options.waitMs < 0 || options.waitMs > 300000))
16657
+ return fail2();
16658
+ return locked(directory, () => continueLocked(client, directory, options));
16659
+ }
16660
+ async function continueLocked(client, directory, options) {
16661
+ const { receipt, bytes } = readPrivatePublicationRecovery(directory);
16662
+ bind(client, receipt);
16663
+ if (!receipt.intent) {
16664
+ receipt.phase = "begin_uncertain";
16665
+ save(directory, receipt);
16666
+ receipt.intent = await client.begin(receipt.skillId, receipt.declaration);
16667
+ receipt.phase = "awaiting_upload";
16668
+ save(directory, receipt);
16669
+ } else {
16670
+ receipt.intent = checkedPublicationView(await client.get(receipt.skillId, receipt.intent.id), receipt.skillId, receipt.intent.id, receipt.declaration);
16671
+ save(directory, receipt);
16672
+ }
16673
+ if (receipt.intent.state === "awaiting_upload") {
16674
+ if (receipt.phase === "awaiting_upload") {
16675
+ receipt.phase = "upload_uncertain";
16676
+ save(directory, receipt);
16677
+ try {
16678
+ await client.upload(receipt.skillId, receipt.intent, bytes);
16679
+ } catch (error) {
16680
+ if (error instanceof PrivatePublicationError && !error.uncertain) {
16681
+ receipt.phase = "awaiting_upload";
16682
+ save(directory, receipt);
16683
+ }
16684
+ throw error;
16685
+ }
16686
+ receipt.phase = "uploaded";
16687
+ save(directory, receipt);
16688
+ }
16689
+ receipt.phase = "finalize_uncertain";
16690
+ save(directory, receipt);
16691
+ receipt.intent = checkedPublicationView(await client.finalize(receipt.skillId, receipt.intent.id), receipt.skillId, receipt.intent.id, receipt.declaration);
16692
+ receipt.phase = "observed";
16693
+ save(directory, receipt);
16694
+ }
16695
+ if (options.waitMs !== undefined && options.waitMs > 0 && ["queued", "verifying"].includes(receipt.intent.state)) {
16696
+ receipt.intent = checkedPublicationView(await client.wait(receipt.skillId, receipt.intent.id, { timeoutMs: options.waitMs }), receipt.skillId, receipt.intent.id, receipt.declaration);
16697
+ receipt.phase = "observed";
16698
+ save(directory, receipt);
16699
+ }
16700
+ return privatePublicationResult(directory, receipt);
16701
+ }
16702
+ async function inspectPrivatePublication(client, directory, cancel = false) {
16703
+ return locked(directory, () => inspectLocked(client, directory, cancel));
16704
+ }
16705
+ async function inspectLocked(client, directory, cancel) {
16706
+ const { receipt } = readPrivatePublicationRecovery(directory);
16707
+ bind(client, receipt);
16708
+ if (receipt.intent) {
16709
+ receipt.intent = checkedPublicationView(await (cancel ? client.cancel(receipt.skillId, receipt.intent.id) : client.get(receipt.skillId, receipt.intent.id)), receipt.skillId, receipt.intent.id, receipt.declaration);
16710
+ save(directory, receipt);
16711
+ } else if (cancel)
16712
+ throw new PrivatePublicationError("PUBLICATION_INTENT_UNKNOWN", "Reconcile the saved begin request with publication resume before cancelling its intent.");
16713
+ return privatePublicationResult(directory, receipt);
16714
+ }
16073
16715
  export {
16074
- AGENT_TARGETS,
16075
- ARTICLE_GENERATION_SLUG,
16076
- BASIC_SKILL_NAMES,
16077
- CATEGORIES,
16078
- CONTENT_HASH_LIMITS,
16079
- ContentHashInputError,
16080
- DEFAULT_EXPORT_DIR,
16081
- HostedApiError,
16082
- InvitationEmailInputError,
16083
- MCP_CONTRACT_SCHEMA_VERSION,
16084
- MissingSkillsFleetError,
16085
- PORTABLE_SKILL_DEFAULT_VERSION,
16086
- PORTABLE_SKILL_SCHEMA,
16087
- PORTABLE_SKILL_STANDARD,
16088
- PROJECT_CONFIG_FILE,
16089
- PullSkillError,
16090
- REFUSED_SCANNER_FLAGGED,
16091
- REMOTE_SKILL_RUN_CONTRACT_VERSION,
16092
- RemoteCapabilityUnavailableError,
16093
- RemoteCreditApprovalError,
16094
- RemoteInvitationEmailError,
16095
- RemoteInvitationEmailUnconfirmedError,
16096
- RemoteQuoteUnavailableError,
16097
- RemoteRequestError,
16098
- RemoteRouteUnsupportedError,
16099
- RemoteSkillsAuthClient,
16100
- RemoteSkillsClient,
16101
- RemoteWorkspaceInvitationError,
16102
- RemoteWorkspaceInvitationReadError,
16103
- RemoteWorkspaceInvitationUnconfirmedError,
16104
- RemoteWorkspaceLeaveError,
16105
- RemoteWorkspaceLeaveUnconfirmedError,
16106
- RemoteWorkspaceMemberError,
16107
- RemoteWorkspaceSelectionError,
16108
- SKILLS,
16109
- SKILLS_API_KEY_ENV,
16110
- SKILLS_API_KEY_ENV_KEYS,
16111
- SKILLS_API_URL_ENV,
16112
- SKILLS_API_URL_ENV_KEYS,
16113
- SKILLS_APP,
16114
- SKILLS_CLI_MCP_PARITY,
16115
- SKILLS_LOCAL_OPT_IN_ENV_KEYS,
16116
- SKILLS_NATIVE_STORAGE_ENV,
16117
- SKILLS_NATIVE_STORAGE_FALLBACK_ENV,
16118
- SKILLS_PROJECT_DIR,
16119
- SKILLS_STORAGE_ENV,
16120
- SKILLS_STORAGE_FALLBACK_ENV,
16121
- SKILLS_STORAGE_TABLES,
16122
- SKILL_ALIASES,
16123
- SKILL_RUNTIMES,
16124
- SKILL_SANDBOX_MODES,
16125
- SKILL_SYSTEM_DEPS_ALLOWLIST,
16126
- STATION_HYDRATION_MANIFEST_SCHEMA,
16127
- STATION_SYNC_MANIFEST_SCHEMA,
16128
- STORAGE_TABLES,
16129
- SYNC_AGENTS,
16130
- SYNC_HOMES,
16131
- SYNC_MARKER_FILE,
16132
- SYNC_MARKER_MANAGED_BY,
16133
- SkillsFleetCredentialError,
16134
- SkillsPostgresSyncStore,
16135
- SkillsS3ObjectStore,
16136
- StationSnapshotError,
16137
- TOOL_PRIMITIVES,
16138
- TOOL_PRIMITIVE_SCHEMA_VERSION,
16139
- WorkspaceContextInputError,
16140
- WorkspaceIdentityMismatchError,
16141
- WorkspaceInvitationInputError,
16142
- WorkspaceLeaveInputError,
16143
- adaptSkillMdForAgent,
16144
- addSchedule,
16145
- agentGlobalSkillsDir,
16146
- appendRunEvent,
16147
- buildSkillsApiUrl,
16148
- buildSkillsS3ObjectUrl,
16149
- canonicalizeManifest,
16150
- clearRegistryCache,
16151
- completeSkillRun,
16152
- computeContentHash,
16153
- computeContentHashFromEntries,
16154
- configuredSkillsApiUrl,
16155
- createLocalSkillManifest,
16156
- createMcpContractManifest,
16157
- createRegistrySyncArtifact,
16158
- createRemoteSkillsClient,
16159
- createSkillMcpMetadata,
16160
- createSkillRun,
16161
- createSkillToolDependencies,
16162
- createSkillsPostgresSyncStore,
16163
- createSkillsS3ObjectStore,
16164
- createSkillsSnapshotSyncRecord,
16165
- describeMcpToolContracts,
16166
- destinationFor,
16167
- disableSkill,
16168
- enableSkill,
16169
- ensureProjectConfig,
16170
- exportSkillsLocalSnapshot,
16171
- findPortableSkill,
16172
- findSimilarSkills,
16173
- findSkillRun,
16174
- findSkillsParityForCliCommand,
16175
- findSkillsParityForMcpTool,
16176
- generateEnvExample,
16177
- generateSkillMd,
16178
- getAgentSkillPath,
16179
- getAgentSkillsDir,
16180
- getAllTags,
16181
- getCompactSkillDiscovery,
16182
- getConfigPath,
16183
- getConfiguredApiUrl,
16184
- getDisabledProjectSkills,
16185
- getDisabledSkills,
16186
- getDueSchedules,
16187
- getFeedbackDbPath,
16188
- getInstallMeta,
16189
- getInstalledSkills,
16190
- getMcpResourceContracts,
16191
- getMcpToolDescriptions,
16192
- getNextRun,
16193
- getPinnedSkills,
16194
- getPortableSkillPath,
16195
- getPortableSkillsRoot,
16196
- getProjectConfigPath,
16197
- getProjectStateDir,
16198
- getPublicSkillDiscovery,
16199
- getRunExportDir,
16200
- getSkill,
16201
- getSkillBestDoc,
16202
- getSkillDocs,
16203
- getSkillPath,
16204
- getSkillRequirements,
16205
- getSkillToolDependencies,
16206
- getSkillsByCategory,
16207
- getSkillsByTag,
16208
- getSkillsNativeStorageStatus,
16209
- getSkillsStorageDatabaseEnv,
16210
- getSkillsStorageDatabaseUrl,
16211
- getSkillsStorageStatus,
16212
- getStorageDatabaseEnv,
16213
- getStorageDatabaseUrl,
16214
- getStorageStatus,
16215
- getToolPrimitive,
16216
- homePathFor,
16217
- importSkillsLocalSnapshot,
16218
- installSkill,
16219
- installSkillForAgent,
16220
- installSkillManifest,
16221
- installSkillSource,
16222
- installSkills,
16223
- isBasicSkillName,
16224
- isExcludedSkillFileName,
16225
- isGatewayBackedSkill,
16226
- isPortableWithinSkill,
16227
- isRegularFile,
16228
- isSkillsLocalOptIn,
16229
- isSyncAgent,
16230
- listMcpToolContracts,
16231
- listPinnedSkills,
16232
- listPortableSkillMetas,
16233
- listPortableSkills,
16234
- listSchedules,
16235
- listSkillRuns,
16236
- listToolPrimitives,
16237
- loadBasicRegistry,
16238
- loadConfig,
16239
- loadProjectConfig,
16240
- loadRegistry,
16241
- loadRegistryProfile,
16242
- loadRemoteRegistry,
16243
- loadRemoteSkill,
16244
- normalizeLineEndings,
16245
- normalizePortableSkillName,
16246
- normalizeRemoteSkillRunContract,
16247
- normalizeSkillSlug,
16248
- normalizeSkillsApiOrigin,
16249
- noticeLocalSkillsMode,
16250
- parseRemoteRegistryPayload,
16251
- parseRemoteSkillPayload,
16252
- parseSkillFrontmatter,
16253
- pinProjectSkill,
16254
- pinSkill,
16255
- planSkillsS3SnapshotUpload,
16256
- planStationHydration,
16257
- planStationSnapshot,
16258
- pointerSkillMd,
16259
- portPortableSkill,
16260
- portPortableSkillDirectory,
16261
- publicDiscoveryDependencies,
16262
- publicDiscoveryDocumentation,
16263
- publicDiscoveryEnvVars,
16264
- pullSkills,
16265
- readPortableSkillManifest,
16266
- recordScheduleRun,
16267
- removeManagedAgentSkill,
16268
- removeSchedule,
16269
- removeSkill,
16270
- removeSkillForAgent,
16271
- requireSkillsApiKey,
16272
- requireSkillsApiOrigin,
16273
- requireSkillsFleet,
16274
- resolveSkillAlias,
16275
- resolveSkillsApiKey,
16276
- resolveSkillsApiOrigin,
16277
- resolveSkillsFleet,
16278
- resolveSkillsNativeStorageConfig,
16279
- resolveStorageConfig,
16280
- resolveSyncAgents,
16281
- revisionIdOf,
16282
- runPortableSkill,
16283
- runSkill,
16284
- sanitizePublicDiscoveryText,
16285
- saveConfig,
16286
- saveFeedback,
16287
- saveProjectConfig,
16288
- scaffoldPortableSkill,
16289
- searchSkills,
16290
- selectsSkillsLocalMode,
16291
- setScheduleEnabled,
16292
- setSkillDisabled,
16293
- sha256File,
16294
- signSkillsAwsV4Request,
16295
- skillExists,
16296
- skillsCredentialFilePath,
16297
- skillsCredentialFiles,
16298
- skillsCredentialOrReason,
16299
- skillsPostgresSyncSchemaSql,
16300
- storageCapabilities,
16301
- summarizeMcpToolContract,
16302
- syncSkillsToAgents,
16303
- unpinProjectSkill,
16304
- unpinSkill,
16305
- unsetConfig,
16306
- updateSkillRun,
16307
- uploadSkillsSnapshotFilesToS3,
16308
- validateBlogArticleRunOptions,
16309
- validateCron,
16310
- validatePortableManifestContract,
16311
- validatePortableSkillDirectory,
16312
- validateRegistryConsistency,
16313
- validateSkillDirectory,
16314
- validateSkillsCliMcpParity,
16315
- validateStationId,
16316
- validateToolPrimitiveCoverage,
16317
- verifyContentHash,
16318
- verifyContentHashFromEntries,
16319
- walkEntries,
16320
- writeCorpusSkill,
16321
- writeManagedAgentSkill,
16322
- writeManagedSkillDir,
16323
- writeRegistrySyncArtifact,
16324
- writeRunLogs,
16716
+ writeStationSnapshot,
16325
16717
  writeStationHydration,
16326
- writeStationSnapshot
16718
+ writeRunLogs,
16719
+ writeRegistrySyncArtifact,
16720
+ writeManagedSkillDir,
16721
+ writeManagedAgentSkill,
16722
+ writeCorpusSkill,
16723
+ walkEntries,
16724
+ verifyContentHashFromEntries,
16725
+ verifyContentHash,
16726
+ validateToolPrimitiveCoverage,
16727
+ validateStationId,
16728
+ validateSkillsCliMcpParity,
16729
+ validateSkillDirectory,
16730
+ validateRegistryConsistency,
16731
+ validatePortableSkillDirectory,
16732
+ validatePortableManifestContract,
16733
+ validateCron,
16734
+ validateBlogArticleRunOptions,
16735
+ uploadSkillsSnapshotFilesToS3,
16736
+ updateSkillRun,
16737
+ unsetConfig,
16738
+ unpinSkill,
16739
+ unpinProjectSkill,
16740
+ syncSkillsToAgents,
16741
+ summarizeMcpToolContract,
16742
+ storageCapabilities,
16743
+ skillsPostgresSyncSchemaSql,
16744
+ skillsCredentialOrReason,
16745
+ skillsCredentialFiles,
16746
+ skillsCredentialFilePath,
16747
+ skillExists,
16748
+ signSkillsAwsV4Request,
16749
+ sha256File,
16750
+ setSkillDisabled,
16751
+ setScheduleEnabled,
16752
+ selectsSkillsLocalMode,
16753
+ searchSkills,
16754
+ scaffoldPortableSkill,
16755
+ saveProjectConfig,
16756
+ saveFeedback,
16757
+ saveConfig,
16758
+ sanitizePublicDiscoveryText,
16759
+ runSkill,
16760
+ runPortableSkill,
16761
+ revisionIdOf,
16762
+ resolveSyncAgents,
16763
+ resolveStorageConfig,
16764
+ resolveSkillsNativeStorageConfig,
16765
+ resolveSkillsFleet,
16766
+ resolveSkillsApiOrigin,
16767
+ resolveSkillsApiKey,
16768
+ resolveSkillAlias,
16769
+ requireSkillsFleet,
16770
+ requireSkillsApiOrigin,
16771
+ requireSkillsApiKey,
16772
+ removeSkillForAgent,
16773
+ removeSkill,
16774
+ removeSchedule,
16775
+ removeManagedAgentSkill,
16776
+ recordScheduleRun,
16777
+ readPrivatePublicationRecovery,
16778
+ readPortableSkillManifest,
16779
+ pullSkills,
16780
+ publicDiscoveryEnvVars,
16781
+ publicDiscoveryDocumentation,
16782
+ publicDiscoveryDependencies,
16783
+ preparePrivatePublication,
16784
+ portPortableSkillDirectory,
16785
+ portPortableSkill,
16786
+ pointerSkillMd,
16787
+ planStationSnapshot,
16788
+ planStationHydration,
16789
+ planSkillsS3SnapshotUpload,
16790
+ pinSkill,
16791
+ pinProjectSkill,
16792
+ parseSkillFrontmatter,
16793
+ parseRemoteSkillPayload,
16794
+ parseRemoteRegistryPayload,
16795
+ noticeLocalSkillsMode,
16796
+ normalizeSkillsApiOrigin,
16797
+ normalizeSkillSlug,
16798
+ normalizeRemoteSkillRunContract,
16799
+ normalizePortableSkillName,
16800
+ normalizeLineEndings,
16801
+ loadRemoteSkill,
16802
+ loadRemoteRegistry,
16803
+ loadRegistryProfile,
16804
+ loadRegistry,
16805
+ loadProjectConfig,
16806
+ loadConfig,
16807
+ loadBasicRegistry,
16808
+ listToolPrimitives,
16809
+ listSkillRuns,
16810
+ listSchedules,
16811
+ listPortableSkills,
16812
+ listPortableSkillMetas,
16813
+ listPinnedSkills,
16814
+ listMcpToolContracts,
16815
+ isSyncAgent,
16816
+ isSkillsLocalOptIn,
16817
+ isRegularFile,
16818
+ isPortableWithinSkill,
16819
+ isGatewayBackedSkill,
16820
+ isExcludedSkillFileName,
16821
+ isBasicSkillName,
16822
+ installSkills,
16823
+ installSkillSource,
16824
+ installSkillManifest,
16825
+ installSkillForAgent,
16826
+ installSkill,
16827
+ inspectPrivatePublication,
16828
+ importSkillsLocalSnapshot,
16829
+ homePathFor,
16830
+ getToolPrimitive,
16831
+ getStorageStatus,
16832
+ getStorageDatabaseUrl,
16833
+ getStorageDatabaseEnv,
16834
+ getSkillsStorageStatus,
16835
+ getSkillsStorageDatabaseUrl,
16836
+ getSkillsStorageDatabaseEnv,
16837
+ getSkillsNativeStorageStatus,
16838
+ getSkillsByTag,
16839
+ getSkillsByCategory,
16840
+ getSkillToolDependencies,
16841
+ getSkillRequirements,
16842
+ getSkillPath,
16843
+ getSkillDocs,
16844
+ getSkillBestDoc,
16845
+ getSkill,
16846
+ getRunExportDir,
16847
+ getPublicSkillDiscovery,
16848
+ getProjectStateDir,
16849
+ getProjectConfigPath,
16850
+ getPortableSkillsRoot,
16851
+ getPortableSkillPath,
16852
+ getPinnedSkills,
16853
+ getNextRun,
16854
+ getMcpToolDescriptions,
16855
+ getMcpResourceContracts,
16856
+ getInstalledSkills,
16857
+ getInstallMeta,
16858
+ getFeedbackDbPath,
16859
+ getDueSchedules,
16860
+ getDisabledSkills,
16861
+ getDisabledProjectSkills,
16862
+ getConfiguredApiUrl,
16863
+ getConfigPath,
16864
+ getCompactSkillDiscovery,
16865
+ getAllTags,
16866
+ getAgentSkillsDir,
16867
+ getAgentSkillPath,
16868
+ generateSkillMd,
16869
+ generateEnvExample,
16870
+ findSkillsParityForMcpTool,
16871
+ findSkillsParityForCliCommand,
16872
+ findSkillRun,
16873
+ findSimilarSkills,
16874
+ findPortableSkill,
16875
+ exportSkillsLocalSnapshot,
16876
+ ensureProjectConfig,
16877
+ enableSkill,
16878
+ disableSkill,
16879
+ destinationFor,
16880
+ describeMcpToolContracts,
16881
+ createSkillsSnapshotSyncRecord,
16882
+ createSkillsS3ObjectStore,
16883
+ createSkillsPostgresSyncStore,
16884
+ createSkillToolDependencies,
16885
+ createSkillRun,
16886
+ createSkillMcpMetadata,
16887
+ createRemoteSkillsClient,
16888
+ createRegistrySyncArtifact,
16889
+ createMcpContractManifest,
16890
+ createLocalSkillManifest,
16891
+ continuePrivatePublication,
16892
+ configuredSkillsApiUrl,
16893
+ computeContentHashFromEntries,
16894
+ computeContentHash,
16895
+ completeSkillRun,
16896
+ clearRegistryCache,
16897
+ canonicalizeManifest,
16898
+ buildSkillsS3ObjectUrl,
16899
+ buildSkillsApiUrl,
16900
+ appendRunEvent,
16901
+ agentGlobalSkillsDir,
16902
+ addSchedule,
16903
+ adaptSkillMdForAgent,
16904
+ WorkspaceLeaveInputError,
16905
+ WorkspaceInvitationInputError,
16906
+ WorkspaceIdentityMismatchError,
16907
+ WorkspaceContextInputError,
16908
+ TOOL_PRIMITIVE_SCHEMA_VERSION,
16909
+ TOOL_PRIMITIVES,
16910
+ StationSnapshotError,
16911
+ SkillsS3ObjectStore,
16912
+ SkillsPostgresSyncStore,
16913
+ SkillsFleetCredentialError,
16914
+ SYNC_MARKER_MANAGED_BY,
16915
+ SYNC_MARKER_FILE,
16916
+ SYNC_HOMES,
16917
+ SYNC_AGENTS,
16918
+ STORAGE_TABLES,
16919
+ STATION_SYNC_MANIFEST_SCHEMA,
16920
+ STATION_HYDRATION_MANIFEST_SCHEMA,
16921
+ SKILL_SYSTEM_DEPS_ALLOWLIST,
16922
+ SKILL_SANDBOX_MODES,
16923
+ SKILL_RUNTIMES,
16924
+ SKILL_ALIASES,
16925
+ SKILLS_STORAGE_TABLES,
16926
+ SKILLS_STORAGE_FALLBACK_ENV,
16927
+ SKILLS_STORAGE_ENV,
16928
+ SKILLS_PROJECT_DIR,
16929
+ SKILLS_NATIVE_STORAGE_FALLBACK_ENV,
16930
+ SKILLS_NATIVE_STORAGE_ENV,
16931
+ SKILLS_LOCAL_OPT_IN_ENV_KEYS,
16932
+ SKILLS_CLI_MCP_PARITY,
16933
+ SKILLS_APP,
16934
+ SKILLS_API_URL_ENV_KEYS,
16935
+ SKILLS_API_URL_ENV,
16936
+ SKILLS_API_KEY_ENV_KEYS,
16937
+ SKILLS_API_KEY_ENV,
16938
+ SKILLS,
16939
+ RemoteWorkspaceSelectionError,
16940
+ RemoteWorkspaceMemberError,
16941
+ RemoteWorkspaceLeaveUnconfirmedError,
16942
+ RemoteWorkspaceLeaveError,
16943
+ RemoteWorkspaceInvitationUnconfirmedError,
16944
+ RemoteWorkspaceInvitationReadError,
16945
+ RemoteWorkspaceInvitationError,
16946
+ RemoteSkillsClient,
16947
+ RemoteSkillsAuthClient,
16948
+ RemoteRouteUnsupportedError,
16949
+ RemoteRequestError,
16950
+ RemoteQuoteUnavailableError,
16951
+ RemotePrivatePublicationsClient,
16952
+ RemoteInvitationEmailUnconfirmedError,
16953
+ RemoteInvitationEmailError,
16954
+ RemoteCreditApprovalError,
16955
+ RemoteCapabilityUnavailableError,
16956
+ REMOTE_SKILL_RUN_CONTRACT_VERSION,
16957
+ REFUSED_SCANNER_FLAGGED,
16958
+ PullSkillError,
16959
+ PrivatePublicationError,
16960
+ PROJECT_CONFIG_FILE,
16961
+ PRIVATE_PUBLICATION_MAX_BYTES,
16962
+ PORTABLE_SKILL_STANDARD,
16963
+ PORTABLE_SKILL_SCHEMA,
16964
+ PORTABLE_SKILL_DEFAULT_VERSION,
16965
+ MissingSkillsFleetError,
16966
+ MCP_CONTRACT_SCHEMA_VERSION,
16967
+ InvitationEmailInputError,
16968
+ HostedApiError,
16969
+ DEFAULT_EXPORT_DIR,
16970
+ ContentHashInputError,
16971
+ CONTENT_HASH_LIMITS,
16972
+ CATEGORIES,
16973
+ BASIC_SKILL_NAMES,
16974
+ ARTICLE_GENERATION_SLUG,
16975
+ AGENT_TARGETS
16327
16976
  };