gitdog 0.0.0-stage → 0.1.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 CHANGED
@@ -1,3 +1,77 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/hyodotdev/gitdog-cli/main/.github/assets/gitdog-logo.png" alt="Gitdog" width="112" height="112">
3
+ </p>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ <h1 align="center">Gitdog CLI</h1>
6
+
7
+ <p align="center">
8
+ Your GitHub assistant. Now in the terminal.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/gitdog"><img src="https://img.shields.io/npm/v/gitdog?logo=npm&amp;color=CB3837" alt="npm version"></a>
13
+ <a href="https://www.npmjs.com/package/gitdog"><img src="https://img.shields.io/npm/dm/gitdog?color=555" alt="npm downloads"></a>
14
+ <a href="https://github.com/hyodotdev/gitdog-cli/actions/workflows/ci.yml"><img src="https://github.com/hyodotdev/gitdog-cli/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
15
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D22.14.0-3c873a?logo=nodedotjs&amp;logoColor=white" alt="Node.js 22.14 or later"></a>
16
+ <a href="https://gitdog.dev"><img src="https://img.shields.io/badge/Gitdog-gitdog.dev-111" alt="Gitdog website"></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <img src="https://raw.githubusercontent.com/hyodotdev/gitdog-cli/main/.github/assets/terminal.svg" alt="Gitdog terminal: connect a project, configure automation and review pending actions" width="800">
21
+ </p>
22
+
23
+ Connect repositories, configure Gitdog's issue and discussion assistants, review
24
+ AI actions, and automate project tasks from your terminal or CI.
25
+
26
+ ## Install
27
+
28
+ Requires **Node.js 22.14+**.
29
+
30
+ ```sh
31
+ npm install --global gitdog
32
+ ```
33
+
34
+ ## Quick start
35
+
36
+ ```sh
37
+ gitdog login
38
+ gitdog github install
39
+ gitdog github recover
40
+ gitdog init owner/repo
41
+ gitdog config set requireApproval true
42
+ gitdog config push
43
+ ```
44
+
45
+ GitHub App setup is only needed once. A project connects an existing GitHub
46
+ repository to Gitdog.
47
+
48
+ ## Everyday commands
49
+
50
+ | Task | Command |
51
+ | ------------------------ | ---------------------------- |
52
+ | List projects | `gitdog projects list` |
53
+ | Read automation settings | `gitdog config get --remote` |
54
+ | Review pending actions | `gitdog tickets pending` |
55
+ | Check code indexing | `gitdog index status` |
56
+ | List team members | `gitdog team members` |
57
+ | Check usage | `gitdog usage summary` |
58
+
59
+ Use `gitdog --help` to explore commands. Add `--json` for scripts.
60
+
61
+ ## Use in CI
62
+
63
+ ```sh
64
+ gitdog keys create --name ci
65
+ gitdog --key-file /path/to/key.json projects list --json
66
+ gitdog keys revoke KEY_ID --yes
67
+ ```
68
+
69
+ `keys create` prints the private key file's path. Keys are project-scoped and
70
+ read-only by default; add `--write` for settings and ticket delete/restore. Keep key
71
+ files out of Git.
72
+
73
+ ---
74
+
75
+ [Command reference](https://github.com/hyodotdev/gitdog-cli/blob/main/docs/REFERENCE.md)
76
+ · [Gitdog](https://gitdog.dev)
77
+ · [Report an issue](https://github.com/hyodotdev/gitdog-cli/issues)
@@ -0,0 +1,432 @@
1
+ [
2
+ {
3
+ "path": "admin/mutation:deleteAiLog",
4
+ "kind": "mutation",
5
+ "args": "{\n logId: v.id(\"aiLogs\"),\n }"
6
+ },
7
+ {
8
+ "path": "admin/mutation:updateAiLogStatus",
9
+ "kind": "mutation",
10
+ "args": "{\n logId: v.id(\"aiLogs\"),\n status: v.union(\n v.literal(\"success\"),\n v.literal(\"failed\"),\n v.literal(\"pending_approval\"),\n v.literal(\"approved\"),\n v.literal(\"rejected\"),\n v.literal(\"request_changes\"),\n ),\n userFeedback: v.optional(v.string()),\n }"
11
+ },
12
+ {
13
+ "path": "admin/paymentMutation:addTokens",
14
+ "kind": "mutation",
15
+ "args": "{\n subscriptionId: v.id(\"subscriptions\"),\n tokensToAdd: v.number(),\n operationId: v.string(),\n adminNote: v.optional(v.string()),\n }"
16
+ },
17
+ {
18
+ "path": "admin/paymentMutation:cancelSubscription",
19
+ "kind": "mutation",
20
+ "args": "{\n subscriptionId: v.id(\"subscriptions\"),\n operationId: v.string(),\n adminNote: v.optional(v.string()),\n }"
21
+ },
22
+ {
23
+ "path": "admin/paymentMutation:reactivateSubscription",
24
+ "kind": "mutation",
25
+ "args": "{\n subscriptionId: v.id(\"subscriptions\"),\n operationId: v.string(),\n adminNote: v.optional(v.string()),\n }"
26
+ },
27
+ {
28
+ "path": "admin/paymentMutation:reconcileGeminiChatUsage",
29
+ "kind": "mutation",
30
+ "args": "{\n reservationKey: v.string(),\n evidenceSource: v.union(\n v.literal(\"google_api_response\"),\n v.literal(\"google_cloud_billing_export\"),\n v.literal(\"google_support_confirmation\"),\n v.literal(\"anthropic_api_response\"),\n v.literal(\"anthropic_usage_report\"),\n v.literal(\"anthropic_support_confirmation\"),\n v.literal(\"openai_api_response\"),\n v.literal(\"openai_usage_report\"),\n v.literal(\"openai_support_confirmation\"),\n),\n evidenceReference: v.string(),\n outcome: v.union(\n v.object({ kind: v.literal(\"known_not_billed\") }),\n v.object({\n kind: v.literal(\"known_billed\"),\n actualInputTokens: v.number(),\n actualOutputTokens: v.number(),\n providerRequestId: v.optional(v.string()),\n }),\n ),\n }"
31
+ },
32
+ {
33
+ "path": "admin/paymentMutation:reconcileGeminiEmbeddingUsage",
34
+ "kind": "mutation",
35
+ "args": "{\n reservationKey: v.string(),\n evidenceSource: v.union(\n v.literal(\"google_api_response\"),\n v.literal(\"google_cloud_billing_export\"),\n v.literal(\"google_support_confirmation\"),\n v.literal(\"openai_api_response\"),\n v.literal(\"openai_billing_export\"),\n v.literal(\"openai_support_confirmation\"),\n),\n evidenceReference: v.string(),\n outcome: v.union(\n v.object({ kind: v.literal(\"known_not_billed\") }),\n v.object({\n kind: v.literal(\"known_billed\"),\n actualProviderTokens: v.number(),\n }),\n ),\n }"
36
+ },
37
+ {
38
+ "path": "admin/paymentMutation:refundTokens",
39
+ "kind": "mutation",
40
+ "args": "{\n subscriptionId: v.id(\"subscriptions\"),\n tokensToRefund: v.number(),\n adminNote: v.optional(v.string()),\n paymentLogId: v.id(\"paymentLogs\"),\n }"
41
+ },
42
+ {
43
+ "path": "admin/paymentMutation:requeueSubscriptionInvoiceReview",
44
+ "kind": "mutation",
45
+ "args": "{\n manualReviewId: v.id(\"paymentSettlementManualReviews\"),\n evidenceReference: v.string(),\n confirmRetry: v.literal(true),\n }"
46
+ },
47
+ {
48
+ "path": "admin/paymentMutation:updatePaymentLog",
49
+ "kind": "mutation",
50
+ "args": "{\n paymentLogId: v.id(\"paymentLogs\"),\n status: v.optional(\n v.union(\n v.literal(\"success\"),\n v.literal(\"failed\"),\n v.literal(\"pending\"),\n v.literal(\"refunded\"),\n v.literal(\"canceled\"),\n ),\n ),\n adminNote: v.optional(v.string()),\n }"
51
+ },
52
+ {
53
+ "path": "admin/paymentQuery:getPaymentStats",
54
+ "kind": "query",
55
+ "args": "{\n year: v.optional(v.number()),\n month: v.optional(v.number()), // 0-11 (January = 0)\n }"
56
+ },
57
+ {
58
+ "path": "admin/paymentQuery:listOpenSettlementReviews",
59
+ "kind": "query",
60
+ "args": "{ limit: v.optional(v.number()) }"
61
+ },
62
+ {
63
+ "path": "admin/paymentQuery:listPaymentLogs",
64
+ "kind": "query",
65
+ "args": "{\n limit: v.optional(v.number()),\n cursor: v.optional(v.string()),\n statusFilter: v.optional(\n v.union(\n v.literal(\"success\"),\n v.literal(\"failed\"),\n v.literal(\"pending\"),\n v.literal(\"refunded\"),\n v.literal(\"canceled\"),\n ),\n ),\n eventTypeFilter: v.optional(v.string()),\n }"
66
+ },
67
+ {
68
+ "path": "admin/paymentQuery:listPaymentLogsByUser",
69
+ "kind": "query",
70
+ "args": "{\n year: v.optional(v.number()),\n month: v.optional(v.number()), // 0-11 (January = 0)\n }"
71
+ },
72
+ {
73
+ "path": "admin/paymentQuery:listSubscriptions",
74
+ "kind": "query",
75
+ "args": "{\n limit: v.optional(v.number()),\n }"
76
+ },
77
+ {
78
+ "path": "admin/query:getAiLogById",
79
+ "kind": "query",
80
+ "args": "{\n logId: v.id(\"aiLogs\"),\n }"
81
+ },
82
+ {
83
+ "path": "admin/query:getAiLogStats",
84
+ "kind": "query",
85
+ "args": "{}"
86
+ },
87
+ {
88
+ "path": "admin/query:isAdmin",
89
+ "kind": "query",
90
+ "args": "{}"
91
+ },
92
+ {
93
+ "path": "admin/query:listAiLogs",
94
+ "kind": "query",
95
+ "args": "{\n status: v.optional(\n v.union(\n v.literal(\"success\"),\n v.literal(\"failed\"),\n v.literal(\"pending_approval\"),\n v.literal(\"approved\"),\n v.literal(\"rejected\"),\n v.literal(\"request_changes\"),\n ),\n ),\n workflowType: v.optional(v.string()),\n limit: v.optional(v.number()),\n cursor: v.optional(v.string()),\n }"
96
+ },
97
+ {
98
+ "path": "admin/query:listAiLogsByIndexing",
99
+ "kind": "query",
100
+ "args": "{\n limit: v.optional(v.number()),\n cursor: v.optional(v.number()),\n }"
101
+ },
102
+ {
103
+ "path": "admin/query:listAiLogsByTicket",
104
+ "kind": "query",
105
+ "args": "{\n limit: v.optional(v.number()),\n cursor: v.optional(v.number()), // createdAt timestamp for cursor-based pagination\n }"
106
+ },
107
+ {
108
+ "path": "apiKeys/action:create",
109
+ "kind": "action",
110
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n name: v.string(),\n scopes: v.array(v.union(v.literal(\"read\"), v.literal(\"write\"))),\n expiresInDays: v.number(),\n }"
111
+ },
112
+ {
113
+ "path": "apiKeys/mutation:revoke",
114
+ "kind": "mutation",
115
+ "args": "{ keyId: v.id(\"projectApiKeys\") }"
116
+ },
117
+ {
118
+ "path": "apiKeys/query:list",
119
+ "kind": "query",
120
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n paginationOpts: paginationOptsValidator,\n }"
121
+ },
122
+ {
123
+ "path": "github/action:listUserRepositories",
124
+ "kind": "action",
125
+ "args": "{\n cursor: v.optional(v.string()),\n reconnectRepositoryId: v.optional(v.string()),\n }"
126
+ },
127
+ {
128
+ "path": "github/action:recoverOAuthInstallation",
129
+ "kind": "action",
130
+ "args": "{}"
131
+ },
132
+ {
133
+ "path": "github/action:retryRepositoryAccessReconciliation",
134
+ "kind": "action",
135
+ "args": "{ installationId: v.number() }"
136
+ },
137
+ {
138
+ "path": "github/action:saveInstallation",
139
+ "kind": "action",
140
+ "args": "{\n installationId: v.number(),\n }"
141
+ },
142
+ {
143
+ "path": "github/query:getInstallation",
144
+ "kind": "query",
145
+ "args": "{\n installationId: v.number(),\n }"
146
+ },
147
+ {
148
+ "path": "github/query:getUserInstallation",
149
+ "kind": "query",
150
+ "args": "{}"
151
+ },
152
+ {
153
+ "path": "github/query:getUserInstallations",
154
+ "kind": "query",
155
+ "args": "{}"
156
+ },
157
+ {
158
+ "path": "invitations/action:create",
159
+ "kind": "action",
160
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n inviteeGithubUsername: v.string(),\n role: v.literal(\"viewer\"),\n }"
161
+ },
162
+ {
163
+ "path": "invitations/mutation:accept",
164
+ "kind": "mutation",
165
+ "args": "{\n invitationId: v.id(\"invitations\"),\n }"
166
+ },
167
+ {
168
+ "path": "invitations/mutation:cancel",
169
+ "kind": "mutation",
170
+ "args": "{\n invitationId: v.id(\"invitations\"),\n }"
171
+ },
172
+ {
173
+ "path": "invitations/mutation:reject",
174
+ "kind": "mutation",
175
+ "args": "{\n invitationId: v.id(\"invitations\"),\n }"
176
+ },
177
+ {
178
+ "path": "invitations/query:getPendingForRepository",
179
+ "kind": "query",
180
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
181
+ },
182
+ {
183
+ "path": "invitations/query:listForRepository",
184
+ "kind": "query",
185
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
186
+ },
187
+ {
188
+ "path": "invitations/query:listPending",
189
+ "kind": "query",
190
+ "args": "{}"
191
+ },
192
+ {
193
+ "path": "notifications/mutation:markAllAsRead",
194
+ "kind": "mutation",
195
+ "args": "{}"
196
+ },
197
+ {
198
+ "path": "notifications/mutation:markAsRead",
199
+ "kind": "mutation",
200
+ "args": "{\n notificationId: v.id(\"notifications\"),\n }"
201
+ },
202
+ {
203
+ "path": "notifications/query:getUnreadCount",
204
+ "kind": "query",
205
+ "args": "{}"
206
+ },
207
+ {
208
+ "path": "notifications/query:list",
209
+ "kind": "query",
210
+ "args": "{\n limit: v.optional(v.number()),\n }"
211
+ },
212
+ {
213
+ "path": "notifications/query:listUnread",
214
+ "kind": "query",
215
+ "args": "{}"
216
+ },
217
+ {
218
+ "path": "repoIndexing/mutation:initializeIndexing",
219
+ "kind": "mutation",
220
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
221
+ },
222
+ {
223
+ "path": "repoIndexing/query:getFileContent",
224
+ "kind": "query",
225
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n filePath: v.string(),\n }"
226
+ },
227
+ {
228
+ "path": "repoIndexing/query:getFileList",
229
+ "kind": "query",
230
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n limit: v.optional(v.number()),\n }"
231
+ },
232
+ {
233
+ "path": "repoIndexing/query:getIndexingLogs",
234
+ "kind": "query",
235
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n limit: v.optional(v.number()),\n }"
236
+ },
237
+ {
238
+ "path": "repoIndexing/query:getIndexingStatus",
239
+ "kind": "query",
240
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
241
+ },
242
+ {
243
+ "path": "repoLearnings/query:countByRepository",
244
+ "kind": "query",
245
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
246
+ },
247
+ {
248
+ "path": "repositories/action:createAndIndex",
249
+ "kind": "action",
250
+ "args": "{\n installationId: v.number(),\n githubRepositoryId: v.number(),\n reconnectRepositoryId: v.optional(v.string()),\n }"
251
+ },
252
+ {
253
+ "path": "repositories/action:syncRepository",
254
+ "kind": "action",
255
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
256
+ },
257
+ {
258
+ "path": "repositories/mutation:remove",
259
+ "kind": "mutation",
260
+ "args": "{ repositoryId: v.id(\"repositories\") }"
261
+ },
262
+ {
263
+ "path": "repositories/mutation:toggleActive",
264
+ "kind": "mutation",
265
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n isActive: v.optional(v.boolean()),\n }"
266
+ },
267
+ {
268
+ "path": "repositories/mutation:updateSettings",
269
+ "kind": "mutation",
270
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n settings: v.object({\n issueAssistantEnabled: v.boolean(),\n autoFormatIssue: v.boolean(),\n autoAnswerQuestions: v.boolean(),\n tagMaintainersForCodeChanges: v.boolean(),\n issueResponseStyle: v.optional(v.string()),\n requireApproval: v.boolean(),\n discussionAssistantEnabled: v.boolean(),\n autoFormatDiscussion: v.boolean(),\n autoAnswerDiscussionQuestions: v.boolean(),\n tagMaintainersForDiscussionCodeChanges: v.boolean(),\n discussionResponseStyle: v.optional(v.string()),\n requireApprovalForDiscussions: v.boolean(),\n }),\n }"
271
+ },
272
+ {
273
+ "path": "repositories/query:get",
274
+ "kind": "query",
275
+ "args": "{\n owner: v.string(),\n repo: v.string(),\n }"
276
+ },
277
+ {
278
+ "path": "repositories/query:getById",
279
+ "kind": "query",
280
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
281
+ },
282
+ {
283
+ "path": "repositories/query:getConnectedRepos",
284
+ "kind": "query",
285
+ "args": "{}"
286
+ },
287
+ {
288
+ "path": "repositories/query:getReconnectTarget",
289
+ "kind": "query",
290
+ "args": "{\n reconnectRepositoryId: v.string(),\n }"
291
+ },
292
+ {
293
+ "path": "repositories/query:list",
294
+ "kind": "query",
295
+ "args": "{}"
296
+ },
297
+ {
298
+ "path": "repositoryMembers/mutation:leave",
299
+ "kind": "mutation",
300
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
301
+ },
302
+ {
303
+ "path": "repositoryMembers/mutation:remove",
304
+ "kind": "mutation",
305
+ "args": "{\n memberId: v.id(\"repositoryMembers\"),\n }"
306
+ },
307
+ {
308
+ "path": "repositoryMembers/query:getMyRole",
309
+ "kind": "query",
310
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
311
+ },
312
+ {
313
+ "path": "repositoryMembers/query:list",
314
+ "kind": "query",
315
+ "args": "{\n repositoryId: v.id(\"repositories\"),\n }"
316
+ },
317
+ {
318
+ "path": "subscriptions/action:createCheckoutUrl",
319
+ "kind": "action",
320
+ "args": "{\n plan: v.literal(\"pro\"),\n }"
321
+ },
322
+ {
323
+ "path": "subscriptions/action:createTokenPackCheckoutUrl",
324
+ "kind": "action",
325
+ "args": "{\n packSize: v.union(v.literal(\"small\"), v.literal(\"large\")),\n }"
326
+ },
327
+ {
328
+ "path": "subscriptions/query:calculateOverage",
329
+ "kind": "query",
330
+ "args": "{}"
331
+ },
332
+ {
333
+ "path": "subscriptions/query:canPerformAction",
334
+ "kind": "query",
335
+ "args": "{}"
336
+ },
337
+ {
338
+ "path": "subscriptions/query:getCurrent",
339
+ "kind": "query",
340
+ "args": "{}"
341
+ },
342
+ {
343
+ "path": "subscriptions/query:getPricingInfo",
344
+ "kind": "query",
345
+ "args": "{}"
346
+ },
347
+ {
348
+ "path": "ticketActions/action:approve",
349
+ "kind": "action",
350
+ "args": "{\n actionId: v.id(\"ticketActions\"),\n }"
351
+ },
352
+ {
353
+ "path": "ticketActions/action:reject",
354
+ "kind": "action",
355
+ "args": "{\n actionId: v.id(\"ticketActions\"),\n }"
356
+ },
357
+ {
358
+ "path": "ticketActions/action:requestChanges",
359
+ "kind": "action",
360
+ "args": "{\n actionId: v.id(\"ticketActions\"),\n feedback: v.string(),\n }"
361
+ },
362
+ {
363
+ "path": "ticketActions/query:getById",
364
+ "kind": "query",
365
+ "args": "{\n id: v.id(\"ticketActions\"),\n }"
366
+ },
367
+ {
368
+ "path": "ticketActions/query:listByTicket",
369
+ "kind": "query",
370
+ "args": "{\n ticketId: v.id(\"tickets\"),\n }"
371
+ },
372
+ {
373
+ "path": "tickets/mutation:restore",
374
+ "kind": "mutation",
375
+ "args": "{\n id: v.id(\"tickets\"),\n }"
376
+ },
377
+ {
378
+ "path": "tickets/mutation:softDelete",
379
+ "kind": "mutation",
380
+ "args": "{\n id: v.id(\"tickets\"),\n }"
381
+ },
382
+ {
383
+ "path": "tickets/query:getById",
384
+ "kind": "query",
385
+ "args": "{\n id: v.id(\"tickets\"),\n }"
386
+ },
387
+ {
388
+ "path": "tickets/query:list",
389
+ "kind": "query",
390
+ "args": "{\n githubRepo: v.optional(v.string()),\n status: v.optional(v.union(v.literal(\"open\"), v.literal(\"closed\"))),\n ticketType: v.optional(\n v.union(v.literal(\"issue\"), v.literal(\"discussion\")),\n ),\n search: v.optional(v.string()),\n limit: v.optional(v.number()),\n offset: v.optional(v.number()),\n }"
391
+ },
392
+ {
393
+ "path": "tickets/query:listPendingApproval",
394
+ "kind": "query",
395
+ "args": "{\n githubRepo: v.optional(v.string()),\n ticketType: v.optional(\n v.union(v.literal(\"issue\"), v.literal(\"discussion\")),\n ),\n search: v.optional(v.string()),\n limit: v.optional(v.number()),\n offset: v.optional(v.number()),\n }"
396
+ },
397
+ {
398
+ "path": "tokenUsage/query:getCurrentMonthUsage",
399
+ "kind": "query",
400
+ "args": "{}"
401
+ },
402
+ {
403
+ "path": "tokenUsage/query:getUsageByRepository",
404
+ "kind": "query",
405
+ "args": "{}"
406
+ },
407
+ {
408
+ "path": "users/action:ensureProfile",
409
+ "kind": "action",
410
+ "args": "{}"
411
+ },
412
+ {
413
+ "path": "users/mutation:attestLegalEligibility",
414
+ "kind": "mutation",
415
+ "args": "{\n minimumAgeConfirmed: v.literal(true),\n professionalUseConfirmed: v.literal(true),\n termsAccepted: v.literal(true),\n privacyPolicyAccepted: v.literal(true),\n displayedTermsVersion: v.string(),\n displayedPrivacyPolicyVersion: v.string(),\n }"
416
+ },
417
+ {
418
+ "path": "users/mutation:ensureProfileFromAuth",
419
+ "kind": "mutation",
420
+ "args": "{}"
421
+ },
422
+ {
423
+ "path": "users/query:getCurrentUser",
424
+ "kind": "query",
425
+ "args": "{}"
426
+ },
427
+ {
428
+ "path": "users/query:getProfile",
429
+ "kind": "query",
430
+ "args": "{ userId: v.id(\"users\") }"
431
+ }
432
+ ]
@@ -0,0 +1,15 @@
1
+ export type ProjectKey = {
2
+ version: 1;
3
+ apiKey: string;
4
+ apiUrl: string;
5
+ httpUrl: string;
6
+ keyId: string;
7
+ repositoryId: string;
8
+ name: string;
9
+ scopes: string[];
10
+ expiresAt: number;
11
+ };
12
+ export declare function projectHttpUrl(apiUrl: string): string;
13
+ export declare function readProjectKey(path: string): Promise<ProjectKey>;
14
+ export declare function saveProjectKey(path: string, key: ProjectKey): Promise<void>;
15
+ export declare function callWithProjectKey<T>(apiUrl: string, keyFile: string | undefined, rawKey: string | undefined, kind: string, path: string, args: unknown, timeoutMs: number): Promise<T>;
@@ -0,0 +1,93 @@
1
+ import { mkdir, open, readFile, rm } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { asRecord } from "./ui.js";
4
+ import { validateUrl } from "./storage.js";
5
+ export function projectHttpUrl(apiUrl) {
6
+ return validateUrl(process.env.GITDOG_HTTP_URL ??
7
+ apiUrl.replace(/\.convex\.cloud$/, ".convex.site"), "Project HTTP URL");
8
+ }
9
+ export async function readProjectKey(path) {
10
+ let value;
11
+ try {
12
+ value = JSON.parse(await readFile(resolve(path), "utf8"));
13
+ }
14
+ catch {
15
+ throw new Error("Cannot read the API key file. Check its path, permissions and JSON syntax.");
16
+ }
17
+ const key = asRecord(value);
18
+ if (key?.version !== 1 ||
19
+ typeof key.apiKey !== "string" ||
20
+ !/^gdk_[a-f0-9]{64}$/.test(key.apiKey) ||
21
+ typeof key.apiUrl !== "string" ||
22
+ typeof key.httpUrl !== "string" ||
23
+ typeof key.keyId !== "string" ||
24
+ typeof key.repositoryId !== "string" ||
25
+ typeof key.name !== "string" ||
26
+ !Array.isArray(key.scopes) ||
27
+ !key.scopes.every((scope) => typeof scope === "string") ||
28
+ typeof key.expiresAt !== "number")
29
+ throw new Error("Invalid Gitdog project API key file.");
30
+ return {
31
+ version: 1,
32
+ apiKey: key.apiKey,
33
+ apiUrl: validateUrl(key.apiUrl, "Key API URL"),
34
+ httpUrl: validateUrl(key.httpUrl, "Key HTTP URL"),
35
+ keyId: key.keyId,
36
+ repositoryId: key.repositoryId,
37
+ name: key.name,
38
+ scopes: key.scopes,
39
+ expiresAt: key.expiresAt,
40
+ };
41
+ }
42
+ export async function saveProjectKey(path, key) {
43
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
44
+ const handle = await open(path, "wx", 0o600);
45
+ try {
46
+ await handle.writeFile(JSON.stringify(key, null, 2) + "\n");
47
+ }
48
+ catch (error) {
49
+ await handle.close();
50
+ await rm(path, { force: true });
51
+ throw error;
52
+ }
53
+ await handle.close();
54
+ }
55
+ export async function callWithProjectKey(apiUrl, keyFile, rawKey, kind, path, args, timeoutMs) {
56
+ const deadline = Date.now() + timeoutMs;
57
+ if (rawKey && keyFile)
58
+ throw new Error("Select GITDOG_API_KEY or a key file, not both.");
59
+ const credential = keyFile ? await readProjectKey(keyFile) : undefined;
60
+ if (credential && credential.apiUrl !== apiUrl)
61
+ throw new Error("This API key file belongs to another API URL. Select the matching --api-url.");
62
+ const key = credential?.apiKey ?? rawKey;
63
+ if (!key || !/^gdk_[a-f0-9]{64}$/.test(key))
64
+ throw new Error("Invalid Gitdog project API key.");
65
+ if (kind === "action")
66
+ throw new Error("This command requires an owner login. Project API keys cannot manage accounts, keys, billing or GitHub actions.");
67
+ const remaining = deadline - Date.now();
68
+ if (remaining <= 0)
69
+ throw new Error("Project API request timed out.");
70
+ const response = await fetch(`${credential?.httpUrl ?? projectHttpUrl(apiUrl)}/v1/cli`, {
71
+ method: "POST",
72
+ redirect: "error",
73
+ signal: AbortSignal.timeout(remaining),
74
+ headers: { "Content-Type": "application/json", "X-Gitdog-Api-Key": key },
75
+ body: JSON.stringify({ kind, path, args }),
76
+ });
77
+ let result;
78
+ try {
79
+ result = asRecord(await response.json());
80
+ }
81
+ catch {
82
+ throw new Error(`Project API returned HTTP ${response.status} without a JSON response.`);
83
+ }
84
+ if (!response.ok) {
85
+ const error = asRecord(result?.error);
86
+ throw new Error(typeof error?.message === "string"
87
+ ? error.message
88
+ : `Project API returned HTTP ${response.status}.`);
89
+ }
90
+ if (!result || !Object.hasOwn(result, "value"))
91
+ throw new Error("Invalid project API response.");
92
+ return result.value;
93
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { SessionStore } from "./storage.js";
2
+ import { type Ui } from "./ui.js";
3
+ export declare const CALLBACK_PATH = "/gitdog/callback";
4
+ export declare function openBrowser(url: string, signal?: AbortSignal): Promise<boolean>;
5
+ export declare function createLoginListener(timeoutMs?: number): Promise<{
6
+ redirectTo: string;
7
+ signal: AbortSignal;
8
+ code: Promise<string>;
9
+ close: () => Promise<void>;
10
+ }>;
11
+ export declare function validateAuthRedirect(value: string, apiUrl: string): void;
12
+ export declare function login(apiUrl: string, siteUrl: string, ui: Ui, noBrowser?: boolean): Promise<void>;
13
+ export declare function logout(ui: Ui, localOnly?: boolean, store?: SessionStore): Promise<void>;