@hasna/skills 0.10.89 → 0.10.91
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 +12 -3
- package/THIRD_PARTY_NOTICES +5926 -0
- package/bin/index.js +573 -1787
- package/bin/mcp.js +66 -1353
- package/bin/migrate.js +8 -5
- package/bin/server.js +73819 -12659
- package/bin/worker.js +60586 -7855
- package/dist/index.js +11 -5
- package/dist/lib/execution-secrets.d.ts +17 -1
- package/dist/runtime-prerequisites.js +8 -5
- package/dist/sdk/index.js +78725 -18745
- package/package.json +8 -5
package/bin/index.js
CHANGED
|
@@ -36888,7 +36888,7 @@ var package_default;
|
|
|
36888
36888
|
var init_package = __esm(() => {
|
|
36889
36889
|
package_default = {
|
|
36890
36890
|
name: "@hasna/skills",
|
|
36891
|
-
version: "0.10.
|
|
36891
|
+
version: "0.10.91",
|
|
36892
36892
|
description: "Skills library for AI coding agents",
|
|
36893
36893
|
type: "module",
|
|
36894
36894
|
bin: {
|
|
@@ -36932,6 +36932,7 @@ var init_package = __esm(() => {
|
|
|
36932
36932
|
"docs/runtime-activation.md",
|
|
36933
36933
|
"schemas/",
|
|
36934
36934
|
"LICENSE",
|
|
36935
|
+
"THIRD_PARTY_NOTICES",
|
|
36935
36936
|
"README.md",
|
|
36936
36937
|
"!dist/**/*test-utils.d.ts",
|
|
36937
36938
|
"!dist/**/*fixtures.d.ts"
|
|
@@ -36939,9 +36940,9 @@ var init_package = __esm(() => {
|
|
|
36939
36940
|
main: "./dist/index.js",
|
|
36940
36941
|
types: "./dist/index.d.ts",
|
|
36941
36942
|
scripts: {
|
|
36942
|
-
clean: "rm -rf bin/ dist/",
|
|
36943
|
-
build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @modelcontextprotocol/sdk ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
36944
|
-
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
36943
|
+
clean: "rm -rf bin/ dist/ THIRD_PARTY_NOTICES",
|
|
36944
|
+
build: "bun run clean && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/secrets --external @modelcontextprotocol/sdk ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/secrets --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/secrets ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts && bun scripts/third-party-notices.ts",
|
|
36945
|
+
"build:js": "rm -rf dist && bun build --external @hasna/secrets ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts ./src/runtime-prerequisites.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist && bun scripts/stage-kernel-lock-assets.ts",
|
|
36945
36946
|
test: "bun test --timeout 30000",
|
|
36946
36947
|
dev: "bun run ./src/cli/index.tsx",
|
|
36947
36948
|
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
@@ -36992,7 +36993,6 @@ var init_package = __esm(() => {
|
|
|
36992
36993
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
36993
36994
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
36994
36995
|
"@hasna/events": "0.1.18",
|
|
36995
|
-
"@hasna/secrets": "0.4.2",
|
|
36996
36996
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
36997
36997
|
chalk: "^5.3.0",
|
|
36998
36998
|
commander: "^12.1.0",
|
|
@@ -37004,6 +37004,9 @@ var init_package = __esm(() => {
|
|
|
37004
37004
|
zod: "^4.3.6",
|
|
37005
37005
|
"@hasna/contracts": "1.3.5"
|
|
37006
37006
|
},
|
|
37007
|
+
optionalDependencies: {
|
|
37008
|
+
"@hasna/secrets": "0.4.2"
|
|
37009
|
+
},
|
|
37007
37010
|
engines: {
|
|
37008
37011
|
bun: ">=1.0.0"
|
|
37009
37012
|
},
|
|
@@ -55539,6 +55542,9 @@ async function resolveSkillsConnection(env3 = process.env, options = {}) {
|
|
|
55539
55542
|
completed = await completePointerCredential(SKILLS_APP, pointer, explicitVault ? snapshotEnv : env3);
|
|
55540
55543
|
} catch (error) {
|
|
55541
55544
|
const translated = asSkillsFleetCredentialError(error, pointer.pointerVaultKey);
|
|
55545
|
+
if (translated?.message.includes("(@hasna/secrets) is not installed")) {
|
|
55546
|
+
throw new SkillsFleetCredentialError(`${translated.message} @hasna/secrets is optional and private: install it with access to it.`, "MISSING_API_CREDENTIAL");
|
|
55547
|
+
}
|
|
55542
55548
|
if (translated)
|
|
55543
55549
|
throw translated;
|
|
55544
55550
|
throw error;
|
|
@@ -96948,1312 +96954,19 @@ var init_cloud_executions = __esm(() => {
|
|
|
96948
96954
|
init_fleet_credentials();
|
|
96949
96955
|
});
|
|
96950
96956
|
|
|
96951
|
-
//
|
|
96952
|
-
import {
|
|
96953
|
-
import {
|
|
96954
|
-
|
|
96955
|
-
|
|
96956
|
-
import { createRequire as createRequire2 } from "module";
|
|
96957
|
-
import { hostname as osHostname2 } from "os";
|
|
96958
|
-
import { isAbsolute as isAbsolute34, join as join60 } from "path";
|
|
96959
|
-
import { createRequire as createRequire22 } from "module";
|
|
96960
|
-
function envToken2(name) {
|
|
96961
|
-
return name.toUpperCase().replace(/-/g, "_");
|
|
96962
|
-
}
|
|
96963
|
-
function clientTransportEnvKeys2(name) {
|
|
96964
|
-
const envSegment = envToken2(name);
|
|
96965
|
-
return {
|
|
96966
|
-
apiUrlKeys: [`HASNA_${envSegment}_API_URL`, `${envSegment}_API_URL`],
|
|
96967
|
-
apiKeyKeys: [`HASNA_${envSegment}_API_KEY`, `${envSegment}_API_KEY`]
|
|
96968
|
-
};
|
|
96969
|
-
}
|
|
96970
|
-
function credentialOverrideEnvKey2(name) {
|
|
96971
|
-
return `HASNA_${envToken2(name)}_API_KEY_OVERRIDE`;
|
|
96972
|
-
}
|
|
96973
|
-
function credentialPointerEnvKey2(name) {
|
|
96974
|
-
return `HASNA_${envToken2(name)}_API_KEY_REF`;
|
|
96975
|
-
}
|
|
96976
|
-
function homeDir2(env3) {
|
|
96977
|
-
const home = env3.HOME?.trim();
|
|
96978
|
-
return home ? home : null;
|
|
96979
|
-
}
|
|
96980
|
-
function absoluteOverride2(env3, key2) {
|
|
96981
|
-
const value = env3[key2]?.trim();
|
|
96982
|
-
return value && isAbsolute34(value) ? value : null;
|
|
96983
|
-
}
|
|
96984
|
-
function hasnaHomeDir2(env3) {
|
|
96985
|
-
const override = absoluteOverride2(env3, HASNA_HOME_ENV_KEY2);
|
|
96986
|
-
if (override)
|
|
96987
|
-
return override;
|
|
96988
|
-
const home = homeDir2(env3);
|
|
96989
|
-
return home ? join60(home, HASNA_HOME_DIR2) : null;
|
|
96990
|
-
}
|
|
96991
|
-
function appConfigDir2(name, env3) {
|
|
96992
|
-
const configRoot = absoluteOverride2(env3, HASNA_CONFIG_HOME_ENV_KEY2);
|
|
96993
|
-
if (configRoot)
|
|
96994
|
-
return join60(configRoot, name);
|
|
96995
|
-
const root2 = hasnaHomeDir2(env3);
|
|
96996
|
-
return root2 ? join60(root2, name, CONFIG_SUBDIR2) : null;
|
|
96997
|
-
}
|
|
96998
|
-
function credentialDiskSourceList2(name, env3, profile = null) {
|
|
96999
|
-
if (!SAFE_APP_SLUG2.test(name))
|
|
97000
|
-
return [];
|
|
97001
|
-
const directory2 = appConfigDir2(name, env3);
|
|
97002
|
-
if (!directory2)
|
|
97003
|
-
return [];
|
|
97004
|
-
const file2 = profile ? `${CREDENTIALS_FILE2}-${profile}` : CREDENTIALS_FILE2;
|
|
97005
|
-
return [{ path: join60(directory2, file2), tier: "disk" }];
|
|
97006
|
-
}
|
|
97007
|
-
function credentialDiskSources2(name, env3) {
|
|
97008
|
-
return credentialDiskSourceList2(name, env3, null).map((s) => s.path);
|
|
97009
|
-
}
|
|
97010
|
-
function profileDiskSources2(name, env3, profile) {
|
|
97011
|
-
return credentialDiskSourceList2(name, env3, profile).map((s) => s.path);
|
|
97012
|
-
}
|
|
97013
|
-
function parseEnvFile2(text5) {
|
|
97014
|
-
const values2 = new Map;
|
|
97015
|
-
const unusable = new Set;
|
|
97016
|
-
for (const rawLine of text5.split(/\r?\n/)) {
|
|
97017
|
-
const line = rawLine.trim();
|
|
97018
|
-
if (line.length === 0 || line.startsWith("#"))
|
|
97019
|
-
continue;
|
|
97020
|
-
const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
|
|
97021
|
-
const equals = withoutExport.indexOf("=");
|
|
97022
|
-
if (equals <= 0)
|
|
97023
|
-
continue;
|
|
97024
|
-
const key2 = withoutExport.slice(0, equals).trim();
|
|
97025
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key2))
|
|
97026
|
-
continue;
|
|
97027
|
-
let value = withoutExport.slice(equals + 1).trim();
|
|
97028
|
-
const quote3 = value[0];
|
|
97029
|
-
if (quote3 === '"' || quote3 === "'") {
|
|
97030
|
-
if (value.length < 2 || !value.endsWith(quote3)) {
|
|
97031
|
-
unusable.add(key2);
|
|
97032
|
-
continue;
|
|
97033
|
-
}
|
|
97034
|
-
value = value.slice(1, -1);
|
|
97035
|
-
}
|
|
97036
|
-
if (value.trim().length === 0) {
|
|
97037
|
-
unusable.add(key2);
|
|
97038
|
-
continue;
|
|
97039
|
-
}
|
|
97040
|
-
if (values2.has(key2) && values2.get(key2) !== value)
|
|
97041
|
-
unusable.add(key2);
|
|
97042
|
-
values2.set(key2, value);
|
|
97043
|
-
}
|
|
97044
|
-
return { values: values2, unusable };
|
|
97045
|
-
}
|
|
97046
|
-
function configFileModeAllowed2(mode) {
|
|
97047
|
-
const permissions = mode & 4095;
|
|
97048
|
-
return permissions === 256 || permissions === 384;
|
|
97049
|
-
}
|
|
97050
|
-
function configFileReadsCoherent2(before2, after2) {
|
|
97051
|
-
return before2.dev === after2.dev && before2.ino === after2.ino && before2.size === after2.size && before2.mtimeMs === after2.mtimeMs && before2.ctimeMs === after2.ctimeMs;
|
|
97052
|
-
}
|
|
97053
|
-
function readAppConfigFile2(path) {
|
|
97054
|
-
const unsafe = (reason2) => {
|
|
97055
|
-
throw new CredentialFileUnsafeError2(path, reason2);
|
|
97056
|
-
};
|
|
97057
|
-
let fd = -1;
|
|
97058
|
-
try {
|
|
97059
|
-
fd = openSync26(path, O_RDONLY3 | O_NOFOLLOW3 | O_NONBLOCK3);
|
|
97060
|
-
} catch (error61) {
|
|
97061
|
-
const code = error61.code;
|
|
97062
|
-
if (code === "ENOENT" || code === "ENOTDIR")
|
|
97063
|
-
return null;
|
|
97064
|
-
if (code === "ELOOP")
|
|
97065
|
-
unsafe("the path is a symlink");
|
|
97066
|
-
unsafe(`the path could not be opened (${code ?? "unknown error"})`);
|
|
97067
|
-
}
|
|
97068
|
-
try {
|
|
97069
|
-
const before2 = fstatSync26(fd);
|
|
97070
|
-
if (!before2.isFile())
|
|
97071
|
-
unsafe("the path is not a regular file");
|
|
97072
|
-
if (!configFileModeAllowed2(before2.mode)) {
|
|
97073
|
-
unsafe(`permission mode ${(before2.mode & 4095).toString(8).padStart(4, "0")} is not owner-only 0400 or 0600`);
|
|
97074
|
-
}
|
|
97075
|
-
const uid2 = process.getuid?.() ?? process.geteuid?.();
|
|
97076
|
-
if (uid2 !== undefined && before2.uid !== uid2)
|
|
97077
|
-
unsafe("the file is not owned by the current user");
|
|
97078
|
-
if (before2.size > MAX_CREDENTIAL_FILE_BYTES2)
|
|
97079
|
-
unsafe("the file exceeds the size limit");
|
|
97080
|
-
const bytes = readFileSync38(fd);
|
|
97081
|
-
const after2 = fstatSync26(fd);
|
|
97082
|
-
if (!configFileReadsCoherent2(before2, after2)) {
|
|
97083
|
-
unsafe("the file changed while being read");
|
|
97084
|
-
}
|
|
97085
|
-
return parseEnvFile2(bytes.toString("utf8"));
|
|
97086
|
-
} finally {
|
|
97087
|
-
if (fd !== -1)
|
|
97088
|
-
closeSync26(fd);
|
|
97089
|
-
}
|
|
97090
|
-
}
|
|
97091
|
-
function readCredentialFile2(path, apiKeyKeys) {
|
|
97092
|
-
const parsed = readAppConfigFile2(path);
|
|
97093
|
-
if (!parsed)
|
|
97094
|
-
return null;
|
|
97095
|
-
for (const key2 of apiKeyKeys) {
|
|
97096
|
-
if (parsed.unusable.has(key2)) {
|
|
97097
|
-
throw new CredentialFileUnsafeError2(path, `${key2} is declared but blank or malformed`);
|
|
97098
|
-
}
|
|
97099
|
-
}
|
|
97100
|
-
const values2 = apiKeyKeys.map((key2) => parsed.values.get(key2)?.trim()).filter((value) => Boolean(value));
|
|
97101
|
-
if (new Set(values2).size > 1) {
|
|
97102
|
-
throw new CredentialFileUnsafeError2(path, "credential aliases disagree");
|
|
97103
|
-
}
|
|
97104
|
-
return values2[0] ?? null;
|
|
97105
|
-
}
|
|
97106
|
-
function appConfigDiskValue2(name, env3, keys3) {
|
|
97107
|
-
const wanted = keys3.filter((key2) => !CREDENTIAL_SHAPED_KEY2.test(key2));
|
|
97108
|
-
if (wanted.length === 0)
|
|
97109
|
-
return null;
|
|
97110
|
-
for (const path of credentialDiskSources2(name, env3)) {
|
|
97111
|
-
const parsed = readAppConfigFile2(path);
|
|
97112
|
-
if (!parsed)
|
|
97113
|
-
continue;
|
|
97114
|
-
if (wanted.some((key2) => parsed.unusable.has(key2))) {
|
|
97115
|
-
return { key: wanted.find((key2) => parsed.unusable.has(key2)), value: "", path, unusable: true };
|
|
97116
|
-
}
|
|
97117
|
-
const values2 = wanted.map((key2) => parsed.values.get(key2)?.trim()).filter((value) => Boolean(value));
|
|
97118
|
-
if (new Set(values2).size > 1)
|
|
97119
|
-
throw new CredentialFileUnsafeError2(path, "configuration aliases disagree");
|
|
97120
|
-
for (const key2 of wanted) {
|
|
97121
|
-
if (parsed.unusable.has(key2))
|
|
97122
|
-
return { key: key2, value: "", path, unusable: true };
|
|
97123
|
-
const value = parsed.values.get(key2)?.trim();
|
|
97124
|
-
if (value)
|
|
97125
|
-
return { key: key2, value, path };
|
|
97126
|
-
}
|
|
97127
|
-
}
|
|
97128
|
-
return null;
|
|
97129
|
-
}
|
|
97130
|
-
function assertUsableCredential2(appName, source, value) {
|
|
97131
|
-
if (VAULT_POINTER_SHAPE2.test(value)) {
|
|
97132
|
-
throw new CredentialResolutionError2(appName, `The credential from ${source} looks like a secrets-vault pointer (a path-shaped reference like ` + `'namespace/app/live/api_key'). A vault path is NEVER accepted as a literal API key. ` + `Use ${credentialPointerEnvKey2(appName)} to resolve the key through the vault, or provide the actual key value.`, [source]);
|
|
97133
|
-
}
|
|
97134
|
-
if (!ILLEGAL_IN_HEADER_VALUE2.test(value))
|
|
97135
|
-
return;
|
|
97136
|
-
throw new CredentialResolutionError2(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
|
|
97137
|
-
}
|
|
97138
|
-
function sealCredential2(fields2) {
|
|
97139
|
-
const { apiKey } = fields2;
|
|
97140
|
-
const visible = {
|
|
97141
|
-
tier: fields2.tier,
|
|
97142
|
-
source: fields2.source,
|
|
97143
|
-
deliberate: fields2.deliberate,
|
|
97144
|
-
diskCandidates: Object.freeze([...fields2.diskCandidates]),
|
|
97145
|
-
warning: fields2.warning
|
|
97146
|
-
};
|
|
97147
|
-
const sealed = { ...visible };
|
|
97148
|
-
Object.defineProperty(sealed, "apiKey", {
|
|
97149
|
-
value: apiKey,
|
|
97150
|
-
enumerable: false,
|
|
97151
|
-
writable: false,
|
|
97152
|
-
configurable: false
|
|
97153
|
-
});
|
|
97154
|
-
if (fields2.pointerVaultKey !== undefined) {
|
|
97155
|
-
Object.defineProperty(sealed, "pointerVaultKey", {
|
|
97156
|
-
value: fields2.pointerVaultKey,
|
|
97157
|
-
enumerable: false,
|
|
97158
|
-
writable: false,
|
|
97159
|
-
configurable: false
|
|
97160
|
-
});
|
|
97161
|
-
}
|
|
97162
|
-
Object.defineProperty(sealed, INSPECT_CUSTOM2, {
|
|
97163
|
-
value: () => ({ ...visible, apiKey: "[redacted]" }),
|
|
97164
|
-
enumerable: false,
|
|
97165
|
-
writable: false,
|
|
97166
|
-
configurable: false
|
|
97167
|
-
});
|
|
97168
|
-
Object.defineProperty(sealed, CREDENTIAL_SEAL2, {
|
|
97169
|
-
value: true,
|
|
97170
|
-
enumerable: false,
|
|
97171
|
-
writable: false,
|
|
97172
|
-
configurable: false
|
|
97173
|
-
});
|
|
97174
|
-
return Object.freeze(sealed);
|
|
97175
|
-
}
|
|
97176
|
-
function isSealedCredential(credential) {
|
|
97177
|
-
return credential[CREDENTIAL_SEAL2] === true;
|
|
97178
|
-
}
|
|
97179
|
-
function explicitCredential(appName, apiKey) {
|
|
97180
|
-
const source = "explicit apiKey option";
|
|
97181
|
-
assertUsableCredential2(appName, source, apiKey);
|
|
97182
|
-
return sealCredential2({
|
|
97183
|
-
apiKey,
|
|
97184
|
-
tier: "argument",
|
|
97185
|
-
source,
|
|
97186
|
-
deliberate: true,
|
|
97187
|
-
diskCandidates: [],
|
|
97188
|
-
warning: null
|
|
97189
|
-
});
|
|
97190
|
-
}
|
|
97191
|
-
function validateAndSealResolvedCredential(appName, credential) {
|
|
97192
|
-
const apiKey = credential.apiKey;
|
|
97193
|
-
assertUsableCredential2(appName, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE, apiKey);
|
|
97194
|
-
if (!isSealedCredential(credential)) {
|
|
97195
|
-
return sealCredential2({
|
|
97196
|
-
apiKey,
|
|
97197
|
-
tier: "argument",
|
|
97198
|
-
source: CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE,
|
|
97199
|
-
deliberate: true,
|
|
97200
|
-
diskCandidates: [],
|
|
97201
|
-
warning: null
|
|
97202
|
-
});
|
|
97203
|
-
}
|
|
97204
|
-
return sealCredential2({
|
|
97205
|
-
apiKey,
|
|
97206
|
-
tier: credential.tier,
|
|
97207
|
-
source: credential.source,
|
|
97208
|
-
deliberate: credential.deliberate,
|
|
97209
|
-
diskCandidates: credential.diskCandidates,
|
|
97210
|
-
warning: credential.warning,
|
|
97211
|
-
...credential.pointerVaultKey !== undefined ? { pointerVaultKey: credential.pointerVaultKey } : {}
|
|
97212
|
-
});
|
|
97213
|
-
}
|
|
97214
|
-
function firstEnvValue2(env3, keys3) {
|
|
97215
|
-
for (const key2 of keys3) {
|
|
97216
|
-
if (!Object.prototype.hasOwnProperty.call(env3, key2))
|
|
97217
|
-
continue;
|
|
97218
|
-
const value = env3[key2]?.trim();
|
|
97219
|
-
if (value)
|
|
97220
|
-
return { key: key2, value };
|
|
97221
|
-
}
|
|
97222
|
-
return null;
|
|
97223
|
-
}
|
|
97224
|
-
function isAmbientEnvironment2(env3) {
|
|
97225
|
-
return env3 === process.env || env3[AMBIENT_ENVIRONMENT2] === true;
|
|
97226
|
-
}
|
|
97227
|
-
function defaultKeychainRunner2(argv) {
|
|
97228
|
-
const result2 = spawnSync5(KEYCHAIN_SECURITY_BIN2, [...argv], {
|
|
97229
|
-
encoding: "utf8",
|
|
97230
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
97231
|
-
timeout: KEYCHAIN_SPAWN_TIMEOUT_MS2
|
|
97232
|
-
});
|
|
97233
|
-
return {
|
|
97234
|
-
status: result2.status,
|
|
97235
|
-
stdout: result2.stdout ?? "",
|
|
97236
|
-
stderr: result2.error ? result2.error.message : result2.stderr ?? ""
|
|
97237
|
-
};
|
|
97238
|
-
}
|
|
97239
|
-
function keychainTierEnabled2(env3, options) {
|
|
97240
|
-
if ((options.platform ?? process.platform) !== "darwin")
|
|
97241
|
-
return false;
|
|
97242
|
-
if (options.enabled !== undefined)
|
|
97243
|
-
return options.enabled;
|
|
97244
|
-
return options.run !== undefined || isAmbientEnvironment2(env3);
|
|
97245
|
-
}
|
|
97246
|
-
function keychainAccount2(env3, options) {
|
|
97247
|
-
const station = env3[KEYCHAIN_STATION_ENV_KEY2]?.trim();
|
|
97248
|
-
if (station)
|
|
97249
|
-
return station;
|
|
97250
|
-
const host = (options.hostname ?? osHostname2)().split(".")[0]?.trim() ?? "";
|
|
97251
|
-
if (host)
|
|
97252
|
-
return host;
|
|
97253
|
-
const user = env3.USER?.trim();
|
|
97254
|
-
return user || null;
|
|
97255
|
-
}
|
|
97256
|
-
function keychainFailureHint2(text5) {
|
|
97257
|
-
const line = text5.split(/\r?\n/).find((entry) => entry.trim().length > 0)?.trim() ?? "";
|
|
97258
|
-
const clean = line.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 200);
|
|
97259
|
-
return clean ? `: ${clean}` : "";
|
|
97260
|
-
}
|
|
97261
|
-
function readKeychainItem2(name, env3, kind, options) {
|
|
97262
|
-
if (!SAFE_APP_SLUG2.test(name) || !keychainTierEnabled2(env3, options))
|
|
97263
|
-
return null;
|
|
97264
|
-
const account = keychainAccount2(env3, options);
|
|
97265
|
-
if (!account)
|
|
97266
|
-
return null;
|
|
97267
|
-
const service = `${KEYCHAIN_SERVICE_PREFIX2}.${name}.${kind}`;
|
|
97268
|
-
const source = `keychain:${service}@${account}`;
|
|
97269
|
-
const run = options.run ?? defaultKeychainRunner2;
|
|
97270
|
-
let result2;
|
|
97271
|
-
try {
|
|
97272
|
-
result2 = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
|
|
97273
|
-
} catch (error61) {
|
|
97274
|
-
const reason2 = keychainFailureHint2(error61 instanceof Error ? error61.message : String(error61));
|
|
97275
|
-
throw new CredentialResolutionError2(name, `The Keychain lookup for ${source} could not run${reason2}. A Keychain failure is never resolved ` + `around: fix the keychain, or delete the item to fall through to the credential on disk.`, [source]);
|
|
97276
|
-
}
|
|
97277
|
-
if (result2.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS2)
|
|
97278
|
-
return null;
|
|
97279
|
-
if (result2.status !== 0) {
|
|
97280
|
-
throw new CredentialResolutionError2(name, `The Keychain lookup for ${source} failed (security exited ` + `${result2.status ?? "without a status"}${keychainFailureHint2(result2.stderr)}). A Keychain item that ` + `exists but cannot be read is never resolved around: unlock the keychain, run from a session that ` + `may use it, or delete the item to fall through to the credential on disk.`, [source]);
|
|
97281
|
-
}
|
|
97282
|
-
const value = result2.stdout.trim();
|
|
97283
|
-
if (!value) {
|
|
97284
|
-
throw new CredentialResolutionError2(name, `${source} exists but holds an empty value; a declared item never falls through to another ` + `identity. Store a value in it or delete the item.`, [source]);
|
|
97285
|
-
}
|
|
97286
|
-
return { value, source };
|
|
97287
|
-
}
|
|
97288
|
-
function keychainConfigValue2(name, env3, options = {}) {
|
|
97289
|
-
return readKeychainItem2(name, env3, "api-url", options);
|
|
97290
|
-
}
|
|
97291
|
-
function snapshotClientEnvironment2(name, env3) {
|
|
97292
|
-
const keys3 = clientTransportEnvKeys2(name);
|
|
97293
|
-
const ambient = isAmbientEnvironment2(env3);
|
|
97294
|
-
const snapshot2 = Object.create(null);
|
|
97295
|
-
for (const key2 of [
|
|
97296
|
-
...keys3.apiUrlKeys,
|
|
97297
|
-
...keys3.apiKeyKeys,
|
|
97298
|
-
credentialOverrideEnvKey2(name),
|
|
97299
|
-
credentialPointerEnvKey2(name),
|
|
97300
|
-
CREDENTIAL_PROFILE_ENV_KEY2,
|
|
97301
|
-
"HOME",
|
|
97302
|
-
HASNA_HOME_ENV_KEY2,
|
|
97303
|
-
HASNA_CONFIG_HOME_ENV_KEY2,
|
|
97304
|
-
KEYCHAIN_STATION_ENV_KEY2,
|
|
97305
|
-
"USER"
|
|
97306
|
-
]) {
|
|
97307
|
-
const descriptor = Object.getOwnPropertyDescriptor(env3, key2);
|
|
97308
|
-
if (!descriptor)
|
|
97309
|
-
continue;
|
|
97310
|
-
if (!("value" in descriptor)) {
|
|
97311
|
-
throw new CredentialResolutionError2(name, `${key2} is accessor-backed; client configuration requires own data properties.`, [key2]);
|
|
97312
|
-
}
|
|
97313
|
-
if (descriptor.value !== undefined && typeof descriptor.value !== "string") {
|
|
97314
|
-
throw new CredentialResolutionError2(name, `${key2} must be a string data property.`, [key2]);
|
|
97315
|
-
}
|
|
97316
|
-
snapshot2[key2] = descriptor.value;
|
|
97317
|
-
}
|
|
97318
|
-
if (ambient) {
|
|
97319
|
-
Object.defineProperty(snapshot2, AMBIENT_ENVIRONMENT2, {
|
|
97320
|
-
value: true,
|
|
97321
|
-
enumerable: false,
|
|
97322
|
-
writable: false,
|
|
97323
|
-
configurable: false
|
|
97324
|
-
});
|
|
97325
|
-
}
|
|
97326
|
-
return Object.freeze(snapshot2);
|
|
97327
|
-
}
|
|
97328
|
-
function resolveCredential2(name, env3, options = {}) {
|
|
97329
|
-
env3 = snapshotClientEnvironment2(name, env3);
|
|
97330
|
-
const { apiKeyKeys } = clientTransportEnvKeys2(name);
|
|
97331
|
-
const diskPaths = credentialDiskSources2(name, env3);
|
|
97332
|
-
if (options.apiKey !== undefined) {
|
|
97333
|
-
const explicitKey = options.apiKey.trim();
|
|
97334
|
-
if (!explicitKey) {
|
|
97335
|
-
throw new CredentialResolutionError2(name, "The explicit apiKey argument is blank; an explicit credential never falls through to another identity.", ["explicit apiKey argument"]);
|
|
97336
|
-
}
|
|
97337
|
-
assertUsableCredential2(name, "the explicit apiKey argument", explicitKey);
|
|
97338
|
-
return sealCredential2({
|
|
97339
|
-
apiKey: explicitKey,
|
|
97340
|
-
tier: "argument",
|
|
97341
|
-
source: "explicit apiKey argument",
|
|
97342
|
-
deliberate: true,
|
|
97343
|
-
diskCandidates: diskPaths,
|
|
97344
|
-
warning: null
|
|
97345
|
-
});
|
|
97346
|
-
}
|
|
97347
|
-
const overrideKeyName = credentialOverrideEnvKey2(name);
|
|
97348
|
-
const overrideRaw = Object.prototype.hasOwnProperty.call(env3, overrideKeyName) ? env3[overrideKeyName] : undefined;
|
|
97349
|
-
if (overrideRaw !== undefined) {
|
|
97350
|
-
const override = overrideRaw.trim();
|
|
97351
|
-
if (!override) {
|
|
97352
|
-
throw new CredentialResolutionError2(name, `${overrideKeyName} is set but empty. It is a deliberate override, so it is not resolved around: ` + `either give it a real key or unset it to fall back to the credential on disk.`, [overrideKeyName]);
|
|
97353
|
-
}
|
|
97354
|
-
assertUsableCredential2(name, overrideKeyName, override);
|
|
97355
|
-
return sealCredential2({
|
|
97356
|
-
apiKey: override,
|
|
97357
|
-
tier: "override",
|
|
97358
|
-
source: overrideKeyName,
|
|
97359
|
-
deliberate: true,
|
|
97360
|
-
diskCandidates: diskPaths,
|
|
97361
|
-
warning: null
|
|
97362
|
-
});
|
|
97363
|
-
}
|
|
97364
|
-
const pointerKeyName = credentialPointerEnvKey2(name);
|
|
97365
|
-
const pointerRaw = Object.prototype.hasOwnProperty.call(env3, pointerKeyName) ? env3[pointerKeyName] : undefined;
|
|
97366
|
-
if (pointerRaw !== undefined) {
|
|
97367
|
-
const pointer = pointerRaw.trim();
|
|
97368
|
-
if (!pointer) {
|
|
97369
|
-
throw new CredentialResolutionError2(name, `${pointerKeyName} is set but empty. It is a deliberate vault pointer, so it is not resolved around: ` + `either give it a vault item key or unset it to fall back to the credential on disk.`, [pointerKeyName]);
|
|
97370
|
-
}
|
|
97371
|
-
if (!VAULT_POINTER_SHAPE2.test(pointer)) {
|
|
97372
|
-
throw new CredentialResolutionError2(name, `${pointerKeyName} must name a vault ITEM KEY (a path-shaped reference like ` + `'namespace/app/live/api_key'), not a credential value. A pointer that carries a literal is refused.`, [pointerKeyName]);
|
|
97373
|
-
}
|
|
97374
|
-
return sealCredential2({
|
|
97375
|
-
apiKey: "",
|
|
97376
|
-
pointerVaultKey: pointer,
|
|
97377
|
-
tier: "pointer",
|
|
97378
|
-
source: pointerKeyName,
|
|
97379
|
-
deliberate: true,
|
|
97380
|
-
diskCandidates: diskPaths,
|
|
97381
|
-
warning: null
|
|
97382
|
-
});
|
|
97383
|
-
}
|
|
97384
|
-
if (options.profile !== undefined && !options.profile.trim()) {
|
|
97385
|
-
throw new CredentialResolutionError2(name, "The explicit profile argument is blank; an explicit identity selection never falls through.", ["explicit profile argument"]);
|
|
97386
|
-
}
|
|
97387
|
-
const profileRaw = Object.prototype.hasOwnProperty.call(env3, CREDENTIAL_PROFILE_ENV_KEY2) ? env3[CREDENTIAL_PROFILE_ENV_KEY2] : undefined;
|
|
97388
|
-
if (profileRaw !== undefined && !profileRaw.trim()) {
|
|
97389
|
-
throw new CredentialResolutionError2(name, `${CREDENTIAL_PROFILE_ENV_KEY2} is set but blank.`, [CREDENTIAL_PROFILE_ENV_KEY2]);
|
|
97390
|
-
}
|
|
97391
|
-
const profile = options.profile?.trim() || profileRaw?.trim();
|
|
97392
|
-
if (profile) {
|
|
97393
|
-
const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY2;
|
|
97394
|
-
if (!SAFE_PROFILE2.test(profile)) {
|
|
97395
|
-
throw new CredentialResolutionError2(name, `Profile name from ${profileSource} is not usable in a path. ` + `Use letters, digits, dot, dash, or underscore.`, [profileSource]);
|
|
97396
|
-
}
|
|
97397
|
-
const paths = profileDiskSources2(name, env3, profile);
|
|
97398
|
-
for (const path of paths) {
|
|
97399
|
-
const value = readCredentialFile2(path, apiKeyKeys);
|
|
97400
|
-
if (value) {
|
|
97401
|
-
assertUsableCredential2(name, path, value);
|
|
97402
|
-
return sealCredential2({
|
|
97403
|
-
apiKey: value,
|
|
97404
|
-
tier: "profile",
|
|
97405
|
-
source: path,
|
|
97406
|
-
deliberate: true,
|
|
97407
|
-
diskCandidates: paths,
|
|
97408
|
-
warning: null
|
|
97409
|
-
});
|
|
97410
|
-
}
|
|
97411
|
-
}
|
|
97412
|
-
throw new CredentialResolutionError2(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY2}.`, paths);
|
|
97413
|
-
}
|
|
97414
|
-
const definedEnvEntries = apiKeyKeys.filter((key2) => Object.prototype.hasOwnProperty.call(env3, key2) && env3[key2] !== undefined).map((key2) => ({ key: key2, value: String(env3[key2]).trim() }));
|
|
97415
|
-
const blankEnv = definedEnvEntries.find((entry) => entry.value.length === 0);
|
|
97416
|
-
if (blankEnv) {
|
|
97417
|
-
throw new CredentialResolutionError2(name, `${blankEnv.key} is set but blank; a declared credential never falls through to another alias or identity.`, [blankEnv.key]);
|
|
97418
|
-
}
|
|
97419
|
-
if (definedEnvEntries.length > 1 && new Set(definedEnvEntries.map((entry) => entry.value)).size > 1) {
|
|
97420
|
-
throw new CredentialResolutionError2(name, `${definedEnvEntries.map((entry) => entry.key).join(" and ")} disagree; credential aliases must be identical or only one may be set.`, definedEnvEntries.map((entry) => entry.key));
|
|
97421
|
-
}
|
|
97422
|
-
const envHit = firstEnvValue2(env3, apiKeyKeys);
|
|
97423
|
-
const keychainHit = readKeychainItem2(name, env3, "api-key", options.keychain ?? {});
|
|
97424
|
-
if (keychainHit) {
|
|
97425
|
-
assertUsableCredential2(name, keychainHit.source, keychainHit.value);
|
|
97426
|
-
const warning = envHit && envHit.value !== keychainHit.value ? `Credential sources disagree for '${name}': ${keychainHit.source} and ${envHit.key} hold ` + `different keys. ${keychainHit.source} wins, because the Keychain is re-read on every call while ` + `an environment variable is a snapshot. Reconcile them \u2014 a rotation that updated only one leaves ` + `the other to fail 401 wherever it is loaded first.` : null;
|
|
97427
|
-
return sealCredential2({
|
|
97428
|
-
apiKey: keychainHit.value,
|
|
97429
|
-
tier: "keychain",
|
|
97430
|
-
source: keychainHit.source,
|
|
97431
|
-
deliberate: false,
|
|
97432
|
-
diskCandidates: diskPaths,
|
|
97433
|
-
warning
|
|
97434
|
-
});
|
|
97435
|
-
}
|
|
97436
|
-
const diskSourceList = credentialDiskSourceList2(name, env3, null);
|
|
97437
|
-
const diskHits = diskSourceList.map((src) => ({ src, value: readCredentialFile2(src.path, apiKeyKeys) })).filter((hit) => hit.value !== null);
|
|
97438
|
-
if (diskHits.length > 0) {
|
|
97439
|
-
const winner = diskHits[0];
|
|
97440
|
-
assertUsableCredential2(name, winner.src.path, winner.value);
|
|
97441
|
-
const divergentSources = [
|
|
97442
|
-
...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.src.path),
|
|
97443
|
-
...envHit && envHit.value !== winner.value ? [envHit.key] : []
|
|
97444
|
-
];
|
|
97445
|
-
const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.src.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.src.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
|
|
97446
|
-
return sealCredential2({
|
|
97447
|
-
apiKey: winner.value,
|
|
97448
|
-
tier: winner.src.tier,
|
|
97449
|
-
source: winner.src.path,
|
|
97450
|
-
deliberate: false,
|
|
97451
|
-
diskCandidates: diskPaths,
|
|
97452
|
-
warning
|
|
97453
|
-
});
|
|
97454
|
-
}
|
|
97455
|
-
if (envHit) {
|
|
97456
|
-
assertUsableCredential2(name, envHit.key, envHit.value);
|
|
97457
|
-
return sealCredential2({
|
|
97458
|
-
apiKey: envHit.value,
|
|
97459
|
-
tier: "env",
|
|
97460
|
-
source: envHit.key,
|
|
97461
|
-
deliberate: false,
|
|
97462
|
-
diskCandidates: diskPaths,
|
|
97463
|
-
warning: null
|
|
97464
|
-
});
|
|
97465
|
-
}
|
|
97466
|
-
return null;
|
|
97467
|
-
}
|
|
97468
|
-
async function completePointerCredential2(name, pointerResolution, env3 = process.env) {
|
|
97469
|
-
const vaultKey = pointerResolution.pointerVaultKey;
|
|
97470
|
-
const pointerEnvKey = pointerResolution.source;
|
|
97471
|
-
if (!vaultKey) {
|
|
97472
|
-
throw new CredentialResolutionError2(name, `Pointer resolution from ${pointerEnvKey} carries no vault item key; this is a defect in the resolver.`, [pointerEnvKey]);
|
|
97473
|
-
}
|
|
97474
|
-
let secretsSdk;
|
|
97475
|
-
try {
|
|
97476
|
-
secretsSdk = requireSecretsSdk2(SECRETS_PACKAGE_SPECIFIER);
|
|
97477
|
-
} catch {
|
|
97478
|
-
throw new CredentialResolutionError2(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets SDK (@hasna/secrets) is not installed ` + `in this process. A vault pointer is TERMINAL: install @hasna/secrets to resolve it, or unset ${pointerEnvKey}.`, [pointerEnvKey]);
|
|
97479
|
-
}
|
|
97480
|
-
let client2;
|
|
97481
|
-
try {
|
|
97482
|
-
client2 = secretsSdk.createSecretsClientFromEnv(env3);
|
|
97483
|
-
} catch {
|
|
97484
|
-
throw new CredentialResolutionError2(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets client could not be configured from this ` + `environment (the secrets service URL and key env are missing or invalid). A vault pointer is TERMINAL and ` + `never falls through to a literal or disk credential.`, [pointerEnvKey]);
|
|
97485
|
-
}
|
|
97486
|
-
let secret;
|
|
96957
|
+
// src/lib/execution-secrets.ts
|
|
96958
|
+
import { constants as constants26, closeSync as closeSync26, fstatSync as fstatSync26, openSync as openSync26, readSync as readSync20, realpathSync as realpathSync16 } from "fs";
|
|
96959
|
+
import { hostname as stationHostname } from "os";
|
|
96960
|
+
async function defaultSecretsClientFactory(load = () => import("@hasna/secrets/sdk")) {
|
|
96961
|
+
let sdk;
|
|
97487
96962
|
try {
|
|
97488
|
-
|
|
96963
|
+
sdk = await load();
|
|
97489
96964
|
} catch {
|
|
97490
|
-
|
|
97491
|
-
}
|
|
97492
|
-
const value = secret.value;
|
|
97493
|
-
if (!value) {
|
|
97494
|
-
throw new CredentialResolutionError2(name, `${pointerEnvKey} resolved vault item '${vaultKey}', but it holds no value. A vault pointer is TERMINAL.`, [pointerEnvKey]);
|
|
97495
|
-
}
|
|
97496
|
-
assertUsableCredential2(name, `${pointerEnvKey} -> vault:${vaultKey}`, value);
|
|
97497
|
-
return sealCredential2({
|
|
97498
|
-
apiKey: value,
|
|
97499
|
-
tier: "pointer",
|
|
97500
|
-
source: `${pointerEnvKey} -> vault:${vaultKey}`,
|
|
97501
|
-
deliberate: true,
|
|
97502
|
-
diskCandidates: pointerResolution.diskCandidates,
|
|
97503
|
-
warning: null
|
|
97504
|
-
});
|
|
97505
|
-
}
|
|
97506
|
-
function defaultFleetGatewayBaseUrl2(name) {
|
|
97507
|
-
return `${DEFAULT_FLEET_GATEWAY_ORIGIN2}/${validateAppSlug2(name)}`;
|
|
97508
|
-
}
|
|
97509
|
-
function isValidDnsDomain2(value) {
|
|
97510
|
-
if (value.length === 0 || value.length > 253 || ASCII_CONTROL_PATTERN2.test(value) || /[^\x00-\x7f]/.test(value)) {
|
|
97511
|
-
return false;
|
|
97512
|
-
}
|
|
97513
|
-
return value.split(".").every((label) => label.length <= 63 && !label.startsWith("xn--") && DNS_LABEL_PATTERN2.test(label));
|
|
97514
|
-
}
|
|
97515
|
-
function validateAppSlug2(name) {
|
|
97516
|
-
if (name.length > 63 || !DNS_LABEL_PATTERN2.test(name)) {
|
|
97517
|
-
throw new Error("App name must be one lowercase DNS label.");
|
|
97518
|
-
}
|
|
97519
|
-
return name;
|
|
97520
|
-
}
|
|
97521
|
-
function rawAuthority2(value) {
|
|
97522
|
-
const match = /^[a-z][a-z0-9+.-]*:\/\//i.exec(value);
|
|
97523
|
-
if (!match)
|
|
97524
|
-
throw new Error("API URL must be absolute.");
|
|
97525
|
-
const afterScheme = value.slice(match[0].length);
|
|
97526
|
-
const boundary = afterScheme.search(/[/?#]/);
|
|
97527
|
-
const authority = boundary === -1 ? afterScheme : afterScheme.slice(0, boundary);
|
|
97528
|
-
if (!authority)
|
|
97529
|
-
throw new Error("API URL must include a hostname.");
|
|
97530
|
-
return authority;
|
|
97531
|
-
}
|
|
97532
|
-
function assertCanonicalPort2(port) {
|
|
97533
|
-
if (!/^[0-9]+$/.test(port) || port.length > 1 && port.startsWith("0")) {
|
|
97534
|
-
throw new Error("API URL authority must contain a canonical port between 1 and 65535.");
|
|
97535
|
-
}
|
|
97536
|
-
const numericPort = Number(port);
|
|
97537
|
-
if (!Number.isSafeInteger(numericPort) || numericPort < 1 || numericPort > 65535) {
|
|
97538
|
-
throw new Error("API URL authority must contain a canonical port between 1 and 65535.");
|
|
97539
|
-
}
|
|
97540
|
-
}
|
|
97541
|
-
function canonicalAuthorityHostname2(authority) {
|
|
97542
|
-
let rawHostname;
|
|
97543
|
-
if (authority.startsWith("[")) {
|
|
97544
|
-
const closingBracket = authority.indexOf("]");
|
|
97545
|
-
if (closingBracket === -1) {
|
|
97546
|
-
throw new Error("API URL authority must contain a canonical hostname.");
|
|
97547
|
-
}
|
|
97548
|
-
rawHostname = authority.slice(0, closingBracket + 1);
|
|
97549
|
-
const portSuffix = authority.slice(closingBracket + 1);
|
|
97550
|
-
if (portSuffix) {
|
|
97551
|
-
if (!portSuffix.startsWith(":")) {
|
|
97552
|
-
throw new Error("API URL authority must contain a canonical hostname and port.");
|
|
97553
|
-
}
|
|
97554
|
-
assertCanonicalPort2(portSuffix.slice(1));
|
|
97555
|
-
}
|
|
97556
|
-
if (isIP3(rawHostname.slice(1, -1)) !== 6) {
|
|
97557
|
-
throw new Error("API URL authority must contain a canonical IPv6 literal.");
|
|
97558
|
-
}
|
|
97559
|
-
} else {
|
|
97560
|
-
const firstColon = authority.indexOf(":");
|
|
97561
|
-
const lastColon = authority.lastIndexOf(":");
|
|
97562
|
-
if (firstColon !== lastColon) {
|
|
97563
|
-
throw new Error("IPv6 API URL authorities must use brackets.");
|
|
97564
|
-
}
|
|
97565
|
-
if (lastColon !== -1) {
|
|
97566
|
-
const port = authority.slice(lastColon + 1);
|
|
97567
|
-
assertCanonicalPort2(port);
|
|
97568
|
-
rawHostname = authority.slice(0, lastColon);
|
|
97569
|
-
} else {
|
|
97570
|
-
rawHostname = authority;
|
|
97571
|
-
}
|
|
97572
|
-
const ipVersion = isIP3(rawHostname);
|
|
97573
|
-
const numericAddressParts = rawHostname.split(".");
|
|
97574
|
-
const looksLikeNonCanonicalIpv4 = numericAddressParts.every((part) => /^(?:0x[0-9a-f]+|[0-9]+)$/i.test(part));
|
|
97575
|
-
if (ipVersion !== 4 && looksLikeNonCanonicalIpv4 || ipVersion !== 4 && !isValidDnsDomain2(rawHostname.toLowerCase())) {
|
|
97576
|
-
throw new Error("API URL authority must contain a canonical ASCII hostname.");
|
|
97577
|
-
}
|
|
97578
|
-
}
|
|
97579
|
-
return rawHostname.toLowerCase();
|
|
97580
|
-
}
|
|
97581
|
-
function isDeliberateLoopbackHttpAuthority2(authority) {
|
|
97582
|
-
return /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/i.test(authority);
|
|
97583
|
-
}
|
|
97584
|
-
function toV1BaseUrl2(apiUrl) {
|
|
97585
|
-
if (ASCII_CONTROL_PATTERN2.test(apiUrl)) {
|
|
97586
|
-
throw new Error("API URL must not contain ASCII control characters.");
|
|
97587
|
-
}
|
|
97588
|
-
const input2 = apiUrl.trim();
|
|
97589
|
-
const authority = rawAuthority2(input2);
|
|
97590
|
-
if (authority.includes("@") || authority.includes("\\") || authority.includes("%") || /[^\x00-\x7f]/.test(authority)) {
|
|
97591
|
-
throw new Error("API URL authority must be canonical ASCII without credentials.");
|
|
97592
|
-
}
|
|
97593
|
-
const canonicalHostname = canonicalAuthorityHostname2(authority);
|
|
97594
|
-
const url2 = new URL(input2);
|
|
97595
|
-
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
97596
|
-
throw new Error("API URL must use http or https.");
|
|
97597
|
-
}
|
|
97598
|
-
if (url2.username || url2.password) {
|
|
97599
|
-
throw new Error("API URL must not include credentials.");
|
|
97600
|
-
}
|
|
97601
|
-
if (!url2.hostname || url2.hostname.endsWith(".")) {
|
|
97602
|
-
throw new Error("API URL must include a canonical hostname.");
|
|
97603
|
-
}
|
|
97604
|
-
if (url2.hostname.toLowerCase() !== canonicalHostname) {
|
|
97605
|
-
throw new Error("API URL authority must not rely on parser hostname normalization.");
|
|
97606
|
-
}
|
|
97607
|
-
if (url2.hostname.split(".").some((label) => label.toLowerCase().startsWith("xn--"))) {
|
|
97608
|
-
throw new Error("API URL must not use IDN or punycode hostnames.");
|
|
97609
|
-
}
|
|
97610
|
-
if (url2.protocol === "http:" && !isDeliberateLoopbackHttpAuthority2(authority)) {
|
|
97611
|
-
throw new Error("API URL may use http only for an exact loopback authority.");
|
|
97612
|
-
}
|
|
97613
|
-
if (url2.search || url2.hash) {
|
|
97614
|
-
throw new Error("API URL must not include a query string or fragment.");
|
|
97615
|
-
}
|
|
97616
|
-
let path = url2.pathname.replace(/\/+$/, "");
|
|
97617
|
-
if (path.endsWith("/v1"))
|
|
97618
|
-
path = path.slice(0, -"/v1".length);
|
|
97619
|
-
url2.pathname = `${path}/v1`;
|
|
97620
|
-
return url2.toString().replace(/\/+$/, "");
|
|
97621
|
-
}
|
|
97622
|
-
function resolveClientTransportSnapshot(name, env3 = process.env, options = {}) {
|
|
97623
|
-
env3 = snapshotClientEnvironment2(name, env3);
|
|
97624
|
-
const keys3 = clientTransportEnvKeys2(name);
|
|
97625
|
-
const definedUrlEntries = keys3.apiUrlKeys.filter((key2) => Object.prototype.hasOwnProperty.call(env3, key2) && env3[key2] !== undefined).map((key2) => ({ key: key2, raw: String(env3[key2]) }));
|
|
97626
|
-
const blankUrl = definedUrlEntries.find((entry) => entry.raw.trim().length === 0);
|
|
97627
|
-
if (blankUrl) {
|
|
97628
|
-
throw new ClientTransportConfigurationError2(name, `${blankUrl.key} is set but blank; public clients require an explicit HTTPS API URL and never select local storage.`, [blankUrl.key]);
|
|
97629
|
-
}
|
|
97630
|
-
const controlledUrl = definedUrlEntries.find((entry) => ASCII_CONTROL_PATTERN2.test(entry.raw));
|
|
97631
|
-
if (controlledUrl) {
|
|
97632
|
-
throw new ClientTransportConfigurationError2(name, `${controlledUrl.key} contains ASCII control characters.`, [controlledUrl.key]);
|
|
97633
|
-
}
|
|
97634
|
-
const usableUrlEntries = definedUrlEntries.map((entry) => ({ key: entry.key, value: entry.raw.trim() }));
|
|
97635
|
-
if (usableUrlEntries.length > 1 && new Set(usableUrlEntries.map((entry) => entry.value)).size > 1) {
|
|
97636
|
-
throw new ClientTransportConfigurationError2(name, `${usableUrlEntries.map((entry) => entry.key).join(" and ")} disagree; client authority aliases must be identical or only one may be set.`, usableUrlEntries.map((entry) => entry.key));
|
|
97637
|
-
}
|
|
97638
|
-
const envUrlHit = usableUrlEntries[0] ?? null;
|
|
97639
|
-
const keychainUrlHit = keychainConfigValue2(name, env3, options.credentials?.keychain);
|
|
97640
|
-
const diskConfigUrlHit = appConfigDiskValue2(name, env3, keys3.apiUrlKeys);
|
|
97641
|
-
if (diskConfigUrlHit?.unusable) {
|
|
97642
|
-
throw new ClientTransportConfigurationError2(name, `${diskConfigUrlHit.key} in ${diskConfigUrlHit.path} is declared but blank or malformed; public clients require a valid HTTPS service authority.`, [diskConfigUrlHit.path]);
|
|
97643
|
-
}
|
|
97644
|
-
const urlCandidates = [
|
|
97645
|
-
...envUrlHit ? [envUrlHit] : [],
|
|
97646
|
-
...keychainUrlHit ? [{ key: keychainUrlHit.source, value: keychainUrlHit.value }] : [],
|
|
97647
|
-
...diskConfigUrlHit ? [{ key: diskConfigUrlHit.path, value: diskConfigUrlHit.value.trim() }] : []
|
|
97648
|
-
];
|
|
97649
|
-
const configuredUrl = urlCandidates[0] ?? null;
|
|
97650
|
-
const divergentUrls = urlCandidates.filter((candidate2) => candidate2.value !== configuredUrl?.value);
|
|
97651
|
-
if (configuredUrl && divergentUrls.length > 0) {
|
|
97652
|
-
throw new ClientTransportConfigurationError2(name, `${configuredUrl.key} and ${divergentUrls.map((candidate2) => candidate2.key).join(" and ")} select different service authorities; refusing to send a credential written for one authority to the other.`, urlCandidates.map((candidate2) => candidate2.key));
|
|
97653
|
-
}
|
|
97654
|
-
const warnings = [];
|
|
97655
|
-
if (configuredUrl && !envUrlHit) {
|
|
97656
|
-
warnings.push(`No ${keys3.apiUrlKeys[0]} in the environment; the server URL in ${configuredUrl.key} was used, so this client connects to the server. ` + `Keep that entry aligned with the intended service authority.`);
|
|
97657
|
-
}
|
|
97658
|
-
const credential = resolveCredential2(name, env3, options.credentials);
|
|
97659
|
-
if (!credential) {
|
|
97660
|
-
const diskHint = credentialDiskSourcesForMessage(name, env3);
|
|
97661
|
-
const lead = configuredUrl ? `${configuredUrl.key} selects the HTTP server for '${name}', but no API key could be resolved` : `${keys3.apiUrlKeys[0]} is not set and no API key could be resolved for '${name}'; a credential is required before the default fleet gateway authority applies`;
|
|
97662
|
-
warnings.push(`${lead}; refusing to create an unauthenticated client \u2014 public clients never fall back to SQLite or another local store. ` + `Looked in the Keychain (macOS only), then for a credential file at ${diskHint}, then for ${keys3.apiKeyKeys[0]} in the environment.`);
|
|
97663
|
-
throw new ClientTransportConfigurationError2(name, warnings.join(" "), [configuredUrl?.key ?? keys3.apiUrlKeys[0]]);
|
|
97664
|
-
}
|
|
97665
|
-
if (credential.warning)
|
|
97666
|
-
warnings.push(credential.warning);
|
|
97667
|
-
let urlHit;
|
|
97668
|
-
if (configuredUrl) {
|
|
97669
|
-
urlHit = configuredUrl;
|
|
97670
|
-
} else {
|
|
97671
|
-
try {
|
|
97672
|
-
urlHit = { key: DEFAULT_AUTHORITY_SOURCE, value: defaultFleetGatewayBaseUrl2(name) };
|
|
97673
|
-
} catch (error61) {
|
|
97674
|
-
const message = error61 instanceof Error ? error61.message : String(error61);
|
|
97675
|
-
throw new ClientTransportConfigurationError2(name, `No ${keys3.apiUrlKeys[0]} is configured and the default fleet gateway authority cannot be composed for '${name}': ${message}`, [keys3.apiUrlKeys[0]]);
|
|
97676
|
-
}
|
|
97677
|
-
}
|
|
97678
|
-
const apiUrlSource = urlHit.key;
|
|
97679
|
-
let baseUrl;
|
|
97680
|
-
try {
|
|
97681
|
-
baseUrl = toV1BaseUrl2(urlHit.value);
|
|
97682
|
-
} catch (error61) {
|
|
97683
|
-
const message = error61 instanceof Error ? error61.message : String(error61);
|
|
97684
|
-
throw new ClientTransportConfigurationError2(name, `Invalid API URL from ${apiUrlSource}: ${message}`, [apiUrlSource]);
|
|
96965
|
+
return refuse6("LOCAL_SECRET_UNAVAILABLE", "Vault secret bindings need the optional @hasna/secrets package, which this install could not load. Install @hasna/secrets with access to it, then run again.");
|
|
97685
96966
|
}
|
|
97686
|
-
return
|
|
97687
|
-
resolution: {
|
|
97688
|
-
transport: "http",
|
|
97689
|
-
transportSource: urlHit.key,
|
|
97690
|
-
baseUrl,
|
|
97691
|
-
apiUrlSource,
|
|
97692
|
-
apiKeyPresent: true,
|
|
97693
|
-
apiKeySource: credential.source,
|
|
97694
|
-
apiKeyTier: credential.tier,
|
|
97695
|
-
misconfigured: false,
|
|
97696
|
-
warning: warnings.length > 0 ? warnings.join(" ") : null
|
|
97697
|
-
},
|
|
97698
|
-
credential
|
|
97699
|
-
};
|
|
97700
|
-
}
|
|
97701
|
-
function resolveClientTransport(name, env3 = process.env, options = {}) {
|
|
97702
|
-
return resolveClientTransportSnapshot(name, env3, options).resolution;
|
|
97703
|
-
}
|
|
97704
|
-
function credentialDiskSourcesForMessage(name, env3) {
|
|
97705
|
-
const paths = credentialDiskSources2(name, env3);
|
|
97706
|
-
return paths.length > 0 ? paths.join(" or ") : "<no HOME or HASNA_HOME set in this environment, so no credential file was consulted>";
|
|
96967
|
+
return () => sdk.createSecretsClientFromEnv();
|
|
97707
96968
|
}
|
|
97708
|
-
function
|
|
97709
|
-
if (typeof apiKey === "function") {
|
|
97710
|
-
return validateAndSealResolvedCredential(name, apiKey());
|
|
97711
|
-
}
|
|
97712
|
-
return explicitCredential(name, apiKey);
|
|
97713
|
-
}
|
|
97714
|
-
async function resolveRequestCredential(name, apiKey, env3 = process.env) {
|
|
97715
|
-
const resolved = currentCredential(name, apiKey);
|
|
97716
|
-
if (resolved.tier === "pointer") {
|
|
97717
|
-
return completePointerCredential2(name, resolved, env3);
|
|
97718
|
-
}
|
|
97719
|
-
return resolved;
|
|
97720
|
-
}
|
|
97721
|
-
function authFailureGuidance(credential) {
|
|
97722
|
-
const origin = `The API key for this request came from ${credential.source}`;
|
|
97723
|
-
if (credential.deliberate) {
|
|
97724
|
-
const remedy = credential.source === CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE ? `Fix that provider so it returns the current key, or replace it with resolveCredential() ` + `so diagnostics can name the original source.` : `Rotate that key, or unset the override to use the credential on disk.`;
|
|
97725
|
-
return `${origin} \u2014 a credential you selected deliberately. It was NOT substituted with any other key: ` + `falling back here would authenticate as a different principal than the one you named, which is ` + `exactly the failure an override exists to prevent. ${remedy}`;
|
|
97726
|
-
}
|
|
97727
|
-
if (credential.tier === "env") {
|
|
97728
|
-
const target = credential.diskCandidates[0];
|
|
97729
|
-
const remedy = target ? `Store the CURRENT key in the Keychain or write it to ${target} \u2014 both are re-read on every call, so ` + `rotations take effect immediately and in every shell. Do not simply unset ${credential.source}: ` + `nothing was found in the Keychain or on disk, so that would leave this client with no credential at all.` : `This environment has no HOME or HASNA_HOME, so no credential file could be consulted; the disk tier is ` + `unavailable here and there is nothing to fall back to. Set HOME, or supply the key explicitly.`;
|
|
97730
|
-
return `${origin}, a variable in this process's environment. If a wrapper injected it for this one process, the ` + `wrapper re-reads its store on every invocation and the stored key itself is being rejected \u2014 rotate it. ` + `If this SHELL exported it, the export is a snapshot taken when the shell started: a STALE SHELL that ` + `exported the key before it was rotated keeps sending the old one until it exits. ${remedy}`;
|
|
97731
|
-
}
|
|
97732
|
-
if (credential.tier === "keychain") {
|
|
97733
|
-
return `${origin}, which was re-read from the Keychain on this very call \u2014 so a stale shell is NOT the cause ` + `here. The stored item is genuinely being rejected: update it with the current key, or re-run the fleet ` + `key distribution so this machine gets the current key.`;
|
|
97734
|
-
}
|
|
97735
|
-
return `${origin}, which was re-read from disk on this very call \u2014 so a stale shell is NOT the cause here. ` + `The stored credential is genuinely being rejected: rotate it, or re-run the fleet key distribution ` + `so this machine gets the current key.`;
|
|
97736
|
-
}
|
|
97737
|
-
function assertNoAuthorityOverrideHeaders(headers, source) {
|
|
97738
|
-
if (!headers)
|
|
97739
|
-
return;
|
|
97740
|
-
const forbidden = Object.keys(headers).find((name) => AUTHORITY_OVERRIDE_HEADERS2.has(name.trim().toLowerCase()));
|
|
97741
|
-
if (forbidden) {
|
|
97742
|
-
throw new Error(`Authenticated ${source} headers must not set authority header '${forbidden}'.`);
|
|
97743
|
-
}
|
|
97744
|
-
}
|
|
97745
|
-
function appendQuery(path, query) {
|
|
97746
|
-
if (!query)
|
|
97747
|
-
return path;
|
|
97748
|
-
const params = query instanceof URLSearchParams ? query : new URLSearchParams;
|
|
97749
|
-
if (!(query instanceof URLSearchParams)) {
|
|
97750
|
-
for (const [key2, value] of Object.entries(query)) {
|
|
97751
|
-
if (value === null || value === undefined)
|
|
97752
|
-
continue;
|
|
97753
|
-
if (Array.isArray(value)) {
|
|
97754
|
-
for (const v of value)
|
|
97755
|
-
params.append(key2, String(v));
|
|
97756
|
-
} else {
|
|
97757
|
-
params.append(key2, String(value));
|
|
97758
|
-
}
|
|
97759
|
-
}
|
|
97760
|
-
}
|
|
97761
|
-
const qs = params.toString();
|
|
97762
|
-
if (!qs)
|
|
97763
|
-
return path;
|
|
97764
|
-
return `${path}${path.includes("?") ? "&" : "?"}${qs}`;
|
|
97765
|
-
}
|
|
97766
|
-
function createHasnaHttpTransportInternal(options, requestBindingProvider) {
|
|
97767
|
-
const fetchImpl = options.fetchImpl ?? ((input2, init) => fetch(input2, init));
|
|
97768
|
-
const base2 = toV1BaseUrl2(options.baseUrl);
|
|
97769
|
-
const timeoutMs = options.timeoutMs ?? 30000;
|
|
97770
|
-
const sleep = options.sleepImpl ?? defaultSleep;
|
|
97771
|
-
const defaultRetry = options.retry;
|
|
97772
|
-
function resolveRetry(callRetry) {
|
|
97773
|
-
const chosen = callRetry !== undefined ? callRetry : defaultRetry;
|
|
97774
|
-
if (chosen === false)
|
|
97775
|
-
return null;
|
|
97776
|
-
const r2 = chosen ?? {};
|
|
97777
|
-
return {
|
|
97778
|
-
retries: r2.retries ?? 2,
|
|
97779
|
-
baseDelayMs: r2.baseDelayMs ?? 200,
|
|
97780
|
-
maxDelayMs: r2.maxDelayMs ?? 2000,
|
|
97781
|
-
retryStatuses: r2.retryStatuses ?? [...DEFAULT_RETRY_STATUSES]
|
|
97782
|
-
};
|
|
97783
|
-
}
|
|
97784
|
-
async function once2(method2, rel, url2, body, opts, credential) {
|
|
97785
|
-
assertNoAuthorityOverrideHeaders(options.headers, "transport");
|
|
97786
|
-
assertNoAuthorityOverrideHeaders(opts.headers, "request");
|
|
97787
|
-
const headers = {
|
|
97788
|
-
"x-api-key": credential.apiKey,
|
|
97789
|
-
Authorization: `Bearer ${credential.apiKey}`,
|
|
97790
|
-
Accept: "application/json",
|
|
97791
|
-
...options.headers ?? {},
|
|
97792
|
-
...opts.headers ?? {}
|
|
97793
|
-
};
|
|
97794
|
-
if (opts.idempotencyKey)
|
|
97795
|
-
headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
97796
|
-
const init = {
|
|
97797
|
-
method: method2,
|
|
97798
|
-
headers,
|
|
97799
|
-
redirect: "manual"
|
|
97800
|
-
};
|
|
97801
|
-
if (body !== undefined) {
|
|
97802
|
-
headers["Content-Type"] = "application/json";
|
|
97803
|
-
init.body = JSON.stringify(body);
|
|
97804
|
-
}
|
|
97805
|
-
const controller = new AbortController;
|
|
97806
|
-
const onAbort = () => controller.abort();
|
|
97807
|
-
if (opts.signal) {
|
|
97808
|
-
if (opts.signal.aborted)
|
|
97809
|
-
controller.abort();
|
|
97810
|
-
else
|
|
97811
|
-
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
97812
|
-
}
|
|
97813
|
-
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? timeoutMs);
|
|
97814
|
-
init.signal = controller.signal;
|
|
97815
|
-
let response;
|
|
97816
|
-
try {
|
|
97817
|
-
response = await fetchImpl(url2, init);
|
|
97818
|
-
} catch (error61) {
|
|
97819
|
-
const err = error61 instanceof Error ? error61 : new Error(String(error61));
|
|
97820
|
-
if (opts.signal?.aborted)
|
|
97821
|
-
return { ok: false, retryable: false, error: err };
|
|
97822
|
-
return { ok: false, retryable: true, error: err };
|
|
97823
|
-
} finally {
|
|
97824
|
-
clearTimeout(timer);
|
|
97825
|
-
if (opts.signal)
|
|
97826
|
-
opts.signal.removeEventListener("abort", onAbort);
|
|
97827
|
-
}
|
|
97828
|
-
const authenticationFailure = response.status === 401 || response.status === 403;
|
|
97829
|
-
let parsed = undefined;
|
|
97830
|
-
if (authenticationFailure) {
|
|
97831
|
-
try {
|
|
97832
|
-
await response.body?.cancel();
|
|
97833
|
-
} catch {}
|
|
97834
|
-
} else {
|
|
97835
|
-
const text5 = await response.text();
|
|
97836
|
-
if (text5.length > 0) {
|
|
97837
|
-
try {
|
|
97838
|
-
parsed = JSON.parse(text5);
|
|
97839
|
-
} catch {
|
|
97840
|
-
parsed = text5;
|
|
97841
|
-
}
|
|
97842
|
-
}
|
|
97843
|
-
}
|
|
97844
|
-
if (!response.ok) {
|
|
97845
|
-
if (response.status >= 300 && response.status < 400) {
|
|
97846
|
-
return {
|
|
97847
|
-
ok: false,
|
|
97848
|
-
retryable: false,
|
|
97849
|
-
error: new HasnaHttpError(method2, rel, response.status, parsed)
|
|
97850
|
-
};
|
|
97851
|
-
}
|
|
97852
|
-
if (authenticationFailure) {
|
|
97853
|
-
return {
|
|
97854
|
-
ok: false,
|
|
97855
|
-
retryable: false,
|
|
97856
|
-
error: new HasnaHttpError(method2, rel, response.status, undefined, {
|
|
97857
|
-
source: credential.source,
|
|
97858
|
-
tier: credential.tier,
|
|
97859
|
-
guidance: authFailureGuidance(credential)
|
|
97860
|
-
})
|
|
97861
|
-
};
|
|
97862
|
-
}
|
|
97863
|
-
const retry = resolveRetry(opts.retry);
|
|
97864
|
-
const retryable = retry ? retry.retryStatuses.includes(response.status) : false;
|
|
97865
|
-
return { ok: false, retryable, error: new HasnaHttpError(method2, rel, response.status, parsed) };
|
|
97866
|
-
}
|
|
97867
|
-
return { ok: true, value: parsed };
|
|
97868
|
-
}
|
|
97869
|
-
async function request(method2, path, body, opts = {}) {
|
|
97870
|
-
const upper = method2.toUpperCase();
|
|
97871
|
-
const rel = appendQuery(path.startsWith("/") ? path : `/${path}`, opts.query);
|
|
97872
|
-
const retry = resolveRetry(opts.retry);
|
|
97873
|
-
const methodRetryable = IDEMPOTENT_METHODS2.has(upper) || Boolean(opts.idempotencyKey);
|
|
97874
|
-
const maxAttempts = retry && methodRetryable ? retry.retries + 1 : 1;
|
|
97875
|
-
const binding = requestBindingProvider ? await requestBindingProvider() : {
|
|
97876
|
-
baseUrl: base2,
|
|
97877
|
-
credential: await resolveRequestCredential(options.name, options.apiKey)
|
|
97878
|
-
};
|
|
97879
|
-
const url2 = `${binding.baseUrl}${rel}`;
|
|
97880
|
-
const credential = binding.credential;
|
|
97881
|
-
let last2 = null;
|
|
97882
|
-
for (let attempt2 = 1;attempt2 <= maxAttempts; attempt2++) {
|
|
97883
|
-
const result2 = await once2(upper, rel, url2, body, opts, credential);
|
|
97884
|
-
if (result2.ok)
|
|
97885
|
-
return result2.value;
|
|
97886
|
-
last2 = result2;
|
|
97887
|
-
const canRetry = retry !== null && methodRetryable && result2.retryable && attempt2 < maxAttempts;
|
|
97888
|
-
if (!canRetry)
|
|
97889
|
-
break;
|
|
97890
|
-
const backoff = Math.min(retry.maxDelayMs, retry.baseDelayMs * 2 ** (attempt2 - 1));
|
|
97891
|
-
const jitter = Math.floor(Math.random() * (backoff / 2 + 1));
|
|
97892
|
-
await sleep(backoff + jitter);
|
|
97893
|
-
}
|
|
97894
|
-
throw last2.error;
|
|
97895
|
-
}
|
|
97896
|
-
return {
|
|
97897
|
-
baseUrl: base2,
|
|
97898
|
-
request,
|
|
97899
|
-
get: (path, opts) => request("GET", path, undefined, opts),
|
|
97900
|
-
post: (path, body, opts) => request("POST", path, body, opts),
|
|
97901
|
-
put: (path, body, opts) => request("PUT", path, body, opts),
|
|
97902
|
-
patch: (path, body, opts) => request("PATCH", path, body, opts),
|
|
97903
|
-
del: (path, body, opts) => request("DELETE", path, body, opts)
|
|
97904
|
-
};
|
|
97905
|
-
}
|
|
97906
|
-
function createHasnaHttpTransport(options) {
|
|
97907
|
-
return createHasnaHttpTransportInternal(options);
|
|
97908
|
-
}
|
|
97909
|
-
|
|
97910
|
-
class SecretsClient {
|
|
97911
|
-
transport;
|
|
97912
|
-
probeRoot;
|
|
97913
|
-
fetchImpl;
|
|
97914
|
-
baseHeaders;
|
|
97915
|
-
constructor(options) {
|
|
97916
|
-
if (!options.baseUrl)
|
|
97917
|
-
throw new Error("SecretsClient requires a baseUrl.");
|
|
97918
|
-
this.transport = createHasnaHttpTransport({
|
|
97919
|
-
name: "secrets",
|
|
97920
|
-
baseUrl: options.baseUrl.replace(/\/+$/, ""),
|
|
97921
|
-
apiKey: options.apiKey ?? "",
|
|
97922
|
-
...options.fetch ? { fetchImpl: (input2, init) => options.fetch(input2, init) } : {},
|
|
97923
|
-
...options.headers ? { headers: options.headers } : {}
|
|
97924
|
-
});
|
|
97925
|
-
this.probeRoot = this.transport.baseUrl.replace(/\/v1$/, "");
|
|
97926
|
-
this.fetchImpl = options.fetch ? (input2, init) => options.fetch(input2, init) : (input2, init) => fetch(input2, init);
|
|
97927
|
-
this.baseHeaders = options.headers ?? {};
|
|
97928
|
-
}
|
|
97929
|
-
get baseUrl() {
|
|
97930
|
-
return this.transport.baseUrl;
|
|
97931
|
-
}
|
|
97932
|
-
async probe(path, init) {
|
|
97933
|
-
const headers = { Accept: "application/json", ...this.baseHeaders };
|
|
97934
|
-
new Headers(init?.headers ?? {}).forEach((value, key2) => {
|
|
97935
|
-
headers[key2] = value;
|
|
97936
|
-
});
|
|
97937
|
-
const response = await this.fetchImpl(`${this.probeRoot}${path}`, {
|
|
97938
|
-
...init,
|
|
97939
|
-
method: "GET",
|
|
97940
|
-
headers,
|
|
97941
|
-
redirect: "manual"
|
|
97942
|
-
});
|
|
97943
|
-
const text5 = await response.text();
|
|
97944
|
-
let body = undefined;
|
|
97945
|
-
if (text5) {
|
|
97946
|
-
try {
|
|
97947
|
-
body = JSON.parse(text5);
|
|
97948
|
-
} catch {
|
|
97949
|
-
body = text5;
|
|
97950
|
-
}
|
|
97951
|
-
}
|
|
97952
|
-
if (!response.ok) {
|
|
97953
|
-
throw new ApiError(response.status, `GET ${path} failed: ${response.status}`, body);
|
|
97954
|
-
}
|
|
97955
|
-
return body;
|
|
97956
|
-
}
|
|
97957
|
-
async request(method2, path, opts) {
|
|
97958
|
-
const requestOpts = {};
|
|
97959
|
-
if (opts.query)
|
|
97960
|
-
requestOpts.query = opts.query;
|
|
97961
|
-
const initHeaders = opts.init?.headers;
|
|
97962
|
-
if (initHeaders)
|
|
97963
|
-
requestOpts.headers = initHeaders;
|
|
97964
|
-
if (opts.init?.signal)
|
|
97965
|
-
requestOpts.signal = opts.init.signal;
|
|
97966
|
-
try {
|
|
97967
|
-
return await this.transport.request(method2, path, opts.body, requestOpts);
|
|
97968
|
-
} catch (error61) {
|
|
97969
|
-
if (error61 instanceof HasnaHttpError) {
|
|
97970
|
-
throw new ApiError(error61.status, `${method2} ${path} failed: ${error61.status}`, error61.body);
|
|
97971
|
-
}
|
|
97972
|
-
throw error61;
|
|
97973
|
-
}
|
|
97974
|
-
}
|
|
97975
|
-
async health(init) {
|
|
97976
|
-
return this.probe(`/health`, init);
|
|
97977
|
-
}
|
|
97978
|
-
async ready(init) {
|
|
97979
|
-
return this.probe(`/ready`, init);
|
|
97980
|
-
}
|
|
97981
|
-
async listAudit(query, init) {
|
|
97982
|
-
return this.request("GET", `/audit`, {
|
|
97983
|
-
body: undefined,
|
|
97984
|
-
query,
|
|
97985
|
-
init
|
|
97986
|
-
});
|
|
97987
|
-
}
|
|
97988
|
-
async listItems(query, init) {
|
|
97989
|
-
return this.request("GET", `/items`, {
|
|
97990
|
-
body: undefined,
|
|
97991
|
-
query,
|
|
97992
|
-
init
|
|
97993
|
-
});
|
|
97994
|
-
}
|
|
97995
|
-
async putItem(body, init) {
|
|
97996
|
-
return this.request("POST", `/items`, {
|
|
97997
|
-
body,
|
|
97998
|
-
query: undefined,
|
|
97999
|
-
init
|
|
98000
|
-
});
|
|
98001
|
-
}
|
|
98002
|
-
async searchItems(query, init) {
|
|
98003
|
-
return this.request("GET", `/items/search`, {
|
|
98004
|
-
body: undefined,
|
|
98005
|
-
query,
|
|
98006
|
-
init
|
|
98007
|
-
});
|
|
98008
|
-
}
|
|
98009
|
-
async getItem(id, init) {
|
|
98010
|
-
return this.request("GET", `/items/${encodeURIComponent(String(id))}`, {
|
|
98011
|
-
body: undefined,
|
|
98012
|
-
query: undefined,
|
|
98013
|
-
init
|
|
98014
|
-
});
|
|
98015
|
-
}
|
|
98016
|
-
async deleteItem(id, init) {
|
|
98017
|
-
return this.request("DELETE", `/items/${encodeURIComponent(String(id))}`, {
|
|
98018
|
-
body: undefined,
|
|
98019
|
-
query: undefined,
|
|
98020
|
-
init
|
|
98021
|
-
});
|
|
98022
|
-
}
|
|
98023
|
-
async listSecrets(query, init) {
|
|
98024
|
-
return this.request("GET", `/secrets`, {
|
|
98025
|
-
body: undefined,
|
|
98026
|
-
query,
|
|
98027
|
-
init
|
|
98028
|
-
});
|
|
98029
|
-
}
|
|
98030
|
-
async putSecret(body, init) {
|
|
98031
|
-
return this.request("POST", `/secrets`, {
|
|
98032
|
-
body,
|
|
98033
|
-
query: undefined,
|
|
98034
|
-
init
|
|
98035
|
-
});
|
|
98036
|
-
}
|
|
98037
|
-
async deleteSecret(query, init) {
|
|
98038
|
-
return this.request("DELETE", `/secrets`, {
|
|
98039
|
-
body: undefined,
|
|
98040
|
-
query,
|
|
98041
|
-
init
|
|
98042
|
-
});
|
|
98043
|
-
}
|
|
98044
|
-
async getSecret(query, init) {
|
|
98045
|
-
return this.request("GET", `/secrets/get`, {
|
|
98046
|
-
body: undefined,
|
|
98047
|
-
query,
|
|
98048
|
-
init
|
|
98049
|
-
});
|
|
98050
|
-
}
|
|
98051
|
-
async searchSecrets(query, init) {
|
|
98052
|
-
return this.request("GET", `/secrets/search`, {
|
|
98053
|
-
body: undefined,
|
|
98054
|
-
query,
|
|
98055
|
-
init
|
|
98056
|
-
});
|
|
98057
|
-
}
|
|
98058
|
-
async listSecretVersions(query, init) {
|
|
98059
|
-
return this.request("GET", `/secrets/versions`, {
|
|
98060
|
-
body: undefined,
|
|
98061
|
-
query,
|
|
98062
|
-
init
|
|
98063
|
-
});
|
|
98064
|
-
}
|
|
98065
|
-
async checkSecretVersion(query, init) {
|
|
98066
|
-
return this.request("GET", `/secrets/versions/check`, {
|
|
98067
|
-
body: undefined,
|
|
98068
|
-
query,
|
|
98069
|
-
init
|
|
98070
|
-
});
|
|
98071
|
-
}
|
|
98072
|
-
async restoreSecretVersion(body, init) {
|
|
98073
|
-
return this.request("POST", `/secrets/restore`, {
|
|
98074
|
-
body,
|
|
98075
|
-
query: undefined,
|
|
98076
|
-
init
|
|
98077
|
-
});
|
|
98078
|
-
}
|
|
98079
|
-
async listUsers(query, init) {
|
|
98080
|
-
return this.request("GET", `/users`, {
|
|
98081
|
-
body: undefined,
|
|
98082
|
-
query,
|
|
98083
|
-
init
|
|
98084
|
-
});
|
|
98085
|
-
}
|
|
98086
|
-
async registerUser(body, init) {
|
|
98087
|
-
return this.request("POST", `/users`, {
|
|
98088
|
-
body,
|
|
98089
|
-
query: undefined,
|
|
98090
|
-
init
|
|
98091
|
-
});
|
|
98092
|
-
}
|
|
98093
|
-
async version(init) {
|
|
98094
|
-
return this.probe(`/version`, init);
|
|
98095
|
-
}
|
|
98096
|
-
}
|
|
98097
|
-
function createSecretsClientFromEnv(env3 = process.env, overrides = {}) {
|
|
98098
|
-
const { credentials, ...clientOverrides } = overrides;
|
|
98099
|
-
if (clientOverrides.baseUrl !== undefined) {
|
|
98100
|
-
const pinnedKey = clientOverrides.apiKey ?? credentials?.apiKey;
|
|
98101
|
-
if (pinnedKey === undefined || pinnedKey === "") {
|
|
98102
|
-
throw new Error("createSecretsClientFromEnv: an explicit baseUrl requires an explicit apiKey; the ambient " + "credential (Keychain hasna.credentials.secrets.api-key, ~/.hasna/secrets/config/credentials, " + "HASNA_SECRETS_API_KEY) is never sent to a caller-supplied authority (hasna/apps#1794).");
|
|
98103
|
-
}
|
|
98104
|
-
return new SecretsClient({ ...clientOverrides, baseUrl: clientOverrides.baseUrl, apiKey: pinnedKey });
|
|
98105
|
-
}
|
|
98106
|
-
const clientEnv = env3;
|
|
98107
|
-
const resolverOptions = credentials ? { credentials } : {};
|
|
98108
|
-
const resolution = resolveClientTransport("secrets", clientEnv, resolverOptions);
|
|
98109
|
-
const baseUrl = resolution.baseUrl;
|
|
98110
|
-
const apiKey = clientOverrides.apiKey ?? (() => {
|
|
98111
|
-
const resolved = resolveCredential2("secrets", clientEnv, credentials);
|
|
98112
|
-
if (!resolved) {
|
|
98113
|
-
throw new Error("createSecretsClientFromEnv: no @hasna/secrets credential resolved (Keychain, " + "~/.hasna/secrets/config/credentials, HASNA_SECRETS_API_KEY).");
|
|
98114
|
-
}
|
|
98115
|
-
return resolved;
|
|
98116
|
-
});
|
|
98117
|
-
return new SecretsClient({ ...clientOverrides, baseUrl, apiKey });
|
|
98118
|
-
}
|
|
98119
|
-
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), CREDENTIAL_PROFILE_ENV_KEY2 = "HASNA_PROFILE", CredentialResolutionError2, CredentialFileUnsafeError2, HASNA_HOME_ENV_KEY2 = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY2 = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY2 = "HASNA_STATION", HASNA_HOME_DIR2 = ".hasna", CONFIG_SUBDIR2 = "config", CREDENTIALS_FILE2 = "credentials", KEYCHAIN_SECURITY_BIN2 = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX2 = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS2 = 44, KEYCHAIN_SPAWN_TIMEOUT_MS2 = 1e4, MAX_CREDENTIAL_FILE_BYTES2, SAFE_APP_SLUG2, SAFE_PROFILE2, ILLEGAL_IN_HEADER_VALUE2, VAULT_POINTER_SHAPE2, CREDENTIAL_SHAPED_KEY2, INSPECT_CUSTOM2, CREDENTIAL_SEAL2, CALLER_SUPPLIED_CREDENTIAL_PROVIDER_SOURCE = "caller-supplied CredentialProvider", AMBIENT_ENVIRONMENT2, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk2, DEFAULT_FLEET_GATEWAY_ORIGIN2 = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN2, DNS_LABEL_PATTERN2, ClientTransportConfigurationError2, HasnaHttpError, DEFAULT_RETRY_STATUSES, IDEMPOTENT_METHODS2, AUTHORITY_OVERRIDE_HEADERS2, defaultSleep = (ms) => new Promise((resolve34) => setTimeout(resolve34, ms)), init_transport2, MAX_CREDENTIAL_FILE_BYTES22, INSPECT_CUSTOM22, CREDENTIAL_SEAL22, AMBIENT_ENVIRONMENT22, SECRETS_PACKAGE_SPECIFIER2, requireSecretsSdk22, IDEMPOTENT_METHODS22, AUTHORITY_OVERRIDE_HEADERS22, init_storage, PATHS_RESOLVER_APP_SLUG_RE3, SQLITE_SIDECAR_FILES, LEGACY_SERVICE_FILES, init_data_dir, SecretsTestIsolationError, TEST_ENTRYPOINT_RE, init_test_isolation, init_client, ApiError;
|
|
98120
|
-
var init_sdk = __esm(() => {
|
|
98121
|
-
init_transport2 = __esm2(() => {
|
|
98122
|
-
CredentialResolutionError2 = class CredentialResolutionError3 extends Error {
|
|
98123
|
-
appName;
|
|
98124
|
-
attempted;
|
|
98125
|
-
constructor(appName, message, attempted) {
|
|
98126
|
-
super(message);
|
|
98127
|
-
this.name = "CredentialResolutionError";
|
|
98128
|
-
this.appName = appName;
|
|
98129
|
-
this.attempted = attempted;
|
|
98130
|
-
}
|
|
98131
|
-
};
|
|
98132
|
-
CredentialFileUnsafeError2 = class CredentialFileUnsafeError3 extends Error {
|
|
98133
|
-
path;
|
|
98134
|
-
constructor(path, reason2) {
|
|
98135
|
-
super(`Refusing unsafe credential/config file ${path}: ${reason2}.`);
|
|
98136
|
-
this.name = "CredentialFileUnsafeError";
|
|
98137
|
-
this.path = path;
|
|
98138
|
-
}
|
|
98139
|
-
};
|
|
98140
|
-
MAX_CREDENTIAL_FILE_BYTES2 = 64 * 1024;
|
|
98141
|
-
SAFE_APP_SLUG2 = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
98142
|
-
SAFE_PROFILE2 = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
98143
|
-
ILLEGAL_IN_HEADER_VALUE2 = /[^\t\x20-\x7e]/;
|
|
98144
|
-
VAULT_POINTER_SHAPE2 = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-_.]*){2,}$/;
|
|
98145
|
-
CREDENTIAL_SHAPED_KEY2 = /(?:^|_)(?:API_KEY|KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)(?:_|$)/;
|
|
98146
|
-
INSPECT_CUSTOM2 = Symbol.for("nodejs.util.inspect.custom");
|
|
98147
|
-
CREDENTIAL_SEAL2 = Symbol.for("hasna:contracts:sealedCredential");
|
|
98148
|
-
AMBIENT_ENVIRONMENT2 = Symbol.for("hasna:contracts:ambientClientEnvironment");
|
|
98149
|
-
SECRETS_PACKAGE_SPECIFIER = "@hasna/" + "secrets";
|
|
98150
|
-
requireSecretsSdk2 = createRequire2(import.meta.url);
|
|
98151
|
-
ASCII_CONTROL_PATTERN2 = /[\u0000-\u001f\u007f]/;
|
|
98152
|
-
DNS_LABEL_PATTERN2 = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
98153
|
-
ClientTransportConfigurationError2 = class ClientTransportConfigurationError3 extends Error {
|
|
98154
|
-
appName;
|
|
98155
|
-
sources;
|
|
98156
|
-
constructor(appName, message, sources = []) {
|
|
98157
|
-
super(message);
|
|
98158
|
-
this.name = "ClientTransportConfigurationError";
|
|
98159
|
-
this.appName = appName;
|
|
98160
|
-
this.sources = Object.freeze([...sources]);
|
|
98161
|
-
}
|
|
98162
|
-
};
|
|
98163
|
-
HasnaHttpError = class HasnaHttpError2 extends Error {
|
|
98164
|
-
status;
|
|
98165
|
-
method;
|
|
98166
|
-
path;
|
|
98167
|
-
credentialSource;
|
|
98168
|
-
credentialTier;
|
|
98169
|
-
constructor(method2, path, status, body, credential) {
|
|
98170
|
-
const guidance = credential ? `. ${credential.guidance}` : "";
|
|
98171
|
-
super(`Hasna cloud request failed: ${method2} ${path} -> ${status}${guidance}`);
|
|
98172
|
-
this.name = "HasnaHttpError";
|
|
98173
|
-
this.status = status;
|
|
98174
|
-
this.method = method2;
|
|
98175
|
-
this.path = path;
|
|
98176
|
-
Object.defineProperty(this, "body", {
|
|
98177
|
-
value: body,
|
|
98178
|
-
enumerable: status !== 401 && status !== 403,
|
|
98179
|
-
writable: false,
|
|
98180
|
-
configurable: false
|
|
98181
|
-
});
|
|
98182
|
-
this.credentialSource = credential?.source ?? null;
|
|
98183
|
-
this.credentialTier = credential?.tier ?? null;
|
|
98184
|
-
}
|
|
98185
|
-
};
|
|
98186
|
-
DEFAULT_RETRY_STATUSES = [408, 425, 429, 500, 502, 503, 504];
|
|
98187
|
-
IDEMPOTENT_METHODS2 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
98188
|
-
AUTHORITY_OVERRIDE_HEADERS2 = new Set([
|
|
98189
|
-
"host",
|
|
98190
|
-
":authority",
|
|
98191
|
-
"forwarded",
|
|
98192
|
-
"x-forwarded-host",
|
|
98193
|
-
"x-original-host"
|
|
98194
|
-
]);
|
|
98195
|
-
});
|
|
98196
|
-
init_storage = __esm2(() => {
|
|
98197
|
-
MAX_CREDENTIAL_FILE_BYTES22 = 64 * 1024;
|
|
98198
|
-
INSPECT_CUSTOM22 = Symbol.for("nodejs.util.inspect.custom");
|
|
98199
|
-
CREDENTIAL_SEAL22 = Symbol.for("hasna:contracts:sealedCredential");
|
|
98200
|
-
AMBIENT_ENVIRONMENT22 = Symbol.for("hasna:contracts:ambientClientEnvironment");
|
|
98201
|
-
SECRETS_PACKAGE_SPECIFIER2 = "@hasna/" + "secrets";
|
|
98202
|
-
requireSecretsSdk22 = createRequire22(import.meta.url);
|
|
98203
|
-
IDEMPOTENT_METHODS22 = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
98204
|
-
AUTHORITY_OVERRIDE_HEADERS22 = new Set([
|
|
98205
|
-
"host",
|
|
98206
|
-
":authority",
|
|
98207
|
-
"forwarded",
|
|
98208
|
-
"x-forwarded-host",
|
|
98209
|
-
"x-original-host"
|
|
98210
|
-
]);
|
|
98211
|
-
});
|
|
98212
|
-
init_data_dir = __esm2(() => {
|
|
98213
|
-
PATHS_RESOLVER_APP_SLUG_RE3 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
98214
|
-
SQLITE_SIDECAR_FILES = ["vault.db-wal", "vault.db-shm", "vault.db-journal"];
|
|
98215
|
-
LEGACY_SERVICE_FILES = [
|
|
98216
|
-
"vault.key",
|
|
98217
|
-
"vault.key.enc",
|
|
98218
|
-
"kms.json",
|
|
98219
|
-
"aws.json",
|
|
98220
|
-
".serve-token"
|
|
98221
|
-
];
|
|
98222
|
-
});
|
|
98223
|
-
init_test_isolation = __esm2(() => {
|
|
98224
|
-
init_data_dir();
|
|
98225
|
-
SecretsTestIsolationError = class SecretsTestIsolationError2 extends Error {
|
|
98226
|
-
constructor(message) {
|
|
98227
|
-
super(message);
|
|
98228
|
-
this.name = "SecretsTestIsolationError";
|
|
98229
|
-
}
|
|
98230
|
-
};
|
|
98231
|
-
TEST_ENTRYPOINT_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
98232
|
-
});
|
|
98233
|
-
init_client = __esm2(() => {
|
|
98234
|
-
init_transport2();
|
|
98235
|
-
init_storage();
|
|
98236
|
-
init_test_isolation();
|
|
98237
|
-
init_transport2();
|
|
98238
|
-
});
|
|
98239
|
-
init_client();
|
|
98240
|
-
ApiError = class ApiError extends Error {
|
|
98241
|
-
status;
|
|
98242
|
-
body;
|
|
98243
|
-
constructor(status, message, body) {
|
|
98244
|
-
super(message);
|
|
98245
|
-
this.status = status;
|
|
98246
|
-
this.body = body;
|
|
98247
|
-
this.name = "ApiError";
|
|
98248
|
-
}
|
|
98249
|
-
};
|
|
98250
|
-
init_client();
|
|
98251
|
-
});
|
|
98252
|
-
|
|
98253
|
-
// src/lib/execution-secrets.ts
|
|
98254
|
-
import { constants as constants26, closeSync as closeSync27, fstatSync as fstatSync27, openSync as openSync27, readSync as readSync20, realpathSync as realpathSync16 } from "fs";
|
|
98255
|
-
import { hostname as stationHostname } from "os";
|
|
98256
|
-
function selectedSecretBindingsTemplate(scope, names, createClient2 = createSecretsClientFromEnv) {
|
|
96969
|
+
function selectedSecretBindingsTemplate(scope, names, createClient2) {
|
|
98257
96970
|
let secretsAuthority;
|
|
98258
96971
|
try {
|
|
98259
96972
|
secretsAuthority = createClient2().baseUrl;
|
|
@@ -98295,8 +97008,8 @@ function declaredSecretNames(value) {
|
|
|
98295
97008
|
function readSelectedSecretBindings(path) {
|
|
98296
97009
|
let fd;
|
|
98297
97010
|
try {
|
|
98298
|
-
fd =
|
|
98299
|
-
const stat2 =
|
|
97011
|
+
fd = openSync26(path, constants26.O_RDONLY | constants26.O_NOFOLLOW | constants26.O_NONBLOCK);
|
|
97012
|
+
const stat2 = fstatSync26(fd);
|
|
98300
97013
|
if (!stat2.isFile() || stat2.size > 65536)
|
|
98301
97014
|
throw new Error;
|
|
98302
97015
|
const bytes = Buffer.alloc(65537);
|
|
@@ -98311,7 +97024,7 @@ function readSelectedSecretBindings(path) {
|
|
|
98311
97024
|
return refuse6("INVALID_SECRET_BINDINGS", "Secret bindings must be a regular JSON file of at most 64 KiB, containing reference names only.");
|
|
98312
97025
|
} finally {
|
|
98313
97026
|
if (fd !== undefined)
|
|
98314
|
-
|
|
97027
|
+
closeSync26(fd);
|
|
98315
97028
|
}
|
|
98316
97029
|
}
|
|
98317
97030
|
function validateSelectedSecretBindings(value, scope, names) {
|
|
@@ -98345,12 +97058,13 @@ function validateSelectedSecretBindings(value, scope, names) {
|
|
|
98345
97058
|
}
|
|
98346
97059
|
return structuredClone(value);
|
|
98347
97060
|
}
|
|
98348
|
-
async function resolveSelectedSecrets(bindings, createClient2
|
|
97061
|
+
async function resolveSelectedSecrets(bindings, createClient2) {
|
|
98349
97062
|
if (!Object.keys(bindings.bindings).length)
|
|
98350
97063
|
return {};
|
|
97064
|
+
const factory = createClient2 ?? await defaultSecretsClientFactory();
|
|
98351
97065
|
let client2;
|
|
98352
97066
|
try {
|
|
98353
|
-
client2 =
|
|
97067
|
+
client2 = factory();
|
|
98354
97068
|
} catch {
|
|
98355
97069
|
return refuse6("LOCAL_SECRET_UNAVAILABLE", "The configured Secrets client could not authenticate. No ambient credential fallback was attempted.");
|
|
98356
97070
|
}
|
|
@@ -98383,7 +97097,6 @@ function redactExecutionSecrets(text5, env3) {
|
|
|
98383
97097
|
return text5;
|
|
98384
97098
|
}
|
|
98385
97099
|
var init_execution_secrets = __esm(() => {
|
|
98386
|
-
init_sdk();
|
|
98387
97100
|
init_selection_cache();
|
|
98388
97101
|
});
|
|
98389
97102
|
|
|
@@ -98607,7 +97320,7 @@ __export(exports_selected_run, {
|
|
|
98607
97320
|
import { createHash as createHash41 } from "crypto";
|
|
98608
97321
|
import { spawn as spawn4 } from "child_process";
|
|
98609
97322
|
import { mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync5, writeFileSync as writeFileSync18, realpathSync as realpathSync17 } from "fs";
|
|
98610
|
-
import { dirname as dirname37, join as
|
|
97323
|
+
import { dirname as dirname37, join as join60, resolve as resolve34 } from "path";
|
|
98611
97324
|
import { tmpdir, hostname as stationHostname2 } from "os";
|
|
98612
97325
|
async function resolveSelectedRun(spec, profileId, options = {}) {
|
|
98613
97326
|
const context = await resolveSelectionContext(profileId, options);
|
|
@@ -98664,7 +97377,7 @@ async function prepareSelectedSecretBindings(selected, cwd2 = process.cwd()) {
|
|
|
98664
97377
|
const names = declaredSecretNames(manifest.runtime?.env);
|
|
98665
97378
|
if (!names.length)
|
|
98666
97379
|
throw new SkillSelectionError("NO_DECLARED_SECRETS", "This selected executable does not declare any environment references.");
|
|
98667
|
-
return selectedSecretBindingsTemplate({ selection: selected.selection, profileId: selected.profileId, cwd: cwd2 }, names);
|
|
97380
|
+
return selectedSecretBindingsTemplate({ selection: selected.selection, profileId: selected.profileId, cwd: cwd2 }, names, await defaultSecretsClientFactory());
|
|
98668
97381
|
}
|
|
98669
97382
|
async function executeSelectedLocal(selected, options = {}) {
|
|
98670
97383
|
const selection2 = structuredClone(selected.selection), profileId = selected.profileId;
|
|
@@ -98730,7 +97443,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
98730
97443
|
throw new SkillSelectionError("INVALID_SECRET_BINDINGS", "Explicit environment values must be bounded strings without NUL bytes.");
|
|
98731
97444
|
secretEnv[name] = value;
|
|
98732
97445
|
}
|
|
98733
|
-
const runDirectory = mkdtempSync5(
|
|
97446
|
+
const runDirectory = mkdtempSync5(join60(tmpdir(), "skills-execution-"));
|
|
98734
97447
|
const paths = new SkillEntryPaths;
|
|
98735
97448
|
for (const entry of entries) {
|
|
98736
97449
|
paths.add(entry.path, 100, () => {
|
|
@@ -98738,7 +97451,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
98738
97451
|
}, () => {
|
|
98739
97452
|
throw new SkillSelectionError("INVALID_BUNDLE_PATH", "The execution bundle path exceeds its limit.");
|
|
98740
97453
|
});
|
|
98741
|
-
const destination =
|
|
97454
|
+
const destination = join60(runDirectory, entry.path);
|
|
98742
97455
|
mkdirSync19(dirname37(destination), { recursive: true, mode: 448 });
|
|
98743
97456
|
writeFileSync18(destination, entry.bytes, { flag: "wx", mode: entry.mode & 511 });
|
|
98744
97457
|
}
|
|
@@ -98766,7 +97479,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
98766
97479
|
}
|
|
98767
97480
|
if (bindings)
|
|
98768
97481
|
secretEnv = await resolveSelectedSecrets(bindings, options.createSecretsClient);
|
|
98769
|
-
const result2 = await subprocess([executable2, ...runtimeName === "bun" ? ["--no-env-file"] : [],
|
|
97482
|
+
const result2 = await subprocess([executable2, ...runtimeName === "bun" ? ["--no-env-file"] : [], join60(runDirectory, declaredEntry), ...args], runDirectory, { ...env3, ...secretEnv }, input2, timeoutMs);
|
|
98770
97483
|
result2.stdout = redactExecutionSecrets(result2.stdout, secretEnv);
|
|
98771
97484
|
result2.stderr = redactExecutionSecrets(result2.stderr, secretEnv);
|
|
98772
97485
|
const receipt = {
|
|
@@ -98780,7 +97493,7 @@ async function executeSelectedLocal(selected, options = {}) {
|
|
|
98780
97493
|
...bindings ? { secretBinding: { schema: bindings.schema, selection: bindings.selection, consumer: bindings.consumer, secretsAuthority: bindings.secretsAuthority, bindings: bindings.bindings } } : {},
|
|
98781
97494
|
...result2
|
|
98782
97495
|
};
|
|
98783
|
-
writeFileSync18(
|
|
97496
|
+
writeFileSync18(join60(runDirectory, ".execution-receipt.json"), JSON.stringify({ ...receipt, stdout: undefined, stderr: undefined }), { mode: 384 });
|
|
98784
97497
|
return receipt;
|
|
98785
97498
|
}
|
|
98786
97499
|
async function subprocess(command, cwd2, env3, input2, timeoutMs) {
|
|
@@ -102992,20 +101705,20 @@ var init_runs = __esm(() => {
|
|
|
102992
101705
|
|
|
102993
101706
|
// src/lib/run-state.ts
|
|
102994
101707
|
import { createHash as createHash42, randomBytes } from "crypto";
|
|
102995
|
-
import { existsSync as existsSync28, mkdirSync as mkdirSync20, readFileSync as
|
|
102996
|
-
import { extname, join as
|
|
101708
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync20, readFileSync as readFileSync38, readdirSync as readdirSync23, statSync as statSync19, writeFileSync as writeFileSync19 } from "fs";
|
|
101709
|
+
import { extname, join as join61, relative as relative11 } from "path";
|
|
102997
101710
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
102998
101711
|
const now3 = new Date;
|
|
102999
101712
|
const id2 = createRunId(now3);
|
|
103000
101713
|
const day = now3.toISOString().slice(0, 10);
|
|
103001
101714
|
const skillName = normalizeSkillName(params.skill);
|
|
103002
101715
|
const root2 = getProjectStateDir(targetDir);
|
|
103003
|
-
const runDir =
|
|
103004
|
-
const logsDir =
|
|
103005
|
-
const exportDir =
|
|
101716
|
+
const runDir = join61(root2, "runs", day, id2);
|
|
101717
|
+
const logsDir = join61(runDir, "logs");
|
|
101718
|
+
const exportDir = join61(root2, "exports", skillName, id2);
|
|
103006
101719
|
mkdirSync20(logsDir, { recursive: true });
|
|
103007
101720
|
mkdirSync20(exportDir, { recursive: true });
|
|
103008
|
-
mkdirSync20(
|
|
101721
|
+
mkdirSync20(join61(root2, "tmp"), { recursive: true });
|
|
103009
101722
|
const record13 = {
|
|
103010
101723
|
id: id2,
|
|
103011
101724
|
skill: skillName,
|
|
@@ -103057,27 +101770,27 @@ function updateSkillRun(context, patch) {
|
|
|
103057
101770
|
return context.record;
|
|
103058
101771
|
}
|
|
103059
101772
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
103060
|
-
writeFileSync19(
|
|
103061
|
-
writeFileSync19(
|
|
101773
|
+
writeFileSync19(join61(context.logsDir, "stdout.log"), stdout);
|
|
101774
|
+
writeFileSync19(join61(context.logsDir, "stderr.log"), stderr);
|
|
103062
101775
|
}
|
|
103063
101776
|
function appendRunEvent(context, event, data = {}) {
|
|
103064
101777
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
103065
101778
|
`;
|
|
103066
|
-
const path =
|
|
103067
|
-
const previous = existsSync28(path) ?
|
|
101779
|
+
const path = join61(context.runDir, "events.ndjson");
|
|
101780
|
+
const previous = existsSync28(path) ? readFileSync38(path, "utf-8") : "";
|
|
103068
101781
|
writeFileSync19(path, previous + line);
|
|
103069
101782
|
}
|
|
103070
101783
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
103071
|
-
const runsRoot =
|
|
101784
|
+
const runsRoot = join61(getProjectStateDir(targetDir), "runs");
|
|
103072
101785
|
if (!existsSync28(runsRoot))
|
|
103073
101786
|
return [];
|
|
103074
101787
|
const records = [];
|
|
103075
101788
|
for (const day of readdirSync23(runsRoot).sort().reverse()) {
|
|
103076
|
-
const dayDir =
|
|
101789
|
+
const dayDir = join61(runsRoot, day);
|
|
103077
101790
|
if (!statSync19(dayDir).isDirectory())
|
|
103078
101791
|
continue;
|
|
103079
101792
|
for (const runId2 of readdirSync23(dayDir).sort().reverse()) {
|
|
103080
|
-
const record13 = readRunRecord(
|
|
101793
|
+
const record13 = readRunRecord(join61(dayDir, runId2));
|
|
103081
101794
|
if (record13)
|
|
103082
101795
|
records.push(record13);
|
|
103083
101796
|
if (records.length >= limit)
|
|
@@ -103087,11 +101800,11 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
103087
101800
|
return records;
|
|
103088
101801
|
}
|
|
103089
101802
|
function findSkillRun(runId2, targetDir = process.cwd()) {
|
|
103090
|
-
const runsRoot =
|
|
101803
|
+
const runsRoot = join61(getProjectStateDir(targetDir), "runs");
|
|
103091
101804
|
if (!existsSync28(runsRoot))
|
|
103092
101805
|
return null;
|
|
103093
101806
|
for (const day of readdirSync23(runsRoot)) {
|
|
103094
|
-
const record13 = readRunRecord(
|
|
101807
|
+
const record13 = readRunRecord(join61(runsRoot, day, runId2));
|
|
103095
101808
|
if (record13)
|
|
103096
101809
|
return record13;
|
|
103097
101810
|
}
|
|
@@ -103108,14 +101821,14 @@ function skillRunEnv(context) {
|
|
|
103108
101821
|
};
|
|
103109
101822
|
}
|
|
103110
101823
|
function getRunExportDir(runId2, skill, targetDir = process.cwd()) {
|
|
103111
|
-
return
|
|
101824
|
+
return join61(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId2);
|
|
103112
101825
|
}
|
|
103113
101826
|
function writeRunRecord(context) {
|
|
103114
|
-
writeFileSync19(
|
|
101827
|
+
writeFileSync19(join61(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
103115
101828
|
`);
|
|
103116
101829
|
}
|
|
103117
101830
|
function writeArtifactsManifest(context, artifacts) {
|
|
103118
|
-
writeFileSync19(
|
|
101831
|
+
writeFileSync19(join61(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
103119
101832
|
`);
|
|
103120
101833
|
}
|
|
103121
101834
|
function collectRunArtifacts(context) {
|
|
@@ -103124,7 +101837,7 @@ function collectRunArtifacts(context) {
|
|
|
103124
101837
|
const artifacts = [];
|
|
103125
101838
|
for (const path of walkFiles(context.exportDir)) {
|
|
103126
101839
|
const stat2 = statSync19(path);
|
|
103127
|
-
const bytes =
|
|
101840
|
+
const bytes = readFileSync38(path);
|
|
103128
101841
|
artifacts.push({
|
|
103129
101842
|
path: toProjectRelative(context.targetDir, path),
|
|
103130
101843
|
mime: mimeForPath(path),
|
|
@@ -103135,11 +101848,11 @@ function collectRunArtifacts(context) {
|
|
|
103135
101848
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
103136
101849
|
}
|
|
103137
101850
|
function readRunRecord(runDir) {
|
|
103138
|
-
const path =
|
|
101851
|
+
const path = join61(runDir, "run.json");
|
|
103139
101852
|
if (!existsSync28(path))
|
|
103140
101853
|
return null;
|
|
103141
101854
|
try {
|
|
103142
|
-
return JSON.parse(
|
|
101855
|
+
return JSON.parse(readFileSync38(path, "utf-8"));
|
|
103143
101856
|
} catch {
|
|
103144
101857
|
return null;
|
|
103145
101858
|
}
|
|
@@ -103147,7 +101860,7 @@ function readRunRecord(runDir) {
|
|
|
103147
101860
|
function walkFiles(dir) {
|
|
103148
101861
|
const files = [];
|
|
103149
101862
|
for (const entry of readdirSync23(dir)) {
|
|
103150
|
-
const full =
|
|
101863
|
+
const full = join61(dir, entry);
|
|
103151
101864
|
if (statSync19(full).isDirectory())
|
|
103152
101865
|
files.push(...walkFiles(full));
|
|
103153
101866
|
else
|
|
@@ -103197,7 +101910,7 @@ var init_run_state = __esm(() => {
|
|
|
103197
101910
|
});
|
|
103198
101911
|
|
|
103199
101912
|
// src/lib/mcp-registration-environment.ts
|
|
103200
|
-
import { isAbsolute as
|
|
101913
|
+
import { isAbsolute as isAbsolute34 } from "path";
|
|
103201
101914
|
function skillsMcpRegistrationEnvironment(env3 = process.env, origin) {
|
|
103202
101915
|
const result2 = {};
|
|
103203
101916
|
const profile = selectedSkillsProfile(env3);
|
|
@@ -103207,7 +101920,7 @@ function skillsMcpRegistrationEnvironment(env3 = process.env, origin) {
|
|
|
103207
101920
|
const value = env3[key2];
|
|
103208
101921
|
if (value === undefined)
|
|
103209
101922
|
continue;
|
|
103210
|
-
if (!
|
|
101923
|
+
if (!isAbsolute34(value) || /[\x00-\x1f\x7f]/.test(value))
|
|
103211
101924
|
throw new Error(`Invalid ${key2}; expected an absolute configuration path`);
|
|
103212
101925
|
result2[key2] = value;
|
|
103213
101926
|
}
|
|
@@ -103237,7 +101950,7 @@ var init_mcp_registration_environment = __esm(() => {
|
|
|
103237
101950
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
103238
101951
|
import fs2 from "fs";
|
|
103239
101952
|
import { homedir as homedir10 } from "os";
|
|
103240
|
-
import { dirname as dirname38, isAbsolute as
|
|
101953
|
+
import { dirname as dirname38, isAbsolute as isAbsolute35, join as join62, relative as relative12, resolve as resolve35, sep as sep18 } from "path";
|
|
103241
101954
|
function guardedAgentConfigRefusal(error61) {
|
|
103242
101955
|
return error61 instanceof ConfigWriteRefusal ? error61.message : "configuration update was refused";
|
|
103243
101956
|
}
|
|
@@ -103276,7 +101989,7 @@ function unchanged3(before2, current) {
|
|
|
103276
101989
|
function parentWitnesses(path) {
|
|
103277
101990
|
const home = canonicalSystemPath(homedir10()), local = relative12(home, path);
|
|
103278
101991
|
const creationParent = dirname38(path);
|
|
103279
|
-
if (!
|
|
101992
|
+
if (!isAbsolute35(path) || resolve35(path) !== path || !local || local === ".." || local.startsWith(".." + sep18))
|
|
103280
101993
|
throw new Error;
|
|
103281
101994
|
const parents3 = [];
|
|
103282
101995
|
for (let ancestor = home;; ancestor = dirname38(ancestor)) {
|
|
@@ -103285,7 +101998,7 @@ function parentWitnesses(path) {
|
|
|
103285
101998
|
break;
|
|
103286
101999
|
}
|
|
103287
102000
|
for (const part of relative12(home, dirname38(path)).split(sep18).filter(Boolean))
|
|
103288
|
-
parents3.push(
|
|
102001
|
+
parents3.push(join62(parents3[parents3.length - 1], part));
|
|
103289
102002
|
return parents3.map((path2) => {
|
|
103290
102003
|
if (!statOrMissing(path2)) {
|
|
103291
102004
|
if (!path2.startsWith(home + sep18))
|
|
@@ -103345,7 +102058,7 @@ function guardedAgentConfigWrite(path, transform3) {
|
|
|
103345
102058
|
if (typeof process.getuid !== "function")
|
|
103346
102059
|
throw new Error;
|
|
103347
102060
|
refusal3 = "UNSAFE_PATH";
|
|
103348
|
-
if (!
|
|
102061
|
+
if (!isAbsolute35(path) || resolve35(path) !== path)
|
|
103349
102062
|
throw new Error;
|
|
103350
102063
|
path = canonicalSystemPath(path);
|
|
103351
102064
|
const parents3 = parentWitnesses(path);
|
|
@@ -110186,8 +108899,8 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
110186
108899
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
110187
108900
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
110188
108901
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
110189
|
-
function reescapeHostDelimiters(host,
|
|
110190
|
-
const re =
|
|
108902
|
+
function reescapeHostDelimiters(host, isIP3) {
|
|
108903
|
+
const re = isIP3 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
110191
108904
|
re.lastIndex = 0;
|
|
110192
108905
|
return host.replace(re, (ch) => HOST_DELIMS[ch]);
|
|
110193
108906
|
}
|
|
@@ -110837,8 +109550,8 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
110837
109550
|
const host = matches2[4];
|
|
110838
109551
|
return hasMalformedPercentEncoding(matches2[3]) || host !== undefined && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches2[6]) || hasMalformedPercentEncoding(matches2[7]) || hasMalformedPercentEncoding(matches2[8]);
|
|
110839
109552
|
}
|
|
110840
|
-
function canonicalizeHost(parsed, options, schemeHandler,
|
|
110841
|
-
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) &&
|
|
109553
|
+
function canonicalizeHost(parsed, options, schemeHandler, isIP3) {
|
|
109554
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP3 === false && nonSimpleDomain(parsed.host)) {
|
|
110842
109555
|
try {
|
|
110843
109556
|
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
110844
109557
|
} catch (e2) {
|
|
@@ -110865,7 +109578,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
110865
109578
|
let malformedHost = false;
|
|
110866
109579
|
let malformedIPLiteral = false;
|
|
110867
109580
|
let malformedScheme = false;
|
|
110868
|
-
let
|
|
109581
|
+
let isIP3 = false;
|
|
110869
109582
|
if (options.reference === "suffix") {
|
|
110870
109583
|
if (options.scheme) {
|
|
110871
109584
|
uri = options.scheme + ":" + uri;
|
|
@@ -110928,15 +109641,15 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
110928
109641
|
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
110929
109642
|
const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
|
|
110930
109643
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
110931
|
-
|
|
109644
|
+
isIP3 = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
110932
109645
|
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
110933
|
-
parsed.host =
|
|
109646
|
+
parsed.host = isIP3 ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
110934
109647
|
if (malformedIPLiteral) {
|
|
110935
109648
|
parsed.error = parsed.error || "URI host is malformed.";
|
|
110936
109649
|
malformedAuthorityOrPort = true;
|
|
110937
109650
|
}
|
|
110938
109651
|
} else {
|
|
110939
|
-
|
|
109652
|
+
isIP3 = true;
|
|
110940
109653
|
}
|
|
110941
109654
|
}
|
|
110942
109655
|
if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && parsed.query === undefined && !parsed.path) {
|
|
@@ -110953,13 +109666,13 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
110953
109666
|
}
|
|
110954
109667
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
110955
109668
|
if (!malformedIPLiteral) {
|
|
110956
|
-
malformedHost = canonicalizeHost(parsed, options, schemeHandler,
|
|
109669
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP3);
|
|
110957
109670
|
}
|
|
110958
109671
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
110959
109672
|
if (uri.indexOf("%") !== -1) {
|
|
110960
109673
|
if (parsed.host !== undefined && !malformedIPLiteral) {
|
|
110961
|
-
const host =
|
|
110962
|
-
parsed.host = reescapeHostDelimiters(host,
|
|
109674
|
+
const host = isIP3 ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
109675
|
+
parsed.host = reescapeHostDelimiters(host, isIP3);
|
|
110963
109676
|
}
|
|
110964
109677
|
}
|
|
110965
109678
|
if (parsed.path) {
|
|
@@ -115388,8 +114101,8 @@ var init_remote_customer_operations = __esm(() => {
|
|
|
115388
114101
|
});
|
|
115389
114102
|
|
|
115390
114103
|
// src/lib/workspace-profile.ts
|
|
115391
|
-
import { constants as constants27, closeSync as
|
|
115392
|
-
import { dirname as dirname39, join as
|
|
114104
|
+
import { constants as constants27, closeSync as closeSync27, fstatSync as fstatSync27, lstatSync as lstatSync40, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync6, openSync as openSync27, readFileSync as readFileSync39, renameSync as renameSync13, rmSync as rmSync9, writeFileSync as writeFileSync20 } from "fs";
|
|
114105
|
+
import { dirname as dirname39, join as join63 } from "path";
|
|
115393
114106
|
function stat2(path) {
|
|
115394
114107
|
try {
|
|
115395
114108
|
return lstatSync40(path);
|
|
@@ -115402,14 +114115,14 @@ function stat2(path) {
|
|
|
115402
114115
|
function safeText(file2) {
|
|
115403
114116
|
if (stat2(file2) === null)
|
|
115404
114117
|
return null;
|
|
115405
|
-
const fd =
|
|
114118
|
+
const fd = openSync27(file2, constants27.O_RDONLY | constants27.O_NOFOLLOW | constants27.O_NONBLOCK);
|
|
115406
114119
|
try {
|
|
115407
|
-
const s =
|
|
114120
|
+
const s = fstatSync27(fd);
|
|
115408
114121
|
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
115409
114122
|
return fail4("The selected profile must use bounded owner-only regular files.");
|
|
115410
|
-
return
|
|
114123
|
+
return readFileSync39(fd, "utf8");
|
|
115411
114124
|
} finally {
|
|
115412
|
-
|
|
114125
|
+
closeSync27(fd);
|
|
115413
114126
|
}
|
|
115414
114127
|
}
|
|
115415
114128
|
function checkIdentityMetadata(file2, identity6) {
|
|
@@ -115541,11 +114254,11 @@ async function prepareWorkspaceEnrollment(membershipId, source = process.env) {
|
|
|
115541
114254
|
const identity6 = JSON.stringify({ email: verified.user.email, userId: verified.user.id, orgId: verified.organization.id, orgSlug: verified.organization.slug, apiUrl: origin, issuedBy: "sign-in" }, null, 2) + `
|
|
115542
114255
|
`;
|
|
115543
114256
|
mkdirSync21(dirname39(file2), { recursive: true, mode: 448 });
|
|
115544
|
-
const temp = mkdtempSync6(
|
|
114257
|
+
const temp = mkdtempSync6(join63(dirname39(file2), ".workspace-login-"));
|
|
115545
114258
|
let identityInstalled = false;
|
|
115546
114259
|
try {
|
|
115547
|
-
writeFileSync20(
|
|
115548
|
-
writeFileSync20(
|
|
114260
|
+
writeFileSync20(join63(temp, "credentials"), body, { mode: 384, flag: "wx" });
|
|
114261
|
+
writeFileSync20(join63(temp, "identity"), identity6, { mode: 384, flag: "wx" });
|
|
115549
114262
|
unchangedFiles();
|
|
115550
114263
|
for (const [path, before2] of parents3) {
|
|
115551
114264
|
const now3 = stat2(path);
|
|
@@ -115554,16 +114267,16 @@ async function prepareWorkspaceEnrollment(membershipId, source = process.env) {
|
|
|
115554
114267
|
if (!before2 && now3 && ((now3.mode & 63) !== 0 || process.getuid && now3.uid !== process.getuid()))
|
|
115555
114268
|
changed();
|
|
115556
114269
|
}
|
|
115557
|
-
renameSync13(
|
|
114270
|
+
renameSync13(join63(temp, "identity"), identityFile);
|
|
115558
114271
|
identityInstalled = true;
|
|
115559
|
-
renameSync13(
|
|
114272
|
+
renameSync13(join63(temp, "credentials"), file2);
|
|
115560
114273
|
} catch (error61) {
|
|
115561
114274
|
if (identityInstalled) {
|
|
115562
114275
|
if (oldIdentity === null)
|
|
115563
114276
|
rmSync9(identityFile, { force: true });
|
|
115564
114277
|
else {
|
|
115565
|
-
writeFileSync20(
|
|
115566
|
-
renameSync13(
|
|
114278
|
+
writeFileSync20(join63(temp, "restore"), oldIdentity, { mode: 384, flag: "wx" });
|
|
114279
|
+
renameSync13(join63(temp, "restore"), identityFile);
|
|
115567
114280
|
}
|
|
115568
114281
|
}
|
|
115569
114282
|
throw error61;
|
|
@@ -115757,8 +114470,8 @@ var init_recurring_customer = __esm(() => {
|
|
|
115757
114470
|
});
|
|
115758
114471
|
|
|
115759
114472
|
// src/lib/recurring-recovery.ts
|
|
115760
|
-
import { constants as constants28, closeSync as
|
|
115761
|
-
import { dirname as dirname40, isAbsolute as
|
|
114473
|
+
import { constants as constants28, closeSync as closeSync28, fsyncSync as fsyncSync8, fstatSync as fstatSync28, lstatSync as lstatSync41, mkdirSync as mkdirSync22, openSync as openSync28, readSync as readSync21, realpathSync as realpathSync18, renameSync as renameSync14, unlinkSync as unlinkSync10, writeFileSync as writeFileSync21 } from "fs";
|
|
114474
|
+
import { dirname as dirname40, isAbsolute as isAbsolute36, join as join64, resolve as resolve36 } from "path";
|
|
115762
114475
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
115763
114476
|
|
|
115764
114477
|
class RecoveryOwner {
|
|
@@ -115766,7 +114479,7 @@ class RecoveryOwner {
|
|
|
115766
114479
|
parents = [];
|
|
115767
114480
|
constructor(directory2) {
|
|
115768
114481
|
this.directory = directory2;
|
|
115769
|
-
if (!
|
|
114482
|
+
if (!isAbsolute36(directory2) || resolve36(directory2) !== directory2 || realpathSync18(directory2) !== directory2)
|
|
115770
114483
|
invalid6();
|
|
115771
114484
|
for (let path = directory2;; path = dirname40(path)) {
|
|
115772
114485
|
const stat3 = lstatSync41(path);
|
|
@@ -115792,9 +114505,9 @@ class RecoveryOwner {
|
|
|
115792
114505
|
}
|
|
115793
114506
|
read(name) {
|
|
115794
114507
|
this.assert();
|
|
115795
|
-
const fd =
|
|
114508
|
+
const fd = openSync28(join64(this.directory, name), constants28.O_RDONLY | constants28.O_NOFOLLOW | constants28.O_NONBLOCK);
|
|
115796
114509
|
try {
|
|
115797
|
-
const before2 =
|
|
114510
|
+
const before2 = fstatSync28(fd);
|
|
115798
114511
|
if (!before2.isFile() || before2.nlink !== 1 || (before2.mode & 4095) !== 384 || before2.size < 1 || before2.size > 8192 || process.getuid && before2.uid !== process.getuid())
|
|
115799
114512
|
invalid6();
|
|
115800
114513
|
const bytes = Buffer.alloc(before2.size + 1);
|
|
@@ -115805,36 +114518,36 @@ class RecoveryOwner {
|
|
|
115805
114518
|
break;
|
|
115806
114519
|
size2 += n2;
|
|
115807
114520
|
}
|
|
115808
|
-
const after2 =
|
|
114521
|
+
const after2 = fstatSync28(fd), path = lstatSync41(join64(this.directory, name));
|
|
115809
114522
|
if (size2 !== before2.size || after2.size !== before2.size || before2.mtimeMs !== after2.mtimeMs || before2.ctimeMs !== after2.ctimeMs || path.dev !== before2.dev || path.ino !== before2.ino || !path.isFile() || path.isSymbolicLink())
|
|
115810
114523
|
invalid6();
|
|
115811
114524
|
this.assert();
|
|
115812
114525
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size2));
|
|
115813
114526
|
} finally {
|
|
115814
|
-
|
|
114527
|
+
closeSync28(fd);
|
|
115815
114528
|
}
|
|
115816
114529
|
}
|
|
115817
114530
|
write(name, value) {
|
|
115818
114531
|
this.assert();
|
|
115819
114532
|
const bytes = canonicalJson2(value, 8192) + `
|
|
115820
114533
|
`;
|
|
115821
|
-
const fd =
|
|
114534
|
+
const fd = openSync28(join64(this.directory, name), constants28.O_WRONLY | constants28.O_CREAT | constants28.O_EXCL | constants28.O_NOFOLLOW, 384);
|
|
115822
114535
|
try {
|
|
115823
114536
|
writeFileSync21(fd, bytes);
|
|
115824
114537
|
fsyncSync8(fd);
|
|
115825
114538
|
} finally {
|
|
115826
|
-
|
|
114539
|
+
closeSync28(fd);
|
|
115827
114540
|
}
|
|
115828
114541
|
this.assert();
|
|
115829
114542
|
this.sync();
|
|
115830
114543
|
}
|
|
115831
114544
|
sync() {
|
|
115832
114545
|
this.assert();
|
|
115833
|
-
const fd =
|
|
114546
|
+
const fd = openSync28(this.directory, constants28.O_RDONLY | constants28.O_NOFOLLOW);
|
|
115834
114547
|
try {
|
|
115835
114548
|
fsyncSync8(fd);
|
|
115836
114549
|
} finally {
|
|
115837
|
-
|
|
114550
|
+
closeSync28(fd);
|
|
115838
114551
|
}
|
|
115839
114552
|
}
|
|
115840
114553
|
saveState(state, expected) {
|
|
@@ -115845,30 +114558,30 @@ class RecoveryOwner {
|
|
|
115845
114558
|
if (this.read("state.json") !== expected)
|
|
115846
114559
|
invalid6();
|
|
115847
114560
|
this.assert();
|
|
115848
|
-
renameSync14(
|
|
114561
|
+
renameSync14(join64(this.directory, name), join64(this.directory, "state.json"));
|
|
115849
114562
|
this.sync();
|
|
115850
114563
|
}
|
|
115851
114564
|
async locked(action) {
|
|
115852
114565
|
this.assert();
|
|
115853
114566
|
let fd;
|
|
115854
114567
|
try {
|
|
115855
|
-
fd =
|
|
114568
|
+
fd = openSync28(join64(this.directory, "operation.lock"), constants28.O_WRONLY | constants28.O_CREAT | constants28.O_EXCL | constants28.O_NOFOLLOW, 384);
|
|
115856
114569
|
} catch {
|
|
115857
114570
|
throw new RecurringCustomerError("RECURRING_RECOVERY_BUSY", "Recovery is locked. Confirm the original process has stopped before explicitly reconciling the retained lock; no request was retried.");
|
|
115858
114571
|
}
|
|
115859
|
-
const owner3 =
|
|
114572
|
+
const owner3 = fstatSync28(fd);
|
|
115860
114573
|
try {
|
|
115861
114574
|
writeFileSync21(fd, JSON.stringify({ pid: process.pid }));
|
|
115862
114575
|
fsyncSync8(fd);
|
|
115863
114576
|
this.sync();
|
|
115864
114577
|
return await action();
|
|
115865
114578
|
} finally {
|
|
115866
|
-
|
|
114579
|
+
closeSync28(fd);
|
|
115867
114580
|
this.assert();
|
|
115868
|
-
const now3 = lstatSync41(
|
|
114581
|
+
const now3 = lstatSync41(join64(this.directory, "operation.lock"));
|
|
115869
114582
|
if (owner3.dev !== now3.dev || owner3.ino !== now3.ino || !now3.isFile() || now3.isSymbolicLink())
|
|
115870
114583
|
invalid6();
|
|
115871
|
-
unlinkSync10(
|
|
114584
|
+
unlinkSync10(join64(this.directory, "operation.lock"));
|
|
115872
114585
|
this.sync();
|
|
115873
114586
|
}
|
|
115874
114587
|
}
|
|
@@ -115901,7 +114614,7 @@ function base2(customer, organizationId) {
|
|
|
115901
114614
|
};
|
|
115902
114615
|
}
|
|
115903
114616
|
function create3(directory2, intent) {
|
|
115904
|
-
if (!
|
|
114617
|
+
if (!isAbsolute36(directory2) || resolve36(directory2) !== directory2 || realpathSync18(dirname40(directory2)) !== dirname40(directory2))
|
|
115905
114618
|
invalid6();
|
|
115906
114619
|
mkdirSync22(directory2, { mode: 448 });
|
|
115907
114620
|
const owner3 = new RecoveryOwner(directory2), checked = intentSchema.parse(intent), intentSha256 = canonicalJsonSha256(checked);
|
|
@@ -116064,7 +114777,7 @@ var init_recurring_recovery = __esm(() => {
|
|
|
116064
114777
|
});
|
|
116065
114778
|
|
|
116066
114779
|
// src/lib/recurring-surface.ts
|
|
116067
|
-
import { isAbsolute as
|
|
114780
|
+
import { isAbsolute as isAbsolute37 } from "path";
|
|
116068
114781
|
function recurringMcpSchema(action) {
|
|
116069
114782
|
return action === "activate" ? recurringSurfaceSchemas.activate.extend({ code, confirm: exports_external.literal(true) }) : recurringSurfaceSchemas[action];
|
|
116070
114783
|
}
|
|
@@ -116169,7 +114882,7 @@ var init_recurring_surface = __esm(() => {
|
|
|
116169
114882
|
uuid12 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
116170
114883
|
context = { userId: uuid12.optional(), membershipId: uuid12.optional() };
|
|
116171
114884
|
page2 = { limit: exports_external.number().int().min(1).max(100).optional(), cursor: exports_external.string().min(1).max(2048).optional() };
|
|
116172
|
-
directory2 = exports_external.string().max(4096).refine(
|
|
114885
|
+
directory2 = exports_external.string().max(4096).refine(isAbsolute37, "Use an absolute recovery directory");
|
|
116173
114886
|
email4 = exports_external.string().trim().toLowerCase().email().max(254);
|
|
116174
114887
|
code = exports_external.string().regex(/^\d{6}$/);
|
|
116175
114888
|
recurringSurfaceSchemas = {
|
|
@@ -117542,7 +116255,7 @@ var init_run_routing = __esm(() => {
|
|
|
117542
116255
|
|
|
117543
116256
|
// src/mcp/operation-tools.ts
|
|
117544
116257
|
import { existsSync as existsSync29, readdirSync as readdirSync24, statSync as statSync20 } from "fs";
|
|
117545
|
-
import { join as
|
|
116258
|
+
import { join as join65 } from "path";
|
|
117546
116259
|
function registerOperationTools(server) {
|
|
117547
116260
|
server.registerTool("cloud_skill_eligibility", {
|
|
117548
116261
|
title: "Cloud Skill Eligibility",
|
|
@@ -118024,7 +116737,7 @@ function registerOperationTools(server) {
|
|
|
118024
116737
|
if (exists) {
|
|
118025
116738
|
try {
|
|
118026
116739
|
skillCount = readdirSync24(agentSkillsPath).filter((f) => {
|
|
118027
|
-
const full =
|
|
116740
|
+
const full = join65(agentSkillsPath, f);
|
|
118028
116741
|
return !f.startsWith(".") && statSync20(full).isDirectory();
|
|
118029
116742
|
}).length;
|
|
118030
116743
|
} catch {}
|
|
@@ -118092,10 +116805,10 @@ var init_operation_tools = __esm(() => {
|
|
|
118092
116805
|
|
|
118093
116806
|
// src/lib/feedback.ts
|
|
118094
116807
|
import { appendFileSync as appendFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync23 } from "fs";
|
|
118095
|
-
import { dirname as dirname41, join as
|
|
116808
|
+
import { dirname as dirname41, join as join66 } from "path";
|
|
118096
116809
|
import { Database } from "bun:sqlite";
|
|
118097
116810
|
function getFeedbackDbPath() {
|
|
118098
|
-
return
|
|
116811
|
+
return join66(getDataDir(), "skills.db");
|
|
118099
116812
|
}
|
|
118100
116813
|
function getFeedbackDb() {
|
|
118101
116814
|
const dbPath = getFeedbackDbPath();
|
|
@@ -118127,7 +116840,7 @@ function saveFeedback(input2) {
|
|
|
118127
116840
|
throw new Error("Feedback message is required");
|
|
118128
116841
|
const category = input2.category ?? "general";
|
|
118129
116842
|
if (isApiMode()) {
|
|
118130
|
-
const path =
|
|
116843
|
+
const path = join66(getDataDir(), "feedback.jsonl");
|
|
118131
116844
|
const dir = dirname41(path);
|
|
118132
116845
|
if (!existsSync30(dir))
|
|
118133
116846
|
mkdirSync23(dir, { recursive: true });
|
|
@@ -118291,23 +117004,23 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
118291
117004
|
});
|
|
118292
117005
|
|
|
118293
117006
|
// src/lib/scheduler.ts
|
|
118294
|
-
import { existsSync as existsSync31, readFileSync as
|
|
118295
|
-
import { join as
|
|
117007
|
+
import { existsSync as existsSync31, readFileSync as readFileSync40, writeFileSync as writeFileSync22, mkdirSync as mkdirSync24 } from "fs";
|
|
117008
|
+
import { join as join67 } from "path";
|
|
118296
117009
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
118297
|
-
return
|
|
117010
|
+
return join67(targetDir, ".skills", "schedules.json");
|
|
118298
117011
|
}
|
|
118299
117012
|
function loadSchedules(targetDir = process.cwd()) {
|
|
118300
117013
|
const path = getSchedulesPath(targetDir);
|
|
118301
117014
|
if (existsSync31(path)) {
|
|
118302
117015
|
try {
|
|
118303
|
-
return JSON.parse(
|
|
117016
|
+
return JSON.parse(readFileSync40(path, "utf-8"));
|
|
118304
117017
|
} catch {}
|
|
118305
117018
|
}
|
|
118306
117019
|
return { version: 1, schedules: [] };
|
|
118307
117020
|
}
|
|
118308
117021
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
118309
117022
|
const path = getSchedulesPath(targetDir);
|
|
118310
|
-
const dir =
|
|
117023
|
+
const dir = join67(targetDir, ".skills");
|
|
118311
117024
|
if (!existsSync31(dir))
|
|
118312
117025
|
mkdirSync24(dir, { recursive: true });
|
|
118313
117026
|
writeFileSync22(path, JSON.stringify(data, null, 2));
|
|
@@ -118610,12 +117323,12 @@ import { createHash as createHash43, createHmac as createHmac3 } from "crypto";
|
|
|
118610
117323
|
import {
|
|
118611
117324
|
existsSync as existsSync32,
|
|
118612
117325
|
mkdirSync as mkdirSync25,
|
|
118613
|
-
readFileSync as
|
|
117326
|
+
readFileSync as readFileSync41,
|
|
118614
117327
|
readdirSync as readdirSync25,
|
|
118615
117328
|
statSync as statSync21,
|
|
118616
117329
|
writeFileSync as writeFileSync23
|
|
118617
117330
|
} from "fs";
|
|
118618
|
-
import { dirname as dirname42, join as
|
|
117331
|
+
import { dirname as dirname42, join as join68, normalize as normalize5, relative as relative13, sep as sep19 } from "path";
|
|
118619
117332
|
function resolveSkillsNativeStorageConfig(env3 = process.env) {
|
|
118620
117333
|
assertNoRetiredModeEnvVars(env3, {
|
|
118621
117334
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -118656,7 +117369,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
118656
117369
|
local: {
|
|
118657
117370
|
dataDir: getDataDir(),
|
|
118658
117371
|
projectStateDir: getProjectStateDir(targetDir),
|
|
118659
|
-
feedbackDbPath:
|
|
117372
|
+
feedbackDbPath: join68(getDataDir(), "skills.db")
|
|
118660
117373
|
},
|
|
118661
117374
|
remote: {
|
|
118662
117375
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -118678,7 +117391,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
118678
117391
|
const files = [];
|
|
118679
117392
|
if (existsSync32(projectStateDir)) {
|
|
118680
117393
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
118681
|
-
const bytes =
|
|
117394
|
+
const bytes = readFileSync41(filePath);
|
|
118682
117395
|
const relativePath = toPosix(relative13(targetDir, filePath));
|
|
118683
117396
|
files.push({
|
|
118684
117397
|
path: relativePath,
|
|
@@ -118737,7 +117450,7 @@ function parsePositiveInteger(value) {
|
|
|
118737
117450
|
function walkFiles2(dir) {
|
|
118738
117451
|
const files = [];
|
|
118739
117452
|
for (const entry of readdirSync25(dir)) {
|
|
118740
|
-
const full =
|
|
117453
|
+
const full = join68(dir, entry);
|
|
118741
117454
|
const stats = statSync21(full);
|
|
118742
117455
|
if (stats.isDirectory())
|
|
118743
117456
|
files.push(...walkFiles2(full));
|
|
@@ -119009,11 +117722,11 @@ var init_private_publication_customer = __esm(() => {
|
|
|
119009
117722
|
});
|
|
119010
117723
|
|
|
119011
117724
|
// src/lib/private-publication-recovery.ts
|
|
119012
|
-
import { constants as constants29, closeSync as
|
|
119013
|
-
import { dirname as dirname43, isAbsolute as
|
|
117725
|
+
import { constants as constants29, closeSync as closeSync29, fsyncSync as fsyncSync9, fstatSync as fstatSync29, lstatSync as lstatSync42, mkdirSync as mkdirSync26, openSync as openSync29, readSync as readSync22, realpathSync as realpathSync19, renameSync as renameSync15, unlinkSync as unlinkSync11, writeFileSync as writeFileSync24 } from "fs";
|
|
117726
|
+
import { dirname as dirname43, isAbsolute as isAbsolute38, join as join69, resolve as resolve37 } from "path";
|
|
119014
117727
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
119015
117728
|
function safeDirectory(directory3) {
|
|
119016
|
-
if (!
|
|
117729
|
+
if (!isAbsolute38(directory3) || directory3 !== resolve37(directory3) || realpathSync19(directory3) !== directory3)
|
|
119017
117730
|
return fail5();
|
|
119018
117731
|
for (let path = directory3;; path = dirname43(path)) {
|
|
119019
117732
|
const stat3 = lstatSync42(path);
|
|
@@ -119033,10 +117746,10 @@ function unchangedDirectory(directory3, identity6) {
|
|
|
119033
117746
|
return fail5();
|
|
119034
117747
|
}
|
|
119035
117748
|
function readOwned(directory3, name, max2) {
|
|
119036
|
-
const identity6 = safeDirectory(directory3), file2 =
|
|
119037
|
-
const fd =
|
|
117749
|
+
const identity6 = safeDirectory(directory3), file2 = join69(directory3, name);
|
|
117750
|
+
const fd = openSync29(file2, constants29.O_RDONLY | constants29.O_NOFOLLOW);
|
|
119038
117751
|
try {
|
|
119039
|
-
const stat3 =
|
|
117752
|
+
const stat3 = fstatSync29(fd);
|
|
119040
117753
|
if (!stat3.isFile() || stat3.nlink !== 1 || stat3.size > max2 || stat3.size < 1 || (stat3.mode & 63) !== 0 || process.getuid && stat3.uid !== process.getuid())
|
|
119041
117754
|
return fail5();
|
|
119042
117755
|
const buffer = Buffer.alloc(Math.min(max2, stat3.size) + 1);
|
|
@@ -119047,22 +117760,22 @@ function readOwned(directory3, name, max2) {
|
|
|
119047
117760
|
break;
|
|
119048
117761
|
length += count;
|
|
119049
117762
|
}
|
|
119050
|
-
const bytes = buffer.subarray(0, length), after2 =
|
|
117763
|
+
const bytes = buffer.subarray(0, length), after2 = fstatSync29(fd);
|
|
119051
117764
|
unchangedDirectory(directory3, identity6);
|
|
119052
117765
|
if (bytes.length !== stat3.size || stat3.size !== after2.size || stat3.mtimeMs !== after2.mtimeMs || stat3.ctimeMs !== after2.ctimeMs)
|
|
119053
117766
|
return fail5();
|
|
119054
117767
|
return bytes;
|
|
119055
117768
|
} finally {
|
|
119056
|
-
|
|
117769
|
+
closeSync29(fd);
|
|
119057
117770
|
}
|
|
119058
117771
|
}
|
|
119059
117772
|
function writeOwned(directory3, name, bytes) {
|
|
119060
|
-
const fd =
|
|
117773
|
+
const fd = openSync29(join69(directory3, name), constants29.O_WRONLY | constants29.O_CREAT | constants29.O_EXCL | constants29.O_NOFOLLOW, 384);
|
|
119061
117774
|
try {
|
|
119062
117775
|
writeFileSync24(fd, bytes);
|
|
119063
117776
|
fsyncSync9(fd);
|
|
119064
117777
|
} finally {
|
|
119065
|
-
|
|
117778
|
+
closeSync29(fd);
|
|
119066
117779
|
}
|
|
119067
117780
|
}
|
|
119068
117781
|
function save2(directory3, value) {
|
|
@@ -119070,12 +117783,12 @@ function save2(directory3, value) {
|
|
|
119070
117783
|
writeOwned(directory3, temporary, JSON.stringify(value) + `
|
|
119071
117784
|
`);
|
|
119072
117785
|
unchangedDirectory(directory3, identity6);
|
|
119073
|
-
renameSync15(
|
|
119074
|
-
const fd =
|
|
117786
|
+
renameSync15(join69(directory3, temporary), join69(directory3, "receipt.json"));
|
|
117787
|
+
const fd = openSync29(directory3, constants29.O_RDONLY | constants29.O_NOFOLLOW);
|
|
119075
117788
|
try {
|
|
119076
117789
|
fsyncSync9(fd);
|
|
119077
117790
|
} finally {
|
|
119078
|
-
|
|
117791
|
+
closeSync29(fd);
|
|
119079
117792
|
}
|
|
119080
117793
|
}
|
|
119081
117794
|
function bind2(client2, receipt) {
|
|
@@ -119083,21 +117796,21 @@ function bind2(client2, receipt) {
|
|
|
119083
117796
|
throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
|
|
119084
117797
|
}
|
|
119085
117798
|
async function locked(directory3, action) {
|
|
119086
|
-
const identity6 = safeDirectory(directory3), file2 =
|
|
117799
|
+
const identity6 = safeDirectory(directory3), file2 = join69(directory3, "operation.lock");
|
|
119087
117800
|
let fd;
|
|
119088
117801
|
try {
|
|
119089
|
-
fd =
|
|
117802
|
+
fd = openSync29(file2, constants29.O_WRONLY | constants29.O_CREAT | constants29.O_EXCL | constants29.O_NOFOLLOW, 384);
|
|
119090
117803
|
} catch {
|
|
119091
117804
|
throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
|
|
119092
117805
|
}
|
|
119093
|
-
const lock =
|
|
117806
|
+
const lock = fstatSync29(fd);
|
|
119094
117807
|
try {
|
|
119095
117808
|
writeFileSync24(fd, JSON.stringify({ pid: process.pid }) + `
|
|
119096
117809
|
`);
|
|
119097
117810
|
fsyncSync9(fd);
|
|
119098
117811
|
return await action();
|
|
119099
117812
|
} finally {
|
|
119100
|
-
|
|
117813
|
+
closeSync29(fd);
|
|
119101
117814
|
unchangedDirectory(directory3, identity6);
|
|
119102
117815
|
const now3 = lstatSync42(file2);
|
|
119103
117816
|
if (now3.dev !== lock.dev || now3.ino !== lock.ino || !now3.isFile() || now3.isSymbolicLink())
|
|
@@ -119151,15 +117864,15 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
|
|
|
119151
117864
|
phase: "prepared",
|
|
119152
117865
|
intent: null
|
|
119153
117866
|
};
|
|
119154
|
-
if (!
|
|
117867
|
+
if (!isAbsolute38(recoveryDirectory) || resolve37(recoveryDirectory) !== recoveryDirectory || realpathSync19(dirname43(recoveryDirectory)) !== dirname43(recoveryDirectory))
|
|
119155
117868
|
return fail5();
|
|
119156
117869
|
mkdirSync26(recoveryDirectory, { mode: 448 });
|
|
119157
117870
|
safeDirectory(recoveryDirectory);
|
|
119158
|
-
const parent =
|
|
117871
|
+
const parent = openSync29(dirname43(recoveryDirectory), constants29.O_RDONLY | constants29.O_NOFOLLOW);
|
|
119159
117872
|
try {
|
|
119160
117873
|
fsyncSync9(parent);
|
|
119161
117874
|
} finally {
|
|
119162
|
-
|
|
117875
|
+
closeSync29(parent);
|
|
119163
117876
|
}
|
|
119164
117877
|
writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
|
|
119165
117878
|
save2(recoveryDirectory, receipt);
|
|
@@ -119252,10 +117965,10 @@ var init_private_publication_recovery = __esm(() => {
|
|
|
119252
117965
|
});
|
|
119253
117966
|
|
|
119254
117967
|
// src/mcp/private-publication-tools.ts
|
|
119255
|
-
import { isAbsolute as
|
|
117968
|
+
import { isAbsolute as isAbsolute39 } from "path";
|
|
119256
117969
|
function registerPrivatePublicationTools(server) {
|
|
119257
117970
|
const uuid9 = exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
|
|
119258
|
-
const directory3 = exports_external.string().max(4096).refine(
|
|
117971
|
+
const directory3 = exports_external.string().max(4096).refine(isAbsolute39, "Use an absolute local directory");
|
|
119259
117972
|
const verification2 = { email: exports_external.string().email().max(254), code: exports_external.string().regex(/^\d{6}$/), userId: uuid9, membershipId: uuid9, recoveryDirectory: directory3 };
|
|
119260
117973
|
const description = "Manage a private source publication using fresh workspace-bound verification and a host-local recovery directory. Publishing requires explicit UUID current-version comparison and upload consent. Resume reuses immutable saved bytes and intent; an uncertain PUT is finalized for server verification, never uploaded twice. Sessions and signed URLs are not returned or persisted. Private execution is unavailable. Host request history may contain the supplied verification code.";
|
|
119261
117974
|
const handler = (action) => async (value) => {
|
|
@@ -119716,10 +118429,10 @@ var exports_runtime_mcp = {};
|
|
|
119716
118429
|
__export(exports_runtime_mcp, {
|
|
119717
118430
|
handleMcp: () => handleMcp
|
|
119718
118431
|
});
|
|
119719
|
-
import { closeSync as
|
|
118432
|
+
import { closeSync as closeSync30, constants as constants30, existsSync as existsSync33, fchmodSync as fchmodSync3, fchownSync, fstatSync as fstatSync30, fsyncSync as fsyncSync10, lstatSync as lstatSync43, mkdirSync as mkdirSync27, openSync as openSync30, readFileSync as readFileSync42, readSync as readSync23, renameSync as renameSync16, rmdirSync as rmdirSync4, statSync as statSync22, unlinkSync as unlinkSync12, writeFileSync as writeFileSync25, writeSync as writeSync6 } from "fs";
|
|
119720
118433
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
119721
118434
|
import { homedir as homedir11 } from "os";
|
|
119722
|
-
import { dirname as dirname44, isAbsolute as
|
|
118435
|
+
import { dirname as dirname44, isAbsolute as isAbsolute40, join as join70 } from "path";
|
|
119723
118436
|
import { isDeepStrictEqual as isDeepStrictEqual19, stripVTControlCharacters } from "util";
|
|
119724
118437
|
async function handleMcp(options) {
|
|
119725
118438
|
if (options.register) {
|
|
@@ -119766,15 +118479,15 @@ async function registerMcpForAgent(agent, command, environment) {
|
|
|
119766
118479
|
case "codex":
|
|
119767
118480
|
return registerCodexMcp(command, environment);
|
|
119768
118481
|
case "gemini":
|
|
119769
|
-
return registerJsonMcpServer(agent,
|
|
118482
|
+
return registerJsonMcpServer(agent, join70(homedir11(), ".gemini", "settings.json"), "mcpServers", server2);
|
|
119770
118483
|
case "pi":
|
|
119771
|
-
return registerJsonMcpServer(agent,
|
|
118484
|
+
return registerJsonMcpServer(agent, join70(homedir11(), ".pi", "agent", "mcp.json"), "mcpServers", server2);
|
|
119772
118485
|
case "opencode":
|
|
119773
118486
|
return registerOpenCodeMcp(command, environment);
|
|
119774
118487
|
case "cursor":
|
|
119775
|
-
return registerJsonMcpServer(agent,
|
|
118488
|
+
return registerJsonMcpServer(agent, join70(homedir11(), ".cursor", "mcp.json"), "mcpServers", server2);
|
|
119776
118489
|
case "windsurf":
|
|
119777
|
-
return registerJsonMcpServer(agent,
|
|
118490
|
+
return registerJsonMcpServer(agent, join70(homedir11(), ".windsurf", "mcp.json"), "mcpServers", server2);
|
|
119778
118491
|
default:
|
|
119779
118492
|
return { agent, success: false, error: `Unknown agent: ${agent}. Available: ${AGENT_TARGETS.join(", ")}, all` };
|
|
119780
118493
|
}
|
|
@@ -119798,7 +118511,7 @@ async function registerClaudeMcp(command, server2, environment) {
|
|
|
119798
118511
|
const info = lstatSync43(path);
|
|
119799
118512
|
const backup = `${path}.skills-mcp-${randomUUID12()}.backup`;
|
|
119800
118513
|
writeFileSync25(backup, bytes, { flag: "wx", mode: 384 });
|
|
119801
|
-
if (!
|
|
118514
|
+
if (!readFileSync42(backup).equals(bytes))
|
|
119802
118515
|
throw new Error;
|
|
119803
118516
|
preimage = { bytes, mode: info.mode & 4095, uid: info.uid, gid: info.gid, nlink: info.nlink, backup };
|
|
119804
118517
|
}
|
|
@@ -119862,13 +118575,13 @@ async function registerClaudeMcp(command, server2, environment) {
|
|
|
119862
118575
|
`);
|
|
119863
118576
|
const temporary = `${path}.skills-mcp-${randomUUID12()}.tmp`;
|
|
119864
118577
|
writeFileSync25(temporary, next, { flag: "wx", mode: 384 });
|
|
119865
|
-
if (!
|
|
118578
|
+
if (!readFileSync42(temporary).equals(next))
|
|
119866
118579
|
throw new Error;
|
|
119867
118580
|
const latest = firstRun ? readClaudeFirstRunConfig(path, firstRun) : readClaudeConfig(path);
|
|
119868
118581
|
if (claudeUserConfigPath() !== path || (before2 === null ? latest !== null : latest === null || !latest.equals(before2)))
|
|
119869
118582
|
throw new Error;
|
|
119870
118583
|
renameSync16(temporary, path);
|
|
119871
|
-
if (!
|
|
118584
|
+
if (!readFileSync42(path).equals(next))
|
|
119872
118585
|
throw new Error;
|
|
119873
118586
|
}
|
|
119874
118587
|
return { agent: "claude", success: true, path, command: cliCommand };
|
|
@@ -119896,12 +118609,12 @@ function restoreClaudePreimage(path, preimage, observed, command) {
|
|
|
119896
118609
|
}
|
|
119897
118610
|
const rewrite = `${path}.skills-mcp-${randomUUID12()}.rewrite`;
|
|
119898
118611
|
writeFileSync25(rewrite, observed, { flag: "wx", mode: 384 });
|
|
119899
|
-
if (!
|
|
118612
|
+
if (!readFileSync42(rewrite).equals(observed))
|
|
119900
118613
|
throw new Error;
|
|
119901
118614
|
const temporary = `${path}.skills-mcp-${randomUUID12()}.tmp`;
|
|
119902
118615
|
let renamed = false;
|
|
119903
118616
|
try {
|
|
119904
|
-
const fd =
|
|
118617
|
+
const fd = openSync30(temporary, constants30.O_WRONLY | constants30.O_CREAT | constants30.O_EXCL | constants30.O_NOFOLLOW, 384);
|
|
119905
118618
|
try {
|
|
119906
118619
|
for (let offset = 0;offset < preimage.bytes.length; )
|
|
119907
118620
|
offset += writeSync6(fd, preimage.bytes, offset);
|
|
@@ -119909,9 +118622,9 @@ function restoreClaudePreimage(path, preimage, observed, command) {
|
|
|
119909
118622
|
fchmodSync3(fd, preimage.mode);
|
|
119910
118623
|
fsyncSync10(fd);
|
|
119911
118624
|
} finally {
|
|
119912
|
-
|
|
118625
|
+
closeSync30(fd);
|
|
119913
118626
|
}
|
|
119914
|
-
if (!
|
|
118627
|
+
if (!readFileSync42(temporary).equals(preimage.bytes))
|
|
119915
118628
|
throw new Error;
|
|
119916
118629
|
const latest = readClaudeConfig(path);
|
|
119917
118630
|
if (claudeUserConfigPath() !== path || latest === null || !latest.equals(observed))
|
|
@@ -119923,7 +118636,7 @@ function restoreClaudePreimage(path, preimage, observed, command) {
|
|
|
119923
118636
|
unlinkSync12(temporary);
|
|
119924
118637
|
}
|
|
119925
118638
|
const info = lstatSync43(path);
|
|
119926
|
-
if (!
|
|
118639
|
+
if (!readFileSync42(path).equals(preimage.bytes) || (info.mode & 4095) !== preimage.mode || info.uid !== preimage.uid || info.gid !== preimage.gid)
|
|
119927
118640
|
throw new Error;
|
|
119928
118641
|
return {
|
|
119929
118642
|
agent: "claude",
|
|
@@ -119964,10 +118677,10 @@ function claudeConfigLeft(path, preimage, command, rewrite) {
|
|
|
119964
118677
|
}
|
|
119965
118678
|
function claudeUserConfigPath() {
|
|
119966
118679
|
const override = process.env.CLAUDE_CONFIG_DIR;
|
|
119967
|
-
if (override !== undefined && (!override || !
|
|
118680
|
+
if (override !== undefined && (!override || !isAbsolute40(override) || /[\x00-\x1f\x7f]/.test(override)))
|
|
119968
118681
|
throw new Error;
|
|
119969
|
-
const modern =
|
|
119970
|
-
const legacy =
|
|
118682
|
+
const modern = join70(override || homedir11(), process.env.CLAUDE_CODE_CUSTOM_OAUTH_URL ? ".claude-custom-oauth.json" : ".claude.json");
|
|
118683
|
+
const legacy = join70(override ?? join70(homedir11(), ".claude"), ".config.json");
|
|
119971
118684
|
if (existsSync33(legacy)) {
|
|
119972
118685
|
const info = lstatSync43(legacy);
|
|
119973
118686
|
if (!info.isFile())
|
|
@@ -119983,7 +118696,7 @@ function readClaudeConfig(path) {
|
|
|
119983
118696
|
return null;
|
|
119984
118697
|
if (!info.isFile() || info.size > 16777216)
|
|
119985
118698
|
throw new Error;
|
|
119986
|
-
const bytes =
|
|
118699
|
+
const bytes = readFileSync42(path);
|
|
119987
118700
|
parseClaudeConfig(bytes);
|
|
119988
118701
|
return bytes;
|
|
119989
118702
|
}
|
|
@@ -120030,9 +118743,9 @@ function readClaudeFirstRunConfig(path, verifyPath) {
|
|
|
120030
118743
|
const identity6 = (info) => `${info.dev}:${info.ino}:${info.uid}:${info.gid}:${info.mode}:${info.nlink}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`;
|
|
120031
118744
|
if (!before2.isFile() || before2.nlink !== 1n || before2.uid !== BigInt(process.getuid()) || (before2.mode & 0o022n) !== 0n || before2.size > 16n * 1024n * 1024n)
|
|
120032
118745
|
throw new Error;
|
|
120033
|
-
const fd =
|
|
118746
|
+
const fd = openSync30(path, constants30.O_RDONLY | constants30.O_NOFOLLOW | constants30.O_NONBLOCK);
|
|
120034
118747
|
try {
|
|
120035
|
-
if (identity6(
|
|
118748
|
+
if (identity6(fstatSync30(fd, { bigint: true })) !== identity6(before2))
|
|
120036
118749
|
throw new Error;
|
|
120037
118750
|
const bytes = Buffer.alloc(Number(before2.size) + 1);
|
|
120038
118751
|
let length = 0;
|
|
@@ -120042,14 +118755,14 @@ function readClaudeFirstRunConfig(path, verifyPath) {
|
|
|
120042
118755
|
break;
|
|
120043
118756
|
length += count;
|
|
120044
118757
|
}
|
|
120045
|
-
if (BigInt(length) !== before2.size || identity6(
|
|
118758
|
+
if (BigInt(length) !== before2.size || identity6(fstatSync30(fd, { bigint: true })) !== identity6(before2) || identity6(lstatSync43(path, { bigint: true })) !== identity6(before2))
|
|
120046
118759
|
throw new Error;
|
|
120047
118760
|
const content = bytes.subarray(0, length);
|
|
120048
118761
|
verifyPath();
|
|
120049
118762
|
parseClaudeConfig(content);
|
|
120050
118763
|
return content;
|
|
120051
118764
|
} finally {
|
|
120052
|
-
|
|
118765
|
+
closeSync30(fd);
|
|
120053
118766
|
}
|
|
120054
118767
|
}
|
|
120055
118768
|
function parseClaudeConfig(bytes) {
|
|
@@ -120113,7 +118826,7 @@ function withClaudeConfigLock(path, action) {
|
|
|
120113
118826
|
}
|
|
120114
118827
|
}
|
|
120115
118828
|
function registerCodexMcp(command, environment) {
|
|
120116
|
-
const path =
|
|
118829
|
+
const path = join70(homedir11(), ".codex", "config.toml");
|
|
120117
118830
|
const body = `command = ${JSON.stringify(command)}
|
|
120118
118831
|
args = ["--stdio"]${Object.keys(environment).length ? `
|
|
120119
118832
|
env = { ${Object.entries(environment).map(([key2, value]) => `${JSON.stringify(key2)} = ${JSON.stringify(value)}`).join(", ")} }` : ""}`;
|
|
@@ -120135,7 +118848,7 @@ ${body}`;
|
|
|
120135
118848
|
}
|
|
120136
118849
|
}
|
|
120137
118850
|
function registerOpenCodeMcp(command, environment) {
|
|
120138
|
-
const path =
|
|
118851
|
+
const path = join70(homedir11(), ".config", "opencode", "opencode.json");
|
|
120139
118852
|
const server2 = {
|
|
120140
118853
|
type: "local",
|
|
120141
118854
|
command: [command, "--stdio"],
|
|
@@ -120232,7 +118945,7 @@ function findCommandOnPath(command) {
|
|
|
120232
118945
|
for (const dir of pathValue.split(":")) {
|
|
120233
118946
|
if (!dir)
|
|
120234
118947
|
continue;
|
|
120235
|
-
const candidate2 =
|
|
118948
|
+
const candidate2 = join70(dir, command);
|
|
120236
118949
|
if (existsSync33(candidate2))
|
|
120237
118950
|
return candidate2;
|
|
120238
118951
|
}
|
|
@@ -120457,21 +119170,21 @@ var init_reviewed_runtime_lock = __esm(() => {
|
|
|
120457
119170
|
import { createHash as createHash45, randomUUID as randomUUID13 } from "crypto";
|
|
120458
119171
|
import { Readable } from "stream";
|
|
120459
119172
|
import { createGunzip as createGunzip2 } from "zlib";
|
|
120460
|
-
import { spawnSync as
|
|
119173
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
120461
119174
|
import {
|
|
120462
119175
|
chmodSync as chmodSync4,
|
|
120463
119176
|
copyFileSync,
|
|
120464
119177
|
existsSync as existsSync34,
|
|
120465
119178
|
lstatSync as lstatSync44,
|
|
120466
119179
|
mkdirSync as mkdirSync28,
|
|
120467
|
-
readFileSync as
|
|
119180
|
+
readFileSync as readFileSync43,
|
|
120468
119181
|
readdirSync as readdirSync26,
|
|
120469
119182
|
readlinkSync as readlinkSync8,
|
|
120470
119183
|
realpathSync as realpathSync20,
|
|
120471
119184
|
renameSync as renameSync17,
|
|
120472
119185
|
writeFileSync as writeFileSync26
|
|
120473
119186
|
} from "fs";
|
|
120474
|
-
import { basename as basename23, delimiter as delimiter3, dirname as dirname45, isAbsolute as
|
|
119187
|
+
import { basename as basename23, delimiter as delimiter3, dirname as dirname45, isAbsolute as isAbsolute41, join as join71, relative as relative14, resolve as resolve38, sep as sep20 } from "path";
|
|
120475
119188
|
function oldLauncherState(item) {
|
|
120476
119189
|
if (item.oldShape === "pinned")
|
|
120477
119190
|
return { shape: "pinned", runtime: item.oldRuntime, cwd: item.oldCwd, target: item.oldTarget, sha256: item.oldSha256, format: item.oldFormat, profile: item.oldProfile };
|
|
@@ -120497,7 +119210,7 @@ function assertLauncherRecordShapes(item, expectedProfile) {
|
|
|
120497
119210
|
throw new Error("RECEIPT_LAUNCHER_SHAPE_INVALID");
|
|
120498
119211
|
continue;
|
|
120499
119212
|
}
|
|
120500
|
-
if (shape !== "pinned" || typeof runtime !== "string" || !
|
|
119213
|
+
if (shape !== "pinned" || typeof runtime !== "string" || !isAbsolute41(runtime) || typeof cwd2 !== "string" || !isAbsolute41(cwd2) || typeof sha7 !== "string" || !hex4.test(sha7)) {
|
|
120501
119214
|
throw new Error("RECEIPT_LAUNCHER_SHAPE_INVALID");
|
|
120502
119215
|
}
|
|
120503
119216
|
const target = side === "old" ? item.oldTarget : item.newTarget;
|
|
@@ -120524,10 +119237,10 @@ function verifyTargetPrerequisites(manifest, packageRoot, home, cwd2, pathValue)
|
|
|
120524
119237
|
if (Object.keys(contract).length !== 2 || contract.version !== 1 || contract.entry !== "dist/runtime-prerequisites.js") {
|
|
120525
119238
|
throw new Error("RUNTIME_PREREQUISITE_CONTRACT_INVALID");
|
|
120526
119239
|
}
|
|
120527
|
-
const entry =
|
|
119240
|
+
const entry = join71(packageRoot, contract.entry);
|
|
120528
119241
|
if (!entryExists(entry) || !lstatSync44(entry).isFile() || lstatSync44(entry).isSymbolicLink())
|
|
120529
119242
|
throw new Error("RUNTIME_PREREQUISITE_ENTRY_MISSING");
|
|
120530
|
-
const entrySha256 = hash6(
|
|
119243
|
+
const entrySha256 = hash6(readFileSync43(entry));
|
|
120531
119244
|
const env3 = { HOME: home, PATH: pathValue, BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0" };
|
|
120532
119245
|
for (const key2 of ["HASNA_SKILLS_DIR", "HASNA_SKILLS_HOME", "SKILLS_HOME", "HASNA_DATA_HOME", "XDG_DATA_HOME", "XDG_CONFIG_HOME", "XDG_CACHE_HOME", "XDG_STATE_HOME", "SUMI_HOME", "SUMI_CONFIG_DIR", "HERMES_HOME"]) {
|
|
120533
119246
|
if (process.env[key2] !== undefined)
|
|
@@ -120536,7 +119249,7 @@ function verifyTargetPrerequisites(manifest, packageRoot, home, cwd2, pathValue)
|
|
|
120536
119249
|
for (const key2 of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
|
|
120537
119250
|
if (process.env[key2] !== undefined)
|
|
120538
119251
|
env3[key2] = "";
|
|
120539
|
-
const child2 =
|
|
119252
|
+
const child2 = spawnSync5(pinnedLauncherRuntime(), [...PINNED_LAUNCHER_BUN_FLAGS2, entry, home, cwd2], {
|
|
120540
119253
|
cwd: dirname45(packageRoot),
|
|
120541
119254
|
env: env3,
|
|
120542
119255
|
encoding: "buffer",
|
|
@@ -120570,7 +119283,7 @@ function verifyTargetPrerequisites(manifest, packageRoot, home, cwd2, pathValue)
|
|
|
120570
119283
|
}
|
|
120571
119284
|
if (child2.status !== 0 || result2.code !== null)
|
|
120572
119285
|
throw new Error("RUNTIME_PREREQUISITE_RESPONSE_INVALID");
|
|
120573
|
-
if (hash6(
|
|
119286
|
+
if (hash6(readFileSync43(entry)) !== entrySha256)
|
|
120574
119287
|
throw new Error("RUNTIME_PREREQUISITE_ENTRY_CHANGED");
|
|
120575
119288
|
return { status: "verified", schema: result2.schema, targetVersion: result2.targetVersion, entry: contract.entry, entrySha256, checked: result2.checked };
|
|
120576
119289
|
}
|
|
@@ -120584,7 +119297,7 @@ function hash6(bytes) {
|
|
|
120584
119297
|
}
|
|
120585
119298
|
function isWithin(parent, child2) {
|
|
120586
119299
|
const rel = relative14(parent, child2);
|
|
120587
|
-
return rel === "" || !rel.startsWith(`..${sep20}`) && rel !== ".." && !
|
|
119300
|
+
return rel === "" || !rel.startsWith(`..${sep20}`) && rel !== ".." && !isAbsolute41(rel);
|
|
120588
119301
|
}
|
|
120589
119302
|
function entryExists(path) {
|
|
120590
119303
|
try {
|
|
@@ -120599,7 +119312,7 @@ function assertNoSymlinkAncestors(path) {
|
|
|
120599
119312
|
const parts = absolute.split(sep20).filter(Boolean);
|
|
120600
119313
|
let current = sep20;
|
|
120601
119314
|
for (const part of parts) {
|
|
120602
|
-
current =
|
|
119315
|
+
current = join71(current, part);
|
|
120603
119316
|
const stat3 = lstatSync44(current);
|
|
120604
119317
|
if (stat3.isSymbolicLink())
|
|
120605
119318
|
throw new Error("PATH_ANCESTOR_SYMLINK_UNSUPPORTED");
|
|
@@ -120608,23 +119321,23 @@ function assertNoSymlinkAncestors(path) {
|
|
|
120608
119321
|
}
|
|
120609
119322
|
}
|
|
120610
119323
|
function acquireRuntimeLock(runtimeRoot) {
|
|
120611
|
-
const lockPath =
|
|
119324
|
+
const lockPath = join71(runtimeRoot, ".copyfile-update-lock");
|
|
120612
119325
|
try {
|
|
120613
119326
|
mkdirSync28(lockPath, { mode: 448 });
|
|
120614
119327
|
} catch {
|
|
120615
119328
|
throw new Error("RUNTIME_UPDATE_LOCKED");
|
|
120616
119329
|
}
|
|
120617
119330
|
try {
|
|
120618
|
-
writeJsonPrivate(
|
|
119331
|
+
writeJsonPrivate(join71(lockPath, "lock.json"), { operationId: randomUUID13(), pid: process.pid, startedAt: new Date().toISOString() });
|
|
120619
119332
|
} catch {
|
|
120620
119333
|
try {
|
|
120621
|
-
renameSync17(lockPath,
|
|
119334
|
+
renameSync17(lockPath, join71(runtimeRoot, `.copyfile-update-lock-incomplete-${randomUUID13()}`));
|
|
120622
119335
|
} catch {}
|
|
120623
119336
|
throw new Error("RUNTIME_LOCK_INITIALIZATION_FAILED");
|
|
120624
119337
|
}
|
|
120625
119338
|
return () => {
|
|
120626
|
-
const released =
|
|
120627
|
-
writeJsonPrivate(
|
|
119339
|
+
const released = join71(runtimeRoot, `.copyfile-update-lock-released-${randomUUID13()}`);
|
|
119340
|
+
writeJsonPrivate(join71(lockPath, "released.json"), { completedAt: new Date().toISOString() });
|
|
120628
119341
|
renameSync17(lockPath, released);
|
|
120629
119342
|
};
|
|
120630
119343
|
}
|
|
@@ -120633,7 +119346,7 @@ function packageBins(pkg) {
|
|
|
120633
119346
|
const entries = typeof raw === "string" ? [[PACKAGE_NAME.slice(PACKAGE_NAME.lastIndexOf("/") + 1), raw]] : raw && typeof raw === "object" && !Array.isArray(raw) ? Object.entries(raw) : [];
|
|
120634
119347
|
const output2 = {};
|
|
120635
119348
|
for (const [name, value] of entries) {
|
|
120636
|
-
if (!/^[a-zA-Z0-9._-]+$/.test(name) || typeof value !== "string" || !value ||
|
|
119349
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name) || typeof value !== "string" || !value || isAbsolute41(value)) {
|
|
120637
119350
|
throw new Error("PACKAGE_BIN_INVALID");
|
|
120638
119351
|
}
|
|
120639
119352
|
const normalized2 = value.replaceAll("\\", "/");
|
|
@@ -120648,7 +119361,7 @@ function packageBins(pkg) {
|
|
|
120648
119361
|
function readPackage(path) {
|
|
120649
119362
|
let data;
|
|
120650
119363
|
try {
|
|
120651
|
-
data = object11(JSON.parse(
|
|
119364
|
+
data = object11(JSON.parse(readFileSync43(path, "utf8")), "PACKAGE_JSON_INVALID");
|
|
120652
119365
|
} catch {
|
|
120653
119366
|
throw new Error("PACKAGE_JSON_INVALID");
|
|
120654
119367
|
}
|
|
@@ -120659,7 +119372,7 @@ function readPackage(path) {
|
|
|
120659
119372
|
}
|
|
120660
119373
|
function resolveRuntimeLayout(homeInput, pathInput) {
|
|
120661
119374
|
const home = realpathSync20(homeInput);
|
|
120662
|
-
const runtimeRoot =
|
|
119375
|
+
const runtimeRoot = join71(home, ".hasna", "skills", "runtime");
|
|
120663
119376
|
assertNoSymlinkAncestors(runtimeRoot);
|
|
120664
119377
|
const runtimeRootStat = lstatSync44(runtimeRoot);
|
|
120665
119378
|
if (!runtimeRootStat.isDirectory() || runtimeRootStat.isSymbolicLink() || (runtimeRootStat.mode & 63) !== 0) {
|
|
@@ -120690,8 +119403,8 @@ function resolveRuntimeLayout(homeInput, pathInput) {
|
|
|
120690
119403
|
if (relativePackage.length !== 4 || relativePackage[1] !== "node_modules" || relativePackage[2] !== "@hasna" || relativePackage[3] !== "skills") {
|
|
120691
119404
|
throw new Error("ACTIVE_RUNTIME_LAYOUT_UNSUPPORTED");
|
|
120692
119405
|
}
|
|
120693
|
-
const current = readPackage(
|
|
120694
|
-
if (!activeBinary.startsWith(`${currentPackageRoot}${sep20}`) || activeBinary !==
|
|
119406
|
+
const current = readPackage(join71(currentPackageRoot, "package.json"));
|
|
119407
|
+
if (!activeBinary.startsWith(`${currentPackageRoot}${sep20}`) || activeBinary !== join71(currentPackageRoot, current.bins.skills)) {
|
|
120695
119408
|
throw new Error("ACTIVE_RUNTIME_ENTRYPOINT_MISMATCH");
|
|
120696
119409
|
}
|
|
120697
119410
|
const launchers = [];
|
|
@@ -120702,7 +119415,7 @@ function resolveRuntimeLayout(homeInput, pathInput) {
|
|
|
120702
119415
|
continue;
|
|
120703
119416
|
seenDirectories.add(dir);
|
|
120704
119417
|
for (const [name, target] of Object.entries(current.bins)) {
|
|
120705
|
-
const path =
|
|
119418
|
+
const path = join71(dir, name);
|
|
120706
119419
|
if (!existsSync34(path))
|
|
120707
119420
|
continue;
|
|
120708
119421
|
assertNoSymlinkAncestors(dir);
|
|
@@ -120717,9 +119430,9 @@ function resolveRuntimeLayout(homeInput, pathInput) {
|
|
|
120717
119430
|
if (launcher.kind === "foreign")
|
|
120718
119431
|
continue;
|
|
120719
119432
|
const actual = launcher.target;
|
|
120720
|
-
if (actual !==
|
|
119433
|
+
if (actual !== join71(currentPackageRoot, target))
|
|
120721
119434
|
continue;
|
|
120722
|
-
const newTarget =
|
|
119435
|
+
const newTarget = join71(runtimeRoot, "__target__", "node_modules", "@hasna", "skills", target);
|
|
120723
119436
|
const backupPath = `${path}.skills-prev-${randomUUID13()}`;
|
|
120724
119437
|
if (launcher.kind === "symlink") {
|
|
120725
119438
|
if (resolve38(dirname45(path), launcher.linkTarget) !== actual)
|
|
@@ -120878,14 +119591,14 @@ async function downloadPackage(version2, stagingRoot, registryOrigin, fetcher) {
|
|
|
120878
119591
|
if (unpacked.name !== PACKAGE_NAME || unpacked.version !== version2)
|
|
120879
119592
|
throw new Error("TARBALL_PACKAGE_IDENTITY_MISMATCH");
|
|
120880
119593
|
const bins = packageBins(unpacked);
|
|
120881
|
-
const archiveRoot =
|
|
119594
|
+
const archiveRoot = join71(stagingRoot, "verified-archive");
|
|
120882
119595
|
mkdirSync28(archiveRoot, { mode: 448 });
|
|
120883
119596
|
await archive.extract(archiveRoot);
|
|
120884
|
-
const extractedPackage =
|
|
119597
|
+
const extractedPackage = join71(archiveRoot, "package");
|
|
120885
119598
|
const extracted = readTree(extractedPackage, false);
|
|
120886
|
-
const tarballPath =
|
|
119599
|
+
const tarballPath = join71(stagingRoot, "verified-package.tgz");
|
|
120887
119600
|
writeFileSync26(tarballPath, bytes, { flag: "wx", mode: 384 });
|
|
120888
|
-
if (!
|
|
119601
|
+
if (!readFileSync43(tarballPath).equals(Buffer.from(bytes)))
|
|
120889
119602
|
throw new Error("TARBALL_READBACK_MISMATCH");
|
|
120890
119603
|
return {
|
|
120891
119604
|
tarballPath,
|
|
@@ -120931,7 +119644,7 @@ function readTree(root2, includeModes, excludeNpmDependencies = false) {
|
|
|
120931
119644
|
const entries = [];
|
|
120932
119645
|
const walk2 = (dir, prefix = "") => {
|
|
120933
119646
|
for (const item of readdirSync26(dir).sort()) {
|
|
120934
|
-
const path =
|
|
119647
|
+
const path = join71(dir, item);
|
|
120935
119648
|
const rel = prefix ? `${prefix}/${item}` : item;
|
|
120936
119649
|
const stat3 = lstatSync44(path);
|
|
120937
119650
|
if (excludeNpmDependencies && !prefix && item === "node_modules") {
|
|
@@ -120959,7 +119672,7 @@ function readTree(root2, includeModes, excludeNpmDependencies = false) {
|
|
|
120959
119672
|
} else if (stat3.isFile()) {
|
|
120960
119673
|
if (stat3.nlink !== 1)
|
|
120961
119674
|
throw new Error("TREE_HARDLINK_UNSUPPORTED");
|
|
120962
|
-
const bytes =
|
|
119675
|
+
const bytes = readFileSync43(path);
|
|
120963
119676
|
entries.push({ path: rel, kind: "file", sha256: hash6(bytes), ...includeModes ? { mode: stat3.mode & 511 } : {} });
|
|
120964
119677
|
} else
|
|
120965
119678
|
throw new Error("TREE_ENTRY_TYPE_UNSUPPORTED");
|
|
@@ -120975,7 +119688,7 @@ function normalizeModes(root2) {
|
|
|
120975
119688
|
chmodSync4(root2, rootStat.mode & 493);
|
|
120976
119689
|
const walk2 = (dir) => {
|
|
120977
119690
|
for (const item of readdirSync26(dir)) {
|
|
120978
|
-
const path =
|
|
119691
|
+
const path = join71(dir, item);
|
|
120979
119692
|
const stat3 = lstatSync44(path);
|
|
120980
119693
|
if (stat3.isSymbolicLink())
|
|
120981
119694
|
continue;
|
|
@@ -120995,23 +119708,23 @@ function writeJsonPrivate(path, value) {
|
|
|
120995
119708
|
`);
|
|
120996
119709
|
writeFileSync26(path, bytes, { flag: "wx", mode: 384 });
|
|
120997
119710
|
chmodSync4(path, 384);
|
|
120998
|
-
if (!
|
|
119711
|
+
if (!readFileSync43(path).equals(bytes))
|
|
120999
119712
|
throw new Error("PRIVATE_JSON_READBACK_MISMATCH");
|
|
121000
119713
|
}
|
|
121001
119714
|
function writePreimageManifest(stage, configs, launchers) {
|
|
121002
|
-
const path =
|
|
119715
|
+
const path = join71(stage, "preimage", "manifest.json");
|
|
121003
119716
|
const value = { schema: "skills.copyfile-preimage.v1", configs, launchers };
|
|
121004
119717
|
const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
|
|
121005
119718
|
`);
|
|
121006
119719
|
writeFileSync26(path, bytes, { flag: "wx", mode: 256 });
|
|
121007
119720
|
chmodSync4(path, 256);
|
|
121008
|
-
if (!
|
|
119721
|
+
if (!readFileSync43(path).equals(bytes))
|
|
121009
119722
|
throw new Error("PREIMAGE_MANIFEST_READBACK_MISMATCH");
|
|
121010
119723
|
return hash6(bytes);
|
|
121011
119724
|
}
|
|
121012
119725
|
function verifyPreimageManifest(runtimePath, receipt, home) {
|
|
121013
|
-
const path =
|
|
121014
|
-
const bytes =
|
|
119726
|
+
const path = join71(runtimePath, "preimage", "manifest.json");
|
|
119727
|
+
const bytes = readFileSync43(path);
|
|
121015
119728
|
if (hash6(bytes) !== receipt.preimageSha256)
|
|
121016
119729
|
throw new Error("PREIMAGE_MANIFEST_DRIFT");
|
|
121017
119730
|
const manifest = object11(JSON.parse(bytes.toString("utf8")), "PREIMAGE_MANIFEST_INVALID");
|
|
@@ -121022,33 +119735,33 @@ function verifyPreimageManifest(runtimePath, receipt, home) {
|
|
|
121022
119735
|
throw new Error("PREIMAGE_CONFIG_SET_INVALID");
|
|
121023
119736
|
for (const [index, relativePath] of CONFIG_PREIMAGE_PATHS.entries()) {
|
|
121024
119737
|
const config2 = receipt.configs[index];
|
|
121025
|
-
if (config2.relativePath !== relativePath || config2.sourcePath !==
|
|
119738
|
+
if (config2.relativePath !== relativePath || config2.sourcePath !== join71(home, relativePath))
|
|
121026
119739
|
throw new Error("PREIMAGE_CONFIG_BINDING_INVALID");
|
|
121027
119740
|
if (config2.present) {
|
|
121028
|
-
const backup =
|
|
119741
|
+
const backup = join71(runtimePath, "preimage", "configs", relativePath);
|
|
121029
119742
|
const stat3 = lstatSync44(backup);
|
|
121030
|
-
const saved =
|
|
119743
|
+
const saved = readFileSync43(backup);
|
|
121031
119744
|
if (!stat3.isFile() || stat3.isSymbolicLink() || hash6(saved) !== config2.sha256 || saved.byteLength !== config2.byteSize)
|
|
121032
119745
|
throw new Error("PREIMAGE_CONFIG_DRIFT");
|
|
121033
119746
|
}
|
|
121034
119747
|
}
|
|
121035
119748
|
}
|
|
121036
119749
|
function captureConfigPreimages(home, stage) {
|
|
121037
|
-
const root2 =
|
|
119750
|
+
const root2 = join71(stage, "preimage", "configs");
|
|
121038
119751
|
mkdirSync28(root2, { recursive: true, mode: 448 });
|
|
121039
119752
|
return CONFIG_PREIMAGE_PATHS.map((relativePath) => {
|
|
121040
|
-
const sourcePath =
|
|
119753
|
+
const sourcePath = join71(home, relativePath);
|
|
121041
119754
|
if (!entryExists(sourcePath))
|
|
121042
119755
|
return { sourcePath, relativePath, present: false };
|
|
121043
119756
|
const stat3 = lstatSync44(sourcePath);
|
|
121044
119757
|
if (!stat3.isFile() || stat3.isSymbolicLink())
|
|
121045
119758
|
throw new Error("CONFIG_PREIMAGE_NOT_REGULAR_FILE");
|
|
121046
|
-
const bytes =
|
|
121047
|
-
const backupPath =
|
|
119759
|
+
const bytes = readFileSync43(sourcePath);
|
|
119760
|
+
const backupPath = join71(root2, relativePath);
|
|
121048
119761
|
mkdirSync28(dirname45(backupPath), { recursive: true, mode: 448 });
|
|
121049
119762
|
writeFileSync26(backupPath, bytes, { flag: "wx", mode: 384 });
|
|
121050
119763
|
chmodSync4(backupPath, 384);
|
|
121051
|
-
const readback =
|
|
119764
|
+
const readback = readFileSync43(backupPath);
|
|
121052
119765
|
if (!readback.equals(bytes))
|
|
121053
119766
|
throw new Error("CONFIG_PREIMAGE_READBACK_MISMATCH");
|
|
121054
119767
|
return { sourcePath, relativePath, present: true, sha256: hash6(bytes), byteSize: bytes.byteLength };
|
|
@@ -121061,7 +119774,7 @@ function assertConfigUnchanged(configs) {
|
|
|
121061
119774
|
throw new Error("CONFIG_DRIFT_DURING_UPDATE");
|
|
121062
119775
|
if (present2) {
|
|
121063
119776
|
const stat3 = lstatSync44(config2.sourcePath);
|
|
121064
|
-
if (!stat3.isFile() || stat3.isSymbolicLink() || hash6(
|
|
119777
|
+
if (!stat3.isFile() || stat3.isSymbolicLink() || hash6(readFileSync43(config2.sourcePath)) !== config2.sha256) {
|
|
121065
119778
|
throw new Error("CONFIG_DRIFT_DURING_UPDATE");
|
|
121066
119779
|
}
|
|
121067
119780
|
}
|
|
@@ -121102,7 +119815,7 @@ async function npmReleaseAgeCapability(cwd2, env3) {
|
|
|
121102
119815
|
}
|
|
121103
119816
|
}
|
|
121104
119817
|
async function runNpm(args2, cwd2, home, staging, policy) {
|
|
121105
|
-
const userNpmrc =
|
|
119818
|
+
const userNpmrc = join71(staging, "npm-user.npmrc"), globalNpmrc = join71(staging, "npm-global.npmrc");
|
|
121106
119819
|
const baseConfig = `registry=${REGISTRY_ORIGIN}/
|
|
121107
119820
|
ignore-scripts=true
|
|
121108
119821
|
`;
|
|
@@ -121117,14 +119830,14 @@ ignore-scripts=true
|
|
|
121117
119830
|
CI: "1",
|
|
121118
119831
|
NPM_CONFIG_USERCONFIG: userNpmrc,
|
|
121119
119832
|
NPM_CONFIG_GLOBALCONFIG: globalNpmrc,
|
|
121120
|
-
NPM_CONFIG_CACHE:
|
|
119833
|
+
NPM_CONFIG_CACHE: join71(staging, "npm-cache"),
|
|
121121
119834
|
NPM_CONFIG_REGISTRY: `${REGISTRY_ORIGIN}/`,
|
|
121122
119835
|
NPM_CONFIG_IGNORE_SCRIPTS: "true"
|
|
121123
119836
|
};
|
|
121124
|
-
const capabilityNpmrc =
|
|
119837
|
+
const capabilityNpmrc = join71(staging, "npm-capability.npmrc");
|
|
121125
119838
|
if (policy && !existsSync34(capabilityNpmrc))
|
|
121126
119839
|
writeFileSync26(capabilityNpmrc, baseConfig, { mode: 384, flag: "wx" });
|
|
121127
|
-
if (policy &&
|
|
119840
|
+
if (policy && readFileSync43(capabilityNpmrc, "utf8") !== baseConfig)
|
|
121128
119841
|
throw new Error("NPM_ISOLATED_CONFIG_DRIFT");
|
|
121129
119842
|
const npmVersion = policy ? await npmReleaseAgeCapability(cwd2, { ...env3, NPM_CONFIG_USERCONFIG: capabilityNpmrc }) : undefined;
|
|
121130
119843
|
const config2 = baseConfig + (policy ? `min-release-age=${policy.minReleaseAge}
|
|
@@ -121132,7 +119845,7 @@ ${policy.minReleaseAgeExclude.map((pattern) => `min-release-age-exclude[]=${patt
|
|
|
121132
119845
|
`).join("")}` : "");
|
|
121133
119846
|
if (!existsSync34(userNpmrc))
|
|
121134
119847
|
writeFileSync26(userNpmrc, config2, { mode: 384, flag: "wx" });
|
|
121135
|
-
if (
|
|
119848
|
+
if (readFileSync43(userNpmrc, "utf8") !== config2 || readFileSync43(globalNpmrc, "utf8") !== baseConfig)
|
|
121136
119849
|
throw new Error("NPM_ISOLATED_CONFIG_DRIFT");
|
|
121137
119850
|
const proc = Bun.spawn(["npm", ...args2, "--registry", `${REGISTRY_ORIGIN}/`, "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
121138
119851
|
cwd: cwd2,
|
|
@@ -121146,8 +119859,58 @@ ${policy.minReleaseAgeExclude.map((pattern) => `min-release-age-exclude[]=${patt
|
|
|
121146
119859
|
throw new Error("DEPENDENCY_GRAPH_INSTALL_FAILED");
|
|
121147
119860
|
return npmVersion;
|
|
121148
119861
|
}
|
|
119862
|
+
async function carryInstalledSecrets(layout, stagePackage, home, stagePath, policy) {
|
|
119863
|
+
const runtimePath = dirname45(dirname45(dirname45(layout.currentPackageRoot)));
|
|
119864
|
+
const source = [join71(layout.currentPackageRoot, "node_modules", PRIVATE_OPTIONAL_PACKAGE), join71(runtimePath, "node_modules", PRIVATE_OPTIONAL_PACKAGE)].find((path) => entryExists(join71(path, "package.json")));
|
|
119865
|
+
if (!source)
|
|
119866
|
+
return null;
|
|
119867
|
+
assertNoSymlinkAncestors(source);
|
|
119868
|
+
const manifest = object11(JSON.parse(readFileSync43(join71(source, "package.json"), "utf8")), "CARRIED_SECRETS_MANIFEST_INVALID");
|
|
119869
|
+
if (manifest.name !== PRIVATE_OPTIONAL_PACKAGE || typeof manifest.version !== "string" || !STABLE_SEMVER_PATTERN.test(manifest.version))
|
|
119870
|
+
throw new Error("CARRIED_SECRETS_MANIFEST_INVALID");
|
|
119871
|
+
let verification2 = "unrecorded";
|
|
119872
|
+
if (entryExists(join71(runtimePath, "rollout-receipt.json"))) {
|
|
119873
|
+
const prior = object11(JSON.parse(readFileSync43(join71(runtimePath, "rollout-receipt.json"), "utf8")), "CARRIED_SECRETS_RUNTIME_DRIFT");
|
|
119874
|
+
if (readTree(join71(runtimePath, "node_modules"), true).digest !== prior.runtimeTreeSha256)
|
|
119875
|
+
throw new Error("CARRIED_SECRETS_RUNTIME_DRIFT");
|
|
119876
|
+
verification2 = "runtime-receipt";
|
|
119877
|
+
}
|
|
119878
|
+
if (entryExists(join71(runtimePath, "install-lock.json"))) {
|
|
119879
|
+
const lock = object11(JSON.parse(readFileSync43(join71(runtimePath, "install-lock.json"), "utf8")), "CARRIED_SECRETS_LOCK_MISMATCH");
|
|
119880
|
+
const locked2 = lock.packages?.[relative14(runtimePath, source).split(sep20).join("/")];
|
|
119881
|
+
if (locked2?.version !== manifest.version)
|
|
119882
|
+
throw new Error("CARRIED_SECRETS_LOCK_MISMATCH");
|
|
119883
|
+
}
|
|
119884
|
+
const carried = join71(stagePackage, "carried");
|
|
119885
|
+
mkdirSync28(carried, { mode: 448 });
|
|
119886
|
+
await runNpm(["pack", source, "--pack-destination", carried], stagePackage, home, stagePath, policy);
|
|
119887
|
+
const file2 = `hasna-secrets-${manifest.version}.tgz`;
|
|
119888
|
+
if (JSON.stringify(readdirSync26(carried)) !== JSON.stringify([file2]))
|
|
119889
|
+
throw new Error("CARRIED_SECRETS_PACK_FAILED");
|
|
119890
|
+
return { spec: `file:./carried/${file2}`, source, record: {
|
|
119891
|
+
version: manifest.version,
|
|
119892
|
+
tarballSha256: hash6(readFileSync43(join71(carried, file2))),
|
|
119893
|
+
packageTreeSha256: readTree(source, false, true).digest,
|
|
119894
|
+
verification: verification2
|
|
119895
|
+
} };
|
|
119896
|
+
}
|
|
119897
|
+
function unresolvedOptionalDependencies(lockBytes) {
|
|
119898
|
+
const lock = object11(JSON.parse(new TextDecoder().decode(lockBytes)), "INSTALL_LOCK_INVALID");
|
|
119899
|
+
const packages = object11(lock.packages, "INSTALL_LOCK_INVALID");
|
|
119900
|
+
const resolves = (from, name) => {
|
|
119901
|
+
for (let base3 = from;; ) {
|
|
119902
|
+
if (packages[base3 ? `${base3}/node_modules/${name}` : `node_modules/${name}`])
|
|
119903
|
+
return true;
|
|
119904
|
+
if (!base3)
|
|
119905
|
+
return false;
|
|
119906
|
+
const at2 = base3.lastIndexOf("node_modules/");
|
|
119907
|
+
base3 = at2 > 0 ? base3.slice(0, at2 - 1) : "";
|
|
119908
|
+
}
|
|
119909
|
+
};
|
|
119910
|
+
return Object.entries(packages).filter(([, entry]) => !entry.link).flatMap(([from, entry]) => Object.keys(entry.optionalDependencies ?? {}).filter((name) => !resolves(from, name)).map((name) => `${from} ${name}`)).sort();
|
|
119911
|
+
}
|
|
121149
119912
|
function comparePackageTrees(expectedRoot, installedRoot) {
|
|
121150
|
-
if (entryExists(
|
|
119913
|
+
if (entryExists(join71(expectedRoot, "node_modules")))
|
|
121151
119914
|
throw new Error("TARBALL_BUNDLED_DEPENDENCIES_UNSUPPORTED");
|
|
121152
119915
|
const expected = readTree(expectedRoot, false);
|
|
121153
119916
|
const installed = readTree(installedRoot, false, true);
|
|
@@ -121183,12 +119946,12 @@ function atomicJson(path, value) {
|
|
|
121183
119946
|
writeFileSync26(temp, bytes, { flag: "wx", mode: 384 });
|
|
121184
119947
|
chmodSync4(temp, 384);
|
|
121185
119948
|
renameSync17(temp, path);
|
|
121186
|
-
if (!
|
|
119949
|
+
if (!readFileSync43(path).equals(bytes))
|
|
121187
119950
|
throw new Error("RECEIPT_READBACK_MISMATCH");
|
|
121188
119951
|
}
|
|
121189
119952
|
function persistReceipt(path, value) {
|
|
121190
119953
|
if (entryExists(path)) {
|
|
121191
|
-
const priorBytes =
|
|
119954
|
+
const priorBytes = readFileSync43(path);
|
|
121192
119955
|
const prior = object11(JSON.parse(priorBytes.toString("utf8")), "RECEIPT_INVALID");
|
|
121193
119956
|
if (prior.id !== value.id || prior.schema !== "skills.copyfile-runtime-receipt.v1" || typeof prior.state !== "string" || !/^[a-z-]{1,32}$/.test(prior.state))
|
|
121194
119957
|
throw new Error("RECEIPT_ID_DRIFT");
|
|
@@ -121196,23 +119959,23 @@ function persistReceipt(path, value) {
|
|
|
121196
119959
|
`);
|
|
121197
119960
|
if (priorBytes.equals(nextBytes))
|
|
121198
119961
|
return;
|
|
121199
|
-
const historyRoot =
|
|
119962
|
+
const historyRoot = join71(dirname45(path), "receipt-history");
|
|
121200
119963
|
if (!entryExists(historyRoot))
|
|
121201
119964
|
mkdirSync28(historyRoot, { mode: 448 });
|
|
121202
119965
|
const historyStat = lstatSync44(historyRoot);
|
|
121203
119966
|
if (!historyStat.isDirectory() || historyStat.isSymbolicLink() || (historyStat.mode & 63) !== 0)
|
|
121204
119967
|
throw new Error("RECEIPT_HISTORY_NOT_PRIVATE");
|
|
121205
|
-
const historyPath =
|
|
119968
|
+
const historyPath = join71(historyRoot, `${value.id}-${String(prior.state)}-${randomUUID13()}.json`);
|
|
121206
119969
|
writeFileSync26(historyPath, priorBytes, { flag: "wx", mode: 256 });
|
|
121207
119970
|
chmodSync4(historyPath, 256);
|
|
121208
|
-
if (!
|
|
119971
|
+
if (!readFileSync43(historyPath).equals(priorBytes))
|
|
121209
119972
|
throw new Error("RECEIPT_HISTORY_READBACK_MISMATCH");
|
|
121210
119973
|
}
|
|
121211
119974
|
atomicJson(path, value);
|
|
121212
119975
|
}
|
|
121213
119976
|
function persistAliasReceipt(path, value) {
|
|
121214
119977
|
if (entryExists(path)) {
|
|
121215
|
-
const priorBytes =
|
|
119978
|
+
const priorBytes = readFileSync43(path);
|
|
121216
119979
|
const prior = object11(JSON.parse(priorBytes.toString("utf8")), "ALIAS_RECEIPT_INVALID");
|
|
121217
119980
|
if (prior.schema !== value.schema || prior.id !== value.id || typeof prior.state !== "string")
|
|
121218
119981
|
throw new Error("ALIAS_RECEIPT_DRIFT");
|
|
@@ -121220,16 +119983,16 @@ function persistAliasReceipt(path, value) {
|
|
|
121220
119983
|
`);
|
|
121221
119984
|
if (priorBytes.equals(nextBytes))
|
|
121222
119985
|
return;
|
|
121223
|
-
const history =
|
|
119986
|
+
const history = join71(dirname45(path), "history");
|
|
121224
119987
|
if (!entryExists(history))
|
|
121225
119988
|
mkdirSync28(history, { mode: 448 });
|
|
121226
119989
|
const historyStat = lstatSync44(history);
|
|
121227
119990
|
if (!historyStat.isDirectory() || historyStat.isSymbolicLink() || (historyStat.mode & 63) !== 0)
|
|
121228
119991
|
throw new Error("ALIAS_RECEIPT_HISTORY_UNSAFE");
|
|
121229
|
-
const saved =
|
|
119992
|
+
const saved = join71(history, `${value.id}-${String(prior.state)}-${randomUUID13()}.json`);
|
|
121230
119993
|
writeFileSync26(saved, priorBytes, { flag: "wx", mode: 256 });
|
|
121231
119994
|
chmodSync4(saved, 256);
|
|
121232
|
-
if (!
|
|
119995
|
+
if (!readFileSync43(saved).equals(priorBytes))
|
|
121233
119996
|
throw new Error("ALIAS_RECEIPT_HISTORY_READBACK_MISMATCH");
|
|
121234
119997
|
}
|
|
121235
119998
|
atomicJson(path, value);
|
|
@@ -121249,21 +120012,21 @@ function assertOwnedSafePath(path, home, requireWritable = false) {
|
|
|
121249
120012
|
function activeAliasRuntime(home, pathValue) {
|
|
121250
120013
|
const layout = resolveRuntimeLayout(home, pathValue);
|
|
121251
120014
|
const runtimePath = dirname45(dirname45(dirname45(layout.currentPackageRoot)));
|
|
121252
|
-
const receiptPath =
|
|
121253
|
-
const rollout = object11(JSON.parse(
|
|
121254
|
-
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 || hash6(
|
|
120015
|
+
const receiptPath = join71(runtimePath, "rollout-receipt.json");
|
|
120016
|
+
const rollout = object11(JSON.parse(readFileSync43(receiptPath, "utf8")), "ROLLOUT_RECEIPT_INVALID");
|
|
120017
|
+
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 || hash6(readFileSync43(join71(runtimePath, "verified-package.tgz"))) !== rollout.tarballSha256 || readTree(join71(runtimePath, "node_modules"), true).digest !== rollout.runtimeTreeSha256) {
|
|
121255
120018
|
throw new Error("ACTIVE_RUNTIME_RECEIPT_DRIFT");
|
|
121256
120019
|
}
|
|
121257
120020
|
verifyPreimageManifest(runtimePath, rollout, home);
|
|
121258
120021
|
return { layout, rollout };
|
|
121259
120022
|
}
|
|
121260
120023
|
function aliasDirs(home) {
|
|
121261
|
-
return [
|
|
120024
|
+
return [join71(home, ".local", "bin"), join71(home, ".bun", "bin"), "/opt/homebrew/bin", "/usr/local/bin"];
|
|
121262
120025
|
}
|
|
121263
120026
|
function aliasPackageLink(home, aliasPath) {
|
|
121264
120027
|
const dir = dirname45(aliasPath);
|
|
121265
|
-
if (dir ===
|
|
121266
|
-
return
|
|
120028
|
+
if (dir === join71(home, ".local", "bin"))
|
|
120029
|
+
return join71(home, ".local", "lib", "node_modules", "@hasna", "skills");
|
|
121267
120030
|
if (dir === "/opt/homebrew/bin")
|
|
121268
120031
|
return "/opt/homebrew/lib/node_modules/@hasna/skills";
|
|
121269
120032
|
if (dir === "/usr/local/bin")
|
|
@@ -121279,7 +120042,7 @@ function inspectAliasPackageChain(home, aliasPath, oldLinkTarget, oldTarget, bin
|
|
|
121279
120042
|
}
|
|
121280
120043
|
const packageLinkPath = aliasPackageLink(home, aliasPath);
|
|
121281
120044
|
const physicalPackage = dirname45(dirname45(oldTarget));
|
|
121282
|
-
if (!packageLinkPath || immediate !==
|
|
120045
|
+
if (!packageLinkPath || immediate !== join71(packageLinkPath, bin) || !isWithin(join71(home, ".hasna", "skills", "runtime"), physicalPackage)) {
|
|
121283
120046
|
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
121284
120047
|
}
|
|
121285
120048
|
assertOwnedSafePath(dirname45(packageLinkPath), home);
|
|
@@ -121302,19 +120065,19 @@ function inspectAliasPackageChain(home, aliasPath, oldLinkTarget, oldTarget, bin
|
|
|
121302
120065
|
}
|
|
121303
120066
|
function validateAliasSource(home, name, actual) {
|
|
121304
120067
|
const packageRoot = dirname45(dirname45(actual));
|
|
121305
|
-
const roots = [
|
|
120068
|
+
const roots = [join71(home, ".hasna", "skills", "runtime"), join71(home, ".bun", "install"), join71(home, ".local", "lib", "node_modules")];
|
|
121306
120069
|
if (basename23(packageRoot) !== "skills" || basename23(dirname45(packageRoot)) !== "@hasna" || !roots.some((root2) => isWithin(root2, packageRoot))) {
|
|
121307
120070
|
throw new Error("ALIAS_SOURCE_ROOT_UNSUPPORTED");
|
|
121308
120071
|
}
|
|
121309
120072
|
assertOwnedSafePath(packageRoot, home);
|
|
121310
|
-
const manifestPath =
|
|
120073
|
+
const manifestPath = join71(packageRoot, "package.json");
|
|
121311
120074
|
const manifestStat = lstatSync44(manifestPath), binaryStat = lstatSync44(actual);
|
|
121312
120075
|
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)
|
|
121313
120076
|
throw new Error("ALIAS_SOURCE_UNSAFE");
|
|
121314
120077
|
const pkg = readPackage(manifestPath);
|
|
121315
|
-
if (!pkg.bins[name] || actual !==
|
|
120078
|
+
if (!pkg.bins[name] || actual !== join71(packageRoot, pkg.bins[name]))
|
|
121316
120079
|
throw new Error("ALIAS_SOURCE_BIN_MISMATCH");
|
|
121317
|
-
return { version: pkg.version, packageRoot, binarySha256: hash6(
|
|
120080
|
+
return { version: pkg.version, packageRoot, binarySha256: hash6(readFileSync43(actual)) };
|
|
121318
120081
|
}
|
|
121319
120082
|
function adoptCopyfileAliases(options = {}) {
|
|
121320
120083
|
const home = realpathSync20(resolve38(options.homeDir ?? process.env.HOME ?? ""));
|
|
@@ -121322,12 +120085,12 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
121322
120085
|
const { layout, rollout } = activeAliasRuntime(home, pathValue);
|
|
121323
120086
|
const currentRuntime = dirname45(dirname45(dirname45(layout.currentPackageRoot)));
|
|
121324
120087
|
const launcherRuntime = pinnedLauncherRuntime();
|
|
121325
|
-
pinnedLauncherState(launcherRuntime, currentRuntime,
|
|
120088
|
+
pinnedLauncherState(launcherRuntime, currentRuntime, join71(layout.currentPackageRoot, layout.bin.skills ?? "bin/index.js"));
|
|
121326
120089
|
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
121327
120090
|
const id2 = randomUUID13();
|
|
121328
120091
|
const aliases = [];
|
|
121329
|
-
const receiptDir =
|
|
121330
|
-
const receiptPath =
|
|
120092
|
+
const receiptDir = join71(currentRuntime, "alias-adoptions");
|
|
120093
|
+
const receiptPath = join71(receiptDir, `${id2}.json`);
|
|
121331
120094
|
let receipt;
|
|
121332
120095
|
try {
|
|
121333
120096
|
const locked2 = activeAliasRuntime(home, pathValue);
|
|
@@ -121337,7 +120100,7 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
121337
120100
|
if (!entryExists(dir))
|
|
121338
120101
|
continue;
|
|
121339
120102
|
for (const [name, target] of Object.entries(layout.bin)) {
|
|
121340
|
-
const path =
|
|
120103
|
+
const path = join71(dir, name);
|
|
121341
120104
|
if (!entryExists(path))
|
|
121342
120105
|
continue;
|
|
121343
120106
|
assertOwnedSafePath(dir, home, true);
|
|
@@ -121354,7 +120117,7 @@ function adoptCopyfileAliases(options = {}) {
|
|
|
121354
120117
|
throw new Error("ALIAS_LINK_NOT_OWNED");
|
|
121355
120118
|
const actual = launcher.target;
|
|
121356
120119
|
const oldLinkTarget = launcher.kind === "symlink" ? launcher.linkTarget : actual;
|
|
121357
|
-
const newTarget =
|
|
120120
|
+
const newTarget = join71(layout.currentPackageRoot, target);
|
|
121358
120121
|
if (actual === newTarget && launcher.kind === "pinned" && launcher.format === "v3" && launcher.profile === launcherProfileForBin(name, target))
|
|
121359
120122
|
continue;
|
|
121360
120123
|
const chain = inspectAliasPackageChain(home, path, oldLinkTarget, actual, target);
|
|
@@ -121476,19 +120239,19 @@ function rollbackCopyfileAliases(receiptId, options = {}) {
|
|
|
121476
120239
|
const pathValue = options.pathValue ?? process.env.PATH ?? "";
|
|
121477
120240
|
const { layout, rollout } = activeAliasRuntime(home, pathValue);
|
|
121478
120241
|
const runtimePath = dirname45(dirname45(dirname45(layout.currentPackageRoot)));
|
|
121479
|
-
const receiptPath =
|
|
120242
|
+
const receiptPath = join71(runtimePath, "alias-adoptions", `${receiptId}.json`);
|
|
121480
120243
|
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
121481
120244
|
try {
|
|
121482
120245
|
const locked2 = activeAliasRuntime(home, pathValue);
|
|
121483
120246
|
if (locked2.layout.currentPackageRoot !== layout.currentPackageRoot || locked2.rollout.id !== rollout.id)
|
|
121484
120247
|
throw new Error("ACTIVE_RUNTIME_CHANGED_BEFORE_ALIAS_ROLLBACK");
|
|
121485
|
-
const receipt = object11(JSON.parse(
|
|
120248
|
+
const receipt = object11(JSON.parse(readFileSync43(receiptPath, "utf8")), "ALIAS_RECEIPT_INVALID");
|
|
121486
120249
|
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))
|
|
121487
120250
|
throw new Error("ALIAS_RECEIPT_BINDING_INVALID");
|
|
121488
120251
|
const seen = new Set;
|
|
121489
120252
|
for (const item of receipt.aliases) {
|
|
121490
120253
|
const name = basename23(item.path), target = layout.bin[name];
|
|
121491
|
-
if (!target || seen.has(item.path) || !aliasDirs(home).includes(dirname45(item.path)) || item.newTarget !==
|
|
120254
|
+
if (!target || seen.has(item.path) || !aliasDirs(home).includes(dirname45(item.path)) || item.newTarget !== join71(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")) {
|
|
121492
120255
|
throw new Error("ALIAS_RECEIPT_LAUNCHER_INVALID");
|
|
121493
120256
|
}
|
|
121494
120257
|
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, target, item.chain, true);
|
|
@@ -121564,13 +120327,13 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121564
120327
|
throw new Error("REVIEWED_LOCK_INPUT_PAIR_REQUIRED");
|
|
121565
120328
|
if (!policy)
|
|
121566
120329
|
throw new Error("REVIEWED_LOCK_RELEASE_AGE_REQUIRED");
|
|
121567
|
-
if (!
|
|
120330
|
+
if (!isAbsolute41(options.reviewedLock))
|
|
121568
120331
|
throw new Error("REVIEWED_LOCK_ABSOLUTE_PATH_REQUIRED");
|
|
121569
120332
|
assertNoSymlinkAncestors(dirname45(options.reviewedLock));
|
|
121570
120333
|
const stat3 = lstatSync44(options.reviewedLock);
|
|
121571
120334
|
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.nlink !== 1 || stat3.uid !== process.getuid?.() || stat3.mode & 63 || stat3.size > 8 * 1024 * 1024)
|
|
121572
120335
|
throw new Error("REVIEWED_LOCK_FILE_UNSAFE");
|
|
121573
|
-
reviewedLockBytes =
|
|
120336
|
+
reviewedLockBytes = readFileSync43(options.reviewedLock);
|
|
121574
120337
|
if (!/^[a-f0-9]{64}$/.test(options.reviewedLockSha256))
|
|
121575
120338
|
throw new Error("REVIEWED_LOCK_SHA256_INVALID");
|
|
121576
120339
|
if (hash6(reviewedLockBytes) !== options.reviewedLockSha256)
|
|
@@ -121592,11 +120355,11 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121592
120355
|
if ((runtimeRootStat.mode & 63) !== 0)
|
|
121593
120356
|
throw new Error("RUNTIME_ROOT_NOT_PRIVATE");
|
|
121594
120357
|
const id2 = randomUUID13();
|
|
121595
|
-
const stagePath =
|
|
121596
|
-
const finalPath =
|
|
120358
|
+
const stagePath = join71(layout.runtimeRoot, `.stage-${version2}-${id2}`);
|
|
120359
|
+
const finalPath = join71(layout.runtimeRoot, `${version2}-copyfile`);
|
|
121597
120360
|
const launcherRuntime = pinnedLauncherRuntime();
|
|
121598
120361
|
const launchers = layout.launchers.map((item) => {
|
|
121599
|
-
const newTarget =
|
|
120362
|
+
const newTarget = join71(finalPath, "node_modules", "@hasna", "skills", layout.bin[basename23(item.path)]);
|
|
121600
120363
|
return { ...item, newTarget, ...pinnedRecordFields(launcherRuntime, finalPath, newTarget, launcherProfileForBin(basename23(item.path), layout.bin[basename23(item.path)])) };
|
|
121601
120364
|
});
|
|
121602
120365
|
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
@@ -121613,50 +120376,71 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121613
120376
|
if (artifact.packageBins && JSON.stringify(Object.entries(artifact.packageBins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
|
|
121614
120377
|
throw new Error("PACKAGE_BIN_SET_CHANGED");
|
|
121615
120378
|
}
|
|
121616
|
-
const stagePackage =
|
|
120379
|
+
const stagePackage = join71(stagePath, "install");
|
|
121617
120380
|
mkdirSync28(stagePackage, { mode: 448 });
|
|
121618
|
-
copyFileSync(artifact.tarballPath,
|
|
121619
|
-
|
|
120381
|
+
copyFileSync(artifact.tarballPath, join71(stagePackage, "verified.tgz"));
|
|
120382
|
+
const carriedSecrets = await carryInstalledSecrets(layout, stagePackage, home, stagePath, policy);
|
|
120383
|
+
if (carriedSecrets && reviewedLockBytes)
|
|
120384
|
+
throw new Error("REVIEWED_LOCK_CANNOT_CARRY_SECRETS");
|
|
120385
|
+
writeFileSync26(join71(stagePackage, "package.json"), JSON.stringify({
|
|
120386
|
+
name: "skills-runtime-install",
|
|
120387
|
+
version: "0.0.0",
|
|
120388
|
+
private: true,
|
|
120389
|
+
dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" },
|
|
120390
|
+
...carriedSecrets ? { overrides: { [PRIVATE_OPTIONAL_PACKAGE]: carriedSecrets.spec } } : {}
|
|
120391
|
+
}, null, 2), { mode: 384, flag: "wx" });
|
|
121620
120392
|
let npmVersion;
|
|
121621
120393
|
if (reviewedLockBytes) {
|
|
121622
120394
|
await validateReviewedRuntimeLock(reviewedLockBytes, options.reviewedLockSha256, {
|
|
121623
120395
|
version: version2,
|
|
121624
120396
|
archiveIntegrity: artifact.integrity,
|
|
121625
|
-
packageManifest: object11(JSON.parse(
|
|
120397
|
+
packageManifest: object11(JSON.parse(readFileSync43(join71(stagePath, "verified-archive", "package", "package.json"), "utf8")), "PACKAGE_MANIFEST_INVALID"),
|
|
121626
120398
|
registryOrigin: REGISTRY_ORIGIN,
|
|
121627
120399
|
...policy,
|
|
121628
120400
|
fetcher
|
|
121629
120401
|
});
|
|
121630
|
-
writeFileSync26(
|
|
121631
|
-
if (!
|
|
120402
|
+
writeFileSync26(join71(stagePackage, "package-lock.json"), reviewedLockBytes, { mode: 384, flag: "wx" });
|
|
120403
|
+
if (!readFileSync43(join71(stagePackage, "package-lock.json")).equals(reviewedLockBytes))
|
|
121632
120404
|
throw new Error("REVIEWED_LOCK_COPY_MISMATCH");
|
|
121633
120405
|
} else {
|
|
121634
120406
|
npmVersion = await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
121635
120407
|
}
|
|
121636
|
-
const
|
|
120408
|
+
const plannedLock = readFileSync43(join71(stagePackage, "package-lock.json"));
|
|
120409
|
+
const refusedOptional = unresolvedOptionalDependencies(plannedLock);
|
|
120410
|
+
if (refusedOptional.some((item) => item !== `node_modules/${PACKAGE_NAME} ${PRIVATE_OPTIONAL_PACKAGE}`))
|
|
120411
|
+
throw new Error("DEPENDENCY_GRAPH_OPTIONAL_REFUSED");
|
|
120412
|
+
const ciNpmVersion = await runNpm([refusedOptional.length ? "install" : "ci", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
121637
120413
|
if (!reviewedLockBytes && npmVersion !== ciNpmVersion)
|
|
121638
120414
|
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
120415
|
+
if (!readFileSync43(join71(stagePackage, "package-lock.json")).equals(plannedLock))
|
|
120416
|
+
throw new Error(reviewedLockBytes ? "REVIEWED_LOCK_DRIFT_DURING_INSTALL" : "INSTALL_LOCK_DRIFT_DURING_INSTALL");
|
|
121639
120417
|
npmVersion = ciNpmVersion;
|
|
121640
|
-
const lockBytes =
|
|
120418
|
+
const lockBytes = readFileSync43(join71(stagePackage, "package-lock.json"));
|
|
121641
120419
|
if (reviewedLockBytes) {
|
|
121642
120420
|
if (!lockBytes.equals(reviewedLockBytes))
|
|
121643
120421
|
throw new Error("REVIEWED_LOCK_DRIFT_DURING_INSTALL");
|
|
121644
120422
|
const treeNpmVersion = await runNpm(["sbom", "--sbom-format=cyclonedx", "--package-lock-only=false", "--omit=dev", "--prefix", stagePackage], stagePackage, home, stagePath, policy);
|
|
121645
120423
|
if (treeNpmVersion !== npmVersion)
|
|
121646
120424
|
throw new Error("NPM_VERSION_DRIFT_DURING_UPDATE");
|
|
121647
|
-
if (!
|
|
120425
|
+
if (!readFileSync43(join71(stagePackage, "package-lock.json")).equals(reviewedLockBytes))
|
|
121648
120426
|
throw new Error("REVIEWED_LOCK_DRIFT_DURING_INSTALL");
|
|
121649
120427
|
}
|
|
121650
|
-
const installedPackage =
|
|
121651
|
-
const
|
|
120428
|
+
const installedPackage = join71(stagePackage, "node_modules", "@hasna", "skills");
|
|
120429
|
+
const installedSecrets = [join71(installedPackage, "node_modules", PRIVATE_OPTIONAL_PACKAGE), join71(stagePackage, "node_modules", PRIVATE_OPTIONAL_PACKAGE)].find((path) => entryExists(join71(path, "package.json")));
|
|
120430
|
+
if (carriedSecrets) {
|
|
120431
|
+
if (!installedSecrets || object11(JSON.parse(readFileSync43(join71(installedSecrets, "package.json"), "utf8")), "CARRIED_SECRETS_INSTALL_MISMATCH").version !== carriedSecrets.record.version || readTree(installedSecrets, false, true).digest !== carriedSecrets.record.packageTreeSha256)
|
|
120432
|
+
throw new Error("CARRIED_SECRETS_INSTALL_MISMATCH");
|
|
120433
|
+
} else if (installedSecrets && refusedOptional.length)
|
|
120434
|
+
throw new Error("PRIVATE_OPTIONAL_PACKAGE_UNEXPECTED");
|
|
120435
|
+
const installed = readPackage(join71(installedPackage, "package.json"));
|
|
121652
120436
|
if (installed.version !== version2 || JSON.stringify(Object.entries(installed.bins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
|
|
121653
120437
|
throw new Error("INSTALLED_PACKAGE_IDENTITY_MISMATCH");
|
|
121654
120438
|
}
|
|
121655
|
-
const unpackedPackage =
|
|
120439
|
+
const unpackedPackage = join71(stagePath, "verified-archive", "package");
|
|
121656
120440
|
const packageTreeSha256 = comparePackageTrees(unpackedPackage, installedPackage);
|
|
121657
120441
|
if (packageTreeSha256 !== artifact.packageTreeSha256)
|
|
121658
120442
|
throw new Error("PACKAGE_TREE_HASH_MISMATCH");
|
|
121659
|
-
const nodeModules =
|
|
120443
|
+
const nodeModules = join71(stagePackage, "node_modules");
|
|
121660
120444
|
normalizeModes(nodeModules);
|
|
121661
120445
|
chmodSync4(stagePath, 448);
|
|
121662
120446
|
const runtimeTree = readTree(nodeModules, true);
|
|
@@ -121664,7 +120448,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121664
120448
|
throw new Error("RUNTIME_FILE_MODE_UNSAFE");
|
|
121665
120449
|
}
|
|
121666
120450
|
assertConfigUnchanged(configPreimages);
|
|
121667
|
-
const targetPackageRoot =
|
|
120451
|
+
const targetPackageRoot = join71(finalPath, "node_modules", "@hasna", "skills");
|
|
121668
120452
|
const preimageSha256 = writePreimageManifest(stagePath, configPreimages, launchers);
|
|
121669
120453
|
const receipt = {
|
|
121670
120454
|
schema: "skills.copyfile-runtime-receipt.v1",
|
|
@@ -121688,36 +120472,38 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121688
120472
|
preimageSha256,
|
|
121689
120473
|
...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {},
|
|
121690
120474
|
...reviewedLockBytes ? { reviewedLockSha256: options.reviewedLockSha256 } : {},
|
|
120475
|
+
...carriedSecrets ? { carriedSecrets: carriedSecrets.record } : {},
|
|
120476
|
+
...refusedOptional.length ? { refusedOptionalPackages: [PRIVATE_OPTIONAL_PACKAGE] } : {},
|
|
121691
120477
|
launchers,
|
|
121692
120478
|
switchedLaunchers: [],
|
|
121693
120479
|
rollbackCompletedLaunchers: []
|
|
121694
120480
|
};
|
|
121695
|
-
writeJsonPrivate(
|
|
120481
|
+
writeJsonPrivate(join71(stagePath, "rollout-receipt.json"), receipt);
|
|
121696
120482
|
try {
|
|
121697
120483
|
receipt.prerequisites = verifyTargetPrerequisites(installed.data, installedPackage, home, resolve38(options.cwd ?? process.env[LAUNCH_CWD_VARIABLE3] ?? process.cwd()), options.pathValue ?? process.env.PATH ?? "");
|
|
121698
120484
|
} catch (error61) {
|
|
121699
120485
|
const code2 = error61 instanceof Error && /^RUNTIME_PREREQUISITE_[A-Z_]+$/.test(error61.message) ? error61.message : "RUNTIME_PREREQUISITE_CHECK_UNAVAILABLE";
|
|
121700
120486
|
receipt.prerequisites = { status: "refused", code: code2 };
|
|
121701
|
-
persistReceipt(
|
|
120487
|
+
persistReceipt(join71(stagePath, "rollout-receipt.json"), receipt);
|
|
121702
120488
|
throw new Error(code2);
|
|
121703
120489
|
}
|
|
121704
|
-
persistReceipt(
|
|
121705
|
-
const finalNodeModules =
|
|
121706
|
-
const stagedNodeModules =
|
|
120490
|
+
persistReceipt(join71(stagePath, "rollout-receipt.json"), receipt);
|
|
120491
|
+
const finalNodeModules = join71(stagePackage, "node_modules");
|
|
120492
|
+
const stagedNodeModules = join71(stagePath, "node_modules");
|
|
121707
120493
|
renameSync17(finalNodeModules, stagedNodeModules);
|
|
121708
|
-
copyFileSync(
|
|
121709
|
-
chmodSync4(
|
|
120494
|
+
copyFileSync(join71(stagePackage, "package-lock.json"), join71(stagePath, "install-lock.json"));
|
|
120495
|
+
chmodSync4(join71(stagePath, "install-lock.json"), 384);
|
|
121710
120496
|
const stagedTree = readTree(stagedNodeModules, true);
|
|
121711
120497
|
if (stagedTree.digest !== runtimeTree.digest)
|
|
121712
120498
|
throw new Error("STAGED_RUNTIME_READBACK_MISMATCH");
|
|
121713
|
-
receipt.targetPackageRoot =
|
|
120499
|
+
receipt.targetPackageRoot = join71(finalPath, "node_modules", "@hasna", "skills");
|
|
121714
120500
|
receipt.runtimeTreeSha256 = stagedTree.digest;
|
|
121715
|
-
persistReceipt(
|
|
120501
|
+
persistReceipt(join71(stagePath, "rollout-receipt.json"), receipt);
|
|
121716
120502
|
renameSync17(stagePath, finalPath);
|
|
121717
120503
|
moved = true;
|
|
121718
120504
|
const finalReceiptBytes = Buffer.from(`${JSON.stringify(receipt, null, 2)}
|
|
121719
120505
|
`);
|
|
121720
|
-
if (!
|
|
120506
|
+
if (!readFileSync43(join71(finalPath, "rollout-receipt.json")).equals(finalReceiptBytes))
|
|
121721
120507
|
throw new Error("RECEIPT_MOVE_READBACK_MISMATCH");
|
|
121722
120508
|
assertConfigUnchanged(configPreimages);
|
|
121723
120509
|
for (const item of launchers) {
|
|
@@ -121725,7 +120511,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121725
120511
|
throw new Error("LAUNCHER_DRIFT_BEFORE_SWITCH");
|
|
121726
120512
|
receipt.state = "switching";
|
|
121727
120513
|
receipt.switchedLaunchers = [...receipt.switchedLaunchers, item.path];
|
|
121728
|
-
persistReceipt(
|
|
120514
|
+
persistReceipt(join71(finalPath, "rollout-receipt.json"), receipt);
|
|
121729
120515
|
replaceLauncher(item);
|
|
121730
120516
|
if (!launcherIs(item.path, newLauncherState(item)) || !launcherIs(item.backupPath, oldLauncherState(item))) {
|
|
121731
120517
|
throw new Error("LAUNCHER_SWITCH_READBACK_MISMATCH");
|
|
@@ -121734,7 +120520,7 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121734
120520
|
options.onLauncherSwitched?.(item.path);
|
|
121735
120521
|
}
|
|
121736
120522
|
receipt.state = "switched";
|
|
121737
|
-
persistReceipt(
|
|
120523
|
+
persistReceipt(join71(finalPath, "rollout-receipt.json"), receipt);
|
|
121738
120524
|
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, prerequisites: receipt.prerequisites, ...policy ? { dependencyPolicy: { ...policy, npmVersion } } : {}, ...reviewedLockBytes ? { reviewedLockSha256: options.reviewedLockSha256 } : {} };
|
|
121739
120525
|
} catch (error61) {
|
|
121740
120526
|
if (switched.length > 0 || moved) {
|
|
@@ -121757,8 +120543,8 @@ async function updateCopyfileRuntime(version2, options = {}) {
|
|
|
121757
120543
|
}
|
|
121758
120544
|
}
|
|
121759
120545
|
try {
|
|
121760
|
-
const receiptPath =
|
|
121761
|
-
const receipt = JSON.parse(
|
|
120546
|
+
const receiptPath = join71(finalPath, "rollout-receipt.json");
|
|
120547
|
+
const receipt = JSON.parse(readFileSync43(receiptPath, "utf8"));
|
|
121762
120548
|
receipt.state = rollbackFailed ? "rollback-required" : "rolled-back";
|
|
121763
120549
|
persistReceipt(receiptPath, receipt);
|
|
121764
120550
|
} catch {
|
|
@@ -121777,7 +120563,7 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
|
|
|
121777
120563
|
const home = resolve38(options.homeDir ?? process.env.HOME ?? "");
|
|
121778
120564
|
if (!home || !existsSync34(home))
|
|
121779
120565
|
throw new Error("HOME_NOT_FOUND");
|
|
121780
|
-
const runtimeRoot =
|
|
120566
|
+
const runtimeRoot = join71(realpathSync20(home), ".hasna", "skills", "runtime");
|
|
121781
120567
|
assertNoSymlinkAncestors(runtimeRoot);
|
|
121782
120568
|
const rootStat = lstatSync44(runtimeRoot);
|
|
121783
120569
|
if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || (rootStat.mode & 63) !== 0)
|
|
@@ -121787,40 +120573,40 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
|
|
|
121787
120573
|
const matches2 = readdirSync26(runtimeRoot).filter((name) => {
|
|
121788
120574
|
if (!/^\d+\.\d+\.\d+-copyfile$/.test(name))
|
|
121789
120575
|
return false;
|
|
121790
|
-
const receipt2 =
|
|
120576
|
+
const receipt2 = join71(runtimeRoot, name, "rollout-receipt.json");
|
|
121791
120577
|
if (!existsSync34(receipt2))
|
|
121792
120578
|
return false;
|
|
121793
120579
|
try {
|
|
121794
|
-
return JSON.parse(
|
|
120580
|
+
return JSON.parse(readFileSync43(receipt2, "utf8")).id === receiptId;
|
|
121795
120581
|
} catch {
|
|
121796
120582
|
return false;
|
|
121797
120583
|
}
|
|
121798
120584
|
});
|
|
121799
120585
|
if (matches2.length !== 1)
|
|
121800
120586
|
throw new Error("RECEIPT_NOT_FOUND_OR_AMBIGUOUS");
|
|
121801
|
-
const receiptPath =
|
|
121802
|
-
const receipt = object11(JSON.parse(
|
|
120587
|
+
const receiptPath = join71(runtimeRoot, matches2[0], "rollout-receipt.json");
|
|
120588
|
+
const receipt = object11(JSON.parse(readFileSync43(receiptPath, "utf8")), "RECEIPT_INVALID");
|
|
121803
120589
|
if (receipt.schema !== "skills.copyfile-runtime-receipt.v1" || !["switched", "switching", "rollback-required"].includes(receipt.state) || receipt.runtimeRoot !== runtimeRoot) {
|
|
121804
120590
|
throw new Error("RECEIPT_STATE_INVALID");
|
|
121805
120591
|
}
|
|
121806
120592
|
if (receipt.package !== PACKAGE_NAME || matches2[0] !== `${receipt.targetVersion}-copyfile` || !new RegExp(SEMVER_PATTERN).test(receipt.currentVersion) || !new RegExp(SEMVER_PATTERN).test(receipt.targetVersion))
|
|
121807
120593
|
throw new Error("RECEIPT_BINDING_INVALID");
|
|
121808
|
-
const runtimePath =
|
|
121809
|
-
if (receipt.targetPackageRoot !==
|
|
120594
|
+
const runtimePath = join71(runtimeRoot, matches2[0]);
|
|
120595
|
+
if (receipt.targetPackageRoot !== join71(runtimePath, "node_modules", "@hasna", "skills") || receipt.currentPackageRoot !== join71(runtimeRoot, `${receipt.currentVersion}-copyfile`, "node_modules", "@hasna", "skills"))
|
|
121810
120596
|
throw new Error("RECEIPT_RUNTIME_BINDING_INVALID");
|
|
121811
|
-
if (readTree(
|
|
120597
|
+
if (readTree(join71(runtimePath, "node_modules"), true).digest !== receipt.runtimeTreeSha256) {
|
|
121812
120598
|
throw new Error("ACTIVE_RUNTIME_TREE_DRIFT");
|
|
121813
120599
|
}
|
|
121814
120600
|
verifyPreimageManifest(runtimePath, receipt, home);
|
|
121815
|
-
const oldBins = readPackage(
|
|
121816
|
-
const newPackage = readPackage(
|
|
120601
|
+
const oldBins = readPackage(join71(receipt.currentPackageRoot, "package.json")).bins;
|
|
120602
|
+
const newPackage = readPackage(join71(receipt.targetPackageRoot, "package.json"));
|
|
121817
120603
|
if (newPackage.version !== receipt.targetVersion || JSON.stringify(Object.entries(oldBins).sort()) !== JSON.stringify(Object.entries(newPackage.bins).sort()))
|
|
121818
120604
|
throw new Error("RECEIPT_BIN_BINDING_INVALID");
|
|
121819
120605
|
const switched = [];
|
|
121820
120606
|
const seen = new Set;
|
|
121821
120607
|
for (const item of receipt.launchers) {
|
|
121822
120608
|
const bin = oldBins[basename23(item.path)];
|
|
121823
|
-
if (!bin || !
|
|
120609
|
+
if (!bin || !isAbsolute41(item.path) || seen.has(item.path) || item.oldTarget !== join71(receipt.currentPackageRoot, bin) || item.newTarget !== join71(receipt.targetPackageRoot, bin) || !/^.+\.skills-prev-[0-9a-f-]{36}$/.test(item.backupPath) || !item.backupPath.startsWith(`${item.path}.skills-prev-`) || typeof item.oldLinkTarget !== "string" || resolve38(dirname45(item.path), item.oldLinkTarget) !== item.oldTarget)
|
|
121824
120610
|
throw new Error("RECEIPT_LAUNCHER_BINDING_INVALID");
|
|
121825
120611
|
assertNoSymlinkAncestors(dirname45(item.path));
|
|
121826
120612
|
assertLauncherRecordShapes(item, launcherProfileForBin(basename23(item.path), bin));
|
|
@@ -121865,7 +120651,7 @@ function rollbackCopyfileRuntime(receiptId, options = {}) {
|
|
|
121865
120651
|
releaseLock();
|
|
121866
120652
|
}
|
|
121867
120653
|
}
|
|
121868
|
-
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;
|
|
120654
|
+
var PACKAGE_NAME = "@hasna/skills", PRIVATE_OPTIONAL_PACKAGE = "@hasna/secrets", REGISTRY_ORIGIN = "https://registry.npmjs.org", STABLE_SEMVER_PATTERN, MAX_TARBALL_BYTES, MAX_TARBALL_EXPANDED_BYTES, MAX_TARBALL_ENTRIES = 20000, CONFIG_PREIMAGE_PATHS;
|
|
121869
120655
|
var init_runtime_copyfile = __esm(() => {
|
|
121870
120656
|
init_skill_contract();
|
|
121871
120657
|
init_reviewed_runtime_lock();
|
|
@@ -121888,8 +120674,8 @@ __export(exports_runtime, {
|
|
|
121888
120674
|
registerRuntime: () => registerRuntime
|
|
121889
120675
|
});
|
|
121890
120676
|
import { createHash as createHash46, randomUUID as randomUUID14 } from "crypto";
|
|
121891
|
-
import { lstatSync as lstatSync45, mkdirSync as mkdirSync29, readFileSync as
|
|
121892
|
-
import { basename as basename24, isAbsolute as
|
|
120677
|
+
import { lstatSync as lstatSync45, mkdirSync as mkdirSync29, readFileSync as readFileSync44, writeFileSync as writeFileSync27 } from "fs";
|
|
120678
|
+
import { basename as basename24, isAbsolute as isAbsolute42, join as join72 } from "path";
|
|
121893
120679
|
import { createInterface } from "readline";
|
|
121894
120680
|
function registerRuntime(parent) {
|
|
121895
120681
|
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
|
|
@@ -122092,7 +120878,7 @@ Updating ${name}...
|
|
|
122092
120878
|
async function readUpdatedVersion(bunExecutable) {
|
|
122093
120879
|
const output2 = await readUpdateCommand([bunExecutable, "pm", "bin", "-g"]);
|
|
122094
120880
|
const globalBin = output2.replace(/\r?\n$/, "");
|
|
122095
|
-
if (!globalBin || !
|
|
120881
|
+
if (!globalBin || !isAbsolute42(globalBin) || /[\r\n\0]/.test(globalBin))
|
|
122096
120882
|
throw new Error("Update bin discovery failed");
|
|
122097
120883
|
const installed = Bun.which("skills", { PATH: globalBin, cwd: globalBin });
|
|
122098
120884
|
const selected = Bun.which("skills");
|
|
@@ -122431,7 +121217,7 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
|
|
|
122431
121217
|
const info = lstatSync45(path);
|
|
122432
121218
|
if (!info.isFile() || info.size > 20 * 1024 * 1024)
|
|
122433
121219
|
throw new Error("Input must be a regular file no larger than 20 MiB");
|
|
122434
|
-
return { name: basename24(path), bytes: new Uint8Array(
|
|
121220
|
+
return { name: basename24(path), bytes: new Uint8Array(readFileSync44(path)) };
|
|
122435
121221
|
});
|
|
122436
121222
|
describeRemoteFiles(inputFiles);
|
|
122437
121223
|
client2 = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
|
|
@@ -122817,7 +121603,7 @@ async function handleExportsDownload(runId2, options) {
|
|
|
122817
121603
|
continue;
|
|
122818
121604
|
const verified = await client2.getVerifiedRunArtifact(runId2, artifactId);
|
|
122819
121605
|
const relativePath = safeArtifactRelativePath(typeof artifact.relativePath === "string" ? artifact.relativePath : artifact.fileName, String(artifact.fileName || artifactId));
|
|
122820
|
-
const outputPath =
|
|
121606
|
+
const outputPath = join72(exportDir, relativePath);
|
|
122821
121607
|
ensureSafeExportParent(exportDir, relativePath);
|
|
122822
121608
|
writeFileSync27(outputPath, verified.bytes, { flag: "wx", mode: 384 });
|
|
122823
121609
|
downloaded.push({ id: artifactId, path: outputPath, byteSize: verified.byteSize });
|
|
@@ -122856,7 +121642,7 @@ function ensureSafeExportParent(root2, relativePath) {
|
|
|
122856
121642
|
let parent = root2;
|
|
122857
121643
|
for (const part of ["", ...parts]) {
|
|
122858
121644
|
if (part)
|
|
122859
|
-
parent =
|
|
121645
|
+
parent = join72(parent, part);
|
|
122860
121646
|
try {
|
|
122861
121647
|
if (!lstatSync45(parent).isDirectory() || lstatSync45(parent).isSymbolicLink())
|
|
122862
121648
|
throw new Error("Unsafe artifact directory");
|
|
@@ -123153,12 +121939,12 @@ var exports_recurring = {};
|
|
|
123153
121939
|
__export(exports_recurring, {
|
|
123154
121940
|
registerRecurringCommands: () => registerRecurringCommands
|
|
123155
121941
|
});
|
|
123156
|
-
import { constants as constants31, closeSync as
|
|
121942
|
+
import { constants as constants31, closeSync as closeSync31, fstatSync as fstatSync31, lstatSync as lstatSync46, openSync as openSync31, readSync as readSync24 } from "fs";
|
|
123157
121943
|
import { createInterface as createInterface2 } from "readline";
|
|
123158
121944
|
function readRequest(path) {
|
|
123159
|
-
const fd =
|
|
121945
|
+
const fd = openSync31(path, constants31.O_RDONLY | constants31.O_NOFOLLOW | constants31.O_NONBLOCK);
|
|
123160
121946
|
try {
|
|
123161
|
-
const before2 =
|
|
121947
|
+
const before2 = fstatSync31(fd);
|
|
123162
121948
|
if (!before2.isFile() || before2.size > 1048576 || before2.size < 1)
|
|
123163
121949
|
throw new RecurringInputError;
|
|
123164
121950
|
const bytes = Buffer.alloc(before2.size + 1);
|
|
@@ -123169,14 +121955,14 @@ function readRequest(path) {
|
|
|
123169
121955
|
break;
|
|
123170
121956
|
length += n2;
|
|
123171
121957
|
}
|
|
123172
|
-
const after2 =
|
|
121958
|
+
const after2 = fstatSync31(fd), current = lstatSync46(path);
|
|
123173
121959
|
if (length !== before2.size || after2.size !== before2.size || before2.mtimeMs !== after2.mtimeMs || before2.ctimeMs !== after2.ctimeMs || current.dev !== before2.dev || current.ino !== before2.ino || !current.isFile() || current.isSymbolicLink())
|
|
123174
121960
|
throw new RecurringInputError;
|
|
123175
121961
|
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length)));
|
|
123176
121962
|
} catch {
|
|
123177
121963
|
throw new RecurringInputError;
|
|
123178
121964
|
} finally {
|
|
123179
|
-
|
|
121965
|
+
closeSync31(fd);
|
|
123180
121966
|
}
|
|
123181
121967
|
}
|
|
123182
121968
|
async function acceptAtTerminal(draft) {
|
|
@@ -123455,7 +122241,7 @@ var init_completion = __esm(() => {
|
|
|
123455
122241
|
// src/lib/portable-snapshot-filter.ts
|
|
123456
122242
|
import { readdirSync as readdirSync27, statSync as statSync23 } from "fs";
|
|
123457
122243
|
import { homedir as homedir12 } from "os";
|
|
123458
|
-
import { join as
|
|
122244
|
+
import { join as join73, sep as sep21 } from "path";
|
|
123459
122245
|
function isExcludedSkillFileName(fileName) {
|
|
123460
122246
|
if (EXCLUDE_FILE_NAMES.has(fileName)) {
|
|
123461
122247
|
return true;
|
|
@@ -123478,16 +122264,16 @@ function isPortableWithinSkill(relativeParts) {
|
|
|
123478
122264
|
function homePathFor(definition, homesRoot) {
|
|
123479
122265
|
const home = homesRoot ?? homedir12();
|
|
123480
122266
|
if (definition.subClass === "skills" || definition.subClass === "custom") {
|
|
123481
|
-
return
|
|
122267
|
+
return join73(skillsDataRootForHome(home), definition.name);
|
|
123482
122268
|
}
|
|
123483
122269
|
if (definition.agent === "opencode") {
|
|
123484
|
-
return
|
|
122270
|
+
return join73(home, ".config", "opencode", "skills");
|
|
123485
122271
|
}
|
|
123486
|
-
return
|
|
122272
|
+
return join73(home, `.${definition.agent}`, "skills");
|
|
123487
122273
|
}
|
|
123488
122274
|
function destinationFor(definition, stationId, relativePath) {
|
|
123489
|
-
const category = definition.subClass === "agent-homes" ?
|
|
123490
|
-
return
|
|
122275
|
+
const category = definition.subClass === "agent-homes" ? join73("agent-homes", definition.agent ?? "") : definition.name;
|
|
122276
|
+
return join73("resources", stationId, "skills", category, ...relativePath.split(sep21));
|
|
123491
122277
|
}
|
|
123492
122278
|
function walkEntries(absoluteRoot) {
|
|
123493
122279
|
let entries;
|
|
@@ -123498,7 +122284,7 @@ function walkEntries(absoluteRoot) {
|
|
|
123498
122284
|
}
|
|
123499
122285
|
const output2 = [];
|
|
123500
122286
|
for (const entry of entries) {
|
|
123501
|
-
const childFull =
|
|
122287
|
+
const childFull = join73(absoluteRoot, entry.name);
|
|
123502
122288
|
if (entry.isSymbolicLink()) {
|
|
123503
122289
|
output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
|
|
123504
122290
|
continue;
|
|
@@ -123509,7 +122295,7 @@ function walkEntries(absoluteRoot) {
|
|
|
123509
122295
|
}
|
|
123510
122296
|
const nested = walkEntries(childFull);
|
|
123511
122297
|
for (const item of nested) {
|
|
123512
|
-
output2.push({ ...item, relativePath:
|
|
122298
|
+
output2.push({ ...item, relativePath: join73(entry.name, item.relativePath) });
|
|
123513
122299
|
}
|
|
123514
122300
|
continue;
|
|
123515
122301
|
}
|
|
@@ -123606,18 +122392,18 @@ import { createHash as createHash47 } from "crypto";
|
|
|
123606
122392
|
import {
|
|
123607
122393
|
copyFileSync as copyFileSync2,
|
|
123608
122394
|
mkdirSync as mkdirSync30,
|
|
123609
|
-
readFileSync as
|
|
122395
|
+
readFileSync as readFileSync45,
|
|
123610
122396
|
statSync as statSync24,
|
|
123611
122397
|
writeFileSync as writeFileSync28
|
|
123612
122398
|
} from "fs";
|
|
123613
|
-
import { dirname as dirname47, isAbsolute as
|
|
122399
|
+
import { dirname as dirname47, isAbsolute as isAbsolute43, relative as relative15, resolve as resolve39, sep as sep22 } from "path";
|
|
123614
122400
|
function validateStationId(stationId) {
|
|
123615
122401
|
if (!/^[a-z0-9-]+$/.test(stationId)) {
|
|
123616
122402
|
throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
|
|
123617
122403
|
}
|
|
123618
122404
|
}
|
|
123619
122405
|
function sha256File(filePath) {
|
|
123620
|
-
return createHash47("sha256").update(
|
|
122406
|
+
return createHash47("sha256").update(readFileSync45(filePath)).digest("hex");
|
|
123621
122407
|
}
|
|
123622
122408
|
function scanHome(definition, homesRoot) {
|
|
123623
122409
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -123716,7 +122502,7 @@ function writeStationSnapshot(options) {
|
|
|
123716
122502
|
for (const plan of plans) {
|
|
123717
122503
|
const destination = resolve39(repoRoot, plan.destination);
|
|
123718
122504
|
const destinationRelative = relative15(repoRoot, destination);
|
|
123719
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep22) ||
|
|
122505
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep22) || isAbsolute43(destinationRelative)) {
|
|
123720
122506
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
123721
122507
|
}
|
|
123722
122508
|
let existingDigest = null;
|
|
@@ -123790,7 +122576,7 @@ __export(exports_create_sync_config, {
|
|
|
123790
122576
|
registerCreateSync: () => registerCreateSync
|
|
123791
122577
|
});
|
|
123792
122578
|
import { existsSync as existsSync35 } from "fs";
|
|
123793
|
-
import { join as
|
|
122579
|
+
import { join as join74 } from "path";
|
|
123794
122580
|
function registerCreateSync(parent) {
|
|
123795
122581
|
const configCmd = parent.command("config").description("Manage skills configuration");
|
|
123796
122582
|
configCmd.command("show", { isDefault: true }).option("--json", "Output as JSON", false).description("Show current merged configuration").action((options) => {
|
|
@@ -123900,7 +122686,7 @@ function handleCreate(name, options) {
|
|
|
123900
122686
|
console.log(source_default.green(`\u2713 Created custom skill '${result2.name}' at ${result2.path}`));
|
|
123901
122687
|
console.log(source_default.dim(` Category: ${result2.manifest.category}`));
|
|
123902
122688
|
console.log(source_default.dim(` Tags: ${result2.manifest.tags?.join(", ")}`));
|
|
123903
|
-
console.log(` ${source_default.cyan("Edit:")} ${
|
|
122689
|
+
console.log(` ${source_default.cyan("Edit:")} ${join74(result2.path, options.kind === "instruction" ? "SKILL.md" : "src/index.ts")}`);
|
|
123904
122690
|
console.log(` ${source_default.cyan("Prepare:")} skills prepare ${result2.name} --version <new-semver>`);
|
|
123905
122691
|
}
|
|
123906
122692
|
} catch (error61) {
|
|
@@ -124190,8 +122976,8 @@ var init_create_sync_config = __esm(() => {
|
|
|
124190
122976
|
|
|
124191
122977
|
// src/lib/claude-hook-disable.ts
|
|
124192
122978
|
import { createHash as createHash48, randomUUID as randomUUID15 } from "crypto";
|
|
124193
|
-
import { constants as constants32, closeSync as
|
|
124194
|
-
import { basename as basename25, dirname as dirname48, isAbsolute as
|
|
122979
|
+
import { constants as constants32, closeSync as closeSync32, fchownSync as fchownSync2, fchmodSync as fchmodSync4, fsyncSync as fsyncSync11, lstatSync as lstatSync47, mkdirSync as mkdirSync31, openSync as openSync32, realpathSync as realpathSync21 } from "fs";
|
|
122980
|
+
import { basename as basename25, dirname as dirname48, isAbsolute as isAbsolute44, join as join75, resolve as resolve40 } from "path";
|
|
124195
122981
|
import { KernelLock as KernelLock2 } from "@hasna/contracts/kernel-lock";
|
|
124196
122982
|
function fail6(reason2) {
|
|
124197
122983
|
throw new Error(`CLAUDE_HOOK_DISABLE_${reason2}`);
|
|
@@ -124204,7 +122990,7 @@ function serialize(value) {
|
|
|
124204
122990
|
return value.kind === "string" ? JSON.stringify(value.value) : value.value;
|
|
124205
122991
|
}
|
|
124206
122992
|
function path(value) {
|
|
124207
|
-
if (!
|
|
122993
|
+
if (!isAbsolute44(value) || resolve40(value) !== value || /[\x00-\x1f\x7f]/.test(value))
|
|
124208
122994
|
fail6("PATH_INVALID");
|
|
124209
122995
|
}
|
|
124210
122996
|
function parents3(file2) {
|
|
@@ -124239,10 +123025,10 @@ function executable2(command, expectedSha256) {
|
|
|
124239
123025
|
if (commandFile.sha256 !== expectedSha256)
|
|
124240
123026
|
fail6("COMMAND_CHANGED");
|
|
124241
123027
|
const entry = resolved.entry, packageRoot = dirname48(dirname48(entry));
|
|
124242
|
-
if (entry !==
|
|
123028
|
+
if (entry !== join75(packageRoot, "bin", "index.js") || realpathSync21(entry) !== entry)
|
|
124243
123029
|
fail6("COMMAND_UNVERIFIED");
|
|
124244
123030
|
const entryFile = snapshot(entry, false, { readOnlyPackage: true });
|
|
124245
|
-
const manifest = snapshot(
|
|
123031
|
+
const manifest = snapshot(join75(packageRoot, "package.json"), false, { readOnlyPackage: true });
|
|
124246
123032
|
const parsed = parseNativeJsonObject(manifest.text);
|
|
124247
123033
|
if (string6(field3(parsed, "name")) !== "@hasna/skills" || string6(field3(field3(parsed, "bin"), "skills")) !== "bin/index.js")
|
|
124248
123034
|
fail6("COMMAND_UNVERIFIED");
|
|
@@ -124383,26 +123169,26 @@ function disableClaudeHooks(options) {
|
|
|
124383
123169
|
mkdirSync31(options.preimageDir, { mode: 448 });
|
|
124384
123170
|
if ((lstatSync47(options.preimageDir).mode & 511) !== 448)
|
|
124385
123171
|
fail6("PREIMAGE_INVALID");
|
|
124386
|
-
preimage =
|
|
123172
|
+
preimage = join75(options.preimageDir, "settings.preimage.json");
|
|
124387
123173
|
save(preimage, before2.bytes);
|
|
124388
123174
|
const preserved = snapshot(preimage, true), checkPreservationRoot = parents3(preimage);
|
|
124389
123175
|
if (preserved.sha256 !== before2.sha256 || !preserved.bytes.equals(before2.bytes))
|
|
124390
123176
|
fail6("PRESERVATION_FAILED");
|
|
124391
|
-
displaced =
|
|
123177
|
+
displaced = join75(dirname48(options.settings), `.settings.skills-disable-${randomUUID15()}.json`);
|
|
124392
123178
|
save(displaced, after2);
|
|
124393
|
-
const fd =
|
|
123179
|
+
const fd = openSync32(displaced, constants32.O_RDONLY | constants32.O_NOFOLLOW);
|
|
124394
123180
|
try {
|
|
124395
123181
|
fchownSync2(fd, Number(before2.stat.uid), Number(before2.stat.gid));
|
|
124396
123182
|
fchmodSync4(fd, 384);
|
|
124397
123183
|
fsyncSync11(fd);
|
|
124398
123184
|
} finally {
|
|
124399
|
-
|
|
123185
|
+
closeSync32(fd);
|
|
124400
123186
|
}
|
|
124401
123187
|
const staged = snapshot(displaced, true);
|
|
124402
123188
|
assertNativePathAcl(displaced, staged.stat);
|
|
124403
123189
|
if (staged.sha256 !== result2.plannedAfterSha256)
|
|
124404
123190
|
fail6("STAGING_CHANGED");
|
|
124405
|
-
receipt =
|
|
123191
|
+
receipt = join75(options.preimageDir, "receipt.json");
|
|
124406
123192
|
const preparationBytes = `${JSON.stringify({
|
|
124407
123193
|
...result2,
|
|
124408
123194
|
state: "prepared",
|
|
@@ -124438,14 +123224,14 @@ function disableClaudeHooks(options) {
|
|
|
124438
123224
|
unchanged2(preserved);
|
|
124439
123225
|
if (movedIdentity(displacedOriginal.stat) !== movedIdentity(before2.stat) || displacedOriginal.sha256 !== before2.sha256 || movedIdentity(current.stat) !== movedIdentity(staged.stat) || current.sha256 !== staged.sha256)
|
|
124440
123226
|
fail6("POST_WRITE_CHANGED");
|
|
124441
|
-
const directory3 =
|
|
123227
|
+
const directory3 = openSync32(dirname48(options.settings), constants32.O_RDONLY | constants32.O_DIRECTORY);
|
|
124442
123228
|
try {
|
|
124443
123229
|
fsyncSync11(directory3);
|
|
124444
123230
|
} finally {
|
|
124445
|
-
|
|
123231
|
+
closeSync32(directory3);
|
|
124446
123232
|
}
|
|
124447
123233
|
const applied = { ...result2, state: "applied", mutationOccurred: true, afterSha256: staged.sha256, preimage, displaced, receipt };
|
|
124448
|
-
const completed =
|
|
123234
|
+
const completed = join75(options.preimageDir, "completed.json");
|
|
124449
123235
|
const completionBytes = `${JSON.stringify({ ...applied, receipt: completed })}
|
|
124450
123236
|
`;
|
|
124451
123237
|
save(completed, completionBytes);
|
|
@@ -124489,7 +123275,7 @@ var init_claude_hook_disable = __esm(() => {
|
|
|
124489
123275
|
|
|
124490
123276
|
// src/lib/agent-install-selection.ts
|
|
124491
123277
|
import { lstatSync as lstatSync48 } from "fs";
|
|
124492
|
-
import { dirname as dirname49, join as
|
|
123278
|
+
import { dirname as dirname49, join as join76, resolve as resolve41 } from "path";
|
|
124493
123279
|
import { homedir as homedir13 } from "os";
|
|
124494
123280
|
function detectedIntegrationAgents(options = {}) {
|
|
124495
123281
|
const home = options.home ?? homedir13(), env3 = options.env ?? process.env;
|
|
@@ -124513,12 +123299,12 @@ function detectedIntegrationAgents(options = {}) {
|
|
|
124513
123299
|
if (agent === "sumi") {
|
|
124514
123300
|
const xdg = env3.XDG_CONFIG_HOME;
|
|
124515
123301
|
const xdgSumi = xdg?.trim() ? resolve41(options.cwd ?? process.cwd(), xdg === "~" ? home : xdg.startsWith("~/") || xdg.startsWith("~\\") ? resolve41(home, xdg.slice(2)) : xdg, "sumi") : null;
|
|
124516
|
-
return ["SUMI_HOME", "SUMI_CONFIG_DIR", "SUMI_CONFIG", "SUMI_CONFIG_CONTENT"].some((key2) => env3[key2] !== undefined) || Bun.which("sumi-paths", { PATH: env3.PATH ?? "" }) !== null || NATIVE_SKILL_ROOTS.some(([provider, root2]) => provider === "sumi" && present2(
|
|
123302
|
+
return ["SUMI_HOME", "SUMI_CONFIG_DIR", "SUMI_CONFIG", "SUMI_CONFIG_CONTENT"].some((key2) => env3[key2] !== undefined) || Bun.which("sumi-paths", { PATH: env3.PATH ?? "" }) !== null || NATIVE_SKILL_ROOTS.some(([provider, root2]) => provider === "sumi" && present2(join76(home, dirname49(root2)))) || present2(join76(home, ".config", "sumi")) || xdgSumi !== null && present2(xdgSumi);
|
|
124517
123303
|
}
|
|
124518
123304
|
if (agent === "hermes" && env3.HERMES_HOME !== undefined)
|
|
124519
123305
|
return true;
|
|
124520
123306
|
const adapter = AGENT_ADAPTERS[agent];
|
|
124521
|
-
return present2(
|
|
123307
|
+
return present2(join76(home, dirname49(adapter.config))) || NATIVE_SKILL_ROOTS.some(([provider, root2]) => provider === agent && dirname49(root2) === `.${agent}` && present2(join76(home, dirname49(root2))));
|
|
124522
123308
|
});
|
|
124523
123309
|
if (!selected.length && !options.allowEmpty)
|
|
124524
123310
|
throw new Error(`No configured or detected agents; select --agent <agent> explicitly (${INTEGRATION_AGENTS.join(", ")}).`);
|
|
@@ -124533,10 +123319,10 @@ var init_agent_install_selection = __esm(() => {
|
|
|
124533
123319
|
|
|
124534
123320
|
// src/lib/codex-hook-trust-identity.ts
|
|
124535
123321
|
import { lstatSync as lstatSync49, realpathSync as realpathSync22 } from "fs";
|
|
124536
|
-
import { dirname as dirname50, isAbsolute as
|
|
123322
|
+
import { dirname as dirname50, isAbsolute as isAbsolute45, join as join77, resolve as resolve42 } from "path";
|
|
124537
123323
|
import { isDeepStrictEqual as isDeepStrictEqual20 } from "util";
|
|
124538
123324
|
function commandPhysical(command) {
|
|
124539
|
-
need8(typeof command === "string" &&
|
|
123325
|
+
need8(typeof command === "string" && isAbsolute45(command) && !command.includes("\x00") && resolve42(command) === command, COMMAND_NOT_CANONICAL);
|
|
124540
123326
|
const physical = realpathSync22(command);
|
|
124541
123327
|
let spelled;
|
|
124542
123328
|
try {
|
|
@@ -124591,12 +123377,12 @@ function targetUnchanged(command, target) {
|
|
|
124591
123377
|
assertPinnedRuntime(target.runtime);
|
|
124592
123378
|
}
|
|
124593
123379
|
function inspectRecordedSkillsCli(command, expected) {
|
|
124594
|
-
need8(
|
|
123380
|
+
need8(isAbsolute45(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
124595
123381
|
const target = skillsCommandTarget(command), resolved = target.entry;
|
|
124596
123382
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
124597
|
-
const manifest = snapshot(
|
|
123383
|
+
const manifest = snapshot(join77(dirname50(dirname50(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
124598
123384
|
const pkg = JSON.parse(manifest.text);
|
|
124599
|
-
need8(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved ===
|
|
123385
|
+
need8(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join77(dirname50(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
124600
123386
|
need8((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
124601
123387
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
124602
123388
|
need8(isDeepStrictEqual20(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
@@ -124610,16 +123396,16 @@ function bindSkillsCli(command, reviewed) {
|
|
|
124610
123396
|
const normal = Bun.which("skills", { PATH: process.env.PATH });
|
|
124611
123397
|
need8(normal, "SKILLS_COMMAND_UNAVAILABLE");
|
|
124612
123398
|
const normalTarget = skillsCommandTarget(normal), resolved = normalTarget.entry;
|
|
124613
|
-
const bound =
|
|
123399
|
+
const bound = isAbsolute45(command) ? command : command === "skills" ? normal : undefined;
|
|
124614
123400
|
need8(bound, "SKILLS_COMMAND_MISMATCH");
|
|
124615
123401
|
const boundTarget = skillsCommandTarget(bound);
|
|
124616
123402
|
need8(boundTarget.entry === resolved, "SKILLS_COMMAND_MISMATCH");
|
|
124617
123403
|
const entrypoint = reviewed?.path ?? process.argv[1];
|
|
124618
123404
|
need8(entrypoint && realpathSync22(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
|
|
124619
123405
|
const cli = snapshot(resolved, false, { readOnlyPackage: true });
|
|
124620
|
-
const manifest = snapshot(
|
|
123406
|
+
const manifest = snapshot(join77(dirname50(dirname50(resolved)), "package.json"), false, { readOnlyPackage: true });
|
|
124621
123407
|
const pkg = JSON.parse(manifest.text);
|
|
124622
|
-
need8(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved ===
|
|
123408
|
+
need8(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join77(dirname50(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
124623
123409
|
if (reviewed)
|
|
124624
123410
|
need8(/^[a-f0-9]{64}$/.test(reviewed.sha256) && reviewed.sha256 === cli.sha256 && reviewed.version === pkg.version, "SKILLS_RELEASE_MISMATCH");
|
|
124625
123411
|
need8((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
@@ -124638,7 +123424,7 @@ function captureSkillsCliProducerIdentity(command) {
|
|
|
124638
123424
|
const bound = bindSkillsCli(command);
|
|
124639
123425
|
const target = skillsCommandTarget(command);
|
|
124640
123426
|
const cli = snapshot(target.entry, false, { readOnlyPackage: true });
|
|
124641
|
-
const manifest = snapshot(
|
|
123427
|
+
const manifest = snapshot(join77(dirname50(dirname50(target.entry)), "package.json"), false, { readOnlyPackage: true });
|
|
124642
123428
|
const pkg = JSON.parse(manifest.text);
|
|
124643
123429
|
need8(pkg.name === "@hasna/skills" && pkg.version === bound.receipt.version && target.entry === bound.receipt.path, "SKILLS_PACKAGE_MISMATCH");
|
|
124644
123430
|
const launcher = target.launcher ?? cli;
|
|
@@ -124692,23 +123478,23 @@ var init_codex_hook_policy_recovery = __esm(() => {
|
|
|
124692
123478
|
});
|
|
124693
123479
|
|
|
124694
123480
|
// src/lib/agent-codex-trust.ts
|
|
124695
|
-
import { lstatSync as lstatSync50, mkdirSync as mkdirSync32, readdirSync as readdirSync28, readFileSync as
|
|
123481
|
+
import { lstatSync as lstatSync50, mkdirSync as mkdirSync32, readdirSync as readdirSync28, readFileSync as readFileSync46, realpathSync as realpathSync23, statSync as statSync25 } from "fs";
|
|
124696
123482
|
import { createHash as createHash49, randomUUID as randomUUID16 } from "crypto";
|
|
124697
123483
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
124698
|
-
import { dirname as dirname51, isAbsolute as
|
|
123484
|
+
import { dirname as dirname51, isAbsolute as isAbsolute46, join as join78, resolve as resolve43 } from "path";
|
|
124699
123485
|
import { homedir as homedir14 } from "os";
|
|
124700
123486
|
import { isDeepStrictEqual as isDeepStrictEqual22 } from "util";
|
|
124701
123487
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
124702
|
-
need8(
|
|
123488
|
+
need8(isAbsolute46(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
124703
123489
|
const tarPath = realpathSync23(proof.packageTarPath);
|
|
124704
123490
|
need8(statSync25(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
124705
|
-
const tarBytes =
|
|
123491
|
+
const tarBytes = readFileSync46(tarPath);
|
|
124706
123492
|
need8(tarBytes.byteLength === proof.packageTarBytes && createHash49("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
124707
123493
|
const packageRoot = dirname51(dirname51(realpathSync23(skillsCli.receipt.path)));
|
|
124708
|
-
const manifest =
|
|
123494
|
+
const manifest = readFileSync46(join78(packageRoot, "package.json"));
|
|
124709
123495
|
need8(createHash49("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
|
|
124710
|
-
need8(createHash49("sha256").update(
|
|
124711
|
-
const members2 = [["package/package.json", manifest], ["package/bin/index.js",
|
|
123496
|
+
need8(createHash49("sha256").update(readFileSync46(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
|
|
123497
|
+
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync46(skillsCli.receipt.path)]];
|
|
124712
123498
|
for (const [member, expected] of members2) {
|
|
124713
123499
|
let actual;
|
|
124714
123500
|
try {
|
|
@@ -124723,25 +123509,25 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
124723
123509
|
let rpc;
|
|
124724
123510
|
try {
|
|
124725
123511
|
const home = resolve43(options.home ?? homedir14()), dataDir3 = resolve43(options.dataDir ?? getDataDirReadOnly());
|
|
124726
|
-
const parent = resolve43(
|
|
123512
|
+
const parent = resolve43(join78(dataDir3, "native-hook-trust")), journal = resolve43(options.journal);
|
|
124727
123513
|
need8(journal.startsWith(parent + "/") && /^[a-f0-9-]{36}$/.test(journal.slice(parent.length + 1)), "RECONCILE_JOURNAL_PATH");
|
|
124728
123514
|
const dir = lstatSync50(journal, { throwIfNoEntry: false });
|
|
124729
123515
|
need8(!!dir && dir.isDirectory() && !dir.isSymbolicLink() && dir.uid === process.getuid() && (dir.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
124730
|
-
need8(!lstatSync50(
|
|
124731
|
-
need8(lstatSync50(
|
|
124732
|
-
const stoppedFile = snapshot(
|
|
124733
|
-
const intentFile = snapshot(
|
|
124734
|
-
const configBefore = snapshot(
|
|
123516
|
+
need8(!lstatSync50(join78(journal, "receipt.json"), { throwIfNoEntry: false }), "RECONCILE_ALREADY_COMPLETE");
|
|
123517
|
+
need8(lstatSync50(join78(journal, "stopped.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_JOURNAL_INCOMPLETE");
|
|
123518
|
+
const stoppedFile = snapshot(join78(journal, "stopped.json"), true);
|
|
123519
|
+
const intentFile = snapshot(join78(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
|
|
123520
|
+
const configBefore = snapshot(join78(journal, "config.before.toml"), true), hooksBefore = snapshot(join78(journal, "hooks.before.json"), true), policyBefore = snapshot(join78(journal, "policy.before.json"), true);
|
|
124735
123521
|
const legacyIntent = intent.home === undefined && intent.configSha256 === undefined && intent.configVersion === undefined && intent.declarations === undefined && intent.admitted === undefined && intent.nativeHooks === undefined;
|
|
124736
123522
|
need8(intent.version === 1 && /^[a-f0-9]{64}$/.test(intent.planDigest) && typeof intent.configPath === "string" && /^[a-f0-9]{64}$/.test(intent.policySha256) && /^[a-f0-9]{64}$/.test(intent.hooksSha256) && Array.isArray(intent.hooks) && intent.hooks.length > 0 && intent.hooks.length <= 100 && (legacyIntent || typeof intent.home === "string" && intent.home === home && /^[a-f0-9]{64}$/.test(intent.configSha256) && typeof intent.configVersion === "string" && Array.isArray(intent.declarations) && Array.isArray(intent.admitted) && Array.isArray(intent.nativeHooks)), "RECONCILE_INTENT_INVALID");
|
|
124737
|
-
const declarations = intent.declarations ?? intent.hooks.map((hook) => ({ event: hook.event, key: `${
|
|
123523
|
+
const declarations = intent.declarations ?? intent.hooks.map((hook) => ({ event: hook.event, key: `${join78(home, ".codex/hooks.json")}${hook.key.slice(hook.key.indexOf(".codex/hooks.json") + ".codex/hooks.json".length)}`, command: hook.command }));
|
|
124738
123524
|
const admitted = intent.admitted ?? intent.hooks;
|
|
124739
123525
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
124740
123526
|
const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
|
|
124741
123527
|
need8(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
124742
123528
|
need8(isDeepStrictEqual22(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
124743
123529
|
need8(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
124744
|
-
const policyCurrent = snapshot(
|
|
123530
|
+
const policyCurrent = snapshot(join78(dataDir3, "agent-policy.json"), true);
|
|
124745
123531
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
124746
123532
|
const recovery = claudeRecovery ?? additionsRecovery;
|
|
124747
123533
|
need8(!recovery || !options.supersedeBinding, "RECONCILE_RECOVERY_MODE_CONFLICT");
|
|
@@ -124758,17 +123544,17 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
124758
123544
|
if (supersededBinding || recovery) {
|
|
124759
123545
|
need8(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
|
|
124760
123546
|
const proof = options.releaseProof;
|
|
124761
|
-
need8(!!proof &&
|
|
123547
|
+
need8(!!proof && isAbsolute46(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
|
|
124762
123548
|
}
|
|
124763
123549
|
const currentConfig = snapshot(intent.configPath, true);
|
|
124764
123550
|
const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
|
|
124765
|
-
const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ??
|
|
124766
|
-
need8(resolve43(intent.configPath) === resolve43(
|
|
123551
|
+
const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join78(home, ".codex"), hooksPath = join78(physicalRoot, "hooks.json");
|
|
123552
|
+
need8(resolve43(intent.configPath) === resolve43(join78(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
|
|
124767
123553
|
const nativeConfigPath = intent.nativeConfigPath ?? intent.configPath;
|
|
124768
|
-
need8([
|
|
123554
|
+
need8([join78(home, ".codex/config.toml"), join78(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
|
|
124769
123555
|
const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname51(nativeConfigPath);
|
|
124770
123556
|
const hooksCurrent = snapshot(hooksPath);
|
|
124771
|
-
const additions = additionsRecovery ? reviewNativeHookAdditions({ path: additionsRecovery.path, intent: intentFile, policy: policyCurrent, configBefore, configCurrent: currentConfig, hooksBefore, hooksCurrent, nativeHooksPath:
|
|
123557
|
+
const additions = additionsRecovery ? reviewNativeHookAdditions({ path: additionsRecovery.path, intent: intentFile, policy: policyCurrent, configBefore, configCurrent: currentConfig, hooksBefore, hooksCurrent, nativeHooksPath: join78(dirname51(nativeConfigPath), "hooks.json"), journalHooks: intent.hooks, recordedNativeHooks: intent.nativeHooks }) : undefined;
|
|
124772
123558
|
const managedHooks = [...intent.hooks, ...additions?.hooks ?? []];
|
|
124773
123559
|
const managedKeys = managedHooks.map((h) => h.key);
|
|
124774
123560
|
const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
|
|
@@ -124832,7 +123618,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
124832
123618
|
need8(isDeepStrictEqual22(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
124833
123619
|
}
|
|
124834
123620
|
for (const wanted of intent.hooks) {
|
|
124835
|
-
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(
|
|
123621
|
+
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join78(home, ".codex/hooks.json"), hooksPath));
|
|
124836
123622
|
need8(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
124837
123623
|
const h = matches2[0];
|
|
124838
123624
|
const trusted = h.trustStatus === "trusted";
|
|
@@ -124881,7 +123667,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
124881
123667
|
const receipt = { version: 1, status: preview2 ? "recovery-review-required" : supersededBinding ? "superseded" : "reconciled", reconciled: !preview2, supersededBinding, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: !nativeTrustPending, nativeTrustPending, bindingRefreshRequired: !!configDiscoveryRecovery, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, previousSkillsCli: intent.skillsCli, skillsCli: skillsCli.receipt, releaseProof: options.releaseProof, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true, ...recoveryPlan ? { recoveryPlan, recoveryPlanDigest } : {}, ...codexAdmissionReceipt(rpc.admission) };
|
|
124882
123668
|
if (preview2)
|
|
124883
123669
|
return receipt;
|
|
124884
|
-
save(
|
|
123670
|
+
save(join78(journal, "receipt.json"), json2(receipt));
|
|
124885
123671
|
return receipt;
|
|
124886
123672
|
} catch (error61) {
|
|
124887
123673
|
const message = error61 instanceof Error && error61.message.startsWith("CODEX_HOOK_TRUST_") ? error61.message : "CODEX_HOOK_TRUST_RECONCILE_REFUSED";
|
|
@@ -124892,20 +123678,20 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
124892
123678
|
}
|
|
124893
123679
|
async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRpc) {
|
|
124894
123680
|
const home = resolve43(options.home ?? homedir14());
|
|
124895
|
-
return withCodexCorpusWriteAsync([
|
|
123681
|
+
return withCodexCorpusWriteAsync([join78(home, ".codex")], (_assertCurrent, admission) => enrollCodexNativeHooksUnlocked(options, connect, admission), options);
|
|
124896
123682
|
}
|
|
124897
123683
|
async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
|
|
124898
123684
|
let rpc, journal;
|
|
124899
123685
|
try {
|
|
124900
123686
|
const home = resolve43(options.home ?? homedir14()), dataDir3 = resolve43(options.dataDir ?? getDataDirReadOnly());
|
|
124901
123687
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
|
|
124902
|
-
const policyFile = snapshot(
|
|
123688
|
+
const policyFile = snapshot(join78(dataDir3, "agent-policy.json"), true), policy = parseManagedSkillPolicy(policyFile.text);
|
|
124903
123689
|
const binding = policy.bridge, alias = binding.rootAliases?.find((item) => item.agent === "codex");
|
|
124904
123690
|
const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
124905
|
-
const physicalRoot = alias?.target ??
|
|
124906
|
-
need8(options.codexHome === undefined ||
|
|
123691
|
+
const physicalRoot = alias?.target ?? join78(home, ".codex"), hooksPath = join78(physicalRoot, "hooks.json"), configPath = join78(physicalRoot, "config.toml");
|
|
123692
|
+
need8(options.codexHome === undefined || isAbsolute46(options.codexHome) && [join78(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
|
|
124907
123693
|
const codexHome = options.codexHome === undefined ? undefined : physicalRoot;
|
|
124908
|
-
const nativeConfigPath =
|
|
123694
|
+
const nativeConfigPath = join78(codexHome ?? join78(home, ".codex"), "config.toml");
|
|
124909
123695
|
const hooksFile = snapshot(hooksPath), configFile = snapshot(configPath, true), original = structure3(configFile.text);
|
|
124910
123696
|
const declarations = JSON.parse(hooksFile.text).hooks, expected = [];
|
|
124911
123697
|
for (const [event, [nativeEvent, label]] of Object.entries(eventNames)) {
|
|
@@ -124914,25 +123700,25 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
|
|
|
124914
123700
|
need8(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
124915
123701
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
124916
123702
|
need8(matches2.length === 1 && isDeepStrictEqual22(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
124917
|
-
expected.push({ event: nativeEvent, key: `${
|
|
123703
|
+
expected.push({ event: nativeEvent, key: `${join78(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
124918
123704
|
}
|
|
124919
123705
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
124920
123706
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
124921
123707
|
need8(SUPPORTED_CODEX_HOOK_VERSIONS.includes(rpc.version), "NATIVE_UNSUPPORTED_VERSION");
|
|
124922
123708
|
const list2 = async () => {
|
|
124923
123709
|
const response = await rpc.request("hooks/list", { cwds: [home] });
|
|
124924
|
-
const nativeRoots = [...new Set([physicalRoot,
|
|
124925
|
-
const coexistenceWarnings = nativeRoots.flatMap((hooksRoot) => nativeRoots.map((configRoot) => `loading hooks from both ${
|
|
123710
|
+
const nativeRoots = [...new Set([physicalRoot, join78(home, ".codex")])];
|
|
123711
|
+
const coexistenceWarnings = nativeRoots.flatMap((hooksRoot) => nativeRoots.map((configRoot) => `loading hooks from both ${join78(hooksRoot, "hooks.json")} and ${join78(configRoot, "config.toml")}; prefer a single representation for this layer`));
|
|
124926
123712
|
const entry = response?.data?.[0];
|
|
124927
123713
|
need8(Array.isArray(response?.data) && response.data.length === 1 && entry.cwd === home && Array.isArray(entry.hooks) && entry.hooks.length <= 1e4 && Array.isArray(entry.errors) && entry.errors.length === 0 && Array.isArray(entry.warnings) && entry.warnings.length <= 1 && entry.warnings.every((warning) => typeof warning === "string" && coexistenceWarnings.includes(warning)), "NATIVE_DISCOVERY_REFUSED");
|
|
124928
123714
|
return response.data[0].hooks;
|
|
124929
123715
|
};
|
|
124930
123716
|
const admit = (hooks) => expected.map((wanted) => {
|
|
124931
|
-
const canonicalKey = wanted.key.replace(
|
|
123717
|
+
const canonicalKey = wanted.key.replace(join78(home, ".codex/hooks.json"), hooksPath);
|
|
124932
123718
|
const matches2 = hooks.filter((h2) => h2.key === wanted.key || h2.key === canonicalKey);
|
|
124933
123719
|
need8(matches2.length === 1, "AMBIGUOUS_IDENTITY");
|
|
124934
123720
|
const h = matches2[0];
|
|
124935
|
-
need8(h.eventName === wanted.event && h.handlerType === "command" && h.command === wanted.command && h.matcher === null && h.timeoutSec === 15 && h.async === false && h.statusMessage === null && h.additionalContextLimit === null && [hooksPath,
|
|
123721
|
+
need8(h.eventName === wanted.event && h.handlerType === "command" && h.command === wanted.command && h.matcher === null && h.timeoutSec === 15 && h.async === false && h.statusMessage === null && h.additionalContextLimit === null && [hooksPath, join78(home, ".codex/hooks.json")].includes(h.sourcePath) && h.source === "user" && h.pluginId === null && h.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(h.currentHash) && typeof h.enabled === "boolean", "NATIVE_IDENTITY_CHANGED");
|
|
124936
123722
|
need8(hooks.filter((other) => other.command === wanted.command).length === 1, "AMBIGUOUS_IDENTITY");
|
|
124937
123723
|
need8(["trusted", "untrusted", "modified"].includes(h.trustStatus), "UNKNOWN_TRUST_STATUS");
|
|
124938
123724
|
return { key: h.key, event: wanted.event, command: wanted.command, handlerType: h.handlerType, timeoutSec: h.timeoutSec, sourcePath: h.sourcePath, currentHash: h.currentHash, enabled: h.enabled, trustStatus: h.trustStatus };
|
|
@@ -124958,7 +123744,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
|
|
|
124958
123744
|
const baseReceipt = { planDigest, skillsCli: skillsCli.receipt, nativeVersion: rpc.version, nativeConfigPath, ownedNativeProcessId: rpc.processId, agent: "codex", applied: false, planned, nativeEligible: planned.length === 0, bindingRefreshRequired: false, transport: "owned-stdio-process", existingSessionsReloaded: false, nativeExecutionVerified: false, ...codexAdmissionReceipt(admission) };
|
|
124959
123745
|
if (options.apply)
|
|
124960
123746
|
need8(options.reviewedPlanDigest === planDigest, "PLAN_CHANGED: run the dry-run again and pass its reviewed --plan-digest with --apply");
|
|
124961
|
-
const parent =
|
|
123747
|
+
const parent = join78(dataDir3, "native-hook-trust");
|
|
124962
123748
|
const st = lstatSync50(parent, { throwIfNoEntry: false });
|
|
124963
123749
|
if (st) {
|
|
124964
123750
|
need8(st.isDirectory() && !st.isSymbolicLink() && st.uid === process.getuid() && (st.mode & 511) === 448, "UNSAFE_JOURNAL");
|
|
@@ -124966,19 +123752,19 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
|
|
|
124966
123752
|
need8(priorJournals.length < 1000, "JOURNAL_BOUND");
|
|
124967
123753
|
for (const name of priorJournals) {
|
|
124968
123754
|
need8(/^[a-f0-9-]{36}$/.test(name), "UNSAFE_JOURNAL");
|
|
124969
|
-
need8(lstatSync50(
|
|
123755
|
+
need8(lstatSync50(join78(parent, name, "receipt.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_REQUIRED: inspect the incomplete private journal before applying again");
|
|
124970
123756
|
}
|
|
124971
123757
|
}
|
|
124972
123758
|
if (!options.apply || !planned.length)
|
|
124973
123759
|
return baseReceipt;
|
|
124974
123760
|
if (!st)
|
|
124975
123761
|
mkdirSync32(parent, { mode: 448 });
|
|
124976
|
-
journal =
|
|
123762
|
+
journal = join78(parent, randomUUID16());
|
|
124977
123763
|
mkdirSync32(journal, { mode: 448 });
|
|
124978
|
-
save(
|
|
124979
|
-
save(
|
|
124980
|
-
save(
|
|
124981
|
-
save(
|
|
123764
|
+
save(join78(journal, "config.before.toml"), configFile.bytes);
|
|
123765
|
+
save(join78(journal, "hooks.before.json"), hooksFile.bytes);
|
|
123766
|
+
save(join78(journal, "policy.before.json"), policyFile.bytes);
|
|
123767
|
+
save(join78(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook), ...codexAdmissionReceipt(admission) }));
|
|
124982
123768
|
need8(isDeepStrictEqual22(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
124983
123769
|
checkInputs();
|
|
124984
123770
|
let result2;
|
|
@@ -124994,7 +123780,7 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
|
|
|
124994
123780
|
for (const h of planned)
|
|
124995
123781
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
124996
123782
|
need8(isDeepStrictEqual22(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys4) === preservationText, "PRESERVATION_FAILED");
|
|
124997
|
-
const savedConfig = snapshot(
|
|
123783
|
+
const savedConfig = snapshot(join78(journal, "config.before.toml"), true);
|
|
124998
123784
|
need8(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
124999
123785
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
125000
123786
|
unchanged2(policyFile);
|
|
@@ -125010,13 +123796,13 @@ async function enrollCodexNativeHooksUnlocked(options, connect, admission) {
|
|
|
125010
123796
|
skillsCli.recheck();
|
|
125011
123797
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
125012
123798
|
const receipt = { ...baseReceipt, applied: true, nativeEligible: true, bindingRefreshRequired: !!discoveryTransition, ...discoveryTransition ? { discoveryTransition } : {}, nativeConfigVersion: result2.version, journal, beforeSha256: configFile.sha256, afterSha256: after2.sha256, unrelatedSettingsAndCommentsPreserved: true };
|
|
125013
|
-
save(
|
|
123799
|
+
save(join78(journal, "receipt.json"), json2(receipt));
|
|
125014
123800
|
return receipt;
|
|
125015
123801
|
} catch (error61) {
|
|
125016
123802
|
const message = error61 instanceof Error && error61.message.startsWith("CODEX_HOOK_TRUST_") ? error61.message : "CODEX_HOOK_TRUST_REFUSED: verify managed bridge installation and native Codex support";
|
|
125017
123803
|
if (journal) {
|
|
125018
123804
|
try {
|
|
125019
|
-
save(
|
|
123805
|
+
save(join78(journal, "stopped.json"), json2({ error: message.split(":")[0], automaticRollback: false, reconcileBeforeRetry: true, ...codexAdmissionReceipt(admission) }));
|
|
125020
123806
|
} catch {}
|
|
125021
123807
|
}
|
|
125022
123808
|
throw new Error(message);
|
|
@@ -125050,8 +123836,8 @@ var init_agent_codex_trust = __esm(() => {
|
|
|
125050
123836
|
|
|
125051
123837
|
// src/lib/codex-native-trust.ts
|
|
125052
123838
|
import { createHash as createHash50 } from "crypto";
|
|
125053
|
-
import { readFileSync as
|
|
125054
|
-
import { join as
|
|
123839
|
+
import { readFileSync as readFileSync47 } from "fs";
|
|
123840
|
+
import { join as join79, resolve as resolve44 } from "path";
|
|
125055
123841
|
function refuse7(detail) {
|
|
125056
123842
|
throw new Error(`NATIVE_SKILL_POLICY_TRUST_REFUSED: ${detail}`);
|
|
125057
123843
|
}
|
|
@@ -125070,7 +123856,7 @@ function planCodexNativeTrust(options) {
|
|
|
125070
123856
|
refuse7("digests must be unique");
|
|
125071
123857
|
if (typeof expectedPolicySha256 !== "string" || !HEX_DIGEST2.test(expectedPolicySha256))
|
|
125072
123858
|
refuse7("--expected-policy-sha256 must be lowercase SHA-256 hex");
|
|
125073
|
-
const dataDir3 = resolve44(options.dataDir ?? getDataDirReadOnly()), policyPath =
|
|
123859
|
+
const dataDir3 = resolve44(options.dataDir ?? getDataDirReadOnly()), policyPath = join79(dataDir3, "agent-policy.json");
|
|
125074
123860
|
assertOperatorTrustRoot(dataDir3);
|
|
125075
123861
|
const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
|
|
125076
123862
|
if (!snapshot2)
|
|
@@ -125108,7 +123894,7 @@ function applyCodexNativeTrust(plan, expectedPolicySha256) {
|
|
|
125108
123894
|
const backupPath = result2.backups[0];
|
|
125109
123895
|
if (result2.backups.length !== 1 || typeof backupPath !== "string")
|
|
125110
123896
|
throw new Error("NATIVE_SKILL_POLICY_TRUST_WRITE_UNVERIFIED: the pre-change backup was not recorded");
|
|
125111
|
-
const backupSha256 = sha7(
|
|
123897
|
+
const backupSha256 = sha7(readFileSync47(backupPath));
|
|
125112
123898
|
if (backupSha256 !== plan.policySha256Before)
|
|
125113
123899
|
throw new Error(`NATIVE_SKILL_POLICY_TRUST_WRITE_UNVERIFIED: the backup at ${backupPath} does not read back as the pre-change policy`);
|
|
125114
123900
|
receipt.backup = { path: backupPath, sha256: backupSha256, verified: true };
|
|
@@ -125135,8 +123921,8 @@ var init_codex_native_trust = __esm(() => {
|
|
|
125135
123921
|
|
|
125136
123922
|
// src/lib/claude-prospective-review.ts
|
|
125137
123923
|
import { createHash as createHash51 } from "crypto";
|
|
125138
|
-
import { lstatSync as lstatSync51, readFileSync as
|
|
125139
|
-
import { basename as basename26, dirname as dirname52, isAbsolute as
|
|
123924
|
+
import { lstatSync as lstatSync51, readFileSync as readFileSync48, readdirSync as readdirSync29, realpathSync as realpathSync24 } from "fs";
|
|
123925
|
+
import { basename as basename26, dirname as dirname52, isAbsolute as isAbsolute47, join as join80, relative as relative16, resolve as resolve45, sep as sep23 } from "path";
|
|
125140
123926
|
import { isDeepStrictEqual as isDeepStrictEqual23 } from "util";
|
|
125141
123927
|
function hashClaudeDiscoveryBinding(binding) {
|
|
125142
123928
|
return sha8(JSON.stringify(binding));
|
|
@@ -125187,7 +123973,7 @@ function keys4(value, expected, name) {
|
|
|
125187
123973
|
refuse8(`${name} has an unsupported shape`);
|
|
125188
123974
|
}
|
|
125189
123975
|
function abs(path2, name) {
|
|
125190
|
-
if (typeof path2 !== "string" || !path2 || path2.length > 4096 || /[\x00-\x1f\x7f]/.test(path2) || !
|
|
123976
|
+
if (typeof path2 !== "string" || !path2 || path2.length > 4096 || /[\x00-\x1f\x7f]/.test(path2) || !isAbsolute47(path2) || resolve45(path2) !== path2)
|
|
125191
123977
|
refuse8(`${name} must be a normalized absolute path`);
|
|
125192
123978
|
}
|
|
125193
123979
|
function validUtcTimestamp(value) {
|
|
@@ -125205,7 +123991,7 @@ function safeFile(path2, name) {
|
|
|
125205
123991
|
const stat3 = lstatSync51(path2, { throwIfNoEntry: false });
|
|
125206
123992
|
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > MAX_CANDIDATE_BYTES)
|
|
125207
123993
|
refuse8(`${name} is not a bounded regular file`);
|
|
125208
|
-
return
|
|
123994
|
+
return readFileSync48(path2);
|
|
125209
123995
|
}
|
|
125210
123996
|
function canonicalDir(path2, name) {
|
|
125211
123997
|
abs(path2, name);
|
|
@@ -125231,7 +124017,7 @@ function captureClaudeProspectiveCandidateClosure(root2) {
|
|
|
125231
124017
|
for (const name of names) {
|
|
125232
124018
|
if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\") || /[\x00-\x1f\x7f]/.test(name))
|
|
125233
124019
|
refuse8("candidate contains an unsupported path member");
|
|
125234
|
-
const path2 =
|
|
124020
|
+
const path2 = join80(directory3, name), rel = relative16(root2, path2).split(sep23).join("/");
|
|
125235
124021
|
if (entries.length >= MAX_CANDIDATE_FILES)
|
|
125236
124022
|
refuse8("candidate closure exceeds its member limit");
|
|
125237
124023
|
const stat3 = lstatSync51(path2);
|
|
@@ -125262,9 +124048,9 @@ function captureClaudeProspectiveCandidateClosure(root2) {
|
|
|
125262
124048
|
function captureClaudeInstallerCandidate(root2, policyPath) {
|
|
125263
124049
|
const closureSha256 = captureClaudeProspectiveCandidateClosure(root2);
|
|
125264
124050
|
const dataDir3 = getDataDirReadOnly();
|
|
125265
|
-
if (policyPath !==
|
|
124051
|
+
if (policyPath !== join80(dataDir3, "agent-policy.json"))
|
|
125266
124052
|
refuse8("Skills policy path differs from the package-owned resolver");
|
|
125267
|
-
const catalogPath =
|
|
124053
|
+
const catalogPath = join80(root2, ".claude-plugin/marketplace.json");
|
|
125268
124054
|
const catalog = jsonFile(catalogPath, "candidate catalog");
|
|
125269
124055
|
if (!catalog || !Array.isArray(catalog.plugins))
|
|
125270
124056
|
refuse8("candidate catalog is invalid");
|
|
@@ -125275,13 +124061,13 @@ function captureClaudeInstallerCandidate(root2, policyPath) {
|
|
|
125275
124061
|
if (names.has(row2.name))
|
|
125276
124062
|
refuse8("candidate catalog repeats a plugin name");
|
|
125277
124063
|
names.add(row2.name);
|
|
125278
|
-
if (typeof row2.source !== "string" ||
|
|
124064
|
+
if (typeof row2.source !== "string" || isAbsolute47(row2.source) || /[\\\x00-\x1f\x7f]/.test(row2.source))
|
|
125279
124065
|
refuse8("candidate catalog plugin source is invalid");
|
|
125280
124066
|
const pluginRoot = resolve45(root2, row2.source);
|
|
125281
124067
|
const relativeRoot = relative16(root2, pluginRoot);
|
|
125282
|
-
if (!relativeRoot || relativeRoot === "." || relativeRoot === ".." || relativeRoot.startsWith(`..${sep23}`) ||
|
|
124068
|
+
if (!relativeRoot || relativeRoot === "." || relativeRoot === ".." || relativeRoot.startsWith(`..${sep23}`) || isAbsolute47(relativeRoot))
|
|
125283
124069
|
refuse8("candidate plugin source escapes the candidate root");
|
|
125284
|
-
const path2 =
|
|
124070
|
+
const path2 = join80(pluginRoot, ".claude-plugin/plugin.json");
|
|
125285
124071
|
return { plugin: row2.name, ...captureClaudePluginManifestFile(path2) };
|
|
125286
124072
|
});
|
|
125287
124073
|
const policy = readManagedSkillPolicySnapshot(dataDir3);
|
|
@@ -125381,7 +124167,7 @@ function assertNoUnprojectedSkillPayload(root2, catalogPath, marketplace, plugin
|
|
|
125381
124167
|
refuse8("candidate marketplace entry declares native skill or command payloads");
|
|
125382
124168
|
const walk2 = (directory3) => {
|
|
125383
124169
|
for (const name of readdirSync29(directory3).sort()) {
|
|
125384
|
-
const path2 =
|
|
124170
|
+
const path2 = join80(directory3, name), rel = relative16(root2, path2).split(sep23).join("/");
|
|
125385
124171
|
const stat3 = lstatSync51(path2);
|
|
125386
124172
|
if (stat3.isDirectory())
|
|
125387
124173
|
walk2(path2);
|
|
@@ -125439,11 +124225,11 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125439
124225
|
refuse8("Skills policy root alias changed or is invalid");
|
|
125440
124226
|
}
|
|
125441
124227
|
const canonical9 = (path2) => canonicalAgentPath(path2, aliases);
|
|
125442
|
-
const configRoot = canonical9(
|
|
124228
|
+
const configRoot = canonical9(join80(request2.native.home, ".claude"));
|
|
125443
124229
|
canonicalDir(configRoot, "Claude config root");
|
|
125444
124230
|
if (request2.native.configRoot !== configRoot)
|
|
125445
124231
|
refuse8("Claude config root differs from the current resolver and reviewed root alias");
|
|
125446
|
-
const pluginRoot =
|
|
124232
|
+
const pluginRoot = join80(configRoot, "plugins");
|
|
125447
124233
|
if (process.env.CLAUDE_CODE_PLUGIN_CACHE_DIR)
|
|
125448
124234
|
refuse8("CLAUDE_CODE_PLUGIN_CACHE_DIR overrides are unsupported by this prospective schema; bind and review the actual cache root first");
|
|
125449
124235
|
if (request2.native.pluginRoot !== pluginRoot)
|
|
@@ -125460,7 +124246,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125460
124246
|
refuse8("candidate must select between one and 32 plugins from one marketplace");
|
|
125461
124247
|
if (candidate2.functionHooksEnv !== undefined && candidate2.functionHooksEnv !== "1")
|
|
125462
124248
|
refuse8("function-hook environment mutation only supports the explicit value 1");
|
|
125463
|
-
const settingsMarketplaceWriterVersions = ["2.1.276", "2.1.278", "2.1.282", "2.1.287", "2.1.293", "2.1.296", "2.1.299", "2.1.300", "2.1.301", "2.1.302"];
|
|
124249
|
+
const settingsMarketplaceWriterVersions = ["2.1.276", "2.1.278", "2.1.282", "2.1.287", "2.1.293", "2.1.296", "2.1.299", "2.1.300", "2.1.301", "2.1.302", "2.1.314", "2.1.316"];
|
|
125464
124250
|
const nativeVersion = request2.native.executable.version;
|
|
125465
124251
|
const measuredSettingsWriter = settingsMarketplaceWriterVersions.some((version2) => nativeVersion === version2 || nativeVersion === `${version2} (Claude Code)`);
|
|
125466
124252
|
if (hasOwn2(candidate2, "settingsMarketplaceSource") && (candidate2.settingsMarketplaceSource !== "directory" || request2.scope.kind !== "user" || !measuredSettingsWriter))
|
|
@@ -125468,7 +124254,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125468
124254
|
abs(candidate2.catalogPath, "catalog path");
|
|
125469
124255
|
if (!candidate2.catalogPath.startsWith(candidate2.root + sep23))
|
|
125470
124256
|
refuse8("catalog must be inside the frozen candidate root");
|
|
125471
|
-
if (candidate2.catalogPath !==
|
|
124257
|
+
if (candidate2.catalogPath !== join80(candidate2.root, ".claude-plugin", "marketplace.json"))
|
|
125472
124258
|
refuse8("marketplace catalog must be at the frozen root's .claude-plugin/marketplace.json path");
|
|
125473
124259
|
if (!SHA2562.test(candidate2.catalogSha256) || !SHA2562.test(candidate2.closureSha256))
|
|
125474
124260
|
refuse8("candidate digests must be lowercase SHA-256");
|
|
@@ -125510,11 +124296,11 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125510
124296
|
if (Buffer.byteLength(canonicalJson3(selected.optionsPatch), "utf8") > 16 * 1024)
|
|
125511
124297
|
refuse8("selected plugin optionsPatch exceeds its byte bound");
|
|
125512
124298
|
abs(selected.manifestPath, "manifest path");
|
|
125513
|
-
if (!selected.manifestPath.startsWith(candidate2.root + sep23) || basename26(dirname52(selected.manifestPath)) !== ".claude-plugin" || selected.manifestPath !==
|
|
124299
|
+
if (!selected.manifestPath.startsWith(candidate2.root + sep23) || basename26(dirname52(selected.manifestPath)) !== ".claude-plugin" || selected.manifestPath !== join80(dirname52(dirname52(selected.manifestPath)), ".claude-plugin", "plugin.json"))
|
|
125514
124300
|
refuse8("plugin manifest must be under a plugin root inside the frozen marketplace closure");
|
|
125515
124301
|
const pluginRoot2 = dirname52(dirname52(selected.manifestPath));
|
|
125516
124302
|
const relativePluginRoot = relative16(candidate2.root, pluginRoot2);
|
|
125517
|
-
if (!relativePluginRoot || relativePluginRoot === "." || relativePluginRoot === ".." || relativePluginRoot.startsWith(`..${sep23}`) ||
|
|
124303
|
+
if (!relativePluginRoot || relativePluginRoot === "." || relativePluginRoot === ".." || relativePluginRoot.startsWith(`..${sep23}`) || isAbsolute47(relativePluginRoot))
|
|
125518
124304
|
refuse8("plugin root must be a child of the frozen marketplace root");
|
|
125519
124305
|
canonicalDir(pluginRoot2, "candidate plugin root");
|
|
125520
124306
|
if (!SHA2562.test(selected.manifestSha256))
|
|
@@ -125548,7 +124334,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125548
124334
|
const sourcePath = selectedEntry.source;
|
|
125549
124335
|
const sourceRelativePath = relative16(candidate2.root, pluginRoot2).split(sep23).join("/");
|
|
125550
124336
|
const expectedSourcePath = sourceRelativePath === "." ? "./" : `./${sourceRelativePath}`;
|
|
125551
|
-
if (typeof sourcePath !== "string" ||
|
|
124337
|
+
if (typeof sourcePath !== "string" || isAbsolute47(sourcePath) || sourcePath !== expectedSourcePath || resolve45(dirname52(dirname52(candidate2.catalogPath)), sourcePath) !== pluginRoot2)
|
|
125552
124338
|
refuse8("marketplace source does not resolve to the frozen plugin closure; external source mapping is not supported by this schema");
|
|
125553
124339
|
const entry = captureClaudeMarketplaceEntry(candidate2.catalogPath, candidate2.marketplace, selected.plugin);
|
|
125554
124340
|
if (entry.sha256.length !== 64)
|
|
@@ -125568,12 +124354,12 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125568
124354
|
for (const name of ["settings", "projectSettings", "projectSettingsLocal", "marketplaces", "installedPlugins"])
|
|
125569
124355
|
keys4(request2.preimages[name], ["path", "sha256"], `${name} preimage`);
|
|
125570
124356
|
const expectedPaths = {
|
|
125571
|
-
settings: request2.scope.kind === "project" ? canonical9(
|
|
125572
|
-
userSettings: canonical9(
|
|
125573
|
-
projectSettings: canonical9(
|
|
125574
|
-
projectSettingsLocal: canonical9(
|
|
125575
|
-
marketplaces: canonical9(
|
|
125576
|
-
installedPlugins: canonical9(
|
|
124357
|
+
settings: request2.scope.kind === "project" ? canonical9(join80(request2.scope.projectPath, ".claude/settings.json")) : canonical9(join80(request2.native.home, ".claude/settings.json")),
|
|
124358
|
+
userSettings: canonical9(join80(request2.native.home, ".claude/settings.json")),
|
|
124359
|
+
projectSettings: canonical9(join80(request2.scope.projectPath, ".claude/settings.json")),
|
|
124360
|
+
projectSettingsLocal: canonical9(join80(request2.scope.projectPath, ".claude/settings.local.json")),
|
|
124361
|
+
marketplaces: canonical9(join80(request2.native.home, ".claude/plugins/known_marketplaces.json")),
|
|
124362
|
+
installedPlugins: canonical9(join80(request2.native.home, ".claude/plugins/installed_plugins.json"))
|
|
125577
124363
|
};
|
|
125578
124364
|
for (const name of Object.keys(expectedPaths)) {
|
|
125579
124365
|
const witness = request2.preimages[name];
|
|
@@ -125706,7 +124492,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125706
124492
|
const manifestVersion = manifest.version;
|
|
125707
124493
|
const sanitizeComponent = (value) => value.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
125708
124494
|
const sanitizedVersion = manifestVersion === "." || manifestVersion === ".." ? "-" : manifestVersion.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
125709
|
-
const expectedInstallPath =
|
|
124495
|
+
const expectedInstallPath = join80(pluginRoot, "cache", sanitizeComponent(candidate2.marketplace), sanitizeComponent(plugin), sanitizedVersion);
|
|
125710
124496
|
if (expectedInstallPath.length > 4096)
|
|
125711
124497
|
refuse8("candidate native cache path exceeds its bound");
|
|
125712
124498
|
const targetRegistration = {
|
|
@@ -125845,7 +124631,7 @@ function reviewClaudeProspectiveCandidate(request2, requestFileSha256) {
|
|
|
125845
124631
|
{ name: "project-settings-local", path: expectedPaths.projectSettingsLocal, sha256: request2.preimages.projectSettingsLocal.sha256 },
|
|
125846
124632
|
{ name: "known-marketplaces", path: expectedPaths.marketplaces, sha256: request2.preimages.marketplaces.sha256 },
|
|
125847
124633
|
{ name: "installed-plugins", path: expectedPaths.installedPlugins, sha256: request2.preimages.installedPlugins.sha256 },
|
|
125848
|
-
{ name: "skills-policy", path:
|
|
124634
|
+
{ name: "skills-policy", path: join80(request2.skills.dataDir, "agent-policy.json"), sha256: request2.skills.policySha256 }
|
|
125849
124635
|
],
|
|
125850
124636
|
ownedDelta: { settingsEnabledPlugins: selectedPluginIds, selectedPluginOptions: selectedOptionPaths, functionHooksEnv: candidate2.functionHooksEnv ?? null, knownMarketplace: candidate2.marketplace, installedPlugins: selectedPluginIds, unrelatedGraph: "preserved-by-complete-semantic-documents" },
|
|
125851
124637
|
identityPrecondition: "Harnesses must resolve the installed Claude home/config/plugin roots and executable path/version/hash plus the installed Skills data directory through their package-owned consumers, then compare every value before trusting this receipt; this validator checks supplied paths and current bytes only and does not establish actual consumer identity",
|
|
@@ -125887,7 +124673,7 @@ var init_claude_prospective_review = __esm(() => {
|
|
|
125887
124673
|
|
|
125888
124674
|
// src/lib/codex-plugin-reconcile.ts
|
|
125889
124675
|
import { realpathSync as realpathSync25 } from "fs";
|
|
125890
|
-
import { join as
|
|
124676
|
+
import { join as join81, resolve as resolve46 } from "path";
|
|
125891
124677
|
function boundedMessage(text5) {
|
|
125892
124678
|
const line = text5.replace(/[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g, " ").trim();
|
|
125893
124679
|
return line.length > 800 ? `${line.slice(0, 797)}...` : line;
|
|
@@ -125930,11 +124716,11 @@ async function runCodexPluginReconcile(options) {
|
|
|
125930
124716
|
return resolve46(path2);
|
|
125931
124717
|
}
|
|
125932
124718
|
};
|
|
125933
|
-
if (canonical9(options.codexHome) !== canonical9(
|
|
124719
|
+
if (canonical9(options.codexHome) !== canonical9(join81(home, ".codex")))
|
|
125934
124720
|
throw new CodexPluginReconcileRefusal("CODEX_RECONCILE_CODEX_HOME_UNSUPPORTED", "CODEX_HOME names a Codex home other than the one the Skills binding reviews");
|
|
125935
124721
|
}
|
|
125936
124722
|
const capture2 = options.captureCatalog ?? captureCodexNativeSkillCatalog;
|
|
125937
|
-
const remoteReceipts = captureCodexRemoteReceiptSnapshot(
|
|
124723
|
+
const remoteReceipts = captureCodexRemoteReceiptSnapshot(join81(home, ".codex/plugins/cache"));
|
|
125938
124724
|
const captured = await capture2({ command: options.codexCommand ?? "codex", home, ...options.codexHome === undefined ? {} : { codexHome: options.codexHome }, cwd: projectDir });
|
|
125939
124725
|
const catalog = captured && typeof captured === "object" ? { ...captured, remoteReceipts } : captured;
|
|
125940
124726
|
base3.codexVersion = typeof catalog?.version === "string" ? catalog.version : null;
|
|
@@ -125996,7 +124782,7 @@ var init_codex_plugin_reconcile = __esm(() => {
|
|
|
125996
124782
|
|
|
125997
124783
|
// src/lib/claude-installer-binding.ts
|
|
125998
124784
|
import { createHash as createHash52 } from "crypto";
|
|
125999
|
-
import { join as
|
|
124785
|
+
import { join as join82 } from "path";
|
|
126000
124786
|
function readPolicy(dataDir3) {
|
|
126001
124787
|
let snapshot2;
|
|
126002
124788
|
try {
|
|
@@ -126008,7 +124794,7 @@ function readPolicy(dataDir3) {
|
|
|
126008
124794
|
}
|
|
126009
124795
|
function verifyClaudeInstallerDiscoveryBinding(policyPath) {
|
|
126010
124796
|
const dataDir3 = getDataDirReadOnly();
|
|
126011
|
-
if (policyPath !==
|
|
124797
|
+
if (policyPath !== join82(dataDir3, "agent-policy.json"))
|
|
126012
124798
|
refuse9("POLICY_PATH_MISMATCH");
|
|
126013
124799
|
const snapshot2 = readPolicy(dataDir3);
|
|
126014
124800
|
const bridge = snapshot2.value.bridge;
|
|
@@ -126087,8 +124873,8 @@ var init_claude_installer_binding = __esm(() => {
|
|
|
126087
124873
|
});
|
|
126088
124874
|
|
|
126089
124875
|
// src/cli/commands/claude-installer-capture.ts
|
|
126090
|
-
import { closeSync as
|
|
126091
|
-
import { isAbsolute as
|
|
124876
|
+
import { closeSync as closeSync33, constants as constants33, fstatSync as fstatSync32, lstatSync as lstatSync52, openSync as openSync33, readSync as readSync25 } from "fs";
|
|
124877
|
+
import { isAbsolute as isAbsolute48, resolve as resolve47 } from "path";
|
|
126092
124878
|
function need11(condition, code2) {
|
|
126093
124879
|
if (!condition)
|
|
126094
124880
|
throw new CaptureRefusal(code2);
|
|
@@ -126102,16 +124888,16 @@ function exactKeys5(value, expected, operation) {
|
|
|
126102
124888
|
}
|
|
126103
124889
|
}
|
|
126104
124890
|
function canonicalPath2(value) {
|
|
126105
|
-
need11(typeof value === "string" &&
|
|
124891
|
+
need11(typeof value === "string" && isAbsolute48(value) && resolve47(value) === value && !/[\x00-\x1f\x7f]/.test(value), "INVALID_PATH: path fields must be normalized absolute paths without control characters");
|
|
126106
124892
|
}
|
|
126107
124893
|
function readRequest2(path2) {
|
|
126108
124894
|
canonicalPath2(path2);
|
|
126109
124895
|
const before2 = lstatSync52(path2, { throwIfNoEntry: false });
|
|
126110
124896
|
if (!before2?.isFile() || before2.isSymbolicLink() || before2.size > MAX_REQUEST_BYTES)
|
|
126111
124897
|
throw new CaptureRefusal("INVALID_REQUEST_FILE: --request must name a regular non-symlink JSON file no larger than 64 KiB");
|
|
126112
|
-
const fd =
|
|
124898
|
+
const fd = openSync33(path2, constants33.O_RDONLY | (constants33.O_NOFOLLOW ?? 0) | constants33.O_NONBLOCK);
|
|
126113
124899
|
try {
|
|
126114
|
-
const opened =
|
|
124900
|
+
const opened = fstatSync32(fd);
|
|
126115
124901
|
need11(opened.isFile() && opened.dev === before2.dev && opened.ino === before2.ino && opened.size === before2.size, "INVALID_REQUEST_FILE: --request must remain the same regular file while read");
|
|
126116
124902
|
const bytes = Buffer.alloc(MAX_REQUEST_BYTES + 1);
|
|
126117
124903
|
let length = 0;
|
|
@@ -126121,7 +124907,7 @@ function readRequest2(path2) {
|
|
|
126121
124907
|
break;
|
|
126122
124908
|
length += count;
|
|
126123
124909
|
}
|
|
126124
|
-
const after2 =
|
|
124910
|
+
const after2 = fstatSync32(fd), current = lstatSync52(path2);
|
|
126125
124911
|
need11(length <= MAX_REQUEST_BYTES && length === opened.size && after2.size === opened.size && after2.mtimeMs === opened.mtimeMs && after2.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "INVALID_REQUEST_FILE: --request must remain unchanged while read");
|
|
126126
124912
|
try {
|
|
126127
124913
|
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length)));
|
|
@@ -126129,7 +124915,7 @@ function readRequest2(path2) {
|
|
|
126129
124915
|
throw new CaptureRefusal("INVALID_REQUEST_JSON: --request must contain one valid UTF-8 JSON object");
|
|
126130
124916
|
}
|
|
126131
124917
|
} finally {
|
|
126132
|
-
|
|
124918
|
+
closeSync33(fd);
|
|
126133
124919
|
}
|
|
126134
124920
|
}
|
|
126135
124921
|
function paths(value) {
|
|
@@ -126283,10 +125069,10 @@ __export(exports_agent_integration, {
|
|
|
126283
125069
|
MANAGED_HOOK_CONTEXT_MAX_SKILLS: () => MANAGED_HOOK_CONTEXT_MAX_SKILLS,
|
|
126284
125070
|
MANAGED_HOOK_CONTEXT_MAX_CHARS: () => MANAGED_HOOK_CONTEXT_MAX_CHARS
|
|
126285
125071
|
});
|
|
126286
|
-
import { existsSync as existsSync36, lstatSync as lstatSync53, readFileSync as
|
|
125072
|
+
import { existsSync as existsSync36, lstatSync as lstatSync53, readFileSync as readFileSync49, readSync as readSync26, writeFileSync as writeFileSync29 } from "fs";
|
|
126287
125073
|
import { createHash as createHash53 } from "crypto";
|
|
126288
125074
|
import { homedir as homedir15 } from "os";
|
|
126289
|
-
import { basename as basename27, isAbsolute as
|
|
125075
|
+
import { basename as basename27, isAbsolute as isAbsolute49, resolve as resolve48 } from "path";
|
|
126290
125076
|
function boundedEnvInteger(name, fallback, [min2, max2]) {
|
|
126291
125077
|
const raw = process.env[name];
|
|
126292
125078
|
if (raw === undefined || !/^[0-9]{1,6}$/.test(raw.trim()))
|
|
@@ -126470,7 +125256,7 @@ function registerAgentIntegration(parent) {
|
|
|
126470
125256
|
});
|
|
126471
125257
|
hook.command("native-catalog").requiredOption("--cwd <path>", "Absolute project directory observed by native Codex skills/list").requiredOption("--output <file>", "New private file for the projected native catalog").option("--codex-command <path>", "Installed Codex executable used by the native client", "codex").option("--json", "Output bounded capture metadata as JSON", false).description("Capture a reviewed Codex native skill catalog for exact hook enrollment").action(async (options) => {
|
|
126472
125258
|
try {
|
|
126473
|
-
if (!
|
|
125259
|
+
if (!isAbsolute49(options.cwd) || !isAbsolute49(options.output))
|
|
126474
125260
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_ABSOLUTE_PATH_REQUIRED");
|
|
126475
125261
|
if (existsSync36(options.output))
|
|
126476
125262
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_OUTPUT_EXISTS");
|
|
@@ -126495,7 +125281,7 @@ function registerAgentIntegration(parent) {
|
|
|
126495
125281
|
throw error61;
|
|
126496
125282
|
}
|
|
126497
125283
|
const written = lstatSync53(options.output);
|
|
126498
|
-
if (!written.isFile() || written.isSymbolicLink() || (written.mode & 63) !== 0 ||
|
|
125284
|
+
if (!written.isFile() || written.isSymbolicLink() || (written.mode & 63) !== 0 || readFileSync49(options.output, "utf8") !== bytes)
|
|
126499
125285
|
throw new Error("CODEX_NATIVE_SKILL_CATALOG_READBACK_FAILED");
|
|
126500
125286
|
const receipt = {
|
|
126501
125287
|
version: catalog.version,
|
|
@@ -126572,12 +125358,12 @@ function registerAgentIntegration(parent) {
|
|
|
126572
125358
|
});
|
|
126573
125359
|
hook.command("review-claude-candidate").requiredOption("--request <file>", "Frozen skills.claude-plugin-prospective-review/v1 JSON request").option("--json", "Return the validation-only receipt as JSON", false).description("Validate a prospective Claude plugin candidate and current preimages without changing native state; Harnesses must compare native and Skills paths against installed package-owned resolvers before trusting the receipt").action(async (options) => {
|
|
126574
125360
|
try {
|
|
126575
|
-
if (!
|
|
125361
|
+
if (!isAbsolute49(options.request))
|
|
126576
125362
|
throw new Error("CLAUDE_CANDIDATE_REVIEW_REFUSED: --request must be an absolute path");
|
|
126577
125363
|
const stat3 = lstatSync53(options.request);
|
|
126578
125364
|
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > 1024 * 1024)
|
|
126579
125365
|
throw new Error("CLAUDE_CANDIDATE_REVIEW_REFUSED: request must be a regular file no larger than 1 MiB");
|
|
126580
|
-
const requestBytes =
|
|
125366
|
+
const requestBytes = readFileSync49(options.request);
|
|
126581
125367
|
const request2 = JSON.parse(requestBytes.toString("utf8"));
|
|
126582
125368
|
const receipt = reviewClaudeProspectiveCandidate(request2, createHash53("sha256").update(requestBytes).digest("hex"));
|
|
126583
125369
|
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Claude plugin candidate review accepted for ${receipt.pluginIds.length} selected plugin(s); native state was not changed.`);
|
|
@@ -126651,8 +125437,8 @@ function registerAgentIntegration(parent) {
|
|
|
126651
125437
|
});
|
|
126652
125438
|
hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all (default: configured or detected agents; explicit all requires every adapter)`).option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--codex-skill-denials <file>", "Explicit reviewed remote skill denials: array of exact name, path and document sha256; requires fresh catalog and discovery inputs").option("--codex-native-catalog <file>", "Reviewed supported Codex skill and installed-plugin receipt for exact qualified-name disables").option("--reviewed-cache-alias <path>", "Exact skill-containing vendor cache alias reviewed for this hook plan").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
|
|
126653
125439
|
try {
|
|
126654
|
-
const discoveryInputs = options.discoveryInputs ? JSON.parse(
|
|
126655
|
-
const plan = planAgentIntegration({ projectDir: process.cwd(), agents: options.agent === undefined ? detectedIntegrationAgents() : agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, codexSkillDenials: options.codexSkillDenials ? JSON.parse(
|
|
125440
|
+
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync49(options.discoveryInputs, "utf8")) : undefined;
|
|
125441
|
+
const plan = planAgentIntegration({ projectDir: process.cwd(), agents: options.agent === undefined ? detectedIntegrationAgents() : agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, codexSkillDenials: options.codexSkillDenials ? JSON.parse(readFileSync49(options.codexSkillDenials, "utf8")) : undefined, codexNativeCatalog: options.codexNativeCatalog ? JSON.parse(readFileSync49(options.codexNativeCatalog, "utf8")) : undefined });
|
|
126656
125442
|
const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
|
|
126657
125443
|
const receipt = { codexPluginSkillReview: plan.codexPluginSkillReview, codexPluginSkills: plan.changes.filter((change) => change.path.endsWith("agent-policy.json")).map((change) => JSON.parse(change.after).bridge?.codexPluginSkills ?? []).flat(), applied: options.apply, planned: plan.changes.map((change) => change.path), ...result2, rootAliases: plan.rootAliases ?? [], discovery: plan.discoveryAfter, nativeSkills: plan.nativeSkills.map((entry) => ({ agent: entry.agent, path: entry.path, managed: entry.managed, vendor: entry.vendor, system: entry.system === true, bridge: entry.bridge === true })), requiresNativeRetirement: plan.nativeSkills.some((entry) => !entry.bridge && !entry.system) };
|
|
126658
125444
|
if (options.json)
|
|
@@ -126714,7 +125500,7 @@ function registerAgentIntegration(parent) {
|
|
|
126714
125500
|
try {
|
|
126715
125501
|
if (agents(options.agent).length !== 1)
|
|
126716
125502
|
throw new Error("A hook invocation requires one agent");
|
|
126717
|
-
const inputBytes =
|
|
125503
|
+
const inputBytes = readFileSync49(0), inputText = inputBytes.toString("utf8");
|
|
126718
125504
|
if (inputText.length > 1024 * 1024)
|
|
126719
125505
|
throw new Error("Hook input is too large");
|
|
126720
125506
|
let input3 = JSON.parse(inputText);
|
|
@@ -126727,12 +125513,12 @@ function registerAgentIntegration(parent) {
|
|
|
126727
125513
|
input3.hook_event_name = event;
|
|
126728
125514
|
const projects = [process.cwd()];
|
|
126729
125515
|
if (input3.cwd !== undefined) {
|
|
126730
|
-
if (typeof input3.cwd !== "string" || !
|
|
125516
|
+
if (typeof input3.cwd !== "string" || !isAbsolute49(input3.cwd) || input3.cwd.includes("\x00"))
|
|
126731
125517
|
throw new Error("Invalid native hook working directory");
|
|
126732
125518
|
projects.push(input3.cwd);
|
|
126733
125519
|
}
|
|
126734
125520
|
if (options.agent === "cursor") {
|
|
126735
|
-
if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path2) => typeof path2 !== "string" || !
|
|
125521
|
+
if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path2) => typeof path2 !== "string" || !isAbsolute49(path2) || path2.includes("\x00")))
|
|
126736
125522
|
throw new Error("Invalid Cursor workspace roots");
|
|
126737
125523
|
projects.push(...input3.workspace_roots);
|
|
126738
125524
|
input3.cwd ??= input3.workspace_roots[0] ?? process.cwd();
|
|
@@ -126880,7 +125666,7 @@ ${driftWarning}` : unavailable2, ...output2 }));
|
|
|
126880
125666
|
try {
|
|
126881
125667
|
if (options.agent && !INTEGRATION_AGENTS.includes(options.agent))
|
|
126882
125668
|
throw new Error(`Unsupported migration agent: ${options.agent}`);
|
|
126883
|
-
const discoveryInputs = options.discoveryInputs ? JSON.parse(
|
|
125669
|
+
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync49(options.discoveryInputs, "utf8")) : undefined;
|
|
126884
125670
|
if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
|
|
126885
125671
|
throw new Error("--target-manifest cannot be combined with broad native migration selectors");
|
|
126886
125672
|
const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
|
|
@@ -127075,11 +125861,11 @@ var exports_profiles = {};
|
|
|
127075
125861
|
__export(exports_profiles, {
|
|
127076
125862
|
registerProfiles: () => registerProfiles
|
|
127077
125863
|
});
|
|
127078
|
-
import { closeSync as
|
|
125864
|
+
import { closeSync as closeSync34, constants as constants34, fstatSync as fstatSync33, openSync as openSync34, readSync as readSync27, writeFileSync as writeFileSync30 } from "fs";
|
|
127079
125865
|
function readProfileInput(path2) {
|
|
127080
|
-
const fd =
|
|
125866
|
+
const fd = openSync34(path2, constants34.O_RDONLY | constants34.O_NONBLOCK);
|
|
127081
125867
|
try {
|
|
127082
|
-
const stat3 =
|
|
125868
|
+
const stat3 = fstatSync33(fd);
|
|
127083
125869
|
if (!stat3.isFile() || stat3.size > MAX_PROFILE_DOCUMENT_BYTES)
|
|
127084
125870
|
throw new Error("Profile input exceeds the size limit or is not a regular file");
|
|
127085
125871
|
const bytes = Buffer.alloc(MAX_PROFILE_DOCUMENT_BYTES + 1);
|
|
@@ -127094,7 +125880,7 @@ function readProfileInput(path2) {
|
|
|
127094
125880
|
throw new Error("Profile input exceeds the size limit");
|
|
127095
125881
|
return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size2));
|
|
127096
125882
|
} finally {
|
|
127097
|
-
|
|
125883
|
+
closeSync34(fd);
|
|
127098
125884
|
}
|
|
127099
125885
|
}
|
|
127100
125886
|
function registerProfiles(parent) {
|
|
@@ -127509,16 +126295,16 @@ __export(exports_grants, {
|
|
|
127509
126295
|
});
|
|
127510
126296
|
import {
|
|
127511
126297
|
constants as constants35,
|
|
127512
|
-
closeSync as
|
|
127513
|
-
fstatSync as
|
|
127514
|
-
openSync as
|
|
126298
|
+
closeSync as closeSync35,
|
|
126299
|
+
fstatSync as fstatSync34,
|
|
126300
|
+
openSync as openSync35,
|
|
127515
126301
|
readSync as readSync28,
|
|
127516
126302
|
writeFileSync as writeFileSync31
|
|
127517
126303
|
} from "fs";
|
|
127518
126304
|
function readInput(path2) {
|
|
127519
|
-
const fd =
|
|
126305
|
+
const fd = openSync35(path2, constants35.O_RDONLY | constants35.O_NONBLOCK);
|
|
127520
126306
|
try {
|
|
127521
|
-
const stat3 =
|
|
126307
|
+
const stat3 = fstatSync34(fd);
|
|
127522
126308
|
if (!stat3.isFile() || stat3.size > MAX_EXECUTION_GRANT_BYTES)
|
|
127523
126309
|
throw new Error("Grant input must be a regular file within the policy size limit.");
|
|
127524
126310
|
const bytes = Buffer.alloc(MAX_EXECUTION_GRANT_BYTES + 1);
|
|
@@ -127537,7 +126323,7 @@ function readInput(path2) {
|
|
|
127537
126323
|
throw new Error("Grant input must be a JSON document containing a grants array.");
|
|
127538
126324
|
}
|
|
127539
126325
|
} finally {
|
|
127540
|
-
|
|
126326
|
+
closeSync35(fd);
|
|
127541
126327
|
}
|
|
127542
126328
|
}
|
|
127543
126329
|
function registerGrants(parent) {
|
|
@@ -127578,23 +126364,23 @@ import {
|
|
|
127578
126364
|
copyFileSync as copyFileSync3,
|
|
127579
126365
|
mkdirSync as mkdirSync33,
|
|
127580
126366
|
readdirSync as readdirSync30,
|
|
127581
|
-
readFileSync as
|
|
126367
|
+
readFileSync as readFileSync50,
|
|
127582
126368
|
statSync as statSync26,
|
|
127583
126369
|
writeFileSync as writeFileSync32
|
|
127584
126370
|
} from "fs";
|
|
127585
|
-
import { dirname as dirname53, join as
|
|
126371
|
+
import { dirname as dirname53, join as join83, resolve as resolve49, sep as sep24 } from "path";
|
|
127586
126372
|
function fail7(code2, message, detail = []) {
|
|
127587
126373
|
throw new StationSnapshotError(code2, message, detail);
|
|
127588
126374
|
}
|
|
127589
126375
|
function snapshotRootFor(repoRoot, stationId) {
|
|
127590
|
-
return
|
|
126376
|
+
return join83(repoRoot, "resources", stationId, "skills");
|
|
127591
126377
|
}
|
|
127592
126378
|
function readSnapshotManifest(repoRoot, stationId) {
|
|
127593
126379
|
const snapshotRoot = snapshotRootFor(repoRoot, stationId);
|
|
127594
|
-
const manifestPath =
|
|
126380
|
+
const manifestPath = join83(snapshotRoot, "sync-manifest.json");
|
|
127595
126381
|
let manifest;
|
|
127596
126382
|
try {
|
|
127597
|
-
manifest = JSON.parse(
|
|
126383
|
+
manifest = JSON.parse(readFileSync50(manifestPath, "utf8"));
|
|
127598
126384
|
} catch (error61) {
|
|
127599
126385
|
fail7("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
|
|
127600
126386
|
}
|
|
@@ -127618,7 +126404,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
127618
126404
|
const hashMismatches = [];
|
|
127619
126405
|
const skippedByRule = [];
|
|
127620
126406
|
for (const agent of SYNC_AGENTS) {
|
|
127621
|
-
const agentRoot =
|
|
126407
|
+
const agentRoot = join83(snapshotRoot, "agent-homes", agent);
|
|
127622
126408
|
let identEntries;
|
|
127623
126409
|
try {
|
|
127624
126410
|
identEntries = readdirSync30(agentRoot, { withFileTypes: true });
|
|
@@ -127629,7 +126415,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
127629
126415
|
if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
|
|
127630
126416
|
continue;
|
|
127631
126417
|
}
|
|
127632
|
-
const identRoot =
|
|
126418
|
+
const identRoot = join83(agentRoot, identEntry.name);
|
|
127633
126419
|
const entries = walkEntries(identRoot);
|
|
127634
126420
|
for (const entry of entries) {
|
|
127635
126421
|
const relativeParts = [identEntry.name, ...entry.relativePath.split(sep24)];
|
|
@@ -127730,7 +126516,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
127730
126516
|
for (const copy of copies) {
|
|
127731
126517
|
let isStub = false;
|
|
127732
126518
|
try {
|
|
127733
|
-
isStub = isPointerSkillMd(
|
|
126519
|
+
isStub = isPointerSkillMd(readFileSync50(copy.fullPath, "utf8"));
|
|
127734
126520
|
} catch {
|
|
127735
126521
|
isStub = false;
|
|
127736
126522
|
}
|
|
@@ -127814,7 +126600,7 @@ function writeStationHydration(options) {
|
|
|
127814
126600
|
const toWrite = [];
|
|
127815
126601
|
for (const skill of plan.winners) {
|
|
127816
126602
|
for (const file2 of skill.files) {
|
|
127817
|
-
const destination =
|
|
126603
|
+
const destination = join83(cacheRoot, skill.ident, file2.withinIdent);
|
|
127818
126604
|
const digest7 = sha256File(file2.winner.fullPath);
|
|
127819
126605
|
let existingDigest = null;
|
|
127820
126606
|
try {
|
|
@@ -127856,7 +126642,7 @@ function writeStationHydration(options) {
|
|
|
127856
126642
|
},
|
|
127857
126643
|
skills: resultSkills
|
|
127858
126644
|
};
|
|
127859
|
-
const hydrationManifestPath =
|
|
126645
|
+
const hydrationManifestPath = join83(dirname53(cacheRoot), `hydration-${options.stationId}.json`);
|
|
127860
126646
|
mkdirSync33(dirname53(hydrationManifestPath), { recursive: true });
|
|
127861
126647
|
writeFileSync32(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
127862
126648
|
`);
|
|
@@ -127934,14 +126720,14 @@ var init_hydrate = __esm(() => {
|
|
|
127934
126720
|
|
|
127935
126721
|
// src/lib/prepare-skill.ts
|
|
127936
126722
|
import { createHash as createHash55, randomUUID as randomUUID17 } from "crypto";
|
|
127937
|
-
import { chmodSync as chmodSync5, existsSync as existsSync37, lstatSync as lstatSync54, mkdirSync as mkdirSync34, mkdtempSync as mkdtempSync7, readFileSync as
|
|
126723
|
+
import { chmodSync as chmodSync5, existsSync as existsSync37, lstatSync as lstatSync54, mkdirSync as mkdirSync34, mkdtempSync as mkdtempSync7, readFileSync as readFileSync51, readdirSync as readdirSync31, renameSync as renameSync18, rmSync as rmSync10, writeFileSync as writeFileSync33 } from "fs";
|
|
127938
126724
|
import { tmpdir as tmpdir2 } from "os";
|
|
127939
|
-
import { dirname as dirname54, isAbsolute as
|
|
126725
|
+
import { dirname as dirname54, isAbsolute as isAbsolute50, join as join84, relative as relative17, resolve as resolve50, sep as sep25 } from "path";
|
|
127940
126726
|
function prepareSkill(name, options) {
|
|
127941
126727
|
const normalized2 = normalizePortableSkillName(name);
|
|
127942
126728
|
if (!new RegExp(SEMVER_PATTERN).test(options.version))
|
|
127943
126729
|
throw new Error("--version must be a semantic version, for example 0.2.0.");
|
|
127944
|
-
const path2 =
|
|
126730
|
+
const path2 = join84(getPortableSkillsRootReadOnly(options), normalized2);
|
|
127945
126731
|
assertPortableAuthoringPath(path2, options);
|
|
127946
126732
|
const before2 = snapshot2(path2);
|
|
127947
126733
|
const manifestFile = before2.files.find((file2) => file2.path === "skill.json");
|
|
@@ -127976,16 +126762,16 @@ function prepareSkill(name, options) {
|
|
|
127976
126762
|
throw new Error("skill.json provenance must be an object.");
|
|
127977
126763
|
const provenance = manifest.provenance;
|
|
127978
126764
|
const candidate2 = { ...manifest, version: options.version, kind, provenance: { ...provenance } };
|
|
127979
|
-
const staging = mkdtempSync7(
|
|
126765
|
+
const staging = mkdtempSync7(join84(tmpdir2(), "skills-prepare-"));
|
|
127980
126766
|
try {
|
|
127981
126767
|
for (const directory3 of before2.directories)
|
|
127982
|
-
mkdirSync34(
|
|
126768
|
+
mkdirSync34(join84(staging, directory3), { recursive: true });
|
|
127983
126769
|
for (const file2 of before2.files) {
|
|
127984
|
-
const destination =
|
|
126770
|
+
const destination = join84(staging, file2.path);
|
|
127985
126771
|
mkdirSync34(dirname54(destination), { recursive: true });
|
|
127986
126772
|
writeFileSync33(destination, file2.bytes, { mode: file2.mode });
|
|
127987
126773
|
}
|
|
127988
|
-
const stagedManifest =
|
|
126774
|
+
const stagedManifest = join84(staging, "skill.json");
|
|
127989
126775
|
writeFileSync33(stagedManifest, `${JSON.stringify(candidate2, null, 2)}
|
|
127990
126776
|
`);
|
|
127991
126777
|
const contentHash = computeContentHash(staging);
|
|
@@ -128008,11 +126794,11 @@ ${validation.issues.map((issue2) => `${issue2.code}: ${issue2.message}`).join(`
|
|
|
128008
126794
|
if (snapshot2(path2).identity !== before2.identity)
|
|
128009
126795
|
throw new Error("Skill changed during preparation; review the draft and run prepare again.");
|
|
128010
126796
|
if (changed3 && !options.dryRun) {
|
|
128011
|
-
const temporary =
|
|
126797
|
+
const temporary = join84(path2, `.skill-prepare-${randomUUID17()}.tmp`);
|
|
128012
126798
|
try {
|
|
128013
126799
|
writeFileSync33(temporary, candidateBytes, { flag: "wx", mode: manifestFile.mode });
|
|
128014
126800
|
chmodSync5(temporary, manifestFile.mode);
|
|
128015
|
-
renameSync18(temporary,
|
|
126801
|
+
renameSync18(temporary, join84(path2, "skill.json"));
|
|
128016
126802
|
} finally {
|
|
128017
126803
|
rmSync10(temporary, { force: true });
|
|
128018
126804
|
}
|
|
@@ -128043,7 +126829,7 @@ function validateRuntimeEntrypoint(candidate2, root2) {
|
|
|
128043
126829
|
if (typeof entry !== "string")
|
|
128044
126830
|
return;
|
|
128045
126831
|
const target = resolve50(root2, entry), rel = relative17(root2, target);
|
|
128046
|
-
if (
|
|
126832
|
+
if (isAbsolute50(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep25}`) || isAbsolute50(rel) || !existsSync37(target) || !lstatSync54(target).isFile()) {
|
|
128047
126833
|
throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
|
|
128048
126834
|
}
|
|
128049
126835
|
}
|
|
@@ -128135,13 +126921,13 @@ function snapshot2(root2) {
|
|
|
128135
126921
|
const hash8 = createHash55("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
|
|
128136
126922
|
let count = 0, bytes = 0;
|
|
128137
126923
|
const walk2 = (relative18) => {
|
|
128138
|
-
for (const name of readdirSync31(
|
|
126924
|
+
for (const name of readdirSync31(join84(root2, relative18)).sort()) {
|
|
128139
126925
|
if (IGNORED.has(name.toLowerCase()) || !relative18 && ROOT_IGNORED.has(name.toLowerCase()))
|
|
128140
126926
|
continue;
|
|
128141
126927
|
const path2 = relative18 ? `${relative18}/${name}` : name;
|
|
128142
126928
|
if (++count > 20000)
|
|
128143
126929
|
throw new Error("Skill exceeds the preparation entry limit (20000).");
|
|
128144
|
-
const absolute =
|
|
126930
|
+
const absolute = join84(root2, path2), stat3 = lstatSync54(absolute);
|
|
128145
126931
|
if (stat3.isSymbolicLink())
|
|
128146
126932
|
throw new Error(`Skill contains a symlink: ${path2}. No files were changed.`);
|
|
128147
126933
|
if (stat3.isDirectory() && name === "node_modules")
|
|
@@ -128154,7 +126940,7 @@ function snapshot2(root2) {
|
|
|
128154
126940
|
bytes += stat3.size;
|
|
128155
126941
|
if (bytes > 50000000)
|
|
128156
126942
|
throw new Error("Skill exceeds the preparation size limit (50000000 bytes).");
|
|
128157
|
-
const content =
|
|
126943
|
+
const content = readFileSync51(absolute);
|
|
128158
126944
|
if (content.length !== stat3.size)
|
|
128159
126945
|
throw new Error("Skill changed during preparation.");
|
|
128160
126946
|
files.push({ path: path2, bytes: content, mode: stat3.mode & 511 });
|
|
@@ -128320,11 +127106,11 @@ var exports_metadata = {};
|
|
|
128320
127106
|
__export(exports_metadata, {
|
|
128321
127107
|
registerMetadata: () => registerMetadata
|
|
128322
127108
|
});
|
|
128323
|
-
import { closeSync as
|
|
127109
|
+
import { closeSync as closeSync36, constants as constants36, fstatSync as fstatSync35, openSync as openSync36, readFileSync as readFileSync52, readSync as readSync29, writeFileSync as writeFileSync34 } from "fs";
|
|
128324
127110
|
function readPlan(path2) {
|
|
128325
|
-
const fd =
|
|
127111
|
+
const fd = openSync36(path2, constants36.O_RDONLY | constants36.O_NONBLOCK | constants36.O_NOFOLLOW);
|
|
128326
127112
|
try {
|
|
128327
|
-
const stat3 =
|
|
127113
|
+
const stat3 = fstatSync35(fd);
|
|
128328
127114
|
if (!stat3.isFile() || stat3.size > 8 * 1024 * 1024)
|
|
128329
127115
|
throw new SkillMetadataUpdateError("INVALID_METADATA_PLAN");
|
|
128330
127116
|
const bytes = Buffer.alloc(stat3.size + 1);
|
|
@@ -128339,7 +127125,7 @@ function readPlan(path2) {
|
|
|
128339
127125
|
throw new SkillMetadataUpdateError("INVALID_METADATA_PLAN");
|
|
128340
127126
|
return metadataPlan(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size2))));
|
|
128341
127127
|
} finally {
|
|
128342
|
-
|
|
127128
|
+
closeSync36(fd);
|
|
128343
127129
|
}
|
|
128344
127130
|
}
|
|
128345
127131
|
function failure(error61) {
|
|
@@ -128360,7 +127146,7 @@ function registerMetadata(parent) {
|
|
|
128360
127146
|
const bytes = `${JSON.stringify(plan)}
|
|
128361
127147
|
`;
|
|
128362
127148
|
writeFileSync34(options.save, bytes, { mode: 384, flag: "wx" });
|
|
128363
|
-
if (
|
|
127149
|
+
if (readFileSync52(options.save, "utf8") !== bytes)
|
|
128364
127150
|
throw new SkillMetadataUpdateError("INVALID_METADATA_PLAN");
|
|
128365
127151
|
await writeCliOutput(JSON.stringify(metadataPlanSummary(plan), null, 2));
|
|
128366
127152
|
} catch (error61) {
|
|
@@ -128705,8 +127491,8 @@ var init_registry_sync = __esm(() => {
|
|
|
128705
127491
|
});
|
|
128706
127492
|
|
|
128707
127493
|
// src/lib/review-export.ts
|
|
128708
|
-
import { constants as constants37, closeSync as
|
|
128709
|
-
import { basename as basename28, dirname as dirname56, isAbsolute as
|
|
127494
|
+
import { constants as constants37, closeSync as closeSync37, existsSync as existsSync38, fstatSync as fstatSync36, fsyncSync as fsyncSync12, lstatSync as lstatSync55, openSync as openSync37, realpathSync as realpathSync26, statSync as statSync27, writeFileSync as writeFileSync36 } from "fs";
|
|
127495
|
+
import { basename as basename28, dirname as dirname56, isAbsolute as isAbsolute51, join as join85, parse as parse13, resolve as resolve51, sep as sep26 } from "path";
|
|
128710
127496
|
function exactVersion(spec) {
|
|
128711
127497
|
const at2 = spec.indexOf("@");
|
|
128712
127498
|
const slug2 = spec.slice(0, at2);
|
|
@@ -128717,7 +127503,7 @@ function exactVersion(spec) {
|
|
|
128717
127503
|
return { slug: slug2, version: version2 };
|
|
128718
127504
|
}
|
|
128719
127505
|
function assertPrivateDestination(output2) {
|
|
128720
|
-
if (!
|
|
127506
|
+
if (!isAbsolute51(output2) || resolve51(output2) !== output2 || !basename28(output2).endsWith(".tar.gz")) {
|
|
128721
127507
|
throw new Error("Review output must be an absolute, normalized .tar.gz path.");
|
|
128722
127508
|
}
|
|
128723
127509
|
const parent = dirname56(output2);
|
|
@@ -128733,7 +127519,7 @@ function assertPrivateDestination(output2) {
|
|
|
128733
127519
|
throw new Error("Review output cannot be placed in agent-native discovery or Skills installation paths.");
|
|
128734
127520
|
}
|
|
128735
127521
|
for (let current = parent;; current = dirname56(current)) {
|
|
128736
|
-
if (existsSync38(
|
|
127522
|
+
if (existsSync38(join85(current, ".git"))) {
|
|
128737
127523
|
throw new Error("Review output cannot be placed inside a Git worktree.");
|
|
128738
127524
|
}
|
|
128739
127525
|
if (current === parse13(current).root)
|
|
@@ -128769,16 +127555,16 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
128769
127555
|
if (bytes.byteLength !== metadata2.bundleByteSize || sha256Hex(bytes) !== metadata2.bundleSha256.toLowerCase()) {
|
|
128770
127556
|
throw new Error("Published bundle bytes do not match the exact version metadata.");
|
|
128771
127557
|
}
|
|
128772
|
-
const fd =
|
|
127558
|
+
const fd = openSync37(target, constants37.O_WRONLY | constants37.O_CREAT | constants37.O_EXCL | constants37.O_NOFOLLOW, 384);
|
|
128773
127559
|
try {
|
|
128774
127560
|
writeFileSync36(fd, bytes);
|
|
128775
127561
|
fsyncSync12(fd);
|
|
128776
|
-
if (
|
|
127562
|
+
if (fstatSync36(fd).size !== bytes.byteLength)
|
|
128777
127563
|
throw new Error("Incomplete review output write.");
|
|
128778
127564
|
} catch (error61) {
|
|
128779
127565
|
throw new Error(`Review output write failed. A private partial file may remain at ${target}; use Trash to remove it before reusing this path.`, { cause: error61 });
|
|
128780
127566
|
} finally {
|
|
128781
|
-
|
|
127567
|
+
closeSync37(fd);
|
|
128782
127568
|
}
|
|
128783
127569
|
return { slug: slug2, version: version2, bundleSha256: metadata2.bundleSha256.toLowerCase(), bundleByteSize: bytes.byteLength, output: target };
|
|
128784
127570
|
}
|
|
@@ -129018,9 +127804,9 @@ __export(exports_publish, {
|
|
|
129018
127804
|
PushSkillError: () => PushSkillError
|
|
129019
127805
|
});
|
|
129020
127806
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
129021
|
-
import { existsSync as existsSync39, readFileSync as
|
|
127807
|
+
import { existsSync as existsSync39, readFileSync as readFileSync53 } from "fs";
|
|
129022
127808
|
import { hostname as hostname6 } from "os";
|
|
129023
|
-
import { join as
|
|
127809
|
+
import { join as join86 } from "path";
|
|
129024
127810
|
function registerPublish(parent) {
|
|
129025
127811
|
parent.command("push").argument("<name>", "Name of a skill in the local corpus (~/.hasna/skills/installed or the migrated ~/.hasna/skills/skills)").option("--version <version>", "Override the version recorded on the instance").option("--force-new-version", "If name@version already exists with different content, publish as the next patch version", false).option("--dry-run", "Pack and validate without uploading", false).option("--json", "Output result as JSON", false).description("Publish a local skill to the configured skills instance").action(async (name, options) => {
|
|
129026
127812
|
try {
|
|
@@ -129072,8 +127858,8 @@ async function pushSkill(name, options = {}) {
|
|
|
129072
127858
|
const packed = packSkillBundle(skill.path, { maxUnpackedBytes: MAX_UNPACKED_BYTES });
|
|
129073
127859
|
await verifyPackedCanonicalHash(skill.name, packed);
|
|
129074
127860
|
const versionManifest = buildVersionManifest(skill.path, packed);
|
|
129075
|
-
const skillMdPath =
|
|
129076
|
-
const skillMd = existsSync39(skillMdPath) ?
|
|
127861
|
+
const skillMdPath = join86(skill.path, "SKILL.md");
|
|
127862
|
+
const skillMd = existsSync39(skillMdPath) ? readFileSync53(skillMdPath, "utf-8") : undefined;
|
|
129077
127863
|
const base3 = {
|
|
129078
127864
|
slug: skill.name,
|
|
129079
127865
|
path: skill.path,
|
|
@@ -130892,15 +129678,15 @@ function registerStorage(parent) {
|
|
|
130892
129678
|
}
|
|
130893
129679
|
});
|
|
130894
129680
|
}
|
|
130895
|
-
var
|
|
129681
|
+
var init_storage = __esm(() => {
|
|
130896
129682
|
init_source();
|
|
130897
129683
|
init_native_storage();
|
|
130898
129684
|
init_home_migration();
|
|
130899
129685
|
});
|
|
130900
129686
|
|
|
130901
129687
|
// src/lib/registry-reconcile.ts
|
|
130902
|
-
import { existsSync as existsSync40, readFileSync as
|
|
130903
|
-
import { join as
|
|
129688
|
+
import { existsSync as existsSync40, readFileSync as readFileSync54, statSync as statSync28, writeFileSync as writeFileSync37 } from "fs";
|
|
129689
|
+
import { join as join87 } from "path";
|
|
130904
129690
|
function isDirectory2(path2) {
|
|
130905
129691
|
try {
|
|
130906
129692
|
return statSync28(path2).isDirectory();
|
|
@@ -130911,15 +129697,15 @@ function isDirectory2(path2) {
|
|
|
130911
129697
|
function migrationNeeded(options) {
|
|
130912
129698
|
if (options.rootDir)
|
|
130913
129699
|
return false;
|
|
130914
|
-
const appDir = options.homeDir ?
|
|
130915
|
-
return !(isOwnerLayoutMigrated(appDir) && isDirectory2(
|
|
129700
|
+
const appDir = options.homeDir ? join87(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
129701
|
+
return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join87(appDir, SKILLS_CACHE_DIRNAME)));
|
|
130916
129702
|
}
|
|
130917
129703
|
function readBaseline(skillDir) {
|
|
130918
|
-
const markerPath =
|
|
129704
|
+
const markerPath = join87(skillDir, PULL_MARKER_FILE);
|
|
130919
129705
|
if (!existsSync40(markerPath))
|
|
130920
129706
|
return;
|
|
130921
129707
|
try {
|
|
130922
|
-
const marker = JSON.parse(
|
|
129708
|
+
const marker = JSON.parse(readFileSync54(markerPath, "utf-8"));
|
|
130923
129709
|
if (!isSkillsOwnershipMarker(marker))
|
|
130924
129710
|
return;
|
|
130925
129711
|
return {
|
|
@@ -130931,11 +129717,11 @@ function readBaseline(skillDir) {
|
|
|
130931
129717
|
}
|
|
130932
129718
|
}
|
|
130933
129719
|
function readCursor(root2) {
|
|
130934
|
-
const path2 =
|
|
129720
|
+
const path2 = join87(root2, SYNC_CURSOR_FILE);
|
|
130935
129721
|
if (!existsSync40(path2))
|
|
130936
129722
|
return { runCount: 0 };
|
|
130937
129723
|
try {
|
|
130938
|
-
const cursor2 = JSON.parse(
|
|
129724
|
+
const cursor2 = JSON.parse(readFileSync54(path2, "utf-8"));
|
|
130939
129725
|
return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
|
|
130940
129726
|
} catch {
|
|
130941
129727
|
return { runCount: 0 };
|
|
@@ -130944,12 +129730,12 @@ function readCursor(root2) {
|
|
|
130944
129730
|
function resolveCorpusRootReadOnly(options) {
|
|
130945
129731
|
if (options.rootDir)
|
|
130946
129732
|
return { root: options.rootDir, migrationPending: false };
|
|
130947
|
-
const appDir = options.homeDir ?
|
|
130948
|
-
const cache3 =
|
|
129733
|
+
const appDir = options.homeDir ? join87(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
|
|
129734
|
+
const cache3 = join87(appDir, SKILLS_CACHE_DIRNAME);
|
|
130949
129735
|
if (isOwnerLayoutMigrated(appDir) && isDirectory2(cache3)) {
|
|
130950
129736
|
return { root: cache3, migrationPending: false };
|
|
130951
129737
|
}
|
|
130952
|
-
return { root:
|
|
129738
|
+
return { root: join87(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
|
|
130953
129739
|
}
|
|
130954
129740
|
function remoteRowToSkill(record14) {
|
|
130955
129741
|
const slug2 = typeof record14.slug === "string" ? record14.slug : typeof record14.name === "string" ? record14.name : undefined;
|
|
@@ -131083,7 +129869,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
131083
129869
|
for (const slug2 of allSlugs) {
|
|
131084
129870
|
const local = locals.get(slug2);
|
|
131085
129871
|
const remote = remotes.get(slug2);
|
|
131086
|
-
const baseline = local ? readBaseline(
|
|
129872
|
+
const baseline = local ? readBaseline(join87(root2, slug2)) : undefined;
|
|
131087
129873
|
const { state, reason: reason2 } = classifySkill(local, remote, baseline);
|
|
131088
129874
|
let { action, reason: actionReason } = resolveAction(state, direction, conflict);
|
|
131089
129875
|
if (state === "remote-only" && isDigestless(remote)) {
|
|
@@ -131142,7 +129928,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
131142
129928
|
try {
|
|
131143
129929
|
await pushSkill(slug2, { rootDir: root2, client: client2 });
|
|
131144
129930
|
const pushed = locals.get(slug2);
|
|
131145
|
-
writePullMarker(
|
|
129931
|
+
writePullMarker(join87(root2, slug2), {
|
|
131146
129932
|
skill: slug2,
|
|
131147
129933
|
...pushed?.version ? { version: pushed.version } : {},
|
|
131148
129934
|
...pushed?.sha256 ? { contentHash: pushed.sha256 } : {},
|
|
@@ -131214,7 +130000,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
131214
130000
|
runCount: readCursor(root2).runCount + 1,
|
|
131215
130001
|
summary
|
|
131216
130002
|
};
|
|
131217
|
-
writeFileSync37(
|
|
130003
|
+
writeFileSync37(join87(root2, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
|
|
131218
130004
|
`);
|
|
131219
130005
|
return {
|
|
131220
130006
|
corpusRoot: root2,
|
|
@@ -139550,7 +138336,7 @@ var { registerCustomerProfileCommands: registerCustomerProfileCommands2 } = awai
|
|
|
139550
138336
|
registerCustomerProfileCommands2(program2);
|
|
139551
138337
|
var { registerFeedback: registerFeedback2 } = await Promise.resolve().then(() => (init_feedback2(), exports_feedback));
|
|
139552
138338
|
registerFeedback2(program2);
|
|
139553
|
-
var { registerStorage: registerStorage2 } = await Promise.resolve().then(() => (
|
|
138339
|
+
var { registerStorage: registerStorage2 } = await Promise.resolve().then(() => (init_storage(), exports_storage));
|
|
139554
138340
|
registerStorage2(program2);
|
|
139555
138341
|
var { registerRegistryReconcile: registerRegistryReconcile2 } = await Promise.resolve().then(() => (init_registry_reconcile2(), exports_registry_reconcile));
|
|
139556
138342
|
registerRegistryReconcile2(program2);
|