@a5c-ai/tasks-adapter 5.1.1-staging.52898ebfc24f

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 (202) hide show
  1. package/README.md +125 -0
  2. package/dist/auth/forge-interface.d.ts +67 -0
  3. package/dist/auth/forge-interface.d.ts.map +1 -0
  4. package/dist/auth/forge-interface.js +69 -0
  5. package/dist/auth/github-app.d.ts +64 -0
  6. package/dist/auth/github-app.d.ts.map +1 -0
  7. package/dist/auth/github-app.js +141 -0
  8. package/dist/auth/github-oauth.d.ts +27 -0
  9. package/dist/auth/github-oauth.d.ts.map +1 -0
  10. package/dist/auth/github-oauth.js +89 -0
  11. package/dist/auth/index.d.ts +8 -0
  12. package/dist/auth/index.d.ts.map +1 -0
  13. package/dist/auth/index.js +14 -0
  14. package/dist/auth/jwt.d.ts +24 -0
  15. package/dist/auth/jwt.d.ts.map +1 -0
  16. package/dist/auth/jwt.js +43 -0
  17. package/dist/auth/middleware.d.ts +22 -0
  18. package/dist/auth/middleware.d.ts.map +1 -0
  19. package/dist/auth/middleware.js +36 -0
  20. package/dist/auth/ssh-keys.d.ts +21 -0
  21. package/dist/auth/ssh-keys.d.ts.map +1 -0
  22. package/dist/auth/ssh-keys.js +59 -0
  23. package/dist/auth/types.d.ts +165 -0
  24. package/dist/auth/types.d.ts.map +1 -0
  25. package/dist/auth/types.js +53 -0
  26. package/dist/backend.d.ts +248 -0
  27. package/dist/backend.d.ts.map +1 -0
  28. package/dist/backend.js +40 -0
  29. package/dist/backends/adapters.d.ts +99 -0
  30. package/dist/backends/adapters.d.ts.map +1 -0
  31. package/dist/backends/adapters.js +308 -0
  32. package/dist/backends/external-tracker.d.ts +133 -0
  33. package/dist/backends/external-tracker.d.ts.map +1 -0
  34. package/dist/backends/external-tracker.js +731 -0
  35. package/dist/backends/git-native.d.ts +69 -0
  36. package/dist/backends/git-native.d.ts.map +1 -0
  37. package/dist/backends/git-native.js +797 -0
  38. package/dist/backends/github-issues.d.ts +78 -0
  39. package/dist/backends/github-issues.d.ts.map +1 -0
  40. package/dist/backends/github-issues.js +806 -0
  41. package/dist/backends/index.d.ts +52 -0
  42. package/dist/backends/index.d.ts.map +1 -0
  43. package/dist/backends/index.js +151 -0
  44. package/dist/backends/server.d.ts +42 -0
  45. package/dist/backends/server.d.ts.map +1 -0
  46. package/dist/backends/server.js +305 -0
  47. package/dist/cli/auth-store.d.ts +49 -0
  48. package/dist/cli/auth-store.d.ts.map +1 -0
  49. package/dist/cli/auth-store.js +150 -0
  50. package/dist/cli/client-config.d.ts +10 -0
  51. package/dist/cli/client-config.d.ts.map +1 -0
  52. package/dist/cli/client-config.js +87 -0
  53. package/dist/cli/commands/ask.d.ts +3 -0
  54. package/dist/cli/commands/ask.d.ts.map +1 -0
  55. package/dist/cli/commands/ask.js +171 -0
  56. package/dist/cli/commands/auth.d.ts +3 -0
  57. package/dist/cli/commands/auth.d.ts.map +1 -0
  58. package/dist/cli/commands/auth.js +510 -0
  59. package/dist/cli/commands/breakpoints.d.ts +3 -0
  60. package/dist/cli/commands/breakpoints.d.ts.map +1 -0
  61. package/dist/cli/commands/breakpoints.js +311 -0
  62. package/dist/cli/commands/responder-loop.d.ts +3 -0
  63. package/dist/cli/commands/responder-loop.d.ts.map +1 -0
  64. package/dist/cli/commands/responder-loop.js +78 -0
  65. package/dist/cli/commands/responders.d.ts +3 -0
  66. package/dist/cli/commands/responders.d.ts.map +1 -0
  67. package/dist/cli/commands/responders.js +157 -0
  68. package/dist/cli/commands/rules.d.ts +3 -0
  69. package/dist/cli/commands/rules.d.ts.map +1 -0
  70. package/dist/cli/commands/rules.js +105 -0
  71. package/dist/cli/commands/server.d.ts +3 -0
  72. package/dist/cli/commands/server.d.ts.map +1 -0
  73. package/dist/cli/commands/server.js +34 -0
  74. package/dist/cli/commands/tasks.d.ts +3 -0
  75. package/dist/cli/commands/tasks.d.ts.map +1 -0
  76. package/dist/cli/commands/tasks.js +281 -0
  77. package/dist/cli/commands/templates.d.ts +3 -0
  78. package/dist/cli/commands/templates.d.ts.map +1 -0
  79. package/dist/cli/commands/templates.js +100 -0
  80. package/dist/cli/index.d.ts +4 -0
  81. package/dist/cli/index.d.ts.map +1 -0
  82. package/dist/cli/index.js +9 -0
  83. package/dist/cli/output.d.ts +26 -0
  84. package/dist/cli/output.d.ts.map +1 -0
  85. package/dist/cli/output.js +143 -0
  86. package/dist/cli/program.d.ts +6 -0
  87. package/dist/cli/program.d.ts.map +1 -0
  88. package/dist/cli/program.js +38 -0
  89. package/dist/cli/tasks-adapter.d.ts +3 -0
  90. package/dist/cli/tasks-adapter.d.ts.map +1 -0
  91. package/dist/cli/tasks-adapter.js +4 -0
  92. package/dist/client/answer-poller.d.ts +52 -0
  93. package/dist/client/answer-poller.d.ts.map +1 -0
  94. package/dist/client/answer-poller.js +200 -0
  95. package/dist/client/auth-client.d.ts +200 -0
  96. package/dist/client/auth-client.d.ts.map +1 -0
  97. package/dist/client/auth-client.js +309 -0
  98. package/dist/client/breakpoint-router.d.ts +45 -0
  99. package/dist/client/breakpoint-router.d.ts.map +1 -0
  100. package/dist/client/breakpoint-router.js +45 -0
  101. package/dist/client/index.d.ts +17 -0
  102. package/dist/client/index.d.ts.map +1 -0
  103. package/dist/client/index.js +16 -0
  104. package/dist/client/profile-validator.d.ts +34 -0
  105. package/dist/client/profile-validator.d.ts.map +1 -0
  106. package/dist/client/profile-validator.js +89 -0
  107. package/dist/client/responder-client.d.ts +39 -0
  108. package/dist/client/responder-client.d.ts.map +1 -0
  109. package/dist/client/responder-client.js +72 -0
  110. package/dist/client/responder-matcher.d.ts +49 -0
  111. package/dist/client/responder-matcher.d.ts.map +1 -0
  112. package/dist/client/responder-matcher.js +226 -0
  113. package/dist/client/server-client.d.ts +124 -0
  114. package/dist/client/server-client.d.ts.map +1 -0
  115. package/dist/client/server-client.js +266 -0
  116. package/dist/client/timeout-manager.d.ts +47 -0
  117. package/dist/client/timeout-manager.d.ts.map +1 -0
  118. package/dist/client/timeout-manager.js +77 -0
  119. package/dist/config.d.ts +20 -0
  120. package/dist/config.d.ts.map +1 -0
  121. package/dist/config.js +93 -0
  122. package/dist/harness/index.d.ts +4 -0
  123. package/dist/harness/index.d.ts.map +1 -0
  124. package/dist/harness/index.js +2 -0
  125. package/dist/harness/interaction-provider.d.ts +71 -0
  126. package/dist/harness/interaction-provider.d.ts.map +1 -0
  127. package/dist/harness/interaction-provider.js +124 -0
  128. package/dist/harness/routing-rules.d.ts +7 -0
  129. package/dist/harness/routing-rules.d.ts.map +1 -0
  130. package/dist/harness/routing-rules.js +37 -0
  131. package/dist/index.d.ts +29 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +33 -0
  134. package/dist/mcp/backend-resolver.d.ts +43 -0
  135. package/dist/mcp/backend-resolver.d.ts.map +1 -0
  136. package/dist/mcp/backend-resolver.js +111 -0
  137. package/dist/mcp/http-transport.d.ts +37 -0
  138. package/dist/mcp/http-transport.d.ts.map +1 -0
  139. package/dist/mcp/http-transport.js +103 -0
  140. package/dist/mcp/index.d.ts +16 -0
  141. package/dist/mcp/index.d.ts.map +1 -0
  142. package/dist/mcp/index.js +12 -0
  143. package/dist/mcp/server.d.ts +20 -0
  144. package/dist/mcp/server.d.ts.map +1 -0
  145. package/dist/mcp/server.js +259 -0
  146. package/dist/mcp/tools/answer-breakpoint.d.ts +32 -0
  147. package/dist/mcp/tools/answer-breakpoint.d.ts.map +1 -0
  148. package/dist/mcp/tools/answer-breakpoint.js +45 -0
  149. package/dist/mcp/tools/ask-breakpoint.d.ts +58 -0
  150. package/dist/mcp/tools/ask-breakpoint.d.ts.map +1 -0
  151. package/dist/mcp/tools/ask-breakpoint.js +78 -0
  152. package/dist/mcp/tools/check-status.d.ts +16 -0
  153. package/dist/mcp/tools/check-status.d.ts.map +1 -0
  154. package/dist/mcp/tools/check-status.js +18 -0
  155. package/dist/mcp/tools/claim-breakpoint.d.ts +18 -0
  156. package/dist/mcp/tools/claim-breakpoint.d.ts.map +1 -0
  157. package/dist/mcp/tools/claim-breakpoint.js +28 -0
  158. package/dist/mcp/tools/list-breakpoints.d.ts +16 -0
  159. package/dist/mcp/tools/list-breakpoints.d.ts.map +1 -0
  160. package/dist/mcp/tools/list-breakpoints.js +14 -0
  161. package/dist/mcp/tools/list-responders.d.ts +18 -0
  162. package/dist/mcp/tools/list-responders.d.ts.map +1 -0
  163. package/dist/mcp/tools/list-responders.js +37 -0
  164. package/dist/mcp/tools/native-tasks.d.ts +270 -0
  165. package/dist/mcp/tools/native-tasks.d.ts.map +1 -0
  166. package/dist/mcp/tools/native-tasks.js +481 -0
  167. package/dist/mcp/tools/poll-breakpoints.d.ts +18 -0
  168. package/dist/mcp/tools/poll-breakpoints.d.ts.map +1 -0
  169. package/dist/mcp/tools/poll-breakpoints.js +36 -0
  170. package/dist/mcp/tools/verify-answer.d.ts +16 -0
  171. package/dist/mcp/tools/verify-answer.d.ts.map +1 -0
  172. package/dist/mcp/tools/verify-answer.js +38 -0
  173. package/dist/proven/index.d.ts +5 -0
  174. package/dist/proven/index.d.ts.map +1 -0
  175. package/dist/proven/index.js +3 -0
  176. package/dist/proven/keys.d.ts +33 -0
  177. package/dist/proven/keys.d.ts.map +1 -0
  178. package/dist/proven/keys.js +117 -0
  179. package/dist/proven/sign.d.ts +16 -0
  180. package/dist/proven/sign.d.ts.map +1 -0
  181. package/dist/proven/sign.js +60 -0
  182. package/dist/proven/types.d.ts +26 -0
  183. package/dist/proven/types.d.ts.map +1 -0
  184. package/dist/proven/types.js +5 -0
  185. package/dist/proven/verify.d.ts +6 -0
  186. package/dist/proven/verify.d.ts.map +1 -0
  187. package/dist/proven/verify.js +58 -0
  188. package/dist/responders/types.d.ts +38 -0
  189. package/dist/responders/types.d.ts.map +1 -0
  190. package/dist/responders/types.js +1 -0
  191. package/dist/router.d.ts +51 -0
  192. package/dist/router.d.ts.map +1 -0
  193. package/dist/router.js +200 -0
  194. package/dist/types.d.ts +7711 -0
  195. package/dist/types.d.ts.map +1 -0
  196. package/dist/types.js +479 -0
  197. package/package.json +96 -0
  198. package/responder/README.md +42 -0
  199. package/responder/backend-responder.json +9 -0
  200. package/responder/devops-responder.json +9 -0
  201. package/responder/frontend-responder.json +9 -0
  202. package/responder/schema.json +89 -0
