@hasna/hooks 0.9.4 → 0.9.5
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 +13 -2
- package/bin/hooks-mcp.js +73 -51
- package/bin/index.js +377 -126
- package/bin/serve.js +230 -38
- package/dist/index.d.ts +1 -1
- package/dist/index.js +314 -63
- package/dist/lib/resolver-types.d.ts +1 -1
- package/dist/lib/sync.d.ts +3 -2
- package/dist/lib/transport.d.ts +5 -1
- package/dist/sdk/index.d.ts +2 -2
- package/dist/sdk/index.js +275 -46
- package/package.json +3 -3
- package/scripts/validate-package.ts +70 -2
package/dist/index.js
CHANGED
|
@@ -5101,14 +5101,19 @@ var init_profiles = __esm(() => {
|
|
|
5101
5101
|
init_app_home();
|
|
5102
5102
|
});
|
|
5103
5103
|
|
|
5104
|
-
//
|
|
5104
|
+
// ../contracts/dist/client/transport.js
|
|
5105
5105
|
import { isIP } from "net";
|
|
5106
5106
|
import { spawnSync } from "child_process";
|
|
5107
|
-
import { closeSync as closeSync2, fstatSync, openSync as openSync2, readFileSync as
|
|
5108
|
-
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
5109
|
-
import { createRequire } from "module";
|
|
5107
|
+
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync22 } from "fs";
|
|
5108
|
+
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
|
5110
5109
|
import { hostname as osHostname } from "os";
|
|
5111
|
-
import { isAbsolute as isAbsolute2, join as
|
|
5110
|
+
import { isAbsolute as isAbsolute2, join as join22 } from "path";
|
|
5111
|
+
import { createRequire } from "module";
|
|
5112
|
+
import { fileURLToPath as fileURLToPath22 } from "url";
|
|
5113
|
+
import { closeSync as closeSync3, fstatSync, lstatSync as lstatSync2, openSync as openSync3, readFileSync as readFileSync7, realpathSync as realpathSync2, statSync } from "fs";
|
|
5114
|
+
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
5115
|
+
import { basename as basename2, dirname as dirname5, join as join9, relative, sep as sep2 } from "path";
|
|
5116
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
5112
5117
|
function envToken(name) {
|
|
5113
5118
|
return name.toUpperCase().replace(/-/g, "_");
|
|
5114
5119
|
}
|
|
@@ -5125,6 +5130,112 @@ function credentialOverrideEnvKey(name) {
|
|
|
5125
5130
|
function credentialPointerEnvKey(name) {
|
|
5126
5131
|
return `HASNA_${envToken(name)}_API_KEY_REF`;
|
|
5127
5132
|
}
|
|
5133
|
+
function e(e2, n, r) {
|
|
5134
|
+
throw new Error(r ? `No known conditions for "${n}" specifier in "${e2}" package` : `Missing "${n}" specifier in "${e2}" package`);
|
|
5135
|
+
}
|
|
5136
|
+
function n(n2, i, o, f) {
|
|
5137
|
+
let s, u, l = r(n2, o), c = function(e2) {
|
|
5138
|
+
let n3 = new Set(["default", ...e2.conditions || []]);
|
|
5139
|
+
return e2.unsafe || n3.add(e2.require ? "require" : "import"), e2.unsafe || n3.add(e2.browser ? "browser" : "node"), n3;
|
|
5140
|
+
}(f || {}), a = i[l];
|
|
5141
|
+
if (a === undefined) {
|
|
5142
|
+
let e2, n3, r, t;
|
|
5143
|
+
for (t in i)
|
|
5144
|
+
n3 && t.length < n3.length || (t[t.length - 1] === "/" && l.startsWith(t) ? (u = l.substring(t.length), n3 = t) : t.length > 1 && (r = t.indexOf("*", 1), ~r && (e2 = RegExp("^" + t.substring(0, r) + "(.*)" + t.substring(1 + r) + "$").exec(l), e2 && e2[1] && (u = e2[1], n3 = t))));
|
|
5145
|
+
a = i[n3];
|
|
5146
|
+
}
|
|
5147
|
+
return a || e(n2, l), s = t(a, c), s || e(n2, l, 1), u && function(e2, n3) {
|
|
5148
|
+
let r, t = 0, i2 = e2.length, o2 = /[*]/g, f2 = /[/]$/;
|
|
5149
|
+
for (;t < i2; t++)
|
|
5150
|
+
e2[t] = o2.test(r = e2[t]) ? r.replace(o2, n3) : f2.test(r) ? r + n3 : r;
|
|
5151
|
+
}(s, u), s;
|
|
5152
|
+
}
|
|
5153
|
+
function r(e2, n2, r2) {
|
|
5154
|
+
if (e2 === n2 || n2 === ".")
|
|
5155
|
+
return ".";
|
|
5156
|
+
let t = e2 + "/", i = t.length, o = n2.slice(0, i) === t, f = o ? n2.slice(i) : n2;
|
|
5157
|
+
return f[0] === "#" ? f : o || !r2 ? f.slice(0, 2) === "./" ? f : "./" + f : f;
|
|
5158
|
+
}
|
|
5159
|
+
function t(e2, n2, r2) {
|
|
5160
|
+
if (e2) {
|
|
5161
|
+
if (typeof e2 == "string")
|
|
5162
|
+
return r2 && r2.add(e2), [e2];
|
|
5163
|
+
let i, o;
|
|
5164
|
+
if (Array.isArray(e2)) {
|
|
5165
|
+
for (o = r2 || new Set, i = 0;i < e2.length; i++)
|
|
5166
|
+
t(e2[i], n2, o);
|
|
5167
|
+
if (!r2 && o.size)
|
|
5168
|
+
return [...o];
|
|
5169
|
+
} else
|
|
5170
|
+
for (i in e2)
|
|
5171
|
+
if (n2.has(i))
|
|
5172
|
+
return t(e2[i], n2, r2);
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
function o(e2, r2, t2) {
|
|
5176
|
+
let i, o2 = e2.exports;
|
|
5177
|
+
if (o2) {
|
|
5178
|
+
if (typeof o2 == "string")
|
|
5179
|
+
o2 = { ".": o2 };
|
|
5180
|
+
else
|
|
5181
|
+
for (i in o2) {
|
|
5182
|
+
i[0] !== "." && (o2 = { ".": o2 });
|
|
5183
|
+
break;
|
|
5184
|
+
}
|
|
5185
|
+
return n(e2.name, o2, r2 || ".", t2);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
function entryPoint(directory) {
|
|
5189
|
+
const root = realpathSync2(directory);
|
|
5190
|
+
const fd = openSync3(join9(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
5191
|
+
let pkg;
|
|
5192
|
+
try {
|
|
5193
|
+
const stat = fstatSync(fd);
|
|
5194
|
+
if (!stat.isFile() || stat.size > MAX_PACKAGE_BYTES)
|
|
5195
|
+
throw new Error("Invalid Secrets package metadata");
|
|
5196
|
+
pkg = JSON.parse(readFileSync7(fd, "utf8"));
|
|
5197
|
+
} finally {
|
|
5198
|
+
closeSync3(fd);
|
|
5199
|
+
}
|
|
5200
|
+
if (!pkg || typeof pkg !== "object" || !("name" in pkg) || pkg.name !== PACKAGE) {
|
|
5201
|
+
throw new Error("Invalid Secrets package identity");
|
|
5202
|
+
}
|
|
5203
|
+
const target = o(pkg, ".")?.[0];
|
|
5204
|
+
if (typeof target !== "string" || !target.startsWith("./"))
|
|
5205
|
+
throw new Error("Missing Secrets import export");
|
|
5206
|
+
const parts = target.slice(2).split("/");
|
|
5207
|
+
if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
|
|
5208
|
+
throw new Error("Invalid Secrets import export");
|
|
5209
|
+
}
|
|
5210
|
+
const file = realpathSync2(join9(root, ...parts));
|
|
5211
|
+
const within = relative(root, file);
|
|
5212
|
+
if (!within || within === ".." || within.startsWith(`..${sep2}`) || !statSync(file).isFile()) {
|
|
5213
|
+
throw new Error("Secrets import export escapes its package or is not a file");
|
|
5214
|
+
}
|
|
5215
|
+
return pathToFileURL(file).href;
|
|
5216
|
+
}
|
|
5217
|
+
function resolveInstalledSecrets(parentUrl) {
|
|
5218
|
+
let directory = dirname5(fileURLToPath3(parentUrl));
|
|
5219
|
+
for (;; ) {
|
|
5220
|
+
if (basename2(directory) !== "node_modules") {
|
|
5221
|
+
const candidate = join9(directory, "node_modules", PACKAGE);
|
|
5222
|
+
let absent = false;
|
|
5223
|
+
try {
|
|
5224
|
+
lstatSync2(candidate);
|
|
5225
|
+
} catch (error) {
|
|
5226
|
+
if (error.code !== "ENOENT")
|
|
5227
|
+
throw error;
|
|
5228
|
+
absent = true;
|
|
5229
|
+
}
|
|
5230
|
+
if (!absent)
|
|
5231
|
+
return entryPoint(candidate);
|
|
5232
|
+
}
|
|
5233
|
+
const parent = dirname5(directory);
|
|
5234
|
+
if (parent === directory)
|
|
5235
|
+
throw new Error("Secrets SDK is not installed for this consumer");
|
|
5236
|
+
directory = parent;
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5128
5239
|
function homeDir(env) {
|
|
5129
5240
|
const home = env.HOME?.trim();
|
|
5130
5241
|
return home ? home : null;
|
|
@@ -5138,14 +5249,14 @@ function hasnaHomeDir(env) {
|
|
|
5138
5249
|
if (override)
|
|
5139
5250
|
return override;
|
|
5140
5251
|
const home = homeDir(env);
|
|
5141
|
-
return home ?
|
|
5252
|
+
return home ? join22(home, HASNA_HOME_DIR) : null;
|
|
5142
5253
|
}
|
|
5143
5254
|
function appConfigDir(name, env) {
|
|
5144
5255
|
const configRoot = absoluteOverride(env, HASNA_CONFIG_HOME_ENV_KEY);
|
|
5145
5256
|
if (configRoot)
|
|
5146
|
-
return
|
|
5257
|
+
return join22(configRoot, name);
|
|
5147
5258
|
const root = hasnaHomeDir(env);
|
|
5148
|
-
return root ?
|
|
5259
|
+
return root ? join22(root, name, CONFIG_SUBDIR) : null;
|
|
5149
5260
|
}
|
|
5150
5261
|
function credentialDiskSourceList(name, env, profile = null) {
|
|
5151
5262
|
if (!SAFE_APP_SLUG.test(name))
|
|
@@ -5154,7 +5265,7 @@ function credentialDiskSourceList(name, env, profile = null) {
|
|
|
5154
5265
|
if (!directory)
|
|
5155
5266
|
return [];
|
|
5156
5267
|
const file = profile ? `${CREDENTIALS_FILE}-${profile}` : CREDENTIALS_FILE;
|
|
5157
|
-
return [{ path:
|
|
5268
|
+
return [{ path: join22(directory, file), tier: "disk" }];
|
|
5158
5269
|
}
|
|
5159
5270
|
function credentialDiskSources(name, env) {
|
|
5160
5271
|
return credentialDiskSourceList(name, env, null).map((s) => s.path);
|
|
@@ -5208,7 +5319,7 @@ function readAppConfigFile(path) {
|
|
|
5208
5319
|
};
|
|
5209
5320
|
let fd = -1;
|
|
5210
5321
|
try {
|
|
5211
|
-
fd = openSync2(path,
|
|
5322
|
+
fd = openSync2(path, O_RDONLY2 | O_NOFOLLOW2 | O_NONBLOCK2);
|
|
5212
5323
|
} catch (error) {
|
|
5213
5324
|
const code = error.code;
|
|
5214
5325
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -5218,7 +5329,7 @@ function readAppConfigFile(path) {
|
|
|
5218
5329
|
unsafe(`the path could not be opened (${code ?? "unknown error"})`);
|
|
5219
5330
|
}
|
|
5220
5331
|
try {
|
|
5221
|
-
const before =
|
|
5332
|
+
const before = fstatSync2(fd);
|
|
5222
5333
|
if (!before.isFile())
|
|
5223
5334
|
unsafe("the path is not a regular file");
|
|
5224
5335
|
if (!configFileModeAllowed(before.mode)) {
|
|
@@ -5229,8 +5340,8 @@ function readAppConfigFile(path) {
|
|
|
5229
5340
|
unsafe("the file is not owned by the current user");
|
|
5230
5341
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES)
|
|
5231
5342
|
unsafe("the file exceeds the size limit");
|
|
5232
|
-
const bytes =
|
|
5233
|
-
const after =
|
|
5343
|
+
const bytes = readFileSync22(fd);
|
|
5344
|
+
const after = fstatSync2(fd);
|
|
5234
5345
|
if (!configFileReadsCoherent(before, after)) {
|
|
5235
5346
|
unsafe("the file changed while being read");
|
|
5236
5347
|
}
|
|
@@ -5240,20 +5351,28 @@ function readAppConfigFile(path) {
|
|
|
5240
5351
|
closeSync2(fd);
|
|
5241
5352
|
}
|
|
5242
5353
|
}
|
|
5243
|
-
function readCredentialFile(path, apiKeyKeys) {
|
|
5354
|
+
function readCredentialFile(path, apiKeyKeys, pointerKey) {
|
|
5244
5355
|
const parsed = readAppConfigFile(path);
|
|
5245
5356
|
if (!parsed)
|
|
5246
5357
|
return null;
|
|
5247
|
-
for (const key of apiKeyKeys) {
|
|
5358
|
+
for (const key of [...apiKeyKeys, pointerKey]) {
|
|
5248
5359
|
if (parsed.unusable.has(key)) {
|
|
5249
5360
|
throw new CredentialFileUnsafeError(path, `${key} is declared but blank or malformed`);
|
|
5250
5361
|
}
|
|
5251
5362
|
}
|
|
5252
5363
|
const values = apiKeyKeys.map((key) => parsed.values.get(key)?.trim()).filter((value) => Boolean(value));
|
|
5364
|
+
const pointer = parsed.values.get(pointerKey)?.trim();
|
|
5365
|
+
if (pointer !== undefined) {
|
|
5366
|
+
if (!VAULT_POINTER_SHAPE.test(pointer))
|
|
5367
|
+
throw new CredentialFileUnsafeError(path, `${pointerKey} must name a vault item`);
|
|
5368
|
+
if (values.length)
|
|
5369
|
+
throw new CredentialFileUnsafeError(path, "a credential file cannot select both a literal key and a vault reference");
|
|
5370
|
+
return { apiKey: "", pointerVaultKey: pointer };
|
|
5371
|
+
}
|
|
5253
5372
|
if (new Set(values).size > 1) {
|
|
5254
5373
|
throw new CredentialFileUnsafeError(path, "credential aliases disagree");
|
|
5255
5374
|
}
|
|
5256
|
-
return values[0]
|
|
5375
|
+
return values[0] === undefined ? null : { apiKey: values[0] };
|
|
5257
5376
|
}
|
|
5258
5377
|
function appConfigDiskValue(name, env, keys) {
|
|
5259
5378
|
const wanted = keys.filter((key) => !CREDENTIAL_SHAPED_KEY.test(key));
|
|
@@ -5325,6 +5444,18 @@ function sealCredential(fields) {
|
|
|
5325
5444
|
});
|
|
5326
5445
|
return Object.freeze(sealed);
|
|
5327
5446
|
}
|
|
5447
|
+
function explicitCredential(appName, apiKey) {
|
|
5448
|
+
const source = "explicit apiKey option";
|
|
5449
|
+
assertUsableCredential(appName, source, apiKey);
|
|
5450
|
+
return sealCredential({
|
|
5451
|
+
apiKey,
|
|
5452
|
+
tier: "argument",
|
|
5453
|
+
source,
|
|
5454
|
+
deliberate: true,
|
|
5455
|
+
diskCandidates: [],
|
|
5456
|
+
warning: null
|
|
5457
|
+
});
|
|
5458
|
+
}
|
|
5328
5459
|
function firstEnvValue(env, keys) {
|
|
5329
5460
|
for (const key of keys) {
|
|
5330
5461
|
if (!Object.prototype.hasOwnProperty.call(env, key))
|
|
@@ -5510,12 +5641,13 @@ function resolveCredential(name, env, options = {}) {
|
|
|
5510
5641
|
}
|
|
5511
5642
|
const paths = profileDiskSources(name, env, profile);
|
|
5512
5643
|
for (const path of paths) {
|
|
5513
|
-
const value = readCredentialFile(path, apiKeyKeys);
|
|
5644
|
+
const value = readCredentialFile(path, apiKeyKeys, pointerKeyName);
|
|
5514
5645
|
if (value) {
|
|
5515
|
-
|
|
5646
|
+
if (!value.pointerVaultKey)
|
|
5647
|
+
assertUsableCredential(name, path, value.apiKey);
|
|
5516
5648
|
return sealCredential({
|
|
5517
|
-
|
|
5518
|
-
tier: "profile",
|
|
5649
|
+
...value,
|
|
5650
|
+
tier: value.pointerVaultKey ? "pointer" : "profile",
|
|
5519
5651
|
source: path,
|
|
5520
5652
|
deliberate: true,
|
|
5521
5653
|
diskCandidates: paths,
|
|
@@ -5523,7 +5655,7 @@ function resolveCredential(name, env, options = {}) {
|
|
|
5523
5655
|
});
|
|
5524
5656
|
}
|
|
5525
5657
|
}
|
|
5526
|
-
throw new CredentialResolutionError(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_KEY}.`, paths);
|
|
5658
|
+
throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} or ${pointerKeyName} 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_KEY}.`, paths);
|
|
5527
5659
|
}
|
|
5528
5660
|
const definedEnvEntries = apiKeyKeys.filter((key) => Object.prototype.hasOwnProperty.call(env, key) && env[key] !== undefined).map((key) => ({ key, value: String(env[key]).trim() }));
|
|
5529
5661
|
const blankEnv = definedEnvEntries.find((entry) => entry.value.length === 0);
|
|
@@ -5548,17 +5680,27 @@ function resolveCredential(name, env, options = {}) {
|
|
|
5548
5680
|
});
|
|
5549
5681
|
}
|
|
5550
5682
|
const diskSourceList = credentialDiskSourceList(name, env, null);
|
|
5551
|
-
const diskHits = diskSourceList.map((src) => ({ src, value: readCredentialFile(src.path, apiKeyKeys) })).filter((hit) => hit.value !== null);
|
|
5683
|
+
const diskHits = diskSourceList.map((src) => ({ src, value: readCredentialFile(src.path, apiKeyKeys, pointerKeyName) })).filter((hit) => hit.value !== null);
|
|
5552
5684
|
if (diskHits.length > 0) {
|
|
5553
5685
|
const winner = diskHits[0];
|
|
5554
|
-
|
|
5686
|
+
if (winner.value.pointerVaultKey) {
|
|
5687
|
+
return sealCredential({
|
|
5688
|
+
...winner.value,
|
|
5689
|
+
tier: "pointer",
|
|
5690
|
+
source: winner.src.path,
|
|
5691
|
+
deliberate: false,
|
|
5692
|
+
diskCandidates: diskPaths,
|
|
5693
|
+
warning: null
|
|
5694
|
+
});
|
|
5695
|
+
}
|
|
5696
|
+
assertUsableCredential(name, winner.src.path, winner.value.apiKey);
|
|
5555
5697
|
const divergentSources = [
|
|
5556
|
-
...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.src.path),
|
|
5557
|
-
...envHit && envHit.value !== winner.value ? [envHit.key] : []
|
|
5698
|
+
...diskHits.slice(1).filter((hit) => hit.value.apiKey !== winner.value.apiKey || hit.value.pointerVaultKey !== winner.value.pointerVaultKey).map((hit) => hit.src.path),
|
|
5699
|
+
...envHit && envHit.value !== winner.value.apiKey ? [envHit.key] : []
|
|
5558
5700
|
];
|
|
5559
5701
|
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;
|
|
5560
5702
|
return sealCredential({
|
|
5561
|
-
apiKey: winner.value,
|
|
5703
|
+
apiKey: winner.value.apiKey,
|
|
5562
5704
|
tier: winner.src.tier,
|
|
5563
5705
|
source: winner.src.path,
|
|
5564
5706
|
deliberate: false,
|
|
@@ -5579,6 +5721,53 @@ function resolveCredential(name, env, options = {}) {
|
|
|
5579
5721
|
}
|
|
5580
5722
|
return null;
|
|
5581
5723
|
}
|
|
5724
|
+
async function completePointerCredential(name, pointerResolution, env = process.env) {
|
|
5725
|
+
const secretsEnv = snapshotClientEnvironment("secrets", env);
|
|
5726
|
+
const vaultKey = pointerResolution.pointerVaultKey;
|
|
5727
|
+
const pointerEnvKey = pointerResolution.source;
|
|
5728
|
+
if (!vaultKey) {
|
|
5729
|
+
throw new CredentialResolutionError(name, `Pointer resolution from ${pointerEnvKey} carries no vault item key; this is a defect in the resolver.`, [pointerEnvKey]);
|
|
5730
|
+
}
|
|
5731
|
+
if (name === "secrets") {
|
|
5732
|
+
throw new CredentialResolutionError(name, "The Secrets bootstrap credential cannot reference the same hosted vault; configure an independent bootstrap provider.", [pointerEnvKey]);
|
|
5733
|
+
}
|
|
5734
|
+
let secretsSdk;
|
|
5735
|
+
try {
|
|
5736
|
+
const sdkUrl = resolveInstalledSecrets(import.meta.url);
|
|
5737
|
+
secretsSdk = requireSecretsSdk(fileURLToPath22(sdkUrl));
|
|
5738
|
+
} catch {
|
|
5739
|
+
throw new CredentialResolutionError(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]);
|
|
5740
|
+
}
|
|
5741
|
+
let client;
|
|
5742
|
+
try {
|
|
5743
|
+
const bootstrap = resolveCredential("secrets", secretsEnv);
|
|
5744
|
+
if (!bootstrap || bootstrap.tier === "pointer") {
|
|
5745
|
+
throw new CredentialResolutionError("secrets", "The Secrets vault requires an independent, non-reference bootstrap credential.", bootstrap ? [bootstrap.source] : []);
|
|
5746
|
+
}
|
|
5747
|
+
client = secretsSdk.createSecretsClientFromEnv(secretsEnv);
|
|
5748
|
+
} catch {
|
|
5749
|
+
throw new CredentialResolutionError(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]);
|
|
5750
|
+
}
|
|
5751
|
+
let secret;
|
|
5752
|
+
try {
|
|
5753
|
+
secret = await client.getSecret({ key: vaultKey });
|
|
5754
|
+
} catch {
|
|
5755
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the vault could not be reached or the item is ` + `unavailable. A vault pointer is TERMINAL and never falls through to a literal or disk credential.`, [pointerEnvKey]);
|
|
5756
|
+
}
|
|
5757
|
+
const value = secret.value;
|
|
5758
|
+
if (!value) {
|
|
5759
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} resolved vault item '${vaultKey}', but it holds no value. A vault pointer is TERMINAL.`, [pointerEnvKey]);
|
|
5760
|
+
}
|
|
5761
|
+
assertUsableCredential(name, `${pointerEnvKey} -> vault:${vaultKey}`, value);
|
|
5762
|
+
return sealCredential({
|
|
5763
|
+
apiKey: value,
|
|
5764
|
+
tier: "pointer",
|
|
5765
|
+
source: `${pointerEnvKey} -> vault:${vaultKey}`,
|
|
5766
|
+
deliberate: pointerResolution.deliberate,
|
|
5767
|
+
diskCandidates: pointerResolution.diskCandidates,
|
|
5768
|
+
warning: null
|
|
5769
|
+
});
|
|
5770
|
+
}
|
|
5582
5771
|
function defaultFleetGatewayBaseUrl(name) {
|
|
5583
5772
|
return `${DEFAULT_FLEET_GATEWAY_ORIGIN}/${validateAppSlug(name)}`;
|
|
5584
5773
|
}
|
|
@@ -5781,8 +5970,9 @@ function credentialDiskSourcesForMessage(name, env) {
|
|
|
5781
5970
|
const paths = credentialDiskSources(name, env);
|
|
5782
5971
|
return paths.length > 0 ? paths.join(" or ") : "<no HOME or HASNA_HOME set in this environment, so no credential file was consulted>";
|
|
5783
5972
|
}
|
|
5784
|
-
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", CredentialResolutionError, CredentialFileUnsafeError, HASNA_HOME_ENV_KEY = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY = "HASNA_STATION", HASNA_HOME_DIR = ".hasna", CONFIG_SUBDIR = "config", CREDENTIALS_FILE = "credentials", KEYCHAIN_SECURITY_BIN = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS = 44, KEYCHAIN_SPAWN_TIMEOUT_MS = 1e4, MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, VAULT_POINTER_SHAPE, CREDENTIAL_SHAPED_KEY, INSPECT_CUSTOM, CREDENTIAL_SEAL, AMBIENT_ENVIRONMENT,
|
|
5973
|
+
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", PACKAGE = "@hasna/secrets", MAX_PACKAGE_BYTES, CredentialResolutionError, CredentialFileUnsafeError, HASNA_HOME_ENV_KEY = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY = "HASNA_STATION", HASNA_HOME_DIR = ".hasna", CONFIG_SUBDIR = "config", CREDENTIALS_FILE = "credentials", KEYCHAIN_SECURITY_BIN = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS = 44, KEYCHAIN_SPAWN_TIMEOUT_MS = 1e4, MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, VAULT_POINTER_SHAPE, CREDENTIAL_SHAPED_KEY, INSPECT_CUSTOM, CREDENTIAL_SEAL, AMBIENT_ENVIRONMENT, requireSecretsSdk, DEFAULT_FLEET_GATEWAY_ORIGIN = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN, DNS_LABEL_PATTERN, ClientTransportConfigurationError, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS;
|
|
5785
5974
|
var init_transport = __esm(() => {
|
|
5975
|
+
MAX_PACKAGE_BYTES = 1024 * 1024;
|
|
5786
5976
|
CredentialResolutionError = class CredentialResolutionError extends Error {
|
|
5787
5977
|
appName;
|
|
5788
5978
|
attempted;
|
|
@@ -5810,7 +6000,6 @@ var init_transport = __esm(() => {
|
|
|
5810
6000
|
INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
|
|
5811
6001
|
CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
|
|
5812
6002
|
AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
|
|
5813
|
-
SECRETS_PACKAGE_SPECIFIER = "@hasna/" + "secrets";
|
|
5814
6003
|
requireSecretsSdk = createRequire(import.meta.url);
|
|
5815
6004
|
ASCII_CONTROL_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
5816
6005
|
DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -6013,8 +6202,8 @@ var init_db_writer = __esm(() => {
|
|
|
6013
6202
|
});
|
|
6014
6203
|
|
|
6015
6204
|
// src/lib/hook-env.ts
|
|
6016
|
-
import { dirname as
|
|
6017
|
-
import { existsSync as existsSync11, realpathSync as
|
|
6205
|
+
import { dirname as dirname7, isAbsolute as isAbsolute3 } from "path";
|
|
6206
|
+
import { existsSync as existsSync11, realpathSync as realpathSync3, statSync as statSync2 } from "fs";
|
|
6018
6207
|
import { tmpdir as tmpdir2 } from "os";
|
|
6019
6208
|
function isDeniedEnvName(name) {
|
|
6020
6209
|
const upper = name.toUpperCase();
|
|
@@ -6043,7 +6232,7 @@ function isUnsafePathEntry(entry, home) {
|
|
|
6043
6232
|
return true;
|
|
6044
6233
|
const canonical = (path) => {
|
|
6045
6234
|
try {
|
|
6046
|
-
return
|
|
6235
|
+
return realpathSync3(path);
|
|
6047
6236
|
} catch {
|
|
6048
6237
|
return path;
|
|
6049
6238
|
}
|
|
@@ -6055,7 +6244,7 @@ function isUnsafePathEntry(entry, home) {
|
|
|
6055
6244
|
return true;
|
|
6056
6245
|
}
|
|
6057
6246
|
try {
|
|
6058
|
-
if ((
|
|
6247
|
+
if ((statSync2(entry).mode & 2) !== 0)
|
|
6059
6248
|
return true;
|
|
6060
6249
|
} catch {}
|
|
6061
6250
|
return false;
|
|
@@ -6064,7 +6253,7 @@ function sanitizeHookPath(pathValue, home) {
|
|
|
6064
6253
|
const baseline = PATH_BASELINE.slice();
|
|
6065
6254
|
if (process.platform === "darwin" && existsSync11("/opt/homebrew/bin"))
|
|
6066
6255
|
baseline.push("/opt/homebrew/bin");
|
|
6067
|
-
const bunDir =
|
|
6256
|
+
const bunDir = dirname7(process.execPath);
|
|
6068
6257
|
const seen = new Set;
|
|
6069
6258
|
const out = [];
|
|
6070
6259
|
for (const dir of baseline) {
|
|
@@ -6189,9 +6378,9 @@ __export(exports_run, {
|
|
|
6189
6378
|
});
|
|
6190
6379
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
6191
6380
|
import { rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
6192
|
-
import { basename as
|
|
6381
|
+
import { basename as basename3, dirname as dirname8, isAbsolute as isAbsolute4, join as join12 } from "path";
|
|
6193
6382
|
function extensionOf(scriptPath) {
|
|
6194
|
-
const base =
|
|
6383
|
+
const base = basename3(scriptPath);
|
|
6195
6384
|
const dot = base.lastIndexOf(".");
|
|
6196
6385
|
return dot > 0 ? base.slice(dot + 1) : "ts";
|
|
6197
6386
|
}
|
|
@@ -6203,9 +6392,9 @@ function shebangInterpreter(firstLine) {
|
|
|
6203
6392
|
return null;
|
|
6204
6393
|
const parts = rest.split(/\s+/);
|
|
6205
6394
|
if (parts[0] === "env" || parts[0] === "/usr/bin/env") {
|
|
6206
|
-
return parts[1] && parts[1] !== "-S" ?
|
|
6395
|
+
return parts[1] && parts[1] !== "-S" ? basename3(parts[1]) : parts[2] ? basename3(parts[2]) : null;
|
|
6207
6396
|
}
|
|
6208
|
-
return
|
|
6397
|
+
return basename3(parts[0]);
|
|
6209
6398
|
}
|
|
6210
6399
|
function interpreterFor(name, scriptPath, content) {
|
|
6211
6400
|
const ext = extensionOf(scriptPath);
|
|
@@ -6239,8 +6428,8 @@ async function readPipeWithDeadline(stream, deadlineMs) {
|
|
|
6239
6428
|
result = await Promise.race([
|
|
6240
6429
|
reader.read(),
|
|
6241
6430
|
new Promise((_, reject) => {
|
|
6242
|
-
const
|
|
6243
|
-
|
|
6431
|
+
const t2 = setTimeout(() => reject(new Error("pipe-drain-deadline")), remaining);
|
|
6432
|
+
t2.unref?.();
|
|
6244
6433
|
})
|
|
6245
6434
|
]);
|
|
6246
6435
|
} catch {
|
|
@@ -6258,7 +6447,7 @@ async function readPipeWithDeadline(stream, deadlineMs) {
|
|
|
6258
6447
|
return chunks.join("");
|
|
6259
6448
|
}
|
|
6260
6449
|
async function executeVerifiedScript(options) {
|
|
6261
|
-
const scriptDir =
|
|
6450
|
+
const scriptDir = dirname8(options.scriptPath);
|
|
6262
6451
|
const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
|
|
6263
6452
|
const tempPath = join12(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
|
|
6264
6453
|
let proc = null;
|
|
@@ -8321,6 +8510,41 @@ function rethrowHooksAuthorityFailure(error) {
|
|
|
8321
8510
|
}
|
|
8322
8511
|
throw new Error(`REMOTE_API_URL_INVALID: ${message} local mode fallback is disabled`, { cause: error });
|
|
8323
8512
|
}
|
|
8513
|
+
var authorityBindings = new WeakMap;
|
|
8514
|
+
function sameCredential(left, right) {
|
|
8515
|
+
return left === null || right === null ? left === right : left.apiKey === right.apiKey && left.pointerVaultKey === right.pointerVaultKey && left.source === right.source && left.tier === right.tier;
|
|
8516
|
+
}
|
|
8517
|
+
function changedBinding() {
|
|
8518
|
+
throw new Error("REMOTE_API_CREDENTIAL_INVALID: The configured Hooks authority or credential changed while preparing a request; no request was sent.");
|
|
8519
|
+
}
|
|
8520
|
+
async function resolveHooksRequestAuthority(authority) {
|
|
8521
|
+
if (authority.origin !== hooksRegistryOrigin(authority.v1BaseUrl))
|
|
8522
|
+
changedBinding();
|
|
8523
|
+
const binding = authorityBindings.get(authority);
|
|
8524
|
+
if (!binding)
|
|
8525
|
+
return { ...authority, apiKey: explicitCredential("hooks", authority.apiKey).apiKey };
|
|
8526
|
+
const snapshot = () => {
|
|
8527
|
+
const next = resolveHooksTransport(binding.env, binding.options);
|
|
8528
|
+
if (next.mode !== "remote" || !next.authority || next.authority.v1BaseUrl !== authority.v1BaseUrl)
|
|
8529
|
+
changedBinding();
|
|
8530
|
+
return { authority: next.authority, binding: authorityBindings.get(next.authority) };
|
|
8531
|
+
};
|
|
8532
|
+
const first = snapshot();
|
|
8533
|
+
const reviewed = snapshot();
|
|
8534
|
+
if (!sameCredential(first.binding.credential, reviewed.binding.credential))
|
|
8535
|
+
changedBinding();
|
|
8536
|
+
let credential = reviewed.binding.credential;
|
|
8537
|
+
try {
|
|
8538
|
+
if (credential.tier === "pointer")
|
|
8539
|
+
credential = await completePointerCredential("hooks", credential, binding.env);
|
|
8540
|
+
} catch (error) {
|
|
8541
|
+
rethrowHooksAuthorityFailure(error);
|
|
8542
|
+
}
|
|
8543
|
+
const final = snapshot();
|
|
8544
|
+
if (!sameCredential(reviewed.binding.credential, final.binding.credential))
|
|
8545
|
+
changedBinding();
|
|
8546
|
+
return { ...reviewed.authority, apiKey: credential.apiKey };
|
|
8547
|
+
}
|
|
8324
8548
|
function resolveHooksTransport(env = process.env, options = {}) {
|
|
8325
8549
|
if (selectsHooksLocalStore(env)) {
|
|
8326
8550
|
announceLocal(options.notice, "HASNA_HOOKS_LOCAL is set and nothing configures a registry authority");
|
|
@@ -8334,7 +8558,7 @@ function resolveHooksTransport(env = process.env, options = {}) {
|
|
|
8334
8558
|
try {
|
|
8335
8559
|
credential = resolveCredential("hooks", resolverEnv, credentialOptions);
|
|
8336
8560
|
resolution = resolveClientTransport("hooks", resolverEnv, {
|
|
8337
|
-
credentials: credential ? { ...credentialOptions, apiKey: credential.apiKey } : credentialOptions
|
|
8561
|
+
credentials: credential && credential.tier !== "pointer" ? { ...credentialOptions, apiKey: credential.apiKey } : credentialOptions
|
|
8338
8562
|
});
|
|
8339
8563
|
} catch (error) {
|
|
8340
8564
|
rethrowHooksAuthorityFailure(error);
|
|
@@ -8342,25 +8566,43 @@ function resolveHooksTransport(env = process.env, options = {}) {
|
|
|
8342
8566
|
if (!credential) {
|
|
8343
8567
|
throw new Error("REMOTE_API_CONFIG_MISSING: no hooks registry credential resolved; " + "local mode fallback is disabled");
|
|
8344
8568
|
}
|
|
8569
|
+
const authority = {
|
|
8570
|
+
origin: hooksRegistryOrigin(resolution.baseUrl),
|
|
8571
|
+
apiKey: credential.apiKey,
|
|
8572
|
+
apiUrlSource: resolution.apiUrlSource,
|
|
8573
|
+
apiKeySource: credential.source,
|
|
8574
|
+
apiKeyTier: credential.tier,
|
|
8575
|
+
v1BaseUrl: resolution.baseUrl,
|
|
8576
|
+
warning: resolution.warning
|
|
8577
|
+
};
|
|
8578
|
+
authorityBindings.set(authority, { env, options, credential });
|
|
8345
8579
|
return {
|
|
8346
8580
|
mode: "remote",
|
|
8347
8581
|
source: `${credential.source}+${resolution.apiUrlSource ?? "default"}`,
|
|
8348
|
-
authority
|
|
8349
|
-
origin: hooksRegistryOrigin(resolution.baseUrl),
|
|
8350
|
-
apiKey: credential.apiKey,
|
|
8351
|
-
apiUrlSource: resolution.apiUrlSource,
|
|
8352
|
-
apiKeySource: credential.source,
|
|
8353
|
-
apiKeyTier: credential.tier,
|
|
8354
|
-
v1BaseUrl: resolution.baseUrl,
|
|
8355
|
-
warning: resolution.warning
|
|
8356
|
-
}
|
|
8582
|
+
authority
|
|
8357
8583
|
};
|
|
8358
8584
|
}
|
|
8359
8585
|
function resolveHooksServePublishKey(env = process.env, options = {}) {
|
|
8360
8586
|
const { env: resolverEnv, credentials } = hooksResolverInputs(env, options.credentials);
|
|
8361
8587
|
const tier1 = options.credentials?.apiKey;
|
|
8362
8588
|
const credentialOptions = tier1 !== undefined ? { ...credentials, apiKey: tier1 } : credentials;
|
|
8363
|
-
|
|
8589
|
+
const credential = resolveCredential("hooks", resolverEnv, credentialOptions);
|
|
8590
|
+
if (credential?.tier === "pointer")
|
|
8591
|
+
throw new Error("Vault references require resolveHooksServePublishKeyAsync().");
|
|
8592
|
+
return credential?.apiKey;
|
|
8593
|
+
}
|
|
8594
|
+
async function resolveHooksServePublishKeyAsync(env = process.env, options = {}) {
|
|
8595
|
+
const { env: resolverEnv, credentials } = hooksResolverInputs(env, options.credentials);
|
|
8596
|
+
const read = () => resolveCredential("hooks", resolverEnv, credentials);
|
|
8597
|
+
const selected = read();
|
|
8598
|
+
if (!selected || selected.tier !== "pointer")
|
|
8599
|
+
return selected?.apiKey;
|
|
8600
|
+
if (!sameCredential(selected, read()))
|
|
8601
|
+
changedBinding();
|
|
8602
|
+
const completed = await completePointerCredential("hooks", selected, env);
|
|
8603
|
+
if (!sameCredential(selected, read()))
|
|
8604
|
+
changedBinding();
|
|
8605
|
+
return completed.apiKey;
|
|
8364
8606
|
}
|
|
8365
8607
|
|
|
8366
8608
|
// src/index.ts
|
|
@@ -8499,7 +8741,14 @@ function collectBundledCatalog() {
|
|
|
8499
8741
|
}
|
|
8500
8742
|
async function fetchJson(base, path, apiKey, opts) {
|
|
8501
8743
|
const headers = { accept: "application/json" };
|
|
8502
|
-
if (apiKey)
|
|
8744
|
+
if (typeof apiKey === "object") {
|
|
8745
|
+
if (apiKey.origin !== base)
|
|
8746
|
+
throw new Error("Hooks registry authority does not match the request target");
|
|
8747
|
+
const resolved = await resolveHooksRequestAuthority(apiKey);
|
|
8748
|
+
if (resolved.origin !== base)
|
|
8749
|
+
throw new Error("Hooks registry authority changed before dispatch");
|
|
8750
|
+
headers["x-api-key"] = resolved.apiKey;
|
|
8751
|
+
} else if (apiKey)
|
|
8503
8752
|
headers["x-api-key"] = apiKey;
|
|
8504
8753
|
const res = await fetch(`${base}${path}`, {
|
|
8505
8754
|
headers,
|
|
@@ -8567,8 +8816,8 @@ async function planSync(options = {}) {
|
|
|
8567
8816
|
credentials: options.credentials
|
|
8568
8817
|
});
|
|
8569
8818
|
if (transport.mode === "remote" && transport.authority) {
|
|
8570
|
-
const { origin
|
|
8571
|
-
const { catalog, remoteLock } = await fetchRemoteState(origin,
|
|
8819
|
+
const { origin } = transport.authority;
|
|
8820
|
+
const { catalog, remoteLock } = await fetchRemoteState(origin, transport.authority);
|
|
8572
8821
|
return { apiUrl: origin, dryRun: options.dryRun ?? false, diff: computeDiff(catalog, remoteLock) };
|
|
8573
8822
|
}
|
|
8574
8823
|
const diff = computeDiff(collectBundledCatalog(), null);
|
|
@@ -8596,10 +8845,10 @@ async function syncHooks(options = {}) {
|
|
|
8596
8845
|
}
|
|
8597
8846
|
return plan;
|
|
8598
8847
|
}
|
|
8599
|
-
const { origin
|
|
8600
|
-
const { catalog, remoteLock } = await fetchRemoteState(origin,
|
|
8848
|
+
const { origin } = transport.authority;
|
|
8849
|
+
const { catalog, remoteLock } = await fetchRemoteState(origin, transport.authority);
|
|
8601
8850
|
const diff = computeDiff(catalog, remoteLock);
|
|
8602
|
-
const staged = await stageSyncArtifacts(origin,
|
|
8851
|
+
const staged = await stageSyncArtifacts(origin, transport.authority, [...diff.added, ...diff.updated], remoteLock);
|
|
8603
8852
|
await commitSyncArtifacts(staged, origin, remoteLock);
|
|
8604
8853
|
return { apiUrl: origin, dryRun: false, diff };
|
|
8605
8854
|
}
|
|
@@ -8776,8 +9025,8 @@ async function installPinnedFromBundled(name, version) {
|
|
|
8776
9025
|
init_manifest();
|
|
8777
9026
|
init_store();
|
|
8778
9027
|
import { readFileSync as readFileSync10 } from "fs";
|
|
8779
|
-
import { dirname as
|
|
8780
|
-
import { fileURLToPath as
|
|
9028
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
9029
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8781
9030
|
|
|
8782
9031
|
// src/lib/secure-compare.ts
|
|
8783
9032
|
function secureEqual(a, b) {
|
|
@@ -8785,8 +9034,8 @@ function secureEqual(a, b) {
|
|
|
8785
9034
|
const ab = enc.encode(a);
|
|
8786
9035
|
const bb = enc.encode(b);
|
|
8787
9036
|
let diff = ab.length ^ bb.length;
|
|
8788
|
-
const
|
|
8789
|
-
for (let i = 0;i <
|
|
9037
|
+
const n2 = Math.max(ab.length, bb.length);
|
|
9038
|
+
for (let i = 0;i < n2; i++) {
|
|
8790
9039
|
diff |= (ab[i] ?? 0) ^ (bb[i] ?? 0);
|
|
8791
9040
|
}
|
|
8792
9041
|
return diff === 0;
|
|
@@ -8950,7 +9199,7 @@ var SERVE_HOST = "127.0.0.1";
|
|
|
8950
9199
|
var SERVE_SERVICE_NAME = "hooks-registry";
|
|
8951
9200
|
function packageVersion() {
|
|
8952
9201
|
try {
|
|
8953
|
-
const pkgPath = join11(
|
|
9202
|
+
const pkgPath = join11(dirname6(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
8954
9203
|
return JSON.parse(readFileSync10(pkgPath, "utf-8")).version || "0.0.0";
|
|
8955
9204
|
} catch {
|
|
8956
9205
|
return "0.0.0";
|
|
@@ -9112,7 +9361,7 @@ function startServeServer(options) {
|
|
|
9112
9361
|
hostname: host,
|
|
9113
9362
|
port,
|
|
9114
9363
|
async fetch(req) {
|
|
9115
|
-
return handleServeRequest(req,
|
|
9364
|
+
return handleServeRequest(req, await resolveHooksServePublishKeyAsync());
|
|
9116
9365
|
}
|
|
9117
9366
|
});
|
|
9118
9367
|
console.error(`hooks registry listening on http://${host}:${server.port} (publish requires an API key)`);
|
|
@@ -9334,7 +9583,9 @@ export {
|
|
|
9334
9583
|
resolveScriptPath,
|
|
9335
9584
|
resolveScript,
|
|
9336
9585
|
resolveHooksTransport,
|
|
9586
|
+
resolveHooksServePublishKeyAsync,
|
|
9337
9587
|
resolveHooksServePublishKey,
|
|
9588
|
+
resolveHooksRequestAuthority,
|
|
9338
9589
|
resolveHookMeta,
|
|
9339
9590
|
resolveHookEventSink,
|
|
9340
9591
|
resolveHookDir,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* across its public seam, spelled locally.
|
|
4
4
|
*
|
|
5
5
|
* WHY THIS FILE EXISTS. `@hasna/contracts` is a BUILD-TIME dependency of this
|
|
6
|
-
* package (pinned
|
|
6
|
+
* package (pinned in package.json, inlined by `bun build --target bun`), so the emitted
|
|
7
7
|
* `dist/**/*.d.ts` must never `import ... from "@hasna/contracts/client"` —
|
|
8
8
|
* a published declaration that references a package consumers do not have is
|
|
9
9
|
* a broken install (hasna/apps#1782). Every public signature therefore uses
|