@halyard/cli 0.1.1 → 0.2.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/README.md +115 -0
- package/dist/cli.js +30 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.d.ts.map +1 -0
- package/dist/commands/api-keys.js +90 -0
- package/dist/commands/api-keys.js.map +1 -0
- package/dist/commands/hook.d.ts +6 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +106 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/login.d.ts +17 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +170 -65
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/orgs.d.ts +3 -0
- package/dist/commands/orgs.d.ts.map +1 -0
- package/dist/commands/orgs.js +102 -0
- package/dist/commands/orgs.js.map +1 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +29 -6
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/sessions.d.ts +3 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +139 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +58 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +171 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +51 -49
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/users.d.ts +13 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/users.js +205 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/whoami.d.ts +21 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +77 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/lib/api-client.d.ts +64 -2
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +32 -7
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/auth.d.ts +43 -6
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +106 -25
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/config-files.d.ts +6 -0
- package/dist/lib/config-files.d.ts.map +1 -0
- package/dist/lib/config-files.js +27 -0
- package/dist/lib/config-files.js.map +1 -0
- package/dist/lib/config.d.ts +13 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +22 -8
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/copilot-export.d.ts +31 -0
- package/dist/lib/copilot-export.d.ts.map +1 -0
- package/dist/lib/copilot-export.js +129 -0
- package/dist/lib/copilot-export.js.map +1 -0
- package/dist/lib/discovery.d.ts +60 -2
- package/dist/lib/discovery.d.ts.map +1 -1
- package/dist/lib/discovery.js +187 -15
- package/dist/lib/discovery.js.map +1 -1
- package/dist/lib/hook-discovery.d.ts +43 -0
- package/dist/lib/hook-discovery.d.ts.map +1 -0
- package/dist/lib/hook-discovery.js +90 -0
- package/dist/lib/hook-discovery.js.map +1 -0
- package/dist/lib/hook-input.d.ts +36 -0
- package/dist/lib/hook-input.d.ts.map +1 -0
- package/dist/lib/hook-input.js +161 -0
- package/dist/lib/hook-input.js.map +1 -0
- package/dist/lib/hook-runner.d.ts +48 -0
- package/dist/lib/hook-runner.d.ts.map +1 -0
- package/dist/lib/hook-runner.js +306 -0
- package/dist/lib/hook-runner.js.map +1 -0
- package/dist/lib/http.d.ts +18 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +54 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/opencode-export.d.ts +22 -0
- package/dist/lib/opencode-export.d.ts.map +1 -0
- package/dist/lib/opencode-export.js +53 -0
- package/dist/lib/opencode-export.js.map +1 -0
- package/dist/lib/output.d.ts +27 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +74 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/preflight.d.ts +35 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +54 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/repo-binding.d.ts +43 -0
- package/dist/lib/repo-binding.d.ts.map +1 -0
- package/dist/lib/repo-binding.js +158 -0
- package/dist/lib/repo-binding.js.map +1 -0
- package/dist/lib/repo-identity.d.ts +29 -0
- package/dist/lib/repo-identity.d.ts.map +1 -0
- package/dist/lib/repo-identity.js +113 -0
- package/dist/lib/repo-identity.js.map +1 -0
- package/dist/lib/setup-hooks.d.ts +41 -0
- package/dist/lib/setup-hooks.d.ts.map +1 -0
- package/dist/lib/setup-hooks.js +276 -0
- package/dist/lib/setup-hooks.js.map +1 -0
- package/dist/lib/setup.d.ts +47 -0
- package/dist/lib/setup.d.ts.map +1 -0
- package/dist/lib/setup.js +179 -0
- package/dist/lib/setup.js.map +1 -0
- package/dist/lib/skill-batches.d.ts +16 -0
- package/dist/lib/skill-batches.d.ts.map +1 -0
- package/dist/lib/skill-batches.js +35 -0
- package/dist/lib/skill-batches.js.map +1 -0
- package/dist/lib/skill-files.d.ts +4 -0
- package/dist/lib/skill-files.d.ts.map +1 -0
- package/dist/lib/skill-files.js +36 -0
- package/dist/lib/skill-files.js.map +1 -0
- package/dist/lib/skill-fingerprint.d.ts +55 -0
- package/dist/lib/skill-fingerprint.d.ts.map +1 -0
- package/dist/lib/skill-fingerprint.js +204 -0
- package/dist/lib/skill-fingerprint.js.map +1 -0
- package/dist/lib/skills-discovery.d.ts +57 -0
- package/dist/lib/skills-discovery.d.ts.map +1 -0
- package/dist/lib/skills-discovery.js +157 -0
- package/dist/lib/skills-discovery.js.map +1 -0
- package/dist/lib/skills-sync.d.ts +66 -0
- package/dist/lib/skills-sync.d.ts.map +1 -0
- package/dist/lib/skills-sync.js +251 -0
- package/dist/lib/skills-sync.js.map +1 -0
- package/dist/lib/sync-runner.d.ts +56 -0
- package/dist/lib/sync-runner.d.ts.map +1 -0
- package/dist/lib/sync-runner.js +106 -0
- package/dist/lib/sync-runner.js.map +1 -0
- package/dist/lib/sync-state.d.ts +36 -6
- package/dist/lib/sync-state.d.ts.map +1 -1
- package/dist/lib/sync-state.js +41 -11
- package/dist/lib/sync-state.js.map +1 -1
- package/package.json +16 -8
package/dist/lib/auth.js
CHANGED
|
@@ -1,45 +1,108 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
1
|
+
import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { credentialsDir, credentialsPath, ensureHalyardDir } from './config.js';
|
|
4
|
+
import { findRepoBinding } from './repo-binding.js';
|
|
5
|
+
/** Path of the per-org credential file for an org. */
|
|
6
|
+
export function perOrgCredentialsPath(orgId) {
|
|
7
|
+
return join(credentialsDir(), `${orgId}.json`);
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
|
-
* Get stored credentials
|
|
5
|
-
*
|
|
10
|
+
* Get stored credentials plus the file they came from, so token refresh can
|
|
11
|
+
* write back to the same chain it read.
|
|
12
|
+
*
|
|
13
|
+
* Resolution order:
|
|
14
|
+
* 1. HALYARD_TOKEN env var (headless/CI override; no backing file)
|
|
15
|
+
* 2. Per-org file in ~/.halyard/credentials/ whose orgSlug matches the
|
|
16
|
+
* repo binding (when invoked inside a bound repo)
|
|
17
|
+
* 3. Legacy ~/.halyard/credentials.json (default chain)
|
|
6
18
|
*/
|
|
7
|
-
export function
|
|
19
|
+
export function getCredentialsWithSource(binding = findRepoBinding()) {
|
|
8
20
|
// Check env var first (headless/CI use case)
|
|
9
21
|
const envToken = process.env['HALYARD_TOKEN'];
|
|
10
22
|
if (envToken) {
|
|
11
23
|
return {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
24
|
+
creds: {
|
|
25
|
+
accessToken: envToken,
|
|
26
|
+
refreshToken: '',
|
|
27
|
+
clientId: '',
|
|
28
|
+
expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000, // Never expires for API keys
|
|
29
|
+
apiUrl: '', // Will use global --api-url option
|
|
30
|
+
},
|
|
31
|
+
filePath: null,
|
|
16
32
|
};
|
|
17
33
|
}
|
|
34
|
+
if (binding) {
|
|
35
|
+
const orgCreds = findOrgCredentials(binding.org);
|
|
36
|
+
if (orgCreds)
|
|
37
|
+
return orgCreds;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const filePath = credentialsPath();
|
|
41
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
42
|
+
return { creds: JSON.parse(raw), filePath };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get stored credentials, or fall back to HALYARD_TOKEN env var.
|
|
50
|
+
* Returns null if no credentials are available.
|
|
51
|
+
*/
|
|
52
|
+
export function getCredentials(binding = findRepoBinding()) {
|
|
53
|
+
return getCredentialsWithSource(binding)?.creds ?? null;
|
|
54
|
+
}
|
|
55
|
+
/** Scan the (small) per-org credentials dir for a chain matching an org slug. */
|
|
56
|
+
function findOrgCredentials(orgSlug) {
|
|
57
|
+
let entries;
|
|
18
58
|
try {
|
|
19
|
-
|
|
20
|
-
return JSON.parse(raw);
|
|
59
|
+
entries = readdirSync(credentialsDir());
|
|
21
60
|
}
|
|
22
61
|
catch {
|
|
23
62
|
return null;
|
|
24
63
|
}
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
if (!entry.endsWith('.json'))
|
|
66
|
+
continue;
|
|
67
|
+
const filePath = join(credentialsDir(), entry);
|
|
68
|
+
try {
|
|
69
|
+
const creds = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
70
|
+
if (creds.orgSlug === orgSlug)
|
|
71
|
+
return { creds, filePath };
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Skip unreadable/corrupt files
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
25
78
|
}
|
|
26
79
|
/**
|
|
27
80
|
* Get authorization headers for API requests.
|
|
28
|
-
* Auto-refreshes expired OAuth tokens
|
|
81
|
+
* Auto-refreshes expired OAuth tokens, writing the rotated tokens back to the
|
|
82
|
+
* same file the credentials were read from (per-org or legacy).
|
|
29
83
|
*/
|
|
30
|
-
export async function getAuthHeaders(apiUrl) {
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
84
|
+
export async function getAuthHeaders(apiUrl, binding = findRepoBinding()) {
|
|
85
|
+
const source = getCredentialsWithSource(binding);
|
|
86
|
+
if (!source) {
|
|
33
87
|
throw new Error('Not authenticated. Run `halyard login` or set the HALYARD_TOKEN environment variable.');
|
|
34
88
|
}
|
|
89
|
+
const { creds } = source;
|
|
35
90
|
// API keys don't expire
|
|
36
91
|
if (creds.accessToken.startsWith('sk_halyard_')) {
|
|
37
92
|
return { Authorization: `Bearer ${creds.accessToken}` };
|
|
38
93
|
}
|
|
39
94
|
// Refresh if token is expired or about to expire (30s buffer)
|
|
40
95
|
if (creds.expiresAt < Date.now() + 30_000 && creds.refreshToken) {
|
|
96
|
+
// Credentials saved before clientId was persisted can't be refreshed.
|
|
97
|
+
if (!creds.clientId) {
|
|
98
|
+
throw new Error('Stored credentials are missing the OAuth client ID. Run `halyard login` to re-authenticate.');
|
|
99
|
+
}
|
|
41
100
|
try {
|
|
42
|
-
const refreshed = await refreshToken(creds.refreshToken, apiUrl
|
|
101
|
+
const refreshed = await refreshToken(creds.refreshToken, creds.clientId, apiUrl, undefined, {
|
|
102
|
+
filePath: source.filePath ?? credentialsPath(),
|
|
103
|
+
orgId: creds.orgId,
|
|
104
|
+
orgSlug: creds.orgSlug,
|
|
105
|
+
});
|
|
43
106
|
return { Authorization: `Bearer ${refreshed.accessToken}` };
|
|
44
107
|
}
|
|
45
108
|
catch (err) {
|
|
@@ -50,15 +113,24 @@ export async function getAuthHeaders(apiUrl) {
|
|
|
50
113
|
}
|
|
51
114
|
/**
|
|
52
115
|
* Refresh an expired OAuth access token.
|
|
116
|
+
* Persists to `persist.filePath` when given (per-org chains keep refreshing
|
|
117
|
+
* their own file); defaults to the legacy credentials file.
|
|
53
118
|
*/
|
|
54
|
-
export async function refreshToken(refreshTokenValue, apiUrl) {
|
|
119
|
+
export async function refreshToken(refreshTokenValue, clientId, apiUrl, orgId, persist) {
|
|
120
|
+
const params = new URLSearchParams({
|
|
121
|
+
grant_type: 'refresh_token',
|
|
122
|
+
refresh_token: refreshTokenValue,
|
|
123
|
+
client_id: clientId,
|
|
124
|
+
});
|
|
125
|
+
// Workspace-scoped reissue: the server re-binds the token to the caller's
|
|
126
|
+
// linked member row in this org (fails closed before rotating the refresh
|
|
127
|
+
// token, so a rejected switch leaves stored credentials valid).
|
|
128
|
+
if (orgId)
|
|
129
|
+
params.set('org_id', orgId);
|
|
55
130
|
const res = await fetch(`${apiUrl}/oauth/token`, {
|
|
56
131
|
method: 'POST',
|
|
57
132
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
58
|
-
body:
|
|
59
|
-
grant_type: 'refresh_token',
|
|
60
|
-
refresh_token: refreshTokenValue,
|
|
61
|
-
}),
|
|
133
|
+
body: params,
|
|
62
134
|
});
|
|
63
135
|
if (!res.ok) {
|
|
64
136
|
const body = await res.text();
|
|
@@ -68,17 +140,26 @@ export async function refreshToken(refreshTokenValue, apiUrl) {
|
|
|
68
140
|
const creds = {
|
|
69
141
|
accessToken: data.access_token,
|
|
70
142
|
refreshToken: data.refresh_token,
|
|
143
|
+
clientId,
|
|
71
144
|
expiresAt: Date.now() + data.expires_in * 1000,
|
|
72
145
|
apiUrl,
|
|
146
|
+
...(persist?.orgId ? { orgId: persist.orgId } : {}),
|
|
147
|
+
...(persist?.orgSlug ? { orgSlug: persist.orgSlug } : {}),
|
|
73
148
|
};
|
|
74
|
-
saveCredentials(creds);
|
|
149
|
+
saveCredentials(creds, persist?.filePath);
|
|
75
150
|
return creds;
|
|
76
151
|
}
|
|
77
152
|
/**
|
|
78
|
-
* Save credentials to disk
|
|
153
|
+
* Save credentials to disk (legacy file by default, or an explicit per-org
|
|
154
|
+
* file). Directories are created 0700, files written 0600.
|
|
79
155
|
*/
|
|
80
|
-
export function saveCredentials(creds) {
|
|
156
|
+
export function saveCredentials(creds, filePath = credentialsPath()) {
|
|
81
157
|
ensureHalyardDir();
|
|
82
|
-
|
|
158
|
+
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
159
|
+
writeFileSync(filePath, JSON.stringify(creds, null, 2), { mode: 0o600 });
|
|
160
|
+
}
|
|
161
|
+
/** Save credentials to the per-org store (requires orgId to be set). */
|
|
162
|
+
export function savePerOrgCredentials(creds) {
|
|
163
|
+
saveCredentials(creds, perOrgCredentialsPath(creds.orgId));
|
|
83
164
|
}
|
|
84
165
|
//# sourceMappingURL=auth.js.map
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Small helpers shared by the scaffolders that read and rewrite client config files. */
|
|
2
|
+
export declare function asObject(value: unknown): Record<string, unknown>;
|
|
3
|
+
export declare function formatError(error: unknown): string;
|
|
4
|
+
export declare function readJsonFile(filePath: string): unknown | Error;
|
|
5
|
+
export declare function writeJsonFile(filePath: string, value: unknown): void;
|
|
6
|
+
//# sourceMappingURL=config-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-files.d.ts","sourceRoot":"","sources":["../../src/lib/config-files.ts"],"names":[],"mappings":"AAGA,yFAAyF;AAEzF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMhE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAQ9D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGpE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
/** Small helpers shared by the scaffolders that read and rewrite client config files. */
|
|
4
|
+
export function asObject(value) {
|
|
5
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
6
|
+
return { ...value };
|
|
7
|
+
}
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
export function formatError(error) {
|
|
11
|
+
return error instanceof Error ? error.message : String(error);
|
|
12
|
+
}
|
|
13
|
+
export function readJsonFile(filePath) {
|
|
14
|
+
if (!existsSync(filePath))
|
|
15
|
+
return {};
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
return new Error(`Could not parse JSON at ${filePath}: ${formatError(error)}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function writeJsonFile(filePath, value) {
|
|
24
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
25
|
+
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=config-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-files.js","sourceRoot":"","sources":["../../src/lib/config-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,yFAAyF;AAEzF,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAA;IACrB,CAAC;IAED,OAAO,EAAE,CAAA;AACX,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAA;IAEpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAY,CAAA;IAC9D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,IAAI,KAAK,CAAC,2BAA2B,QAAQ,KAAK,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAChF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAc;IAC5D,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACjD,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;AAChE,CAAC"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
/** Root directory for Halyard CLI config and state */
|
|
2
|
-
export declare const HALYARD_DIR: string;
|
|
3
|
-
/** Path to stored OAuth credentials */
|
|
4
|
-
export declare const CREDENTIALS_PATH: string;
|
|
5
|
-
/** Path to sync state (tracks uploaded files) */
|
|
6
|
-
export declare const SYNC_STATE_PATH: string;
|
|
7
1
|
/** Default API URL */
|
|
8
2
|
export declare const DEFAULT_API_URL = "https://api.usehalyard.ai";
|
|
9
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Root directory for Halyard CLI config and state.
|
|
5
|
+
* Defaults to ~/.halyard; overridable via HALYARD_HOME (tests, sandboxes).
|
|
6
|
+
* Computed per call so tests can point it at a tmpdir without re-importing.
|
|
7
|
+
*/
|
|
8
|
+
export declare function halyardDir(): string;
|
|
9
|
+
/** Path to the legacy (default-org) OAuth credentials file */
|
|
10
|
+
export declare function credentialsPath(): string;
|
|
11
|
+
/** Directory holding per-org OAuth credential files (<orgId>.json) */
|
|
12
|
+
export declare function credentialsDir(): string;
|
|
13
|
+
/** Path to sync state (tracks uploaded files) */
|
|
14
|
+
export declare function syncStatePath(): string;
|
|
15
|
+
/** Ensure the Halyard config directory exists */
|
|
10
16
|
export declare function ensureHalyardDir(): void;
|
|
11
17
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,sBAAsB;AACtB,eAAO,MAAM,eAAe,8BAA8B,CAAA;AAE1D;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,sEAAsE;AACtE,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,iDAAiD;AACjD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
package/dist/lib/config.js
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { mkdirSync } from 'node:fs';
|
|
4
|
-
/** Root directory for Halyard CLI config and state */
|
|
5
|
-
export const HALYARD_DIR = join(homedir(), '.halyard');
|
|
6
|
-
/** Path to stored OAuth credentials */
|
|
7
|
-
export const CREDENTIALS_PATH = join(HALYARD_DIR, 'credentials.json');
|
|
8
|
-
/** Path to sync state (tracks uploaded files) */
|
|
9
|
-
export const SYNC_STATE_PATH = join(HALYARD_DIR, 'sync-state.json');
|
|
10
4
|
/** Default API URL */
|
|
11
5
|
export const DEFAULT_API_URL = 'https://api.usehalyard.ai';
|
|
12
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Root directory for Halyard CLI config and state.
|
|
8
|
+
* Defaults to ~/.halyard; overridable via HALYARD_HOME (tests, sandboxes).
|
|
9
|
+
* Computed per call so tests can point it at a tmpdir without re-importing.
|
|
10
|
+
*/
|
|
11
|
+
export function halyardDir() {
|
|
12
|
+
return process.env['HALYARD_HOME'] ?? join(homedir(), '.halyard');
|
|
13
|
+
}
|
|
14
|
+
/** Path to the legacy (default-org) OAuth credentials file */
|
|
15
|
+
export function credentialsPath() {
|
|
16
|
+
return join(halyardDir(), 'credentials.json');
|
|
17
|
+
}
|
|
18
|
+
/** Directory holding per-org OAuth credential files (<orgId>.json) */
|
|
19
|
+
export function credentialsDir() {
|
|
20
|
+
return join(halyardDir(), 'credentials');
|
|
21
|
+
}
|
|
22
|
+
/** Path to sync state (tracks uploaded files) */
|
|
23
|
+
export function syncStatePath() {
|
|
24
|
+
return join(halyardDir(), 'sync-state.json');
|
|
25
|
+
}
|
|
26
|
+
/** Ensure the Halyard config directory exists */
|
|
13
27
|
export function ensureHalyardDir() {
|
|
14
|
-
mkdirSync(
|
|
28
|
+
mkdirSync(halyardDir(), { recursive: true, mode: 0o700 });
|
|
15
29
|
}
|
|
16
30
|
//# sourceMappingURL=config.js.map
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEnC,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEnC,sBAAsB;AACtB,MAAM,CAAC,MAAM,eAAe,GAAG,2BAA2B,CAAA;AAE1D;;;;GAIG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAA;AACnE,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,kBAAkB,CAAC,CAAA;AAC/C,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,CAAA;AAC1C,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,iBAAiB,CAAC,CAAA;AAC9C,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,gBAAgB;IAC9B,SAAS,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;AAC3D,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot CLI integration.
|
|
3
|
+
*
|
|
4
|
+
* Copilot CLI (verified against v1.0.82) keeps all sessions in one shared
|
|
5
|
+
* SQLite store, `~/.copilot/session-store.db`:
|
|
6
|
+
*
|
|
7
|
+
* - `sessions` (id, cwd, repository, branch, created_at, updated_at)
|
|
8
|
+
* - `turns` (session_id, turn_index, user_message, assistant_response, timestamp)
|
|
9
|
+
* - `assistant_usage_events` (session_id, model, input_tokens, output_tokens,
|
|
10
|
+
* cache_read_tokens, cache_write_tokens, reasoning_tokens, ...)
|
|
11
|
+
* - `forge_trajectory_events` (session_id, event_type, command, exit_code, ...)
|
|
12
|
+
*
|
|
13
|
+
* Since there is no per-session file to upload, each session is exported as
|
|
14
|
+
* a versioned JSONL bundle the server-side parser understands
|
|
15
|
+
* (`packages/queue/src/lib/session-parsers/copilot.ts`): one
|
|
16
|
+
* `halyard_copilot_session` meta line, then `turn` / `usage` / `trajectory`
|
|
17
|
+
* records.
|
|
18
|
+
*
|
|
19
|
+
* The store is read via the built-in `node:sqlite` module (Node ≥ 22.13,
|
|
20
|
+
* read-only — no native dependency). On older Nodes, or when anything about
|
|
21
|
+
* the store is unreadable, discovery degrades to "no sessions" rather than
|
|
22
|
+
* breaking `halyard sync` for other providers.
|
|
23
|
+
*/
|
|
24
|
+
export declare const COPILOT_BUNDLE_VERSION = 1;
|
|
25
|
+
export interface CopilotSessionRef {
|
|
26
|
+
id: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function listCopilotSessions(): CopilotSessionRef[];
|
|
30
|
+
export declare function buildCopilotBundle(sessionId: string): Buffer;
|
|
31
|
+
//# sourceMappingURL=copilot-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot-export.d.ts","sourceRoot":"","sources":["../../src/lib/copilot-export.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;CAClB;AA2BD,wBAAgB,mBAAmB,IAAI,iBAAiB,EAAE,CAqBzD;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAmD5D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
/**
|
|
5
|
+
* GitHub Copilot CLI integration.
|
|
6
|
+
*
|
|
7
|
+
* Copilot CLI (verified against v1.0.82) keeps all sessions in one shared
|
|
8
|
+
* SQLite store, `~/.copilot/session-store.db`:
|
|
9
|
+
*
|
|
10
|
+
* - `sessions` (id, cwd, repository, branch, created_at, updated_at)
|
|
11
|
+
* - `turns` (session_id, turn_index, user_message, assistant_response, timestamp)
|
|
12
|
+
* - `assistant_usage_events` (session_id, model, input_tokens, output_tokens,
|
|
13
|
+
* cache_read_tokens, cache_write_tokens, reasoning_tokens, ...)
|
|
14
|
+
* - `forge_trajectory_events` (session_id, event_type, command, exit_code, ...)
|
|
15
|
+
*
|
|
16
|
+
* Since there is no per-session file to upload, each session is exported as
|
|
17
|
+
* a versioned JSONL bundle the server-side parser understands
|
|
18
|
+
* (`packages/queue/src/lib/session-parsers/copilot.ts`): one
|
|
19
|
+
* `halyard_copilot_session` meta line, then `turn` / `usage` / `trajectory`
|
|
20
|
+
* records.
|
|
21
|
+
*
|
|
22
|
+
* The store is read via the built-in `node:sqlite` module (Node ≥ 22.13,
|
|
23
|
+
* read-only — no native dependency). On older Nodes, or when anything about
|
|
24
|
+
* the store is unreadable, discovery degrades to "no sessions" rather than
|
|
25
|
+
* breaking `halyard sync` for other providers.
|
|
26
|
+
*/
|
|
27
|
+
export const COPILOT_BUNDLE_VERSION = 1;
|
|
28
|
+
function copilotDir() {
|
|
29
|
+
return process.env.COPILOT_DIR ?? join(homedir(), '.copilot');
|
|
30
|
+
}
|
|
31
|
+
function openStore() {
|
|
32
|
+
const dbPath = join(copilotDir(), 'session-store.db');
|
|
33
|
+
if (!existsSync(dbPath))
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
// node:sqlite is unflagged from Node 22.13; getBuiltinModule avoids a
|
|
37
|
+
// hard import so older runtimes degrade gracefully.
|
|
38
|
+
const sqlite = process.getBuiltinModule?.('node:sqlite');
|
|
39
|
+
if (!sqlite?.DatabaseSync)
|
|
40
|
+
return null;
|
|
41
|
+
return new sqlite.DatabaseSync(dbPath, { readOnly: true });
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function listCopilotSessions() {
|
|
48
|
+
const db = openStore();
|
|
49
|
+
if (!db)
|
|
50
|
+
return [];
|
|
51
|
+
try {
|
|
52
|
+
// Only sessions with actual conversation or usage activity — Copilot
|
|
53
|
+
// creates empty session rows for launches that never reach a turn.
|
|
54
|
+
const rows = db
|
|
55
|
+
.prepare(`SELECT s.id AS id, s.updated_at AS updated_at FROM sessions s
|
|
56
|
+
WHERE EXISTS (SELECT 1 FROM turns t WHERE t.session_id = s.id)
|
|
57
|
+
OR EXISTS (SELECT 1 FROM assistant_usage_events u WHERE u.session_id = s.id)`)
|
|
58
|
+
.all();
|
|
59
|
+
return rows
|
|
60
|
+
.filter((r) => typeof r.id === 'string')
|
|
61
|
+
.map((r) => ({ id: r.id, updatedAt: String(r.updated_at ?? '') }));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
db.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function buildCopilotBundle(sessionId) {
|
|
71
|
+
const db = openStore();
|
|
72
|
+
if (!db) {
|
|
73
|
+
throw new Error('Copilot session store is not readable (requires Node >= 22.13)');
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').all(sessionId)[0];
|
|
77
|
+
if (!session)
|
|
78
|
+
throw new Error(`Copilot session not found: ${sessionId}`);
|
|
79
|
+
const lines = [
|
|
80
|
+
JSON.stringify({
|
|
81
|
+
type: 'halyard_copilot_session',
|
|
82
|
+
bundleVersion: COPILOT_BUNDLE_VERSION,
|
|
83
|
+
session,
|
|
84
|
+
workspace: readWorkspaceYaml(sessionId),
|
|
85
|
+
}),
|
|
86
|
+
];
|
|
87
|
+
const push = (type, rows) => {
|
|
88
|
+
for (const row of rows)
|
|
89
|
+
lines.push(JSON.stringify({ type, ...row }));
|
|
90
|
+
};
|
|
91
|
+
push('turn', db
|
|
92
|
+
.prepare('SELECT turn_index, user_message, assistant_response, timestamp FROM turns WHERE session_id = ? ORDER BY turn_index')
|
|
93
|
+
.all(sessionId));
|
|
94
|
+
push('usage', db
|
|
95
|
+
.prepare(`SELECT turn_index, model, input_tokens, output_tokens, cache_read_tokens,
|
|
96
|
+
cache_write_tokens, reasoning_tokens, duration_ms, created_at
|
|
97
|
+
FROM assistant_usage_events WHERE session_id = ? ORDER BY id`)
|
|
98
|
+
.all(sessionId));
|
|
99
|
+
push('trajectory', db
|
|
100
|
+
.prepare(`SELECT tool_call_id, turn_index, event_type, command, exit_code, created_at
|
|
101
|
+
FROM forge_trajectory_events WHERE session_id = ? ORDER BY id`)
|
|
102
|
+
.all(sessionId));
|
|
103
|
+
return Buffer.from(`${lines.join('\n')}\n`);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
db.close();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Best-effort read of the per-session workspace.yaml (flat `key: value`
|
|
111
|
+
* lines only — no YAML dependency needed for this shape).
|
|
112
|
+
*/
|
|
113
|
+
function readWorkspaceYaml(sessionId) {
|
|
114
|
+
const path = join(copilotDir(), 'session-state', sessionId, 'workspace.yaml');
|
|
115
|
+
try {
|
|
116
|
+
const parsed = {};
|
|
117
|
+
for (const line of readFileSync(path, 'utf-8').split('\n')) {
|
|
118
|
+
const idx = line.indexOf(': ');
|
|
119
|
+
if (idx <= 0)
|
|
120
|
+
continue;
|
|
121
|
+
parsed[line.slice(0, idx).trim()] = line.slice(idx + 2).trim();
|
|
122
|
+
}
|
|
123
|
+
return parsed;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=copilot-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot-export.js","sourceRoot":"","sources":["../../src/lib/copilot-export.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/discovery.d.ts
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
|
+
export type SessionProvider = 'claude' | 'codex' | 'gemini' | 'opencode' | 'copilot';
|
|
2
|
+
export declare const SESSION_PROVIDERS: SessionProvider[];
|
|
1
3
|
export interface DiscoveredSession {
|
|
2
|
-
provider:
|
|
4
|
+
provider: SessionProvider;
|
|
5
|
+
/**
|
|
6
|
+
* Stable sync-state key. A real file path for file-backed providers
|
|
7
|
+
* (claude/codex/gemini); a `provider://sessionId` pseudo-path for
|
|
8
|
+
* store-backed providers (opencode/copilot) whose sessions live in a
|
|
9
|
+
* shared database rather than one file.
|
|
10
|
+
*/
|
|
3
11
|
filePath: string;
|
|
4
12
|
sessionId: string;
|
|
5
13
|
sizeBytes: number;
|
|
14
|
+
/**
|
|
15
|
+
* Working directory the session ran in, extracted from the log head.
|
|
16
|
+
* Only populated for providers whose log format is known to carry it
|
|
17
|
+
* (claude, codex); absent sessions count as unresolved in scoped sync.
|
|
18
|
+
*/
|
|
19
|
+
cwd?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Change marker for store-backed sessions — hashed in place of file
|
|
22
|
+
* contents so sync can skip unchanged sessions without exporting them.
|
|
23
|
+
* Absent for file-backed sessions (their bytes are hashed directly).
|
|
24
|
+
*/
|
|
25
|
+
freshness?: string;
|
|
6
26
|
}
|
|
7
27
|
/**
|
|
8
28
|
* Discover Claude Code session log files.
|
|
9
|
-
* Claude Code stores sessions in ~/.claude/projects/<project-hash>/
|
|
29
|
+
* Claude Code stores sessions in ~/.claude/projects/<project-hash>/
|
|
10
30
|
* Session files are JSONL format.
|
|
11
31
|
*/
|
|
12
32
|
export declare function discoverClaudeSessions(): DiscoveredSession[];
|
|
@@ -15,8 +35,46 @@ export declare function discoverClaudeSessions(): DiscoveredSession[];
|
|
|
15
35
|
* Codex stores sessions in ~/.codex/sessions/<yyyy>/<mm>/<dd>/rollout-*.jsonl
|
|
16
36
|
*/
|
|
17
37
|
export declare function discoverCodexSessions(): DiscoveredSession[];
|
|
38
|
+
/**
|
|
39
|
+
* Discover Gemini CLI chat session files.
|
|
40
|
+
* Gemini CLI stores sessions in ~/.gemini/tmp/<project>/chats/session-*.jsonl
|
|
41
|
+
* (legacy sessions are single-JSON `.json` files; subagent sessions live in
|
|
42
|
+
* chats/<parent-session-id>/ subdirectories).
|
|
43
|
+
*/
|
|
44
|
+
export declare function discoverGeminiSessions(): DiscoveredSession[];
|
|
45
|
+
/**
|
|
46
|
+
* Discover OpenCode sessions. OpenCode keeps everything in a SQLite
|
|
47
|
+
* database; sessions are listed via the `opencode` CLI itself and exported
|
|
48
|
+
* one at a time at upload time (see loadSessionContent).
|
|
49
|
+
*/
|
|
50
|
+
export declare function discoverOpencodeSessions(): DiscoveredSession[];
|
|
51
|
+
/**
|
|
52
|
+
* Discover GitHub Copilot CLI sessions. Copilot CLI keeps all sessions in
|
|
53
|
+
* one shared SQLite store (~/.copilot/session-store.db); each session is
|
|
54
|
+
* exported as a JSONL bundle at upload time (see loadSessionContent).
|
|
55
|
+
*/
|
|
56
|
+
export declare function discoverCopilotSessions(): DiscoveredSession[];
|
|
18
57
|
/**
|
|
19
58
|
* Auto-detect which providers have session files available.
|
|
20
59
|
*/
|
|
21
60
|
export declare function discoverAllSessions(providerFilter?: string): DiscoveredSession[];
|
|
61
|
+
/**
|
|
62
|
+
* Materialize the bytes to upload for a discovered session: the file itself
|
|
63
|
+
* for file-backed providers, or a synthesized export for store-backed ones.
|
|
64
|
+
*/
|
|
65
|
+
export declare function loadSessionContent(session: DiscoveredSession): Promise<Buffer>;
|
|
66
|
+
/** The name the ingest endpoint stores for the uploaded blob. */
|
|
67
|
+
export declare function uploadFileName(session: DiscoveredSession): string;
|
|
68
|
+
/**
|
|
69
|
+
* Claude session logs: the first records are often non-cwd entries
|
|
70
|
+
* (type:"queue-operation", "last-prompt", ...); a record carrying a `cwd`
|
|
71
|
+
* string reliably appears within the first few lines. Scan up to
|
|
72
|
+
* MAX_HEAD_LINES JSON lines and take the first `cwd` found.
|
|
73
|
+
*/
|
|
74
|
+
export declare function extractClaudeCwd(head: string): string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Codex session logs: the first line is a session_meta record with the cwd at
|
|
77
|
+
* `payload.cwd` (older formats used a top-level `cwd`).
|
|
78
|
+
*/
|
|
79
|
+
export declare function extractCodexCwd(firstLine: string): string | undefined;
|
|
22
80
|
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/lib/discovery.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/lib/discovery.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;AAEpF,eAAO,MAAM,iBAAiB,EAAE,eAAe,EAM9C,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,eAAe,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAOD;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CAoB5D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,iBAAiB,EAAE,CAO3D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CAmB5D;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,EAAE,CAQ9D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,iBAAiB,EAAE,CAQ7D;AAUD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAOhF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQpF;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAIjE;AAsDD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWrE"}
|