@hasna/skills 0.1.67 → 0.1.69

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/index.js CHANGED
@@ -36860,7 +36860,7 @@ var package_default;
36860
36860
  var init_package = __esm(() => {
36861
36861
  package_default = {
36862
36862
  name: "@hasna/skills",
36863
- version: "0.1.67",
36863
+ version: "0.1.69",
36864
36864
  description: "Skills library for AI coding agents",
36865
36865
  type: "module",
36866
36866
  bin: {
@@ -36882,6 +36882,10 @@ var init_package = __esm(() => {
36882
36882
  "./sdk": {
36883
36883
  import: "./dist/sdk/index.js",
36884
36884
  types: "./dist/sdk/index.d.ts"
36885
+ },
36886
+ "./admin-contract": {
36887
+ import: "./dist/admin-contract.js",
36888
+ types: "./dist/admin-contract.d.ts"
36885
36889
  }
36886
36890
  },
36887
36891
  files: [
@@ -36900,8 +36904,8 @@ var init_package = __esm(() => {
36900
36904
  types: "./dist/index.d.ts",
36901
36905
  scripts: {
36902
36906
  clean: "rm -rf bin/ dist/",
36903
- build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
36904
- "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
36907
+ build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
36908
+ "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
36905
36909
  test: "bun test --timeout 30000",
36906
36910
  dev: "bun run ./src/cli/index.tsx",
36907
36911
  "dev:watch": "bun --watch run ./src/cli/index.tsx",
@@ -62354,8 +62358,8 @@ var require_utils = __commonJS((exports, module) => {
62354
62358
  var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
62355
62359
  var HOST_DELIM_RE = /[@/?#:]/g;
62356
62360
  var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
62357
- function reescapeHostDelimiters(host, isIP) {
62358
- const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
62361
+ function reescapeHostDelimiters(host, isIP2) {
62362
+ const re = isIP2 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
62359
62363
  re.lastIndex = 0;
62360
62364
  return host.replace(re, (ch) => HOST_DELIMS[ch]);
62361
62365
  }
@@ -62804,7 +62808,7 @@ var require_fast_uri = __commonJS((exports, module) => {
62804
62808
  fragment: undefined
62805
62809
  };
62806
62810
  let malformedAuthorityOrPort = false;
62807
- let isIP = false;
62811
+ let isIP2 = false;
62808
62812
  if (options.reference === "suffix") {
62809
62813
  if (options.scheme) {
62810
62814
  uri = options.scheme + ":" + uri;
@@ -62853,9 +62857,9 @@ var require_fast_uri = __commonJS((exports, module) => {
62853
62857
  if (ipv4result === false) {
62854
62858
  const ipv6result = normalizeIPv6(parsed.host);
62855
62859
  parsed.host = ipv6result.host.toLowerCase();
62856
- isIP = ipv6result.isIPV6;
62860
+ isIP2 = ipv6result.isIPV6;
62857
62861
  } else {
62858
- isIP = true;
62862
+ isIP2 = true;
62859
62863
  }
62860
62864
  }
62861
62865
  if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && parsed.query === undefined && !parsed.path) {
@@ -62872,7 +62876,7 @@ var require_fast_uri = __commonJS((exports, module) => {
62872
62876
  }
62873
62877
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
62874
62878
  if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
62875
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
62879
+ if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
62876
62880
  try {
62877
62881
  parsed.host = new URL("http://" + parsed.host).hostname;
62878
62882
  } catch (e) {
@@ -62886,7 +62890,7 @@ var require_fast_uri = __commonJS((exports, module) => {
62886
62890
  parsed.scheme = unescape(parsed.scheme);
62887
62891
  }
62888
62892
  if (parsed.host !== undefined) {
62889
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
62893
+ parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP2);
62890
62894
  }
62891
62895
  }
62892
62896
  if (parsed.path) {
@@ -69902,7 +69906,7 @@ var init_server3 = __esm(() => {
69902
69906
  server = buildServer();
69903
69907
  });
69904
69908
 
69905
- // ../../node_modules/.bun/@hono+node-server@1.19.17+470351d3cbe921c0/node_modules/@hono/node-server/dist/index.mjs
69909
+ // ../../node_modules/.bun/@hono+node-server@1.19.17+804d2c5c04916069/node_modules/@hono/node-server/dist/index.mjs
69906
69910
  import { Readable } from "stream";
69907
69911
  import crypto2 from "crypto";
69908
69912
  var GlobalRequest, Request, newHeadersFromIncoming = (incoming) => {
@@ -74195,7 +74199,7 @@ import process12 from "process";
74195
74199
  // ../../node_modules/.bun/ink@5.2.1+6e93904c7bfe2418/node_modules/ink/build/ink.js
74196
74200
  var import_react10 = __toESM(require_react(), 1);
74197
74201
  import process11 from "process";
74198
- // ../../node_modules/.bun/es-toolkit@1.50.0/node_modules/es-toolkit/dist/function/debounce.mjs
74202
+ // ../../node_modules/.bun/es-toolkit@1.51.0/node_modules/es-toolkit/dist/function/debounce.mjs
74199
74203
  function debounce(func, debounceMs, { signal, edges } = {}) {
74200
74204
  let pendingThis = undefined;
74201
74205
  let pendingArgs = null;
@@ -74253,7 +74257,7 @@ function debounce(func, debounceMs, { signal, edges } = {}) {
74253
74257
  return debounced;
74254
74258
  }
74255
74259
 
74256
- // ../../node_modules/.bun/es-toolkit@1.50.0/node_modules/es-toolkit/dist/compat/function/debounce.mjs
74260
+ // ../../node_modules/.bun/es-toolkit@1.51.0/node_modules/es-toolkit/dist/compat/function/debounce.mjs
74257
74261
  function debounce2(func, debounceMs = 0, options = {}) {
74258
74262
  if (typeof options !== "object")
74259
74263
  options = {};
@@ -74293,7 +74297,7 @@ function debounce2(func, debounceMs = 0, options = {}) {
74293
74297
  return debounced;
74294
74298
  }
74295
74299
 
74296
- // ../../node_modules/.bun/es-toolkit@1.50.0/node_modules/es-toolkit/dist/compat/function/throttle.mjs
74300
+ // ../../node_modules/.bun/es-toolkit@1.51.0/node_modules/es-toolkit/dist/compat/function/throttle.mjs
74297
74301
  function throttle(func, throttleMs = 0, options = {}) {
74298
74302
  const { leading = true, trailing = true } = options;
74299
74303
  return debounce2(func, throttleMs, {
@@ -76209,10 +76213,10 @@ var import_react_reconciler = __toESM(require_react_reconciler(), 1);
76209
76213
  var import_constants = __toESM(require_constants(), 1);
76210
76214
  import process3 from "process";
76211
76215
 
76212
- // ../../node_modules/.bun/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
76216
+ // ../../node_modules/.bun/ansi-regex@6.3.0/node_modules/ansi-regex/index.js
76213
76217
  function ansiRegex({ onlyFirst = false } = {}) {
76214
76218
  const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
76215
- const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
76219
+ const osc = `(?:\\u001B\\][^\\u0007\\u001B\\u009C]*${ST})`;
76216
76220
  const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
76217
76221
  const pattern = `${osc}|${csi}`;
76218
76222
  return new RegExp(pattern, onlyFirst ? undefined : "g");
@@ -79215,8 +79219,12 @@ import { existsSync as existsSync2 } from "fs";
79215
79219
  import { homedir } from "os";
79216
79220
  import { join as join2 } from "path";
79217
79221
  import { createHmac, timingSafeEqual } from "crypto";
79222
+ import { lookup as dnsLookup } from "dns/promises";
79223
+ import { isIP } from "net";
79218
79224
  import { randomUUID } from "crypto";
79219
79225
  import { spawn } from "child_process";
79226
+ import { request as nodeHttpRequest } from "http";
79227
+ import { request as nodeHttpsRequest } from "https";
79220
79228
  import { randomUUID as randomUUID2 } from "crypto";
79221
79229
  function getPathValue(input, path) {
79222
79230
  return path.split(".").reduce((value, part) => {
@@ -79619,6 +79627,214 @@ function signPayload(secret, timestamp, body) {
79619
79627
  const digest = createHmac("sha256", secret).update(buildSignatureBase(timestamp, body)).digest("hex");
79620
79628
  return `sha256=${digest}`;
79621
79629
  }
79630
+ var DEFAULT_MAX_REDIRECTS = 5;
79631
+ var IPV4_PRIVATE_RANGES = [
79632
+ [0, 16777215],
79633
+ [167772160, 184549375],
79634
+ [1681915904, 1686110207],
79635
+ [2130706432, 2147483647],
79636
+ [2851995648, 2852061183],
79637
+ [2886729728, 2887778303],
79638
+ [3221225472, 3221225727],
79639
+ [3221225984, 3221226239],
79640
+ [3227017984, 3227018239],
79641
+ [3232235520, 3232301055],
79642
+ [3323068416, 3323199487],
79643
+ [3325256704, 3325256959],
79644
+ [3405803776, 3405804031],
79645
+ [3758096384, 4294967295]
79646
+ ];
79647
+ var IPV6_SPECIAL_PREFIXES = [
79648
+ { groups: [0, 0, 0, 0, 0, 0, 0, 0], bits: 128 },
79649
+ { groups: [0, 0, 0, 0, 0, 0, 0, 1], bits: 128 },
79650
+ { groups: [0, 0, 0, 0, 0, 65535, 0, 0], bits: 96 },
79651
+ { groups: [100, 65435, 0, 0, 0, 0, 0, 0], bits: 96 },
79652
+ { groups: [256, 0, 0, 0, 0, 0, 0, 0], bits: 64 },
79653
+ { groups: [8193, 0, 0, 0, 0, 0, 0, 0], bits: 32 },
79654
+ { groups: [8193, 2, 0, 0, 0, 0, 0, 0], bits: 48 },
79655
+ { groups: [8193, 16, 0, 0, 0, 0, 0, 0], bits: 28 },
79656
+ { groups: [8193, 3512, 0, 0, 0, 0, 0, 0], bits: 32 },
79657
+ { groups: [8194, 0, 0, 0, 0, 0, 0, 0], bits: 16 },
79658
+ { groups: [16383, 0, 0, 0, 0, 0, 0, 0], bits: 20 },
79659
+ { groups: [64512, 0, 0, 0, 0, 0, 0, 0], bits: 7 },
79660
+ { groups: [65152, 0, 0, 0, 0, 0, 0, 0], bits: 10 },
79661
+ { groups: [65216, 0, 0, 0, 0, 0, 0, 0], bits: 10 },
79662
+ { groups: [65280, 0, 0, 0, 0, 0, 0, 0], bits: 8 }
79663
+ ];
79664
+ function isPrivateAddress(address) {
79665
+ const normalized = stripZoneId(address);
79666
+ const version = isIP(normalized);
79667
+ if (version === 4) {
79668
+ const integer = ipv4ToInt(normalized);
79669
+ if (integer === undefined)
79670
+ return true;
79671
+ return IPV4_PRIVATE_RANGES.some(([low, high]) => integer >= low && integer <= high);
79672
+ }
79673
+ if (version === 6) {
79674
+ const groups = ipv6Groups(normalized);
79675
+ if (!groups)
79676
+ return true;
79677
+ for (const prefix of IPV6_SPECIAL_PREFIXES) {
79678
+ if (!ipv6MatchesPrefix(groups, prefix.groups, prefix.bits))
79679
+ continue;
79680
+ if (prefix.bits === 96 && groups[5] === 65535) {
79681
+ return isPrivateAddress(ipv4IntToString(groups[6] << 16 | groups[7]));
79682
+ }
79683
+ if (prefix.bits === 16 && groups[0] === 8194) {
79684
+ return isPrivateAddress(ipv4IntToString(groups[1] << 16 | groups[2]));
79685
+ }
79686
+ return true;
79687
+ }
79688
+ return false;
79689
+ }
79690
+ return true;
79691
+ }
79692
+ async function resolveWebhookTarget(url, policy = {}) {
79693
+ const hostname = normalizeHostname(url.hostname);
79694
+ const allowlist = (policy.allowPrivateHosts ?? []).map((entry) => normalizeHostname(entry.toLowerCase()));
79695
+ if (allowlist.includes(hostname)) {
79696
+ const version2 = isIP(hostname);
79697
+ if (version2 === 4 || version2 === 6) {
79698
+ return { hostname, addresses: [hostname] };
79699
+ }
79700
+ const lookup2 = policy.lookup ?? defaultTargetLookup;
79701
+ let resolved2;
79702
+ try {
79703
+ resolved2 = await lookup2(hostname);
79704
+ } catch {
79705
+ throw new Error(`Webhook target ${hostname} could not be resolved`);
79706
+ }
79707
+ if (!Array.isArray(resolved2) || resolved2.length === 0) {
79708
+ throw new Error(`Webhook target ${hostname} resolved to no addresses`);
79709
+ }
79710
+ const addresses = resolved2.map((entry) => normalizeHostname(entry.address));
79711
+ return { hostname, addresses };
79712
+ }
79713
+ const version = isIP(hostname);
79714
+ if (version === 4 || version === 6) {
79715
+ if (isPrivateAddress(hostname)) {
79716
+ throw new Error(`Webhook target ${hostname} is a private or special-use address`);
79717
+ }
79718
+ return { hostname, addresses: [hostname] };
79719
+ }
79720
+ const lookup = policy.lookup ?? defaultTargetLookup;
79721
+ let resolved;
79722
+ try {
79723
+ resolved = await lookup(hostname);
79724
+ } catch {
79725
+ throw new Error(`Webhook target ${hostname} could not be resolved`);
79726
+ }
79727
+ if (!Array.isArray(resolved) || resolved.length === 0) {
79728
+ throw new Error(`Webhook target ${hostname} resolved to no addresses`);
79729
+ }
79730
+ const allowed = [];
79731
+ for (const entry of resolved) {
79732
+ const address = normalizeHostname(entry.address);
79733
+ if (isPrivateAddress(address)) {
79734
+ if (allowlist.includes(address)) {
79735
+ allowed.push(address);
79736
+ continue;
79737
+ }
79738
+ throw new Error(`Webhook target ${hostname} resolves to private or special-use address ${address}`);
79739
+ }
79740
+ allowed.push(address);
79741
+ }
79742
+ if (allowed.length === 0) {
79743
+ throw new Error(`Webhook target ${hostname} resolved to no public addresses`);
79744
+ }
79745
+ return { hostname, addresses: allowed };
79746
+ }
79747
+ function normalizeMaxRedirects(value) {
79748
+ if (value === undefined)
79749
+ return DEFAULT_MAX_REDIRECTS;
79750
+ if (!Number.isInteger(value) || value < 0)
79751
+ throw new Error("webhookTargetPolicy.maxRedirects must be a non-negative integer");
79752
+ return value;
79753
+ }
79754
+ var defaultTargetLookup = async (hostname) => {
79755
+ return dnsLookup(hostname, { all: true, verbatim: false });
79756
+ };
79757
+ function normalizeHostname(hostname) {
79758
+ const lower = hostname.toLowerCase();
79759
+ if (lower.startsWith("[") && lower.endsWith("]"))
79760
+ return lower.slice(1, -1);
79761
+ return lower;
79762
+ }
79763
+ function stripZoneId(address) {
79764
+ const percent = address.indexOf("%");
79765
+ return percent === -1 ? address : address.slice(0, percent);
79766
+ }
79767
+ function ipv4ToInt(address) {
79768
+ const parts = address.split(".");
79769
+ if (parts.length !== 4)
79770
+ return;
79771
+ let value = 0;
79772
+ for (const part of parts) {
79773
+ if (!/^\d{1,3}$/.test(part))
79774
+ return;
79775
+ const octet = Number(part);
79776
+ if (octet > 255)
79777
+ return;
79778
+ value = value << 8 | octet;
79779
+ }
79780
+ return value >>> 0;
79781
+ }
79782
+ function ipv4IntToString(integer) {
79783
+ return [
79784
+ integer >>> 24 & 255,
79785
+ integer >>> 16 & 255,
79786
+ integer >>> 8 & 255,
79787
+ integer & 255
79788
+ ].join(".");
79789
+ }
79790
+ function ipv6Groups(address) {
79791
+ const raw = stripZoneId(address);
79792
+ const doubleColon = raw.indexOf("::");
79793
+ const headText = doubleColon === -1 ? raw : raw.slice(0, doubleColon);
79794
+ const tailText = doubleColon === -1 ? "" : raw.slice(doubleColon + 2);
79795
+ const parseGroups = (text) => {
79796
+ if (text === "")
79797
+ return [];
79798
+ const out = [];
79799
+ for (const part of text.split(":")) {
79800
+ if (part.includes(".")) {
79801
+ const v4 = ipv4ToInt(part);
79802
+ if (v4 === undefined)
79803
+ return;
79804
+ out.push(v4 >>> 16 & 65535, v4 & 65535);
79805
+ } else {
79806
+ if (!/^[0-9a-fA-F]{1,4}$/.test(part))
79807
+ return;
79808
+ out.push(parseInt(part, 16));
79809
+ }
79810
+ }
79811
+ return out;
79812
+ };
79813
+ const head2 = parseGroups(headText);
79814
+ if (!head2)
79815
+ return;
79816
+ const tail2 = parseGroups(tailText);
79817
+ if (!tail2)
79818
+ return;
79819
+ const total = head2.length + tail2.length;
79820
+ if (doubleColon === -1) {
79821
+ return total === 8 ? head2 : undefined;
79822
+ }
79823
+ if (total >= 8)
79824
+ return;
79825
+ return [...head2, ...new Array(8 - total).fill(0), ...tail2];
79826
+ }
79827
+ function ipv6MatchesPrefix(groups, prefixGroups, prefixBits) {
79828
+ let remaining = prefixBits;
79829
+ for (let index = 0;index < prefixGroups.length && remaining > 0; index += 1) {
79830
+ const take2 = Math.min(16, remaining);
79831
+ const mask = 65535 << 16 - take2 & 65535;
79832
+ if ((groups[index] & mask) !== (prefixGroups[index] & mask))
79833
+ return false;
79834
+ remaining -= take2;
79835
+ }
79836
+ return true;
79837
+ }
79622
79838
  function now2() {
79623
79839
  return new Date().toISOString();
79624
79840
  }
@@ -79649,9 +79865,18 @@ function buildWebhookRequest(event, channel, options = {}) {
79649
79865
  }
79650
79866
  return { body, headers };
79651
79867
  }
79868
+ function normalizeWebhookUrl(raw) {
79869
+ const url = new URL(raw);
79870
+ if (url.username !== "" || url.password !== "") {
79871
+ url.username = "";
79872
+ url.password = "";
79873
+ }
79874
+ return url.toString();
79875
+ }
79652
79876
  async function dispatchWebhook(event, channel, options = {}) {
79653
79877
  if (!channel.webhook)
79654
79878
  throw new Error(`Channel ${channel.id} has no webhook config`);
79879
+ const webhookUrl = normalizeWebhookUrl(channel.webhook.url);
79655
79880
  const startedAt = now2();
79656
79881
  let secret = channel.webhook.secret;
79657
79882
  if (channel.webhook.secretRef) {
@@ -79668,10 +79893,14 @@ async function dispatchWebhook(event, channel, options = {}) {
79668
79893
  }
79669
79894
  const timestamp = (options.now?.() ?? new Date).toISOString();
79670
79895
  const { body, headers } = buildWebhookRequest(event, channel, { secret, timestamp });
79896
+ const validateTargets = options.webhookTargetPolicy !== undefined || options.fetchImpl === undefined;
79897
+ if (validateTargets) {
79898
+ return dispatchValidatedWebhook(event, channel, { body, headers, startedAt, options });
79899
+ }
79671
79900
  const controller = new AbortController;
79672
79901
  const timeout = setTimeout(() => controller.abort(), channel.webhook.timeoutMs ?? 15000);
79673
79902
  try {
79674
- const response = await (options.fetchImpl ?? fetch)(channel.webhook.url, {
79903
+ const response = await (options.fetchImpl ?? fetch)(webhookUrl, {
79675
79904
  method: "POST",
79676
79905
  headers,
79677
79906
  body,
@@ -79699,6 +79928,130 @@ async function dispatchWebhook(event, channel, options = {}) {
79699
79928
  clearTimeout(timeout);
79700
79929
  }
79701
79930
  }
79931
+ function isRedirectStatus(status) {
79932
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
79933
+ }
79934
+ function redirectKeepsBody(status) {
79935
+ return status === 307 || status === 308;
79936
+ }
79937
+ async function pinnedNativeRequest(target, addresses, method2, headers, body, signal, tls) {
79938
+ const isHttps = target.protocol === "https:";
79939
+ if (!isHttps && target.protocol !== "http:") {
79940
+ throw new Error(`Webhook target uses unsupported protocol ${target.protocol}`);
79941
+ }
79942
+ const defaultPort = isHttps ? 443 : 80;
79943
+ const port = target.port ? Number(target.port) : defaultPort;
79944
+ const requestOptions = {
79945
+ hostname: target.hostname,
79946
+ port,
79947
+ path: `${target.pathname}${target.search}`,
79948
+ method: method2,
79949
+ headers,
79950
+ ...tls?.ca ? { ca: tls.ca } : {},
79951
+ lookup: (hostname, _options, callback) => {
79952
+ const entries = addresses.map((address) => ({
79953
+ address,
79954
+ family: address.includes(":") ? 6 : 4
79955
+ }));
79956
+ callback(null, entries);
79957
+ }
79958
+ };
79959
+ return new Promise((resolve, reject2) => {
79960
+ const request = isHttps ? nodeHttpsRequest(requestOptions, onResponse) : nodeHttpRequest(requestOptions, onResponse);
79961
+ const onAbort = () => {
79962
+ const error = new Error("The operation was aborted.");
79963
+ error.name = "AbortError";
79964
+ request.destroy(error);
79965
+ };
79966
+ if (signal.aborted)
79967
+ onAbort();
79968
+ else
79969
+ signal.addEventListener("abort", onAbort, { once: true });
79970
+ request.on("error", reject2);
79971
+ if (body !== undefined)
79972
+ request.write(body);
79973
+ request.end();
79974
+ function onResponse(response) {
79975
+ const chunks = [];
79976
+ response.on("data", (chunk2) => chunks.push(Buffer.from(chunk2)));
79977
+ response.on("error", reject2);
79978
+ response.on("end", () => {
79979
+ const headersRecord = {};
79980
+ for (const [name, value] of Object.entries(response.headers)) {
79981
+ if (typeof value === "string")
79982
+ headersRecord[name] = value;
79983
+ else if (Array.isArray(value))
79984
+ headersRecord[name] = value.join(", ");
79985
+ }
79986
+ resolve(new Response(Buffer.concat(chunks), { status: response.statusCode ?? 200, headers: headersRecord }));
79987
+ });
79988
+ }
79989
+ });
79990
+ }
79991
+ async function dispatchValidatedWebhook(event, channel, input) {
79992
+ const { body, headers, startedAt, options } = input;
79993
+ const webhook = channel.webhook;
79994
+ if (!webhook)
79995
+ throw new Error(`Channel ${channel.id} has no webhook config`);
79996
+ const policy = options.webhookTargetPolicy ?? {};
79997
+ const maxRedirects = normalizeMaxRedirects(policy.maxRedirects);
79998
+ const controller = new AbortController;
79999
+ const timeout = setTimeout(() => controller.abort(), webhook.timeoutMs ?? 15000);
80000
+ try {
80001
+ let target = new URL(normalizeWebhookUrl(webhook.url));
80002
+ let requestHeaders = headers;
80003
+ let method2 = "POST";
80004
+ let requestBody = body;
80005
+ let redirectsFollowed = 0;
80006
+ for (;; ) {
80007
+ const resolved = await resolveWebhookTarget(target, policy).catch((error) => {
80008
+ throw new Error(`Webhook target rejected by SSRF guard: ${error.message}`);
80009
+ });
80010
+ const response = options.fetchImpl ? await options.fetchImpl(target, {
80011
+ method: method2,
80012
+ headers: requestHeaders,
80013
+ body: requestBody,
80014
+ signal: controller.signal,
80015
+ redirect: "manual"
80016
+ }) : await pinnedNativeRequest(target, resolved.addresses, method2, requestHeaders, requestBody, controller.signal, options.tls);
80017
+ const location = response.headers.get("location");
80018
+ if (isRedirectStatus(response.status) && location) {
80019
+ if (redirectsFollowed >= maxRedirects) {
80020
+ return failedAttempt(startedAt, `Webhook target exceeded ${maxRedirects} redirects`);
80021
+ }
80022
+ redirectsFollowed += 1;
80023
+ const next = new URL(location, target);
80024
+ target = next;
80025
+ if (!redirectKeepsBody(response.status)) {
80026
+ method2 = "GET";
80027
+ requestBody = undefined;
80028
+ requestHeaders = Object.fromEntries(Object.entries(requestHeaders).filter(([name]) => name.toLowerCase() !== "content-type" && name.toLowerCase() !== "content-length"));
80029
+ }
80030
+ continue;
80031
+ }
80032
+ const responseBody = truncate2(await response.text());
80033
+ return {
80034
+ attempt: 1,
80035
+ status: response.ok ? "success" : "failed",
80036
+ startedAt,
80037
+ completedAt: now2(),
80038
+ responseStatus: response.status,
80039
+ responseBody,
80040
+ error: response.ok ? undefined : `Webhook returned HTTP ${response.status}`
80041
+ };
80042
+ }
80043
+ } catch (error) {
80044
+ return {
80045
+ attempt: 1,
80046
+ status: "failed",
80047
+ startedAt,
80048
+ completedAt: now2(),
80049
+ error: error instanceof Error ? error.message : String(error)
80050
+ };
80051
+ } finally {
80052
+ clearTimeout(timeout);
80053
+ }
80054
+ }
79702
80055
  function failedAttempt(startedAt, error) {
79703
80056
  return {
79704
80057
  attempt: 1,
@@ -79908,7 +80261,9 @@ class EventsClient {
79908
80261
  this.transportOptions = {
79909
80262
  fetchImpl: options.fetchImpl,
79910
80263
  secretResolver: options.secretResolver,
79911
- now: options.now
80264
+ now: options.now,
80265
+ tls: options.tls,
80266
+ webhookTargetPolicy: options.webhookTargetPolicy
79912
80267
  };
79913
80268
  this.catalog = options.catalog ?? defaultEventTypeCatalog;
79914
80269
  this.validateCatalogTypes = options.validateCatalogTypes ?? false;
@@ -81508,7 +81863,13 @@ if (process.argv.includes("--no-color")) {
81508
81863
  }
81509
81864
  var program2 = new Command;
81510
81865
  program2.name("skills").description("Discover and run AI agent skills through the Skills CLI/MCP").version(package_default.version).option("--verbose", "Enable verbose logging", false).option("--no-color", "Disable colored output (also respects NO_COLOR env var)").enablePositionalOptions();
81511
- program2.command("interactive", { isDefault: true }).alias("i").description("Interactive skill browser (TUI)").action(() => {
81866
+ program2.command("interactive", { isDefault: true }).alias("i").allowExcessArguments(true).description("Interactive skill browser (TUI)").action((_options, command) => {
81867
+ const stray = command.args[0];
81868
+ if (stray !== undefined) {
81869
+ const verbs = program2.commands.map((c) => c.name()).filter((n) => n !== "interactive").sort();
81870
+ console.error(source_default.red(`error: unknown command '${stray}'. Valid commands: ${verbs.join(", ")}`));
81871
+ process.exit(1);
81872
+ }
81512
81873
  if (!isTTY2) {
81513
81874
  console.log(JSON.stringify(loadBasicRegistry().map(getCompactSkillDiscovery)));
81514
81875
  process.exit(0);
package/bin/mcp.js CHANGED
@@ -12943,7 +12943,7 @@ class StdioServerTransport {
12943
12943
  // package.json
12944
12944
  var package_default = {
12945
12945
  name: "@hasna/skills",
12946
- version: "0.1.67",
12946
+ version: "0.1.69",
12947
12947
  description: "Skills library for AI coding agents",
12948
12948
  type: "module",
12949
12949
  bin: {
@@ -12965,6 +12965,10 @@ var package_default = {
12965
12965
  "./sdk": {
12966
12966
  import: "./dist/sdk/index.js",
12967
12967
  types: "./dist/sdk/index.d.ts"
12968
+ },
12969
+ "./admin-contract": {
12970
+ import: "./dist/admin-contract.js",
12971
+ types: "./dist/admin-contract.d.ts"
12968
12972
  }
12969
12973
  },
12970
12974
  files: [
@@ -12983,8 +12987,8 @@ var package_default = {
12983
12987
  types: "./dist/index.d.ts",
12984
12988
  scripts: {
12985
12989
  clean: "rm -rf bin/ dist/",
12986
- build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
12987
- "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
12990
+ build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
12991
+ "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
12988
12992
  test: "bun test --timeout 30000",
12989
12993
  dev: "bun run ./src/cli/index.tsx",
12990
12994
  "dev:watch": "bun --watch run ./src/cli/index.tsx",
@@ -26944,7 +26948,7 @@ var server = buildServer();
26944
26948
  // src/mcp/http.ts
26945
26949
  import { createServer } from "http";
26946
26950
 
26947
- // ../../node_modules/.bun/@hono+node-server@1.19.17+470351d3cbe921c0/node_modules/@hono/node-server/dist/index.mjs
26951
+ // ../../node_modules/.bun/@hono+node-server@1.19.17+804d2c5c04916069/node_modules/@hono/node-server/dist/index.mjs
26948
26952
  import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
26949
26953
  import { Http2ServerRequest } from "http2";
26950
26954
  import { Readable } from "stream";