@hasna/skills 0.10.81 → 0.10.82

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/sdk/index.js CHANGED
@@ -32371,7 +32371,7 @@ var init_event_sink = __esm(() => {
32371
32371
  init_dist();
32372
32372
  });
32373
32373
  // package.json
32374
- var version = "0.10.81";
32374
+ var version = "0.10.82";
32375
32375
  var package_default = {
32376
32376
  name: "@hasna/skills",
32377
32377
  version,
@@ -41268,7 +41268,7 @@ function hashClaudePluginCacheOrphanMarker(path, budget2, changes) {
41268
41268
  if (!before)
41269
41269
  return null;
41270
41270
  const uid = process.getuid?.();
41271
- if (!before.isFile() || uid === undefined || before.uid !== uid || before.size > 32)
41271
+ if (!before.isFile() || uid === undefined || before.uid !== uid || before.size > 32 || (before.mode & 73) !== 0)
41272
41272
  throw new Error("Invalid Claude orphan marker file or owner");
41273
41273
  const fd = openSync13(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
41274
41274
  try {
@@ -41630,7 +41630,7 @@ function safe2(path) {
41630
41630
  function same2(before, after) {
41631
41631
  return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
41632
41632
  }
41633
- function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
41633
+ function capture(path, budget2, excludedRoots, omittedRoots = [], rows, claudeMarkers) {
41634
41634
  safe2(path);
41635
41635
  if (omittedRoots.some((root2) => path === root2 || path.startsWith(root2 + "/")))
41636
41636
  return { path, sha256: null };
@@ -41658,6 +41658,11 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
41658
41658
  need4(child2.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
41659
41659
  const stat = lstatSync13(child2);
41660
41660
  need4(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
41661
+ if (claudeMarkers && isClaudePluginCacheOrphanMarker(child2)) {
41662
+ need4(hashClaudePluginCacheOrphanMarker(child2) !== null, "Claude orphan marker changed while reading directory");
41663
+ if (claudeMarkers === "omit")
41664
+ continue;
41665
+ }
41661
41666
  const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
41662
41667
  `;
41663
41668
  budget2.bytes += Buffer.byteLength(row);
@@ -41714,20 +41719,27 @@ function verifyDiscoveryDirectoryProjection(directories, omittedRoots, tolerance
41714
41719
  witnessChanged(new NativeDiscoveryWitnessChange("membership", directories[i].path, "Native discovery directory membership changed outside retired Codex roots"), tolerance, tolerance?.acceptsMembership(directories[i].path) === true);
41715
41720
  }
41716
41721
  }
41717
- function captureDiscoveryDirectories(paths, excludedRoots = []) {
41722
+ function captureOrdinaryDirectories(paths, excludedRoots, claudeMarkers) {
41718
41723
  need4(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
41719
41724
  const budget2 = { entries: 0, bytes: 0 };
41720
41725
  for (const root of excludedRoots)
41721
41726
  safe2(root);
41722
- return paths.map((path) => capture(path, budget2, excludedRoots));
41727
+ return paths.map((path) => capture(path, budget2, excludedRoots, [], undefined, claudeMarkers));
41728
+ }
41729
+ function captureDiscoveryDirectories(paths, excludedRoots = []) {
41730
+ return captureOrdinaryDirectories(paths, excludedRoots, "omit");
41723
41731
  }
41724
41732
  function verifyDiscoveryDirectories(directories, excludedRoots = [], tolerance) {
41725
41733
  need4(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
41726
41734
  for (const directory of directories)
41727
41735
  need4(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
41728
41736
  const current = captureDiscoveryDirectories(directories.map((directory) => directory.path), excludedRoots);
41737
+ let legacy;
41729
41738
  for (let i = 0;i < directories.length; i++)
41730
41739
  if (current[i].sha256 !== directories[i].sha256) {
41740
+ legacy ??= captureOrdinaryDirectories(directories.map((directory) => directory.path), excludedRoots, "retain");
41741
+ if (legacy[i].sha256 === directories[i].sha256)
41742
+ continue;
41731
41743
  witnessChanged(new NativeDiscoveryWitnessChange("membership", directories[i].path, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`), tolerance, tolerance?.acceptsMembership(directories[i].path) === true);
41732
41744
  }
41733
41745
  }
@@ -99515,7 +99527,7 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
99515
99527
  return invalid7();
99516
99528
  return Object.freeze({ ...value });
99517
99529
  }
99518
- async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false) {
99530
+ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false, readBinding) {
99519
99531
  const controller = new AbortController;
99520
99532
  let reader;
99521
99533
  let response;
@@ -99531,8 +99543,18 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal,
99531
99543
  redirect: "error",
99532
99544
  credentials: "omit",
99533
99545
  signal: controller.signal,
99534
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }
99546
+ headers: {
99547
+ "Content-Type": "application/json",
99548
+ Authorization: `Bearer ${token}`,
99549
+ ...readBinding ? {
99550
+ "x-skills-user-id": readBinding.userId,
99551
+ "x-skills-workspace-id": readBinding.organizationId,
99552
+ "x-skills-membership-id": readBinding.membershipId
99553
+ } : {}
99554
+ }
99535
99555
  });