@@ -0,0 +1,510 @@
1
+ import { Command } from "commander";
2
+ import { createServer } from "node:http";
3
+ import { randomBytes } from "node:crypto";
4
+ import { mkdirSync, writeFileSync, readdirSync, statSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { GitHubOAuthClient, generateSSHKeyPair, } from "../../auth/index.js";
7
+ import { loadAuthState, saveAuthState, clearAuthState, isTokenExpired, getKeysDir, loadClientConfig, saveClientConfig, } from "../auth-store.js";
8
+ import { printError } from "../output.js";
9
+ import { createCliServerClient, resolveServerUrl, resolveAuthToken } from "../client-config.js";
10
+ // ── Constants ─────────────────────────────────────────────────────────────
11
+ const DEFAULT_OAUTH_CONFIG = {
12
+ clientId: process.env.BMUX_GITHUB_CLIENT_ID ?? "",
13
+ clientSecret: process.env.BMUX_GITHUB_CLIENT_SECRET ?? "",
14
+ callbackUrl: "http://localhost:0/callback",
15
+ scopes: ["read:user", "user:email", "read:org", "repo"],
16
+ };
17
+ // ── Command creation ─────────────────────────────────────────────────────
18
+ export function createAuthCommand() {
19
+ const cmd = new Command("auth").description("Authentication and key management");
20
+ cmd.addCommand(createLoginCommand());
21
+ cmd.addCommand(createServerConfigCommand());
22
+ cmd.addCommand(createTokenCommand());
23
+ cmd.addCommand(createLogoutCommand());
24
+ cmd.addCommand(createStatusCommand());
25
+ cmd.addCommand(createKeygenCommand());
26
+ cmd.addCommand(createKeyPushCommand());
27
+ cmd.addCommand(createKeysCommand());
28
+ return cmd;
29
+ }
30
+ // ── login ────────────────────────────────────────────────────────────────
31
+ function createLoginCommand() {
32
+ return new Command("login")
33
+ .description("Authenticate with GitHub OAuth")
34
+ .action(async (_opts, command) => {
35
+ const allOpts = command.optsWithGlobals();
36
+ const jsonMode = allOpts.json === true;
37
+ try {
38
+ const state = randomBytes(16).toString("hex");
39
+ // Create a one-shot local HTTP server to receive the OAuth callback
40
+ const { port, authCode } = await startCallbackServer(state);
41
+ const oauthConfig = {
42
+ ...DEFAULT_OAUTH_CONFIG,
43
+ callbackUrl: `http://localhost:${port}/callback`,
44
+ };
45
+ const oauthClient = new GitHubOAuthClient(oauthConfig);
46
+ const authUrl = oauthClient.getAuthorizationUrl(state);
47
+ // Try to open the browser; fall back to printing the URL
48
+ let browserOpened = false;
49
+ try {
50
+ const { default: open } = await import("open");
51
+ await open(authUrl);
52
+ browserOpened = true;
53
+ }
54
+ catch {
55
+ // open package not available or failed
56
+ }
57
+ if (!jsonMode) {
58
+ if (browserOpened) {
59
+ console.log("Opening browser for authentication...");
60
+ }
61
+ else {
62
+ console.log("Open the following URL in your browser to authenticate:");
63
+ }
64
+ console.log(`\n ${authUrl}\n`);
65
+ console.log("Waiting for callback...");
66
+ }
67
+ // Wait for the authorization code
68
+ const code = await authCode;
69
+ // Exchange code for token and fetch user profile
70
+ const tokenResult = await oauthClient.exchangeCode(code);
71
+ const user = await oauthClient.getUserProfile(tokenResult.accessToken);
72
+ const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString();
73
+ saveAuthState({
74
+ accessToken: tokenResult.accessToken,
75
+ refreshToken: tokenResult.accessToken, // GitHub OAuth doesn't issue refresh tokens; store access token
76
+ expiresAt,
77
+ user,
78
+ });
79
+ if (allOpts.serverUrl) {
80
+ saveClientConfig({ serverUrl: resolveServerUrl(allOpts.serverUrl) });
81
+ }
82
+ if (jsonMode) {
83
+ console.log(JSON.stringify({ status: "logged_in", user }, null, 2));
84
+ }
85
+ else {
86
+ console.log(`Logged in as ${user.name} (${user.login})`);
87
+ }
88
+ }
89
+ catch (error) {
90
+ printError(error, jsonMode);
91
+ process.exitCode = 1;
92
+ }
93
+ });
94
+ }
95
+ function createServerConfigCommand() {
96
+ const cmd = new Command("server").description("Manage the saved tasks-adapter server URL");
97
+ cmd
98
+ .command("set")
99
+ .argument("<url>", "Breakpoints-adapter server URL")
100
+ .description("Save a default server URL in ~/.tasks-adapter/config.json")
101
+ .action((url, _opts, command) => {
102
+ const allOpts = command.optsWithGlobals();
103
+ const jsonMode = allOpts.json === true;
104
+ try {
105
+ saveClientConfig({ serverUrl: resolveServerUrl(url) });
106
+ if (jsonMode) {
107
+ console.log(JSON.stringify({
108
+ status: "saved",
109
+ key: "serverUrl",
110
+ value: resolveServerUrl(url),
111
+ source: "~/.tasks-adapter/config.json",
112
+ }));
113
+ }
114
+ else {
115
+ console.log(`Saved default server URL to ~/.tasks-adapter/config.json: ${resolveServerUrl(url)}`);
116
+ }
117
+ }
118
+ catch (error) {
119
+ printError(error, jsonMode);
120
+ process.exitCode = 1;
121
+ }
122
+ });
123
+ cmd
124
+ .command("clear")
125
+ .description("Remove the saved default server URL from ~/.tasks-adapter/config.json")
126
+ .action((_opts, command) => {
127
+ const allOpts = command.optsWithGlobals();
128
+ const jsonMode = allOpts.json === true;
129
+ try {
130
+ saveClientConfig({ serverUrl: "" });
131
+ if (jsonMode) {
132
+ console.log(JSON.stringify({ status: "cleared", key: "serverUrl", source: "~/.tasks-adapter/config.json" }));
133
+ }
134
+ else {
135
+ console.log("Cleared saved default server URL from ~/.tasks-adapter/config.json");
136
+ }
137
+ }
138
+ catch (error) {
139
+ printError(error, jsonMode);
140
+ process.exitCode = 1;
141
+ }
142
+ });
143
+ return cmd;
144
+ }
145
+ function createTokenCommand() {
146
+ const cmd = new Command("token").description("Manage a saved bearer token for CLI and MCP use");
147
+ cmd
148
+ .command("set")
149
+ .argument("<token>", "Bearer token value")
150
+ .description("Save a bearer token in ~/.tasks-adapter/config.json")
151
+ .action((token, _opts, command) => {
152
+ const allOpts = command.optsWithGlobals();
153
+ const jsonMode = allOpts.json === true;
154
+ try {
155
+ saveClientConfig({
156
+ authToken: token,
157
+ serverUrl: allOpts.serverUrl ? resolveServerUrl(allOpts.serverUrl) : undefined,
158
+ });
159
+ if (jsonMode) {
160
+ console.log(JSON.stringify({ status: "saved", source: "~/.tasks-adapter/config.json" }));
161
+ }
162
+ else {
163
+ console.log("Saved bearer token to ~/.tasks-adapter/config.json");
164
+ }
165
+ }
166
+ catch (error) {
167
+ printError(error, jsonMode);
168
+ process.exitCode = 1;
169
+ }
170
+ });
171
+ cmd
172
+ .command("clear")
173
+ .description("Remove the saved bearer token from ~/.tasks-adapter/config.json")
174
+ .action((_opts, command) => {
175
+ const allOpts = command.optsWithGlobals();
176
+ const jsonMode = allOpts.json === true;
177
+ try {
178
+ saveClientConfig({ authToken: "" });
179
+ if (jsonMode) {
180
+ console.log(JSON.stringify({ status: "cleared", source: "~/.tasks-adapter/config.json" }));
181
+ }
182
+ else {
183
+ console.log("Cleared saved bearer token from ~/.tasks-adapter/config.json");
184
+ }
185
+ }
186
+ catch (error) {
187
+ printError(error, jsonMode);
188
+ process.exitCode = 1;
189
+ }
190
+ });
191
+ return cmd;
192
+ }
193
+ // ── logout ───────────────────────────────────────────────────────────────
194
+ function createLogoutCommand() {
195
+ return new Command("logout")
196
+ .description("Clear stored authentication tokens")
197
+ .action((_opts, command) => {
198
+ const allOpts = command.optsWithGlobals();
199
+ const jsonMode = allOpts.json === true;
200
+ try {
201
+ clearAuthState();
202
+ saveClientConfig({ authToken: "" });
203
+ if (jsonMode) {
204
+ console.log(JSON.stringify({ status: "logged_out" }));
205
+ }
206
+ else {
207
+ console.log("Logged out. Authentication tokens cleared.");
208
+ }
209
+ }
210
+ catch (error) {
211
+ printError(error, jsonMode);
212
+ process.exitCode = 1;
213
+ }
214
+ });
215
+ }
216
+ // ── status ───────────────────────────────────────────────────────────────
217
+ function createStatusCommand() {
218
+ return new Command("status")
219
+ .description("Show current authentication status")
220
+ .action(async (_opts, command) => {
221
+ const allOpts = command.optsWithGlobals();
222
+ const jsonMode = allOpts.json === true;
223
+ try {
224
+ const auth = loadAuthState();
225
+ const config = loadClientConfig();
226
+ const token = await resolveAuthToken(allOpts.serverUrl, allOpts.authToken);
227
+ const serverUrl = resolveServerUrl(allOpts.serverUrl);
228
+ if (!token && !auth) {
229
+ if (jsonMode) {
230
+ console.log(JSON.stringify({ authenticated: false }));
231
+ }
232
+ else {
233
+ console.log("Not authenticated. Use `tasks-adapter auth login`, `tasks-adapter auth token set`, or BMUX_AUTH_TOKEN.");
234
+ }
235
+ return;
236
+ }
237
+ const client = await createCliServerClient({
238
+ serverUrl: allOpts.serverUrl,
239
+ authToken: allOpts.authToken,
240
+ });
241
+ const user = await client.get("/auth/me");
242
+ const expired = auth ? isTokenExpired(auth.expiresAt) : false;
243
+ const source = allOpts.authToken
244
+ ? "flag"
245
+ : process.env.BMUX_AUTH_TOKEN
246
+ ? "BMUX_AUTH_TOKEN"
247
+ : process.env.AUTH_TOKEN
248
+ ? "AUTH_TOKEN"
249
+ : config.authToken
250
+ ? "~/.tasks-adapter/config.json"
251
+ : auth
252
+ ? "~/.tasks-adapter/auth.json"
253
+ : "unknown";
254
+ if (jsonMode) {
255
+ console.log(JSON.stringify({
256
+ authenticated: true,
257
+ expired,
258
+ source,
259
+ user,
260
+ expiresAt: auth?.expiresAt,
261
+ serverUrl,
262
+ }, null, 2));
263
+ }
264
+ else {
265
+ console.log(`Authenticated as: ${user.name} (${user.login})`);
266
+ if (user.email) {
267
+ console.log(`Email: ${user.email}`);
268
+ }
269
+ console.log(`Server: ${serverUrl}`);
270
+ console.log(`Auth source: ${source}`);
271
+ if (auth?.expiresAt) {
272
+ console.log(`Token expires: ${auth.expiresAt}`);
273
+ }
274
+ console.log(`Status: ${expired ? "EXPIRED" : "Active"}`);
275
+ }
276
+ }
277
+ catch (error) {
278
+ printError(error, jsonMode);
279
+ process.exitCode = 1;
280
+ }
281
+ });
282
+ }
283
+ // ── keygen ───────────────────────────────────────────────────────────────
284
+ function createKeygenCommand() {
285
+ return new Command("keygen")
286
+ .description("Generate a new Ed25519 SSH key pair")
287
+ .action((_opts, command) => {
288
+ const allOpts = command.optsWithGlobals();
289
+ const jsonMode = allOpts.json === true;
290
+ try {
291
+ const keyPair = generateSSHKeyPair();
292
+ // Save private key to ~/.tasks-adapter/keys/{fingerprint}.pem
293
+ const keysDir = getKeysDir();
294
+ mkdirSync(keysDir, { recursive: true });
295
+ const safeName = keyPair.fingerprint.replace(/[/:]/g, "_");
296
+ const privateKeyPath = join(keysDir, `${safeName}.pem`);
297
+ writeFileSync(privateKeyPath, keyPair.privateKey, {
298
+ encoding: "utf-8",
299
+ mode: 0o600,
300
+ });
301
+ if (jsonMode) {
302
+ console.log(JSON.stringify({
303
+ publicKey: keyPair.publicKey,
304
+ fingerprint: keyPair.fingerprint,
305
+ algorithm: keyPair.algorithm,
306
+ privateKeyPath,
307
+ createdAt: keyPair.createdAt,
308
+ }, null, 2));
309
+ }
310
+ else {
311
+ console.log(`Key pair generated successfully.`);
312
+ console.log(` Fingerprint: ${keyPair.fingerprint}`);
313
+ console.log(` Algorithm: ${keyPair.algorithm}`);
314
+ console.log(` Private key: ${privateKeyPath}`);
315
+ console.log(` Created: ${keyPair.createdAt}`);
316
+ console.log(``);
317
+ console.log(`Public key:`);
318
+ console.log(keyPair.publicKey);
319
+ }
320
+ }
321
+ catch (error) {
322
+ printError(error, jsonMode);
323
+ process.exitCode = 1;
324
+ }
325
+ });
326
+ }
327
+ // ── key-push ─────────────────────────────────────────────────────────────
328
+ function createKeyPushCommand() {
329
+ return new Command("key-push")
330
+ .description("Push public key to repository")
331
+ .option("--pr", "Create a pull request with the key", false)
332
+ .option("--key <fingerprint>", "Fingerprint of the key to push (defaults to most recent)")
333
+ .action(async (opts, command) => {
334
+ const allOpts = command.optsWithGlobals();
335
+ const localOpts = opts;
336
+ const jsonMode = allOpts.json === true;
337
+ const serverUrl = resolveServerUrl(allOpts.serverUrl);
338
+ try {
339
+ const keysDir = getKeysDir();
340
+ const keyFiles = listKeyFiles(keysDir);
341
+ if (keyFiles.length === 0) {
342
+ throw new Error("No keys found. Run `tasks-adapter auth keygen` first.");
343
+ }
344
+ // Find the key to push
345
+ let keyFile;
346
+ if (localOpts.key) {
347
+ const safeName = localOpts.key.replace(/[/:]/g, "_");
348
+ const match = keyFiles.find((f) => f.includes(safeName));
349
+ if (!match) {
350
+ throw new Error(`Key not found for fingerprint: ${localOpts.key}`);
351
+ }
352
+ keyFile = match;
353
+ }
354
+ else {
355
+ // Use the most recently created key
356
+ keyFile = keyFiles[keyFiles.length - 1];
357
+ }
358
+ // For key-push, we need to communicate with the server
359
+ if (localOpts.pr) {
360
+ const response = await fetch(`${serverUrl}/api/v1/keys`, {
361
+ method: "POST",
362
+ headers: { "Content-Type": "application/json" },
363
+ body: JSON.stringify({
364
+ keyFile: keyFile.replace(".pem", ""),
365
+ createPR: true,
366
+ }),
367
+ });
368
+ if (!response.ok) {
369
+ throw new Error(`Server returned ${response.status}: ${response.statusText}`);
370
+ }
371
+ const result = (await response.json());
372
+ if (jsonMode) {
373
+ console.log(JSON.stringify(result, null, 2));
374
+ }
375
+ else {
376
+ if (result.prUrl) {
377
+ console.log(`Pull request created: ${result.prUrl}`);
378
+ }
379
+ else {
380
+ console.log(result.message ?? "Key pushed successfully.");
381
+ }
382
+ }
383
+ }
384
+ else {
385
+ if (jsonMode) {
386
+ console.log(JSON.stringify({ keyFile, status: "ready" }, null, 2));
387
+ }
388
+ else {
389
+ console.log(`Key ready to push: ${keyFile}`);
390
+ console.log(`Use --pr to create a pull request.`);
391
+ }
392
+ }
393
+ }
394
+ catch (error) {
395
+ printError(error, jsonMode);
396
+ process.exitCode = 1;
397
+ }
398
+ });
399
+ }
400
+ // ── keys ─────────────────────────────────────────────────────────────────
401
+ function createKeysCommand() {
402
+ return new Command("keys")
403
+ .description("List local SSH keys")
404
+ .action((_opts, command) => {
405
+ const allOpts = command.optsWithGlobals();
406
+ const jsonMode = allOpts.json === true;
407
+ try {
408
+ const keysDir = getKeysDir();
409
+ const keyFiles = listKeyFiles(keysDir);
410
+ if (keyFiles.length === 0) {
411
+ if (jsonMode) {
412
+ console.log(JSON.stringify([]));
413
+ }
414
+ else {
415
+ console.log("No keys found. Run `tasks-adapter auth keygen` to generate a key pair.");
416
+ }
417
+ return;
418
+ }
419
+ const keys = keyFiles.map((file) => {
420
+ const filePath = join(keysDir, file);
421
+ const stats = statSync(filePath);
422
+ const fingerprint = file.replace(".pem", "").replace(/_/g, "/").replace(/SHA256\//, "SHA256:");
423
+ return {
424
+ fingerprint,
425
+ file,
426
+ createdAt: stats.birthtime.toISOString(),
427
+ };
428
+ });
429
+ if (jsonMode) {
430
+ console.log(JSON.stringify(keys, null, 2));
431
+ }
432
+ else {
433
+ console.log("Local SSH keys:\n");
434
+ for (const key of keys) {
435
+ console.log(` ${key.fingerprint}`);
436
+ console.log(` File: ${key.file}`);
437
+ console.log(` Created: ${key.createdAt}`);
438
+ console.log("");
439
+ }
440
+ }
441
+ }
442
+ catch (error) {
443
+ printError(error, jsonMode);
444
+ process.exitCode = 1;
445
+ }
446
+ });
447
+ }
448
+ // ── Internal helpers ─────────────────────────────────────────────────────
449
+ /**
450
+ * Start a temporary HTTP server to receive the OAuth callback.
451
+ * Returns the port and a promise that resolves with the authorization code.
452
+ */
453
+ function startCallbackServer(expectedState) {
454
+ return new Promise((resolveSetup) => {
455
+ let resolveCode;
456
+ let rejectCode;
457
+ const authCode = new Promise((resolve, reject) => {
458
+ resolveCode = resolve;
459
+ rejectCode = reject;
460
+ });
461
+ const server = createServer((req, res) => {
462
+ const url = new URL(req.url ?? "/", `http://localhost`);
463
+ if (url.pathname === "/callback") {
464
+ const code = url.searchParams.get("code");
465
+ const state = url.searchParams.get("state");
466
+ if (state !== expectedState) {
467
+ res.writeHead(400, { "Content-Type": "text/html" });
468
+ res.end("<h1>State mismatch. Authentication failed.</h1>");
469
+ rejectCode(new Error("OAuth state mismatch"));
470
+ server.close();
471
+ return;
472
+ }
473
+ if (!code) {
474
+ res.writeHead(400, { "Content-Type": "text/html" });
475
+ res.end("<h1>No authorization code received.</h1>");
476
+ rejectCode(new Error("No authorization code in callback"));
477
+ server.close();
478
+ return;
479
+ }
480
+ res.writeHead(200, { "Content-Type": "text/html" });
481
+ res.end("<h1>Authentication successful!</h1><p>You can close this window.</p>");
482
+ resolveCode(code);
483
+ // Close the server after a brief delay to let the response flush
484
+ setTimeout(() => server.close(), 500);
485
+ }
486
+ else {
487
+ res.writeHead(404);
488
+ res.end();
489
+ }
490
+ });
491
+ server.listen(0, () => {
492
+ const addr = server.address();
493
+ const port = typeof addr === "object" && addr !== null ? addr.port : 0;
494
+ resolveSetup({ port, authCode });
495
+ });
496
+ });
497
+ }
498
+ /**
499
+ * List .pem files in the keys directory.
500
+ */
501
+ function listKeyFiles(keysDir) {
502
+ try {
503
+ return readdirSync(keysDir)
504
+ .filter((f) => f.endsWith(".pem"))
505
+ .sort();
506
+ }
507
+ catch {
508
+ return [];
509
+ }
510
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createBreakpointsCommand(): Command;
3
+ //# sourceMappingURL=breakpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/breakpoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiDpC,wBAAgB,wBAAwB,IAAI,OAAO,CA+SlD"}