@autohq/cli 0.1.184 → 0.1.186
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 +12 -15
- package/dist/agent-bridge.js +1 -1
- package/dist/index.js +1173 -1043
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,899 +9,6 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/lib/api/base-url.ts
|
|
13
|
-
function resolveApiBaseUrl(input = {}) {
|
|
14
|
-
const explicitValues = Array.isArray(input.explicit) ? input.explicit : [input.explicit];
|
|
15
|
-
const defaultUrl = input.defaultUrl === null ? void 0 : input.defaultUrl ?? DEFAULT_API_BASE_URL;
|
|
16
|
-
for (const value of [
|
|
17
|
-
...explicitValues,
|
|
18
|
-
input.env?.AUTO_API_BASE_URL,
|
|
19
|
-
input.configServerUrl,
|
|
20
|
-
defaultUrl
|
|
21
|
-
]) {
|
|
22
|
-
const normalized = normalizeApiBaseUrl(value);
|
|
23
|
-
if (normalized) return normalized;
|
|
24
|
-
}
|
|
25
|
-
return void 0;
|
|
26
|
-
}
|
|
27
|
-
function normalizeApiBaseUrl(value) {
|
|
28
|
-
const trimmed = value?.trim();
|
|
29
|
-
if (!trimmed) return void 0;
|
|
30
|
-
return trimmed.replace(/\/+$/, "");
|
|
31
|
-
}
|
|
32
|
-
var DEFAULT_API_BASE_URL;
|
|
33
|
-
var init_base_url = __esm({
|
|
34
|
-
"src/lib/api/base-url.ts"() {
|
|
35
|
-
"use strict";
|
|
36
|
-
DEFAULT_API_BASE_URL = "https://www.auto.sh";
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// src/lib/api/http.ts
|
|
41
|
-
async function postJson(fetchImpl, url2, body) {
|
|
42
|
-
const response = await fetchImpl(url2, {
|
|
43
|
-
method: "POST",
|
|
44
|
-
headers: { "content-type": "application/json" },
|
|
45
|
-
body: JSON.stringify(body)
|
|
46
|
-
});
|
|
47
|
-
if (!response.ok) {
|
|
48
|
-
const errorBody = await response.json().catch(() => ({}));
|
|
49
|
-
throw new Error(
|
|
50
|
-
errorBody.error ?? `Request failed with status ${response.status}`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
return response.json();
|
|
54
|
-
}
|
|
55
|
-
var init_http = __esm({
|
|
56
|
-
"src/lib/api/http.ts"() {
|
|
57
|
-
"use strict";
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// src/lib/auth/tokens.ts
|
|
62
|
-
function accessTokenExpiresAt(token2) {
|
|
63
|
-
if (!token2.access_token) {
|
|
64
|
-
throw new Error("Token response did not include an access token.");
|
|
65
|
-
}
|
|
66
|
-
return new Date(Date.now() + token2.expires_in * 1e3).toISOString();
|
|
67
|
-
}
|
|
68
|
-
function hasUsableAccessToken(config2, project) {
|
|
69
|
-
if (!config2.accessToken || !config2.accessTokenExpiresAt) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
const expiresAt = Date.parse(config2.accessTokenExpiresAt);
|
|
73
|
-
return config2.accessTokenOrganizationId === project.organizationId && config2.accessTokenProjectId === project.projectId && Number.isFinite(expiresAt) && expiresAt - ACCESS_TOKEN_REFRESH_SKEW_MS > Date.now();
|
|
74
|
-
}
|
|
75
|
-
var ACCESS_TOKEN_REFRESH_SKEW_MS;
|
|
76
|
-
var init_tokens = __esm({
|
|
77
|
-
"src/lib/auth/tokens.ts"() {
|
|
78
|
-
"use strict";
|
|
79
|
-
ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// src/lib/browser.ts
|
|
84
|
-
import { spawn } from "child_process";
|
|
85
|
-
function openBrowser(url2) {
|
|
86
|
-
if (!shouldOpenBrowser(process.env)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const child = spawn(openCommand(), openArgs(url2), {
|
|
90
|
-
detached: true,
|
|
91
|
-
stdio: "ignore"
|
|
92
|
-
});
|
|
93
|
-
child.unref();
|
|
94
|
-
}
|
|
95
|
-
function shouldOpenBrowser(env) {
|
|
96
|
-
if (env.AUTO_ALLOW_BROWSER_OPEN_IN_TESTS === "1") {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return !env.NODE_TEST_CONTEXT;
|
|
100
|
-
}
|
|
101
|
-
function openCommand() {
|
|
102
|
-
switch (process.platform) {
|
|
103
|
-
case "darwin":
|
|
104
|
-
return "open";
|
|
105
|
-
case "win32":
|
|
106
|
-
return "cmd";
|
|
107
|
-
default:
|
|
108
|
-
return "xdg-open";
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function openArgs(url2) {
|
|
112
|
-
return process.platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
113
|
-
}
|
|
114
|
-
var init_browser = __esm({
|
|
115
|
-
"src/lib/browser.ts"() {
|
|
116
|
-
"use strict";
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// src/lib/config/path.ts
|
|
121
|
-
import { createHash } from "crypto";
|
|
122
|
-
import { homedir } from "os";
|
|
123
|
-
import { basename, dirname, join, normalize } from "path";
|
|
124
|
-
function defaultConfigPath() {
|
|
125
|
-
return process.env.AUTO_CLI_CONFIG ?? join(homedir(), ".auto", "config.yaml");
|
|
126
|
-
}
|
|
127
|
-
function isProfilePath(path2) {
|
|
128
|
-
return basename(dirname(path2)) === PROFILES_DIR_NAME;
|
|
129
|
-
}
|
|
130
|
-
function profilesDir(configPath = defaultConfigPath()) {
|
|
131
|
-
if (isProfilePath(configPath)) return dirname(configPath);
|
|
132
|
-
return normalize(join(configPath, "..", PROFILES_DIR_NAME));
|
|
133
|
-
}
|
|
134
|
-
function profileFilePath(configPath, name) {
|
|
135
|
-
return join(profilesDir(configPath), `${name}.yaml`);
|
|
136
|
-
}
|
|
137
|
-
function profileNameFromPath(path2) {
|
|
138
|
-
return basename(path2).replace(/\.yaml$/, "");
|
|
139
|
-
}
|
|
140
|
-
function assertValidProfileName(name) {
|
|
141
|
-
if (!PROFILE_NAME_PATTERN.test(name)) {
|
|
142
|
-
throw new Error(
|
|
143
|
-
`Invalid profile name "${name}". Profile names use lowercase letters, digits, dots, dashes, and underscores.`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
return name;
|
|
147
|
-
}
|
|
148
|
-
function derivedProfileName(input) {
|
|
149
|
-
const key = `${input.userEmail.toLowerCase()}
|
|
150
|
-
${serverHost(input.serverUrl)}`;
|
|
151
|
-
const hash2 = createHash("sha256").update(key).digest("hex").slice(0, 8);
|
|
152
|
-
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}`;
|
|
153
|
-
}
|
|
154
|
-
function serverHost(serverUrl) {
|
|
155
|
-
try {
|
|
156
|
-
return new URL(serverUrl).host;
|
|
157
|
-
} catch {
|
|
158
|
-
return serverUrl;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function slug(value) {
|
|
162
|
-
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "_");
|
|
163
|
-
}
|
|
164
|
-
var PROFILES_DIR_NAME, PROFILE_NAME_PATTERN;
|
|
165
|
-
var init_path = __esm({
|
|
166
|
-
"src/lib/config/path.ts"() {
|
|
167
|
-
"use strict";
|
|
168
|
-
PROFILES_DIR_NAME = "profiles";
|
|
169
|
-
PROFILE_NAME_PATTERN = /^[a-z0-9._-]+$/;
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// src/lib/config/file.ts
|
|
174
|
-
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
175
|
-
import { dirname as dirname2 } from "path";
|
|
176
|
-
function readConfig(path2 = defaultConfigPath()) {
|
|
177
|
-
const profilePath = activeProfilePath(path2);
|
|
178
|
-
return profilePath ? readProfileFile(profilePath) : {};
|
|
179
|
-
}
|
|
180
|
-
function currentProfileName(path2 = defaultConfigPath()) {
|
|
181
|
-
if (isProfilePath(path2)) return profileNameFromPath(path2);
|
|
182
|
-
return readPointerFile(path2);
|
|
183
|
-
}
|
|
184
|
-
function writeConfig(config2, path2 = defaultConfigPath()) {
|
|
185
|
-
const profilePath = activeProfilePath(path2);
|
|
186
|
-
if (!profilePath) {
|
|
187
|
-
throw new Error("Not logged in. Session `auto auth login` first.");
|
|
188
|
-
}
|
|
189
|
-
writeProfileFile(config2, profilePath);
|
|
190
|
-
}
|
|
191
|
-
function saveProfile(input) {
|
|
192
|
-
const configPath = input.configPath ?? defaultConfigPath();
|
|
193
|
-
const name = resolveProfileName(input);
|
|
194
|
-
writeProfileFile(input.config, profileFilePath(configPath, name));
|
|
195
|
-
return name;
|
|
196
|
-
}
|
|
197
|
-
function setCurrentProfile(name, path2 = defaultConfigPath()) {
|
|
198
|
-
if (isProfilePath(path2)) {
|
|
199
|
-
throw new Error(
|
|
200
|
-
"Cannot change the active profile while pinned to a profile."
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
writeFile(`${CURRENT_PROFILE_KEY}: ${assertValidProfileName(name)}
|
|
204
|
-
`, path2);
|
|
205
|
-
}
|
|
206
|
-
function clearCurrentProfile(path2 = defaultConfigPath()) {
|
|
207
|
-
if (isProfilePath(path2)) {
|
|
208
|
-
throw new Error(
|
|
209
|
-
"Cannot change the active profile while pinned to a profile."
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
writeFile("", path2);
|
|
213
|
-
}
|
|
214
|
-
function activeProfilePath(path2) {
|
|
215
|
-
if (isProfilePath(path2)) return path2;
|
|
216
|
-
const currentProfile = readPointerFile(path2);
|
|
217
|
-
return currentProfile ? profileFilePath(path2, currentProfile) : void 0;
|
|
218
|
-
}
|
|
219
|
-
function resolveProfileName(input) {
|
|
220
|
-
if (input.name !== void 0) return assertValidProfileName(input.name);
|
|
221
|
-
if (!input.config.userEmail || !input.config.serverUrl) {
|
|
222
|
-
throw new Error(
|
|
223
|
-
"A profile name is required for a config without a signed-in account."
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
return derivedProfileName({
|
|
227
|
-
userEmail: input.config.userEmail,
|
|
228
|
-
serverUrl: input.config.serverUrl
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
function readPointerFile(path2) {
|
|
232
|
-
for (const [key, value] of readKeyValueLines(path2)) {
|
|
233
|
-
if (key === CURRENT_PROFILE_KEY) return value || void 0;
|
|
234
|
-
}
|
|
235
|
-
return void 0;
|
|
236
|
-
}
|
|
237
|
-
function readProfileFile(path2) {
|
|
238
|
-
const config2 = {};
|
|
239
|
-
for (const [key, value] of readKeyValueLines(path2)) {
|
|
240
|
-
const known = CONFIG_KEYS.find((candidate) => candidate === key);
|
|
241
|
-
if (known) config2[known] = value;
|
|
242
|
-
}
|
|
243
|
-
return config2;
|
|
244
|
-
}
|
|
245
|
-
function readKeyValueLines(path2) {
|
|
246
|
-
let text;
|
|
247
|
-
try {
|
|
248
|
-
text = readFileSync(path2, "utf8");
|
|
249
|
-
} catch (err) {
|
|
250
|
-
if (err.code === "ENOENT") return [];
|
|
251
|
-
throw err;
|
|
252
|
-
}
|
|
253
|
-
return text.split(/\r?\n/).map((line) => /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line.trim())).filter((match) => match !== null).map((match) => [match[1] ?? "", match[2] ?? ""]);
|
|
254
|
-
}
|
|
255
|
-
function writeProfileFile(config2, path2) {
|
|
256
|
-
const lines = CONFIG_KEYS.filter((key) => config2[key]).map(
|
|
257
|
-
(key) => `${key}: ${config2[key]}`
|
|
258
|
-
);
|
|
259
|
-
writeFile(`${lines.join("\n")}
|
|
260
|
-
`, path2);
|
|
261
|
-
}
|
|
262
|
-
function writeFile(content, path2) {
|
|
263
|
-
mkdirSync(dirname2(path2), { recursive: true });
|
|
264
|
-
writeFileSync(path2, content, { encoding: "utf8", mode: 384 });
|
|
265
|
-
chmodSync(path2, 384);
|
|
266
|
-
}
|
|
267
|
-
var CONFIG_KEYS, CURRENT_PROFILE_KEY;
|
|
268
|
-
var init_file = __esm({
|
|
269
|
-
"src/lib/config/file.ts"() {
|
|
270
|
-
"use strict";
|
|
271
|
-
init_path();
|
|
272
|
-
CONFIG_KEYS = [
|
|
273
|
-
"serverUrl",
|
|
274
|
-
"userId",
|
|
275
|
-
"userEmail",
|
|
276
|
-
"organizationId",
|
|
277
|
-
"projectId",
|
|
278
|
-
"refreshToken",
|
|
279
|
-
"accessToken",
|
|
280
|
-
"accessTokenExpiresAt",
|
|
281
|
-
"accessTokenOrganizationId",
|
|
282
|
-
"accessTokenProjectId"
|
|
283
|
-
];
|
|
284
|
-
CURRENT_PROFILE_KEY = "currentProfile";
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// src/lib/config/profiles.ts
|
|
289
|
-
import { readdirSync } from "fs";
|
|
290
|
-
import { join as join2 } from "path";
|
|
291
|
-
function listProfiles(configPath = defaultConfigPath()) {
|
|
292
|
-
const dir = profilesDir(configPath);
|
|
293
|
-
let entries;
|
|
294
|
-
try {
|
|
295
|
-
entries = readdirSync(dir);
|
|
296
|
-
} catch (err) {
|
|
297
|
-
if (err.code === "ENOENT") return [];
|
|
298
|
-
throw err;
|
|
299
|
-
}
|
|
300
|
-
return entries.filter((entry) => entry.endsWith(".yaml")).sort().map((entry) => {
|
|
301
|
-
const path2 = join2(dir, entry);
|
|
302
|
-
return {
|
|
303
|
-
name: profileNameFromPath(path2),
|
|
304
|
-
path: path2,
|
|
305
|
-
config: readConfig(path2)
|
|
306
|
-
};
|
|
307
|
-
}).filter((profile) => profile.config.userEmail);
|
|
308
|
-
}
|
|
309
|
-
function findAccountProfile(input) {
|
|
310
|
-
const candidates = listProfiles(input.configPath).map((profile) => profile.config).filter((config2) => config2.serverUrl === input.serverUrl);
|
|
311
|
-
return candidates.find(
|
|
312
|
-
(config2) => input.userId && config2.userId === input.userId
|
|
313
|
-
) ?? // Email is only a fallback for when a user id is missing on either side;
|
|
314
|
-
// it must never override a known user-id mismatch (emails can be
|
|
315
|
-
// reassigned to a different user).
|
|
316
|
-
candidates.find(
|
|
317
|
-
(config2) => input.userEmail && config2.userEmail?.toLowerCase() === input.userEmail.toLowerCase() && !(input.userId && config2.userId && config2.userId !== input.userId)
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
var init_profiles = __esm({
|
|
321
|
-
"src/lib/config/profiles.ts"() {
|
|
322
|
-
"use strict";
|
|
323
|
-
init_file();
|
|
324
|
-
init_path();
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// src/lib/oauth/loopback.ts
|
|
329
|
-
import { createServer } from "http";
|
|
330
|
-
async function createOAuthLoopbackCallback(input) {
|
|
331
|
-
const path2 = input?.path ?? "/callback";
|
|
332
|
-
const successHtml = input?.successHtml ?? renderOAuthLoopbackPage({
|
|
333
|
-
status: "success",
|
|
334
|
-
eyebrow: "Auto",
|
|
335
|
-
title: "Authorization complete",
|
|
336
|
-
message: "You can close this window and return to your terminal."
|
|
337
|
-
});
|
|
338
|
-
const failureHtml = input?.failureHtml ?? renderOAuthLoopbackPage({
|
|
339
|
-
status: "failure",
|
|
340
|
-
eyebrow: "Auto",
|
|
341
|
-
title: "Authorization failed",
|
|
342
|
-
message: "Return to your terminal to see the error details."
|
|
343
|
-
});
|
|
344
|
-
let resolveResult;
|
|
345
|
-
let rejectResult;
|
|
346
|
-
const result = new Promise((resolve4, reject) => {
|
|
347
|
-
resolveResult = resolve4;
|
|
348
|
-
rejectResult = reject;
|
|
349
|
-
});
|
|
350
|
-
const server = createServer((request, response) => {
|
|
351
|
-
const url2 = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
352
|
-
if (url2.pathname !== path2) {
|
|
353
|
-
response.writeHead(404).end("Not found");
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
const error51 = url2.searchParams.get("error");
|
|
357
|
-
if (error51) {
|
|
358
|
-
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
359
|
-
endThenSettle(
|
|
360
|
-
response,
|
|
361
|
-
resolveHtml(failureHtml, { code: "" }),
|
|
362
|
-
() => rejectResult(new Error(error51))
|
|
363
|
-
);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
const code = url2.searchParams.get("code") ?? "";
|
|
367
|
-
const sensitiveActionToken = url2.searchParams.get("sensitive_action_token") ?? void 0;
|
|
368
|
-
if (!code && !sensitiveActionToken) {
|
|
369
|
-
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
370
|
-
endThenSettle(
|
|
371
|
-
response,
|
|
372
|
-
renderOAuthLoopbackPage({
|
|
373
|
-
status: "failure",
|
|
374
|
-
eyebrow: "Auto",
|
|
375
|
-
title: "Missing authorization result",
|
|
376
|
-
message: "Return to your terminal to retry the authorization flow."
|
|
377
|
-
}),
|
|
378
|
-
() => rejectResult(new Error("Missing authorization result"))
|
|
379
|
-
);
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
const result2 = {
|
|
383
|
-
code,
|
|
384
|
-
sensitiveActionToken,
|
|
385
|
-
organizationName: url2.searchParams.get("organization_name") ?? void 0,
|
|
386
|
-
projectName: url2.searchParams.get("project_name") ?? void 0,
|
|
387
|
-
state: url2.searchParams.get("state") ?? void 0
|
|
388
|
-
};
|
|
389
|
-
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
390
|
-
endThenSettle(
|
|
391
|
-
response,
|
|
392
|
-
resolveHtml(successHtml, result2),
|
|
393
|
-
() => resolveResult(result2)
|
|
394
|
-
);
|
|
395
|
-
});
|
|
396
|
-
await listenOnPreferredPort(
|
|
397
|
-
server,
|
|
398
|
-
input?.preferredPort ?? DEFAULT_CALLBACK_PORT
|
|
399
|
-
);
|
|
400
|
-
const address = server.address();
|
|
401
|
-
return {
|
|
402
|
-
close: () => {
|
|
403
|
-
server.close();
|
|
404
|
-
server.closeAllConnections();
|
|
405
|
-
},
|
|
406
|
-
redirectUri: `http://127.0.0.1:${address.port}${path2}`,
|
|
407
|
-
result
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
function renderOAuthLoopbackPage(input) {
|
|
411
|
-
const detailRows = (input.details ?? []).filter((detail) => detail.value).map(
|
|
412
|
-
(detail) => `<div class="detail">
|
|
413
|
-
<dt>${escapeHtml(detail.label)}</dt>
|
|
414
|
-
<dd>${escapeHtml(detail.value ?? "")}</dd>
|
|
415
|
-
</div>`
|
|
416
|
-
).join("");
|
|
417
|
-
const hasDetails = detailRows.length > 0;
|
|
418
|
-
const statusLabel = input.status === "success" ? "Authorization received" : "Authorization failed";
|
|
419
|
-
return `<!doctype html>
|
|
420
|
-
<html lang="en">
|
|
421
|
-
<head>
|
|
422
|
-
<meta charset="utf-8">
|
|
423
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
424
|
-
<title>${escapeHtml(input.title)} | Auto</title>
|
|
425
|
-
<style>
|
|
426
|
-
:root {
|
|
427
|
-
color-scheme: light dark;
|
|
428
|
-
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
429
|
-
background: #f6f7f2;
|
|
430
|
-
color: #171814;
|
|
431
|
-
}
|
|
432
|
-
* {
|
|
433
|
-
box-sizing: border-box;
|
|
434
|
-
}
|
|
435
|
-
body {
|
|
436
|
-
min-height: 100vh;
|
|
437
|
-
margin: 0;
|
|
438
|
-
display: grid;
|
|
439
|
-
place-items: center;
|
|
440
|
-
padding: 32px;
|
|
441
|
-
background:
|
|
442
|
-
linear-gradient(135deg, rgba(35, 108, 96, 0.18), transparent 34%),
|
|
443
|
-
linear-gradient(315deg, rgba(194, 90, 62, 0.16), transparent 38%),
|
|
444
|
-
#f6f7f2;
|
|
445
|
-
}
|
|
446
|
-
main {
|
|
447
|
-
width: min(680px, 100%);
|
|
448
|
-
border: 1px solid rgba(23, 24, 20, 0.12);
|
|
449
|
-
border-radius: 8px;
|
|
450
|
-
background: rgba(255, 255, 252, 0.88);
|
|
451
|
-
box-shadow: 0 24px 70px rgba(23, 24, 20, 0.14);
|
|
452
|
-
padding: 32px;
|
|
453
|
-
}
|
|
454
|
-
.mark {
|
|
455
|
-
display: inline-flex;
|
|
456
|
-
align-items: center;
|
|
457
|
-
gap: 10px;
|
|
458
|
-
margin-bottom: 28px;
|
|
459
|
-
color: #4d554b;
|
|
460
|
-
font-size: 13px;
|
|
461
|
-
font-weight: 700;
|
|
462
|
-
letter-spacing: 0;
|
|
463
|
-
text-transform: uppercase;
|
|
464
|
-
}
|
|
465
|
-
.dot {
|
|
466
|
-
width: 12px;
|
|
467
|
-
height: 12px;
|
|
468
|
-
border-radius: 999px;
|
|
469
|
-
background: ${input.status === "success" ? "#22863a" : "#b42318"};
|
|
470
|
-
box-shadow: 0 0 0 6px ${input.status === "success" ? "rgba(34, 134, 58, 0.14)" : "rgba(180, 35, 24, 0.14)"};
|
|
471
|
-
}
|
|
472
|
-
h1 {
|
|
473
|
-
margin: 0;
|
|
474
|
-
max-width: 12ch;
|
|
475
|
-
font-size: 44px;
|
|
476
|
-
line-height: 1;
|
|
477
|
-
letter-spacing: 0;
|
|
478
|
-
}
|
|
479
|
-
p {
|
|
480
|
-
margin: 18px 0 0;
|
|
481
|
-
max-width: 58ch;
|
|
482
|
-
color: #4d554b;
|
|
483
|
-
font-size: 16px;
|
|
484
|
-
line-height: 1.55;
|
|
485
|
-
}
|
|
486
|
-
dl {
|
|
487
|
-
display: grid;
|
|
488
|
-
gap: 0;
|
|
489
|
-
margin: 28px 0 0;
|
|
490
|
-
border-block: 1px solid rgba(23, 24, 20, 0.13);
|
|
491
|
-
}
|
|
492
|
-
.detail {
|
|
493
|
-
display: grid;
|
|
494
|
-
grid-template-columns: minmax(118px, 0.42fr) 1fr;
|
|
495
|
-
gap: 18px;
|
|
496
|
-
padding: 14px 0;
|
|
497
|
-
border-bottom: 1px solid rgba(23, 24, 20, 0.09);
|
|
498
|
-
}
|
|
499
|
-
.detail:last-child {
|
|
500
|
-
border-bottom: 0;
|
|
501
|
-
}
|
|
502
|
-
dt {
|
|
503
|
-
color: #697066;
|
|
504
|
-
font-size: 13px;
|
|
505
|
-
}
|
|
506
|
-
dd {
|
|
507
|
-
margin: 0;
|
|
508
|
-
min-width: 0;
|
|
509
|
-
overflow-wrap: anywhere;
|
|
510
|
-
font-weight: 700;
|
|
511
|
-
}
|
|
512
|
-
.footer {
|
|
513
|
-
margin-top: 28px;
|
|
514
|
-
color: #697066;
|
|
515
|
-
font-size: 13px;
|
|
516
|
-
}
|
|
517
|
-
@media (max-width: 560px) {
|
|
518
|
-
body {
|
|
519
|
-
padding: 18px;
|
|
520
|
-
}
|
|
521
|
-
main {
|
|
522
|
-
padding: 24px;
|
|
523
|
-
}
|
|
524
|
-
h1 {
|
|
525
|
-
max-width: none;
|
|
526
|
-
font-size: 34px;
|
|
527
|
-
}
|
|
528
|
-
.detail {
|
|
529
|
-
grid-template-columns: 1fr;
|
|
530
|
-
gap: 4px;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
@media (prefers-color-scheme: dark) {
|
|
534
|
-
:root {
|
|
535
|
-
background: #10120f;
|
|
536
|
-
color: #f2f4ec;
|
|
537
|
-
}
|
|
538
|
-
body {
|
|
539
|
-
background:
|
|
540
|
-
linear-gradient(135deg, rgba(70, 165, 148, 0.2), transparent 34%),
|
|
541
|
-
linear-gradient(315deg, rgba(222, 116, 82, 0.16), transparent 38%),
|
|
542
|
-
#10120f;
|
|
543
|
-
}
|
|
544
|
-
main {
|
|
545
|
-
border-color: rgba(242, 244, 236, 0.13);
|
|
546
|
-
background: rgba(24, 27, 22, 0.9);
|
|
547
|
-
box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
|
|
548
|
-
}
|
|
549
|
-
p, dt, .mark, .footer {
|
|
550
|
-
color: #aeb6a8;
|
|
551
|
-
}
|
|
552
|
-
dl {
|
|
553
|
-
border-color: rgba(242, 244, 236, 0.14);
|
|
554
|
-
}
|
|
555
|
-
.detail {
|
|
556
|
-
border-bottom-color: rgba(242, 244, 236, 0.09);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
</style>
|
|
560
|
-
</head>
|
|
561
|
-
<body>
|
|
562
|
-
<main>
|
|
563
|
-
<div class="mark"><span class="dot"></span>${escapeHtml(input.eyebrow ?? statusLabel)}</div>
|
|
564
|
-
<h1>${escapeHtml(input.title)}</h1>
|
|
565
|
-
<p>${escapeHtml(input.message)}</p>
|
|
566
|
-
${hasDetails ? `<dl>${detailRows}</dl>` : ""}
|
|
567
|
-
<div class="footer">You can close this window and return to Auto.</div>
|
|
568
|
-
</main>
|
|
569
|
-
</body>
|
|
570
|
-
</html>`;
|
|
571
|
-
}
|
|
572
|
-
function resolveHtml(html, result) {
|
|
573
|
-
return typeof html === "function" ? html(result) : html;
|
|
574
|
-
}
|
|
575
|
-
function endThenSettle(response, html, settle) {
|
|
576
|
-
response.once("finish", settle);
|
|
577
|
-
response.once("close", settle);
|
|
578
|
-
response.end(html);
|
|
579
|
-
}
|
|
580
|
-
function escapeHtml(value) {
|
|
581
|
-
return value.replace(/[&<>"']/g, (character) => {
|
|
582
|
-
switch (character) {
|
|
583
|
-
case "&":
|
|
584
|
-
return "&";
|
|
585
|
-
case "<":
|
|
586
|
-
return "<";
|
|
587
|
-
case ">":
|
|
588
|
-
return ">";
|
|
589
|
-
case '"':
|
|
590
|
-
return """;
|
|
591
|
-
case "'":
|
|
592
|
-
return "'";
|
|
593
|
-
default:
|
|
594
|
-
return character;
|
|
595
|
-
}
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
async function listenOnPreferredPort(server, port) {
|
|
599
|
-
try {
|
|
600
|
-
await listen(server, port);
|
|
601
|
-
} catch (error51) {
|
|
602
|
-
if (error51.code !== "EADDRINUSE") {
|
|
603
|
-
throw error51;
|
|
604
|
-
}
|
|
605
|
-
await listen(server, 0);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
async function listen(server, port) {
|
|
609
|
-
await new Promise((resolve4, reject) => {
|
|
610
|
-
const onError = (error51) => {
|
|
611
|
-
server.off("listening", onListening);
|
|
612
|
-
reject(error51);
|
|
613
|
-
};
|
|
614
|
-
const onListening = () => {
|
|
615
|
-
server.off("error", onError);
|
|
616
|
-
resolve4();
|
|
617
|
-
};
|
|
618
|
-
server.once("error", onError);
|
|
619
|
-
server.once("listening", onListening);
|
|
620
|
-
server.listen(port, "127.0.0.1");
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
var DEFAULT_CALLBACK_PORT;
|
|
624
|
-
var init_loopback = __esm({
|
|
625
|
-
"src/lib/oauth/loopback.ts"() {
|
|
626
|
-
"use strict";
|
|
627
|
-
DEFAULT_CALLBACK_PORT = 4670;
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
// src/lib/output/style.ts
|
|
632
|
-
import { Chalk } from "chalk";
|
|
633
|
-
function createStyle(flags) {
|
|
634
|
-
const textual = flags.outputMode === "text" || flags.outputMode === "tui";
|
|
635
|
-
if (!textual || flags.noColor || !flags.isTTY) {
|
|
636
|
-
return plainStyle;
|
|
637
|
-
}
|
|
638
|
-
const chalk2 = new Chalk({ level: 1 });
|
|
639
|
-
return {
|
|
640
|
-
enabled: true,
|
|
641
|
-
success: (text) => chalk2.green(text),
|
|
642
|
-
warn: (text) => chalk2.yellow(text),
|
|
643
|
-
error: (text) => chalk2.red(text),
|
|
644
|
-
heading: (text) => chalk2.bold.underline(text),
|
|
645
|
-
label: (text) => chalk2.bold(text),
|
|
646
|
-
id: (text) => chalk2.cyan(text),
|
|
647
|
-
url: (text) => chalk2.cyan.underline(text),
|
|
648
|
-
dim: (text) => chalk2.dim(text)
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
function identity(text) {
|
|
652
|
-
return text;
|
|
653
|
-
}
|
|
654
|
-
var plainStyle;
|
|
655
|
-
var init_style = __esm({
|
|
656
|
-
"src/lib/output/style.ts"() {
|
|
657
|
-
"use strict";
|
|
658
|
-
plainStyle = {
|
|
659
|
-
enabled: false,
|
|
660
|
-
success: identity,
|
|
661
|
-
warn: identity,
|
|
662
|
-
error: identity,
|
|
663
|
-
heading: identity,
|
|
664
|
-
label: identity,
|
|
665
|
-
id: identity,
|
|
666
|
-
url: identity,
|
|
667
|
-
dim: identity
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
// src/commands/auth/pkce.ts
|
|
673
|
-
import { createHash as createHash2, randomBytes } from "crypto";
|
|
674
|
-
function pkceVerifier() {
|
|
675
|
-
return randomBytes(32).toString("base64url");
|
|
676
|
-
}
|
|
677
|
-
function pkceChallenge(verifier) {
|
|
678
|
-
return createHash2("sha256").update(verifier).digest("base64url");
|
|
679
|
-
}
|
|
680
|
-
var init_pkce = __esm({
|
|
681
|
-
"src/commands/auth/pkce.ts"() {
|
|
682
|
-
"use strict";
|
|
683
|
-
}
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
// src/commands/auth/login.ts
|
|
687
|
-
async function login(input) {
|
|
688
|
-
const style = input.style ?? plainStyle;
|
|
689
|
-
if (input.options.profile !== void 0) {
|
|
690
|
-
assertValidProfileName(input.options.profile);
|
|
691
|
-
}
|
|
692
|
-
const serverUrl = resolveApiBaseUrl({ explicit: input.options.apiUrl });
|
|
693
|
-
if (input.options.device) {
|
|
694
|
-
const device = await postJson(
|
|
695
|
-
input.fetch,
|
|
696
|
-
`${serverUrl}/api/v1/auth/device/code`,
|
|
697
|
-
{}
|
|
698
|
-
);
|
|
699
|
-
input.writeOutput(
|
|
700
|
-
`Open ${style.url(device.verification_uri)} and enter ${style.label(
|
|
701
|
-
device.user_code
|
|
702
|
-
)}`
|
|
703
|
-
);
|
|
704
|
-
input.writeOutput(
|
|
705
|
-
`${style.label("Device code:")} ${style.id(device.device_code)}`
|
|
706
|
-
);
|
|
707
|
-
const deadline = Date.now() + device.expires_in * 1e3;
|
|
708
|
-
let firstAttempt = true;
|
|
709
|
-
while (Date.now() < deadline) {
|
|
710
|
-
if (!firstAttempt) {
|
|
711
|
-
await sleep(Math.max(0, device.interval) * 1e3);
|
|
712
|
-
}
|
|
713
|
-
firstAttempt = false;
|
|
714
|
-
let token3;
|
|
715
|
-
try {
|
|
716
|
-
token3 = await postJson(
|
|
717
|
-
input.fetch,
|
|
718
|
-
`${serverUrl}/api/v1/auth/cli/token`,
|
|
719
|
-
{
|
|
720
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
721
|
-
device_code: device.device_code
|
|
722
|
-
}
|
|
723
|
-
);
|
|
724
|
-
} catch (error51) {
|
|
725
|
-
if (error51 instanceof Error && error51.message === "authorization_pending") {
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
throw error51;
|
|
729
|
-
}
|
|
730
|
-
await finishLogin({
|
|
731
|
-
token: token3,
|
|
732
|
-
serverUrl,
|
|
733
|
-
fetch: input.fetch,
|
|
734
|
-
configPath: input.configPath,
|
|
735
|
-
profileName: input.options.profile,
|
|
736
|
-
writeOutput: input.writeOutput,
|
|
737
|
-
style
|
|
738
|
-
});
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
throw new Error("Device authorization expired before approval.");
|
|
742
|
-
}
|
|
743
|
-
const verifier = input.options.verifier ?? pkceVerifier();
|
|
744
|
-
if (!input.options.code) {
|
|
745
|
-
const callback = await createOAuthLoopbackCallback({
|
|
746
|
-
successHtml: () => renderOAuthLoopbackPage({
|
|
747
|
-
status: "success",
|
|
748
|
-
eyebrow: "Auto CLI",
|
|
749
|
-
title: "Login authorized",
|
|
750
|
-
message: "Auto received the browser authorization. The CLI will finish signing you in from your terminal.",
|
|
751
|
-
details: [{ label: "Server", value: serverUrl }]
|
|
752
|
-
}),
|
|
753
|
-
failureHtml: () => renderOAuthLoopbackPage({
|
|
754
|
-
status: "failure",
|
|
755
|
-
eyebrow: "Auto CLI",
|
|
756
|
-
title: "Login failed",
|
|
757
|
-
message: "The browser authorization did not complete. Return to your terminal to retry or inspect the error.",
|
|
758
|
-
details: [{ label: "Server", value: serverUrl }]
|
|
759
|
-
})
|
|
760
|
-
});
|
|
761
|
-
try {
|
|
762
|
-
const authorizeUrl = new URL("/auth/cli", serverUrl);
|
|
763
|
-
authorizeUrl.searchParams.set("pkce_challenge", pkceChallenge(verifier));
|
|
764
|
-
authorizeUrl.searchParams.set("redirect_uri", callback.redirectUri);
|
|
765
|
-
input.writeOutput(`Opening ${style.url(authorizeUrl.toString())}`);
|
|
766
|
-
input.writeOutput(style.dim("Waiting for browser authorization..."));
|
|
767
|
-
openBrowser(authorizeUrl.toString());
|
|
768
|
-
const { code } = await callback.result;
|
|
769
|
-
const token3 = await exchangeAuthorizationCode({
|
|
770
|
-
code,
|
|
771
|
-
fetch: input.fetch,
|
|
772
|
-
redirectUri: callback.redirectUri,
|
|
773
|
-
serverUrl,
|
|
774
|
-
verifier
|
|
775
|
-
});
|
|
776
|
-
await finishLogin({
|
|
777
|
-
token: token3,
|
|
778
|
-
serverUrl,
|
|
779
|
-
fetch: input.fetch,
|
|
780
|
-
configPath: input.configPath,
|
|
781
|
-
profileName: input.options.profile,
|
|
782
|
-
writeOutput: input.writeOutput,
|
|
783
|
-
style
|
|
784
|
-
});
|
|
785
|
-
return;
|
|
786
|
-
} finally {
|
|
787
|
-
callback.close();
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
const token2 = await exchangeAuthorizationCode({
|
|
791
|
-
code: input.options.code,
|
|
792
|
-
fetch: input.fetch,
|
|
793
|
-
redirectUri: "http://127.0.0.1/callback",
|
|
794
|
-
serverUrl,
|
|
795
|
-
verifier
|
|
796
|
-
});
|
|
797
|
-
await finishLogin({
|
|
798
|
-
token: token2,
|
|
799
|
-
serverUrl,
|
|
800
|
-
fetch: input.fetch,
|
|
801
|
-
configPath: input.configPath,
|
|
802
|
-
profileName: input.options.profile,
|
|
803
|
-
writeOutput: input.writeOutput,
|
|
804
|
-
style
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
async function exchangeAuthorizationCode(input) {
|
|
808
|
-
return postJson(
|
|
809
|
-
input.fetch,
|
|
810
|
-
`${input.serverUrl}/api/v1/auth/cli/token`,
|
|
811
|
-
{
|
|
812
|
-
grant_type: "authorization_code",
|
|
813
|
-
code: input.code,
|
|
814
|
-
code_verifier: input.verifier,
|
|
815
|
-
redirect_uri: input.redirectUri
|
|
816
|
-
}
|
|
817
|
-
);
|
|
818
|
-
}
|
|
819
|
-
async function finishLogin(input) {
|
|
820
|
-
const { token: token2, serverUrl } = input;
|
|
821
|
-
const profile = token2.user ? findAccountProfile({
|
|
822
|
-
configPath: input.configPath,
|
|
823
|
-
serverUrl,
|
|
824
|
-
userId: token2.user.id,
|
|
825
|
-
userEmail: token2.user.email
|
|
826
|
-
}) : void 0;
|
|
827
|
-
const selection = profile?.organizationId && profile.projectId ? {
|
|
828
|
-
organizationId: profile.organizationId,
|
|
829
|
-
projectId: profile.projectId
|
|
830
|
-
} : void 0;
|
|
831
|
-
let config2 = {
|
|
832
|
-
serverUrl,
|
|
833
|
-
userId: token2.user?.id,
|
|
834
|
-
userEmail: token2.user?.email,
|
|
835
|
-
refreshToken: token2.refresh_token,
|
|
836
|
-
accessToken: token2.access_token,
|
|
837
|
-
accessTokenExpiresAt: token2.access_token ? accessTokenExpiresAt(token2) : void 0
|
|
838
|
-
};
|
|
839
|
-
if (selection) {
|
|
840
|
-
try {
|
|
841
|
-
const scoped = await postJson(
|
|
842
|
-
input.fetch,
|
|
843
|
-
`${serverUrl}/api/v1/auth/cli/token`,
|
|
844
|
-
{
|
|
845
|
-
grant_type: "refresh_token",
|
|
846
|
-
refresh_token: token2.refresh_token,
|
|
847
|
-
organization_id: selection.organizationId,
|
|
848
|
-
project_id: selection.projectId
|
|
849
|
-
}
|
|
850
|
-
);
|
|
851
|
-
config2 = {
|
|
852
|
-
...config2,
|
|
853
|
-
...selection,
|
|
854
|
-
refreshToken: scoped.refresh_token,
|
|
855
|
-
accessToken: scoped.access_token,
|
|
856
|
-
accessTokenExpiresAt: scoped.access_token ? accessTokenExpiresAt(scoped) : void 0,
|
|
857
|
-
accessTokenOrganizationId: scoped.access_token ? selection.organizationId : void 0,
|
|
858
|
-
accessTokenProjectId: scoped.access_token ? selection.projectId : void 0
|
|
859
|
-
};
|
|
860
|
-
} catch {
|
|
861
|
-
input.writeOutput(
|
|
862
|
-
input.style.warn(
|
|
863
|
-
"The saved organization/project selection is not available for this account; run `auto orgs list` to pick a new one."
|
|
864
|
-
)
|
|
865
|
-
);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
persistLogin(config2, input);
|
|
869
|
-
input.writeOutput(
|
|
870
|
-
input.style.success(
|
|
871
|
-
token2.user ? `Logged in as ${token2.user.email}.` : "Logged in."
|
|
872
|
-
)
|
|
873
|
-
);
|
|
874
|
-
}
|
|
875
|
-
function persistLogin(config2, input) {
|
|
876
|
-
const pinned = input.configPath !== void 0 && isProfilePath(input.configPath);
|
|
877
|
-
const name = saveProfile({
|
|
878
|
-
config: config2,
|
|
879
|
-
name: input.profileName ?? (pinned && input.configPath ? profileNameFromPath(input.configPath) : void 0),
|
|
880
|
-
configPath: input.configPath
|
|
881
|
-
});
|
|
882
|
-
if (!pinned) {
|
|
883
|
-
setCurrentProfile(name, input.configPath);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
async function sleep(ms) {
|
|
887
|
-
await new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
888
|
-
}
|
|
889
|
-
var init_login = __esm({
|
|
890
|
-
"src/commands/auth/login.ts"() {
|
|
891
|
-
"use strict";
|
|
892
|
-
init_base_url();
|
|
893
|
-
init_http();
|
|
894
|
-
init_tokens();
|
|
895
|
-
init_browser();
|
|
896
|
-
init_file();
|
|
897
|
-
init_path();
|
|
898
|
-
init_profiles();
|
|
899
|
-
init_loopback();
|
|
900
|
-
init_style();
|
|
901
|
-
init_pkce();
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
|
|
905
12
|
// ../../node_modules/zod/v4/core/core.js
|
|
906
13
|
// @__NO_SIDE_EFFECTS__
|
|
907
14
|
function $constructor(name, initializer3, params) {
|
|
@@ -19794,6 +18901,201 @@ var init_src = __esm({
|
|
|
19794
18901
|
}
|
|
19795
18902
|
});
|
|
19796
18903
|
|
|
18904
|
+
// src/lib/auth/tokens.ts
|
|
18905
|
+
function accessTokenExpiresAt(token2) {
|
|
18906
|
+
if (!token2.access_token) {
|
|
18907
|
+
throw new Error("Token response did not include an access token.");
|
|
18908
|
+
}
|
|
18909
|
+
return new Date(Date.now() + token2.expires_in * 1e3).toISOString();
|
|
18910
|
+
}
|
|
18911
|
+
function hasUsableAccessToken(config2, project) {
|
|
18912
|
+
if (!config2.accessToken || !config2.accessTokenExpiresAt) {
|
|
18913
|
+
return false;
|
|
18914
|
+
}
|
|
18915
|
+
const expiresAt = Date.parse(config2.accessTokenExpiresAt);
|
|
18916
|
+
return config2.accessTokenOrganizationId === project.organizationId && config2.accessTokenProjectId === project.projectId && Number.isFinite(expiresAt) && expiresAt - ACCESS_TOKEN_REFRESH_SKEW_MS > Date.now();
|
|
18917
|
+
}
|
|
18918
|
+
var ACCESS_TOKEN_REFRESH_SKEW_MS;
|
|
18919
|
+
var init_tokens = __esm({
|
|
18920
|
+
"src/lib/auth/tokens.ts"() {
|
|
18921
|
+
"use strict";
|
|
18922
|
+
ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
18923
|
+
}
|
|
18924
|
+
});
|
|
18925
|
+
|
|
18926
|
+
// src/lib/config/path.ts
|
|
18927
|
+
import { createHash } from "crypto";
|
|
18928
|
+
import { homedir } from "os";
|
|
18929
|
+
import { basename, dirname, join, normalize } from "path";
|
|
18930
|
+
function defaultConfigPath() {
|
|
18931
|
+
return process.env.AUTO_CLI_CONFIG ?? join(homedir(), ".auto", "config.yaml");
|
|
18932
|
+
}
|
|
18933
|
+
function isAccountPath(path2) {
|
|
18934
|
+
return basename(dirname(path2)) === ACCOUNTS_DIR_NAME;
|
|
18935
|
+
}
|
|
18936
|
+
function accountsDir(configPath = defaultConfigPath()) {
|
|
18937
|
+
if (isAccountPath(configPath)) return dirname(configPath);
|
|
18938
|
+
return normalize(join(configPath, "..", ACCOUNTS_DIR_NAME));
|
|
18939
|
+
}
|
|
18940
|
+
function accountFilePath(configPath, accountKey) {
|
|
18941
|
+
return join(accountsDir(configPath), `${accountKey}.yaml`);
|
|
18942
|
+
}
|
|
18943
|
+
function accountKeyFromPath(path2) {
|
|
18944
|
+
return basename(path2).replace(/\.yaml$/, "");
|
|
18945
|
+
}
|
|
18946
|
+
function assertValidAccountKey(accountKey) {
|
|
18947
|
+
if (!ACCOUNT_KEY_PATTERN.test(accountKey)) {
|
|
18948
|
+
throw new Error(
|
|
18949
|
+
`Invalid account key "${accountKey}". Account keys use lowercase letters, digits, dots, dashes, and underscores.`
|
|
18950
|
+
);
|
|
18951
|
+
}
|
|
18952
|
+
return accountKey;
|
|
18953
|
+
}
|
|
18954
|
+
function derivedAccountKey(input) {
|
|
18955
|
+
const key = `${input.userEmail.toLowerCase()}
|
|
18956
|
+
${serverHost(input.serverUrl)}`;
|
|
18957
|
+
const hash2 = createHash("sha256").update(key).digest("hex").slice(0, 8);
|
|
18958
|
+
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}`;
|
|
18959
|
+
}
|
|
18960
|
+
function serverHost(serverUrl) {
|
|
18961
|
+
try {
|
|
18962
|
+
return new URL(serverUrl).host;
|
|
18963
|
+
} catch {
|
|
18964
|
+
return serverUrl;
|
|
18965
|
+
}
|
|
18966
|
+
}
|
|
18967
|
+
function slug(value) {
|
|
18968
|
+
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "_");
|
|
18969
|
+
}
|
|
18970
|
+
var ACCOUNTS_DIR_NAME, ACCOUNT_KEY_PATTERN;
|
|
18971
|
+
var init_path = __esm({
|
|
18972
|
+
"src/lib/config/path.ts"() {
|
|
18973
|
+
"use strict";
|
|
18974
|
+
ACCOUNTS_DIR_NAME = "accounts";
|
|
18975
|
+
ACCOUNT_KEY_PATTERN = /^[a-z0-9._-]+$/;
|
|
18976
|
+
}
|
|
18977
|
+
});
|
|
18978
|
+
|
|
18979
|
+
// src/lib/config/file.ts
|
|
18980
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
18981
|
+
import { dirname as dirname2 } from "path";
|
|
18982
|
+
function readConfig(path2 = defaultConfigPath()) {
|
|
18983
|
+
const accountPath = activeAccountPath(path2);
|
|
18984
|
+
return accountPath ? readAccountFile(accountPath) : {};
|
|
18985
|
+
}
|
|
18986
|
+
function activeAccountKey(path2 = defaultConfigPath()) {
|
|
18987
|
+
if (isAccountPath(path2)) return accountKeyFromPath(path2);
|
|
18988
|
+
return readActiveAccountFile(path2);
|
|
18989
|
+
}
|
|
18990
|
+
function writeConfig(config2, path2 = defaultConfigPath()) {
|
|
18991
|
+
const accountPath = activeAccountPath(path2);
|
|
18992
|
+
if (!accountPath) {
|
|
18993
|
+
throw new Error("Not logged in. Session `auto auth login` first.");
|
|
18994
|
+
}
|
|
18995
|
+
writeAccountFile(config2, accountPath);
|
|
18996
|
+
}
|
|
18997
|
+
function saveAccount(input) {
|
|
18998
|
+
const configPath = input.configPath ?? defaultConfigPath();
|
|
18999
|
+
const accountKey = resolveAccountKey(input);
|
|
19000
|
+
writeAccountFile(input.config, accountFilePath(configPath, accountKey));
|
|
19001
|
+
return accountKey;
|
|
19002
|
+
}
|
|
19003
|
+
function setActiveAccount(accountKey, path2 = defaultConfigPath()) {
|
|
19004
|
+
if (isAccountPath(path2)) {
|
|
19005
|
+
throw new Error(
|
|
19006
|
+
"Cannot change the active account while operating on an account file."
|
|
19007
|
+
);
|
|
19008
|
+
}
|
|
19009
|
+
writeFile(
|
|
19010
|
+
`${ACTIVE_ACCOUNT_KEY}: ${assertValidAccountKey(accountKey)}
|
|
19011
|
+
`,
|
|
19012
|
+
path2
|
|
19013
|
+
);
|
|
19014
|
+
}
|
|
19015
|
+
function clearActiveAccount(path2 = defaultConfigPath()) {
|
|
19016
|
+
if (isAccountPath(path2)) {
|
|
19017
|
+
throw new Error(
|
|
19018
|
+
"Cannot change the active account while operating on an account file."
|
|
19019
|
+
);
|
|
19020
|
+
}
|
|
19021
|
+
writeFile("", path2);
|
|
19022
|
+
}
|
|
19023
|
+
function activeAccountPath(path2) {
|
|
19024
|
+
if (isAccountPath(path2)) return path2;
|
|
19025
|
+
const accountKey = readActiveAccountFile(path2);
|
|
19026
|
+
return accountKey ? accountFilePath(path2, accountKey) : void 0;
|
|
19027
|
+
}
|
|
19028
|
+
function resolveAccountKey(input) {
|
|
19029
|
+
if (input.accountKey !== void 0) {
|
|
19030
|
+
return assertValidAccountKey(input.accountKey);
|
|
19031
|
+
}
|
|
19032
|
+
if (!input.config.userEmail || !input.config.serverUrl) {
|
|
19033
|
+
throw new Error(
|
|
19034
|
+
"An account key is required for a config without a signed-in account."
|
|
19035
|
+
);
|
|
19036
|
+
}
|
|
19037
|
+
return derivedAccountKey({
|
|
19038
|
+
userEmail: input.config.userEmail,
|
|
19039
|
+
serverUrl: input.config.serverUrl
|
|
19040
|
+
});
|
|
19041
|
+
}
|
|
19042
|
+
function readActiveAccountFile(path2) {
|
|
19043
|
+
for (const [key, value] of readKeyValueLines(path2)) {
|
|
19044
|
+
if (key === ACTIVE_ACCOUNT_KEY) return value || void 0;
|
|
19045
|
+
}
|
|
19046
|
+
return void 0;
|
|
19047
|
+
}
|
|
19048
|
+
function readAccountFile(path2) {
|
|
19049
|
+
const config2 = {};
|
|
19050
|
+
for (const [key, value] of readKeyValueLines(path2)) {
|
|
19051
|
+
const known = CONFIG_KEYS.find((candidate) => candidate === key);
|
|
19052
|
+
if (known) config2[known] = value;
|
|
19053
|
+
}
|
|
19054
|
+
return config2;
|
|
19055
|
+
}
|
|
19056
|
+
function readKeyValueLines(path2) {
|
|
19057
|
+
let text;
|
|
19058
|
+
try {
|
|
19059
|
+
text = readFileSync(path2, "utf8");
|
|
19060
|
+
} catch (err) {
|
|
19061
|
+
if (err.code === "ENOENT") return [];
|
|
19062
|
+
throw err;
|
|
19063
|
+
}
|
|
19064
|
+
return text.split(/\r?\n/).map((line) => /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line.trim())).filter((match) => match !== null).map((match) => [match[1] ?? "", match[2] ?? ""]);
|
|
19065
|
+
}
|
|
19066
|
+
function writeAccountFile(config2, path2) {
|
|
19067
|
+
const lines = CONFIG_KEYS.filter((key) => config2[key]).map(
|
|
19068
|
+
(key) => `${key}: ${config2[key]}`
|
|
19069
|
+
);
|
|
19070
|
+
writeFile(`${lines.join("\n")}
|
|
19071
|
+
`, path2);
|
|
19072
|
+
}
|
|
19073
|
+
function writeFile(content, path2) {
|
|
19074
|
+
mkdirSync(dirname2(path2), { recursive: true });
|
|
19075
|
+
writeFileSync(path2, content, { encoding: "utf8", mode: 384 });
|
|
19076
|
+
chmodSync(path2, 384);
|
|
19077
|
+
}
|
|
19078
|
+
var CONFIG_KEYS, ACTIVE_ACCOUNT_KEY;
|
|
19079
|
+
var init_file = __esm({
|
|
19080
|
+
"src/lib/config/file.ts"() {
|
|
19081
|
+
"use strict";
|
|
19082
|
+
init_path();
|
|
19083
|
+
CONFIG_KEYS = [
|
|
19084
|
+
"serverUrl",
|
|
19085
|
+
"userId",
|
|
19086
|
+
"userEmail",
|
|
19087
|
+
"organizationId",
|
|
19088
|
+
"projectId",
|
|
19089
|
+
"refreshToken",
|
|
19090
|
+
"accessToken",
|
|
19091
|
+
"accessTokenExpiresAt",
|
|
19092
|
+
"accessTokenOrganizationId",
|
|
19093
|
+
"accessTokenProjectId"
|
|
19094
|
+
];
|
|
19095
|
+
ACTIVE_ACCOUNT_KEY = "activeAccount";
|
|
19096
|
+
}
|
|
19097
|
+
});
|
|
19098
|
+
|
|
19797
19099
|
// src/lib/config/active-project.ts
|
|
19798
19100
|
function requireActiveProject(config2) {
|
|
19799
19101
|
if (!config2.organizationId || !config2.projectId) {
|
|
@@ -19909,6 +19211,34 @@ var init_sse = __esm({
|
|
|
19909
19211
|
}
|
|
19910
19212
|
});
|
|
19911
19213
|
|
|
19214
|
+
// src/lib/api/base-url.ts
|
|
19215
|
+
function resolveApiBaseUrl(input = {}) {
|
|
19216
|
+
const explicitValues = Array.isArray(input.explicit) ? input.explicit : [input.explicit];
|
|
19217
|
+
const defaultUrl = input.defaultUrl === null ? void 0 : input.defaultUrl ?? DEFAULT_API_BASE_URL;
|
|
19218
|
+
for (const value of [
|
|
19219
|
+
...explicitValues,
|
|
19220
|
+
input.env?.AUTO_API_BASE_URL,
|
|
19221
|
+
input.configServerUrl,
|
|
19222
|
+
defaultUrl
|
|
19223
|
+
]) {
|
|
19224
|
+
const normalized = normalizeApiBaseUrl(value);
|
|
19225
|
+
if (normalized) return normalized;
|
|
19226
|
+
}
|
|
19227
|
+
return void 0;
|
|
19228
|
+
}
|
|
19229
|
+
function normalizeApiBaseUrl(value) {
|
|
19230
|
+
const trimmed = value?.trim();
|
|
19231
|
+
if (!trimmed) return void 0;
|
|
19232
|
+
return trimmed.replace(/\/+$/, "");
|
|
19233
|
+
}
|
|
19234
|
+
var DEFAULT_API_BASE_URL;
|
|
19235
|
+
var init_base_url = __esm({
|
|
19236
|
+
"src/lib/api/base-url.ts"() {
|
|
19237
|
+
"use strict";
|
|
19238
|
+
DEFAULT_API_BASE_URL = "https://www.auto.sh";
|
|
19239
|
+
}
|
|
19240
|
+
});
|
|
19241
|
+
|
|
19912
19242
|
// src/lib/api/errors.ts
|
|
19913
19243
|
async function responseErrorMessage(response) {
|
|
19914
19244
|
const text = await response.text();
|
|
@@ -19930,6 +19260,27 @@ var init_errors3 = __esm({
|
|
|
19930
19260
|
}
|
|
19931
19261
|
});
|
|
19932
19262
|
|
|
19263
|
+
// src/lib/api/http.ts
|
|
19264
|
+
async function postJson(fetchImpl, url2, body) {
|
|
19265
|
+
const response = await fetchImpl(url2, {
|
|
19266
|
+
method: "POST",
|
|
19267
|
+
headers: { "content-type": "application/json" },
|
|
19268
|
+
body: JSON.stringify(body)
|
|
19269
|
+
});
|
|
19270
|
+
if (!response.ok) {
|
|
19271
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
19272
|
+
throw new Error(
|
|
19273
|
+
errorBody.error ?? `Request failed with status ${response.status}`
|
|
19274
|
+
);
|
|
19275
|
+
}
|
|
19276
|
+
return response.json();
|
|
19277
|
+
}
|
|
19278
|
+
var init_http = __esm({
|
|
19279
|
+
"src/lib/api/http.ts"() {
|
|
19280
|
+
"use strict";
|
|
19281
|
+
}
|
|
19282
|
+
});
|
|
19283
|
+
|
|
19933
19284
|
// src/lib/api/paths.ts
|
|
19934
19285
|
function apiPath(path2 = "") {
|
|
19935
19286
|
return `${API_PREFIX}${cleanSubpath(path2)}`;
|
|
@@ -19975,7 +19326,7 @@ var init_paths = __esm({
|
|
|
19975
19326
|
});
|
|
19976
19327
|
|
|
19977
19328
|
// src/lib/api/resources.ts
|
|
19978
|
-
import { createHash as
|
|
19329
|
+
import { createHash as createHash2 } from "crypto";
|
|
19979
19330
|
function createResourceApi(context) {
|
|
19980
19331
|
const environments = projectResource(context, {
|
|
19981
19332
|
path: "/environments",
|
|
@@ -20042,7 +19393,7 @@ async function stageEmptyApplyBundle(context, options) {
|
|
|
20042
19393
|
const sizeBytes = Buffer.byteLength(body, "utf8");
|
|
20043
19394
|
const upload = await prepareProjectApplyBundleUpload(
|
|
20044
19395
|
context,
|
|
20045
|
-
{ sha256:
|
|
19396
|
+
{ sha256: createHash2("sha256").update(body).digest("hex"), sizeBytes },
|
|
20046
19397
|
options
|
|
20047
19398
|
);
|
|
20048
19399
|
await uploadProjectApplyBundle(context, {
|
|
@@ -21675,13 +21026,393 @@ var init_client = __esm({
|
|
|
21675
21026
|
}
|
|
21676
21027
|
});
|
|
21677
21028
|
|
|
21029
|
+
// src/lib/config/accounts.ts
|
|
21030
|
+
import { readdirSync } from "fs";
|
|
21031
|
+
import { join as join2 } from "path";
|
|
21032
|
+
function listAccounts(configPath = defaultConfigPath()) {
|
|
21033
|
+
const dir = accountsDir(configPath);
|
|
21034
|
+
let entries;
|
|
21035
|
+
try {
|
|
21036
|
+
entries = readdirSync(dir);
|
|
21037
|
+
} catch (err) {
|
|
21038
|
+
if (err.code === "ENOENT") return [];
|
|
21039
|
+
throw err;
|
|
21040
|
+
}
|
|
21041
|
+
return entries.filter((entry) => entry.endsWith(".yaml")).sort().map((entry) => {
|
|
21042
|
+
const path2 = join2(dir, entry);
|
|
21043
|
+
return {
|
|
21044
|
+
key: accountKeyFromPath(path2),
|
|
21045
|
+
path: path2,
|
|
21046
|
+
config: readConfig(path2)
|
|
21047
|
+
};
|
|
21048
|
+
}).filter((account) => account.config.userEmail);
|
|
21049
|
+
}
|
|
21050
|
+
function findStoredAccountConfig(input) {
|
|
21051
|
+
const candidates = listAccounts(input.configPath).map((account) => account.config).filter((config2) => config2.serverUrl === input.serverUrl);
|
|
21052
|
+
return candidates.find(
|
|
21053
|
+
(config2) => input.userId && config2.userId === input.userId
|
|
21054
|
+
) ?? // Email is only a fallback for when a user id is missing on either side;
|
|
21055
|
+
// it must never override a known user-id mismatch (emails can be
|
|
21056
|
+
// reassigned to a different user).
|
|
21057
|
+
candidates.find(
|
|
21058
|
+
(config2) => input.userEmail && config2.userEmail?.toLowerCase() === input.userEmail.toLowerCase() && !(input.userId && config2.userId && config2.userId !== input.userId)
|
|
21059
|
+
);
|
|
21060
|
+
}
|
|
21061
|
+
var init_accounts = __esm({
|
|
21062
|
+
"src/lib/config/accounts.ts"() {
|
|
21063
|
+
"use strict";
|
|
21064
|
+
init_file();
|
|
21065
|
+
init_path();
|
|
21066
|
+
}
|
|
21067
|
+
});
|
|
21068
|
+
|
|
21069
|
+
// src/lib/browser.ts
|
|
21070
|
+
import { spawn } from "child_process";
|
|
21071
|
+
function openBrowser(url2) {
|
|
21072
|
+
if (!shouldOpenBrowser(process.env)) {
|
|
21073
|
+
return;
|
|
21074
|
+
}
|
|
21075
|
+
const child = spawn(openCommand(), openArgs(url2), {
|
|
21076
|
+
detached: true,
|
|
21077
|
+
stdio: "ignore"
|
|
21078
|
+
});
|
|
21079
|
+
child.unref();
|
|
21080
|
+
}
|
|
21081
|
+
function shouldOpenBrowser(env) {
|
|
21082
|
+
if (env.AUTO_ALLOW_BROWSER_OPEN_IN_TESTS === "1") {
|
|
21083
|
+
return true;
|
|
21084
|
+
}
|
|
21085
|
+
return !env.NODE_TEST_CONTEXT;
|
|
21086
|
+
}
|
|
21087
|
+
function openCommand() {
|
|
21088
|
+
switch (process.platform) {
|
|
21089
|
+
case "darwin":
|
|
21090
|
+
return "open";
|
|
21091
|
+
case "win32":
|
|
21092
|
+
return "cmd";
|
|
21093
|
+
default:
|
|
21094
|
+
return "xdg-open";
|
|
21095
|
+
}
|
|
21096
|
+
}
|
|
21097
|
+
function openArgs(url2) {
|
|
21098
|
+
return process.platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
21099
|
+
}
|
|
21100
|
+
var init_browser = __esm({
|
|
21101
|
+
"src/lib/browser.ts"() {
|
|
21102
|
+
"use strict";
|
|
21103
|
+
}
|
|
21104
|
+
});
|
|
21105
|
+
|
|
21106
|
+
// src/lib/oauth/loopback.ts
|
|
21107
|
+
import { createServer } from "http";
|
|
21108
|
+
async function createOAuthLoopbackCallback(input) {
|
|
21109
|
+
const path2 = input?.path ?? "/callback";
|
|
21110
|
+
const successHtml = input?.successHtml ?? renderOAuthLoopbackPage({
|
|
21111
|
+
status: "success",
|
|
21112
|
+
eyebrow: "Auto",
|
|
21113
|
+
title: "Authorization complete",
|
|
21114
|
+
message: "You can close this window and return to your terminal."
|
|
21115
|
+
});
|
|
21116
|
+
const failureHtml = input?.failureHtml ?? renderOAuthLoopbackPage({
|
|
21117
|
+
status: "failure",
|
|
21118
|
+
eyebrow: "Auto",
|
|
21119
|
+
title: "Authorization failed",
|
|
21120
|
+
message: "Return to your terminal to see the error details."
|
|
21121
|
+
});
|
|
21122
|
+
let resolveResult;
|
|
21123
|
+
let rejectResult;
|
|
21124
|
+
const result = new Promise((resolve4, reject) => {
|
|
21125
|
+
resolveResult = resolve4;
|
|
21126
|
+
rejectResult = reject;
|
|
21127
|
+
});
|
|
21128
|
+
const server = createServer((request, response) => {
|
|
21129
|
+
const url2 = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
21130
|
+
if (url2.pathname !== path2) {
|
|
21131
|
+
response.writeHead(404).end("Not found");
|
|
21132
|
+
return;
|
|
21133
|
+
}
|
|
21134
|
+
const error51 = url2.searchParams.get("error");
|
|
21135
|
+
if (error51) {
|
|
21136
|
+
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
21137
|
+
endThenSettle(
|
|
21138
|
+
response,
|
|
21139
|
+
resolveHtml(failureHtml, { code: "" }),
|
|
21140
|
+
() => rejectResult(new Error(error51))
|
|
21141
|
+
);
|
|
21142
|
+
return;
|
|
21143
|
+
}
|
|
21144
|
+
const code = url2.searchParams.get("code") ?? "";
|
|
21145
|
+
const sensitiveActionToken = url2.searchParams.get("sensitive_action_token") ?? void 0;
|
|
21146
|
+
if (!code && !sensitiveActionToken) {
|
|
21147
|
+
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
21148
|
+
endThenSettle(
|
|
21149
|
+
response,
|
|
21150
|
+
renderOAuthLoopbackPage({
|
|
21151
|
+
status: "failure",
|
|
21152
|
+
eyebrow: "Auto",
|
|
21153
|
+
title: "Missing authorization result",
|
|
21154
|
+
message: "Return to your terminal to retry the authorization flow."
|
|
21155
|
+
}),
|
|
21156
|
+
() => rejectResult(new Error("Missing authorization result"))
|
|
21157
|
+
);
|
|
21158
|
+
return;
|
|
21159
|
+
}
|
|
21160
|
+
const result2 = {
|
|
21161
|
+
code,
|
|
21162
|
+
sensitiveActionToken,
|
|
21163
|
+
organizationName: url2.searchParams.get("organization_name") ?? void 0,
|
|
21164
|
+
projectName: url2.searchParams.get("project_name") ?? void 0,
|
|
21165
|
+
state: url2.searchParams.get("state") ?? void 0
|
|
21166
|
+
};
|
|
21167
|
+
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
21168
|
+
endThenSettle(
|
|
21169
|
+
response,
|
|
21170
|
+
resolveHtml(successHtml, result2),
|
|
21171
|
+
() => resolveResult(result2)
|
|
21172
|
+
);
|
|
21173
|
+
});
|
|
21174
|
+
await listenOnPreferredPort(
|
|
21175
|
+
server,
|
|
21176
|
+
input?.preferredPort ?? DEFAULT_CALLBACK_PORT
|
|
21177
|
+
);
|
|
21178
|
+
const address = server.address();
|
|
21179
|
+
return {
|
|
21180
|
+
close: () => {
|
|
21181
|
+
server.close();
|
|
21182
|
+
server.closeAllConnections();
|
|
21183
|
+
},
|
|
21184
|
+
redirectUri: `http://127.0.0.1:${address.port}${path2}`,
|
|
21185
|
+
result
|
|
21186
|
+
};
|
|
21187
|
+
}
|
|
21188
|
+
function renderOAuthLoopbackPage(input) {
|
|
21189
|
+
const detailRows = (input.details ?? []).filter((detail) => detail.value).map(
|
|
21190
|
+
(detail) => `<div class="detail">
|
|
21191
|
+
<dt>${escapeHtml(detail.label)}</dt>
|
|
21192
|
+
<dd>${escapeHtml(detail.value ?? "")}</dd>
|
|
21193
|
+
</div>`
|
|
21194
|
+
).join("");
|
|
21195
|
+
const hasDetails = detailRows.length > 0;
|
|
21196
|
+
const statusLabel = input.status === "success" ? "Authorization received" : "Authorization failed";
|
|
21197
|
+
return `<!doctype html>
|
|
21198
|
+
<html lang="en">
|
|
21199
|
+
<head>
|
|
21200
|
+
<meta charset="utf-8">
|
|
21201
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
21202
|
+
<title>${escapeHtml(input.title)} | Auto</title>
|
|
21203
|
+
<style>
|
|
21204
|
+
:root {
|
|
21205
|
+
color-scheme: light dark;
|
|
21206
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
21207
|
+
background: #f6f7f2;
|
|
21208
|
+
color: #171814;
|
|
21209
|
+
}
|
|
21210
|
+
* {
|
|
21211
|
+
box-sizing: border-box;
|
|
21212
|
+
}
|
|
21213
|
+
body {
|
|
21214
|
+
min-height: 100vh;
|
|
21215
|
+
margin: 0;
|
|
21216
|
+
display: grid;
|
|
21217
|
+
place-items: center;
|
|
21218
|
+
padding: 32px;
|
|
21219
|
+
background:
|
|
21220
|
+
linear-gradient(135deg, rgba(35, 108, 96, 0.18), transparent 34%),
|
|
21221
|
+
linear-gradient(315deg, rgba(194, 90, 62, 0.16), transparent 38%),
|
|
21222
|
+
#f6f7f2;
|
|
21223
|
+
}
|
|
21224
|
+
main {
|
|
21225
|
+
width: min(680px, 100%);
|
|
21226
|
+
border: 1px solid rgba(23, 24, 20, 0.12);
|
|
21227
|
+
border-radius: 8px;
|
|
21228
|
+
background: rgba(255, 255, 252, 0.88);
|
|
21229
|
+
box-shadow: 0 24px 70px rgba(23, 24, 20, 0.14);
|
|
21230
|
+
padding: 32px;
|
|
21231
|
+
}
|
|
21232
|
+
.mark {
|
|
21233
|
+
display: inline-flex;
|
|
21234
|
+
align-items: center;
|
|
21235
|
+
gap: 10px;
|
|
21236
|
+
margin-bottom: 28px;
|
|
21237
|
+
color: #4d554b;
|
|
21238
|
+
font-size: 13px;
|
|
21239
|
+
font-weight: 700;
|
|
21240
|
+
letter-spacing: 0;
|
|
21241
|
+
text-transform: uppercase;
|
|
21242
|
+
}
|
|
21243
|
+
.dot {
|
|
21244
|
+
width: 12px;
|
|
21245
|
+
height: 12px;
|
|
21246
|
+
border-radius: 999px;
|
|
21247
|
+
background: ${input.status === "success" ? "#22863a" : "#b42318"};
|
|
21248
|
+
box-shadow: 0 0 0 6px ${input.status === "success" ? "rgba(34, 134, 58, 0.14)" : "rgba(180, 35, 24, 0.14)"};
|
|
21249
|
+
}
|
|
21250
|
+
h1 {
|
|
21251
|
+
margin: 0;
|
|
21252
|
+
max-width: 12ch;
|
|
21253
|
+
font-size: 44px;
|
|
21254
|
+
line-height: 1;
|
|
21255
|
+
letter-spacing: 0;
|
|
21256
|
+
}
|
|
21257
|
+
p {
|
|
21258
|
+
margin: 18px 0 0;
|
|
21259
|
+
max-width: 58ch;
|
|
21260
|
+
color: #4d554b;
|
|
21261
|
+
font-size: 16px;
|
|
21262
|
+
line-height: 1.55;
|
|
21263
|
+
}
|
|
21264
|
+
dl {
|
|
21265
|
+
display: grid;
|
|
21266
|
+
gap: 0;
|
|
21267
|
+
margin: 28px 0 0;
|
|
21268
|
+
border-block: 1px solid rgba(23, 24, 20, 0.13);
|
|
21269
|
+
}
|
|
21270
|
+
.detail {
|
|
21271
|
+
display: grid;
|
|
21272
|
+
grid-template-columns: minmax(118px, 0.42fr) 1fr;
|
|
21273
|
+
gap: 18px;
|
|
21274
|
+
padding: 14px 0;
|
|
21275
|
+
border-bottom: 1px solid rgba(23, 24, 20, 0.09);
|
|
21276
|
+
}
|
|
21277
|
+
.detail:last-child {
|
|
21278
|
+
border-bottom: 0;
|
|
21279
|
+
}
|
|
21280
|
+
dt {
|
|
21281
|
+
color: #697066;
|
|
21282
|
+
font-size: 13px;
|
|
21283
|
+
}
|
|
21284
|
+
dd {
|
|
21285
|
+
margin: 0;
|
|
21286
|
+
min-width: 0;
|
|
21287
|
+
overflow-wrap: anywhere;
|
|
21288
|
+
font-weight: 700;
|
|
21289
|
+
}
|
|
21290
|
+
.footer {
|
|
21291
|
+
margin-top: 28px;
|
|
21292
|
+
color: #697066;
|
|
21293
|
+
font-size: 13px;
|
|
21294
|
+
}
|
|
21295
|
+
@media (max-width: 560px) {
|
|
21296
|
+
body {
|
|
21297
|
+
padding: 18px;
|
|
21298
|
+
}
|
|
21299
|
+
main {
|
|
21300
|
+
padding: 24px;
|
|
21301
|
+
}
|
|
21302
|
+
h1 {
|
|
21303
|
+
max-width: none;
|
|
21304
|
+
font-size: 34px;
|
|
21305
|
+
}
|
|
21306
|
+
.detail {
|
|
21307
|
+
grid-template-columns: 1fr;
|
|
21308
|
+
gap: 4px;
|
|
21309
|
+
}
|
|
21310
|
+
}
|
|
21311
|
+
@media (prefers-color-scheme: dark) {
|
|
21312
|
+
:root {
|
|
21313
|
+
background: #10120f;
|
|
21314
|
+
color: #f2f4ec;
|
|
21315
|
+
}
|
|
21316
|
+
body {
|
|
21317
|
+
background:
|
|
21318
|
+
linear-gradient(135deg, rgba(70, 165, 148, 0.2), transparent 34%),
|
|
21319
|
+
linear-gradient(315deg, rgba(222, 116, 82, 0.16), transparent 38%),
|
|
21320
|
+
#10120f;
|
|
21321
|
+
}
|
|
21322
|
+
main {
|
|
21323
|
+
border-color: rgba(242, 244, 236, 0.13);
|
|
21324
|
+
background: rgba(24, 27, 22, 0.9);
|
|
21325
|
+
box-shadow: 0 24px 70px rgba(0, 0, 0, 0.4);
|
|
21326
|
+
}
|
|
21327
|
+
p, dt, .mark, .footer {
|
|
21328
|
+
color: #aeb6a8;
|
|
21329
|
+
}
|
|
21330
|
+
dl {
|
|
21331
|
+
border-color: rgba(242, 244, 236, 0.14);
|
|
21332
|
+
}
|
|
21333
|
+
.detail {
|
|
21334
|
+
border-bottom-color: rgba(242, 244, 236, 0.09);
|
|
21335
|
+
}
|
|
21336
|
+
}
|
|
21337
|
+
</style>
|
|
21338
|
+
</head>
|
|
21339
|
+
<body>
|
|
21340
|
+
<main>
|
|
21341
|
+
<div class="mark"><span class="dot"></span>${escapeHtml(input.eyebrow ?? statusLabel)}</div>
|
|
21342
|
+
<h1>${escapeHtml(input.title)}</h1>
|
|
21343
|
+
<p>${escapeHtml(input.message)}</p>
|
|
21344
|
+
${hasDetails ? `<dl>${detailRows}</dl>` : ""}
|
|
21345
|
+
<div class="footer">You can close this window and return to Auto.</div>
|
|
21346
|
+
</main>
|
|
21347
|
+
</body>
|
|
21348
|
+
</html>`;
|
|
21349
|
+
}
|
|
21350
|
+
function resolveHtml(html, result) {
|
|
21351
|
+
return typeof html === "function" ? html(result) : html;
|
|
21352
|
+
}
|
|
21353
|
+
function endThenSettle(response, html, settle) {
|
|
21354
|
+
response.once("finish", settle);
|
|
21355
|
+
response.once("close", settle);
|
|
21356
|
+
response.end(html);
|
|
21357
|
+
}
|
|
21358
|
+
function escapeHtml(value) {
|
|
21359
|
+
return value.replace(/[&<>"']/g, (character) => {
|
|
21360
|
+
switch (character) {
|
|
21361
|
+
case "&":
|
|
21362
|
+
return "&";
|
|
21363
|
+
case "<":
|
|
21364
|
+
return "<";
|
|
21365
|
+
case ">":
|
|
21366
|
+
return ">";
|
|
21367
|
+
case '"':
|
|
21368
|
+
return """;
|
|
21369
|
+
case "'":
|
|
21370
|
+
return "'";
|
|
21371
|
+
default:
|
|
21372
|
+
return character;
|
|
21373
|
+
}
|
|
21374
|
+
});
|
|
21375
|
+
}
|
|
21376
|
+
async function listenOnPreferredPort(server, port) {
|
|
21377
|
+
try {
|
|
21378
|
+
await listen(server, port);
|
|
21379
|
+
} catch (error51) {
|
|
21380
|
+
if (error51.code !== "EADDRINUSE") {
|
|
21381
|
+
throw error51;
|
|
21382
|
+
}
|
|
21383
|
+
await listen(server, 0);
|
|
21384
|
+
}
|
|
21385
|
+
}
|
|
21386
|
+
async function listen(server, port) {
|
|
21387
|
+
await new Promise((resolve4, reject) => {
|
|
21388
|
+
const onError = (error51) => {
|
|
21389
|
+
server.off("listening", onListening);
|
|
21390
|
+
reject(error51);
|
|
21391
|
+
};
|
|
21392
|
+
const onListening = () => {
|
|
21393
|
+
server.off("error", onError);
|
|
21394
|
+
resolve4();
|
|
21395
|
+
};
|
|
21396
|
+
server.once("error", onError);
|
|
21397
|
+
server.once("listening", onListening);
|
|
21398
|
+
server.listen(port, "127.0.0.1");
|
|
21399
|
+
});
|
|
21400
|
+
}
|
|
21401
|
+
var DEFAULT_CALLBACK_PORT;
|
|
21402
|
+
var init_loopback = __esm({
|
|
21403
|
+
"src/lib/oauth/loopback.ts"() {
|
|
21404
|
+
"use strict";
|
|
21405
|
+
DEFAULT_CALLBACK_PORT = 4670;
|
|
21406
|
+
}
|
|
21407
|
+
});
|
|
21408
|
+
|
|
21678
21409
|
// package.json
|
|
21679
21410
|
var package_default;
|
|
21680
21411
|
var init_package = __esm({
|
|
21681
21412
|
"package.json"() {
|
|
21682
21413
|
package_default = {
|
|
21683
21414
|
name: "@autohq/cli",
|
|
21684
|
-
version: "0.1.
|
|
21415
|
+
version: "0.1.186",
|
|
21685
21416
|
license: "SEE LICENSE IN README.md",
|
|
21686
21417
|
publishConfig: {
|
|
21687
21418
|
access: "public"
|
|
@@ -22450,6 +22181,47 @@ var init_progress = __esm({
|
|
|
22450
22181
|
}
|
|
22451
22182
|
});
|
|
22452
22183
|
|
|
22184
|
+
// src/lib/output/style.ts
|
|
22185
|
+
import { Chalk } from "chalk";
|
|
22186
|
+
function createStyle(flags) {
|
|
22187
|
+
const textual = flags.outputMode === "text" || flags.outputMode === "tui";
|
|
22188
|
+
if (!textual || flags.noColor || !flags.isTTY) {
|
|
22189
|
+
return plainStyle;
|
|
22190
|
+
}
|
|
22191
|
+
const chalk2 = new Chalk({ level: 1 });
|
|
22192
|
+
return {
|
|
22193
|
+
enabled: true,
|
|
22194
|
+
success: (text) => chalk2.green(text),
|
|
22195
|
+
warn: (text) => chalk2.yellow(text),
|
|
22196
|
+
error: (text) => chalk2.red(text),
|
|
22197
|
+
heading: (text) => chalk2.bold.underline(text),
|
|
22198
|
+
label: (text) => chalk2.bold(text),
|
|
22199
|
+
id: (text) => chalk2.cyan(text),
|
|
22200
|
+
url: (text) => chalk2.cyan.underline(text),
|
|
22201
|
+
dim: (text) => chalk2.dim(text)
|
|
22202
|
+
};
|
|
22203
|
+
}
|
|
22204
|
+
function identity(text) {
|
|
22205
|
+
return text;
|
|
22206
|
+
}
|
|
22207
|
+
var plainStyle;
|
|
22208
|
+
var init_style = __esm({
|
|
22209
|
+
"src/lib/output/style.ts"() {
|
|
22210
|
+
"use strict";
|
|
22211
|
+
plainStyle = {
|
|
22212
|
+
enabled: false,
|
|
22213
|
+
success: identity,
|
|
22214
|
+
warn: identity,
|
|
22215
|
+
error: identity,
|
|
22216
|
+
heading: identity,
|
|
22217
|
+
label: identity,
|
|
22218
|
+
id: identity,
|
|
22219
|
+
url: identity,
|
|
22220
|
+
dim: identity
|
|
22221
|
+
};
|
|
22222
|
+
}
|
|
22223
|
+
});
|
|
22224
|
+
|
|
22453
22225
|
// src/commands/apply/agent-tool-connect.ts
|
|
22454
22226
|
async function connectAgentTool(input) {
|
|
22455
22227
|
if (input.manual) {
|
|
@@ -23429,7 +23201,7 @@ var init_apply_result = __esm({
|
|
|
23429
23201
|
});
|
|
23430
23202
|
|
|
23431
23203
|
// ../../packages/schemas/src/project-apply-files/assets.ts
|
|
23432
|
-
import { createHash as
|
|
23204
|
+
import { createHash as createHash3 } from "crypto";
|
|
23433
23205
|
import { extname as extname2 } from "path";
|
|
23434
23206
|
function readApplyAssets(resources, readAsset) {
|
|
23435
23207
|
const assets = {};
|
|
@@ -23476,7 +23248,7 @@ function projectApplyAssetFromSource(input) {
|
|
|
23476
23248
|
);
|
|
23477
23249
|
}
|
|
23478
23250
|
return {
|
|
23479
|
-
sha256:
|
|
23251
|
+
sha256: createHash3("sha256").update(bytes).digest("hex"),
|
|
23480
23252
|
contentType: extension === ".png" ? "image/png" : "image/jpeg",
|
|
23481
23253
|
dataBase64: bytes.toString("base64")
|
|
23482
23254
|
};
|
|
@@ -23665,7 +23437,7 @@ var init_project_apply_files2 = __esm({
|
|
|
23665
23437
|
|
|
23666
23438
|
// src/commands/apply/files.ts
|
|
23667
23439
|
import {
|
|
23668
|
-
existsSync as
|
|
23440
|
+
existsSync as existsSync3,
|
|
23669
23441
|
readFileSync as readFileSync4,
|
|
23670
23442
|
readdirSync as readdirSync3,
|
|
23671
23443
|
realpathSync,
|
|
@@ -23813,7 +23585,7 @@ function applyFileSourceRoot(file2, projectRoot) {
|
|
|
23813
23585
|
}
|
|
23814
23586
|
function directoryHasAutoRoot(directory) {
|
|
23815
23587
|
const autoRoot = resolve2(directory, ".auto");
|
|
23816
|
-
if (!
|
|
23588
|
+
if (!existsSync3(autoRoot)) {
|
|
23817
23589
|
return false;
|
|
23818
23590
|
}
|
|
23819
23591
|
return statSync2(autoRoot).isDirectory();
|
|
@@ -23893,7 +23665,7 @@ var init_files = __esm({
|
|
|
23893
23665
|
});
|
|
23894
23666
|
|
|
23895
23667
|
// src/commands/apply/actions.ts
|
|
23896
|
-
import { createHash as
|
|
23668
|
+
import { createHash as createHash4 } from "crypto";
|
|
23897
23669
|
async function applyResource(input) {
|
|
23898
23670
|
if (input.commandOptions.connect && input.commandOptions.json) {
|
|
23899
23671
|
throw new Error("Cannot use --connect with --json output.");
|
|
@@ -23930,7 +23702,7 @@ async function applyProjectBundleInput(input) {
|
|
|
23930
23702
|
spinner.start("Preparing upload");
|
|
23931
23703
|
const upload = await input.client.prepareProjectApplyBundleUpload(
|
|
23932
23704
|
{
|
|
23933
|
-
sha256:
|
|
23705
|
+
sha256: createHash4("sha256").update(body).digest("hex"),
|
|
23934
23706
|
sizeBytes
|
|
23935
23707
|
},
|
|
23936
23708
|
{ apiBaseUrl: input.commandOptions.apiBaseUrl }
|
|
@@ -24088,6 +23860,233 @@ var init_actions = __esm({
|
|
|
24088
23860
|
}
|
|
24089
23861
|
});
|
|
24090
23862
|
|
|
23863
|
+
// src/commands/auth/pkce.ts
|
|
23864
|
+
import { createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
|
|
23865
|
+
function pkceVerifier() {
|
|
23866
|
+
return randomBytes2(32).toString("base64url");
|
|
23867
|
+
}
|
|
23868
|
+
function pkceChallenge(verifier) {
|
|
23869
|
+
return createHash5("sha256").update(verifier).digest("base64url");
|
|
23870
|
+
}
|
|
23871
|
+
var init_pkce = __esm({
|
|
23872
|
+
"src/commands/auth/pkce.ts"() {
|
|
23873
|
+
"use strict";
|
|
23874
|
+
}
|
|
23875
|
+
});
|
|
23876
|
+
|
|
23877
|
+
// src/commands/auth/login.ts
|
|
23878
|
+
async function login(input) {
|
|
23879
|
+
const style = input.style ?? plainStyle;
|
|
23880
|
+
const serverUrl = resolveApiBaseUrl({ explicit: input.options.apiUrl });
|
|
23881
|
+
if (input.options.device) {
|
|
23882
|
+
const device = await postJson(
|
|
23883
|
+
input.fetch,
|
|
23884
|
+
`${serverUrl}/api/v1/auth/device/code`,
|
|
23885
|
+
{}
|
|
23886
|
+
);
|
|
23887
|
+
input.writeOutput(
|
|
23888
|
+
`Open ${style.url(device.verification_uri)} and enter ${style.label(
|
|
23889
|
+
device.user_code
|
|
23890
|
+
)}`
|
|
23891
|
+
);
|
|
23892
|
+
input.writeOutput(
|
|
23893
|
+
`${style.label("Device code:")} ${style.id(device.device_code)}`
|
|
23894
|
+
);
|
|
23895
|
+
const deadline = Date.now() + device.expires_in * 1e3;
|
|
23896
|
+
let firstAttempt = true;
|
|
23897
|
+
while (Date.now() < deadline) {
|
|
23898
|
+
if (!firstAttempt) {
|
|
23899
|
+
await sleep2(Math.max(0, device.interval) * 1e3);
|
|
23900
|
+
}
|
|
23901
|
+
firstAttempt = false;
|
|
23902
|
+
let token3;
|
|
23903
|
+
try {
|
|
23904
|
+
token3 = await postJson(
|
|
23905
|
+
input.fetch,
|
|
23906
|
+
`${serverUrl}/api/v1/auth/cli/token`,
|
|
23907
|
+
{
|
|
23908
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
23909
|
+
device_code: device.device_code
|
|
23910
|
+
}
|
|
23911
|
+
);
|
|
23912
|
+
} catch (error51) {
|
|
23913
|
+
if (error51 instanceof Error && error51.message === "authorization_pending") {
|
|
23914
|
+
continue;
|
|
23915
|
+
}
|
|
23916
|
+
throw error51;
|
|
23917
|
+
}
|
|
23918
|
+
await finishLogin({
|
|
23919
|
+
token: token3,
|
|
23920
|
+
serverUrl,
|
|
23921
|
+
fetch: input.fetch,
|
|
23922
|
+
configPath: input.configPath,
|
|
23923
|
+
writeOutput: input.writeOutput,
|
|
23924
|
+
style
|
|
23925
|
+
});
|
|
23926
|
+
return;
|
|
23927
|
+
}
|
|
23928
|
+
throw new Error("Device authorization expired before approval.");
|
|
23929
|
+
}
|
|
23930
|
+
const verifier = input.options.verifier ?? pkceVerifier();
|
|
23931
|
+
if (!input.options.code) {
|
|
23932
|
+
const callback = await createOAuthLoopbackCallback({
|
|
23933
|
+
successHtml: () => renderOAuthLoopbackPage({
|
|
23934
|
+
status: "success",
|
|
23935
|
+
eyebrow: "Auto CLI",
|
|
23936
|
+
title: "Login authorized",
|
|
23937
|
+
message: "Auto received the browser authorization. The CLI will finish signing you in from your terminal.",
|
|
23938
|
+
details: [{ label: "Server", value: serverUrl }]
|
|
23939
|
+
}),
|
|
23940
|
+
failureHtml: () => renderOAuthLoopbackPage({
|
|
23941
|
+
status: "failure",
|
|
23942
|
+
eyebrow: "Auto CLI",
|
|
23943
|
+
title: "Login failed",
|
|
23944
|
+
message: "The browser authorization did not complete. Return to your terminal to retry or inspect the error.",
|
|
23945
|
+
details: [{ label: "Server", value: serverUrl }]
|
|
23946
|
+
})
|
|
23947
|
+
});
|
|
23948
|
+
try {
|
|
23949
|
+
const authorizeUrl = new URL("/auth/cli", serverUrl);
|
|
23950
|
+
authorizeUrl.searchParams.set("pkce_challenge", pkceChallenge(verifier));
|
|
23951
|
+
authorizeUrl.searchParams.set("redirect_uri", callback.redirectUri);
|
|
23952
|
+
input.writeOutput(`Opening ${style.url(authorizeUrl.toString())}`);
|
|
23953
|
+
input.writeOutput(style.dim("Waiting for browser authorization..."));
|
|
23954
|
+
openBrowser(authorizeUrl.toString());
|
|
23955
|
+
const { code } = await callback.result;
|
|
23956
|
+
const token3 = await exchangeAuthorizationCode({
|
|
23957
|
+
code,
|
|
23958
|
+
fetch: input.fetch,
|
|
23959
|
+
redirectUri: callback.redirectUri,
|
|
23960
|
+
serverUrl,
|
|
23961
|
+
verifier
|
|
23962
|
+
});
|
|
23963
|
+
await finishLogin({
|
|
23964
|
+
token: token3,
|
|
23965
|
+
serverUrl,
|
|
23966
|
+
fetch: input.fetch,
|
|
23967
|
+
configPath: input.configPath,
|
|
23968
|
+
writeOutput: input.writeOutput,
|
|
23969
|
+
style
|
|
23970
|
+
});
|
|
23971
|
+
return;
|
|
23972
|
+
} finally {
|
|
23973
|
+
callback.close();
|
|
23974
|
+
}
|
|
23975
|
+
}
|
|
23976
|
+
const token2 = await exchangeAuthorizationCode({
|
|
23977
|
+
code: input.options.code,
|
|
23978
|
+
fetch: input.fetch,
|
|
23979
|
+
redirectUri: "http://127.0.0.1/callback",
|
|
23980
|
+
serverUrl,
|
|
23981
|
+
verifier
|
|
23982
|
+
});
|
|
23983
|
+
await finishLogin({
|
|
23984
|
+
token: token2,
|
|
23985
|
+
serverUrl,
|
|
23986
|
+
fetch: input.fetch,
|
|
23987
|
+
configPath: input.configPath,
|
|
23988
|
+
writeOutput: input.writeOutput,
|
|
23989
|
+
style
|
|
23990
|
+
});
|
|
23991
|
+
}
|
|
23992
|
+
async function exchangeAuthorizationCode(input) {
|
|
23993
|
+
return postJson(
|
|
23994
|
+
input.fetch,
|
|
23995
|
+
`${input.serverUrl}/api/v1/auth/cli/token`,
|
|
23996
|
+
{
|
|
23997
|
+
grant_type: "authorization_code",
|
|
23998
|
+
code: input.code,
|
|
23999
|
+
code_verifier: input.verifier,
|
|
24000
|
+
redirect_uri: input.redirectUri
|
|
24001
|
+
}
|
|
24002
|
+
);
|
|
24003
|
+
}
|
|
24004
|
+
async function finishLogin(input) {
|
|
24005
|
+
const { token: token2, serverUrl } = input;
|
|
24006
|
+
const storedAccount = token2.user ? findStoredAccountConfig({
|
|
24007
|
+
configPath: input.configPath,
|
|
24008
|
+
serverUrl,
|
|
24009
|
+
userId: token2.user.id,
|
|
24010
|
+
userEmail: token2.user.email
|
|
24011
|
+
}) : void 0;
|
|
24012
|
+
const selection = storedAccount?.organizationId && storedAccount.projectId ? {
|
|
24013
|
+
organizationId: storedAccount.organizationId,
|
|
24014
|
+
projectId: storedAccount.projectId
|
|
24015
|
+
} : void 0;
|
|
24016
|
+
let config2 = {
|
|
24017
|
+
serverUrl,
|
|
24018
|
+
userId: token2.user?.id,
|
|
24019
|
+
userEmail: token2.user?.email,
|
|
24020
|
+
refreshToken: token2.refresh_token,
|
|
24021
|
+
accessToken: token2.access_token,
|
|
24022
|
+
accessTokenExpiresAt: token2.access_token ? accessTokenExpiresAt(token2) : void 0
|
|
24023
|
+
};
|
|
24024
|
+
if (selection) {
|
|
24025
|
+
try {
|
|
24026
|
+
const scoped = await postJson(
|
|
24027
|
+
input.fetch,
|
|
24028
|
+
`${serverUrl}/api/v1/auth/cli/token`,
|
|
24029
|
+
{
|
|
24030
|
+
grant_type: "refresh_token",
|
|
24031
|
+
refresh_token: token2.refresh_token,
|
|
24032
|
+
organization_id: selection.organizationId,
|
|
24033
|
+
project_id: selection.projectId
|
|
24034
|
+
}
|
|
24035
|
+
);
|
|
24036
|
+
config2 = {
|
|
24037
|
+
...config2,
|
|
24038
|
+
...selection,
|
|
24039
|
+
refreshToken: scoped.refresh_token,
|
|
24040
|
+
accessToken: scoped.access_token,
|
|
24041
|
+
accessTokenExpiresAt: scoped.access_token ? accessTokenExpiresAt(scoped) : void 0,
|
|
24042
|
+
accessTokenOrganizationId: scoped.access_token ? selection.organizationId : void 0,
|
|
24043
|
+
accessTokenProjectId: scoped.access_token ? selection.projectId : void 0
|
|
24044
|
+
};
|
|
24045
|
+
} catch {
|
|
24046
|
+
input.writeOutput(
|
|
24047
|
+
input.style.warn(
|
|
24048
|
+
"The saved organization/project selection is not available for this account; run `auto orgs list` to pick a new one."
|
|
24049
|
+
)
|
|
24050
|
+
);
|
|
24051
|
+
}
|
|
24052
|
+
}
|
|
24053
|
+
persistLogin(config2, input);
|
|
24054
|
+
input.writeOutput(
|
|
24055
|
+
input.style.success(
|
|
24056
|
+
token2.user ? `Logged in as ${token2.user.email}.` : "Logged in."
|
|
24057
|
+
)
|
|
24058
|
+
);
|
|
24059
|
+
}
|
|
24060
|
+
function persistLogin(config2, input) {
|
|
24061
|
+
const directAccountPath = input.configPath !== void 0 && isAccountPath(input.configPath);
|
|
24062
|
+
const accountKey = saveAccount({
|
|
24063
|
+
config: config2,
|
|
24064
|
+
accountKey: directAccountPath && input.configPath ? accountKeyFromPath(input.configPath) : void 0,
|
|
24065
|
+
configPath: input.configPath
|
|
24066
|
+
});
|
|
24067
|
+
if (!directAccountPath) {
|
|
24068
|
+
setActiveAccount(accountKey, input.configPath);
|
|
24069
|
+
}
|
|
24070
|
+
}
|
|
24071
|
+
async function sleep2(ms) {
|
|
24072
|
+
await new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
24073
|
+
}
|
|
24074
|
+
var init_login = __esm({
|
|
24075
|
+
"src/commands/auth/login.ts"() {
|
|
24076
|
+
"use strict";
|
|
24077
|
+
init_base_url();
|
|
24078
|
+
init_http();
|
|
24079
|
+
init_tokens();
|
|
24080
|
+
init_browser();
|
|
24081
|
+
init_accounts();
|
|
24082
|
+
init_file();
|
|
24083
|
+
init_path();
|
|
24084
|
+
init_loopback();
|
|
24085
|
+
init_style();
|
|
24086
|
+
init_pkce();
|
|
24087
|
+
}
|
|
24088
|
+
});
|
|
24089
|
+
|
|
24091
24090
|
// src/lib/resources.ts
|
|
24092
24091
|
function parseProjectResourceReference(resource) {
|
|
24093
24092
|
const [kind, name, extra] = resource.split("/");
|
|
@@ -29501,9 +29500,11 @@ function SplashView({
|
|
|
29501
29500
|
const { stdout } = useStdout8();
|
|
29502
29501
|
const termWidth = stdout?.columns ?? 120;
|
|
29503
29502
|
const termHeight = stdout?.rows ?? 24;
|
|
29504
|
-
const logo =
|
|
29503
|
+
const logo = splashLogo(termWidth);
|
|
29505
29504
|
const logoWidth2 = Math.max(...logo.map((line) => line.length));
|
|
29506
29505
|
const logoRows = keyedLogoRows(logo);
|
|
29506
|
+
const shimmerStep = splashLogoShimmerStep(logoWidth2);
|
|
29507
|
+
const shimmerRadius = splashLogoShimmerRadius(logoWidth2);
|
|
29507
29508
|
const [step, setStep] = useState4(0);
|
|
29508
29509
|
const [frame, setFrame] = useState4(0);
|
|
29509
29510
|
useEffect4(() => {
|
|
@@ -29559,7 +29560,9 @@ function SplashView({
|
|
|
29559
29560
|
{
|
|
29560
29561
|
frame,
|
|
29561
29562
|
line: row.line,
|
|
29562
|
-
rowIndex: row.rowIndex
|
|
29563
|
+
rowIndex: row.rowIndex,
|
|
29564
|
+
shimmerStep,
|
|
29565
|
+
shimmerRadius
|
|
29563
29566
|
},
|
|
29564
29567
|
row.key
|
|
29565
29568
|
))
|
|
@@ -29586,6 +29589,27 @@ function SplashView({
|
|
|
29586
29589
|
}
|
|
29587
29590
|
);
|
|
29588
29591
|
}
|
|
29592
|
+
function splashLogo(termWidth) {
|
|
29593
|
+
return autoLogo(
|
|
29594
|
+
void 0,
|
|
29595
|
+
Math.max(0, termWidth - SPLASH_LOGO_OPTICAL_OFFSET)
|
|
29596
|
+
);
|
|
29597
|
+
}
|
|
29598
|
+
function splashLogoShimmerStep(logoWidth2) {
|
|
29599
|
+
if (splashUsesLargeLogoTreatment(logoWidth2)) {
|
|
29600
|
+
return LARGE_LOGO_SHIMMER_STEP;
|
|
29601
|
+
}
|
|
29602
|
+
if (logoWidth2 === MEDIUM_LOGO_WIDTH) {
|
|
29603
|
+
return MEDIUM_LOGO_SHIMMER_STEP;
|
|
29604
|
+
}
|
|
29605
|
+
return DEFAULT_SHIMMER_STEP;
|
|
29606
|
+
}
|
|
29607
|
+
function splashLogoShimmerRadius(logoWidth2) {
|
|
29608
|
+
return splashUsesLargeLogoTreatment(logoWidth2) ? LARGE_LOGO_SHIMMER_RADIUS : DEFAULT_SHIMMER_RADIUS;
|
|
29609
|
+
}
|
|
29610
|
+
function splashUsesLargeLogoTreatment(logoWidth2) {
|
|
29611
|
+
return logoWidth2 >= LARGE_LOGO_WIDTH;
|
|
29612
|
+
}
|
|
29589
29613
|
function keyedLogoRows(logo) {
|
|
29590
29614
|
const seen = /* @__PURE__ */ new Map();
|
|
29591
29615
|
return logo.map((line, rowIndex) => {
|
|
@@ -29597,9 +29621,11 @@ function keyedLogoRows(logo) {
|
|
|
29597
29621
|
function ShimmerLogoLine({
|
|
29598
29622
|
frame,
|
|
29599
29623
|
line,
|
|
29600
|
-
rowIndex
|
|
29624
|
+
rowIndex,
|
|
29625
|
+
shimmerStep,
|
|
29626
|
+
shimmerRadius
|
|
29601
29627
|
}) {
|
|
29602
|
-
const highlight = frame *
|
|
29628
|
+
const highlight = frame * shimmerStep % (line.length + 8);
|
|
29603
29629
|
return /* @__PURE__ */ jsx15(Text15, { children: Array.from(line).map((char, index) => {
|
|
29604
29630
|
const distance = Math.abs(index - highlight + rowIndex);
|
|
29605
29631
|
const key = `${index}:${char}`;
|
|
@@ -29609,16 +29635,16 @@ function ShimmerLogoLine({
|
|
|
29609
29635
|
return /* @__PURE__ */ jsx15(
|
|
29610
29636
|
Text15,
|
|
29611
29637
|
{
|
|
29612
|
-
color: distance <=
|
|
29613
|
-
bold: distance <=
|
|
29614
|
-
dimColor: distance >
|
|
29638
|
+
color: distance <= shimmerRadius ? "white" : "cyan",
|
|
29639
|
+
bold: distance <= shimmerRadius,
|
|
29640
|
+
dimColor: distance > shimmerRadius + 3,
|
|
29615
29641
|
children: char
|
|
29616
29642
|
},
|
|
29617
29643
|
key
|
|
29618
29644
|
);
|
|
29619
29645
|
}) });
|
|
29620
29646
|
}
|
|
29621
|
-
var STEPS, STEP_MS, FRAME_MS,
|
|
29647
|
+
var STEPS, STEP_MS, FRAME_MS, SPLASH_LOGO_OPTICAL_OFFSET, SPLASH_STATUS_OPTICAL_OFFSET, LARGE_LOGO_WIDTH, MEDIUM_LOGO_WIDTH, DEFAULT_SHIMMER_STEP, LARGE_LOGO_SHIMMER_STEP, MEDIUM_LOGO_SHIMMER_STEP, DEFAULT_SHIMMER_RADIUS, LARGE_LOGO_SHIMMER_RADIUS;
|
|
29622
29648
|
var init_SplashView = __esm({
|
|
29623
29649
|
"src/tui/SplashView.tsx"() {
|
|
29624
29650
|
"use strict";
|
|
@@ -29631,9 +29657,15 @@ var init_SplashView = __esm({
|
|
|
29631
29657
|
];
|
|
29632
29658
|
STEP_MS = 380;
|
|
29633
29659
|
FRAME_MS = 90;
|
|
29634
|
-
SPLASH_LOGO_MAX_WIDTH = 36;
|
|
29635
29660
|
SPLASH_LOGO_OPTICAL_OFFSET = 4;
|
|
29636
29661
|
SPLASH_STATUS_OPTICAL_OFFSET = 2;
|
|
29662
|
+
LARGE_LOGO_WIDTH = 100;
|
|
29663
|
+
MEDIUM_LOGO_WIDTH = 66;
|
|
29664
|
+
DEFAULT_SHIMMER_STEP = 2;
|
|
29665
|
+
LARGE_LOGO_SHIMMER_STEP = 6;
|
|
29666
|
+
MEDIUM_LOGO_SHIMMER_STEP = 4;
|
|
29667
|
+
DEFAULT_SHIMMER_RADIUS = 1;
|
|
29668
|
+
LARGE_LOGO_SHIMMER_RADIUS = 3;
|
|
29637
29669
|
}
|
|
29638
29670
|
});
|
|
29639
29671
|
|
|
@@ -29643,7 +29675,7 @@ __export(launcher_exports, {
|
|
|
29643
29675
|
launch: () => launch
|
|
29644
29676
|
});
|
|
29645
29677
|
import { spawnSync } from "child_process";
|
|
29646
|
-
import { existsSync as
|
|
29678
|
+
import { existsSync as existsSync4 } from "fs";
|
|
29647
29679
|
import { dirname as dirname6, resolve as resolve3 } from "path";
|
|
29648
29680
|
import { fileURLToPath } from "url";
|
|
29649
29681
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
@@ -29833,7 +29865,7 @@ function resolveLatestReleaseVersionFromCheckout() {
|
|
|
29833
29865
|
function findRepoRoot(startDirectory) {
|
|
29834
29866
|
let directory = startDirectory;
|
|
29835
29867
|
while (true) {
|
|
29836
|
-
if (
|
|
29868
|
+
if (existsSync4(resolve3(directory, ".git")) && existsSync4(resolve3(directory, "apps/cli/package.json"))) {
|
|
29837
29869
|
return directory;
|
|
29838
29870
|
}
|
|
29839
29871
|
const parent = dirname6(directory);
|
|
@@ -29900,16 +29932,13 @@ var init_launcher = __esm({
|
|
|
29900
29932
|
});
|
|
29901
29933
|
|
|
29902
29934
|
// src/cli/program.ts
|
|
29903
|
-
import { existsSync as existsSync6 } from "fs";
|
|
29904
29935
|
import { Command, Option as Option4 } from "commander";
|
|
29905
29936
|
|
|
29906
29937
|
// src/commands/account/commands.ts
|
|
29907
|
-
init_base_url();
|
|
29908
|
-
init_login();
|
|
29909
29938
|
import { Option } from "commander";
|
|
29910
29939
|
|
|
29911
|
-
// src/commands/auth/
|
|
29912
|
-
import {
|
|
29940
|
+
// src/commands/auth/accounts.ts
|
|
29941
|
+
import { rmSync } from "fs";
|
|
29913
29942
|
|
|
29914
29943
|
// src/lib/api/context.ts
|
|
29915
29944
|
init_client();
|
|
@@ -29932,10 +29961,102 @@ function createContextApiClient(context) {
|
|
|
29932
29961
|
});
|
|
29933
29962
|
}
|
|
29934
29963
|
|
|
29935
|
-
// src/commands/auth/
|
|
29964
|
+
// src/commands/auth/accounts.ts
|
|
29965
|
+
init_accounts();
|
|
29936
29966
|
init_file();
|
|
29937
|
-
|
|
29938
|
-
|
|
29967
|
+
|
|
29968
|
+
// src/lib/stdio/select.ts
|
|
29969
|
+
async function selectFromList(context, input) {
|
|
29970
|
+
if (input.items.length === 0) {
|
|
29971
|
+
throw new Error("No options available.");
|
|
29972
|
+
}
|
|
29973
|
+
if (!context.io.canPrompt()) {
|
|
29974
|
+
throw new Error("Cannot prompt in a non-interactive terminal.");
|
|
29975
|
+
}
|
|
29976
|
+
const stdin = context.stdin;
|
|
29977
|
+
const stdout = context.stdout;
|
|
29978
|
+
let selected = clamp(input.initialIndex ?? 0, input.items.length);
|
|
29979
|
+
let renderedLines = 0;
|
|
29980
|
+
let rawModeWasEnabled = Boolean(stdin.isRaw);
|
|
29981
|
+
return await new Promise((resolve4, reject) => {
|
|
29982
|
+
let settled = false;
|
|
29983
|
+
const settle = (callback) => {
|
|
29984
|
+
if (settled) return;
|
|
29985
|
+
settled = true;
|
|
29986
|
+
cleanup();
|
|
29987
|
+
callback();
|
|
29988
|
+
};
|
|
29989
|
+
const render2 = () => {
|
|
29990
|
+
clearRendered(stdout, renderedLines);
|
|
29991
|
+
const lines = [
|
|
29992
|
+
input.title,
|
|
29993
|
+
"",
|
|
29994
|
+
...input.items.map(
|
|
29995
|
+
(item, index) => input.renderItem(item, index === selected)
|
|
29996
|
+
),
|
|
29997
|
+
"",
|
|
29998
|
+
"up/down navigate enter select esc cancel"
|
|
29999
|
+
];
|
|
30000
|
+
renderedLines = lines.length;
|
|
30001
|
+
stdout.write(`${lines.join("\n")}
|
|
30002
|
+
`);
|
|
30003
|
+
};
|
|
30004
|
+
const onData = (chunk) => {
|
|
30005
|
+
const key = chunk.toString("utf8");
|
|
30006
|
+
if (key === "") {
|
|
30007
|
+
settle(() => reject(new Error("Cancelled.")));
|
|
30008
|
+
return;
|
|
30009
|
+
}
|
|
30010
|
+
if (key === "\x1B") {
|
|
30011
|
+
settle(() => reject(new Error("Cancelled.")));
|
|
30012
|
+
return;
|
|
30013
|
+
}
|
|
30014
|
+
if (key === "\r" || key === "\n") {
|
|
30015
|
+
settle(() => resolve4(input.items[selected]));
|
|
30016
|
+
return;
|
|
30017
|
+
}
|
|
30018
|
+
if (key === "\x1B[A" || key === "k") {
|
|
30019
|
+
selected = selected === 0 ? input.items.length - 1 : selected - 1;
|
|
30020
|
+
render2();
|
|
30021
|
+
return;
|
|
30022
|
+
}
|
|
30023
|
+
if (key === "\x1B[B" || key === "j") {
|
|
30024
|
+
selected = selected === input.items.length - 1 ? 0 : selected + 1;
|
|
30025
|
+
render2();
|
|
30026
|
+
}
|
|
30027
|
+
};
|
|
30028
|
+
const cleanup = () => {
|
|
30029
|
+
stdin.off?.("data", onData);
|
|
30030
|
+
clearRendered(stdout, renderedLines);
|
|
30031
|
+
renderedLines = 0;
|
|
30032
|
+
stdout.write("\x1B[?25h");
|
|
30033
|
+
if (stdin.setRawMode) {
|
|
30034
|
+
stdin.setRawMode(rawModeWasEnabled);
|
|
30035
|
+
}
|
|
30036
|
+
stdin.pause?.();
|
|
30037
|
+
};
|
|
30038
|
+
try {
|
|
30039
|
+
rawModeWasEnabled = Boolean(stdin.isRaw);
|
|
30040
|
+
stdin.setEncoding?.("utf8");
|
|
30041
|
+
stdin.setRawMode?.(true);
|
|
30042
|
+
stdin.resume?.();
|
|
30043
|
+
stdout.write("\x1B[?25l");
|
|
30044
|
+
stdin.on("data", onData);
|
|
30045
|
+
render2();
|
|
30046
|
+
} catch (error51) {
|
|
30047
|
+
settle(() => reject(error51));
|
|
30048
|
+
}
|
|
30049
|
+
});
|
|
30050
|
+
}
|
|
30051
|
+
function clamp(value, length) {
|
|
30052
|
+
return Math.min(Math.max(0, value), length - 1);
|
|
30053
|
+
}
|
|
30054
|
+
function clearRendered(stdout, lines) {
|
|
30055
|
+
if (lines === 0) return;
|
|
30056
|
+
stdout.write(`\x1B[${lines}A\x1B[J`);
|
|
30057
|
+
}
|
|
30058
|
+
|
|
30059
|
+
// src/commands/auth/accounts.ts
|
|
29939
30060
|
async function handleAuthStatus(context) {
|
|
29940
30061
|
const result = await fetchAuthStatus(context);
|
|
29941
30062
|
context.io.writeResult(result, formatAuthStatusText);
|
|
@@ -29946,7 +30067,7 @@ async function handleWhoami(context) {
|
|
|
29946
30067
|
context.io.writeResult(whoami, formatWhoamiText);
|
|
29947
30068
|
}
|
|
29948
30069
|
function logout(context) {
|
|
29949
|
-
if (
|
|
30070
|
+
if (activeAccountKey(context.configPath) !== void 0) {
|
|
29950
30071
|
const config2 = readConfig(context.configPath);
|
|
29951
30072
|
writeConfig(
|
|
29952
30073
|
{
|
|
@@ -29962,62 +30083,103 @@ function logout(context) {
|
|
|
29962
30083
|
}
|
|
29963
30084
|
context.writeOutput(context.io.style.success("Logged out."));
|
|
29964
30085
|
}
|
|
29965
|
-
function
|
|
29966
|
-
const
|
|
29967
|
-
if (
|
|
30086
|
+
function listAccounts2(context) {
|
|
30087
|
+
const accounts = listAccounts(context.configPath);
|
|
30088
|
+
if (accounts.length === 0) {
|
|
29968
30089
|
throw new Error("No stored accounts. Session `auto auth login` first.");
|
|
29969
30090
|
}
|
|
29970
|
-
for (const
|
|
30091
|
+
for (const account of accounts) {
|
|
29971
30092
|
context.writeOutput(
|
|
29972
30093
|
accountLine(
|
|
29973
|
-
|
|
29974
|
-
|
|
30094
|
+
account,
|
|
30095
|
+
activeAccountKey(context.configPath),
|
|
29975
30096
|
context.io.style
|
|
29976
30097
|
)
|
|
29977
30098
|
);
|
|
29978
30099
|
}
|
|
29979
30100
|
}
|
|
29980
|
-
function switchAccount(context,
|
|
30101
|
+
async function switchAccount(context, input = {}) {
|
|
30102
|
+
const accountRef = input.user ?? input.accountRef;
|
|
30103
|
+
if (input.user && input.accountRef) {
|
|
30104
|
+
throw new Error("Choose either an account argument or --user, not both.");
|
|
30105
|
+
}
|
|
29981
30106
|
if (!accountRef) {
|
|
29982
|
-
|
|
30107
|
+
await switchAccountInteractive(context);
|
|
29983
30108
|
return;
|
|
29984
30109
|
}
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
|
|
29988
|
-
|
|
30110
|
+
useAccount(
|
|
30111
|
+
context,
|
|
30112
|
+
resolveAccountByEmail(context, accountRef, {
|
|
30113
|
+
server: input.server,
|
|
30114
|
+
preferActiveServer: true,
|
|
30115
|
+
command: "switch"
|
|
30116
|
+
})
|
|
30117
|
+
);
|
|
30118
|
+
}
|
|
30119
|
+
function removeAccount(context, input) {
|
|
30120
|
+
const account = resolveAccountByEmail(context, input.accountRef, {
|
|
30121
|
+
server: input.server,
|
|
30122
|
+
preferActiveServer: false,
|
|
30123
|
+
command: "remove"
|
|
30124
|
+
});
|
|
30125
|
+
if (activeAccountKey(context.configPath) === account.key) {
|
|
30126
|
+
clearActiveAccount(context.configPath);
|
|
29989
30127
|
}
|
|
29990
|
-
|
|
29991
|
-
|
|
30128
|
+
rmSync(account.path);
|
|
30129
|
+
context.writeOutput(
|
|
30130
|
+
context.io.style.success(
|
|
30131
|
+
`Removed account ${account.config.userEmail} (${account.config.serverUrl}).`
|
|
30132
|
+
)
|
|
30133
|
+
);
|
|
30134
|
+
}
|
|
30135
|
+
function accountLine(account, activeKey, style) {
|
|
30136
|
+
return [
|
|
30137
|
+
account.config.userEmail,
|
|
30138
|
+
style.dim(`server=${account.config.serverUrl ?? "(unset)"}`),
|
|
30139
|
+
account.config.refreshToken ? void 0 : style.warn("logged_out"),
|
|
30140
|
+
account.key === activeKey ? style.success("(active)") : void 0
|
|
30141
|
+
].filter(Boolean).join(" ");
|
|
30142
|
+
}
|
|
30143
|
+
async function switchAccountInteractive(context) {
|
|
30144
|
+
const accounts = listAccounts(context.configPath);
|
|
30145
|
+
if (accounts.length === 0) {
|
|
29992
30146
|
throw new Error("No stored accounts. Session `auto auth login` first.");
|
|
29993
30147
|
}
|
|
29994
|
-
|
|
29995
|
-
const matches = byName ? [byName] : profiles.filter(
|
|
29996
|
-
(profile) => profile.config.userEmail?.toLowerCase() === accountRef.toLowerCase() && (!options.server || profile.config.serverUrl === options.server)
|
|
29997
|
-
);
|
|
29998
|
-
if (matches.length === 0) {
|
|
30148
|
+
if (!context.io.canPrompt()) {
|
|
29999
30149
|
throw new Error(
|
|
30000
|
-
|
|
30150
|
+
"Cannot prompt for an account in a non-interactive terminal. Rerun with `auto auth switch --user <email>`."
|
|
30001
30151
|
);
|
|
30002
30152
|
}
|
|
30003
|
-
const
|
|
30004
|
-
const
|
|
30005
|
-
|
|
30153
|
+
const activeKey = activeAccountKey(context.configPath);
|
|
30154
|
+
const initialIndex = Math.max(
|
|
30155
|
+
0,
|
|
30156
|
+
accounts.findIndex((account) => account.key === activeKey)
|
|
30006
30157
|
);
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30158
|
+
const selected = await selectFromList(context, {
|
|
30159
|
+
title: "Select account",
|
|
30160
|
+
items: accounts,
|
|
30161
|
+
initialIndex,
|
|
30162
|
+
renderItem: (account, selected2) => interactiveAccountLine(account, activeKey, selected2, context.io.style)
|
|
30163
|
+
});
|
|
30164
|
+
useAccount(context, selected);
|
|
30165
|
+
}
|
|
30166
|
+
function interactiveAccountLine(account, activeKey, selected, style) {
|
|
30167
|
+
return [
|
|
30168
|
+
selected ? ">" : " ",
|
|
30169
|
+
account.config.userEmail,
|
|
30170
|
+
style.dim(account.config.serverUrl ?? "(unset)"),
|
|
30171
|
+
account.config.refreshToken ? void 0 : style.warn("logged_out"),
|
|
30172
|
+
account.key === activeKey ? style.success("active") : void 0
|
|
30173
|
+
].filter(Boolean).join(" ");
|
|
30174
|
+
}
|
|
30175
|
+
function useAccount(context, account) {
|
|
30176
|
+
setActiveAccount(account.key, context.configPath);
|
|
30015
30177
|
context.writeOutput(
|
|
30016
30178
|
context.io.style.success(
|
|
30017
|
-
`Switched to ${
|
|
30179
|
+
`Switched to ${account.config.userEmail} (${account.config.serverUrl}).`
|
|
30018
30180
|
)
|
|
30019
30181
|
);
|
|
30020
|
-
if (!
|
|
30182
|
+
if (!account.config.refreshToken) {
|
|
30021
30183
|
context.writeOutput(
|
|
30022
30184
|
context.io.style.warn(
|
|
30023
30185
|
"This account has no stored credentials; run `auto auth login`."
|
|
@@ -30025,35 +30187,32 @@ function switchAccount(context, accountRef, options = {}) {
|
|
|
30025
30187
|
);
|
|
30026
30188
|
}
|
|
30027
30189
|
}
|
|
30028
|
-
function
|
|
30029
|
-
|
|
30030
|
-
if (
|
|
30031
|
-
throw new Error(
|
|
30190
|
+
function resolveAccountByEmail(context, accountRef, options) {
|
|
30191
|
+
const accounts = listAccounts(context.configPath);
|
|
30192
|
+
if (accounts.length === 0) {
|
|
30193
|
+
throw new Error("No stored accounts. Session `auto auth login` first.");
|
|
30032
30194
|
}
|
|
30033
|
-
const
|
|
30034
|
-
|
|
30035
|
-
|
|
30036
|
-
|
|
30037
|
-
(profile) => profile.name
|
|
30038
|
-
);
|
|
30195
|
+
const matches = accounts.filter(
|
|
30196
|
+
(account) => account.config.userEmail?.toLowerCase() === accountRef.toLowerCase() && (!options.server || account.config.serverUrl === options.server)
|
|
30197
|
+
);
|
|
30198
|
+
if (matches.length === 0) {
|
|
30039
30199
|
throw new Error(
|
|
30040
|
-
`No stored
|
|
30200
|
+
`No stored account for ${accountRef}. Session \`auto auth login\` to add it.`
|
|
30041
30201
|
);
|
|
30042
30202
|
}
|
|
30043
|
-
if (
|
|
30044
|
-
|
|
30203
|
+
if (matches.length === 1) return matches[0];
|
|
30204
|
+
if (options.preferActiveServer) {
|
|
30205
|
+
const active = readConfig(context.configPath);
|
|
30206
|
+
const activeServerMatch = matches.find(
|
|
30207
|
+
(account) => account.config.serverUrl === active.serverUrl
|
|
30208
|
+
);
|
|
30209
|
+
if (activeServerMatch) return activeServerMatch;
|
|
30045
30210
|
}
|
|
30046
|
-
|
|
30047
|
-
|
|
30048
|
-
|
|
30049
|
-
|
|
30050
|
-
|
|
30051
|
-
profile.name,
|
|
30052
|
-
profile.config.userEmail,
|
|
30053
|
-
style.dim(`server=${profile.config.serverUrl ?? "(unset)"}`),
|
|
30054
|
-
profile.config.refreshToken ? void 0 : style.warn("logged_out"),
|
|
30055
|
-
profile.name === activeName ? style.success("(active)") : void 0
|
|
30056
|
-
].filter(Boolean).join(" ");
|
|
30211
|
+
throw new Error(
|
|
30212
|
+
`Multiple servers have a stored account for ${accountRef}: ${matches.map((account) => account.config.serverUrl).join(
|
|
30213
|
+
", "
|
|
30214
|
+
)}. Pick one with \`auto auth ${options.command} ${accountRef} --server <url>\`.`
|
|
30215
|
+
);
|
|
30057
30216
|
}
|
|
30058
30217
|
async function fetchAuthStatus(context) {
|
|
30059
30218
|
const config2 = readConfig(context.configPath);
|
|
@@ -30382,7 +30541,7 @@ async function authorizeSensitiveActionDevice(input) {
|
|
|
30382
30541
|
let firstAttempt = true;
|
|
30383
30542
|
while (Date.now() < deadline) {
|
|
30384
30543
|
if (!firstAttempt) {
|
|
30385
|
-
await
|
|
30544
|
+
await sleep(Math.max(0, device.interval) * 1e3);
|
|
30386
30545
|
}
|
|
30387
30546
|
firstAttempt = false;
|
|
30388
30547
|
try {
|
|
@@ -30407,39 +30566,26 @@ async function requireYes(rl, prompt) {
|
|
|
30407
30566
|
throw new Error("Account deletion cancelled.");
|
|
30408
30567
|
}
|
|
30409
30568
|
}
|
|
30410
|
-
async function
|
|
30569
|
+
async function sleep(ms) {
|
|
30411
30570
|
await new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
30412
30571
|
}
|
|
30413
30572
|
|
|
30414
30573
|
// src/commands/account/commands.ts
|
|
30415
30574
|
function registerAccountCommands(program, context) {
|
|
30416
|
-
const account = program.command("account").description("Manage
|
|
30417
|
-
account.command("list").description("
|
|
30418
|
-
account.command("
|
|
30419
|
-
async (
|
|
30420
|
-
|
|
30421
|
-
|
|
30422
|
-
|
|
30423
|
-
...options,
|
|
30424
|
-
profile: name,
|
|
30425
|
-
apiUrl: resolveApiBaseUrl({
|
|
30426
|
-
explicit: [options.apiUrl, globalOptions.apiUrl],
|
|
30427
|
-
env: context.env
|
|
30428
|
-
})
|
|
30429
|
-
},
|
|
30430
|
-
env: context.env,
|
|
30431
|
-
fetch: context.fetch,
|
|
30432
|
-
configPath: context.configPath,
|
|
30433
|
-
writeOutput: context.writeOutput,
|
|
30434
|
-
writeError: context.writeError,
|
|
30435
|
-
style: context.io.style
|
|
30436
|
-
});
|
|
30437
|
-
}
|
|
30575
|
+
const account = program.command("account").description("Manage the signed-in Auto account.");
|
|
30576
|
+
account.command("list", { hidden: true }).description("Deprecated alias for `auth list`.").action(() => listAccounts2(context));
|
|
30577
|
+
account.command("switch", { hidden: true }).description("Deprecated alias for `auth switch`.").argument("<account>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
|
|
30578
|
+
async (accountRef, options) => await switchAccount(context, {
|
|
30579
|
+
accountRef,
|
|
30580
|
+
server: options.server
|
|
30581
|
+
})
|
|
30438
30582
|
);
|
|
30439
|
-
account.command("
|
|
30440
|
-
(accountRef, options) =>
|
|
30583
|
+
account.command("remove", { hidden: true }).description("Deprecated alias for `auth remove`.").argument("<account>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
|
|
30584
|
+
(accountRef, options) => removeAccount(context, {
|
|
30585
|
+
accountRef,
|
|
30586
|
+
server: options.server
|
|
30587
|
+
})
|
|
30441
30588
|
);
|
|
30442
|
-
account.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
|
|
30443
30589
|
account.command("delete").description("Permanently delete the signed-in Auto account.").option("--api-url <url>", "Auto web server URL").option(
|
|
30444
30590
|
"--delete-admin-orgs",
|
|
30445
30591
|
"delete organizations where this account is an admin"
|
|
@@ -30872,7 +31018,7 @@ import {
|
|
|
30872
31018
|
createCipheriv,
|
|
30873
31019
|
createDecipheriv,
|
|
30874
31020
|
hkdfSync,
|
|
30875
|
-
randomBytes
|
|
31021
|
+
randomBytes
|
|
30876
31022
|
} from "crypto";
|
|
30877
31023
|
var BOOTSTRAP_CRYPTO_INFO = "auto-agent-bridge-bootstrap/v1";
|
|
30878
31024
|
var BOOTSTRAP_KEY_LENGTH = 32;
|
|
@@ -31397,14 +31543,14 @@ function partialTextDeltaPayload(message, messageId) {
|
|
|
31397
31543
|
}
|
|
31398
31544
|
|
|
31399
31545
|
// src/commands/agent-bridge/harness/claude-code/resume-store.ts
|
|
31400
|
-
import { existsSync
|
|
31546
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
31401
31547
|
import { dirname as dirname3 } from "path";
|
|
31402
31548
|
var AGENT_BRIDGE_RUNTIME_DIR = "/tmp/auto-bridge-runtime";
|
|
31403
31549
|
var CLAUDE_SESSION_RESUME_PATH = `${AGENT_BRIDGE_RUNTIME_DIR}/claude-session-id`;
|
|
31404
31550
|
function fileClaudeSessionResumeStore(path2 = CLAUDE_SESSION_RESUME_PATH) {
|
|
31405
31551
|
return {
|
|
31406
31552
|
read(sessionId) {
|
|
31407
|
-
if (!
|
|
31553
|
+
if (!existsSync(path2)) {
|
|
31408
31554
|
return null;
|
|
31409
31555
|
}
|
|
31410
31556
|
const record2 = parseResumeRecord(readFileSync2(path2, "utf8"));
|
|
@@ -32482,7 +32628,7 @@ function statusAgents(input) {
|
|
|
32482
32628
|
// src/commands/agents/connect.ts
|
|
32483
32629
|
init_resources2();
|
|
32484
32630
|
init_browser();
|
|
32485
|
-
import { existsSync as
|
|
32631
|
+
import { existsSync as existsSync2, mkdtempSync, writeFileSync as writeFileSync3 } from "fs";
|
|
32486
32632
|
import { homedir as homedir3, tmpdir } from "os";
|
|
32487
32633
|
import { join as join4 } from "path";
|
|
32488
32634
|
|
|
@@ -32688,7 +32834,7 @@ async function stageAvatarImage(input) {
|
|
|
32688
32834
|
const contentType = response.headers.get("content-type") ?? "";
|
|
32689
32835
|
const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
|
|
32690
32836
|
const downloads = join4(homedir3(), "Downloads");
|
|
32691
|
-
const directory =
|
|
32837
|
+
const directory = existsSync2(downloads) ? downloads : mkdtempSync(join4(tmpdir(), "auto-avatar-"));
|
|
32692
32838
|
const path2 = join4(directory, `${input.agent}-avatar${extension}`);
|
|
32693
32839
|
writeFileSync3(path2, Buffer.from(await response.arrayBuffer()));
|
|
32694
32840
|
return path2;
|
|
@@ -32940,7 +33086,6 @@ function registerAuthCommands(program, context) {
|
|
|
32940
33086
|
await login({
|
|
32941
33087
|
options: {
|
|
32942
33088
|
...options,
|
|
32943
|
-
profile: globalOptions.profile,
|
|
32944
33089
|
apiUrl: resolveApiBaseUrl({
|
|
32945
33090
|
explicit: [options.apiUrl, globalOptions.apiUrl],
|
|
32946
33091
|
env: context.env
|
|
@@ -32955,7 +33100,7 @@ function registerAuthCommands(program, context) {
|
|
|
32955
33100
|
});
|
|
32956
33101
|
});
|
|
32957
33102
|
auth.command("status").description(
|
|
32958
|
-
"Show current
|
|
33103
|
+
"Show current authentication state and validate the token against the server."
|
|
32959
33104
|
).action(async () => {
|
|
32960
33105
|
await handleAuthStatus(context);
|
|
32961
33106
|
});
|
|
@@ -32963,13 +33108,21 @@ function registerAuthCommands(program, context) {
|
|
|
32963
33108
|
await handleWhoami(context);
|
|
32964
33109
|
});
|
|
32965
33110
|
auth.command("logout").description("Remove the local user refresh token.").action(() => logout(context));
|
|
32966
|
-
auth.command("list").description("List stored
|
|
33111
|
+
auth.command("list").description("List stored accounts.").action(() => listAccounts2(context));
|
|
32967
33112
|
auth.command("switch").description(
|
|
32968
|
-
"Switch the active account
|
|
32969
|
-
).argument("[account]", "
|
|
32970
|
-
(account, options) =>
|
|
33113
|
+
"Switch the active account, prompting interactively when no account is provided."
|
|
33114
|
+
).argument("[account]", "Email address of a stored account").option("--user <email>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
|
|
33115
|
+
async (account, options) => {
|
|
33116
|
+
await switchAccount(context, {
|
|
33117
|
+
accountRef: account,
|
|
33118
|
+
user: options.user,
|
|
33119
|
+
server: options.server
|
|
33120
|
+
});
|
|
33121
|
+
}
|
|
33122
|
+
);
|
|
33123
|
+
auth.command("remove").description("Remove a stored account.").argument("<account>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
|
|
33124
|
+
(account, options) => removeAccount(context, { accountRef: account, server: options.server })
|
|
32971
33125
|
);
|
|
32972
|
-
auth.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
|
|
32973
33126
|
}
|
|
32974
33127
|
|
|
32975
33128
|
// src/lib/stdio/confirm.ts
|
|
@@ -36637,10 +36790,6 @@ function registerSyncCommands(program, context) {
|
|
|
36637
36790
|
});
|
|
36638
36791
|
}
|
|
36639
36792
|
|
|
36640
|
-
// src/cli/program.ts
|
|
36641
|
-
init_path();
|
|
36642
|
-
init_profiles();
|
|
36643
|
-
|
|
36644
36793
|
// src/lib/output/iostreams.ts
|
|
36645
36794
|
init_style();
|
|
36646
36795
|
function createIOStreams(flags) {
|
|
@@ -36734,12 +36883,9 @@ function createProgram(options = {}) {
|
|
|
36734
36883
|
"stream-json",
|
|
36735
36884
|
"tui"
|
|
36736
36885
|
])
|
|
36737
|
-
).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL")
|
|
36738
|
-
"--profile <name>",
|
|
36739
|
-
"use this stored profile for the invocation (or set AUTO_PROFILE); for `auth login`, the profile name to store the login under"
|
|
36740
|
-
);
|
|
36886
|
+
).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL");
|
|
36741
36887
|
program.configureHelp({ showGlobalOptions: true });
|
|
36742
|
-
program.hook("preAction", (
|
|
36888
|
+
program.hook("preAction", () => {
|
|
36743
36889
|
if (!options.io) {
|
|
36744
36890
|
const g = program.opts();
|
|
36745
36891
|
context.io = createIOStreams({
|
|
@@ -36749,22 +36895,6 @@ function createProgram(options = {}) {
|
|
|
36749
36895
|
writeError: options.writeError
|
|
36750
36896
|
});
|
|
36751
36897
|
}
|
|
36752
|
-
const isLoginLike = actionCommand.name() === "login" && actionCommand.parent?.name() === "auth" || actionCommand.name() === "create" && actionCommand.parent?.name() === "account";
|
|
36753
|
-
const flagPin = isLoginLike ? void 0 : program.opts().profile;
|
|
36754
|
-
const pin = flagPin ?? context.env.AUTO_PROFILE;
|
|
36755
|
-
if (pin && context.pinnedProfile !== pin) {
|
|
36756
|
-
assertValidProfileName(pin);
|
|
36757
|
-
const base = options.configPath ?? defaultConfigPath();
|
|
36758
|
-
const pinnedPath = profileFilePath(base, pin);
|
|
36759
|
-
if (!existsSync6(pinnedPath)) {
|
|
36760
|
-
const names = listProfiles(base).map((profile) => profile.name);
|
|
36761
|
-
throw new Error(
|
|
36762
|
-
`No stored profile "${pin}". Stored profiles: ${names.join(", ") || "(none)"}.`
|
|
36763
|
-
);
|
|
36764
|
-
}
|
|
36765
|
-
context.configPath = pinnedPath;
|
|
36766
|
-
context.pinnedProfile = pin;
|
|
36767
|
-
}
|
|
36768
36898
|
});
|
|
36769
36899
|
const launchTui = async () => {
|
|
36770
36900
|
const g = program.opts();
|