99556
+ if (readBinding && response.ok && response.status !== 200)
99557
+ return invalid7();
99536
99558
  const length = response.headers.get("content-length");
99537
99559
  if (length !== null && (!/^\d+$/.test(length) || Number(length) > 65536))
99538
99560
  return invalid7();
@@ -99600,6 +99622,88 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal,
99600
99622
  response?.body?.cancel().catch(() => {});
99601
99623
  }
99602
99624
  }
99625
+ var historyInput = () => {
99626
+ throw new PrivatePublicationError("INVALID_PRIVATE_HISTORY_INPUT", "Provide a private skill UUID, limit from 1 to 50, and an optional UUID cursor; timeoutMs must be from 1 to 15000.");
99627
+ };
99628
+ var historyResponse = () => {
99629
+ throw new PrivatePublicationError("INVALID_PRIVATE_HISTORY_RESPONSE", "The server returned invalid private version history.");
99630
+ };
99631
+ var historyText = (value) => typeof value === "string" && value.length > 0 && value.length <= 128 && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value);
99632
+ function historyDate(value) {
99633
+ if (typeof value !== "string")
99634
+ return false;
99635
+ const parts = /^(\d{4})-(\d{2})-(\d{2})(T| )(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-](\d{2})(?::(\d{2}))?)$/.exec(value);
99636
+ if (!parts)
99637
+ return false;
99638
+ const [, y2, m2, d4, separator, h4, minute, second, fraction, zone, offsetHour, offsetMinute] = parts;
99639
+ if (separator === " " ? zone === "Z" || (fraction?.length ?? 0) > 6 : zone !== "Z" && offsetMinute === undefined)
99640
+ return false;
99641
+ const year = Number(y2), month = Number(m2), day = Number(d4);
99642
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
99643
+ const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
99644
+ return year > 0 && month >= 1 && month <= 12 && day >= 1 && day <= days[month - 1] && Number(h4) <= 23 && Number(minute) <= 59 && Number(second) <= 59 && (zone === "Z" || Number(offsetHour) <= 23 && Number(offsetMinute ?? 0) <= 59);
99645
+ }
99646
+ function privateHistory(value, organizationId, skillId, limit, cursor2) {
99647
+ if (!record12(value) || value.organizationId !== organizationId || value.skillId !== skillId || typeof value.slug !== "string" || !/^[a-z0-9-]+$/.test(value.slug) || !(value.currentVersion === null || historyText(value.currentVersion)) || !(value.currentVersionId === null || publicationUuid(value.currentVersionId)) || value.currentVersion === null !== (value.currentVersionId === null) || !(value.nextCursor === null || publicationUuid(value.nextCursor)) || !Array.isArray(value.versions) || value.versions.length > limit)
99648
+ return historyResponse();
99649
+ const seen = new Set;
99650
+ let previous = cursor2;
99651
+ const versions2 = value.versions.map((row) => {
99652
+ if (!record12(row) || !publicationUuid(row.id) || seen.has(row.id) || previous !== undefined && row.id >= previous || !historyText(row.version) || !(row.releasedAt === null || historyDate(row.releasedAt)) || !historyDate(row.createdAt) || row.id === value.currentVersionId !== (row.version === value.currentVersion))
99653
+ return historyResponse();
99654
+ seen.add(row.id);
99655
+ previous = row.id;
99656
+ return Object.freeze({ id: row.id, version: row.version, releasedAt: row.releasedAt, createdAt: row.createdAt });
99657
+ });
99658
+ if (value.nextCursor !== null && (!versions2.length || value.nextCursor !== versions2.at(-1).id))
99659
+ return historyResponse();
99660
+ return Object.freeze({
99661
+ organizationId,
99662
+ skillId,
99663
+ slug: value.slug,
99664
+ currentVersion: value.currentVersion,
99665
+ currentVersionId: value.currentVersionId,
99666
+ versions: Object.freeze(versions2),
99667
+ nextCursor: value.nextCursor
99668
+ });
99669
+ }
99670
+
99671
+ class RemotePrivateSkillHistoryClient {
99672
+ apiOrigin;
99673
+ organizationId;
99674
+ userId;
99675
+ membershipId;
99676
+ #token;
99677
+ constructor(apiUrl, session) {
99678
+ const checked = parseWorkspaceSession(session, { userId: session?.user?.id, membershipId: session?.user?.membershipId });
99679
+ this.apiOrigin = normalizeSkillsApiOrigin(apiUrl);
99680
+ this.#token = checked.token;
99681
+ this.organizationId = checked.organization.id;
99682
+ this.userId = checked.user.id;
99683
+ this.membershipId = checked.user.membershipId;
99684
+ Object.freeze(this);
99685
+ }
99686
+ async listVersions(skillId, options = {}) {
99687
+ if (!publicationUuid(skillId) || options.limit !== undefined && (!Number.isSafeInteger(options.limit) || options.limit < 1 || options.limit > 50) || options.cursor !== undefined && !publicationUuid(options.cursor) || options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 15000))
99688
+ return historyInput();
99689
+ const limit = options.limit ?? 20, cursor2 = options.cursor;
99690
+ const query = new URLSearchParams;
99691
+ if (options.limit !== undefined)
99692
+ query.set("limit", String(limit));
99693
+ if (cursor2 !== undefined)
99694
+ query.set("cursor", cursor2);
99695
+ const path2 = `/api/v1/skills/${skillId}/versions${query.size ? `?${query}` : ""}`;
99696
+ let value;
99697
+ try {
99698
+ value = await boundedJson(skillsApiRequestUrl(this.apiOrigin, path2), { method: "GET" }, this.#token, false, options.timeoutMs, options.signal, false, this);
99699
+ } catch (error61) {
99700
+ if (error61 instanceof PrivatePublicationError && error61.code === "INVALID_PUBLICATION_RESPONSE")
99701
+ return historyResponse();
99702
+ throw new PrivatePublicationError("PRIVATE_HISTORY_UNAVAILABLE", "Unable to read private version history. Check the selected server, current membership and fresh sign-in; no skill or credential was changed.", false, error61 instanceof PrivatePublicationError ? error61.status : undefined);
99703
+ }
99704
+ return privateHistory(value, this.organizationId, skillId, limit, cursor2);
99705
+ }
99706
+ }
99603
99707
 
