@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.
Files changed (142) hide show
  1. package/README.md +115 -0
  2. package/dist/cli.js +30 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/api-keys.d.ts +3 -0
  5. package/dist/commands/api-keys.d.ts.map +1 -0
  6. package/dist/commands/api-keys.js +90 -0
  7. package/dist/commands/api-keys.js.map +1 -0
  8. package/dist/commands/hook.d.ts +6 -0
  9. package/dist/commands/hook.d.ts.map +1 -0
  10. package/dist/commands/hook.js +106 -0
  11. package/dist/commands/hook.js.map +1 -0
  12. package/dist/commands/login.d.ts +17 -0
  13. package/dist/commands/login.d.ts.map +1 -1
  14. package/dist/commands/login.js +170 -65
  15. package/dist/commands/login.js.map +1 -1
  16. package/dist/commands/orgs.d.ts +3 -0
  17. package/dist/commands/orgs.d.ts.map +1 -0
  18. package/dist/commands/orgs.js +102 -0
  19. package/dist/commands/orgs.js.map +1 -0
  20. package/dist/commands/push.d.ts.map +1 -1
  21. package/dist/commands/push.js +29 -6
  22. package/dist/commands/push.js.map +1 -1
  23. package/dist/commands/sessions.d.ts +3 -0
  24. package/dist/commands/sessions.d.ts.map +1 -0
  25. package/dist/commands/sessions.js +139 -0
  26. package/dist/commands/sessions.js.map +1 -0
  27. package/dist/commands/setup.d.ts +3 -0
  28. package/dist/commands/setup.d.ts.map +1 -0
  29. package/dist/commands/setup.js +58 -0
  30. package/dist/commands/setup.js.map +1 -0
  31. package/dist/commands/skills.d.ts +12 -0
  32. package/dist/commands/skills.d.ts.map +1 -0
  33. package/dist/commands/skills.js +171 -0
  34. package/dist/commands/skills.js.map +1 -0
  35. package/dist/commands/sync.d.ts.map +1 -1
  36. package/dist/commands/sync.js +51 -49
  37. package/dist/commands/sync.js.map +1 -1
  38. package/dist/commands/users.d.ts +13 -0
  39. package/dist/commands/users.d.ts.map +1 -0
  40. package/dist/commands/users.js +205 -0
  41. package/dist/commands/users.js.map +1 -0
  42. package/dist/commands/whoami.d.ts +21 -0
  43. package/dist/commands/whoami.d.ts.map +1 -0
  44. package/dist/commands/whoami.js +77 -0
  45. package/dist/commands/whoami.js.map +1 -0
  46. package/dist/lib/api-client.d.ts +64 -2
  47. package/dist/lib/api-client.d.ts.map +1 -1
  48. package/dist/lib/api-client.js +32 -7
  49. package/dist/lib/api-client.js.map +1 -1
  50. package/dist/lib/auth.d.ts +43 -6
  51. package/dist/lib/auth.d.ts.map +1 -1
  52. package/dist/lib/auth.js +106 -25
  53. package/dist/lib/auth.js.map +1 -1
  54. package/dist/lib/config-files.d.ts +6 -0
  55. package/dist/lib/config-files.d.ts.map +1 -0
  56. package/dist/lib/config-files.js +27 -0
  57. package/dist/lib/config-files.js.map +1 -0
  58. package/dist/lib/config.d.ts +13 -7
  59. package/dist/lib/config.d.ts.map +1 -1
  60. package/dist/lib/config.js +22 -8
  61. package/dist/lib/config.js.map +1 -1
  62. package/dist/lib/copilot-export.d.ts +31 -0
  63. package/dist/lib/copilot-export.d.ts.map +1 -0
  64. package/dist/lib/copilot-export.js +129 -0
  65. package/dist/lib/copilot-export.js.map +1 -0
  66. package/dist/lib/discovery.d.ts +60 -2
  67. package/dist/lib/discovery.d.ts.map +1 -1
  68. package/dist/lib/discovery.js +187 -15
  69. package/dist/lib/discovery.js.map +1 -1
  70. package/dist/lib/hook-discovery.d.ts +43 -0
  71. package/dist/lib/hook-discovery.d.ts.map +1 -0
  72. package/dist/lib/hook-discovery.js +90 -0
  73. package/dist/lib/hook-discovery.js.map +1 -0
  74. package/dist/lib/hook-input.d.ts +36 -0
  75. package/dist/lib/hook-input.d.ts.map +1 -0
  76. package/dist/lib/hook-input.js +161 -0
  77. package/dist/lib/hook-input.js.map +1 -0
  78. package/dist/lib/hook-runner.d.ts +48 -0
  79. package/dist/lib/hook-runner.d.ts.map +1 -0
  80. package/dist/lib/hook-runner.js +306 -0
  81. package/dist/lib/hook-runner.js.map +1 -0
  82. package/dist/lib/http.d.ts +18 -0
  83. package/dist/lib/http.d.ts.map +1 -0
  84. package/dist/lib/http.js +54 -0
  85. package/dist/lib/http.js.map +1 -0
  86. package/dist/lib/opencode-export.d.ts +22 -0
  87. package/dist/lib/opencode-export.d.ts.map +1 -0
  88. package/dist/lib/opencode-export.js +53 -0
  89. package/dist/lib/opencode-export.js.map +1 -0
  90. package/dist/lib/output.d.ts +27 -0
  91. package/dist/lib/output.d.ts.map +1 -0
  92. package/dist/lib/output.js +74 -0
  93. package/dist/lib/output.js.map +1 -0
  94. package/dist/lib/preflight.d.ts +35 -0
  95. package/dist/lib/preflight.d.ts.map +1 -0
  96. package/dist/lib/preflight.js +54 -0
  97. package/dist/lib/preflight.js.map +1 -0
  98. package/dist/lib/repo-binding.d.ts +43 -0
  99. package/dist/lib/repo-binding.d.ts.map +1 -0
  100. package/dist/lib/repo-binding.js +158 -0
  101. package/dist/lib/repo-binding.js.map +1 -0
  102. package/dist/lib/repo-identity.d.ts +29 -0
  103. package/dist/lib/repo-identity.d.ts.map +1 -0
  104. package/dist/lib/repo-identity.js +113 -0
  105. package/dist/lib/repo-identity.js.map +1 -0
  106. package/dist/lib/setup-hooks.d.ts +41 -0
  107. package/dist/lib/setup-hooks.d.ts.map +1 -0
  108. package/dist/lib/setup-hooks.js +276 -0
  109. package/dist/lib/setup-hooks.js.map +1 -0
  110. package/dist/lib/setup.d.ts +47 -0
  111. package/dist/lib/setup.d.ts.map +1 -0
  112. package/dist/lib/setup.js +179 -0
  113. package/dist/lib/setup.js.map +1 -0
  114. package/dist/lib/skill-batches.d.ts +16 -0
  115. package/dist/lib/skill-batches.d.ts.map +1 -0
  116. package/dist/lib/skill-batches.js +35 -0
  117. package/dist/lib/skill-batches.js.map +1 -0
  118. package/dist/lib/skill-files.d.ts +4 -0
  119. package/dist/lib/skill-files.d.ts.map +1 -0
  120. package/dist/lib/skill-files.js +36 -0
  121. package/dist/lib/skill-files.js.map +1 -0
  122. package/dist/lib/skill-fingerprint.d.ts +55 -0
  123. package/dist/lib/skill-fingerprint.d.ts.map +1 -0
  124. package/dist/lib/skill-fingerprint.js +204 -0
  125. package/dist/lib/skill-fingerprint.js.map +1 -0
  126. package/dist/lib/skills-discovery.d.ts +57 -0
  127. package/dist/lib/skills-discovery.d.ts.map +1 -0
  128. package/dist/lib/skills-discovery.js +157 -0
  129. package/dist/lib/skills-discovery.js.map +1 -0
  130. package/dist/lib/skills-sync.d.ts +66 -0
  131. package/dist/lib/skills-sync.d.ts.map +1 -0
  132. package/dist/lib/skills-sync.js +251 -0
  133. package/dist/lib/skills-sync.js.map +1 -0
  134. package/dist/lib/sync-runner.d.ts +56 -0
  135. package/dist/lib/sync-runner.d.ts.map +1 -0
  136. package/dist/lib/sync-runner.js +106 -0
  137. package/dist/lib/sync-runner.js.map +1 -0
  138. package/dist/lib/sync-state.d.ts +36 -6
  139. package/dist/lib/sync-state.d.ts.map +1 -1
  140. package/dist/lib/sync-state.js +41 -11
  141. package/dist/lib/sync-state.js.map +1 -1
  142. package/package.json +16 -8
