@getbourdon/participants 0.2.3 → 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/dist/base.d.ts +109 -0
- package/dist/base.js +88 -0
- package/dist/index.d.ts +42 -1083
- package/dist/index.js +53 -6590
- 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 +6 -6
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub-embedded Copilot participant — the v0.3 network-adapter reference.
|
|
3
|
+
* Port of `participants/github_copilot.py`.
|
|
4
|
+
*
|
|
5
|
+
* Cloud-side Copilot (PR review, `@copilot` comments) persists nothing on the
|
|
6
|
+
* user's machine — its state lives on github.com. This adapter federates it by
|
|
7
|
+
* reading the user's GitHub activity over the REST API. The
|
|
8
|
+
* {@link NetworkParticipant} base provides the cache, degradation, and auth
|
|
9
|
+
* boundary; this class provides only the GitHub-specific fetch + payload→L5.
|
|
10
|
+
*
|
|
11
|
+
* Auth: a token with `repo` + `read:user` scope, sourced from `$GITHUB_TOKEN` /
|
|
12
|
+
* `$GH_TOKEN`, else `gh auth token`. Fetched lazily; never stored in the cache
|
|
13
|
+
* or the manifest.
|
|
14
|
+
*/
|
|
15
|
+
import { type L5ManifestModel } from "@getbourdon/l5";
|
|
16
|
+
import { NetworkParticipant, type AuthProvider, type NetworkParticipantOptions } from "../network-base.js";
|
|
17
|
+
/** Resolve a GitHub token: env var first, then the `gh` CLI keychain. */
|
|
18
|
+
export declare function ghTokenProvider(): string | null;
|
|
19
|
+
/** Federates cloud-side GitHub Copilot activity via the GitHub REST API. */
|
|
20
|
+
export declare class GitHubCopilotParticipant extends NetworkParticipant {
|
|
21
|
+
participantSlug: string;
|
|
22
|
+
agentId: string;
|
|
23
|
+
agentType: string;
|
|
24
|
+
nativePath: string;
|
|
25
|
+
displayName: string;
|
|
26
|
+
private readonly maxResults;
|
|
27
|
+
constructor(opts?: NetworkParticipantOptions & {
|
|
28
|
+
maxResults?: number;
|
|
29
|
+
});
|
|
30
|
+
static defaultNativePath(): string;
|
|
31
|
+
fetchPayload(token: string): Record<string, unknown>;
|
|
32
|
+
payloadToL5(payload: Record<string, unknown>): L5ManifestModel;
|
|
33
|
+
}
|
|
34
|
+
export type { AuthProvider };
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub-embedded Copilot participant — the v0.3 network-adapter reference.
|
|
3
|
+
* Port of `participants/github_copilot.py`.
|
|
4
|
+
*
|
|
5
|
+
* Cloud-side Copilot (PR review, `@copilot` comments) persists nothing on the
|
|
6
|
+
* user's machine — its state lives on github.com. This adapter federates it by
|
|
7
|
+
* reading the user's GitHub activity over the REST API. The
|
|
8
|
+
* {@link NetworkParticipant} base provides the cache, degradation, and auth
|
|
9
|
+
* boundary; this class provides only the GitHub-specific fetch + payload→L5.
|
|
10
|
+
*
|
|
11
|
+
* Auth: a token with `repo` + `read:user` scope, sourced from `$GITHUB_TOKEN` /
|
|
12
|
+
* `$GH_TOKEN`, else `gh auth token`. Fetched lazily; never stored in the cache
|
|
13
|
+
* or the manifest.
|
|
14
|
+
*/
|
|
15
|
+
import { execFileSync } from "node:child_process";
|
|
16
|
+
import { Visibility, makeAgentInfo, makeEntity, makeManifest, makeSession, makeVisibilityPolicy, } from "@getbourdon/l5";
|
|
17
|
+
import { redactText } from "@getbourdon/redaction";
|
|
18
|
+
import { SPEC_VERSION } from "../base.js";
|
|
19
|
+
import { NetworkParticipant, NetworkUnavailable, ParticipantAuthError, } from "../network-base.js";
|
|
20
|
+
const AGENT_ID = "github-copilot";
|
|
21
|
+
const AGENT_TYPE = "code-assistant";
|
|
22
|
+
const PARTICIPANT_SLUG = "github-copilot";
|
|
23
|
+
const DISPLAY_NAME = "GitHub Copilot (cloud)";
|
|
24
|
+
const ROLE_NARRATIVE = "Cloud-side GitHub Copilot: PR review and @copilot-authored comments/" +
|
|
25
|
+
"suggestions that live on github.com, not on the user's machine. Federated " +
|
|
26
|
+
"via the GitHub REST API rather than a local artifact.";
|
|
27
|
+
const GITHUB_API_ROOT = "https://api.github.com";
|
|
28
|
+
const DEFAULT_POLICY = makeVisibilityPolicy({
|
|
29
|
+
default: Visibility.TEAM,
|
|
30
|
+
private_tags: ["personal", "financial", "credential", "secret"],
|
|
31
|
+
team_tags: ["github-copilot", "pull-request"],
|
|
32
|
+
});
|
|
33
|
+
// -- Auth resolution ----------------------------------------------------------
|
|
34
|
+
/** Resolve a GitHub token: env var first, then the `gh` CLI keychain. */
|
|
35
|
+
export function ghTokenProvider() {
|
|
36
|
+
for (const varName of ["GITHUB_TOKEN", "GH_TOKEN"]) {
|
|
37
|
+
const val = process.env[varName];
|
|
38
|
+
if (val)
|
|
39
|
+
return val;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const out = execFileSync("gh", ["auth", "token"], {
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
timeout: 5000,
|
|
45
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
46
|
+
});
|
|
47
|
+
const token = out.trim();
|
|
48
|
+
if (token)
|
|
49
|
+
return token;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
/* gh missing / not logged in */
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Synchronous GET via a tiny `curl` shell-out (the base class's fetch contract
|
|
58
|
+
* is synchronous, matching the Python urllib reference). Returns parsed JSON.
|
|
59
|
+
* Classifies failures into the two contract errors: rate-limit / 5xx /
|
|
60
|
+
* transport / non-JSON → {@link NetworkUnavailable}; 401/403 →
|
|
61
|
+
* {@link ParticipantAuthError}. Never raises anything else.
|
|
62
|
+
*/
|
|
63
|
+
function githubGet(path, token, timeoutSec = 10) {
|
|
64
|
+
const url = path.startsWith("http") ? path : `${GITHUB_API_ROOT}${path}`;
|
|
65
|
+
let resp;
|
|
66
|
+
try {
|
|
67
|
+
resp = curlGet(url, token, timeoutSec);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
throw new NetworkUnavailable(`GitHub API unreachable: ${String(err)}`);
|
|
71
|
+
}
|
|
72
|
+
const remaining = resp.headers["x-ratelimit-remaining"];
|
|
73
|
+
if (resp.status === 429 || (resp.status === 403 && String(remaining) === "0")) {
|
|
74
|
+
throw new NetworkUnavailable(`GitHub API ${resp.status}: rate-limited`);
|
|
75
|
+
}
|
|
76
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
77
|
+
throw new ParticipantAuthError(`GitHub API ${resp.status}: token invalid or lacks scope`);
|
|
78
|
+
}
|
|
79
|
+
if (resp.status >= 500) {
|
|
80
|
+
throw new NetworkUnavailable(`GitHub API ${resp.status}`);
|
|
81
|
+
}
|
|
82
|
+
if (resp.status >= 400) {
|
|
83
|
+
throw new NetworkUnavailable(`GitHub API ${resp.status}`);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(resp.body);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
// A 200 with a non-JSON body (captive portal / proxy splash) is a degraded
|
|
90
|
+
// condition, not a hard crash.
|
|
91
|
+
throw new NetworkUnavailable(`GitHub API returned a non-JSON body: ${String(err)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function curlGet(url, token, timeoutSec) {
|
|
95
|
+
// `-sS` quiet but show errors; `-D -` dumps headers to stdout before the body;
|
|
96
|
+
// `-w` appends the status code on its own trailing line for parsing.
|
|
97
|
+
const raw = execFileSync("curl", [
|
|
98
|
+
"-sS",
|
|
99
|
+
"-D",
|
|
100
|
+
"-",
|
|
101
|
+
"-o",
|
|
102
|
+
"-",
|
|
103
|
+
"--max-time",
|
|
104
|
+
String(timeoutSec),
|
|
105
|
+
"-H",
|
|
106
|
+
`Authorization: Bearer ${token}`,
|
|
107
|
+
"-H",
|
|
108
|
+
"Accept: application/vnd.github+json",
|
|
109
|
+
"-H",
|
|
110
|
+
"X-GitHub-Api-Version: 2022-11-28",
|
|
111
|
+
"-H",
|
|
112
|
+
"User-Agent: bourdon-github-copilot-participant",
|
|
113
|
+
"-w",
|
|
114
|
+
"\n__BOURDON_STATUS__%{http_code}",
|
|
115
|
+
url,
|
|
116
|
+
], { encoding: "utf8", timeout: (timeoutSec + 2) * 1000, maxBuffer: 8 * 1024 * 1024 });
|
|
117
|
+
const statusMatch = /\n__BOURDON_STATUS__(\d+)$/.exec(raw);
|
|
118
|
+
const status = statusMatch ? Number(statusMatch[1]) : 0;
|
|
119
|
+
const withoutStatus = statusMatch ? raw.slice(0, statusMatch.index) : raw;
|
|
120
|
+
// Split header block from body (last header section before body).
|
|
121
|
+
const headerBodySplit = withoutStatus.indexOf("\r\n\r\n");
|
|
122
|
+
let headerBlock = "";
|
|
123
|
+
let body = withoutStatus;
|
|
124
|
+
if (headerBodySplit >= 0) {
|
|
125
|
+
headerBlock = withoutStatus.slice(0, headerBodySplit);
|
|
126
|
+
body = withoutStatus.slice(headerBodySplit + 4);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const lfSplit = withoutStatus.indexOf("\n\n");
|
|
130
|
+
if (lfSplit >= 0) {
|
|
131
|
+
headerBlock = withoutStatus.slice(0, lfSplit);
|
|
132
|
+
body = withoutStatus.slice(lfSplit + 2);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const headers = {};
|
|
136
|
+
for (const line of headerBlock.split(/\r?\n/)) {
|
|
137
|
+
const idx = line.indexOf(":");
|
|
138
|
+
if (idx > 0) {
|
|
139
|
+
headers[line.slice(0, idx).trim().toLowerCase()] = line.slice(idx + 1).trim();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { status, headers, body };
|
|
143
|
+
}
|
|
144
|
+
// -- Participant --------------------------------------------------------------
|
|
145
|
+
/** Federates cloud-side GitHub Copilot activity via the GitHub REST API. */
|
|
146
|
+
export class GitHubCopilotParticipant extends NetworkParticipant {
|
|
147
|
+
participantSlug = PARTICIPANT_SLUG;
|
|
148
|
+
agentId = AGENT_ID;
|
|
149
|
+
agentType = AGENT_TYPE;
|
|
150
|
+
nativePath = GITHUB_API_ROOT;
|
|
151
|
+
displayName = DISPLAY_NAME;
|
|
152
|
+
maxResults;
|
|
153
|
+
constructor(opts = {}) {
|
|
154
|
+
super({
|
|
155
|
+
authProvider: opts.authProvider ?? ghTokenProvider,
|
|
156
|
+
cacheRoot: opts.cacheRoot,
|
|
157
|
+
});
|
|
158
|
+
this.maxResults = opts.maxResults ?? 50;
|
|
159
|
+
}
|
|
160
|
+
static defaultNativePath() {
|
|
161
|
+
return GITHUB_API_ROOT;
|
|
162
|
+
}
|
|
163
|
+
fetchPayload(token) {
|
|
164
|
+
// Identify the authenticated user (also validates the token early).
|
|
165
|
+
const me = githubGet("/user", token);
|
|
166
|
+
const login = me && typeof me === "object" ? me["login"] : null;
|
|
167
|
+
// PRs that involve the user where Copilot is a commenter/reviewer.
|
|
168
|
+
const q = "is:pr involves:@me commenter:copilot";
|
|
169
|
+
const encoded = encodeURIComponent(q);
|
|
170
|
+
const search = githubGet(`/search/issues?q=${encoded}&sort=updated&per_page=${this.maxResults}`, token);
|
|
171
|
+
const items = search && typeof search === "object"
|
|
172
|
+
? (search["items"] ?? [])
|
|
173
|
+
: [];
|
|
174
|
+
return {
|
|
175
|
+
fetched_user: login ?? null,
|
|
176
|
+
fetched_at: new Date().toISOString(),
|
|
177
|
+
items,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
payloadToL5(payload) {
|
|
181
|
+
const items = Array.isArray(payload["items"]) ? payload["items"] : [];
|
|
182
|
+
const sessions = [];
|
|
183
|
+
const entities = new Map();
|
|
184
|
+
for (const item of items) {
|
|
185
|
+
if (typeof item !== "object" || item === null)
|
|
186
|
+
continue;
|
|
187
|
+
const it = item;
|
|
188
|
+
const repo = repoFromItem(it);
|
|
189
|
+
const title = redactText(String(it["title"] ?? ""), 160);
|
|
190
|
+
const number = it["number"];
|
|
191
|
+
const updated = isoDate(it["updated_at"]);
|
|
192
|
+
const focus = repo ? [repo] : [];
|
|
193
|
+
sessions.push(makeSession({
|
|
194
|
+
date: updated || "1970-01-01",
|
|
195
|
+
cwd: repo ? `github.com/${repo}` : undefined,
|
|
196
|
+
project_focus: focus,
|
|
197
|
+
key_actions: number != null ? [`PR #${String(number)}: ${title}`] : [title],
|
|
198
|
+
visibility: Visibility.TEAM,
|
|
199
|
+
}));
|
|
200
|
+
if (repo && !entities.has(repo)) {
|
|
201
|
+
entities.set(repo, makeEntity({
|
|
202
|
+
name: repo,
|
|
203
|
+
type: "repository",
|
|
204
|
+
summary: "GitHub repo with Copilot PR activity.",
|
|
205
|
+
aliases: repo.includes("/") ? [repo.split("/").pop() ?? ""] : [],
|
|
206
|
+
last_touched: updated ?? undefined,
|
|
207
|
+
tags: ["github-copilot", "pull-request"],
|
|
208
|
+
visibility: Visibility.TEAM,
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Keep sessions newest-first, bounded.
|
|
213
|
+
sessions.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0));
|
|
214
|
+
return makeManifest({
|
|
215
|
+
spec_version: SPEC_VERSION,
|
|
216
|
+
agent: makeAgentInfo({
|
|
217
|
+
id: this.agentId,
|
|
218
|
+
type: this.agentType,
|
|
219
|
+
role_narrative: ROLE_NARRATIVE,
|
|
220
|
+
instance: payload["fetched_user"] ?? undefined,
|
|
221
|
+
}),
|
|
222
|
+
last_updated: new Date().toISOString(),
|
|
223
|
+
capabilities: ["github-search", "pull-request-comments"],
|
|
224
|
+
recent_sessions: sessions.slice(0, this.maxResults),
|
|
225
|
+
known_entities: [...entities.values()],
|
|
226
|
+
visibility_policy: DEFAULT_POLICY,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// -- Helpers ------------------------------------------------------------------
|
|
231
|
+
function repoFromItem(item) {
|
|
232
|
+
const repoUrl = item["repository_url"];
|
|
233
|
+
if (typeof repoUrl === "string" && repoUrl.includes("/repos/")) {
|
|
234
|
+
return repoUrl.split("/repos/")[1] ?? null;
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
function isoDate(value) {
|
|
239
|
+
if (typeof value === "string" && value.length >= 10)
|
|
240
|
+
return value.slice(0, 10);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermes participant — normalize Hermes Agent (Nous Research) memory + sessions
|
|
3
|
+
* into L5. Port of `participants/hermes.py`.
|
|
4
|
+
*
|
|
5
|
+
* Reads two surfaces under `~/.hermes/` (override `$HERMES_HOME`):
|
|
6
|
+
* * `state.db` — a SQLite store (`sessions` + `messages` tables). Recent
|
|
7
|
+
* non-archived sessions + project entities (from cwd basename) come from it.
|
|
8
|
+
* * `memories/{memory.md,user.md}` — durable cross-session facts (one
|
|
9
|
+
* Markdown bullet/line per memory). The highest-signal entities.
|
|
10
|
+
*
|
|
11
|
+
* Read-only (`mode=ro`), deterministic, visibility-enforced-before-emission,
|
|
12
|
+
* and redaction on every native string. `healthCheck` never throws; `discover`
|
|
13
|
+
* raises only {@link ParticipantDiscoveryError}.
|
|
14
|
+
*/
|
|
15
|
+
import { type L5ManifestModel, type SessionModel } from "@getbourdon/l5";
|
|
16
|
+
import { type AgentStore, type BourdonParticipant, type HealthStatus } from "../base.js";
|
|
17
|
+
/**
|
|
18
|
+
* Conventional Hermes home used by the setup wizard's detection.
|
|
19
|
+
* Precedence: explicit `home` > `$HERMES_HOME` > `~/.hermes`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function defaultNativePath(home?: string): string;
|
|
22
|
+
/** External participant for local Hermes Agent memory + sessions. */
|
|
23
|
+
export declare class HermesParticipant implements BourdonParticipant {
|
|
24
|
+
agentId: string;
|
|
25
|
+
agentType: string;
|
|
26
|
+
displayName: string;
|
|
27
|
+
nativePath: string;
|
|
28
|
+
private readonly home;
|
|
29
|
+
static defaultNativePath(home?: string): string;
|
|
30
|
+
/** `hermesHome` is the `.hermes` directory itself (matching the Python ctor). */
|
|
31
|
+
constructor(hermesHome?: string);
|
|
32
|
+
private sources;
|
|
33
|
+
discover(): AgentStore;
|
|
34
|
+
exportSessions(since?: Date, limit?: number): SessionModel[];
|
|
35
|
+
exportL5(since?: Date): L5ManifestModel;
|
|
36
|
+
healthCheck(): HealthStatus;
|
|
37
|
+
}
|