@hasna/skills 0.10.39 → 0.10.41
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/README.md +26 -0
- package/bin/index.js +222 -41
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/reviewed-runtime-lock.d.ts +17 -0
- package/dist/cli/commands/runtime-copyfile.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2418,3 +2418,29 @@ Migration compares the original witness before projecting the new contract,
|
|
|
2418
2418
|
refuses native discovery or unknown control changes, and changes policy only.
|
|
2419
2419
|
After explicit migration, ordinary supported local MCP updates require no
|
|
2420
2420
|
coordinated policy write.
|
|
2421
|
+
|
|
2422
|
+
### Reproduce a reviewed runtime dependency closure
|
|
2423
|
+
|
|
2424
|
+
For an exact-version copyfile update, supply an owner-private absolute npm
|
|
2425
|
+
lockfile path and its reviewed SHA-256 together:
|
|
2426
|
+
|
|
2427
|
+
```sh
|
|
2428
|
+
skills self-update --version <version> --reviewed-lock /absolute/package-lock.json \
|
|
2429
|
+
--reviewed-lock-sha256 <sha256> --min-release-age 7 --json
|
|
2430
|
+
```
|
|
2431
|
+
|
|
2432
|
+
Use the same lock for a separately reviewed bootstrap. The lock must describe
|
|
2433
|
+
the verified package archive as `file:./verified.tgz`, use lockfile format 3,
|
|
2434
|
+
and bind its version, integrity and dependency declarations. Registry packages
|
|
2435
|
+
must have exact versions and SHA-512 integrity at the configured npm registry.
|
|
2436
|
+
The updater verifies each frozen version against current registry metadata
|
|
2437
|
+
and the requested age policy, runs `npm ci` without re-resolving ranges, then
|
|
2438
|
+
checks lock byte continuity and the actual required dependency closure with
|
|
2439
|
+
`npm sbom --sbom-format=cyclonedx --package-lock-only=false`. This forces
|
|
2440
|
+
installed-manifest inspection; ordinary `npm ls` can trust a hidden lockfile.
|
|
2441
|
+
Existing publisher exemptions remain available through the repeatable
|
|
2442
|
+
`--min-release-age-exclude` option. The receipt records the reviewed lock hash.
|
|
2443
|
+
|
|
2444
|
+
A reviewed lock does not authorize native skill loading, profile changes,
|
|
2445
|
+
credential provisioning or session reconciliation. Without this optional input,
|
|
2446
|
+
the existing update flow continues to resolve its dependency lock normally.
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.41",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -110034,8 +110034,139 @@ var init_remote_account2 = __esm(() => {
|
|
|
110034
110034
|
init_remote_client();
|
|
110035
110035
|
});
|
|
110036
110036
|
|
|
110037
|
+
// src/cli/commands/reviewed-runtime-lock.ts
|
|
110038
|
+
import { createHash as createHash30 } from "crypto";
|
|
110039
|
+
function row(value) {
|
|
110040
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
110041
|
+
throw new Error("REVIEWED_LOCK_SHAPE_INVALID");
|
|
110042
|
+
return value;
|
|
110043
|
+
}
|
|
110044
|
+
function same4(a, b) {
|
|
110045
|
+
const entries = (v) => Object.entries(row(v ?? {})).sort(([a2], [b2]) => a2.localeCompare(b2));
|
|
110046
|
+
return JSON.stringify(entries(a)) === JSON.stringify(entries(b));
|
|
110047
|
+
}
|
|
110048
|
+
function packageName(path) {
|
|
110049
|
+
const parts = path.split("/node_modules/");
|
|
110050
|
+
if (!parts[0].startsWith("node_modules/"))
|
|
110051
|
+
throw new Error("REVIEWED_LOCK_LOCATION_INVALID");
|
|
110052
|
+
parts[0] = parts[0].slice("node_modules/".length);
|
|
110053
|
+
if (parts.some((name) => !NAME.test(name) || name.split("/").some((part) => part === "." || part === "..")))
|
|
110054
|
+
throw new Error("REVIEWED_LOCK_LOCATION_INVALID");
|
|
110055
|
+
return parts.at(-1);
|
|
110056
|
+
}
|
|
110057
|
+
function excluded(name, patterns) {
|
|
110058
|
+
return patterns.some((pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`).test(name));
|
|
110059
|
+
}
|
|
110060
|
+
async function metadata(name, context2) {
|
|
110061
|
+
const url2 = new URL(encodeURIComponent(name), `${context2.registryOrigin}/`);
|
|
110062
|
+
const response = await (context2.fetcher ?? fetch)(url2, { redirect: "error", signal: AbortSignal.timeout(30000) });
|
|
110063
|
+
if (!response.ok || !response.body)
|
|
110064
|
+
throw new Error("REVIEWED_LOCK_METADATA_UNAVAILABLE");
|
|
110065
|
+
const reader = response.body.getReader();
|
|
110066
|
+
const chunks = [];
|
|
110067
|
+
let size2 = 0;
|
|
110068
|
+
try {
|
|
110069
|
+
for (;; ) {
|
|
110070
|
+
const { done, value } = await reader.read();
|
|
110071
|
+
if (done)
|
|
110072
|
+
break;
|
|
110073
|
+
size2 += value.byteLength;
|
|
110074
|
+
if (size2 > MAX_METADATA_BYTES) {
|
|
110075
|
+
await reader.cancel();
|
|
110076
|
+
throw new Error("REVIEWED_LOCK_METADATA_TOO_LARGE");
|
|
110077
|
+
}
|
|
110078
|
+
chunks.push(value);
|
|
110079
|
+
}
|
|
110080
|
+
} finally {
|
|
110081
|
+
reader.releaseLock();
|
|
110082
|
+
}
|
|
110083
|
+
try {
|
|
110084
|
+
return row(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
110085
|
+
} catch {
|
|
110086
|
+
throw new Error("REVIEWED_LOCK_METADATA_INVALID");
|
|
110087
|
+
}
|
|
110088
|
+
}
|
|
110089
|
+
async function validateReviewedRuntimeLock(bytes, sha2562, context2) {
|
|
110090
|
+
if (!/^[a-f0-9]{64}$/.test(sha2562))
|
|
110091
|
+
throw new Error("REVIEWED_LOCK_SHA256_INVALID");
|
|
110092
|
+
if (bytes.byteLength > MAX_LOCK_BYTES || createHash30("sha256").update(bytes).digest("hex") !== sha2562)
|
|
110093
|
+
throw new Error("REVIEWED_LOCK_HASH_MISMATCH");
|
|
110094
|
+
if (!Number.isSafeInteger(context2.minReleaseAge) || context2.minReleaseAge < 1)
|
|
110095
|
+
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REQUIRED");
|
|
110096
|
+
let lock;
|
|
110097
|
+
try {
|
|
110098
|
+
lock = row(JSON.parse(Buffer.from(bytes).toString("utf8")));
|
|
110099
|
+
} catch {
|
|
110100
|
+
throw new Error("REVIEWED_LOCK_SHAPE_INVALID");
|
|
110101
|
+
}
|
|
110102
|
+
if (lock.lockfileVersion !== 3 || lock.requires !== true)
|
|
110103
|
+
throw new Error("REVIEWED_LOCK_FORMAT_UNSUPPORTED");
|
|
110104
|
+
const packages = row(lock.packages);
|
|
110105
|
+
if (Object.keys(packages).length > 5000)
|
|
110106
|
+
throw new Error("REVIEWED_LOCK_POPULATION_TOO_LARGE");
|
|
110107
|
+
const root2 = row(packages[""]);
|
|
110108
|
+
if (!same4(root2.dependencies, { [PACKAGE2]: "file:./verified.tgz" }) || Object.keys(row(root2.devDependencies ?? {})).length || Object.keys(row(root2.optionalDependencies ?? {})).length)
|
|
110109
|
+
throw new Error("REVIEWED_LOCK_ROOT_MISMATCH");
|
|
110110
|
+
const target = row(packages[`node_modules/${PACKAGE2}`]);
|
|
110111
|
+
if (target.version !== context2.version || target.resolved !== "file:verified.tgz" || target.integrity !== context2.archiveIntegrity || target.name !== undefined && target.name !== PACKAGE2)
|
|
110112
|
+
throw new Error("REVIEWED_LOCK_ARCHIVE_MISMATCH");
|
|
110113
|
+
for (const field2 of ["dependencies", "optionalDependencies", "peerDependencies"]) {
|
|
110114
|
+
if (!same4(target[field2], context2.packageManifest[field2]))
|
|
110115
|
+
throw new Error("REVIEWED_LOCK_MANIFEST_MISMATCH");
|
|
110116
|
+
}
|
|
110117
|
+
const registry2 = new URL(context2.registryOrigin);
|
|
110118
|
+
const identities = new Map;
|
|
110119
|
+
for (const [location, value] of Object.entries(packages)) {
|
|
110120
|
+
if (!location)
|
|
110121
|
+
continue;
|
|
110122
|
+
const name = packageName(location), entry = row(value);
|
|
110123
|
+
if (entry.link || entry.dev || entry.name !== undefined && entry.name !== name)
|
|
110124
|
+
throw new Error("REVIEWED_LOCK_LOCATION_INVALID");
|
|
110125
|
+
if (location === `node_modules/${PACKAGE2}`)
|
|
110126
|
+
continue;
|
|
110127
|
+
if (typeof entry.version !== "string" || !VERSION.test(entry.version) || typeof entry.resolved !== "string" || typeof entry.integrity !== "string" || !INTEGRITY.test(entry.integrity))
|
|
110128
|
+
throw new Error("REVIEWED_LOCK_PACKAGE_INVALID");
|
|
110129
|
+
let url2;
|
|
110130
|
+
try {
|
|
110131
|
+
url2 = new URL(entry.resolved);
|
|
110132
|
+
} catch {
|
|
110133
|
+
throw new Error("REVIEWED_LOCK_REGISTRY_INVALID");
|
|
110134
|
+
}
|
|
110135
|
+
if (url2.origin !== registry2.origin || url2.username || url2.password || url2.search || url2.hash || !url2.pathname.endsWith(".tgz"))
|
|
110136
|
+
throw new Error("REVIEWED_LOCK_REGISTRY_INVALID");
|
|
110137
|
+
const entries = identities.get(name) ?? [];
|
|
110138
|
+
entries.push({ name, version: entry.version, resolved: entry.resolved, integrity: entry.integrity });
|
|
110139
|
+
identities.set(name, entries);
|
|
110140
|
+
}
|
|
110141
|
+
const names = [...identities.keys()];
|
|
110142
|
+
const now3 = context2.now ?? Date.now();
|
|
110143
|
+
for (let offset = 0;offset < names.length; offset += 6) {
|
|
110144
|
+
await Promise.all(names.slice(offset, offset + 6).map(async (name) => {
|
|
110145
|
+
const data = await metadata(name, context2), versions2 = row(data.versions), times2 = row(data.time);
|
|
110146
|
+
if (data.name !== name)
|
|
110147
|
+
throw new Error("REVIEWED_LOCK_METADATA_IDENTITY_MISMATCH");
|
|
110148
|
+
for (const entry of identities.get(name)) {
|
|
110149
|
+
const version2 = row(versions2[entry.version]), dist = row(version2.dist);
|
|
110150
|
+
if (version2.name !== name || version2.version !== entry.version || dist.integrity !== entry.integrity || dist.tarball !== entry.resolved)
|
|
110151
|
+
throw new Error("REVIEWED_LOCK_METADATA_IDENTITY_MISMATCH");
|
|
110152
|
+
const published = typeof times2[entry.version] === "string" ? Date.parse(times2[entry.version]) : NaN;
|
|
110153
|
+
if (!Number.isFinite(published) || published > now3 || !excluded(name, context2.minReleaseAgeExclude) && now3 - published < context2.minReleaseAge * 86400000)
|
|
110154
|
+
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REFUSED");
|
|
110155
|
+
}
|
|
110156
|
+
}));
|
|
110157
|
+
}
|
|
110158
|
+
}
|
|
110159
|
+
var PACKAGE2 = "@hasna/skills", NAME, VERSION, INTEGRITY, MAX_LOCK_BYTES, MAX_METADATA_BYTES;
|
|
110160
|
+
var init_reviewed_runtime_lock = __esm(() => {
|
|
110161
|
+
NAME = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
|
|
110162
|
+
VERSION = /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?(?:\+[a-zA-Z0-9.-]+)?$/;
|
|
110163
|
+
INTEGRITY = /^sha512-[A-Za-z0-9+/]{86}==$/;
|
|
110164
|
+
MAX_LOCK_BYTES = 8 * 1024 * 1024;
|
|
110165
|
+
MAX_METADATA_BYTES = 32 * 1024 * 1024;
|
|
110166
|
+
});
|
|
110167
|
+
|
|
110037
110168
|
// src/cli/commands/runtime-copyfile.ts
|
|
110038
|
-
import { createHash as
|
|
110169
|
+
import { createHash as createHash31, randomUUID as randomUUID9 } from "crypto";
|
|
110039
110170
|
import { Readable } from "stream";
|
|
110040
110171
|
import { createGunzip as createGunzip2 } from "zlib";
|
|
110041
110172
|
import {
|
|
@@ -110059,7 +110190,7 @@ function object9(value, code2) {
|
|
|
110059
110190
|
return value;
|
|
110060
110191
|
}
|
|
110061
110192
|
function hash5(bytes) {
|
|
110062
|
-
return
|
|
110193
|
+
return createHash31("sha256").update(bytes).digest("hex");
|
|
110063
110194
|
}
|
|
110064
110195
|
function isWithin(parent, child) {
|
|
110065
110196
|
const rel = relative10(parent, child);
|
|
@@ -110210,7 +110341,7 @@ function verifyIntegrity(bytes, integrity) {
|
|
|
110210
110341
|
const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity);
|
|
110211
110342
|
if (!match)
|
|
110212
110343
|
throw new Error("REGISTRY_INTEGRITY_UNSUPPORTED");
|
|
110213
|
-
const actual =
|
|
110344
|
+
const actual = createHash31("sha512").update(bytes).digest("base64");
|
|
110214
110345
|
if (actual !== match[1])
|
|
110215
110346
|
throw new Error("TARBALL_INTEGRITY_MISMATCH");
|
|
110216
110347
|
}
|
|
@@ -110322,10 +110453,10 @@ async function downloadPackage(version2, stagingRoot, registryOrigin, fetcher) {
|
|
|
110322
110453
|
const metadataResponse = await fetcher(metadataUrl, { redirect: "error", signal: timeout });
|
|
110323
110454
|
if (!metadataResponse.ok || new URL(metadataResponse.url || metadataUrl).origin !== new URL(registryOrigin).origin)
|
|
110324
110455
|
throw new Error("REGISTRY_METADATA_FETCH_FAILED");
|
|
110325
|
-
const
|
|
110326
|
-
if (
|
|
110456
|
+
const metadata2 = object9(JSON.parse(new TextDecoder().decode(await readBodyCapped(metadataResponse, 5 * 1024 * 1024, "REGISTRY_METADATA_SIZE_INVALID"))), "REGISTRY_METADATA_INVALID");
|
|
110457
|
+
if (metadata2.name !== PACKAGE_NAME || metadata2.version !== version2 || metadata2._id !== `${PACKAGE_NAME}@${version2}`)
|
|
110327
110458
|
throw new Error("REGISTRY_PACKAGE_IDENTITY_MISMATCH");
|
|
110328
|
-
const dist = object9(
|
|
110459
|
+
const dist = object9(metadata2.dist, "REGISTRY_DIST_INVALID");
|
|
110329
110460
|
const integrity = typeof dist.integrity === "string" ? dist.integrity : "";
|
|
110330
110461
|
const tarballUrl = expectedTarballUrl(dist.tarball, registryOrigin, version2);
|
|
110331
110462
|
const response = await fetcher(tarballUrl, { redirect: "error", signal: AbortSignal.timeout(30000) });
|
|
@@ -110996,6 +111127,24 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
110996
111127
|
if (!STABLE_SEMVER_PATTERN.test(version2))
|
|
110997
111128
|
throw new Error("EXACT_STABLE_VERSION_REQUIRED");
|
|
110998
111129
|
const policy = releaseAgePolicy(options);
|
|
111130
|
+
let reviewedLockBytes;
|
|
111131
|
+
if (options.reviewedLock !== undefined || options.reviewedLockSha256 !== undefined) {
|
|
111132
|
+
if (!options.reviewedLock || !options.reviewedLockSha256)
|
|
111133
|
+
throw new Error("REVIEWED_LOCK_INPUT_PAIR_REQUIRED");
|
|
111134
|
+
if (!policy)
|
|
111135
|
+
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REQUIRED");
|
|
111136
|
+
if (!isAbsolute25(options.reviewedLock))
|
|
111137
|
+
throw new Error("REVIEWED_LOCK_ABSOLUTE_PATH_REQUIRED");
|
|
111138
|
+
assertNoSymlinkAncestors(dirname31(options.reviewedLock));
|
|
111139
|
+
const stat3 = lstatSync30(options.reviewedLock);
|
|
111140
|
+
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.nlink !== 1 || stat3.uid !== process.getuid?.() || stat3.mode & 63 || stat3.size > 8 * 1024 * 1024)
|
|
111141
|
+
throw new Error("REVIEWED_LOCK_FILE_UNSAFE");
|
|
111142
|
+
reviewedLockBytes = readFileSync38(options.reviewedLock);
|
|
111143
|
+
if (!/^[a-f0-9]{64}$/.test(options.reviewedLockSha256))
|
|
111144
|
+
throw new Error("REVIEWED_LOCK_SHA256_INVALID");
|
|
111145
|
+
if (hash5(reviewedLockBytes) !== options.reviewedLockSha256)
|
|
111146
|
+
throw new Error("REVIEWED_LOCK_HASH_MISMATCH");
|
|
111147
|
+
}
|
|
110999
111148
|
const home = resolve25(options.homeDir ?? process.env.HOME ?? "");
|
|
111000
111149
|
if (!home || !existsSync34(home))
|
|
111001
111150
|
throw new Error("HOME_NOT_FOUND");
|
|
@@ -111032,12 +111181,37 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
111032
111181
|
const stagePackage = join58(stagePath, "install");
|
|
111033
111182
|
mkdirSync24(stagePackage, { mode: 448 });
|
|
111034
111183
|
copyFileSync(artifact.tarballPath, join58(stagePackage, "verified.tgz"));
|
|
111035
|
-
writeFileSync23(join58(stagePackage, "package.json"), JSON.stringify({ private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
|
|
111036
|
-
|
|
111184
|
+
writeFileSync23(join58(stagePackage, "package.json"), JSON.stringify({ name: "skills-runtime-install", version: "0.0.0", private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
|
|
111185
|
+
let npmVersion;
|
|
111186
|
+
if (reviewedLockBytes) {
|
|
111187
|
+
await validateReviewedRuntimeLock(reviewedLockBytes, options.reviewedLockSha256, {
|
|
111188
|
+
version: version2,
|
|
111189
|
+
archiveIntegrity: artifact.integrity,
|
|
111190
|
+
packageManifest: object9(JSON.parse(readFileSync38(join58(stagePath, "verified-archive", "package", "package.json"), "utf8")), "PACKAGE_MANIFEST_INVALID"),
|
|
111191
|
+
registryOrigin: REGISTRY_ORIGIN,
|
|
111192
|
+
...policy,
|
|
111193
|
+
fetcher
|
|
111194
|
+
});
|
|
111195
|
+
writeFileSync23(join58(stagePackage, "package-lock.json"), reviewedLockBytes, { mode: 384, flag: "wx" });
|
|
111196
|
+
if (!readFileSync38(join58(stagePackage, "package-lock.json")).equals(reviewedLockBytes))
|
|
111197
|
+
throw new Error("REVIEWED_LOCK_COPY_MISMATCH");
|
|
111198
|
+
} else {
|
|
111199
|
+
npmVersion = await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
111200
|
+
}
|
|
111037
111201
|
const ciNpmVersion = await runNpm(["ci", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
111038
|
-
if (npmVersion !== ciNpmVersion)
|
|
111202
|
+
if (!reviewedLockBytes && npmVersion !== ciNpmVersion)
|
|
111039
111203
|
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
111204
|
+
npmVersion = ciNpmVersion;
|
|
111040
111205
|
const lockBytes = readFileSync38(join58(stagePackage, "package-lock.json"));
|
|
111206
|
+
if (reviewedLockBytes) {
|
|
111207
|
+
if (!lockBytes.equals(reviewedLockBytes))
|
|
111208
|
+
throw new Error("REVIEWED_LOCK_DRIFT_DURING_INSTALL");
|
|
111209
|
+
const treeNpmVersion = await runNpm(["sbom", "--sbom-format=cyclonedx", "--package-lock-only=false", "--omit=dev", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
111210
|
+
if (treeNpmVersion !== npmVersion)
|
|
111211
|
+
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
111212
|
+
if (!readFileSync38(join58(stagePackage, "package-lock.json")).equals(reviewedLockBytes))
|
|
111213
|
+
throw new Error("REVIEWED_LOCK_DRIFT_DURING_INSTALL");
|
|
111214
|
+
}
|
|
111041
111215
|
const installedPackage = join58(stagePackage, "node_modules", "@hasna", "skills");
|
|
111042
111216
|
const installed = readPackage(join58(installedPackage, "package.json"));
|
|
111043
111217
|
if (installed.version !== version2 || JSON.stringify(Object.entries(installed.bins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
|
|
@@ -111078,6 +111252,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
111078
111252
|
configs: configPreimages,
|
|
111079
111253
|
preimageSha256,
|
|
111080
111254
|
...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {},
|
|
111255
|
+
...reviewedLockBytes ? { reviewedLockSha256: options.reviewedLockSha256 } : {},
|
|
111081
111256
|
launchers,
|
|
111082
111257
|
switchedLaunchers: [],
|
|
111083
111258
|
rollbackCompletedLaunchers: []
|
|
@@ -111116,7 +111291,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
111116
111291
|
}
|
|
111117
111292
|
receipt.state = "switched";
|
|
111118
111293
|
persistReceipt(join58(finalPath, "rollout-receipt.json"), receipt);
|
|
111119
|
-
return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length, ...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {} };
|
|
111294
|
+
return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length, ...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {}, ...reviewedLockBytes ? { reviewedLockSha256: options.reviewedLockSha256 } : {} };
|
|
111120
111295
|
} catch (error61) {
|
|
111121
111296
|
if (switched.length > 0 || moved) {
|
|
111122
111297
|
let rollbackFailed = false;
|
|
@@ -111253,6 +111428,7 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
|
|
|
111253
111428
|
var PACKAGE_NAME = "@hasna/skills", REGISTRY_ORIGIN = "https://registry.npmjs.org", STABLE_SEMVER_PATTERN, MAX_TARBALL_BYTES, MAX_TARBALL_EXPANDED_BYTES, MAX_TARBALL_ENTRIES = 20000, CONFIG_PREIMAGE_PATHS;
|
|
111254
111429
|
var init_runtime_copyfile = __esm(() => {
|
|
111255
111430
|
init_skill_contract();
|
|
111431
|
+
init_reviewed_runtime_lock();
|
|
111256
111432
|
STABLE_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
111257
111433
|
MAX_TARBALL_BYTES = 100 * 1024 * 1024;
|
|
111258
111434
|
MAX_TARBALL_EXPANDED_BYTES = 256 * 1024 * 1024;
|
|
@@ -111269,7 +111445,7 @@ var exports_runtime = {};
|
|
|
111269
111445
|
__export(exports_runtime, {
|
|
111270
111446
|
registerRuntime: () => registerRuntime
|
|
111271
111447
|
});
|
|
111272
|
-
import { createHash as
|
|
111448
|
+
import { createHash as createHash32, randomUUID as randomUUID10 } from "crypto";
|
|
111273
111449
|
import { lstatSync as lstatSync31, mkdirSync as mkdirSync25, readFileSync as readFileSync39, realpathSync as realpathSync17, writeFileSync as writeFileSync24 } from "fs";
|
|
111274
111450
|
import { basename as basename16, isAbsolute as isAbsolute26, join as join59 } from "path";
|
|
111275
111451
|
import { createInterface } from "readline";
|
|
@@ -111310,7 +111486,7 @@ function registerRuntime(parent) {
|
|
|
111310
111486
|
if (!expected)
|
|
111311
111487
|
throw new Error("Cloud artifact not found");
|
|
111312
111488
|
const bytes = await client2.download(id2, name);
|
|
111313
|
-
if (bytes.length !== expected.byteSize ||
|
|
111489
|
+
if (bytes.length !== expected.byteSize || createHash32("sha256").update(bytes).digest("hex") !== expected.sha256)
|
|
111314
111490
|
throw new Error("Cloud artifact integrity verification failed");
|
|
111315
111491
|
writeFileSync24(options.output, bytes, { flag: "wx", mode: 384 });
|
|
111316
111492
|
console.log(JSON.stringify({ path: options.output, sha256: expected.sha256, byteSize: bytes.length }));
|
|
@@ -111340,7 +111516,12 @@ function registerRuntime(parent) {
|
|
|
111340
111516
|
});
|
|
111341
111517
|
const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "Accepted for compatibility; the API origin is always per-user", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
|
|
111342
111518
|
setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
|
|
111343
|
-
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--min-release-age <days>", "Require a positive integer dependency release age in days for an exact-version update").option("--min-release-age-exclude <pattern>", "Exclude matching package identities from the release-age policy (repeatable)", (pattern, prior) => [...prior, pattern], []).option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
111519
|
+
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--min-release-age <days>", "Require a positive integer dependency release age in days for an exact-version update").option("--min-release-age-exclude <pattern>", "Exclude matching package identities from the release-age policy (repeatable)", (pattern, prior) => [...prior, pattern], []).option("--reviewed-lock <path>", "Install from this private absolute package-lock.json path without resolving dependency ranges").option("--reviewed-lock-sha256 <sha256>", "Exact reviewed SHA-256 of the supplied package-lock.json").option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
111520
|
+
if ((options.reviewedLock !== undefined || options.reviewedLockSha256 !== undefined) && (!options.version || options.rollback || options.adoptAliases || options.rollbackAliases)) {
|
|
111521
|
+
console.error(JSON.stringify({ error: "REVIEWED_LOCK_REQUIRES_EXACT_VERSION" }));
|
|
111522
|
+
process.exitCode = 1;
|
|
111523
|
+
return;
|
|
111524
|
+
}
|
|
111344
111525
|
if ((options.minReleaseAge !== undefined || options.minReleaseAgeExclude.length > 0) && (!options.version || options.rollback || options.adoptAliases || options.rollbackAliases)) {
|
|
111345
111526
|
console.error(JSON.stringify({ error: "RELEASE_AGE_POLICY_REQUIRES_EXACT_VERSION" }));
|
|
111346
111527
|
process.exitCode = 1;
|
|
@@ -111382,7 +111563,7 @@ function registerRuntime(parent) {
|
|
|
111382
111563
|
if (options.version) {
|
|
111383
111564
|
try {
|
|
111384
111565
|
const minReleaseAge = options.minReleaseAge === undefined ? undefined : /^\d+$/.test(options.minReleaseAge) ? Number(options.minReleaseAge) : NaN;
|
|
111385
|
-
const result2 = await updateCopyfileRuntime(options.version, { minReleaseAge, minReleaseAgeExclude: options.minReleaseAgeExclude });
|
|
111566
|
+
const result2 = await updateCopyfileRuntime(options.version, { minReleaseAge, minReleaseAgeExclude: options.minReleaseAgeExclude, reviewedLock: options.reviewedLock, reviewedLockSha256: options.reviewedLockSha256 });
|
|
111386
111567
|
if (options.json)
|
|
111387
111568
|
console.log(JSON.stringify(result2));
|
|
111388
111569
|
else
|
|
@@ -112959,7 +113140,7 @@ var init_portable_snapshot_filter = __esm(() => {
|
|
|
112959
113140
|
});
|
|
112960
113141
|
|
|
112961
113142
|
// src/lib/station-snapshot.ts
|
|
112962
|
-
import { createHash as
|
|
113143
|
+
import { createHash as createHash33 } from "crypto";
|
|
112963
113144
|
import {
|
|
112964
113145
|
copyFileSync as copyFileSync2,
|
|
112965
113146
|
mkdirSync as mkdirSync26,
|
|
@@ -112974,7 +113155,7 @@ function validateStationId(stationId) {
|
|
|
112974
113155
|
}
|
|
112975
113156
|
}
|
|
112976
113157
|
function sha256File(filePath) {
|
|
112977
|
-
return
|
|
113158
|
+
return createHash33("sha256").update(readFileSync40(filePath)).digest("hex");
|
|
112978
113159
|
}
|
|
112979
113160
|
function scanHome(definition, homesRoot) {
|
|
112980
113161
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -113664,7 +113845,7 @@ var init_codex_hook_policy_recovery = __esm(() => {
|
|
|
113664
113845
|
|
|
113665
113846
|
// src/lib/agent-codex-trust.ts
|
|
113666
113847
|
import { lstatSync as lstatSync33, mkdirSync as mkdirSync27, readdirSync as readdirSync23, readFileSync as readFileSync41, realpathSync as realpathSync19, statSync as statSync22 } from "fs";
|
|
113667
|
-
import { createHash as
|
|
113848
|
+
import { createHash as createHash34, randomUUID as randomUUID11 } from "crypto";
|
|
113668
113849
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
113669
113850
|
import { dirname as dirname35, isAbsolute as isAbsolute29, join as join63, resolve as resolve27 } from "path";
|
|
113670
113851
|
import { homedir as homedir12 } from "os";
|
|
@@ -113674,11 +113855,11 @@ function verifyReleaseArtifact(proof, skillsCli) {
|
|
|
113674
113855
|
const tarPath = realpathSync19(proof.packageTarPath);
|
|
113675
113856
|
need5(statSync22(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
113676
113857
|
const tarBytes = readFileSync41(tarPath);
|
|
113677
|
-
need5(tarBytes.byteLength === proof.packageTarBytes &&
|
|
113858
|
+
need5(tarBytes.byteLength === proof.packageTarBytes && createHash34("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
113678
113859
|
const packageRoot = dirname35(dirname35(realpathSync19(skillsCli.receipt.path)));
|
|
113679
113860
|
const manifest = readFileSync41(join63(packageRoot, "package.json"));
|
|
113680
|
-
need5(
|
|
113681
|
-
need5(
|
|
113861
|
+
need5(createHash34("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
|
|
113862
|
+
need5(createHash34("sha256").update(readFileSync41(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
|
|
113682
113863
|
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync41(skillsCli.receipt.path)]];
|
|
113683
113864
|
for (const [member, expected] of members2) {
|
|
113684
113865
|
let actual;
|
|
@@ -113996,7 +114177,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect) {
|
|
|
113996
114177
|
}
|
|
113997
114178
|
}
|
|
113998
114179
|
var json2 = (value) => JSON.stringify(value, null, 2) + `
|
|
113999
|
-
`, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) =>
|
|
114180
|
+
`, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) => createHash34("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
|
|
114000
114181
|
const copy = structuredClone(value);
|
|
114001
114182
|
if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
|
|
114002
114183
|
copy.bridge.commands.codex = "<reviewed-codex-binding>";
|
|
@@ -114138,7 +114319,7 @@ __export(exports_agent_integration, {
|
|
|
114138
114319
|
registerAgentIntegration: () => registerAgentIntegration
|
|
114139
114320
|
});
|
|
114140
114321
|
import { existsSync as existsSync36, lstatSync as lstatSync34, readFileSync as readFileSync42, readSync as readSync17, writeFileSync as writeFileSync26 } from "fs";
|
|
114141
|
-
import { createHash as
|
|
114322
|
+
import { createHash as createHash35 } from "crypto";
|
|
114142
114323
|
import { homedir as homedir13 } from "os";
|
|
114143
114324
|
import { isAbsolute as isAbsolute30 } from "path";
|
|
114144
114325
|
function pinnedHookProfile(input3, configuredProfile) {
|
|
@@ -114263,7 +114444,7 @@ function registerAgentIntegration(parent) {
|
|
|
114263
114444
|
skillCount: catalog.skills.length,
|
|
114264
114445
|
output: options.output,
|
|
114265
114446
|
bytes: Buffer.byteLength(bytes),
|
|
114266
|
-
sha256:
|
|
114447
|
+
sha256: createHash35("sha256").update(bytes).digest("hex")
|
|
114267
114448
|
};
|
|
114268
114449
|
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Captured ${receipt.skillCount} native Codex skill(s) in ${receipt.output}.`);
|
|
114269
114450
|
} catch (error61) {
|
|
@@ -114779,7 +114960,7 @@ var init_grants = __esm(() => {
|
|
|
114779
114960
|
});
|
|
114780
114961
|
|
|
114781
114962
|
// src/lib/station-hydrate.ts
|
|
114782
|
-
import { createHash as
|
|
114963
|
+
import { createHash as createHash36 } from "crypto";
|
|
114783
114964
|
import {
|
|
114784
114965
|
copyFileSync as copyFileSync3,
|
|
114785
114966
|
mkdirSync as mkdirSync28,
|
|
@@ -114982,7 +115163,7 @@ function skillSha256(skill) {
|
|
|
114982
115163
|
return sha256File(skill.files[0].winner.fullPath);
|
|
114983
115164
|
}
|
|
114984
115165
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
114985
|
-
return
|
|
115166
|
+
return createHash36("sha256").update(joined.sort().join(`
|
|
114986
115167
|
`)).digest("hex");
|
|
114987
115168
|
}
|
|
114988
115169
|
function writeStationHydration(options) {
|
|
@@ -115139,7 +115320,7 @@ var init_hydrate = __esm(() => {
|
|
|
115139
115320
|
});
|
|
115140
115321
|
|
|
115141
115322
|
// src/lib/prepare-skill.ts
|
|
115142
|
-
import { createHash as
|
|
115323
|
+
import { createHash as createHash37, randomUUID as randomUUID12 } from "crypto";
|
|
115143
115324
|
import { chmodSync as chmodSync5, existsSync as existsSync37, lstatSync as lstatSync35, mkdirSync as mkdirSync29, mkdtempSync as mkdtempSync6, readFileSync as readFileSync44, readdirSync as readdirSync25, renameSync as renameSync14, rmSync as rmSync9, writeFileSync as writeFileSync30 } from "fs";
|
|
115144
115325
|
import { tmpdir as tmpdir2 } from "os";
|
|
115145
115326
|
import { dirname as dirname37, isAbsolute as isAbsolute31, join as join65, relative as relative12, resolve as resolve29, sep as sep15 } from "path";
|
|
@@ -115338,7 +115519,7 @@ function snapshot2(root2) {
|
|
|
115338
115519
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
115339
115520
|
throw new Error("Skill root must be a real directory, not a symlink.");
|
|
115340
115521
|
const files = [], directories = [];
|
|
115341
|
-
const hash6 =
|
|
115522
|
+
const hash6 = createHash37("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
|
|
115342
115523
|
let count = 0, bytes = 0;
|
|
115343
115524
|
const walk2 = (relative13) => {
|
|
115344
115525
|
for (const name of readdirSync25(join65(root2, relative13)).sort()) {
|
|
@@ -115769,7 +115950,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
115769
115950
|
const includeDocs = options.includeDocs ?? true;
|
|
115770
115951
|
const includeRequirements = options.includeRequirements ?? true;
|
|
115771
115952
|
const includeValidation = options.includeValidation ?? true;
|
|
115772
|
-
const
|
|
115953
|
+
const packageName2 = options.packageName ?? "@hasna/skills";
|
|
115773
115954
|
const packageVersion = options.packageVersion ?? package_default.version;
|
|
115774
115955
|
const sourceRepository = options.sourceRepository ?? "hasna/skills";
|
|
115775
115956
|
const registry2 = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -115787,7 +115968,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
115787
115968
|
tags: [...skill.tags].sort(),
|
|
115788
115969
|
dependencies: [...skill.dependencies ?? []].sort(),
|
|
115789
115970
|
source: {
|
|
115790
|
-
packageName,
|
|
115971
|
+
packageName: packageName2,
|
|
115791
115972
|
packageVersion,
|
|
115792
115973
|
repository: sourceRepository,
|
|
115793
115974
|
directory: directory2
|
|
@@ -115805,7 +115986,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
115805
115986
|
return {
|
|
115806
115987
|
schemaVersion: 1,
|
|
115807
115988
|
source: {
|
|
115808
|
-
packageName,
|
|
115989
|
+
packageName: packageName2,
|
|
115809
115990
|
packageVersion,
|
|
115810
115991
|
repository: sourceRepository,
|
|
115811
115992
|
profile
|
|
@@ -115881,10 +116062,10 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
115881
116062
|
const target = assertPrivateDestination(output2);
|
|
115882
116063
|
if (existsSync38(target))
|
|
115883
116064
|
throw new Error("Review output already exists; choose a new filename.");
|
|
115884
|
-
const
|
|
115885
|
-
if (!
|
|
116065
|
+
const metadata2 = await client2.getSkillVersion(slug2, version2);
|
|
116066
|
+
if (!metadata2)
|
|
115886
116067
|
throw new Error(`Published skill version ${slug2}@${version2} was not found.`);
|
|
115887
|
-
if (!/^[a-f0-9]{64}$/i.test(
|
|
116068
|
+
if (!/^[a-f0-9]{64}$/i.test(metadata2.bundleSha256) || !Number.isSafeInteger(metadata2.bundleByteSize) || metadata2.bundleByteSize < 1 || metadata2.bundleByteSize > MAX_REVIEW_BUNDLE_BYTES) {
|
|
115888
116069
|
throw new Error("Published bundle metadata is invalid or exceeds the review size limit.");
|
|
115889
116070
|
}
|
|
115890
116071
|
const response = await client2.getBundle(slug2, version2);
|
|
@@ -115896,12 +116077,12 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
115896
116077
|
}
|
|
115897
116078
|
const headerSha = response.headers.get("X-Skill-Bundle-Sha256");
|
|
115898
116079
|
const headerVersion = response.headers.get("X-Skill-Version");
|
|
115899
|
-
if (!headerSha || headerSha.toLowerCase() !==
|
|
116080
|
+
if (!headerSha || headerSha.toLowerCase() !== metadata2.bundleSha256.toLowerCase() || headerVersion !== version2) {
|
|
115900
116081
|
response.body?.cancel().catch(() => {});
|
|
115901
116082
|
throw new Error("Published bundle headers do not match the exact version metadata.");
|
|
115902
116083
|
}
|
|
115903
|
-
const bytes = await readBoundedResponse(response, Math.min(
|
|
115904
|
-
if (bytes.byteLength !==
|
|
116084
|
+
const bytes = await readBoundedResponse(response, Math.min(metadata2.bundleByteSize, MAX_REVIEW_BUNDLE_BYTES));
|
|
116085
|
+
if (bytes.byteLength !== metadata2.bundleByteSize || sha256Hex(bytes) !== metadata2.bundleSha256.toLowerCase()) {
|
|
115905
116086
|
throw new Error("Published bundle bytes do not match the exact version metadata.");
|
|
115906
116087
|
}
|
|
115907
116088
|
const fd = openSync25(target, constants23.O_WRONLY | constants23.O_CREAT | constants23.O_EXCL | constants23.O_NOFOLLOW, 384);
|
|
@@ -115915,7 +116096,7 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
115915
116096
|
} finally {
|
|
115916
116097
|
closeSync25(fd);
|
|
115917
116098
|
}
|
|
115918
|
-
return { slug: slug2, version: version2, bundleSha256:
|
|
116099
|
+
return { slug: slug2, version: version2, bundleSha256: metadata2.bundleSha256.toLowerCase(), bundleByteSize: bytes.byteLength, output: target };
|
|
115919
116100
|
}
|
|
115920
116101
|
var MAX_REVIEW_BUNDLE_BYTES, SLUG2, NATIVE_CONTAINERS;
|
|
115921
116102
|
var init_review_export = __esm(() => {
|
|
@@ -118099,10 +118280,10 @@ async function reconcileRegistry(options = {}) {
|
|
|
118099
118280
|
throw new ReconcileRegistryError(`Registry listing failed: expected an array of skills, got ${shape}.`, ["Check HASNA_SKILLS_API_URL and the resolved credential; a failed listing must not be read as an empty registry."]);
|
|
118100
118281
|
}
|
|
118101
118282
|
const remotes = new Map;
|
|
118102
|
-
for (const
|
|
118103
|
-
if (!
|
|
118283
|
+
for (const row2 of remoteRowsPayload) {
|
|
118284
|
+
if (!row2 || typeof row2 !== "object")
|
|
118104
118285
|
continue;
|
|
118105
|
-
const skill = remoteRowToSkill(
|
|
118286
|
+
const skill = remoteRowToSkill(row2);
|
|
118106
118287
|
if (skill)
|
|
118107
118288
|
remotes.set(skill.slug, skill);
|
|
118108
118289
|
}
|
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.10.
|
|
20022
|
+
version: "0.10.41",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.10.
|
|
24125
|
+
version: "0.10.41",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.10.
|
|
24128
|
+
version: "0.10.41",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type Row = Record<string, unknown>;
|
|
2
|
+
export interface ReviewedRuntimeLockContext {
|
|
3
|
+
version: string;
|
|
4
|
+
archiveIntegrity: string;
|
|
5
|
+
packageManifest: Row;
|
|
6
|
+
registryOrigin: string;
|
|
7
|
+
minReleaseAge: number;
|
|
8
|
+
minReleaseAgeExclude: string[];
|
|
9
|
+
fetcher?: typeof fetch;
|
|
10
|
+
now?: number;
|
|
11
|
+
}
|
|
12
|
+
/** Validate the immutable install input without resolving any version ranges.
|
|
13
|
+
* npm ci subsequently verifies archive bytes and package/root range agreement;
|
|
14
|
+
* npm sbom forceActual validates the installed required closure, including optional peers.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateReviewedRuntimeLock(bytes: Uint8Array, sha256: string, context: ReviewedRuntimeLockContext): Promise<void>;
|
|
17
|
+
export {};
|
|
@@ -28,6 +28,8 @@ export declare function updateCopyfileRuntime(version: string, options?: {
|
|
|
28
28
|
fetcher?: typeof fetch;
|
|
29
29
|
minReleaseAge?: number;
|
|
30
30
|
minReleaseAgeExclude?: string[];
|
|
31
|
+
reviewedLock?: string;
|
|
32
|
+
reviewedLockSha256?: string;
|
|
31
33
|
onLauncherSwitched?: (path: string) => void;
|
|
32
34
|
}): Promise<JsonObject>;
|
|
33
35
|
export declare function rollbackCopyfileRuntime(receiptId: string, options?: {
|
package/dist/index.js
CHANGED
|
@@ -11040,7 +11040,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
11040
11040
|
// package.json
|
|
11041
11041
|
var package_default = {
|
|
11042
11042
|
name: "@hasna/skills",
|
|
11043
|
-
version: "0.10.
|
|
11043
|
+
version: "0.10.41",
|
|
11044
11044
|
description: "Skills library for AI coding agents",
|
|
11045
11045
|
type: "module",
|
|
11046
11046
|
bin: {
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.41",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|