99604
99708
  class RemotePrivatePublicationsClient {
99605
99709
  apiOrigin;
@@ -99852,6 +99956,10 @@ class RemoteSkillsAuthClient {
99852
99956
  const origin = this.apiOrigin, captured = workspaceContext(context);
99853
99957
  return new RemotePrivatePublicationsClient(origin, await this.switchWorkspace(email4, code, captured));
99854
99958
  }
99959
+ async openPrivateSkillHistory(email4, code, context) {
99960
+ const origin = this.apiOrigin, captured = workspaceContext(context);
99961
+ return new RemotePrivateSkillHistoryClient(origin, await this.switchWorkspace(email4, code, captured));
99962
+ }
99855
99963
  requestInvitationEmailChallenge(input2) {
99856
99964
  return requestInvitationEmail(this.apiOrigin, "challenge", input2);
99857
99965
  }
@@ -100562,6 +100670,7 @@ export {
100562
100670
  RemoteRecurringReadError,
100563
100671
  RemoteRecurringError,
100564
100672
  RemoteQuoteUnavailableError,
100673
+ RemotePrivateSkillHistoryClient,
100565
100674
  RemotePrivatePublicationsClient,
100566
100675
  RemoteInvitationEmailUnconfirmedError,
100567
100676
  RemoteInvitationEmailError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.81",
3
+ "version": "0.10.82",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {