gitdog 0.2.1 → 0.2.2
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 +2 -2
- package/dist/auth.d.ts +5 -1
- package/dist/auth.js +34 -16
- package/dist/storage.d.ts +1 -0
- package/dist/storage.js +1 -0
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<img src="https://raw.githubusercontent.com/hyodotdev/gitdog-cli/main/.github/assets/terminal.svg?v=0.2.
|
|
20
|
+
<img src="https://raw.githubusercontent.com/hyodotdev/gitdog-cli/main/.github/assets/terminal.svg?v=0.2.2" alt="Gitdog terminal: connect a project, configure automation and review pending actions" width="800">
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
Connect repositories, configure issue, discussion and PR automation, review
|
|
@@ -64,7 +64,7 @@ Use `npx gitdog@latest --help` to explore commands. Add `--json` for scripts.
|
|
|
64
64
|
|
|
65
65
|
```sh
|
|
66
66
|
npx gitdog@latest keys create --name ci
|
|
67
|
-
npx --yes gitdog@0.2.
|
|
67
|
+
npx --yes gitdog@0.2.2 --key-file /path/to/key.json projects list --json
|
|
68
68
|
npx gitdog@latest keys revoke KEY_ID --yes
|
|
69
69
|
```
|
|
70
70
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { SessionStore } from "./storage.js";
|
|
2
2
|
import { type Ui } from "./ui.js";
|
|
3
3
|
export declare const CALLBACK_PATH = "/gitdog/callback";
|
|
4
|
+
export declare const LOGIN_RESULT_PATH = "/cli/login";
|
|
5
|
+
type LoginOutcome = "success" | "failed";
|
|
4
6
|
export declare function openBrowser(url: string, signal?: AbortSignal): Promise<boolean>;
|
|
5
|
-
export declare function createLoginListener(timeoutMs?: number): Promise<{
|
|
7
|
+
export declare function createLoginListener(timeoutMs?: number, siteUrl?: string): Promise<{
|
|
6
8
|
redirectTo: string;
|
|
7
9
|
signal: AbortSignal;
|
|
8
10
|
code: Promise<string>;
|
|
11
|
+
complete: (outcome: LoginOutcome) => Promise<void>;
|
|
9
12
|
close: () => Promise<void>;
|
|
10
13
|
}>;
|
|
11
14
|
export declare function validateAuthRedirect(value: string, apiUrl: string): void;
|
|
12
15
|
export declare function login(apiUrl: string, siteUrl: string, ui: Ui, noBrowser?: boolean): Promise<void>;
|
|
13
16
|
export declare function logout(ui: Ui, localOnly?: boolean, store?: SessionStore): Promise<void>;
|
|
17
|
+
export {};
|
package/dist/auth.js
CHANGED
|
@@ -3,10 +3,23 @@ import { promisify } from "node:util";
|
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
5
|
import { httpClient, invoke } from "./client.js";
|
|
6
|
-
import { SessionStore, validTokens, tokenExpiresSoon } from "./storage.js";
|
|
6
|
+
import { DEFAULT_API_URL, DEFAULT_AUTH_SITE_URL, DEFAULT_SITE_URL, SessionStore, validTokens, tokenExpiresSoon, } from "./storage.js";
|
|
7
7
|
import { asRecord } from "./ui.js";
|
|
8
8
|
const runFile = promisify(execFile);
|
|
9
9
|
export const CALLBACK_PATH = "/gitdog/callback";
|
|
10
|
+
export const LOGIN_RESULT_PATH = "/cli/login";
|
|
11
|
+
function sendToLoginResult(response, siteUrl, outcome) {
|
|
12
|
+
if (response.destroyed)
|
|
13
|
+
return Promise.resolve();
|
|
14
|
+
const location = new URL(LOGIN_RESULT_PATH, siteUrl);
|
|
15
|
+
location.searchParams.set("status", outcome);
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
response.once("close", resolve);
|
|
18
|
+
response
|
|
19
|
+
.writeHead(302, { Location: location.href, Connection: "close" })
|
|
20
|
+
.end();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
10
23
|
export async function openBrowser(url, signal) {
|
|
11
24
|
signal?.throwIfAborted();
|
|
12
25
|
const parsed = new URL(url);
|
|
@@ -33,7 +46,7 @@ export async function openBrowser(url, signal) {
|
|
|
33
46
|
return false;
|
|
34
47
|
}
|
|
35
48
|
}
|
|
36
|
-
export async function createLoginListener(timeoutMs = 300_000) {
|
|
49
|
+
export async function createLoginListener(timeoutMs = 300_000, siteUrl = DEFAULT_SITE_URL) {
|
|
37
50
|
const cancellation = new AbortController();
|
|
38
51
|
const state = randomBytes(32).toString("hex");
|
|
39
52
|
let resolveCode;
|
|
@@ -45,6 +58,14 @@ export async function createLoginListener(timeoutMs = 300_000) {
|
|
|
45
58
|
void code.catch(() => { });
|
|
46
59
|
let expectedHost = "";
|
|
47
60
|
let settled = false;
|
|
61
|
+
// Held open so the browser reports the real result, not just a received code.
|
|
62
|
+
let pendingResponse;
|
|
63
|
+
const complete = async (outcome) => {
|
|
64
|
+
const response = pendingResponse;
|
|
65
|
+
pendingResponse = undefined;
|
|
66
|
+
if (response)
|
|
67
|
+
await sendToLoginResult(response, siteUrl, outcome);
|
|
68
|
+
};
|
|
48
69
|
const finish = (error, value) => {
|
|
49
70
|
if (settled)
|
|
50
71
|
return;
|
|
@@ -88,28 +109,19 @@ export async function createLoginListener(timeoutMs = 300_000) {
|
|
|
88
109
|
response.writeHead(410).end("This sign-in has already completed.");
|
|
89
110
|
return;
|
|
90
111
|
}
|
|
91
|
-
if (url.searchParams.has("error")) {
|
|
92
|
-
response
|
|
93
|
-
.writeHead(400)
|
|
94
|
-
.end("Gitdog sign-in was not completed. Return to your terminal.");
|
|
112
|
+
if (url.searchParams.has("error") || !url.searchParams.has("code")) {
|
|
113
|
+
void sendToLoginResult(response, siteUrl, "failed");
|
|
95
114
|
finish(new Error("GitHub sign-in was declined or failed."));
|
|
96
115
|
return;
|
|
97
116
|
}
|
|
98
117
|
const value = url.searchParams.get("code");
|
|
99
|
-
if (!url.searchParams.has("code")) {
|
|
100
|
-
response
|
|
101
|
-
.writeHead(400)
|
|
102
|
-
.end("Gitdog sign-in was not completed. Return to your terminal.");
|
|
103
|
-
finish(new Error("GitHub sign-in was declined or failed."));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
118
|
if (url.searchParams.getAll("code").length !== 1 ||
|
|
107
119
|
!value ||
|
|
108
120
|
value.length > 4096) {
|
|
109
121
|
response.writeHead(400).end("Missing or invalid sign-in code.");
|
|
110
122
|
return;
|
|
111
123
|
}
|
|
112
|
-
|
|
124
|
+
pendingResponse = response;
|
|
113
125
|
finish(undefined, value);
|
|
114
126
|
});
|
|
115
127
|
const timer = setTimeout(() => finish(new Error("Sign-in timed out. Run npx gitdog@latest login to try again.")), timeoutMs);
|
|
@@ -142,8 +154,10 @@ export async function createLoginListener(timeoutMs = 300_000) {
|
|
|
142
154
|
redirectTo,
|
|
143
155
|
signal: cancellation.signal,
|
|
144
156
|
code,
|
|
157
|
+
complete,
|
|
145
158
|
close: async () => {
|
|
146
159
|
finish(new Error("Sign-in cancelled."));
|
|
160
|
+
await complete("failed");
|
|
147
161
|
process.off("SIGINT", onInterrupt);
|
|
148
162
|
server.closeAllConnections();
|
|
149
163
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
@@ -154,7 +168,10 @@ export function validateAuthRedirect(value, apiUrl) {
|
|
|
154
168
|
const url = new URL(value);
|
|
155
169
|
const api = new URL(apiUrl);
|
|
156
170
|
const siteOrigin = api.origin.replace(/\.convex\.cloud$/, ".convex.site");
|
|
157
|
-
|
|
171
|
+
const allowedOrigins = [api.origin, siteOrigin];
|
|
172
|
+
if (api.origin === new URL(DEFAULT_API_URL).origin)
|
|
173
|
+
allowedOrigins.push(new URL(DEFAULT_AUTH_SITE_URL).origin);
|
|
174
|
+
if (!allowedOrigins.includes(url.origin) ||
|
|
158
175
|
url.pathname !== "/api/auth/signin/github" ||
|
|
159
176
|
url.username ||
|
|
160
177
|
url.password ||
|
|
@@ -166,7 +183,7 @@ export async function login(apiUrl, siteUrl, ui, noBrowser = false) {
|
|
|
166
183
|
if (ui.json)
|
|
167
184
|
throw new Error("Browser login needs human interaction. Omit --json; use GITDOG_TOKEN for automation.");
|
|
168
185
|
ui.banner();
|
|
169
|
-
const listener = await createLoginListener();
|
|
186
|
+
const listener = await createLoginListener(undefined, siteUrl);
|
|
170
187
|
const client = httpClient(apiUrl, 30_000, listener.signal);
|
|
171
188
|
try {
|
|
172
189
|
ui.info("1 / 2 Sign in with your GitHub account");
|
|
@@ -207,6 +224,7 @@ export async function login(apiUrl, siteUrl, ui, noBrowser = false) {
|
|
|
207
224
|
const store = new SessionStore();
|
|
208
225
|
await store.locked(() => store.save({ ...tokens, apiUrl, siteUrl }, listener.signal), undefined, listener.signal);
|
|
209
226
|
listener.signal.throwIfAborted();
|
|
227
|
+
await listener.complete("success");
|
|
210
228
|
ui.output(`✓ Signed in as ${String(user.name ?? "GitHub user")}.`);
|
|
211
229
|
if (user.requiresCurrentLegalAttestation)
|
|
212
230
|
ui.info(`Complete account consent at ${siteUrl}/legal/consent before running AI workflows.`);
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const DEFAULT_API_URL = "https://calm-hippopotamus-56.convex.cloud";
|
|
2
2
|
export declare const DEFAULT_SITE_URL = "https://gitdog.dev";
|
|
3
|
+
export declare const DEFAULT_AUTH_SITE_URL = "https://auth.gitdog.dev";
|
|
3
4
|
export type Tokens = {
|
|
4
5
|
token: string;
|
|
5
6
|
refreshToken: string;
|
package/dist/storage.js
CHANGED
|
@@ -5,6 +5,7 @@ import { randomUUID } from "node:crypto";
|
|
|
5
5
|
import { asRecord } from "./ui.js";
|
|
6
6
|
export const DEFAULT_API_URL = "https://calm-hippopotamus-56.convex.cloud";
|
|
7
7
|
export const DEFAULT_SITE_URL = "https://gitdog.dev";
|
|
8
|
+
export const DEFAULT_AUTH_SITE_URL = "https://auth.gitdog.dev";
|
|
8
9
|
export function configDirectory() {
|
|
9
10
|
return (process.env.GITDOG_CONFIG_DIR ||
|
|
10
11
|
join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "gitdog"));
|
package/dist/ui.d.ts
CHANGED
package/dist/ui.js
CHANGED