package/dist/lib/auth.js CHANGED
@@ -1,45 +1,108 @@
1
- import { readFileSync, writeFileSync } from 'node:fs';
2
- import { CREDENTIALS_PATH, ensureHalyardDir } from './config.js';
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, or fall back to HALYARD_TOKEN env var.
5
- * Returns null if no credentials are available.
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 getCredentials() {
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
- accessToken: envToken,
13
- refreshToken: '',
14
- expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000, // Never expires for API keys
15
- apiUrl: '', // Will use global --api-url option
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
- const raw = readFileSync(CREDENTIALS_PATH, 'utf-8');
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 creds = getCredentials();
32
- if (!creds) {
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: new URLSearchParams({
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
- writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { mode: 0o600 });
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
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
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"}
@@ -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
- /** Ensure the ~/.halyard directory exists */
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
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,sDAAsD;AACtD,eAAO,MAAM,WAAW,QAA8B,CAAA;AAEtD,uCAAuC;AACvC,eAAO,MAAM,gBAAgB,QAAwC,CAAA;AAErE,iDAAiD;AACjD,eAAO,MAAM,eAAe,QAAuC,CAAA;AAEnE,sBAAsB;AACtB,eAAO,MAAM,eAAe,8BAA8B,CAAA;AAE1D,6CAA6C;AAC7C,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
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"}
@@ -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
- /** Ensure the ~/.halyard directory exists */
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(HALYARD_DIR, { recursive: true, mode: 0o700 });
28
+ mkdirSync(halyardDir(), { recursive: true, mode: 0o700 });
15
29
  }
16
30
  //# sourceMappingURL=config.js.map
@@ -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,sDAAsD;AACtD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAA;AAEtD,uCAAuC;AACvC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAA;AAErE,iDAAiD;AACjD,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAA;AAEnE,sBAAsB;AACtB,MAAM,CAAC,MAAM,eAAe,GAAG,2BAA2B,CAAA;AAE1D,6CAA6C;AAC7C,MAAM,UAAU,gBAAgB;IAC9B,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;AAC1D,CAAC"}
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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"}
@@ -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: 'claude' | 'codex';
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>/sessions/
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":"AAIA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAA;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CAoB5D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,iBAAiB,EAAE,CAO3D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAYhF"}
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"}