@geoly-ai/skills-hub 0.1.0
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/LICENSE +21 -0
- package/README.md +98 -0
- package/bin/skills-hub.mjs +26 -0
- package/package.json +44 -0
- package/src/adapters/index.mjs +832 -0
- package/src/artifact.mjs +376 -0
- package/src/atomic-fs.mjs +166 -0
- package/src/attestation.mjs +136 -0
- package/src/canonical-json.mjs +147 -0
- package/src/cli.mjs +208 -0
- package/src/commands/check.mjs +295 -0
- package/src/commands/context.mjs +235 -0
- package/src/commands/install.mjs +430 -0
- package/src/commands/locks.mjs +197 -0
- package/src/commands/output.mjs +127 -0
- package/src/commands/query.mjs +266 -0
- package/src/commands/recover.mjs +438 -0
- package/src/commands/registry.mjs +123 -0
- package/src/commands/resolve.mjs +171 -0
- package/src/commands/snapshot-access.mjs +91 -0
- package/src/commands/sync-lock.mjs +189 -0
- package/src/crc32c.mjs +27 -0
- package/src/exit-codes.mjs +265 -0
- package/src/fault-inject.mjs +379 -0
- package/src/install.mjs +732 -0
- package/src/journal.mjs +435 -0
- package/src/ledger.mjs +671 -0
- package/src/lock.mjs +98 -0
- package/src/lockfile.mjs +0 -0
- package/src/pack.mjs +792 -0
- package/src/packer.mjs +351 -0
- package/src/plan.mjs +519 -0
- package/src/recover.mjs +1345 -0
- package/src/safe-fs.mjs +252 -0
- package/src/sigstore.mjs +480 -0
- package/src/snapshot.mjs +528 -0
- package/src/stats.mjs +59 -0
- package/src/target.mjs +738 -0
- package/src/telemetry.mjs +393 -0
- package/src/tree-digest.mjs +103 -0
- package/src/trust-roots/README.md +31 -0
- package/src/trust-roots/sigstore-public-good.json +126 -0
- package/src/trust.mjs +563 -0
- package/src/untar.mjs +570 -0
- package/src/upload.mjs +268 -0
- package/src/vendor.mjs +465 -0
package/src/lock.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// 锁 —— 规范见 04-install.md §5.1
|
|
2
|
+
// 🔴 用 node:sqlite 的 BEGIN EXCLUSIVE:进程退出由内核释放,协议里**没有任何 unlink**。
|
|
3
|
+
// 🔴 本模块**独占**这些 db 路径;禁止任何其它代码用 fs.open/close 打开它们
|
|
4
|
+
// (POSIX 的「关闭任一 fd 释放该进程全部锁」对绕过 SQLite 的 fd 仍然成立)。
|
|
5
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
6
|
+
import { hostname } from 'node:os';
|
|
7
|
+
|
|
8
|
+
const held = new Map(); // path -> { db }
|
|
9
|
+
|
|
10
|
+
export class LockBusyError extends Error {
|
|
11
|
+
constructor(path, holder) {
|
|
12
|
+
const who = holder
|
|
13
|
+
? `上一次持锁的是 pid ${holder.pid}@${holder.host}(可能已不是当前持有者)`
|
|
14
|
+
: '无法读取持有者信息';
|
|
15
|
+
super(`锁被占用:${path}\n ${who}`);
|
|
16
|
+
this.name = 'LockBusyError';
|
|
17
|
+
this.code = 5;
|
|
18
|
+
this.holder = holder;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 🔴 SQLITE_BUSY 不只从 `BEGIN EXCLUSIVE` 抛出。
|
|
24
|
+
*
|
|
25
|
+
* `PRAGMA journal_mode=WAL` 与 `CREATE TABLE` **都是写操作**,在 `busy_timeout=0`
|
|
26
|
+
* 下遇到并发写者会直接抛 `database is locked`,而那发生在 `BEGIN EXCLUSIVE` **之前**。
|
|
27
|
+
* 早先只在 BEGIN 处兜 LockBusyError,于是首次并发建库时绝大多数进程拿到的是裸
|
|
28
|
+
* `Error` —— 退出码 5、持有者信息、调用方的 busy 分支**全部失效**。
|
|
29
|
+
*
|
|
30
|
+
* 实测(16 进程 × 5 轮,全新 db):80 次里 **69 次**是裸 Error,只有 5 次是 LockBusyError。
|
|
31
|
+
* 所以判据放在这里:**只要是 BUSY/LOCKED,无论来自哪一句,都归一成 LockBusyError**。
|
|
32
|
+
*/
|
|
33
|
+
const isBusy = (e) =>
|
|
34
|
+
e?.errcode === 5 || e?.errcode === 6 || // SQLITE_BUSY / SQLITE_LOCKED
|
|
35
|
+
/database is locked|database table is locked/i.test(e?.message ?? '');
|
|
36
|
+
|
|
37
|
+
function openDb(path) {
|
|
38
|
+
const db = new DatabaseSync(path);
|
|
39
|
+
try {
|
|
40
|
+
db.exec('PRAGMA busy_timeout=0'); // 🔴 先设:后面两句都是写,都要不等待
|
|
41
|
+
db.exec('PRAGMA journal_mode=WAL');
|
|
42
|
+
db.exec('CREATE TABLE IF NOT EXISTS holder(k INTEGER PRIMARY KEY, pid INT, host TEXT, cli TEXT, at TEXT)');
|
|
43
|
+
} catch (e) {
|
|
44
|
+
try { db.close(); } catch { /* 已经坏了 */ }
|
|
45
|
+
if (isBusy(e)) throw new LockBusyError(path, readHolder(path));
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
return db;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 只读地看一眼上一次已提交的持有者信息 —— 🔴 必然陈旧,仅供人阅读 */
|
|
52
|
+
export function readHolder(path) {
|
|
53
|
+
let db;
|
|
54
|
+
try {
|
|
55
|
+
db = new DatabaseSync(path, { readOnly: true });
|
|
56
|
+
return db.prepare('SELECT pid, host, cli, at FROM holder WHERE k=1').get() ?? null;
|
|
57
|
+
} catch { return null; }
|
|
58
|
+
finally { try { db?.close(); } catch {} } // 🔴 读完立即关闭,避免 checkpoint starvation
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 取锁。成功返回一个 release 函数。
|
|
63
|
+
* 🔴 holder 在**外层事务内**写、**不中途提交** —— COMMIT 会提交外层事务并释放锁。
|
|
64
|
+
*/
|
|
65
|
+
export function acquire(path, { cli = 'skills-hub' } = {}) {
|
|
66
|
+
if (held.has(path)) throw new Error(`lock: 本进程已持有 ${path}(禁止重入)`);
|
|
67
|
+
const db = openDb(path);
|
|
68
|
+
try {
|
|
69
|
+
db.exec('BEGIN EXCLUSIVE');
|
|
70
|
+
} catch (e) {
|
|
71
|
+
const holder = readHolder(path);
|
|
72
|
+
try { db.close(); } catch { /* 已经坏了 */ }
|
|
73
|
+
if (isBusy(e)) throw new LockBusyError(path, holder);
|
|
74
|
+
throw e; // 不是「被占用」就别谎称是 —— 磁盘满、db 损坏要原样报出去
|
|
75
|
+
}
|
|
76
|
+
// 在外层事务内写 holder,最终 COMMIT 时才对别人可见
|
|
77
|
+
db.prepare('INSERT OR REPLACE INTO holder(k,pid,host,cli,at) VALUES (1,?,?,?,?)')
|
|
78
|
+
.run(process.pid, hostname(), cli, new Date().toISOString().replace(/\.\d+Z$/, 'Z'));
|
|
79
|
+
held.set(path, { db });
|
|
80
|
+
let released = false;
|
|
81
|
+
return function release({ commit = true } = {}) {
|
|
82
|
+
if (released) return;
|
|
83
|
+
released = true;
|
|
84
|
+
try { db.exec(commit ? 'COMMIT' : 'ROLLBACK'); } finally { db.close(); held.delete(path); }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 按 (st_dev, st_ino) 去重后取多把锁;失败时对已持有的逐一 ROLLBACK + close */
|
|
89
|
+
export function acquireAll(paths) {
|
|
90
|
+
const releases = [];
|
|
91
|
+
try {
|
|
92
|
+
for (const p of paths) releases.push(acquire(p));
|
|
93
|
+
return () => { for (const r of releases.reverse()) r(); };
|
|
94
|
+
} catch (e) {
|
|
95
|
+
for (const r of releases.reverse()) { try { r({ commit: false }); } catch {} }
|
|
96
|
+
throw e;
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/lockfile.mjs
ADDED
|
Binary file
|