@hasna/skills 0.8.12 → 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.12",
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"
@@ -49527,6 +49538,7 @@ __export(exports_remote_client, {
49527
49538
  RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
49528
49539
  RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
49529
49540
  RemoteSkillsClient: () => RemoteSkillsClient,
49541
+ RemoteSkillLifecycleError: () => RemoteSkillLifecycleError,
49530
49542
  RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
49531
49543
  RemoteRequestError: () => RemoteRequestError,
49532
49544
  RemoteQuoteUnavailableError: () => RemoteQuoteUnavailableError,
@@ -49658,6 +49670,39 @@ class RemoteSkillsClient {
49658
49670
  })();
49659
49671
  return this.capabilities;
49660
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
+ }
49661
49706
  async submitQuotedRun(slug2, input2 = {}, args = [], approval = {}) {
49662
49707
  runQuoteReceipt(approval.quoteReceipt);
49663
49708
  ({ input: input2, args, approval } = JSON.parse(JSON.stringify({ input: input2, args, approval })));
@@ -50175,6 +50220,19 @@ class RemoteSkillsClient {
50175
50220
  async deleteSkill(slug2) {
50176
50221
  return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}`, { method: "DELETE" });
50177
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
+ }
50178
50236
  async downloadSkillBundle(slug2) {
50179
50237
  return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}/bundle`, { method: "GET" });
50180
50238
  }
@@ -50326,9 +50384,13 @@ function normalizeSkillSummaryList(payload) {
50326
50384
  return payload.map(normalizeSkillSummary);
50327
50385
  }
