@getbourdon/participants 0.2.2 → 0.2.4
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 +7 -1
- package/dist/base.d.ts +109 -0
- package/dist/base.js +88 -0
- package/dist/index.cjs +171 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -4
- package/dist/index.d.ts +42 -970
- package/dist/index.js +53 -6509
- package/dist/index.js.map +1 -1
- package/dist/network-base.d.ts +108 -0
- package/dist/network-base.js +248 -0
- package/dist/readers/cascade.d.ts +49 -0
- package/dist/readers/cascade.js +428 -0
- package/dist/readers/claude-code-automations.d.ts +62 -0
- package/dist/readers/claude-code-automations.js +693 -0
- package/dist/readers/claude-code.d.ts +50 -0
- package/dist/readers/claude-code.js +583 -0
- package/dist/readers/claude-desktop-code.d.ts +54 -0
- package/dist/readers/claude-desktop-code.js +526 -0
- package/dist/readers/claude-desktop-cowork.d.ts +47 -0
- package/dist/readers/claude-desktop-cowork.js +659 -0
- package/dist/readers/codex-automations.d.ts +55 -0
- package/dist/readers/codex-automations.js +519 -0
- package/dist/readers/codex-sessions.d.ts +88 -0
- package/dist/readers/codex-sessions.js +194 -0
- package/dist/readers/copilot-automations.d.ts +64 -0
- package/dist/readers/copilot-automations.js +530 -0
- package/dist/readers/copilot-cli.d.ts +45 -0
- package/dist/readers/copilot-cli.js +416 -0
- package/dist/readers/copilot-vscode.d.ts +54 -0
- package/dist/readers/copilot-vscode.js +553 -0
- package/dist/readers/copilot.d.ts +59 -0
- package/dist/readers/copilot.js +413 -0
- package/dist/readers/cursor-automations.d.ts +57 -0
- package/dist/readers/cursor-automations.js +685 -0
- package/dist/readers/cursor.d.ts +50 -0
- package/dist/readers/cursor.js +670 -0
- package/dist/readers/github-copilot.d.ts +34 -0
- package/dist/readers/github-copilot.js +242 -0
- package/dist/readers/hermes.d.ts +37 -0
- package/dist/readers/hermes.js +426 -0
- package/dist/readers/openclaw.d.ts +78 -0
- package/dist/readers/openclaw.js +386 -0
- package/dist/registry.d.ts +39 -0
- package/dist/registry.js +98 -0
- package/dist/sqlite-base.d.ts +65 -0
- package/dist/sqlite-base.js +163 -0
- package/package.json +5 -5
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared read-only SQLite + normalization helpers — port of
|
|
3
|
+
* `participants/_sqlite_base.py`.
|
|
4
|
+
*
|
|
5
|
+
* A SQLite-backed participant (hermes today; cursor/copilot_cli in the follow-on
|
|
6
|
+
* slice) reads a foreign agent's native DB read-only so a live writer's lock can
|
|
7
|
+
* never block the export, probes the schema defensively (native schemas drift
|
|
8
|
+
* across versions), and turns epoch timestamps + paths into the L5 shapes.
|
|
9
|
+
*
|
|
10
|
+
* `better-sqlite3` is a NATIVE addon. It is loaded lazily through `createRequire`
|
|
11
|
+
* inside a try/catch so this package — and the file/network readers — keep
|
|
12
|
+
* working even on a toolchain where the addon failed to build: in that case
|
|
13
|
+
* {@link sqliteAvailable} returns false and the sqlite readers degrade rather
|
|
14
|
+
* than crashing import. Nothing here applies visibility or builds manifests;
|
|
15
|
+
* that stays in the participant (per PARTICIPANT_CONTRACT.md).
|
|
16
|
+
*/
|
|
17
|
+
import { statSync } from "node:fs";
|
|
18
|
+
import { createRequire } from "node:module";
|
|
19
|
+
const moduleUrl = (typeof import.meta !== "undefined" && import.meta.url) ||
|
|
20
|
+
(typeof __filename !== "undefined" ? __filename : process.cwd());
|
|
21
|
+
const nodeRequire = createRequire(moduleUrl);
|
|
22
|
+
let driver;
|
|
23
|
+
/** Lazily resolve the better-sqlite3 constructor, or null if the addon is
|
|
24
|
+
* unavailable (failed native build). Cached after the first attempt. */
|
|
25
|
+
function loadDriver() {
|
|
26
|
+
if (driver !== undefined)
|
|
27
|
+
return driver;
|
|
28
|
+
try {
|
|
29
|
+
driver = nodeRequire("better-sqlite3");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
driver = null;
|
|
33
|
+
}
|
|
34
|
+
return driver;
|
|
35
|
+
}
|
|
36
|
+
/** True when the better-sqlite3 native addon loaded successfully. Tests gate
|
|
37
|
+
* the SQLite-reader assertions on this so a missing addon skips, not fails. */
|
|
38
|
+
export function sqliteAvailable() {
|
|
39
|
+
return loadDriver() !== null;
|
|
40
|
+
}
|
|
41
|
+
// -- Read-only connection -----------------------------------------------------
|
|
42
|
+
/**
|
|
43
|
+
* Open a SQLite DB read-only so a live writer is never disturbed. The default
|
|
44
|
+
* (`immutable` omitted) is the safe `mode=ro`: it respects SQLite locking and
|
|
45
|
+
* reads the `-wal` sidecar of a live, actively-writing agent. Raises on a
|
|
46
|
+
* genuine open failure; callers wanting the never-raises contract use
|
|
47
|
+
* {@link tryOpenReadonly}.
|
|
48
|
+
*/
|
|
49
|
+
export function openReadonly(dbPath, opts = {}) {
|
|
50
|
+
const Ctor = loadDriver();
|
|
51
|
+
if (!Ctor) {
|
|
52
|
+
throw new Error("better-sqlite3 native addon is not available");
|
|
53
|
+
}
|
|
54
|
+
// better-sqlite3 maps `{readonly:true}` to SQLITE_OPEN_READONLY. There is no
|
|
55
|
+
// first-class `immutable=1` knob; for our static snapshots `readonly` is
|
|
56
|
+
// sufficient (we never open a concurrently-written fixture DB).
|
|
57
|
+
return new Ctor(dbPath, {
|
|
58
|
+
readonly: true,
|
|
59
|
+
fileMustExist: true,
|
|
60
|
+
timeout: opts.timeoutMs ?? 2000,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Like {@link openReadonly} but returns null instead of raising — for the
|
|
65
|
+
* export/health path where a missing or corrupt DB must degrade to "no rows".
|
|
66
|
+
*/
|
|
67
|
+
export function tryOpenReadonly(dbPath, opts = {}) {
|
|
68
|
+
try {
|
|
69
|
+
if (!statSync(dbPath).isFile())
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return openReadonly(dbPath, opts);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// -- Defensive schema probes --------------------------------------------------
|
|
83
|
+
/** True if `name` is a table in the connected DB (swallows errors → false). */
|
|
84
|
+
export function tableExists(db, name) {
|
|
85
|
+
try {
|
|
86
|
+
const row = db
|
|
87
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1")
|
|
88
|
+
.get(name);
|
|
89
|
+
return row !== undefined;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Column names for `table` (empty set if absent / on error). PRAGMA cannot be
|
|
96
|
+
* parameterized, matching the Python f-string. */
|
|
97
|
+
export function tableColumns(db, table) {
|
|
98
|
+
try {
|
|
99
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
100
|
+
return new Set(rows.map((r) => r.name));
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return new Set();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// -- Normalization ------------------------------------------------------------
|
|
107
|
+
function toFiniteNumber(value) {
|
|
108
|
+
if (value === null || value === undefined)
|
|
109
|
+
return null;
|
|
110
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
111
|
+
return Number.isFinite(n) ? n : null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Convert epoch seconds (number/string) to an ISO `YYYY-MM-DD` UTC date, or
|
|
115
|
+
* null for missing/unparseable values. Mirrors `epoch_to_iso_date`.
|
|
116
|
+
*/
|
|
117
|
+
export function epochToIsoDate(value) {
|
|
118
|
+
const n = toFiniteNumber(value);
|
|
119
|
+
if (n === null)
|
|
120
|
+
return null;
|
|
121
|
+
const d = new Date(n * 1000);
|
|
122
|
+
if (Number.isNaN(d.getTime()))
|
|
123
|
+
return null;
|
|
124
|
+
return d.toISOString().slice(0, 10);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Convert epoch seconds to a full ISO 8601 UTC datetime, or null. The offset is
|
|
128
|
+
* rendered `+00:00` (Python `datetime(...).isoformat()` style) rather than `Z`.
|
|
129
|
+
*/
|
|
130
|
+
export function epochToIsoDatetime(value) {
|
|
131
|
+
const n = toFiniteNumber(value);
|
|
132
|
+
if (n === null)
|
|
133
|
+
return null;
|
|
134
|
+
const d = new Date(n * 1000);
|
|
135
|
+
if (Number.isNaN(d.getTime()))
|
|
136
|
+
return null;
|
|
137
|
+
return d.toISOString().replace(/\.\d{3}Z$/, "+00:00").replace(/Z$/, "+00:00");
|
|
138
|
+
}
|
|
139
|
+
function basenameOf(p) {
|
|
140
|
+
const trimmed = p.replace(/[\\/]+$/, "");
|
|
141
|
+
const parts = trimmed.split(/[\\/]/);
|
|
142
|
+
return parts[parts.length - 1] ?? "";
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Derive a lowercase project key from a working-directory path: the basename
|
|
146
|
+
* lowercased, or null when the cwd is empty or its basename is "generic"
|
|
147
|
+
* (carries no project identity). Mirrors `project_key_from_cwd`.
|
|
148
|
+
*/
|
|
149
|
+
export function projectKeyFromCwd(cwd, genericNames = new Set()) {
|
|
150
|
+
if (!cwd)
|
|
151
|
+
return null;
|
|
152
|
+
const name = basenameOf(cwd).trim().toLowerCase();
|
|
153
|
+
if (!name || genericNames.has(name))
|
|
154
|
+
return null;
|
|
155
|
+
return name;
|
|
156
|
+
}
|
|
157
|
+
/** Turn a slug-ish key (`my-cool_project`) into a Title Case label. */
|
|
158
|
+
export function friendlyLabel(key) {
|
|
159
|
+
return key
|
|
160
|
+
.replace(/[-_]+/g, " ")
|
|
161
|
+
.trim()
|
|
162
|
+
.replace(/\S+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());
|
|
163
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getbourdon/participants",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Bourdon participants — the external-agent → L5 reader layer (BUSL-1.1). Each participant normalizes a foreign agent's native memory store into a visibility-filtered, redacted L5 manifest: read-only SQLite readers (better-sqlite3 mode=ro: hermes), file/convention readers (claude-code), and network readers (github-copilot) with a TTL JSON cache + lazy AuthProvider that never persists a token. Four invariants enforced in code, not by trust: visibility-filter-before-emission, deterministic exportL5, healthCheck never throws, redact every native string. Discovery is a static TS registry (agent_id-sorted, first-wins, log-and-skip) replacing Python's pkgutil scan. Python (pip install bourdon) is the oracle; this mirror asserts against @getbourdon/conformance native_stores fixtures.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -29,21 +29,21 @@
|
|
|
29
29
|
"homepage": "https://bourdon.ai",
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
32
|
-
"url": "git+https://gitlab.com/
|
|
32
|
+
"url": "git+https://gitlab.com/bourdonai/bourdon-js.git",
|
|
33
33
|
"directory": "packages/participants"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"smol-toml": "^1.7.0",
|
|
37
37
|
"yaml": "^2.6.1",
|
|
38
|
-
"@getbourdon/l5": "0.
|
|
39
|
-
"@getbourdon/redaction": "0.1.
|
|
38
|
+
"@getbourdon/l5": "0.3.0",
|
|
39
|
+
"@getbourdon/redaction": "0.1.4"
|
|
40
40
|
},
|
|
41
41
|
"optionalDependencies": {
|
|
42
42
|
"better-sqlite3": "^12.2.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/better-sqlite3": "^7.6.12",
|
|
46
|
-
"@getbourdon/conformance": "0.
|
|
46
|
+
"@getbourdon/conformance": "0.3.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsup",
|