@hasna/skills 0.10.81 → 0.10.83

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.83";
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
  }
@@ -46227,6 +46239,7 @@ var OPTIONAL_CONTEXT_FAILURES = new Set([
46227
46239
  "CACHED_PROFILE_EXPIRED",
46228
46240
  "CACHED_PROFILE_MISSING",
46229
46241
  "CACHED_BUNDLE_MISSING",
46242
+ "PROFILE_LOCK_MISMATCH",
46230
46243
  "SESSION_RECONCILIATION_REQUIRED",
46231
46244
  "SESSION_RENEWAL_TIMEOUT",
46232
46245
  "SESSION_WRITE_BUSY",
@@ -99515,7 +99528,7 @@ function checkedPublicationView(value, skillId, intentId, declaration) {
99515
99528
  return invalid7();
99516
99529
  return Object.freeze({ ...value });
99517
99530
  }
99518
- async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false) {
99531
+ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal, create = false, readBinding) {
99519
99532
  const controller = new AbortController;
99520
99533
  let reader;
99521
99534
  let response;
@@ -99531,8 +99544,18 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal,
99531
99544
  redirect: "error",
99532
99545
  credentials: "omit",
99533
99546
  signal: controller.signal,
99534
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }
99547
+ headers: {
99548
+ "Content-Type": "application/json",
99549
+ Authorization: `Bearer ${token}`,
99550
+ ...readBinding ? {
99551
+ "x-skills-user-id": readBinding.userId,
99552
+ "x-skills-workspace-id": readBinding.organizationId,
99553
+ "x-skills-membership-id": readBinding.membershipId
99554
+ } : {}
99555
+ }
99535
99556
  });
99557
+ if (readBinding && response.ok && response.status !== 200)
99558
+ return invalid7();
99536
99559
  const length = response.headers.get("content-length");
99537
99560
  if (length !== null && (!/^\d+$/.test(length) || Number(length) > 65536))
99538
99561
  return invalid7();
@@ -99600,6 +99623,88 @@ async function boundedJson(url2, init, token, mutation, budget3 = 15000, signal,
99600
99623
  response?.body?.cancel().catch(() => {});
99601
99624
  }
99602
99625
  }
99626
+ var historyInput = () => {
99627
+ 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.");
99628
+ };
99629
+ var historyResponse = () => {
99630
+ throw new PrivatePublicationError("INVALID_PRIVATE_HISTORY_RESPONSE", "The server returned invalid private version history.");
99631
+ };
99632
+ var historyText = (value) => typeof value === "string" && value.length > 0 && value.length <= 128 && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value);
99633
+ function historyDate(value) {
99634
+ if (typeof value !== "string")
99635
+ return false;
99636
+ 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);
99637
+ if (!parts)
99638
+ return false;
99639
+ const [, y2, m2, d4, separator, h4, minute, second, fraction, zone, offsetHour, offsetMinute] = parts;
99640
+ if (separator === " " ? zone === "Z" || (fraction?.length ?? 0) > 6 : zone !== "Z" && offsetMinute === undefined)
99641
+ return false;
99642
+ const year = Number(y2), month = Number(m2), day = Number(d4);
99643
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
99644
+ const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
99645
+ 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);
99646
+ }
99647
+ function privateHistory(value, organizationId, skillId, limit, cursor2) {
99648
+ 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)
99649
+ return historyResponse();
99650
+ const seen = new Set;
99651
+ let previous = cursor2;
99652
+ const versions2 = value.versions.map((row) => {
99653
+ 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))
99654
+ return historyResponse();
99655
+ seen.add(row.id);
99656
+ previous = row.id;
99657
+ return Object.freeze({ id: row.id, version: row.version, releasedAt: row.releasedAt, createdAt: row.createdAt });
99658
+ });
99659
+ if (value.nextCursor !== null && (!versions2.length || value.nextCursor !== versions2.at(-1).id))
99660
+ return historyResponse();
99661
+ return Object.freeze({
99662
+ organizationId,
99663
+ skillId,
99664
+ slug: value.slug,
99665
+ currentVersion: value.currentVersion,
99666
+ currentVersionId: value.currentVersionId,
99667
+ versions: Object.freeze(versions2),
99668
+ nextCursor: value.nextCursor
99669
+ });
99670
+ }
99671
+
99672
+ class RemotePrivateSkillHistoryClient {
99673
+ apiOrigin;
99674
+ organizationId;
99675
+ userId;
99676
+ membershipId;
99677
+ #token;
99678
+ constructor(apiUrl, session) {
99679
+ const checked = parseWorkspaceSession(session, { userId: session?.user?.id, membershipId: session?.user?.membershipId });
99680
+ this.apiOrigin = normalizeSkillsApiOrigin(apiUrl);
99681
+ this.#token = checked.token;
99682
+ this.organizationId = checked.organization.id;
99683
+ this.userId = checked.user.id;
99684
+ this.membershipId = checked.user.membershipId;
99685
+ Object.freeze(this);
99686
+ }
99687
+ async listVersions(skillId, options = {}) {
99688
+ 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))
99689
+ return historyInput();
99690
+ const limit = options.limit ?? 20, cursor2 = options.cursor;
99691
+ const query = new URLSearchParams;
99692
+ if (options.limit !== undefined)
99693
+ query.set("limit", String(limit));
99694
+ if (cursor2 !== undefined)
99695
+ query.set("cursor", cursor2);
99696
+ const path2 = `/api/v1/skills/${skillId}/versions${query.size ? `?${query}` : ""}`;
99697
+ let value;
99698
+ try {
99699
+ value = await boundedJson(skillsApiRequestUrl(this.apiOrigin, path2), { method: "GET" }, this.#token, false, options.timeoutMs, options.signal, false, this);
99700
+ } catch (error61) {
99701
+ if (error61 instanceof PrivatePublicationError && error61.code === "INVALID_PUBLICATION_RESPONSE")
99702
+ return historyResponse();
99703
+ 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);
99704
+ }
99705
+ return privateHistory(value, this.organizationId, skillId, limit, cursor2);
99706
+ }
99707
+ }
99603
99708
 
99604
99709
  class RemotePrivatePublicationsClient {
99605
99710
  apiOrigin;
@@ -99852,6 +99957,10 @@ class RemoteSkillsAuthClient {
99852
99957
  const origin = this.apiOrigin, captured = workspaceContext(context);
99853
99958
  return new RemotePrivatePublicationsClient(origin, await this.switchWorkspace(email4, code, captured));
99854
99959
  }
99960
+ async openPrivateSkillHistory(email4, code, context) {
99961
+ const origin = this.apiOrigin, captured = workspaceContext(context);
99962
+ return new RemotePrivateSkillHistoryClient(origin, await this.switchWorkspace(email4, code, captured));
99963
+ }
99855
99964
  requestInvitationEmailChallenge(input2) {
99856
99965
  return requestInvitationEmail(this.apiOrigin, "challenge", input2);
99857
99966
  }
@@ -100562,6 +100671,7 @@ export {
100562
100671
  RemoteRecurringReadError,
100563
100672
  RemoteRecurringError,
100564
100673
  RemoteQuoteUnavailableError,
100674
+ RemotePrivateSkillHistoryClient,
100565
100675
  RemotePrivatePublicationsClient,
100566
100676
  RemoteInvitationEmailUnconfirmedError,
100567
100677
  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.83",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {