@hasna/skills 0.8.11 → 0.9.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/bin/mcp.js CHANGED
@@ -20019,7 +20019,7 @@ var package_default;
20019
20019
  var init_package = __esm(() => {
20020
20020
  package_default = {
20021
20021
  name: "@hasna/skills",
20022
- version: "0.8.11",
20022
+ version: "0.9.1",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -37439,6 +37439,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
37439
37439
  const version2 = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
37440
37440
  const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
37441
37441
  const commands = parseManifestCommands(jsonManifest) ?? (kind === "instruction" ? [] : inferPackageCommands(pkg, name)) ?? [];
37442
+ const runtime = parseManifestRuntime(jsonManifest) ?? inferRuntimeFromCommands(commands);
37442
37443
  return {
37443
37444
  $schema: stringField(jsonManifest, "$schema") ?? PORTABLE_SKILL_SCHEMA,
37444
37445
  standard: stringField(jsonManifest, "standard") ?? PORTABLE_SKILL_STANDARD,
@@ -37451,7 +37452,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
37451
37452
  ...kind ? { kind } : {},
37452
37453
  inputs: kind === "instruction" ? parseManifestInputs(jsonManifest) ?? [] : parseManifestInputs(jsonManifest) ?? DEFAULT_INPUTS,
37453
37454
  commands,
37454
- ...parseManifestRuntime(jsonManifest) ? { runtime: parseManifestRuntime(jsonManifest) } : {},
37455
+ ...runtime ? { runtime } : {},
37455
37456
  ...parseManifestProvenance(jsonManifest) ? { provenance: parseManifestProvenance(jsonManifest) } : {}
37456
37457
  };
37457
37458
  }
@@ -37970,6 +37971,12 @@ function inferPackageCommands(pkg, fallbackName) {
37970
37971
  }
37971
37972
  return;
37972
37973
  }
37974
+ function inferRuntimeFromCommands(commands) {
37975
+ const entrypoint = commands.find((command) => command.entry)?.entry;
37976
+ if (!entrypoint || !entrypoint.toLowerCase().endsWith(".py"))
37977
+ return;
37978
+ return { ...defaultRuntimeContract(entrypoint), runtime: "python3" };
37979
+ }
37973
37980
  function readJsonObject(path) {
37974
37981
  const parsed = JSON.parse(readFileSync8(path, "utf-8"));
37975
37982
  if (!isRecord2(parsed))
@@ -38786,6 +38793,8 @@ function normalizeRemoteSkill(skill) {
38786
38793
  tags: skill.tags || ["remote"],
38787
38794
  dependencies: skill.dependencies,
38788
38795
  ...skill.version ? { version: skill.version } : {},
38796
+ ...skill.revisionId !== undefined ? { revisionId: skill.revisionId } : {},
38797
+ ...skill.lifecycle ? { lifecycle: skill.lifecycle } : {},
38789
38798
  availability: normalizeRemoteAvailability(skill.availability),
38790
38799
  source: "remote"
38791
38800
  };
@@ -38889,6 +38898,8 @@ var init_remote_registry = __esm(() => {
38889
38898
  tags: exports_external.array(exports_external.string()).optional(),
38890
38899
  dependencies: exports_external.array(exports_external.string()).optional(),
38891
38900
  version: exports_external.string().optional(),
38901
+ revisionId: exports_external.union([exports_external.string().regex(/^[\x21-\x7e]+$/), exports_external.null()]).optional(),
38902
+ lifecycle: exports_external.enum(["active", "archived"]).optional(),
38892
38903
  availability: remoteAvailabilitySchema.optional()
38893
38904
  }).passthrough().refine((skill) => skill.name || skill.slug, {
38894
38905
  message: "Remote skill requires name or slug"
@@ -39944,7 +39955,9 @@ function assertAgentPolicyCollections(policy) {
39944
39955
  text(source.path);
39945
39956
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
39946
39957
  if (source.hashMode !== undefined)
39947
- requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
39958
+ requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
39959
+ if (source.hashMode === "claude-settings-v1")
39960
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "settings.json");
39948
39961
  if (source.hashMode === "claude-marketplace-registry")
39949
39962
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
39950
39963
  if (source.hashMode === "claude-plugin-registry") {
@@ -47592,6 +47605,63 @@ var init_claude_marketplace_registry = __esm(() => {
47592
47605
  CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
47593
47606
  });
47594
47607
 
47608
+ // src/lib/claude-settings-witness.ts
47609
+ var CLAUDE_SETTINGS_WITNESS_LIMITS, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS;
47610
+ var init_claude_settings_witness = __esm(() => {
47611
+ CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
47612
+ BOOLEAN_PREFERENCES = new Set([
47613
+ "autoScrollEnabled",
47614
+ "axScreenReader",
47615
+ "emojiCompletionEnabled",
47616
+ "prefersReducedMotion",
47617
+ "showTurnDuration",
47618
+ "spinnerTipsEnabled",
47619
+ "syntaxHighlightingDisabled",
47620
+ "terminalProgressBarEnabled",
47621
+ "terminalTitleFromRename",
47622
+ "verbose",
47623
+ "wheelScrollAccelerationEnabled"
47624
+ ]);
47625
+ ENUM_PREFERENCES = Object.freeze({
47626
+ editorMode: ["normal", "vim"],
47627
+ tui: ["default", "fullscreen"],
47628
+ viewMode: ["default", "verbose", "focus"]
47629
+ });
47630
+ BUILTIN_MODEL_SELECTIONS = new Set([
47631
+ "default",
47632
+ "best",
47633
+ "fable",
47634
+ "fable[1m]",
47635
+ "sonnet",
47636
+ "sonnet[1m]",
47637
+ "opus",
47638
+ "opus[1m]",
47639
+ "haiku",
47640
+ "opusplan",
47641
+ "claude-fable-5-1",
47642
+ "claude-fable-5",
47643
+ "claude-fable-5[1m]",
47644
+ "claude-opus-5",
47645
+ "claude-sonnet-5",
47646
+ "claude-haiku-4-5-20251001",
47647
+ "claude-opus-4-6",
47648
+ "claude-sonnet-4-5",
47649
+ "claude-sonnet-4-5-20250929",
47650
+ "claude-opus-4-8",
47651
+ "claude-opus-4-8[1m]",
47652
+ "claude-opus-4-7",
47653
+ "claude-sonnet-4-6",
47654
+ "claude-opus-4-5-20251101",
47655
+ "claude-opus-4-5",
47656
+ "claude-haiku-4-5",
47657
+ "claude-fable-5-1[1m]",
47658
+ "claude-opus-5[1m]",
47659
+ "claude-opus-4-7[1m]",
47660
+ "claude-opus-4-6[1m]",
47661
+ "claude-sonnet-4-6[1m]"
47662
+ ]);
47663
+ });
47664
+
47595
47665
  // src/lib/agent-discovery.ts
47596
47666
  var init_agent_discovery = __esm(() => {
47597
47667
  init_agent_policy_limits();
@@ -47602,6 +47672,7 @@ var init_agent_discovery = __esm(() => {
47602
47672
  init_plugin_discovery();
47603
47673
  init_plugin_admission();
47604
47674
  init_claude_marketplace_registry();
47675
+ init_claude_settings_witness();
47605
47676
  init_agent_discovery_directories();
47606
47677
  });
47607
47678
 
@@ -49467,6 +49538,7 @@ __export(exports_remote_client, {
49467
49538
  RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
49468
49539
  RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
49469
49540
  RemoteSkillsClient: () => RemoteSkillsClient,
49541
+ RemoteSkillLifecycleError: () => RemoteSkillLifecycleError,
49470
49542
  RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
49471
49543
  RemoteRequestError: () => RemoteRequestError,
49472
49544
  RemoteQuoteUnavailableError: () => RemoteQuoteUnavailableError,
@@ -49598,6 +49670,39 @@ class RemoteSkillsClient {
49598
49670
  })();
49599
49671
  return this.capabilities;
49600
49672
  }
49673
+ async addSkillPublishScope(keyId, expectedScopes, expectedOrgId) {
49674
+ if (!/^[A-Za-z0-9_-]+$/.test(keyId))
49675
+ throw new Error("Invalid API key id");
49676
+ if (!/^[A-Za-z0-9_-]{1,256}$/.test(expectedOrgId))
49677
+ throw new Error("Invalid expected organization id");
49678
+ if (!Array.isArray(expectedScopes) || expectedScopes.length > 32 || expectedScopes.some((scope) => typeof scope !== "string" || scope.length > 128 || !/^(?:\*|[a-z][a-z0-9_-]*:(?:\*|[a-z][a-z0-9_-]*))$/.test(scope)) || new Set(expectedScopes).size !== expectedScopes.length)
49679
+ throw new Error("Invalid expected API key scopes");
49680
+ const path = `/api/v1/admin/keys/${encodeURIComponent(keyId)}/scopes`;
49681
+ const response = await this.request(path, { method: "PATCH", body: JSON.stringify({ expected_scopes: expectedScopes, add_scopes: ["skills:publish"] }) });
49682
+ if (!response.ok)
49683
+ throw new RemoteRequestError(path, response.status);
49684
+ let value;
49685
+ try {
49686
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 64 * 1024)));
49687
+ } catch {
49688
+ throw new Error("Invalid API key scope update response");
49689
+ }
49690
+ if (!value || typeof value !== "object" || Array.isArray(value))
49691
+ throw new Error("Invalid API key scope update response");
49692
+ const body = value;
49693
+ const returnedId = body.keyId;
49694
+ const orgId = body.orgId;
49695
+ const scopes = body.scopes;
49696
+ if (returnedId !== keyId || typeof orgId !== "string" || !orgId || expectedOrgId !== undefined && orgId !== expectedOrgId || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string") || body.updated !== true) {
49697
+ throw new Error("Invalid API key scope update response");
49698
+ }
49699
+ const returnedScopes = scopes;
49700
+ const expectedResult = expectedScopes.includes("skills:publish") ? expectedScopes : [...expectedScopes, "skills:publish"];
49701
+ if (returnedScopes.length !== expectedResult.length || expectedResult.some((scope, index) => returnedScopes[index] !== scope)) {
49702
+ throw new Error("API key scope update response did not preserve the expected scopes");
49703
+ }
49704
+ return { keyId, orgId, scopes: [...returnedScopes], updated: true };
49705
+ }
49601
49706
  async submitQuotedRun(slug2, input2 = {}, args = [], approval = {}) {
49602
49707
  runQuoteReceipt(approval.quoteReceipt);
49603
49708
  ({ input: input2, args, approval } = JSON.parse(JSON.stringify({ input: input2, args, approval })));
@@ -50115,6 +50220,19 @@ class RemoteSkillsClient {
50115
50220
  async deleteSkill(slug2) {
50116
50221
  return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}`, { method: "DELETE" });
50117
50222
  }
50223
+ async setSkillLifecycle(slug2, lifecycle, options) {
50224
+ const path = `/api/v1/skills/${encodeURIComponent(slug2)}/lifecycle`;
50225
+ const response = await this.request(path, {
50226
+ method: "PATCH",
50227
+ headers: { "If-Match": options.expectedRevisionId },
50228
+ body: JSON.stringify({ lifecycle, ...options.reason ? { reason: options.reason } : {}, ...options.replacementSlug ? { replacementSlug: options.replacementSlug } : {} })
50229
+ });
50230
+ if (!response.ok) {
50231
+ const code = await readResponseCode(response);
50232
+ throw new RemoteSkillLifecycleError(path, response.status, code);
50233
+ }
50234
+ return response.json();
50235
+ }
50118
50236
  async downloadSkillBundle(slug2) {
50119
50237
  return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}/bundle`, { method: "GET" });
50120
50238
  }
@@ -50266,9 +50384,13 @@ function normalizeSkillSummaryList(payload) {
50266
50384
  return payload.map(normalizeSkillSummary);
50267
50385
  }
50268
50386
  async function responseBodyCarriesCode(response, codes) {
50387
+ const code = await readResponseCode(response);
50388
+ return code !== undefined && codes.includes(code);
50389
+ }
50390
+ async function readResponseCode(response) {
50269
50391
  const reader = response.body?.getReader();
50270
50392
  if (!reader)
50271
- return false;
50393
+ return;
50272
50394
  const maximum = 8 * 1024;
50273
50395
  let deadline;
50274
50396
  const expired = new Promise((_, reject) => {
@@ -50283,7 +50405,7 @@ async function responseBodyCarriesCode(response, codes) {
50283
50405
  break;
50284
50406
  size += next.value.byteLength;
50285
50407
  if (size > maximum)
50286
- return false;
50408
+ return;
50287
50409
  chunks.push(next.value);
50288
50410
  }
50289
50411
  const bytes = new Uint8Array(size);
@@ -50294,11 +50416,11 @@ async function responseBodyCarriesCode(response, codes) {
50294
50416
  }
50295
50417
  const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
50296
50418
  if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
50297
- return false;
50419
+ return;
50298
50420
  const code = payload.code;
50299
- return typeof code === "string" && codes.includes(code);
50421
+ return typeof code === "string" && /^[A-Z][A-Z0-9_:-]{0,127}$/.test(code) ? code : undefined;
50300
50422
  } catch {
50301
- return false;
50423
+ return;
50302
50424
  } finally {
50303
50425
  clearTimeout(deadline);
50304
50426
  reader.cancel().catch(() => {});
@@ -50327,7 +50449,7 @@ async function createRemoteSkillsClient(env = process.env) {
50327
50449
  function createRemoteSkillsClientReadOnly(env = process.env) {
50328
50450
  return createRemoteSkillsClient(env);
50329
50451
  }
50330
- var RemoteRouteUnsupportedError, RemoteRequestError, RemoteCreditCheckoutError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
50452
+ var RemoteRouteUnsupportedError, RemoteRequestError, KNOWN_SKILL_LIFECYCLE_CODES, RemoteSkillLifecycleError, RemoteCreditCheckoutError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
50331
50453
  var init_remote_client = __esm(() => {
50332
50454
  init_remote_recurring();
50333
50455
  init_remote_invitations();
@@ -50364,6 +50486,17 @@ var init_remote_client = __esm(() => {
50364
50486
  this.name = "RemoteRequestError";
50365
50487
  }
50366
50488
  };
50489
+ KNOWN_SKILL_LIFECYCLE_CODES = new Set(["SKILL_ARCHIVE_PROFILE_CONFLICT", "LIFECYCLE_ROLE_REQUIRED"]);
50490
+ RemoteSkillLifecycleError = class RemoteSkillLifecycleError extends RemoteRequestError {
50491
+ code;
50492
+ constructor(path, status, code) {
50493
+ super(path, status);
50494
+ this.name = "RemoteSkillLifecycleError";
50495
+ const safeCode = code !== undefined && KNOWN_SKILL_LIFECYCLE_CODES.has(code) ? code : undefined;
50496
+ this.code = safeCode;
50497
+ this.message = `Skill lifecycle update was refused (HTTP ${status}${safeCode ? `, code ${safeCode}` : ""})`;
50498
+ }
50499
+ };
50367
50500
  RemoteCreditCheckoutError = class RemoteCreditCheckoutError extends RemoteRequestError {
50368
50501
  code;
50369
50502
  requestIdempotencyKey;
@@ -53758,6 +53891,161 @@ var init_run_routing = __esm(() => {
53758
53891
  init_fleet_credentials();
53759
53892
  });
53760
53893
 
53894
+ // src/lib/cloud-executions.ts
53895
+ var exports_cloud_executions = {};
53896
+ __export(exports_cloud_executions, {
53897
+ CloudExecutionClient: () => CloudExecutionClient
53898
+ });
53899
+
53900
+ class CloudExecutionClient {
53901
+ apiOrigin;
53902
+ apiKey;
53903
+ constructor(apiOrigin, apiKey) {
53904
+ this.apiOrigin = apiOrigin;
53905
+ this.apiKey = apiKey;
53906
+ }
53907
+ static async configured() {
53908
+ const c = await resolveSkillsConnection();
53909
+ if (!c)
53910
+ throw Error("Cloud execution requires a configured Skills API credential");
53911
+ return new CloudExecutionClient(c.apiOrigin, c.apiKey);
53912
+ }
53913
+ async request(route, method = "GET", value) {
53914
+ const response = await fetch(skillsApiRequestUrl(this.apiOrigin, "/api/v1/executions/" + route), {
53915
+ method,
53916
+ headers: {
53917
+ authorization: `Bearer ${this.apiKey}`,
53918
+ "User-Agent": `hasna-skills/${package_default.version}`,
53919
+ ...value === undefined ? {} : { "content-type": "application/json" }
53920
+ },
53921
+ ...value === undefined ? {} : { body: JSON.stringify(value) },
53922
+ redirect: "error",
53923
+ signal: AbortSignal.timeout(15000)
53924
+ });
53925
+ if (!response.ok) {
53926
+ response.body?.cancel();
53927
+ throw Error(`Skills cloud execution refused: HTTP ${response.status}`);
53928
+ }
53929
+ return response;
53930
+ }
53931
+ async eligibility(slug2, version2, bundleDigest) {
53932
+ if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2) || bundleDigest !== undefined && !/^(sha256:)?[a-f0-9]{64}$/.test(bundleDigest))
53933
+ throw Error("Cloud eligibility requires an exact skill version and valid digest");
53934
+ const query = new URLSearchParams({ version: version2, ...bundleDigest ? { bundleDigest } : {} });
53935
+ const v = await jsonBody(await this.request(`${slug2}/eligibility?${query}`));
53936
+ if (!v || v.contractVersion !== 1 || typeof v.eligible !== "boolean" || v.skill !== slug2 || v.version !== version2 || !/^[a-f0-9]{64}$/.test(v.bundleDigest) || bundleDigest && bundleDigest.replace(/^sha256:/, "") !== v.bundleDigest || v.eligible && (!v.executionContract || !["pdf.v1", "regex-test.v1"].includes(v.executionContract.id)))
53937
+ throw Error("Invalid cloud eligibility receipt");
53938
+ return v;
53939
+ }
53940
+ async submit(slug2, version2, input2, key, selection) {
53941
+ if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2))
53942
+ throw Error("Cloud execution requires skill@exact-version");
53943
+ if (!/^[A-Za-z0-9._:-]{1,128}$/.test(key))
53944
+ throw Error("Invalid idempotency key");
53945
+ if (selection?.authority && selection.authority !== skillsApiRequestUrl(this.apiOrigin, "/api/v1/").replace(/\/$/, ""))
53946
+ throw Error("Selected skill belongs to another Skills authority");
53947
+ const execution = parseExecution(await jsonBody(await this.request(slug2, "POST", {
53948
+ version: version2,
53949
+ input: input2,
53950
+ idempotencyKey: key,
53951
+ ...selection ? {
53952
+ bundleDigest: selection.bundleDigest,
53953
+ workspaceId: selection.workspaceId
53954
+ } : {}
53955
+ })));
53956
+ if (execution.skill !== slug2 || execution.version !== version2 || selection && execution.bundleDigest.replace(/^sha256:/, "") !== selection.bundleDigest.replace(/^sha256:/, ""))
53957
+ throw Error("Cloud execution receipt does not match the selected exact skill");
53958
+ return execution;
53959
+ }
53960
+ async get(id) {
53961
+ return parseExecution(await jsonBody(await this.request(runId(id))));
53962
+ }
53963
+ async logs(id) {
53964
+ return jsonBody(await this.request(runId(id) + "/logs"));
53965
+ }
53966
+ async artifacts(id) {
53967
+ return (await this.get(id)).artifacts;
53968
+ }
53969
+ async download(id, name) {
53970
+ if (!["document.pdf", "document.html"].includes(name))
53971
+ throw Error("Invalid cloud artifact name");
53972
+ const response = await this.request(runId(id) + "/artifacts/" + encodeURIComponent(name));
53973
+ if (!response.body)
53974
+ throw Error("Cloud artifact body is missing");
53975
+ const reader = response.body.getReader();
53976
+ const chunks = [];
53977
+ let size = 0;
53978
+ for (;; ) {
53979
+ const r2 = await reader.read();
53980
+ if (r2.done)
53981
+ break;
53982
+ size += r2.value.length;
53983
+ if (size > 2000000) {
53984
+ await reader.cancel();
53985
+ throw Error("Cloud artifact exceeds limit");
53986
+ }
53987
+ chunks.push(r2.value);
53988
+ }
53989
+ return Buffer.concat(chunks);
53990
+ }
53991
+ async cancel(id) {
53992
+ return parseExecution(await jsonBody(await this.request(runId(id) + "/cancel", "POST", {})));
53993
+ }
53994
+ async wait(id, options = {}) {
53995
+ const timeout = options.timeoutMs ?? 300000, interval = options.intervalMs ?? 1000;
53996
+ if (!Number.isFinite(timeout) || timeout < 1 || timeout > 1200000 || !Number.isFinite(interval) || interval < 100 || interval > 60000)
53997
+ throw Error("Invalid cloud polling bounds");
53998
+ const until = Date.now() + timeout;
53999
+ for (;; ) {
54000
+ const run = await this.get(id);
54001
+ if (["succeeded", "failed", "cancelled"].includes(run.status))
54002
+ return run;
54003
+ if (Date.now() >= until)
54004
+ throw Error("Cloud execution is still running; poll the returned execution id");
54005
+ await Bun.sleep(interval);
54006
+ }
54007
+ }
54008
+ }
54009
+ function runId(id) {
54010
+ if (!/^run_[a-z0-9_]+$/.test(id))
54011
+ throw Error("Invalid cloud execution id");
54012
+ return id;
54013
+ }
54014
+ function parseExecution(value) {
54015
+ const v = value;
54016
+ if (!v || v.contractVersion !== 1 || v.target !== "cloud" || ![
54017
+ "admitted",
54018
+ "leased",
54019
+ "running",
54020
+ "succeeded",
54021
+ "failed",
54022
+ "cancelled"
54023
+ ].includes(v.status) || typeof v.skill !== "string" || typeof v.version !== "string" || !Array.isArray(v.artifacts))
54024
+ throw Error("Unsupported cloud execution response");
54025
+ runId(v.id);
54026
+ for (const field of [
54027
+ "bundleDigest",
54028
+ "inputDigest",
54029
+ "runtimeImageDigest"
54030
+ ])
54031
+ if (typeof v[field] !== "string" || !/^(sha256:)?[a-f0-9]{64}$/.test(v[field]))
54032
+ throw Error("Invalid cloud execution integrity receipt");
54033
+ return v;
54034
+ }
54035
+ async function jsonBody(response) {
54036
+ const bytes = await readBoundedResponse(response, 1e5);
54037
+ try {
54038
+ return JSON.parse(new TextDecoder().decode(bytes));
54039
+ } catch {
54040
+ throw Error("Invalid cloud execution JSON response");
54041
+ }
54042
+ }
54043
+ var init_cloud_executions = __esm(() => {
54044
+ init_package();
54045
+ init_remote_files();
54046
+ init_fleet_credentials();
54047
+ });
54048
+
53761
54049
  // node_modules/@hasna/secrets/dist/sdk.js
53762
54050
  import { isIP as isIP2 } from "net";
53763
54051
  import { spawnSync as spawnSync2 } from "child_process";
@@ -55493,152 +55781,6 @@ var init_selected_run = __esm(() => {
55493
55781
  init_execution_secrets();
55494
55782
  });
55495
55783
 
55496
- // src/lib/cloud-executions.ts
55497
- var exports_cloud_executions = {};
55498
- __export(exports_cloud_executions, {
55499
- CloudExecutionClient: () => CloudExecutionClient
55500
- });
55501
-
55502
- class CloudExecutionClient {
55503
- apiOrigin;
55504
- apiKey;
55505
- constructor(apiOrigin, apiKey) {
55506
- this.apiOrigin = apiOrigin;
55507
- this.apiKey = apiKey;
55508
- }
55509
- static async configured() {
55510
- const c = await resolveSkillsConnection();
55511
- if (!c)
55512
- throw Error("Cloud execution requires a configured Skills API credential");
55513
- return new CloudExecutionClient(c.apiOrigin, c.apiKey);
55514
- }
55515
- async request(route, method = "GET", value) {
55516
- const response = await fetch(skillsApiRequestUrl(this.apiOrigin, "/api/v1/executions/" + route), {
55517
- method,
55518
- headers: {
55519
- authorization: `Bearer ${this.apiKey}`,
55520
- "User-Agent": `hasna-skills/${package_default.version}`,
55521
- ...value === undefined ? {} : { "content-type": "application/json" }
55522
- },
55523
- ...value === undefined ? {} : { body: JSON.stringify(value) },
55524
- redirect: "error",
55525
- signal: AbortSignal.timeout(15000)
55526
- });
55527
- if (!response.ok) {
55528
- response.body?.cancel();
55529
- throw Error(`Skills cloud execution refused: HTTP ${response.status}`);
55530
- }
55531
- return response;
55532
- }
55533
- async submit(slug2, version2, input2, key, selection2) {
55534
- if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2))
55535
- throw Error("Cloud execution requires skill@exact-version");
55536
- if (!/^[A-Za-z0-9._:-]{1,128}$/.test(key))
55537
- throw Error("Invalid idempotency key");
55538
- if (selection2?.authority && selection2.authority !== skillsApiRequestUrl(this.apiOrigin, "/api/v1/").replace(/\/$/, ""))
55539
- throw Error("Selected skill belongs to another Skills authority");
55540
- const execution = parseExecution(await jsonBody(await this.request(slug2, "POST", {
55541
- version: version2,
55542
- input: input2,
55543
- idempotencyKey: key,
55544
- ...selection2 ? {
55545
- bundleDigest: selection2.bundleDigest,
55546
- workspaceId: selection2.workspaceId
55547
- } : {}
55548
- })));
55549
- if (execution.skill !== slug2 || execution.version !== version2 || selection2 && execution.bundleDigest.replace(/^sha256:/, "") !== selection2.bundleDigest.replace(/^sha256:/, ""))
55550
- throw Error("Cloud execution receipt does not match the selected exact skill");
55551
- return execution;
55552
- }
55553
- async get(id2) {
55554
- return parseExecution(await jsonBody(await this.request(runId(id2))));
55555
- }
55556
- async logs(id2) {
55557
- return jsonBody(await this.request(runId(id2) + "/logs"));
55558
- }
55559
- async artifacts(id2) {
55560
- return (await this.get(id2)).artifacts;
55561
- }
55562
- async download(id2, name) {
55563
- if (!["document.pdf", "document.html"].includes(name))
55564
- throw Error("Invalid cloud artifact name");
55565
- const response = await this.request(runId(id2) + "/artifacts/" + encodeURIComponent(name));
55566
- if (!response.body)
55567
- throw Error("Cloud artifact body is missing");
55568
- const reader = response.body.getReader();
55569
- const chunks = [];
55570
- let size = 0;
55571
- for (;; ) {
55572
- const r2 = await reader.read();
55573
- if (r2.done)
55574
- break;
55575
- size += r2.value.length;
55576
- if (size > 2000000) {
55577
- await reader.cancel();
55578
- throw Error("Cloud artifact exceeds limit");
55579
- }
55580
- chunks.push(r2.value);
55581
- }
55582
- return Buffer.concat(chunks);
55583
- }
55584
- async cancel(id2) {
55585
- return parseExecution(await jsonBody(await this.request(runId(id2) + "/cancel", "POST", {})));
55586
- }
55587
- async wait(id2, options = {}) {
55588
- const timeout = options.timeoutMs ?? 300000, interval = options.intervalMs ?? 1000;
55589
- if (!Number.isFinite(timeout) || timeout < 1 || timeout > 1200000 || !Number.isFinite(interval) || interval < 100 || interval > 60000)
55590
- throw Error("Invalid cloud polling bounds");
55591
- const until = Date.now() + timeout;
55592
- for (;; ) {
55593
- const run = await this.get(id2);
55594
- if (["succeeded", "failed", "cancelled"].includes(run.status))
55595
- return run;
55596
- if (Date.now() >= until)
55597
- throw Error("Cloud execution is still running; poll the returned execution id");
55598
- await Bun.sleep(interval);
55599
- }
55600
- }
55601
- }
55602
- function runId(id2) {
55603
- if (!/^run_[a-z0-9_]+$/.test(id2))
55604
- throw Error("Invalid cloud execution id");
55605
- return id2;
55606
- }
55607
- function parseExecution(value) {
55608
- const v = value;
55609
- if (!v || v.contractVersion !== 1 || v.target !== "cloud" || ![
55610
- "admitted",
55611
- "leased",
55612
- "running",
55613
- "succeeded",
55614
- "failed",
55615
- "cancelled"
55616
- ].includes(v.status) || typeof v.skill !== "string" || typeof v.version !== "string" || !Array.isArray(v.artifacts))
55617
- throw Error("Unsupported cloud execution response");
55618
- runId(v.id);
55619
- for (const field of [
55620
- "bundleDigest",
55621
- "inputDigest",
55622
- "runtimeImageDigest"
55623
- ])
55624
- if (typeof v[field] !== "string" || !/^(sha256:)?[a-f0-9]{64}$/.test(v[field]))
55625
- throw Error("Invalid cloud execution integrity receipt");
55626
- return v;
55627
- }
55628
- async function jsonBody(response) {
55629
- const bytes = await readBoundedResponse(response, 1e5);
55630
- try {
55631
- return JSON.parse(new TextDecoder().decode(bytes));
55632
- } catch {
55633
- throw Error("Invalid cloud execution JSON response");
55634
- }
55635
- }
55636
- var init_cloud_executions = __esm(() => {
55637
- init_package();
55638
- init_remote_files();
55639
- init_fleet_credentials();
55640
- });
55641
-
55642
55784
  // src/lib/blog-article.ts
55643
55785
  var exports_blog_article = {};
55644
55786
  __export(exports_blog_article, {
@@ -55773,6 +55915,18 @@ var init_blog_article = __esm(() => {
55773
55915
  import { existsSync as existsSync15, readdirSync as readdirSync9, statSync as statSync10 } from "fs";
55774
55916
  import { join as join24 } from "path";
55775
55917
  function registerOperationTools(server) {
55918
+ server.registerTool("cloud_skill_eligibility", {
55919
+ title: "Cloud Skill Eligibility",
55920
+ description: "Inspect the authenticated workspace's exact reviewed cloud contract. Does not execute or admit a skill.",
55921
+ inputSchema: { name: exports_external.string(), version: exports_external.string(), bundleDigest: exports_external.string().optional() }
55922
+ }, async ({ name, version: version2, bundleDigest }) => {
55923
+ try {
55924
+ const { CloudExecutionClient: CloudExecutionClient2 } = await Promise.resolve().then(() => (init_cloud_executions(), exports_cloud_executions));
55925
+ return mcpJson(await (await CloudExecutionClient2.configured()).eligibility(name, version2, bundleDigest));
55926
+ } catch (error61) {
55927
+ return mcpError("CLOUD_ELIGIBILITY_FAILED", error61.message);
55928
+ }
55929
+ });
55776
55930
  server.registerTool("scaffold_skill", {
55777
55931
  title: "Scaffold Skill",
55778
55932
  description: "Create a portable skill folder under ~/.hasna/skills/installed/<name> with SKILL.md, skill.json, AGENTS.md, package.json, and src/index.ts.",