50328
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) {
50329
50391
  const reader = response.body?.getReader();
50330
50392
  if (!reader)
50331
- return false;
50393
+ return;
50332
50394
  const maximum = 8 * 1024;
50333
50395
  let deadline;
50334
50396
  const expired = new Promise((_, reject) => {
@@ -50343,7 +50405,7 @@ async function responseBodyCarriesCode(response, codes) {
50343
50405
  break;
50344
50406
  size += next.value.byteLength;
50345
50407
  if (size > maximum)
50346
- return false;
50408
+ return;
50347
50409
  chunks.push(next.value);
50348
50410
  }
50349
50411
  const bytes = new Uint8Array(size);
@@ -50354,11 +50416,11 @@ async function responseBodyCarriesCode(response, codes) {
50354
50416
  }
50355
50417
  const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
50356
50418
  if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
50357
- return false;
50419
+ return;
50358
50420
  const code = payload.code;
50359
- return typeof code === "string" && codes.includes(code);
50421
+ return typeof code === "string" && /^[A-Z][A-Z0-9_:-]{0,127}$/.test(code) ? code : undefined;
50360
50422
  } catch {
50361
- return false;
50423
+ return;
50362
50424
  } finally {
50363
50425
  clearTimeout(deadline);
50364
50426
  reader.cancel().catch(() => {});
@@ -50387,7 +50449,7 @@ async function createRemoteSkillsClient(env = process.env) {
50387
50449
  function createRemoteSkillsClientReadOnly(env = process.env) {
50388
50450
  return createRemoteSkillsClient(env);
50389
50451
  }
50390
- 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.";
50391
50453
  var init_remote_client = __esm(() => {
50392
50454
  init_remote_recurring();
50393
50455
  init_remote_invitations();
@@ -50424,6 +50486,17 @@ var init_remote_client = __esm(() => {
50424
50486
  this.name = "RemoteRequestError";
50425
50487
  }
50426
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
+ };
50427
50500
  RemoteCreditCheckoutError = class RemoteCreditCheckoutError extends RemoteRequestError {
50428
50501
  code;
50429
50502
  requestIdempotencyKey;
@@ -53818,6 +53891,161 @@ var init_run_routing = __esm(() => {
53818
53891
  init_fleet_credentials();
53819
53892
  });
53820
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
+
53821
54049
  // node_modules/@hasna/secrets/dist/sdk.js
53822
54050
  import { isIP as isIP2 } from "net";
53823
54051
  import { spawnSync as spawnSync2 } from "child_process";
@@ -55553,152 +55781,6 @@ var init_selected_run = __esm(() => {
55553
55781
  init_execution_secrets();
55554
55782
  });
55555
55783
 
55556
- // src/lib/cloud-executions.ts
55557
- var exports_cloud_executions = {};
55558
- __export(exports_cloud_executions, {
55559
- CloudExecutionClient: () => CloudExecutionClient
55560
- });
55561
-
55562
- class CloudExecutionClient {
55563
- apiOrigin;
55564
- apiKey;
55565
- constructor(apiOrigin, apiKey) {
55566
- this.apiOrigin = apiOrigin;
55567
- this.apiKey = apiKey;
55568
- }
55569
- static async configured() {
55570
- const c = await resolveSkillsConnection();
55571
- if (!c)
55572
- throw Error("Cloud execution requires a configured Skills API credential");
55573
- return new CloudExecutionClient(c.apiOrigin, c.apiKey);
55574
- }
55575
- async request(route, method = "GET", value) {
55576
- const response = await fetch(skillsApiRequestUrl(this.apiOrigin, "/api/v1/executions/" + route), {
55577
- method,
55578
- headers: {
55579
- authorization: `Bearer ${this.apiKey}`,
55580
- "User-Agent": `hasna-skills/${package_default.version}`,
55581
- ...value === undefined ? {} : { "content-type": "application/json" }
55582
- },
55583
- ...value === undefined ? {} : { body: JSON.stringify(value) },
55584
- redirect: "error",
55585
- signal: AbortSignal.timeout(15000)
55586
- });
55587
- if (!response.ok) {
55588
- response.body?.cancel();
55589
- throw Error(`Skills cloud execution refused: HTTP ${response.status}`);
55590
- }
55591
- return response;
55592
- }
55593
- async submit(slug2, version2, input2, key, selection2) {
55594
- if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2))
55595
- throw Error("Cloud execution requires skill@exact-version");
55596
- if (!/^[A-Za-z0-9._:-]{1,128}$/.test(key))
55597
- throw Error("Invalid idempotency key");
55598
- if (selection2?.authority && selection2.authority !== skillsApiRequestUrl(this.apiOrigin, "/api/v1/").replace(/\/$/, ""))
55599
- throw Error("Selected skill belongs to another Skills authority");
55600
- const execution = parseExecution(await jsonBody(await this.request(slug2, "POST", {
55601
- version: version2,
55602
- input: input2,
55603
- idempotencyKey: key,
55604
- ...selection2 ? {
55605
- bundleDigest: selection2.bundleDigest,
55606
- workspaceId: selection2.workspaceId
55607
- } : {}
55608
- })));
55609
- if (execution.skill !== slug2 || execution.version !== version2 || selection2 && execution.bundleDigest.replace(/^sha256:/, "") !== selection2.bundleDigest.replace(/^sha256:/, ""))
55610
- throw Error("Cloud execution receipt does not match the selected exact skill");
55611
- return execution;
55612
- }
55613
- async get(id2) {
55614
- return parseExecution(await jsonBody(await this.request(runId(id2))));
55615
- }
55616
- async logs(id2) {
55617
- return jsonBody(await this.request(runId(id2) + "/logs"));
55618
- }
55619
- async artifacts(id2) {
55620
- return (await this.get(id2)).artifacts;
55621
- }
55622
- async download(id2, name) {
55623
- if (!["document.pdf", "document.html"].includes(name))
55624
- throw Error("Invalid cloud artifact name");
55625
- const response = await this.request(runId(id2) + "/artifacts/" + encodeURIComponent(name));
55626
- if (!response.body)
55627
- throw Error("Cloud artifact body is missing");
55628
- const reader = response.body.getReader();
55629
- const chunks = [];
55630
- let size = 0;
55631
- for (;; ) {
55632
- const r2 = await reader.read();
55633
- if (r2.done)
55634
- break;
55635
- size += r2.value.length;
55636
- if (size > 2000000) {
55637
- await reader.cancel();
55638
- throw Error("Cloud artifact exceeds limit");
55639
- }
55640
- chunks.push(r2.value);
55641
- }
55642
- return Buffer.concat(chunks);
55643
- }
55644
- async cancel(id2) {
55645
- return parseExecution(await jsonBody(await this.request(runId(id2) + "/cancel", "POST", {})));
55646
- }
55647
- async wait(id2, options = {}) {
55648
- const timeout = options.timeoutMs ?? 300000, interval = options.intervalMs ?? 1000;
55649
- if (!Number.isFinite(timeout) || timeout < 1 || timeout > 1200000 || !Number.isFinite(interval) || interval < 100 || interval > 60000)
55650
- throw Error("Invalid cloud polling bounds");
55651
- const until = Date.now() + timeout;
55652
- for (;; ) {
55653
- const run = await this.get(id2);
55654
- if (["succeeded", "failed", "cancelled"].includes(run.status))
55655
- return run;
55656
- if (Date.now() >= until)
55657
- throw Error("Cloud execution is still running; poll the returned execution id");
55658
- await Bun.sleep(interval);
55659
- }
55660
- }
55661
- }
55662
- function runId(id2) {
55663
- if (!/^run_[a-z0-9_]+$/.test(id2))
55664
- throw Error("Invalid cloud execution id");
55665
- return id2;
55666
- }
55667
- function parseExecution(value) {
55668
- const v = value;
55669
- if (!v || v.contractVersion !== 1 || v.target !== "cloud" || ![
55670
- "admitted",
55671
- "leased",
55672
- "running",
55673
- "succeeded",
55674
- "failed",
55675
- "cancelled"
55676
- ].includes(v.status) || typeof v.skill !== "string" || typeof v.version !== "string" || !Array.isArray(v.artifacts))
55677
- throw Error("Unsupported cloud execution response");
55678
- runId(v.id);
55679
- for (const field of [
55680
- "bundleDigest",
55681
- "inputDigest",
55682
- "runtimeImageDigest"
55683
- ])
55684
- if (typeof v[field] !== "string" || !/^(sha256:)?[a-f0-9]{64}$/.test(v[field]))
55685
- throw Error("Invalid cloud execution integrity receipt");
55686
- return v;
55687
- }
55688
- async function jsonBody(response) {
55689
- const bytes = await readBoundedResponse(response, 1e5);
55690
- try {
55691
- return JSON.parse(new TextDecoder().decode(bytes));
55692
- } catch {
55693
- throw Error("Invalid cloud execution JSON response");
55694
- }
55695
- }
55696
- var init_cloud_executions = __esm(() => {
55697
- init_package();
55698
- init_remote_files();
55699
- init_fleet_credentials();
55700
- });
55701
-
55702
55784
  // src/lib/blog-article.ts
55703
55785
  var exports_blog_article = {};
55704
55786
  __export(exports_blog_article, {
@@ -55833,6 +55915,18 @@ var init_blog_article = __esm(() => {
55833
55915
  import { existsSync as existsSync15, readdirSync as readdirSync9, statSync as statSync10 } from "fs";
55834
55916
  import { join as join24 } from "path";
55835
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
+ });
55836
55930
  server.registerTool("scaffold_skill", {
55837
55931
  title: "Scaffold Skill",
55838
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.",