@hasna/skills 0.10.16 → 0.10.18
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 +352 -6
- 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/runtime-copyfile.d.ts +9 -0
- package/dist/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/package.json +1 -1
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.18",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -108698,13 +108698,13 @@ function comparePackageTrees(expectedRoot, installedRoot) {
|
|
|
108698
108698
|
throw new Error("INSTALLED_PACKAGE_BYTES_MISMATCH");
|
|
108699
108699
|
return expected.digest;
|
|
108700
108700
|
}
|
|
108701
|
-
function replaceLink(path, target, backupPath, oldLinkTarget) {
|
|
108701
|
+
function replaceLink(path, target, backupPath, oldLinkTarget, oldTarget = resolve23(dirname30(path), oldLinkTarget)) {
|
|
108702
108702
|
const stat3 = lstatSync28(path);
|
|
108703
108703
|
if (!stat3.isSymbolicLink() || entryExists(backupPath))
|
|
108704
108704
|
throw new Error("LAUNCHER_PREIMAGE_DRIFT");
|
|
108705
108705
|
const temp = `${path}.skills-next-${randomUUID9()}`;
|
|
108706
108706
|
symlinkSync(oldLinkTarget, backupPath);
|
|
108707
|
-
if (readlinkSync5(backupPath) !== oldLinkTarget || realpathSync14(backupPath) !==
|
|
108707
|
+
if (readlinkSync5(backupPath) !== oldLinkTarget || realpathSync14(backupPath) !== oldTarget)
|
|
108708
108708
|
throw new Error("LAUNCHER_BACKUP_READBACK_MISMATCH");
|
|
108709
108709
|
symlinkSync(target, temp);
|
|
108710
108710
|
if (realpathSync14(temp) !== target)
|
|
@@ -108745,6 +108745,324 @@ function persistReceipt(path, value) {
|
|
|
108745
108745
|
}
|
|
108746
108746
|
atomicJson(path, value);
|
|
108747
108747
|
}
|
|
108748
|
+
function persistAliasReceipt(path, value) {
|
|
108749
|
+
if (entryExists(path)) {
|
|
108750
|
+
const priorBytes = readFileSync37(path);
|
|
108751
|
+
const prior = object9(JSON.parse(priorBytes.toString("utf8")), "ALIAS_RECEIPT_INVALID");
|
|
108752
|
+
if (prior.schema !== value.schema || prior.id !== value.id || typeof prior.state !== "string")
|
|
108753
|
+
throw new Error("ALIAS_RECEIPT_DRIFT");
|
|
108754
|
+
const nextBytes = Buffer.from(`${JSON.stringify(value, null, 2)}
|
|
108755
|
+
`);
|
|
108756
|
+
if (priorBytes.equals(nextBytes))
|
|
108757
|
+
return;
|
|
108758
|
+
const history = join55(dirname30(path), "history");
|
|
108759
|
+
if (!entryExists(history))
|
|
108760
|
+
mkdirSync24(history, { mode: 448 });
|
|
108761
|
+
const historyStat = lstatSync28(history);
|
|
108762
|
+
if (!historyStat.isDirectory() || historyStat.isSymbolicLink() || (historyStat.mode & 63) !== 0)
|
|
108763
|
+
throw new Error("ALIAS_RECEIPT_HISTORY_UNSAFE");
|
|
108764
|
+
const saved = join55(history, `${value.id}-${String(prior.state)}-${randomUUID9()}.json`);
|
|
108765
|
+
writeFileSync23(saved, priorBytes, { flag: "wx", mode: 256 });
|
|
108766
|
+
chmodSync4(saved, 256);
|
|
108767
|
+
if (!readFileSync37(saved).equals(priorBytes))
|
|
108768
|
+
throw new Error("ALIAS_RECEIPT_HISTORY_READBACK_MISMATCH");
|
|
108769
|
+
}
|
|
108770
|
+
atomicJson(path, value);
|
|
108771
|
+
}
|
|
108772
|
+
function assertOwnedSafePath(path, home, requireWritable = false) {
|
|
108773
|
+
assertNoSymlinkAncestors(path);
|
|
108774
|
+
for (let current = path;; current = dirname30(current)) {
|
|
108775
|
+
const stat3 = lstatSync28(current);
|
|
108776
|
+
if (!stat3.isDirectory() || ![0, process.getuid?.() ?? -1].includes(stat3.uid) || stat3.mode & 18)
|
|
108777
|
+
throw new Error("ALIAS_PATH_UNSAFE");
|
|
108778
|
+
if (current === path && requireWritable && (stat3.uid !== (process.getuid?.() ?? -1) || (stat3.mode & 128) === 0))
|
|
108779
|
+
throw new Error("ALIAS_DIRECTORY_NOT_OWNED");
|
|
108780
|
+
if (current === dirname30(current) || current === home)
|
|
108781
|
+
break;
|
|
108782
|
+
}
|
|
108783
|
+
}
|
|
108784
|
+
function activeAliasRuntime(home, pathValue) {
|
|
108785
|
+
const layout = resolveRuntimeLayout(home, pathValue);
|
|
108786
|
+
const runtimePath = dirname30(dirname30(dirname30(layout.currentPackageRoot)));
|
|
108787
|
+
const receiptPath = join55(runtimePath, "rollout-receipt.json");
|
|
108788
|
+
const rollout = object9(JSON.parse(readFileSync37(receiptPath, "utf8")), "ROLLOUT_RECEIPT_INVALID");
|
|
108789
|
+
if (rollout.schema !== "skills.copyfile-runtime-receipt.v1" || rollout.state !== "switched" || rollout.targetPackageRoot !== layout.currentPackageRoot || rollout.targetVersion !== layout.currentVersion || rollout.runtimeRoot !== layout.runtimeRoot || rollout.package !== PACKAGE_NAME || hash5(readFileSync37(join55(runtimePath, "verified-package.tgz"))) !== rollout.tarballSha256 || readTree(join55(runtimePath, "node_modules"), true).digest !== rollout.runtimeTreeSha256) {
|
|
108790
|
+
throw new Error("ACTIVE_RUNTIME_RECEIPT_DRIFT");
|
|
108791
|
+
}
|
|
108792
|
+
verifyPreimageManifest(runtimePath, rollout, home);
|
|
108793
|
+
return { layout, rollout };
|
|
108794
|
+
}
|
|
108795
|
+
function aliasDirs(home) {
|
|
108796
|
+
return [join55(home, ".local", "bin"), join55(home, ".bun", "bin"), "/opt/homebrew/bin", "/usr/local/bin"];
|
|
108797
|
+
}
|
|
108798
|
+
function aliasPackageLink(home, aliasPath) {
|
|
108799
|
+
const dir = dirname30(aliasPath);
|
|
108800
|
+
if (dir === join55(home, ".local", "bin"))
|
|
108801
|
+
return join55(home, ".local", "lib", "node_modules", "@hasna", "skills");
|
|
108802
|
+
if (dir === "/opt/homebrew/bin")
|
|
108803
|
+
return "/opt/homebrew/lib/node_modules/@hasna/skills";
|
|
108804
|
+
if (dir === "/usr/local/bin")
|
|
108805
|
+
return "/usr/local/lib/node_modules/@hasna/skills";
|
|
108806
|
+
return null;
|
|
108807
|
+
}
|
|
108808
|
+
function inspectAliasPackageChain(home, aliasPath, oldLinkTarget, oldTarget, bin, expected, verify = false) {
|
|
108809
|
+
const immediate = resolve23(dirname30(aliasPath), oldLinkTarget);
|
|
108810
|
+
if (immediate === oldTarget) {
|
|
108811
|
+
if (expected !== undefined)
|
|
108812
|
+
throw new Error("ALIAS_LINK_CHAIN_DRIFT");
|
|
108813
|
+
return;
|
|
108814
|
+
}
|
|
108815
|
+
const packageLinkPath = aliasPackageLink(home, aliasPath);
|
|
108816
|
+
const physicalPackage = dirname30(dirname30(oldTarget));
|
|
108817
|
+
if (!packageLinkPath || immediate !== join55(packageLinkPath, bin) || !isWithin(join55(home, ".hasna", "skills", "runtime"), physicalPackage)) {
|
|
108818
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108819
|
+
}
|
|
108820
|
+
assertOwnedSafePath(dirname30(packageLinkPath), home);
|
|
108821
|
+
let stat3, packageLinkTarget, resolved;
|
|
108822
|
+
try {
|
|
108823
|
+
stat3 = lstatSync28(packageLinkPath);
|
|
108824
|
+
packageLinkTarget = readlinkSync5(packageLinkPath);
|
|
108825
|
+
resolved = realpathSync14(packageLinkPath);
|
|
108826
|
+
} catch {
|
|
108827
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108828
|
+
}
|
|
108829
|
+
if (expected !== undefined && (expected.packageLinkPath !== packageLinkPath || expected.packageLinkTarget !== packageLinkTarget))
|
|
108830
|
+
throw new Error("ALIAS_LINK_CHAIN_DRIFT");
|
|
108831
|
+
if (!stat3.isSymbolicLink() || stat3.uid !== (process.getuid?.() ?? -1) || resolve23(dirname30(packageLinkPath), packageLinkTarget) !== physicalPackage || resolved !== physicalPackage)
|
|
108832
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108833
|
+
const chain = { packageLinkPath, packageLinkTarget };
|
|
108834
|
+
if (verify && expected === undefined)
|
|
108835
|
+
throw new Error("ALIAS_LINK_CHAIN_DRIFT");
|
|
108836
|
+
return chain;
|
|
108837
|
+
}
|
|
108838
|
+
function validateAliasSource(home, name, actual) {
|
|
108839
|
+
const packageRoot = dirname30(dirname30(actual));
|
|
108840
|
+
const roots = [join55(home, ".hasna", "skills", "runtime"), join55(home, ".bun", "install"), join55(home, ".local", "lib", "node_modules")];
|
|
108841
|
+
if (basename13(packageRoot) !== "skills" || basename13(dirname30(packageRoot)) !== "@hasna" || !roots.some((root2) => isWithin(root2, packageRoot))) {
|
|
108842
|
+
throw new Error("ALIAS_SOURCE_ROOT_UNSUPPORTED");
|
|
108843
|
+
}
|
|
108844
|
+
assertOwnedSafePath(packageRoot, home);
|
|
108845
|
+
const manifestPath = join55(packageRoot, "package.json");
|
|
108846
|
+
const manifestStat = lstatSync28(manifestPath), binaryStat = lstatSync28(actual);
|
|
108847
|
+
if (!manifestStat.isFile() || manifestStat.isSymbolicLink() || !binaryStat.isFile() || binaryStat.isSymbolicLink() || ![0, process.getuid?.() ?? -1].includes(manifestStat.uid) || ![0, process.getuid?.() ?? -1].includes(binaryStat.uid) || manifestStat.mode & 18)
|
|
108848
|
+
throw new Error("ALIAS_SOURCE_UNSAFE");
|
|
108849
|
+
const pkg = readPackage(manifestPath);
|
|
108850
|
+
if (!pkg.bins[name] || actual !== join55(packageRoot, pkg.bins[name]))
|
|
108851
|
+
throw new Error("ALIAS_SOURCE_BIN_MISMATCH");
|
|
108852
|
+
return { version: pkg.version, packageRoot, binarySha256: hash5(readFileSync37(actual)) };
|
|
108853
|
+
}
|
|
108854
|
+
function adoptCopyfileAliases(options = {}) {
|
|
108855
|
+
const home = realpathSync14(resolve23(options.homeDir ?? process.env.HOME ?? ""));
|
|
108856
|
+
const pathValue = options.pathValue ?? process.env.PATH ?? "";
|
|
108857
|
+
const { layout, rollout } = activeAliasRuntime(home, pathValue);
|
|
108858
|
+
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
108859
|
+
const id2 = randomUUID9();
|
|
108860
|
+
const aliases = [];
|
|
108861
|
+
const currentRuntime = dirname30(dirname30(dirname30(layout.currentPackageRoot)));
|
|
108862
|
+
const receiptDir = join55(currentRuntime, "alias-adoptions");
|
|
108863
|
+
const receiptPath = join55(receiptDir, `${id2}.json`);
|
|
108864
|
+
let receipt;
|
|
108865
|
+
try {
|
|
108866
|
+
const locked2 = activeAliasRuntime(home, pathValue);
|
|
108867
|
+
if (locked2.layout.currentPackageRoot !== layout.currentPackageRoot || locked2.rollout.id !== rollout.id)
|
|
108868
|
+
throw new Error("ACTIVE_RUNTIME_CHANGED_BEFORE_ALIAS_ADOPTION");
|
|
108869
|
+
for (const dir of aliasDirs(home)) {
|
|
108870
|
+
if (!entryExists(dir))
|
|
108871
|
+
continue;
|
|
108872
|
+
for (const [name, target] of Object.entries(layout.bin)) {
|
|
108873
|
+
const path = join55(dir, name);
|
|
108874
|
+
if (!entryExists(path))
|
|
108875
|
+
continue;
|
|
108876
|
+
assertOwnedSafePath(dir, home, true);
|
|
108877
|
+
const stat3 = lstatSync28(path);
|
|
108878
|
+
if (!stat3.isSymbolicLink() || stat3.uid !== (process.getuid?.() ?? -1))
|
|
108879
|
+
throw new Error("ALIAS_LINK_NOT_OWNED");
|
|
108880
|
+
const oldLinkTarget = readlinkSync5(path);
|
|
108881
|
+
let actual;
|
|
108882
|
+
try {
|
|
108883
|
+
actual = realpathSync14(path);
|
|
108884
|
+
} catch {
|
|
108885
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108886
|
+
}
|
|
108887
|
+
const newTarget = join55(layout.currentPackageRoot, target);
|
|
108888
|
+
if (actual === newTarget)
|
|
108889
|
+
continue;
|
|
108890
|
+
const chain = inspectAliasPackageChain(home, path, oldLinkTarget, actual, target);
|
|
108891
|
+
const source = validateAliasSource(home, name, actual);
|
|
108892
|
+
const backupPath = `${path}.skills-alias-prev-${id2}`;
|
|
108893
|
+
if (entryExists(backupPath))
|
|
108894
|
+
throw new Error("ALIAS_BACKUP_COLLISION");
|
|
108895
|
+
aliases.push({ path, oldTarget: actual, oldLinkTarget, oldVersion: source.version, oldBinarySha256: source.binarySha256, newTarget, backupPath, ...chain ? { chain } : {} });
|
|
108896
|
+
}
|
|
108897
|
+
}
|
|
108898
|
+
if (!aliases.length)
|
|
108899
|
+
return { adopted: true, version: layout.currentVersion, aliasCount: 0 };
|
|
108900
|
+
mkdirSync24(receiptDir, { recursive: true, mode: 448 });
|
|
108901
|
+
const receiptDirStat = lstatSync28(receiptDir);
|
|
108902
|
+
if (!receiptDirStat.isDirectory() || receiptDirStat.isSymbolicLink() || receiptDirStat.mode & 63)
|
|
108903
|
+
throw new Error("ALIAS_RECEIPT_DIRECTORY_UNSAFE");
|
|
108904
|
+
receipt = {
|
|
108905
|
+
schema: "skills.copyfile-alias-adoption.v1",
|
|
108906
|
+
id: id2,
|
|
108907
|
+
state: "prepared",
|
|
108908
|
+
targetVersion: layout.currentVersion,
|
|
108909
|
+
targetPackageRoot: layout.currentPackageRoot,
|
|
108910
|
+
rolloutReceiptId: rollout.id,
|
|
108911
|
+
aliases,
|
|
108912
|
+
switchedAliases: [],
|
|
108913
|
+
rollbackCompletedAliases: []
|
|
108914
|
+
};
|
|
108915
|
+
writeJsonPrivate(receiptPath, receipt);
|
|
108916
|
+
for (const item of aliases) {
|
|
108917
|
+
if (readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget)
|
|
108918
|
+
throw new Error("ALIAS_CHANGED_BEFORE_SWITCH");
|
|
108919
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108920
|
+
const source = validateAliasSource(home, basename13(item.path), item.oldTarget);
|
|
108921
|
+
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
108922
|
+
throw new Error("ALIAS_SOURCE_DRIFT");
|
|
108923
|
+
receipt.state = "switching";
|
|
108924
|
+
receipt.switchedAliases = [...receipt.switchedAliases, item.path];
|
|
108925
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108926
|
+
replaceLink(item.path, item.newTarget, item.backupPath, item.oldLinkTarget, item.oldTarget);
|
|
108927
|
+
if (realpathSync14(item.path) !== item.newTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108928
|
+
throw new Error("ALIAS_SWITCH_READBACK_MISMATCH");
|
|
108929
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108930
|
+
options.onAliasSwitched?.(item.path);
|
|
108931
|
+
}
|
|
108932
|
+
for (const item of aliases) {
|
|
108933
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108934
|
+
if (readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108935
|
+
throw new Error("ALIAS_BACKUP_DRIFT");
|
|
108936
|
+
}
|
|
108937
|
+
receipt.state = "switched";
|
|
108938
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108939
|
+
return { adopted: true, version: layout.currentVersion, receiptId: id2, aliasCount: aliases.length };
|
|
108940
|
+
} catch (error61) {
|
|
108941
|
+
if (receipt) {
|
|
108942
|
+
let failed = false;
|
|
108943
|
+
for (const item of [...aliases].reverse()) {
|
|
108944
|
+
try {
|
|
108945
|
+
if (!entryExists(item.backupPath)) {
|
|
108946
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108947
|
+
const source2 = validateAliasSource(home, basename13(item.path), item.oldTarget);
|
|
108948
|
+
if (!entryExists(item.path) || readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget || source2.version !== item.oldVersion || source2.binarySha256 !== item.oldBinarySha256)
|
|
108949
|
+
failed = true;
|
|
108950
|
+
continue;
|
|
108951
|
+
}
|
|
108952
|
+
if (readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108953
|
+
throw new Error("ALIAS_BACKUP_DRIFT");
|
|
108954
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108955
|
+
const source = validateAliasSource(home, basename13(item.path), item.oldTarget);
|
|
108956
|
+
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
108957
|
+
throw new Error("ALIAS_SOURCE_DRIFT");
|
|
108958
|
+
if (realpathSync14(item.path) === item.oldTarget && readlinkSync5(item.path) === item.oldLinkTarget)
|
|
108959
|
+
continue;
|
|
108960
|
+
if (realpathSync14(item.path) !== item.newTarget)
|
|
108961
|
+
throw new Error("ALIAS_CHANGED_DURING_ROLLBACK");
|
|
108962
|
+
const after2 = `${item.path}.skills-alias-after-${id2}`;
|
|
108963
|
+
if (entryExists(after2))
|
|
108964
|
+
throw new Error("ALIAS_AFTER_COLLISION");
|
|
108965
|
+
symlinkSync(item.newTarget, after2);
|
|
108966
|
+
const temp = `${item.path}.skills-alias-rollback-${randomUUID9()}`;
|
|
108967
|
+
symlinkSync(item.oldLinkTarget, temp);
|
|
108968
|
+
renameSync13(temp, item.path);
|
|
108969
|
+
if (readlinkSync5(item.path) !== item.oldLinkTarget)
|
|
108970
|
+
throw new Error("ALIAS_ROLLBACK_READBACK_MISMATCH");
|
|
108971
|
+
receipt.rollbackCompletedAliases = [...receipt.rollbackCompletedAliases, item.path];
|
|
108972
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108973
|
+
} catch {
|
|
108974
|
+
failed = true;
|
|
108975
|
+
}
|
|
108976
|
+
}
|
|
108977
|
+
receipt.state = failed ? "rollback-required" : "rolled-back";
|
|
108978
|
+
try {
|
|
108979
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108980
|
+
} catch {
|
|
108981
|
+
failed = true;
|
|
108982
|
+
}
|
|
108983
|
+
throw new Error(failed ? "ALIAS_ADOPTION_ROLLBACK_REQUIRED" : "ALIAS_ADOPTION_ROLLED_BACK");
|
|
108984
|
+
}
|
|
108985
|
+
throw error61 instanceof Error ? error61 : new Error("ALIAS_ADOPTION_FAILED");
|
|
108986
|
+
} finally {
|
|
108987
|
+
releaseLock();
|
|
108988
|
+
}
|
|
108989
|
+
}
|
|
108990
|
+
function rollbackCopyfileAliases(receiptId, options = {}) {
|
|
108991
|
+
if (!/^[0-9a-f-]{36}$/.test(receiptId))
|
|
108992
|
+
throw new Error("ALIAS_RECEIPT_ID_INVALID");
|
|
108993
|
+
const home = realpathSync14(resolve23(options.homeDir ?? process.env.HOME ?? ""));
|
|
108994
|
+
const pathValue = options.pathValue ?? process.env.PATH ?? "";
|
|
108995
|
+
const { layout, rollout } = activeAliasRuntime(home, pathValue);
|
|
108996
|
+
const runtimePath = dirname30(dirname30(dirname30(layout.currentPackageRoot)));
|
|
108997
|
+
const receiptPath = join55(runtimePath, "alias-adoptions", `${receiptId}.json`);
|
|
108998
|
+
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
108999
|
+
try {
|
|
109000
|
+
const locked2 = activeAliasRuntime(home, pathValue);
|
|
109001
|
+
if (locked2.layout.currentPackageRoot !== layout.currentPackageRoot || locked2.rollout.id !== rollout.id)
|
|
109002
|
+
throw new Error("ACTIVE_RUNTIME_CHANGED_BEFORE_ALIAS_ROLLBACK");
|
|
109003
|
+
const receipt = object9(JSON.parse(readFileSync37(receiptPath, "utf8")), "ALIAS_RECEIPT_INVALID");
|
|
109004
|
+
if (receipt.schema !== "skills.copyfile-alias-adoption.v1" || receipt.id !== receiptId || !["switched", "switching", "rollback-required"].includes(receipt.state) || receipt.targetPackageRoot !== layout.currentPackageRoot || receipt.targetVersion !== layout.currentVersion || receipt.rolloutReceiptId !== rollout.id || !Array.isArray(receipt.aliases))
|
|
109005
|
+
throw new Error("ALIAS_RECEIPT_BINDING_INVALID");
|
|
109006
|
+
const seen = new Set;
|
|
109007
|
+
for (const item of receipt.aliases) {
|
|
109008
|
+
const name = basename13(item.path), target = layout.bin[name];
|
|
109009
|
+
if (!target || seen.has(item.path) || !aliasDirs(home).includes(dirname30(item.path)) || item.newTarget !== join55(layout.currentPackageRoot, target) || item.backupPath !== `${item.path}.skills-alias-prev-${receiptId}` || item.chain !== undefined && (!item.chain || typeof item.chain !== "object" || typeof item.chain.packageLinkPath !== "string" || typeof item.chain.packageLinkTarget !== "string")) {
|
|
109010
|
+
throw new Error("ALIAS_RECEIPT_LAUNCHER_INVALID");
|
|
109011
|
+
}
|
|
109012
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, target, item.chain, true);
|
|
109013
|
+
seen.add(item.path);
|
|
109014
|
+
assertOwnedSafePath(dirname30(item.path), home, true);
|
|
109015
|
+
const source = validateAliasSource(home, name, item.oldTarget);
|
|
109016
|
+
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
109017
|
+
throw new Error("ALIAS_PREIMAGE_DRIFT");
|
|
109018
|
+
const stat3 = lstatSync28(item.path);
|
|
109019
|
+
if (!stat3.isSymbolicLink() || stat3.uid !== (process.getuid?.() ?? -1))
|
|
109020
|
+
throw new Error("ALIAS_LINK_DRIFT");
|
|
109021
|
+
const current = realpathSync14(item.path);
|
|
109022
|
+
if (current !== item.newTarget && (current !== item.oldTarget || readlinkSync5(item.path) !== item.oldLinkTarget))
|
|
109023
|
+
throw new Error("ALIAS_TARGET_DRIFT");
|
|
109024
|
+
if (entryExists(item.backupPath)) {
|
|
109025
|
+
const backup = lstatSync28(item.backupPath);
|
|
109026
|
+
if (!backup.isSymbolicLink() || readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
109027
|
+
throw new Error("ALIAS_PREIMAGE_DRIFT");
|
|
109028
|
+
} else if (current !== item.oldTarget) {
|
|
109029
|
+
throw new Error("ALIAS_BACKUP_MISSING_FOR_SWITCH");
|
|
109030
|
+
}
|
|
109031
|
+
}
|
|
109032
|
+
let restored = 0;
|
|
109033
|
+
for (const item of [...receipt.aliases].reverse()) {
|
|
109034
|
+
if (realpathSync14(item.path) === item.oldTarget && readlinkSync5(item.path) === item.oldLinkTarget)
|
|
109035
|
+
continue;
|
|
109036
|
+
receipt.state = "rollback-required";
|
|
109037
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
109038
|
+
const after2 = `${item.path}.skills-alias-after-${receiptId}`;
|
|
109039
|
+
if (!entryExists(after2)) {
|
|
109040
|
+
symlinkSync(item.newTarget, after2);
|
|
109041
|
+
if (realpathSync14(after2) !== item.newTarget)
|
|
109042
|
+
throw new Error("ALIAS_AFTER_READBACK_MISMATCH");
|
|
109043
|
+
} else if (realpathSync14(after2) !== item.newTarget)
|
|
109044
|
+
throw new Error("ALIAS_AFTER_DRIFT");
|
|
109045
|
+
if (realpathSync14(item.path) !== item.newTarget)
|
|
109046
|
+
throw new Error("ALIAS_TARGET_DRIFT");
|
|
109047
|
+
const temp = `${item.path}.skills-alias-rollback-${randomUUID9()}`;
|
|
109048
|
+
symlinkSync(item.oldLinkTarget, temp);
|
|
109049
|
+
renameSync13(temp, item.path);
|
|
109050
|
+
if (readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget)
|
|
109051
|
+
throw new Error("ALIAS_ROLLBACK_READBACK_MISMATCH");
|
|
109052
|
+
receipt.rollbackCompletedAliases = [...new Set([...receipt.rollbackCompletedAliases, item.path])];
|
|
109053
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
109054
|
+
restored++;
|
|
109055
|
+
}
|
|
109056
|
+
for (const item of receipt.aliases) {
|
|
109057
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
109058
|
+
}
|
|
109059
|
+
receipt.state = "rolled-back";
|
|
109060
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
109061
|
+
return { rolledBack: true, receiptId, restoredAliasCount: restored };
|
|
109062
|
+
} finally {
|
|
109063
|
+
releaseLock();
|
|
109064
|
+
}
|
|
109065
|
+
}
|
|
108748
109066
|
async function updateCopyfileRuntime(version2, options = {}) {
|
|
108749
109067
|
if (!new RegExp(SEMVER_PATTERN).test(version2))
|
|
108750
109068
|
throw new Error("EXACT_VERSION_REQUIRED");
|
|
@@ -109091,12 +109409,40 @@ function registerRuntime(parent) {
|
|
|
109091
109409
|
});
|
|
109092
109410
|
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));
|
|
109093
109411
|
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 }));
|
|
109094
|
-
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("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").action(async (options) => {
|
|
109095
|
-
if (options.version
|
|
109096
|
-
console.error(JSON.stringify({ error: "
|
|
109412
|
+
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("--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) => {
|
|
109413
|
+
if ([options.version, options.rollback, options.adoptAliases, options.rollbackAliases].filter(Boolean).length > 1) {
|
|
109414
|
+
console.error(JSON.stringify({ error: "SELF_UPDATE_OPERATIONS_ARE_MUTUALLY_EXCLUSIVE" }));
|
|
109097
109415
|
process.exitCode = 1;
|
|
109098
109416
|
return;
|
|
109099
109417
|
}
|
|
109418
|
+
if (options.adoptAliases) {
|
|
109419
|
+
try {
|
|
109420
|
+
const result2 = adoptCopyfileAliases();
|
|
109421
|
+
if (options.json)
|
|
109422
|
+
console.log(JSON.stringify(result2));
|
|
109423
|
+
else
|
|
109424
|
+
console.log(source_default.green(`Adopted ${String(result2.aliasCount)} verified Skills aliases into the active copyfile runtime.`));
|
|
109425
|
+
} catch (error61) {
|
|
109426
|
+
const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "ALIAS_ADOPTION_FAILED";
|
|
109427
|
+
console.error(JSON.stringify({ adopted: false, error: message }));
|
|
109428
|
+
process.exitCode = 1;
|
|
109429
|
+
}
|
|
109430
|
+
return;
|
|
109431
|
+
}
|
|
109432
|
+
if (options.rollbackAliases) {
|
|
109433
|
+
try {
|
|
109434
|
+
const result2 = rollbackCopyfileAliases(options.rollbackAliases);
|
|
109435
|
+
if (options.json)
|
|
109436
|
+
console.log(JSON.stringify(result2));
|
|
109437
|
+
else
|
|
109438
|
+
console.log(source_default.green(`Restored ${String(result2.restoredAliasCount)} Skills aliases from the adoption receipt.`));
|
|
109439
|
+
} catch (error61) {
|
|
109440
|
+
const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "ALIAS_ROLLBACK_FAILED";
|
|
109441
|
+
console.error(JSON.stringify({ rolledBack: false, error: message }));
|
|
109442
|
+
process.exitCode = 1;
|
|
109443
|
+
}
|
|
109444
|
+
return;
|
|
109445
|
+
}
|
|
109100
109446
|
if (options.version) {
|
|
109101
109447
|
try {
|
|
109102
109448
|
const result2 = await updateCopyfileRuntime(options.version);
|
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.18",
|
|
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.18",
|
|
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.18",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
|
@@ -12,6 +12,15 @@ export declare function preflightTarball(bytes: Uint8Array, limits?: {
|
|
|
12
12
|
expandedBytes: number;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function readBodyCapped(response: Response, limit: number, errorCode: string): Promise<Uint8Array>;
|
|
15
|
+
export declare function adoptCopyfileAliases(options?: {
|
|
16
|
+
homeDir?: string;
|
|
17
|
+
pathValue?: string;
|
|
18
|
+
onAliasSwitched?: (path: string) => void;
|
|
19
|
+
}): JsonObject;
|
|
20
|
+
export declare function rollbackCopyfileAliases(receiptId: string, options?: {
|
|
21
|
+
homeDir?: string;
|
|
22
|
+
pathValue?: string;
|
|
23
|
+
}): JsonObject;
|
|
15
24
|
export declare function updateCopyfileRuntime(version: string, options?: {
|
|
16
25
|
homeDir?: string;
|
|
17
26
|
pathValue?: string;
|
package/dist/index.js
CHANGED
|
@@ -10877,7 +10877,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10877
10877
|
// package.json
|
|
10878
10878
|
var package_default = {
|
|
10879
10879
|
name: "@hasna/skills",
|
|
10880
|
-
version: "0.10.
|
|
10880
|
+
version: "0.10.18",
|
|
10881
10881
|
description: "Skills library for AI coding agents",
|
|
10882
10882
|
type: "module",
|
|
10883
10883
|
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.18",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|