gitdog 0.0.0-stage → 0.1.1
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 +76 -2
- package/dist/api-catalog.json +432 -0
- package/dist/api-keys.d.ts +17 -0
- package/dist/api-keys.js +137 -0
- package/dist/auth.d.ts +13 -0
- package/dist/auth.js +243 -0
- package/dist/catalog.d.ts +11 -0
- package/dist/catalog.js +8 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +31 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.js +109 -0
- package/dist/commands.d.ts +9 -0
- package/dist/commands.js +680 -0
- package/dist/projects.d.ts +55 -0
- package/dist/projects.js +355 -0
- package/dist/storage.d.ts +24 -0
- package/dist/storage.js +194 -0
- package/dist/ui.d.ts +19 -0
- package/dist/ui.js +181 -0
- package/package.json +54 -4
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>;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
import { httpClient, invoke } from "./client.js";
|
|
6
|
+
import { SessionStore, validTokens, tokenExpiresSoon } from "./storage.js";
|
|
7
|
+
import { asRecord } from "./ui.js";
|
|
8
|
+
const runFile = promisify(execFile);
|
|
9
|
+
export const CALLBACK_PATH = "/gitdog/callback";
|
|
10
|
+
export async function openBrowser(url, signal) {
|
|
11
|
+
signal?.throwIfAborted();
|
|
12
|
+
const parsed = new URL(url);
|
|
13
|
+
if (!["https:", "http:"].includes(parsed.protocol))
|
|
14
|
+
throw new Error("Invalid browser URL.");
|
|
15
|
+
try {
|
|
16
|
+
switch (process.platform) {
|
|
17
|
+
case "darwin":
|
|
18
|
+
await runFile("open", [url], { timeout: 10_000, signal });
|
|
19
|
+
break;
|
|
20
|
+
case "win32":
|
|
21
|
+
await runFile("rundll32.exe", ["url.dll,FileProtocolHandler", url], {
|
|
22
|
+
timeout: 10_000,
|
|
23
|
+
signal,
|
|
24
|
+
});
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
await runFile("xdg-open", [url], { timeout: 10_000, signal });
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
signal?.throwIfAborted();
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function createLoginListener(timeoutMs = 300_000) {
|
|
37
|
+
const cancellation = new AbortController();
|
|
38
|
+
const state = randomBytes(32).toString("hex");
|
|
39
|
+
let resolveCode;
|
|
40
|
+
let rejectCode;
|
|
41
|
+
const code = new Promise((resolve, reject) => {
|
|
42
|
+
resolveCode = resolve;
|
|
43
|
+
rejectCode = reject;
|
|
44
|
+
});
|
|
45
|
+
void code.catch(() => { });
|
|
46
|
+
let expectedHost = "";
|
|
47
|
+
let settled = false;
|
|
48
|
+
const finish = (error, value) => {
|
|
49
|
+
if (settled)
|
|
50
|
+
return;
|
|
51
|
+
settled = true;
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
if (error) {
|
|
54
|
+
cancellation.abort(error);
|
|
55
|
+
rejectCode(error);
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
resolveCode(value);
|
|
59
|
+
};
|
|
60
|
+
const server = createServer({ maxHeaderSize: 8192, requestTimeout: 10_000, headersTimeout: 5000 }, (request, response) => {
|
|
61
|
+
response.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
62
|
+
response.setHeader("Cache-Control", "no-store");
|
|
63
|
+
response.setHeader("Referrer-Policy", "no-referrer");
|
|
64
|
+
response.setHeader("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'");
|
|
65
|
+
let url;
|
|
66
|
+
try {
|
|
67
|
+
url = new URL(request.url ?? "/", `http://${expectedHost}`);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
response.writeHead(400).end("Invalid request URL.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (request.method !== "GET" ||
|
|
74
|
+
request.headers.host !== expectedHost ||
|
|
75
|
+
url.origin !== `http://${expectedHost}` ||
|
|
76
|
+
url.pathname !== CALLBACK_PATH) {
|
|
77
|
+
response.writeHead(404).end("Not found.");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (url.searchParams.getAll("state").length !== 1 ||
|
|
81
|
+
url.searchParams.get("state") !== state) {
|
|
82
|
+
response
|
|
83
|
+
.writeHead(400)
|
|
84
|
+
.end("Invalid sign-in state. Return to your terminal and try again.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (settled) {
|
|
88
|
+
response.writeHead(410).end("This sign-in has already completed.");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (url.searchParams.has("error")) {
|
|
92
|
+
response
|
|
93
|
+
.writeHead(400)
|
|
94
|
+
.end("Gitdog sign-in was not completed. Return to your terminal.");
|
|
95
|
+
finish(new Error("GitHub sign-in was declined or failed."));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
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
|
+
if (url.searchParams.getAll("code").length !== 1 ||
|
|
107
|
+
!value ||
|
|
108
|
+
value.length > 4096) {
|
|
109
|
+
response.writeHead(400).end("Missing or invalid sign-in code.");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
response.end("Gitdog received your sign-in. Return to your terminal to finish. You can close this tab.");
|
|
113
|
+
finish(undefined, value);
|
|
114
|
+
});
|
|
115
|
+
const timer = setTimeout(() => finish(new Error("Sign-in timed out. Run gitdog login to try again.")), timeoutMs);
|
|
116
|
+
try {
|
|
117
|
+
await new Promise((resolve, reject) => {
|
|
118
|
+
server.once("error", reject);
|
|
119
|
+
server.listen(0, "127.0.0.1", () => {
|
|
120
|
+
server.off("error", reject);
|
|
121
|
+
resolve();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
server.on("error", () => finish(new Error("The local sign-in listener failed.")));
|
|
130
|
+
const address = server.address();
|
|
131
|
+
if (!address || typeof address === "string")
|
|
132
|
+
throw new Error("Could not start the local sign-in listener.");
|
|
133
|
+
expectedHost = `127.0.0.1:${address.port}`;
|
|
134
|
+
const redirectTo = `http://${expectedHost}${CALLBACK_PATH}?state=${state}`;
|
|
135
|
+
const onInterrupt = () => {
|
|
136
|
+
const error = new Error("Sign-in cancelled.");
|
|
137
|
+
cancellation.abort(error);
|
|
138
|
+
finish(error);
|
|
139
|
+
};
|
|
140
|
+
process.once("SIGINT", onInterrupt);
|
|
141
|
+
return {
|
|
142
|
+
redirectTo,
|
|
143
|
+
signal: cancellation.signal,
|
|
144
|
+
code,
|
|
145
|
+
close: async () => {
|
|
146
|
+
finish(new Error("Sign-in cancelled."));
|
|
147
|
+
process.off("SIGINT", onInterrupt);
|
|
148
|
+
server.closeAllConnections();
|
|
149
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export function validateAuthRedirect(value, apiUrl) {
|
|
154
|
+
const url = new URL(value);
|
|
155
|
+
const api = new URL(apiUrl);
|
|
156
|
+
const siteOrigin = api.origin.replace(/\.convex\.cloud$/, ".convex.site");
|
|
157
|
+
if (![api.origin, siteOrigin].includes(url.origin) ||
|
|
158
|
+
url.pathname !== "/api/auth/signin/github" ||
|
|
159
|
+
url.username ||
|
|
160
|
+
url.password ||
|
|
161
|
+
url.hash) {
|
|
162
|
+
throw new Error("The server returned an unexpected sign-in URL.");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export async function login(apiUrl, siteUrl, ui, noBrowser = false) {
|
|
166
|
+
if (ui.json)
|
|
167
|
+
throw new Error("Browser login needs human interaction. Omit --json; use GITDOG_TOKEN for automation.");
|
|
168
|
+
ui.banner();
|
|
169
|
+
const listener = await createLoginListener();
|
|
170
|
+
const client = httpClient(apiUrl, 30_000, listener.signal);
|
|
171
|
+
try {
|
|
172
|
+
ui.info("1 / 2 Sign in with your GitHub account");
|
|
173
|
+
const result = asRecord(await invoke(client, "action", "auth:signIn", {
|
|
174
|
+
provider: "github",
|
|
175
|
+
params: { redirectTo: listener.redirectTo },
|
|
176
|
+
}));
|
|
177
|
+
listener.signal.throwIfAborted();
|
|
178
|
+
if (typeof result?.redirect !== "string" ||
|
|
179
|
+
typeof result.verifier !== "string")
|
|
180
|
+
throw new Error("The server did not start GitHub sign-in.");
|
|
181
|
+
validateAuthRedirect(result.redirect, apiUrl);
|
|
182
|
+
ui.info(`Open this URL in a browser on this computer:\n${result.redirect}\n`);
|
|
183
|
+
if (!noBrowser && !(await openBrowser(result.redirect, listener.signal)))
|
|
184
|
+
ui.info("Could not open your browser automatically. Use the URL above.");
|
|
185
|
+
ui.info("Waiting for GitHub authorization… (Ctrl+C to cancel)");
|
|
186
|
+
const code = await listener.code;
|
|
187
|
+
listener.signal.throwIfAborted();
|
|
188
|
+
ui.info("2 / 2 Securely save your Gitdog session");
|
|
189
|
+
let exchange;
|
|
190
|
+
try {
|
|
191
|
+
exchange = await invoke(client, "action", "auth:signIn", {
|
|
192
|
+
params: { code },
|
|
193
|
+
verifier: result.verifier,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
listener.signal.throwIfAborted();
|
|
198
|
+
throw new Error("Could not finish sign-in. Check that the CLI loopback callback is deployed, then run gitdog login again.");
|
|
199
|
+
}
|
|
200
|
+
const tokens = asRecord(exchange)?.tokens;
|
|
201
|
+
if (!validTokens(tokens))
|
|
202
|
+
throw new Error("Sign-in did not return a session. Run gitdog login again.");
|
|
203
|
+
client.setAuth(tokens.token);
|
|
204
|
+
const user = asRecord(await invoke(client, "query", "users/query:getCurrentUser"));
|
|
205
|
+
if (!user)
|
|
206
|
+
throw new Error("Gitdog could not verify the signed-in account.");
|
|
207
|
+
const store = new SessionStore();
|
|
208
|
+
await store.locked(() => store.save({ ...tokens, apiUrl, siteUrl }, listener.signal), undefined, listener.signal);
|
|
209
|
+
listener.signal.throwIfAborted();
|
|
210
|
+
ui.output(`✓ Signed in as ${String(user.name ?? "GitHub user")}.`);
|
|
211
|
+
if (user.requiresCurrentLegalAttestation)
|
|
212
|
+
ui.info(`Complete account consent at ${siteUrl}/legal/consent before running AI workflows.`);
|
|
213
|
+
ui.info("Next: gitdog init");
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
await listener.close();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
export async function logout(ui, localOnly = false, store = new SessionStore()) {
|
|
220
|
+
if (process.env.GITDOG_TOKEN && !localOnly)
|
|
221
|
+
throw new Error("GITDOG_TOKEN is set. Unset it to log out a saved session, or use --local.");
|
|
222
|
+
let revoked = false;
|
|
223
|
+
await store.locked(async () => {
|
|
224
|
+
let session = localOnly ? undefined : await store.read();
|
|
225
|
+
if (session) {
|
|
226
|
+
const client = httpClient(session.apiUrl, 30_000);
|
|
227
|
+
if (tokenExpiresSoon(session.token)) {
|
|
228
|
+
const result = asRecord(await invoke(client, "action", "auth:signIn", {
|
|
229
|
+
refreshToken: session.refreshToken,
|
|
230
|
+
}));
|
|
231
|
+
if (!validTokens(result?.tokens))
|
|
232
|
+
throw new Error("Could not refresh this session to revoke it. Run logout --local to remove only local credentials.");
|
|
233
|
+
session = { ...session, ...result.tokens };
|
|
234
|
+
await store.save(session);
|
|
235
|
+
}
|
|
236
|
+
client.setAuth(session.token);
|
|
237
|
+
await invoke(client, "action", "auth:signOut");
|
|
238
|
+
revoked = true;
|
|
239
|
+
}
|
|
240
|
+
await store.clear();
|
|
241
|
+
});
|
|
242
|
+
ui.output({ loggedOut: true, revoked });
|
|
243
|
+
}
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import catalog from "./api-catalog.json" with { type: "json" };
|
|
2
|
+
export const API_CATALOG = catalog;
|
|
3
|
+
export function publicOperation(path) {
|
|
4
|
+
const operation = API_CATALOG.find((entry) => entry.path === path);
|
|
5
|
+
if (!operation)
|
|
6
|
+
throw new Error("Unknown public operation. Run gitdog api list. Internal functions cannot be called.");
|
|
7
|
+
return operation;
|
|
8
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { CommanderError } from "commander";
|
|
3
|
+
import { createProgram, normalizeArgv } from "./commands.js";
|
|
4
|
+
import { clean } from "./ui.js";
|
|
5
|
+
const argv = normalizeArgv(process.argv.slice(2));
|
|
6
|
+
const json = argv.includes("--json");
|
|
7
|
+
const program = createProgram();
|
|
8
|
+
program.exitOverride();
|
|
9
|
+
program.configureOutput({
|
|
10
|
+
writeErr: (value) => {
|
|
11
|
+
process.stderr.write(clean(value));
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
if (json)
|
|
15
|
+
program.configureOutput({ writeErr: () => { }, outputError: () => { } });
|
|
16
|
+
try {
|
|
17
|
+
await program.parseAsync(argv, { from: "user" });
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error instanceof CommanderError && error.exitCode === 0)
|
|
21
|
+
process.exitCode = 0;
|
|
22
|
+
else {
|
|
23
|
+
const raw = error instanceof Error ? error.message : "Unexpected error.";
|
|
24
|
+
const message = clean(raw);
|
|
25
|
+
if (json)
|
|
26
|
+
process.stderr.write(`${JSON.stringify({ error: { message } })}\n`);
|
|
27
|
+
else if (!(error instanceof CommanderError))
|
|
28
|
+
process.stderr.write(`\nError: ${message}\n`);
|
|
29
|
+
process.exitCode = error instanceof CommanderError ? error.exitCode : 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import type { Value } from "convex/values";
|
|
3
|
+
import { SessionStore } from "./storage.js";
|
|
4
|
+
export type Kind = "query" | "mutation" | "action";
|
|
5
|
+
export type Args = Record<string, Value>;
|
|
6
|
+
export type CallOptions = {
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
};
|
|
9
|
+
export interface Api {
|
|
10
|
+
call<T = unknown>(kind: Kind, path: string, args?: Args, options?: CallOptions): Promise<T>;
|
|
11
|
+
}
|
|
12
|
+
export declare function httpClient(url: string, timeout?: number, signal?: AbortSignal): ConvexHttpClient;
|
|
13
|
+
export declare function invoke<T = unknown>(client: ConvexHttpClient, kind: Kind, path: string, args?: Args): Promise<T>;
|
|
14
|
+
export declare class GitdogApi implements Api {
|
|
15
|
+
readonly apiUrl: string;
|
|
16
|
+
readonly siteUrl: string;
|
|
17
|
+
private store;
|
|
18
|
+
private readonly keyFile?;
|
|
19
|
+
private client;
|
|
20
|
+
private session?;
|
|
21
|
+
private ready;
|
|
22
|
+
constructor(apiUrl: string, siteUrl: string, store?: SessionStore, keyFile?: string | undefined);
|
|
23
|
+
private authenticate;
|
|
24
|
+
call<T = unknown>(kind: Kind, path: string, args?: Args, options?: CallOptions): Promise<T>;
|
|
25
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import { makeFunctionReference } from "convex/server";
|
|
3
|
+
import { SessionStore, tokenExpiresSoon, validTokens, } from "./storage.js";
|
|
4
|
+
import { asRecord } from "./ui.js";
|
|
5
|
+
import { callWithProjectKey } from "./api-keys.js";
|
|
6
|
+
function remainingTimeout(deadline) {
|
|
7
|
+
if (deadline === undefined)
|
|
8
|
+
return 120_000;
|
|
9
|
+
const remaining = Math.ceil(deadline - Date.now());
|
|
10
|
+
if (remaining <= 0)
|
|
11
|
+
throw new Error("The request timed out.");
|
|
12
|
+
return remaining;
|
|
13
|
+
}
|
|
14
|
+
export function httpClient(url, timeout = 120_000, signal) {
|
|
15
|
+
return new ConvexHttpClient(url, {
|
|
16
|
+
logger: false,
|
|
17
|
+
skipConvexDeploymentUrlCheck: true,
|
|
18
|
+
fetch: (input, init) => fetch(input, {
|
|
19
|
+
...init,
|
|
20
|
+
redirect: "error",
|
|
21
|
+
signal: signal
|
|
22
|
+
? AbortSignal.any([signal, AbortSignal.timeout(timeout)])
|
|
23
|
+
: AbortSignal.timeout(timeout),
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function invoke(client, kind, path, args = {}) {
|
|
28
|
+
switch (kind) {
|
|
29
|
+
case "query":
|
|
30
|
+
return (await client.query(makeFunctionReference(path), args));
|
|
31
|
+
case "mutation":
|
|
32
|
+
return (await client.mutation(makeFunctionReference(path), args));
|
|
33
|
+
case "action":
|
|
34
|
+
return (await client.action(makeFunctionReference(path), args));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class GitdogApi {
|
|
38
|
+
apiUrl;
|
|
39
|
+
siteUrl;
|
|
40
|
+
store;
|
|
41
|
+
keyFile;
|
|
42
|
+
client;
|
|
43
|
+
session;
|
|
44
|
+
ready = false;
|
|
45
|
+
constructor(apiUrl, siteUrl, store = new SessionStore(), keyFile) {
|
|
46
|
+
this.apiUrl = apiUrl;
|
|
47
|
+
this.siteUrl = siteUrl;
|
|
48
|
+
this.store = store;
|
|
49
|
+
this.keyFile = keyFile;
|
|
50
|
+
this.client = httpClient(apiUrl);
|
|
51
|
+
}
|
|
52
|
+
async authenticate(deadline) {
|
|
53
|
+
if (this.ready && (!this.session || !tokenExpiresSoon(this.session.token)))
|
|
54
|
+
return;
|
|
55
|
+
const token = process.env.GITDOG_TOKEN;
|
|
56
|
+
if (token) {
|
|
57
|
+
this.client.setAuth(token);
|
|
58
|
+
this.ready = true;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const initial = await this.store.read();
|
|
62
|
+
if (!initial)
|
|
63
|
+
throw new Error("You are not logged in. Run gitdog login.");
|
|
64
|
+
if (initial.apiUrl !== this.apiUrl)
|
|
65
|
+
throw new Error("This session belongs to another API URL. Run gitdog login --api-url <url> first.");
|
|
66
|
+
this.session = initial;
|
|
67
|
+
if (tokenExpiresSoon(initial.token)) {
|
|
68
|
+
this.session = await this.store.locked(async () => {
|
|
69
|
+
const current = await this.store.read();
|
|
70
|
+
if (!current || current.apiUrl !== this.apiUrl)
|
|
71
|
+
throw new Error("Session changed. Run gitdog login again.");
|
|
72
|
+
if (!tokenExpiresSoon(current.token))
|
|
73
|
+
return current;
|
|
74
|
+
let result;
|
|
75
|
+
try {
|
|
76
|
+
result = await invoke(httpClient(this.apiUrl, remainingTimeout(deadline)), "action", "auth:signIn", {
|
|
77
|
+
refreshToken: current.refreshToken,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw new Error("Could not refresh your session. Check your connection or run gitdog login again.");
|
|
82
|
+
}
|
|
83
|
+
const tokens = asRecord(result)?.tokens;
|
|
84
|
+
if (!validTokens(tokens))
|
|
85
|
+
throw new Error("Your session expired. Run gitdog login again.");
|
|
86
|
+
const updated = { ...current, ...tokens };
|
|
87
|
+
await this.store.save(updated);
|
|
88
|
+
return updated;
|
|
89
|
+
}, deadline);
|
|
90
|
+
}
|
|
91
|
+
this.client.setAuth(this.session.token);
|
|
92
|
+
this.ready = true;
|
|
93
|
+
}
|
|
94
|
+
async call(kind, path, args = {}, options) {
|
|
95
|
+
const deadline = options?.timeoutMs === undefined
|
|
96
|
+
? undefined
|
|
97
|
+
: Date.now() + options.timeoutMs;
|
|
98
|
+
const keyFile = this.keyFile ?? process.env.GITDOG_API_KEY_FILE;
|
|
99
|
+
if (keyFile || process.env.GITDOG_API_KEY)
|
|
100
|
+
return callWithProjectKey(this.apiUrl, keyFile, process.env.GITDOG_API_KEY, kind, path, args, remainingTimeout(deadline));
|
|
101
|
+
await this.authenticate(deadline);
|
|
102
|
+
if (options?.timeoutMs !== undefined) {
|
|
103
|
+
const client = httpClient(this.apiUrl, remainingTimeout(deadline));
|
|
104
|
+
client.setAuth(process.env.GITDOG_TOKEN ?? this.session.token);
|
|
105
|
+
return invoke(client, kind, path, args);
|
|
106
|
+
}
|
|
107
|
+
return invoke(this.client, kind, path, args);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type Api, type Args } from "./client.js";
|
|
3
|
+
import { Ui } from "./ui.js";
|
|
4
|
+
export declare function jsonArguments(value: string): Promise<Args>;
|
|
5
|
+
export declare function createProgram(dependencies?: {
|
|
6
|
+
api?: Api;
|
|
7
|
+
ui?: Ui;
|
|
8
|
+
}): Command;
|
|
9
|
+
export declare function normalizeArgv(argv: string[]): string[];
|