@hasna/skills 0.10.0 → 0.10.2
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 +32 -0
- package/bin/index.js +379 -132
- package/bin/mcp.js +245 -49
- package/bin/migrate.js +2 -6
- package/bin/server.js +5 -6
- package/bin/worker.js +2 -6
- package/dist/cli/profile-selection.d.ts +1 -1
- package/dist/index.js +257 -67
- package/dist/lib/cli-credential-profile.d.ts +2 -0
- package/dist/lib/selection-cache.d.ts +12 -0
- package/dist/sdk/index.js +330 -141
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.2",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -20974,10 +20974,16 @@ var init_transport = __esm(() => {
|
|
|
20974
20974
|
]);
|
|
20975
20975
|
});
|
|
20976
20976
|
|
|
20977
|
+
// src/lib/cli-credential-profile.ts
|
|
20978
|
+
function selectedCliSkillsCredentialProfile() {
|
|
20979
|
+
return cliCredentialProfile;
|
|
20980
|
+
}
|
|
20981
|
+
var cliCredentialProfile;
|
|
20982
|
+
|
|
20977
20983
|
// src/lib/instance-credentials.ts
|
|
20978
20984
|
import { closeSync as closeSync3, constants, fstatSync as fstatSync3, lstatSync as lstatSync2, openSync as openSync3, readSync } from "fs";
|
|
20979
20985
|
function selectedSkillsProfile(env, explicit) {
|
|
20980
|
-
const selected = explicit ?? env.HASNA_PROFILE;
|
|
20986
|
+
const selected = explicit ?? selectedCliSkillsCredentialProfile() ?? env.HASNA_PROFILE;
|
|
20981
20987
|
if (selected === undefined)
|
|
20982
20988
|
return null;
|
|
20983
20989
|
const profile = selected.trim();
|
|
@@ -21263,11 +21269,13 @@ function snapshotSkillsEnvironment(env) {
|
|
|
21263
21269
|
return Object.freeze(snapshot);
|
|
21264
21270
|
}
|
|
21265
21271
|
function snapshotSkillsOptions(env, options) {
|
|
21272
|
+
const profile = options.credentials?.profile ?? selectedCliSkillsCredentialProfile();
|
|
21273
|
+
const credentials = profile === undefined ? options.credentials : { ...options.credentials, profile };
|
|
21266
21274
|
if (env !== process.env)
|
|
21267
|
-
return options;
|
|
21268
|
-
return { ...options, credentials: { ...
|
|
21269
|
-
...
|
|
21270
|
-
enabled:
|
|
21275
|
+
return credentials === options.credentials ? options : { ...options, credentials };
|
|
21276
|
+
return { ...options, credentials: { ...credentials, keychain: {
|
|
21277
|
+
...credentials?.keychain,
|
|
21278
|
+
enabled: credentials?.keychain?.enabled ?? true
|
|
21271
21279
|
} } };
|
|
21272
21280
|
}
|
|
21273
21281
|
function resolveSkillsFleetOrThrow(env, options) {
|
|
@@ -21355,8 +21363,9 @@ async function resolveSkillsApiKey(env = process.env, options = {}) {
|
|
|
21355
21363
|
}
|
|
21356
21364
|
async function resolveSkillsConnection(env = process.env, options = {}) {
|
|
21357
21365
|
const snapshotEnv = snapshotSkillsEnvironment(env);
|
|
21358
|
-
const
|
|
21359
|
-
const
|
|
21366
|
+
const resolvedOptions = snapshotSkillsOptions(env, options);
|
|
21367
|
+
const assertFilesUnchanged = selectsSkillsLocalMode(snapshotEnv) ? () => {} : captureSkillsCredentialFiles(skillsProfileCredentialFiles(snapshotEnv, resolvedOptions.credentials?.profile));
|
|
21368
|
+
const fleet = resolveSkillsFleet(snapshotEnv, resolvedOptions);
|
|
21360
21369
|
if (fleet.mode === "local" && env === process.env)
|
|
21361
21370
|
noticeLocalSkillsMode();
|
|
21362
21371
|
if (fleet.mode !== "hosted")
|
|
@@ -46391,7 +46400,9 @@ var init_skill_version = __esm(() => {
|
|
|
46391
46400
|
|
|
46392
46401
|
// src/lib/selection-cache.ts
|
|
46393
46402
|
import { createHash as createHash7, randomUUID } from "crypto";
|
|
46394
|
-
import {
|
|
46403
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
46404
|
+
import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync6, readFileSync as readFileSync10, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
46405
|
+
import { hostname as hostname3 } from "os";
|
|
46395
46406
|
import { dirname as dirname8, join as join14, parse as parse7, resolve as resolve7 } from "path";
|
|
46396
46407
|
import { isDeepStrictEqual } from "util";
|
|
46397
46408
|
function selectionCacheRoot(options = {}) {
|
|
@@ -46661,6 +46672,164 @@ function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
|
|
|
46661
46672
|
function skillSessionSnapshotBinding(snapshot) {
|
|
46662
46673
|
return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
|
|
46663
46674
|
}
|
|
46675
|
+
function localBootId() {
|
|
46676
|
+
try {
|
|
46677
|
+
if (process.platform === "linux") {
|
|
46678
|
+
const value = readFileSync10("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
46679
|
+
return /^[a-f0-9-]{36}$/i.test(value) ? value : null;
|
|
46680
|
+
}
|
|
46681
|
+
if (process.platform === "darwin") {
|
|
46682
|
+
const result = spawnSync2("/usr/sbin/sysctl", ["-n", "kern.boottime"], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
|
|
46683
|
+
if (result.status !== 0 || result.error)
|
|
46684
|
+
return null;
|
|
46685
|
+
const seconds = /\bsec\s*=\s*(\d+)\b/.exec(result.stdout)?.[1];
|
|
46686
|
+
return seconds ? `darwin:${seconds}` : null;
|
|
46687
|
+
}
|
|
46688
|
+
} catch {}
|
|
46689
|
+
return null;
|
|
46690
|
+
}
|
|
46691
|
+
function processStart(pid) {
|
|
46692
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
46693
|
+
return { state: "unknown" };
|
|
46694
|
+
if (process.platform === "linux") {
|
|
46695
|
+
try {
|
|
46696
|
+
const stat = readFileSync10(`/proc/${pid}/stat`, "utf8");
|
|
46697
|
+
const close = stat.lastIndexOf(")");
|
|
46698
|
+
const start = close < 0 ? undefined : stat.slice(close + 2).trim().split(/\s+/)[19];
|
|
46699
|
+
return start && /^\d+$/.test(start) ? { state: "alive", start: `linux:${start}` } : { state: "unknown" };
|
|
46700
|
+
} catch (error61) {
|
|
46701
|
+
return { state: error61.code === "ENOENT" ? "absent" : "unknown" };
|
|
46702
|
+
}
|
|
46703
|
+
}
|
|
46704
|
+
if (process.platform === "darwin") {
|
|
46705
|
+
const result = spawnSync2("/bin/ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
|
|
46706
|
+
if (result.error || result.signal)
|
|
46707
|
+
return { state: "unknown" };
|
|
46708
|
+
const start = result.stdout.trim();
|
|
46709
|
+
if (result.status === 1 && !start && !result.stderr.trim())
|
|
46710
|
+
return { state: "absent" };
|
|
46711
|
+
return result.status === 0 && start ? { state: "alive", start: `darwin:${start}` } : { state: "unknown" };
|
|
46712
|
+
}
|
|
46713
|
+
return { state: "unknown" };
|
|
46714
|
+
}
|
|
46715
|
+
function localLockIdentity() {
|
|
46716
|
+
const bootId = localBootId(), owner = processStart(process.pid);
|
|
46717
|
+
if (!bootId || owner.state !== "alive")
|
|
46718
|
+
return null;
|
|
46719
|
+
let pidNamespace;
|
|
46720
|
+
if (process.platform === "linux") {
|
|
46721
|
+
try {
|
|
46722
|
+
pidNamespace = readlinkSync2("/proc/self/ns/pid");
|
|
46723
|
+
} catch {
|
|
46724
|
+
return null;
|
|
46725
|
+
}
|
|
46726
|
+
if (!/^pid:\[\d+\]$/.test(pidNamespace))
|
|
46727
|
+
return null;
|
|
46728
|
+
}
|
|
46729
|
+
return { host: hostname3(), bootId, pidNamespace, processStart: owner.start };
|
|
46730
|
+
}
|
|
46731
|
+
function readSessionLock(sessionId, options) {
|
|
46732
|
+
const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
|
|
46733
|
+
const before = lstatSync8(path, { throwIfNoEntry: false });
|
|
46734
|
+
if (!before)
|
|
46735
|
+
return null;
|
|
46736
|
+
if (!before.isFile() || before.nlink !== 1 || process.getuid && before.uid !== process.getuid()) {
|
|
46737
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
|
|
46738
|
+
}
|
|
46739
|
+
const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
|
|
46740
|
+
const after = lstatSync8(path, { throwIfNoEntry: false });
|
|
46741
|
+
if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs) {
|
|
46742
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
|
|
46743
|
+
}
|
|
46744
|
+
let marker;
|
|
46745
|
+
try {
|
|
46746
|
+
marker = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
46747
|
+
} catch {
|
|
46748
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is unreadable; recovery requires review.");
|
|
46749
|
+
}
|
|
46750
|
+
if (!marker || ![1, 2].includes(marker.schemaVersion) || marker.sessionId !== sessionId || !/^[a-f0-9-]{36}$/i.test(marker.operationId) || !Number.isSafeInteger(marker.pid) || marker.pid <= 0 || marker.schemaVersion === 2 && (!marker.host || !marker.bootId || !marker.processStart || process.platform === "linux" && !marker.pidNamespace)) {
|
|
46751
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has invalid ownership metadata; recovery requires review.");
|
|
46752
|
+
}
|
|
46753
|
+
return { path, bytes, marker, sha256: sha256Hex(bytes), dev: after.dev, ino: after.ino, mtimeMs: after.mtimeMs };
|
|
46754
|
+
}
|
|
46755
|
+
function writerIsGone(lock, explicitLocalHost) {
|
|
46756
|
+
if (Date.now() - lock.mtimeMs < SESSION_LOCK_MIN_AGE_MS)
|
|
46757
|
+
return false;
|
|
46758
|
+
const owner = processStart(lock.marker.pid);
|
|
46759
|
+
if (owner.state === "unknown")
|
|
46760
|
+
return false;
|
|
46761
|
+
if (lock.marker.schemaVersion === 1)
|
|
46762
|
+
return explicitLocalHost && owner.state === "absent";
|
|
46763
|
+
const local = localLockIdentity();
|
|
46764
|
+
if (!local || lock.marker.host !== local.host || lock.marker.bootId !== local.bootId || lock.marker.pidNamespace !== local.pidNamespace)
|
|
46765
|
+
return false;
|
|
46766
|
+
return owner.state === "absent" || owner.state === "alive" && owner.start !== lock.marker.processStart;
|
|
46767
|
+
}
|
|
46768
|
+
function archiveSessionLock(lock, receipt, explicitLocalHost) {
|
|
46769
|
+
const guardPath = `${lock.path}.recovery-guard`;
|
|
46770
|
+
let guardFd;
|
|
46771
|
+
try {
|
|
46772
|
+
guardFd = openSync6(guardPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46773
|
+
} catch (error61) {
|
|
46774
|
+
if (error61.code === "EEXIST")
|
|
46775
|
+
return null;
|
|
46776
|
+
throw error61;
|
|
46777
|
+
}
|
|
46778
|
+
const guardStat = fstatSync6(guardFd);
|
|
46779
|
+
try {
|
|
46780
|
+
writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
|
|
46781
|
+
`);
|
|
46782
|
+
fsyncSync(guardFd);
|
|
46783
|
+
const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
|
|
46784
|
+
const current = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
46785
|
+
if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
|
|
46786
|
+
return null;
|
|
46787
|
+
const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
|
|
46788
|
+
if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
|
|
46789
|
+
return null;
|
|
46790
|
+
const directory = join14(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
|
|
46791
|
+
const archivePath = join14(directory, "original.write-lock");
|
|
46792
|
+
const recordPath = join14(directory, "recovery.json");
|
|
46793
|
+
assertRegularPath(archivePath, true);
|
|
46794
|
+
archiveBytes(recordPath, Buffer.from(`${JSON.stringify({
|
|
46795
|
+
schemaVersion: 1,
|
|
46796
|
+
sessionId: lock.marker.sessionId,
|
|
46797
|
+
originalPath: lock.path,
|
|
46798
|
+
lockSha256: lock.sha256,
|
|
46799
|
+
lockMtime: new Date(lock.mtimeMs).toISOString(),
|
|
46800
|
+
lockPid: lock.marker.pid,
|
|
46801
|
+
receiptSha256: receipt?.sha256 ?? null,
|
|
46802
|
+
receiptGeneration: receipt?.generation ?? null,
|
|
46803
|
+
status: "prepared"
|
|
46804
|
+
})}
|
|
46805
|
+
`));
|
|
46806
|
+
syncDirectory(directory);
|
|
46807
|
+
syncDirectory(dirname8(directory));
|
|
46808
|
+
const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
|
|
46809
|
+
if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
|
|
46810
|
+
return null;
|
|
46811
|
+
let moved = false;
|
|
46812
|
+
try {
|
|
46813
|
+
renameSync(lock.path, archivePath);
|
|
46814
|
+
moved = true;
|
|
46815
|
+
syncDirectory(directory);
|
|
46816
|
+
syncDirectory(dirname8(lock.path));
|
|
46817
|
+
if (sha256Hex(readRegularFile(archivePath, SESSION_LOCK_MAX_BYTES)) !== lock.sha256) {
|
|
46818
|
+
throw new Error("Archived lock bytes changed");
|
|
46819
|
+
}
|
|
46820
|
+
} catch (error61) {
|
|
46821
|
+
if (moved)
|
|
46822
|
+
throw new SkillSelectionError("SESSION_LOCK_RECOVERY_INCOMPLETE", "The lock moved, but durable recovery could not be confirmed; inspect the preserved recovery directory and session receipt.");
|
|
46823
|
+
throw error61;
|
|
46824
|
+
}
|
|
46825
|
+
return archivePath;
|
|
46826
|
+
} finally {
|
|
46827
|
+
closeSync6(guardFd);
|
|
46828
|
+
const current = lstatSync8(guardPath, { throwIfNoEntry: false });
|
|
46829
|
+
if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
|
|
46830
|
+
unlinkSync(guardPath);
|
|
46831
|
+
}
|
|
46832
|
+
}
|
|
46664
46833
|
function withSessionWriteLocks(sessionIds, options, action) {
|
|
46665
46834
|
const operationId = randomUUID();
|
|
46666
46835
|
const locks = [...new Set(sessionIds)].map((sessionId) => ({ sessionId, path: `${sessionReceiptPath(sessionId, options)}.write-lock` })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
@@ -46680,13 +46849,30 @@ function withSessionWriteLocks(sessionIds, options, action) {
|
|
|
46680
46849
|
try {
|
|
46681
46850
|
fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46682
46851
|
} catch (error61) {
|
|
46683
|
-
if (error61.code
|
|
46684
|
-
throw
|
|
46685
|
-
|
|
46852
|
+
if (error61.code !== "EEXIST")
|
|
46853
|
+
throw error61;
|
|
46854
|
+
try {
|
|
46855
|
+
const stale = readSessionLock(lock.sessionId, options);
|
|
46856
|
+
const receipt = stale && readSkillSessionSnapshotIfExists(lock.sessionId, options);
|
|
46857
|
+
if (stale?.marker.schemaVersion === 2 && writerIsGone(stale, false)) {
|
|
46858
|
+
archiveSessionLock(stale, receipt, false);
|
|
46859
|
+
}
|
|
46860
|
+
} catch (recoveryError) {
|
|
46861
|
+
if (recoveryError instanceof SkillSelectionError && recoveryError.code === "SESSION_LOCK_RECOVERY_INCOMPLETE")
|
|
46862
|
+
throw recoveryError;
|
|
46863
|
+
}
|
|
46864
|
+
try {
|
|
46865
|
+
fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46866
|
+
} catch (retryError) {
|
|
46867
|
+
if (retryError.code === "EEXIST")
|
|
46868
|
+
throw new SkillSelectionError("SESSION_WRITE_LOCKED", "Another Skills process owns a required session write lock; retry after it completes. A surviving legacy lock requires explicit recovery review.");
|
|
46869
|
+
throw retryError;
|
|
46870
|
+
}
|
|
46686
46871
|
}
|
|
46687
46872
|
const stat = fstatSync6(fd);
|
|
46688
46873
|
owned.push({ path: lock.path, fd, dev: stat.dev, ino: stat.ino });
|
|
46689
|
-
|
|
46874
|
+
const identity3 = localLockIdentity();
|
|
46875
|
+
writeFileSync3(fd, `${JSON.stringify({ schemaVersion: identity3 ? 2 : 1, operationId, sessionId: lock.sessionId, pid: process.pid, ...identity3 })}
|
|
46690
46876
|
`);
|
|
46691
46877
|
}
|
|
46692
46878
|
return action(assertOwned);
|
|
@@ -46749,7 +46935,17 @@ function syncDirectory(path) {
|
|
|
46749
46935
|
closeSync6(fd);
|
|
46750
46936
|
}
|
|
46751
46937
|
}
|
|
46752
|
-
|
|
46938
|
+
function archiveBytes(path, bytes) {
|
|
46939
|
+
assertRegularPath(path, true);
|
|
46940
|
+
const fd = openSync6(path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
|
|
46941
|
+
try {
|
|
46942
|
+
writeFileSync3(fd, bytes);
|
|
46943
|
+
fsyncSync(fd);
|
|
46944
|
+
} finally {
|
|
46945
|
+
closeSync6(fd);
|
|
46946
|
+
}
|
|
46947
|
+
}
|
|
46948
|
+
var SkillSelectionError, MAX_CACHED_PROFILE_AGE_MS, SELECTION_LOCK_FILE = "selection.lock.json", SESSION_LOCK_MAX_BYTES = 4096, SESSION_LOCK_MIN_AGE_MS = 30000;
|
|
46753
46949
|
var init_selection_cache = __esm(() => {
|
|
46754
46950
|
init_config();
|
|
46755
46951
|
init_skill_bundle();
|
|
@@ -46807,7 +47003,7 @@ function capturePreparationProcess(child, platform = process.platform, signalGro
|
|
|
46807
47003
|
}
|
|
46808
47004
|
|
|
46809
47005
|
// src/lib/dependency-preparation.ts
|
|
46810
|
-
import { constants as constants4, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync9, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as
|
|
47006
|
+
import { constants as constants4, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync9, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as readFileSync11, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
|
|
46811
47007
|
import { join as join15 } from "path";
|
|
46812
47008
|
function hasDependencyPreparationMarker(skillPath) {
|
|
46813
47009
|
try {
|
|
@@ -46837,7 +47033,7 @@ function claimPreparation(skillPath) {
|
|
|
46837
47033
|
return;
|
|
46838
47034
|
if (!lstatSync9(state).isFile() || lstatSync9(state).isSymbolicLink() || lstatSync9(state).size > 1024)
|
|
46839
47035
|
return;
|
|
46840
|
-
const previous = JSON.parse(
|
|
47036
|
+
const previous = JSON.parse(readFileSync11(state, "utf8"));
|
|
46841
47037
|
if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
|
|
46842
47038
|
return;
|
|
46843
47039
|
}
|
|
@@ -47389,7 +47585,7 @@ var init_skill_aliases = __esm(() => {
|
|
|
47389
47585
|
});
|
|
47390
47586
|
|
|
47391
47587
|
// src/lib/project-state.ts
|
|
47392
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as
|
|
47588
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
47393
47589
|
import { join as join17 } from "path";
|
|
47394
47590
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
47395
47591
|
return join17(targetDir, SKILLS_PROJECT_DIR);
|
|
@@ -47402,7 +47598,7 @@ function loadProjectConfig(targetDir = process.cwd()) {
|
|
|
47402
47598
|
if (!existsSync8(path))
|
|
47403
47599
|
return null;
|
|
47404
47600
|
try {
|
|
47405
|
-
return normalizeProjectConfig(JSON.parse(
|
|
47601
|
+
return normalizeProjectConfig(JSON.parse(readFileSync12(path, "utf-8")));
|
|
47406
47602
|
} catch {
|
|
47407
47603
|
return null;
|
|
47408
47604
|
}
|
|
@@ -47510,7 +47706,7 @@ var init_project_state = __esm(() => {
|
|
|
47510
47706
|
});
|
|
47511
47707
|
|
|
47512
47708
|
// src/lib/installer.ts
|
|
47513
|
-
import { existsSync as existsSync9, readFileSync as
|
|
47709
|
+
import { existsSync as existsSync9, readFileSync as readFileSync13, rmSync } from "fs";
|
|
47514
47710
|
import { join as join18 } from "path";
|
|
47515
47711
|
import { homedir as homedir2 } from "os";
|
|
47516
47712
|
function getSkillPath(name) {
|
|
@@ -47598,7 +47794,7 @@ function readBundledSkillVersion(name) {
|
|
|
47598
47794
|
if (!existsSync9(pkgPath))
|
|
47599
47795
|
return "unknown";
|
|
47600
47796
|
try {
|
|
47601
|
-
const pkg = JSON.parse(
|
|
47797
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
47602
47798
|
return pkg.version || "unknown";
|
|
47603
47799
|
} catch {
|
|
47604
47800
|
return "unknown";
|
|
@@ -47662,7 +47858,7 @@ var init_skill_doc_file = __esm(() => {
|
|
|
47662
47858
|
});
|
|
47663
47859
|
|
|
47664
47860
|
// src/lib/skillinfo.ts
|
|
47665
|
-
import { existsSync as existsSync10, readFileSync as
|
|
47861
|
+
import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
|
|
47666
47862
|
import { join as join19 } from "path";
|
|
47667
47863
|
function isInstructionSkillDir(skillPath, meta3) {
|
|
47668
47864
|
if (meta3?.kind === "instruction")
|
|
@@ -47671,7 +47867,7 @@ function isInstructionSkillDir(skillPath, meta3) {
|
|
|
47671
47867
|
if (!existsSync10(skillMdPath))
|
|
47672
47868
|
return false;
|
|
47673
47869
|
try {
|
|
47674
|
-
return parseSkillFrontmatter(
|
|
47870
|
+
return parseSkillFrontmatter(readFileSync14(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
47675
47871
|
} catch {
|
|
47676
47872
|
return false;
|
|
47677
47873
|
}
|
|
@@ -47706,7 +47902,7 @@ function getSkillRequirements(name) {
|
|
|
47706
47902
|
const meta3 = getSkill(name);
|
|
47707
47903
|
let dependencies = {};
|
|
47708
47904
|
try {
|
|
47709
|
-
dependencies = JSON.parse(
|
|
47905
|
+
dependencies = JSON.parse(readFileSync14(join19(skillPath, "package.json"), "utf8")).dependencies || {};
|
|
47710
47906
|
} catch {}
|
|
47711
47907
|
return getSkillRequirementsFromContent(name, texts, dependencies, meta3);
|
|
47712
47908
|
}
|
|
@@ -47774,7 +47970,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
47774
47970
|
}
|
|
47775
47971
|
let entryPoint2;
|
|
47776
47972
|
try {
|
|
47777
|
-
const pkg = JSON.parse(
|
|
47973
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
47778
47974
|
if (pkg.bin) {
|
|
47779
47975
|
const binValues = Object.values(pkg.bin);
|
|
47780
47976
|
entryPoint2 = binValues[0];
|
|
@@ -47819,7 +48015,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
47819
48015
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
47820
48016
|
let pkg;
|
|
47821
48017
|
try {
|
|
47822
|
-
pkg = JSON.parse(
|
|
48018
|
+
pkg = JSON.parse(readFileSync14(join19(cwd, "package.json"), "utf8"));
|
|
47823
48019
|
} catch {
|
|
47824
48020
|
return { detected: [], recommended: [] };
|
|
47825
48021
|
}
|
|
@@ -47858,7 +48054,7 @@ function extractEnvVars(text2) {
|
|
|
47858
48054
|
function readIfExists(path) {
|
|
47859
48055
|
try {
|
|
47860
48056
|
if (existsSync10(path)) {
|
|
47861
|
-
return
|
|
48057
|
+
return readFileSync14(path, "utf-8");
|
|
47862
48058
|
}
|
|
47863
48059
|
} catch {}
|
|
47864
48060
|
return null;
|
|
@@ -48976,7 +49172,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
|
|
|
48976
49172
|
|
|
48977
49173
|
// src/lib/agent-discovery.ts
|
|
48978
49174
|
import { createHash as createHash14 } from "crypto";
|
|
48979
|
-
import { lstatSync as lstatSync16, readFileSync as
|
|
49175
|
+
import { lstatSync as lstatSync16, readFileSync as readFileSync15, readdirSync as readdirSync6, realpathSync as realpathSync3 } from "fs";
|
|
48980
49176
|
import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute13, join as join23, resolve as resolve13, sep as sep4 } from "path";
|
|
48981
49177
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
48982
49178
|
function parseConfig(text2, path, toml = false) {
|
|
@@ -49008,7 +49204,7 @@ function read(path, changes) {
|
|
|
49008
49204
|
return null;
|
|
49009
49205
|
if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
|
|
49010
49206
|
throw new Error(`Unsupported or oversized native discovery input: ${path}`);
|
|
49011
|
-
return
|
|
49207
|
+
return readFileSync15(path, "utf8");
|
|
49012
49208
|
}
|
|
49013
49209
|
function projected(source, changes, budget = discoveryByteBudget()) {
|
|
49014
49210
|
if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
|
|
@@ -49885,7 +50081,7 @@ var init_search = __esm(() => {
|
|
|
49885
50081
|
});
|
|
49886
50082
|
|
|
49887
50083
|
// src/lib/registry.ts
|
|
49888
|
-
import { existsSync as existsSync12, readFileSync as
|
|
50084
|
+
import { existsSync as existsSync12, readFileSync as readFileSync16, readdirSync as readdirSync8 } from "fs";
|
|
49889
50085
|
import { join as join26 } from "path";
|
|
49890
50086
|
function parseSkillMdFrontmatter(content) {
|
|
49891
50087
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -49932,7 +50128,7 @@ function discoverSkillsInDir(dir, source = "custom") {
|
|
|
49932
50128
|
continue;
|
|
49933
50129
|
let content;
|
|
49934
50130
|
try {
|
|
49935
|
-
content =
|
|
50131
|
+
content = readFileSync16(skillMdPath, "utf-8");
|
|
49936
50132
|
} catch {
|
|
49937
50133
|
continue;
|
|
49938
50134
|
}
|
|
@@ -49969,7 +50165,7 @@ function findExtensionSkillPath(name) {
|
|
|
49969
50165
|
continue;
|
|
49970
50166
|
let content;
|
|
49971
50167
|
try {
|
|
49972
|
-
content =
|
|
50168
|
+
content = readFileSync16(skillMdPath, "utf-8");
|
|
49973
50169
|
} catch {
|
|
49974
50170
|
continue;
|
|
49975
50171
|
}
|
|
@@ -51161,7 +51357,7 @@ var init_remote_workspace_leave = __esm(() => {
|
|
|
51161
51357
|
});
|
|
51162
51358
|
|
|
51163
51359
|
// src/lib/auth-store.ts
|
|
51164
|
-
import { chmodSync, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as
|
|
51360
|
+
import { chmodSync, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync17, renameSync as renameSync4, statSync as statSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
51165
51361
|
import { basename as basename10, dirname as dirname17, join as join27 } from "path";
|
|
51166
51362
|
function getAuthFilePath(env = process.env) {
|
|
51167
51363
|
return skillsCredentialFilePath(env);
|
|
@@ -53175,7 +53371,7 @@ var init_remote_auth = __esm(() => {
|
|
|
53175
53371
|
});
|
|
53176
53372
|
|
|
53177
53373
|
// src/lib/workspace-profile.ts
|
|
53178
|
-
import { constants as constants11, closeSync as closeSync14, fstatSync as fstatSync14, lstatSync as lstatSync18, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync2, openSync as openSync14, readFileSync as
|
|
53374
|
+
import { constants as constants11, closeSync as closeSync14, fstatSync as fstatSync14, lstatSync as lstatSync18, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync2, openSync as openSync14, readFileSync as readFileSync18, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
53179
53375
|
function stat2(path) {
|
|
53180
53376
|
try {
|
|
53181
53377
|
return lstatSync18(path);
|
|
@@ -53193,7 +53389,7 @@ function safeText(file2) {
|
|
|
53193
53389
|
const s = fstatSync14(fd);
|
|
53194
53390
|
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
53195
53391
|
return fail("The selected profile must use bounded owner-only regular files.");
|
|
53196
|
-
return
|
|
53392
|
+
return readFileSync18(fd, "utf8");
|
|
53197
53393
|
} finally {
|
|
53198
53394
|
closeSync14(fd);
|
|
53199
53395
|
}
|
|
@@ -55649,7 +55845,7 @@ var init_credential_state = __esm(() => {
|
|
|
55649
55845
|
|
|
55650
55846
|
// src/lib/run-state.ts
|
|
55651
55847
|
import { createHash as createHash17, randomBytes } from "crypto";
|
|
55652
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as
|
|
55848
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync19, readdirSync as readdirSync9, statSync as statSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
55653
55849
|
import { extname, join as join29, relative as relative5 } from "path";
|
|
55654
55850
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
55655
55851
|
const now = new Date;
|
|
@@ -55721,7 +55917,7 @@ function appendRunEvent(context2, event, data = {}) {
|
|
|
55721
55917
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
55722
55918
|
`;
|
|
55723
55919
|
const path = join29(context2.runDir, "events.ndjson");
|
|
55724
|
-
const previous = existsSync14(path) ?
|
|
55920
|
+
const previous = existsSync14(path) ? readFileSync19(path, "utf-8") : "";
|
|
55725
55921
|
writeFileSync10(path, previous + line);
|
|
55726
55922
|
}
|
|
55727
55923
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
@@ -55759,7 +55955,7 @@ function collectRunArtifacts(context2) {
|
|
|
55759
55955
|
const artifacts = [];
|
|
55760
55956
|
for (const path of walkFiles(context2.exportDir)) {
|
|
55761
55957
|
const stat3 = statSync9(path);
|
|
55762
|
-
const bytes =
|
|
55958
|
+
const bytes = readFileSync19(path);
|
|
55763
55959
|
artifacts.push({
|
|
55764
55960
|
path: toProjectRelative(context2.targetDir, path),
|
|
55765
55961
|
mime: mimeForPath(path),
|
|
@@ -55774,7 +55970,7 @@ function readRunRecord(runDir) {
|
|
|
55774
55970
|
if (!existsSync14(path))
|
|
55775
55971
|
return null;
|
|
55776
55972
|
try {
|
|
55777
|
-
return JSON.parse(
|
|
55973
|
+
return JSON.parse(readFileSync19(path, "utf-8"));
|
|
55778
55974
|
} catch {
|
|
55779
55975
|
return null;
|
|
55780
55976
|
}
|
|
@@ -56034,8 +56230,8 @@ var init_cloud_executions = __esm(() => {
|
|
|
56034
56230
|
|
|
56035
56231
|
// node_modules/@hasna/secrets/dist/sdk.js
|
|
56036
56232
|
import { isIP as isIP2 } from "net";
|
|
56037
|
-
import { spawnSync as
|
|
56038
|
-
import { closeSync as closeSync16, fstatSync as fstatSync16, openSync as openSync16, readFileSync as
|
|
56233
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
56234
|
+
import { closeSync as closeSync16, fstatSync as fstatSync16, openSync as openSync16, readFileSync as readFileSync20 } from "fs";
|
|
56039
56235
|
import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
|
|
56040
56236
|
import { createRequire as createRequire2 } from "module";
|
|
56041
56237
|
import { hostname as osHostname2 } from "os";
|
|
@@ -56161,7 +56357,7 @@ function readAppConfigFile2(path) {
|
|
|
56161
56357
|
unsafe("the file is not owned by the current user");
|
|
56162
56358
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES2)
|
|
56163
56359
|
unsafe("the file exceeds the size limit");
|
|
56164
|
-
const bytes =
|
|
56360
|
+
const bytes = readFileSync20(fd);
|
|
56165
56361
|
const after = fstatSync16(fd);
|
|
56166
56362
|
if (!configFileReadsCoherent2(before, after)) {
|
|
56167
56363
|
unsafe("the file changed while being read");
|
|
@@ -56309,7 +56505,7 @@ function isAmbientEnvironment2(env) {
|
|
|
56309
56505
|
return env === process.env || env[AMBIENT_ENVIRONMENT2] === true;
|
|
56310
56506
|
}
|
|
56311
56507
|
function defaultKeychainRunner2(argv) {
|
|
56312
|
-
const result =
|
|
56508
|
+
const result = spawnSync3(KEYCHAIN_SECURITY_BIN2, [...argv], {
|
|
56313
56509
|
encoding: "utf8",
|
|
56314
56510
|
stdio: ["ignore", "pipe", "pipe"],
|
|
56315
56511
|
timeout: KEYCHAIN_SPAWN_TIMEOUT_MS2
|
|
@@ -58643,7 +58839,7 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
58643
58839
|
});
|
|
58644
58840
|
|
|
58645
58841
|
// src/lib/scheduler.ts
|
|
58646
|
-
import { existsSync as existsSync17, readFileSync as
|
|
58842
|
+
import { existsSync as existsSync17, readFileSync as readFileSync21, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
|
|
58647
58843
|
import { join as join34 } from "path";
|
|
58648
58844
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
58649
58845
|
return join34(targetDir, ".skills", "schedules.json");
|
|
@@ -58652,7 +58848,7 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
58652
58848
|
const path = getSchedulesPath(targetDir);
|
|
58653
58849
|
if (existsSync17(path)) {
|
|
58654
58850
|
try {
|
|
58655
|
-
return JSON.parse(
|
|
58851
|
+
return JSON.parse(readFileSync21(path, "utf-8"));
|
|
58656
58852
|
} catch {}
|
|
58657
58853
|
}
|
|
58658
58854
|
return { version: 1, schedules: [] };
|
|
@@ -58935,7 +59131,7 @@ import { createHash as createHash19, createHmac } from "crypto";
|
|
|
58935
59131
|
import {
|
|
58936
59132
|
existsSync as existsSync18,
|
|
58937
59133
|
mkdirSync as mkdirSync15,
|
|
58938
|
-
readFileSync as
|
|
59134
|
+
readFileSync as readFileSync22,
|
|
58939
59135
|
readdirSync as readdirSync11,
|
|
58940
59136
|
statSync as statSync11,
|
|
58941
59137
|
writeFileSync as writeFileSync13
|
|
@@ -59003,7 +59199,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
59003
59199
|
const files = [];
|
|
59004
59200
|
if (existsSync18(projectStateDir)) {
|
|
59005
59201
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
59006
|
-
const bytes =
|
|
59202
|
+
const bytes = readFileSync22(filePath);
|
|
59007
59203
|
const relativePath = toPosix(relative6(targetDir, filePath));
|
|
59008
59204
|
files.push({
|
|
59009
59205
|
path: relativePath,
|
|
@@ -62205,7 +62401,7 @@ async function handleMcpHttpNodeRequest(req, res) {
|
|
|
62205
62401
|
return true;
|
|
62206
62402
|
}
|
|
62207
62403
|
async function startSkillsMcpHttpServer(options = {}) {
|
|
62208
|
-
const
|
|
62404
|
+
const hostname4 = options.hostname ?? "127.0.0.1";
|
|
62209
62405
|
const port = options.port ?? parseMcpHttpPort(process.argv.slice(2));
|
|
62210
62406
|
const httpServer = createServer(async (req, res) => {
|
|
62211
62407
|
try {
|
|
@@ -62223,11 +62419,11 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
62223
62419
|
});
|
|
62224
62420
|
await new Promise((resolve19, reject) => {
|
|
62225
62421
|
httpServer.once("error", reject);
|
|
62226
|
-
httpServer.listen(port,
|
|
62422
|
+
httpServer.listen(port, hostname4, () => resolve19());
|
|
62227
62423
|
});
|
|
62228
62424
|
const address = httpServer.address();
|
|
62229
62425
|
const listenPort = typeof address === "object" && address ? address.port : port;
|
|
62230
|
-
console.error(`skills-mcp HTTP listening on http://${
|
|
62426
|
+
console.error(`skills-mcp HTTP listening on http://${hostname4}:${listenPort}/mcp`);
|
|
62231
62427
|
return httpServer;
|
|
62232
62428
|
}
|
|
62233
62429
|
var MCP_HTTP_DEFAULT_PORT = 8836, MCP_HTTP_SERVICE_NAME = "skills";
|
package/bin/migrate.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@hasna/skills",
|
|
10
|
-
version: "0.10.
|
|
10
|
+
version: "0.10.2",
|
|
11
11
|
description: "Skills library for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -201,15 +201,11 @@ function resolveServerConfig(env = process.env) {
|
|
|
201
201
|
requestBodyLimitBytes: parsePositiveInt(env.HASNA_SKILLS_REQUEST_BODY_LIMIT_BYTES, 1e6),
|
|
202
202
|
skillBundleLimitBytes: parsePositiveInt(env.HASNA_SKILLS_BUNDLE_LIMIT_BYTES, 25000000),
|
|
203
203
|
tombstoneWindowMs: parsePositiveInt(env.HASNA_SKILLS_TOMBSTONE_WINDOW_MS, 7 * 24 * 60 * 60 * 1000),
|
|
204
|
-
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL ||
|
|
204
|
+
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL || "").replace(/\/+$/, ""),
|
|
205
205
|
nodeEnv,
|
|
206
206
|
allowEphemeralStore: env.HASNA_SKILLS_ALLOW_EPHEMERAL_STORE === "1"
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
-
function localOrigin(host, port) {
|
|
210
|
-
const hostname = host === "0.0.0.0" || host === "::" ? "localhost" : host;
|
|
211
|
-
return `http://${hostname.includes(":") ? `[${hostname}]` : hostname}:${port}`;
|
|
212
|
-
}
|
|
213
209
|
function parsePositiveInt(value, fallback) {
|
|
214
210
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
215
211
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.10.
|
|
24125
|
+
version: "0.10.2",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
|
@@ -42903,15 +42903,11 @@ function resolveServerConfig(env = process.env) {
|
|
|
42903
42903
|
requestBodyLimitBytes: parsePositiveInt(env.HASNA_SKILLS_REQUEST_BODY_LIMIT_BYTES, 1e6),
|
|
42904
42904
|
skillBundleLimitBytes: parsePositiveInt(env.HASNA_SKILLS_BUNDLE_LIMIT_BYTES, 25000000),
|
|
42905
42905
|
tombstoneWindowMs: parsePositiveInt(env.HASNA_SKILLS_TOMBSTONE_WINDOW_MS, 7 * 24 * 60 * 60 * 1000),
|
|
42906
|
-
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL ||
|
|
42906
|
+
publicBaseUrl: (env.SKILLS_PUBLIC_BASE_URL || "").replace(/\/+$/, ""),
|
|
42907
42907
|
nodeEnv,
|
|
42908
42908
|
allowEphemeralStore: env.HASNA_SKILLS_ALLOW_EPHEMERAL_STORE === "1"
|
|
42909
42909
|
};
|
|
42910
42910
|
}
|
|
42911
|
-
function localOrigin(host, port) {
|
|
42912
|
-
const hostname = host === "0.0.0.0" || host === "::" ? "localhost" : host;
|
|
42913
|
-
return `http://${hostname.includes(":") ? `[${hostname}]` : hostname}:${port}`;
|
|
42914
|
-
}
|
|
42915
42911
|
function parsePositiveInt(value, fallback) {
|
|
42916
42912
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
42917
42913
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -44150,6 +44146,9 @@ async function createSkillsFetchHandler(options = {}) {
|
|
|
44150
44146
|
const config = { ...resolveServerConfig(), ...options.config };
|
|
44151
44147
|
if (!options.store)
|
|
44152
44148
|
assertDurableTarget(resolveDatabaseTarget(config.databaseUrl), config);
|
|
44149
|
+
if (!config.publicBaseUrl) {
|
|
44150
|
+
throw new Error("refusing to start: set SKILLS_PUBLIC_BASE_URL to this Skills server's public API origin; a bound socket is not an authority for client receipts.");
|
|
44151
|
+
}
|
|
44153
44152
|
const store = options.store ?? await createStore({
|
|
44154
44153
|
databaseUrl: config.databaseUrl,
|
|
44155
44154
|
bootstrapApiKey: config.bootstrapApiKey
|