@autohq/cli 0.1.184 → 0.1.185
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 +1131 -1034
- 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.185",
|
|
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("/");
|
|
@@ -29643,7 +29642,7 @@ __export(launcher_exports, {
|
|
|
29643
29642
|
launch: () => launch
|
|
29644
29643
|
});
|
|
29645
29644
|
import { spawnSync } from "child_process";
|
|
29646
|
-
import { existsSync as
|
|
29645
|
+
import { existsSync as existsSync4 } from "fs";
|
|
29647
29646
|
import { dirname as dirname6, resolve as resolve3 } from "path";
|
|
29648
29647
|
import { fileURLToPath } from "url";
|
|
29649
29648
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
@@ -29833,7 +29832,7 @@ function resolveLatestReleaseVersionFromCheckout() {
|
|
|
29833
29832
|
function findRepoRoot(startDirectory) {
|
|
29834
29833
|
let directory = startDirectory;
|
|
29835
29834
|
while (true) {
|
|
29836
|
-
if (
|
|
29835
|
+
if (existsSync4(resolve3(directory, ".git")) && existsSync4(resolve3(directory, "apps/cli/package.json"))) {
|
|
29837
29836
|
return directory;
|
|
29838
29837
|
}
|
|
29839
29838
|
const parent = dirname6(directory);
|
|
@@ -29900,16 +29899,13 @@ var init_launcher = __esm({
|
|
|
29900
29899
|
});
|
|
29901
29900
|
|
|
29902
29901
|
// src/cli/program.ts
|
|
29903
|
-
import { existsSync as existsSync6 } from "fs";
|
|
29904
29902
|
import { Command, Option as Option4 } from "commander";
|
|
29905
29903
|
|
|
29906
29904
|
// src/commands/account/commands.ts
|
|
29907
|
-
init_base_url();
|
|
29908
|
-
init_login();
|
|
29909
29905
|
import { Option } from "commander";
|
|
29910
29906
|
|
|
29911
|
-
// src/commands/auth/
|
|
29912
|
-
import {
|
|
29907
|
+
// src/commands/auth/accounts.ts
|
|
29908
|
+
import { rmSync } from "fs";
|
|
29913
29909
|
|
|
29914
29910
|
// src/lib/api/context.ts
|
|
29915
29911
|
init_client();
|
|
@@ -29932,10 +29928,102 @@ function createContextApiClient(context) {
|
|
|
29932
29928
|
});
|
|
29933
29929
|
}
|
|
29934
29930
|
|
|
29935
|
-
// src/commands/auth/
|
|
29931
|
+
// src/commands/auth/accounts.ts
|
|
29932
|
+
init_accounts();
|
|
29936
29933
|
init_file();
|
|
29937
|
-
|
|
29938
|
-
|
|
29934
|
+
|
|
29935
|
+
// src/lib/stdio/select.ts
|
|
29936
|
+
async function selectFromList(context, input) {
|
|
29937
|
+
if (input.items.length === 0) {
|
|
29938
|
+
throw new Error("No options available.");
|
|
29939
|
+
}
|
|
29940
|
+
if (!context.io.canPrompt()) {
|
|
29941
|
+
throw new Error("Cannot prompt in a non-interactive terminal.");
|
|
29942
|
+
}
|
|
29943
|
+
const stdin = context.stdin;
|
|
29944
|
+
const stdout = context.stdout;
|
|
29945
|
+
let selected = clamp(input.initialIndex ?? 0, input.items.length);
|
|
29946
|
+
let renderedLines = 0;
|
|
29947
|
+
let rawModeWasEnabled = Boolean(stdin.isRaw);
|
|
29948
|
+
return await new Promise((resolve4, reject) => {
|
|
29949
|
+
let settled = false;
|
|
29950
|
+
const settle = (callback) => {
|
|
29951
|
+
if (settled) return;
|
|
29952
|
+
settled = true;
|
|
29953
|
+
cleanup();
|
|
29954
|
+
callback();
|
|
29955
|
+
};
|
|
29956
|
+
const render2 = () => {
|
|
29957
|
+
clearRendered(stdout, renderedLines);
|
|
29958
|
+
const lines = [
|
|
29959
|
+
input.title,
|
|
29960
|
+
"",
|
|
29961
|
+
...input.items.map(
|
|
29962
|
+
(item, index) => input.renderItem(item, index === selected)
|
|
29963
|
+
),
|
|
29964
|
+
"",
|
|
29965
|
+
"up/down navigate enter select esc cancel"
|
|
29966
|
+
];
|
|
29967
|
+
renderedLines = lines.length;
|
|
29968
|
+
stdout.write(`${lines.join("\n")}
|
|
29969
|
+
`);
|
|
29970
|
+
};
|
|
29971
|
+
const onData = (chunk) => {
|
|
29972
|
+
const key = chunk.toString("utf8");
|
|
29973
|
+
if (key === "") {
|
|
29974
|
+
settle(() => reject(new Error("Cancelled.")));
|
|
29975
|
+
return;
|
|
29976
|
+
}
|
|
29977
|
+
if (key === "\x1B") {
|
|
29978
|
+
settle(() => reject(new Error("Cancelled.")));
|
|
29979
|
+
return;
|
|
29980
|
+
}
|
|
29981
|
+
if (key === "\r" || key === "\n") {
|
|
29982
|
+
settle(() => resolve4(input.items[selected]));
|
|
29983
|
+
return;
|
|
29984
|
+
}
|
|
29985
|
+
if (key === "\x1B[A" || key === "k") {
|
|
29986
|
+
selected = selected === 0 ? input.items.length - 1 : selected - 1;
|
|
29987
|
+
render2();
|
|
29988
|
+
return;
|
|
29989
|
+
}
|
|
29990
|
+
if (key === "\x1B[B" || key === "j") {
|
|
29991
|
+
selected = selected === input.items.length - 1 ? 0 : selected + 1;
|
|
29992
|
+
render2();
|
|
29993
|
+
}
|
|
29994
|
+
};
|
|
29995
|
+
const cleanup = () => {
|
|
29996
|
+
stdin.off?.("data", onData);
|
|
29997
|
+
clearRendered(stdout, renderedLines);
|
|
29998
|
+
renderedLines = 0;
|
|
29999
|
+
stdout.write("\x1B[?25h");
|
|
30000
|
+
if (stdin.setRawMode) {
|
|
30001
|
+
stdin.setRawMode(rawModeWasEnabled);
|
|
30002
|
+
}
|
|
30003
|
+
stdin.pause?.();
|
|
30004
|
+
};
|
|
30005
|
+
try {
|
|
30006
|
+
rawModeWasEnabled = Boolean(stdin.isRaw);
|
|
30007
|
+
stdin.setEncoding?.("utf8");
|
|
30008
|
+
stdin.setRawMode?.(true);
|
|
30009
|
+
stdin.resume?.();
|
|
30010
|
+
stdout.write("\x1B[?25l");
|
|
30011
|
+
stdin.on("data", onData);
|
|
30012
|
+
render2();
|
|
30013
|
+
} catch (error51) {
|
|
30014
|
+
settle(() => reject(error51));
|
|
30015
|
+
}
|
|
30016
|
+
});
|
|
30017
|
+
}
|
|
30018
|
+
function clamp(value, length) {
|
|
30019
|
+
return Math.min(Math.max(0, value), length - 1);
|
|
30020
|
+
}
|
|
30021
|
+
function clearRendered(stdout, lines) {
|
|
30022
|
+
if (lines === 0) return;
|
|
30023
|
+
stdout.write(`\x1B[${lines}A\x1B[J`);
|
|
30024
|
+
}
|
|
30025
|
+
|
|
30026
|
+
// src/commands/auth/accounts.ts
|
|
29939
30027
|
async function handleAuthStatus(context) {
|
|
29940
30028
|
const result = await fetchAuthStatus(context);
|
|
29941
30029
|
context.io.writeResult(result, formatAuthStatusText);
|
|
@@ -29946,7 +30034,7 @@ async function handleWhoami(context) {
|
|
|
29946
30034
|
context.io.writeResult(whoami, formatWhoamiText);
|
|
29947
30035
|
}
|
|
29948
30036
|
function logout(context) {
|
|
29949
|
-
if (
|
|
30037
|
+
if (activeAccountKey(context.configPath) !== void 0) {
|
|
29950
30038
|
const config2 = readConfig(context.configPath);
|
|
29951
30039
|
writeConfig(
|
|
29952
30040
|
{
|
|
@@ -29962,62 +30050,103 @@ function logout(context) {
|
|
|
29962
30050
|
}
|
|
29963
30051
|
context.writeOutput(context.io.style.success("Logged out."));
|
|
29964
30052
|
}
|
|
29965
|
-
function
|
|
29966
|
-
const
|
|
29967
|
-
if (
|
|
30053
|
+
function listAccounts2(context) {
|
|
30054
|
+
const accounts = listAccounts(context.configPath);
|
|
30055
|
+
if (accounts.length === 0) {
|
|
29968
30056
|
throw new Error("No stored accounts. Session `auto auth login` first.");
|
|
29969
30057
|
}
|
|
29970
|
-
for (const
|
|
30058
|
+
for (const account of accounts) {
|
|
29971
30059
|
context.writeOutput(
|
|
29972
30060
|
accountLine(
|
|
29973
|
-
|
|
29974
|
-
|
|
30061
|
+
account,
|
|
30062
|
+
activeAccountKey(context.configPath),
|
|
29975
30063
|
context.io.style
|
|
29976
30064
|
)
|
|
29977
30065
|
);
|
|
29978
30066
|
}
|
|
29979
30067
|
}
|
|
29980
|
-
function switchAccount(context,
|
|
30068
|
+
async function switchAccount(context, input = {}) {
|
|
30069
|
+
const accountRef = input.user ?? input.accountRef;
|
|
30070
|
+
if (input.user && input.accountRef) {
|
|
30071
|
+
throw new Error("Choose either an account argument or --user, not both.");
|
|
30072
|
+
}
|
|
29981
30073
|
if (!accountRef) {
|
|
29982
|
-
|
|
30074
|
+
await switchAccountInteractive(context);
|
|
29983
30075
|
return;
|
|
29984
30076
|
}
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
|
|
29988
|
-
|
|
30077
|
+
useAccount(
|
|
30078
|
+
context,
|
|
30079
|
+
resolveAccountByEmail(context, accountRef, {
|
|
30080
|
+
server: input.server,
|
|
30081
|
+
preferActiveServer: true,
|
|
30082
|
+
command: "switch"
|
|
30083
|
+
})
|
|
30084
|
+
);
|
|
30085
|
+
}
|
|
30086
|
+
function removeAccount(context, input) {
|
|
30087
|
+
const account = resolveAccountByEmail(context, input.accountRef, {
|
|
30088
|
+
server: input.server,
|
|
30089
|
+
preferActiveServer: false,
|
|
30090
|
+
command: "remove"
|
|
30091
|
+
});
|
|
30092
|
+
if (activeAccountKey(context.configPath) === account.key) {
|
|
30093
|
+
clearActiveAccount(context.configPath);
|
|
29989
30094
|
}
|
|
29990
|
-
|
|
29991
|
-
|
|
30095
|
+
rmSync(account.path);
|
|
30096
|
+
context.writeOutput(
|
|
30097
|
+
context.io.style.success(
|
|
30098
|
+
`Removed account ${account.config.userEmail} (${account.config.serverUrl}).`
|
|
30099
|
+
)
|
|
30100
|
+
);
|
|
30101
|
+
}
|
|
30102
|
+
function accountLine(account, activeKey, style) {
|
|
30103
|
+
return [
|
|
30104
|
+
account.config.userEmail,
|
|
30105
|
+
style.dim(`server=${account.config.serverUrl ?? "(unset)"}`),
|
|
30106
|
+
account.config.refreshToken ? void 0 : style.warn("logged_out"),
|
|
30107
|
+
account.key === activeKey ? style.success("(active)") : void 0
|
|
30108
|
+
].filter(Boolean).join(" ");
|
|
30109
|
+
}
|
|
30110
|
+
async function switchAccountInteractive(context) {
|
|
30111
|
+
const accounts = listAccounts(context.configPath);
|
|
30112
|
+
if (accounts.length === 0) {
|
|
29992
30113
|
throw new Error("No stored accounts. Session `auto auth login` first.");
|
|
29993
30114
|
}
|
|
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) {
|
|
30115
|
+
if (!context.io.canPrompt()) {
|
|
29999
30116
|
throw new Error(
|
|
30000
|
-
|
|
30117
|
+
"Cannot prompt for an account in a non-interactive terminal. Rerun with `auto auth switch --user <email>`."
|
|
30001
30118
|
);
|
|
30002
30119
|
}
|
|
30003
|
-
const
|
|
30004
|
-
const
|
|
30005
|
-
|
|
30120
|
+
const activeKey = activeAccountKey(context.configPath);
|
|
30121
|
+
const initialIndex = Math.max(
|
|
30122
|
+
0,
|
|
30123
|
+
accounts.findIndex((account) => account.key === activeKey)
|
|
30006
30124
|
);
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30125
|
+
const selected = await selectFromList(context, {
|
|
30126
|
+
title: "Select account",
|
|
30127
|
+
items: accounts,
|
|
30128
|
+
initialIndex,
|
|
30129
|
+
renderItem: (account, selected2) => interactiveAccountLine(account, activeKey, selected2, context.io.style)
|
|
30130
|
+
});
|
|
30131
|
+
useAccount(context, selected);
|
|
30132
|
+
}
|
|
30133
|
+
function interactiveAccountLine(account, activeKey, selected, style) {
|
|
30134
|
+
return [
|
|
30135
|
+
selected ? ">" : " ",
|
|
30136
|
+
account.config.userEmail,
|
|
30137
|
+
style.dim(account.config.serverUrl ?? "(unset)"),
|
|
30138
|
+
account.config.refreshToken ? void 0 : style.warn("logged_out"),
|
|
30139
|
+
account.key === activeKey ? style.success("active") : void 0
|
|
30140
|
+
].filter(Boolean).join(" ");
|
|
30141
|
+
}
|
|
30142
|
+
function useAccount(context, account) {
|
|
30143
|
+
setActiveAccount(account.key, context.configPath);
|
|
30015
30144
|
context.writeOutput(
|
|
30016
30145
|
context.io.style.success(
|
|
30017
|
-
`Switched to ${
|
|
30146
|
+
`Switched to ${account.config.userEmail} (${account.config.serverUrl}).`
|
|
30018
30147
|
)
|
|
30019
30148
|
);
|
|
30020
|
-
if (!
|
|
30149
|
+
if (!account.config.refreshToken) {
|
|
30021
30150
|
context.writeOutput(
|
|
30022
30151
|
context.io.style.warn(
|
|
30023
30152
|
"This account has no stored credentials; run `auto auth login`."
|
|
@@ -30025,35 +30154,32 @@ function switchAccount(context, accountRef, options = {}) {
|
|
|
30025
30154
|
);
|
|
30026
30155
|
}
|
|
30027
30156
|
}
|
|
30028
|
-
function
|
|
30029
|
-
|
|
30030
|
-
if (
|
|
30031
|
-
throw new Error(
|
|
30157
|
+
function resolveAccountByEmail(context, accountRef, options) {
|
|
30158
|
+
const accounts = listAccounts(context.configPath);
|
|
30159
|
+
if (accounts.length === 0) {
|
|
30160
|
+
throw new Error("No stored accounts. Session `auto auth login` first.");
|
|
30032
30161
|
}
|
|
30033
|
-
const
|
|
30034
|
-
|
|
30035
|
-
|
|
30036
|
-
|
|
30037
|
-
(profile) => profile.name
|
|
30038
|
-
);
|
|
30162
|
+
const matches = accounts.filter(
|
|
30163
|
+
(account) => account.config.userEmail?.toLowerCase() === accountRef.toLowerCase() && (!options.server || account.config.serverUrl === options.server)
|
|
30164
|
+
);
|
|
30165
|
+
if (matches.length === 0) {
|
|
30039
30166
|
throw new Error(
|
|
30040
|
-
`No stored
|
|
30167
|
+
`No stored account for ${accountRef}. Session \`auto auth login\` to add it.`
|
|
30041
30168
|
);
|
|
30042
30169
|
}
|
|
30043
|
-
if (
|
|
30044
|
-
|
|
30170
|
+
if (matches.length === 1) return matches[0];
|
|
30171
|
+
if (options.preferActiveServer) {
|
|
30172
|
+
const active = readConfig(context.configPath);
|
|
30173
|
+
const activeServerMatch = matches.find(
|
|
30174
|
+
(account) => account.config.serverUrl === active.serverUrl
|
|
30175
|
+
);
|
|
30176
|
+
if (activeServerMatch) return activeServerMatch;
|
|
30045
30177
|
}
|
|
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(" ");
|
|
30178
|
+
throw new Error(
|
|
30179
|
+
`Multiple servers have a stored account for ${accountRef}: ${matches.map((account) => account.config.serverUrl).join(
|
|
30180
|
+
", "
|
|
30181
|
+
)}. Pick one with \`auto auth ${options.command} ${accountRef} --server <url>\`.`
|
|
30182
|
+
);
|
|
30057
30183
|
}
|
|
30058
30184
|
async function fetchAuthStatus(context) {
|
|
30059
30185
|
const config2 = readConfig(context.configPath);
|
|
@@ -30382,7 +30508,7 @@ async function authorizeSensitiveActionDevice(input) {
|
|
|
30382
30508
|
let firstAttempt = true;
|
|
30383
30509
|
while (Date.now() < deadline) {
|
|
30384
30510
|
if (!firstAttempt) {
|
|
30385
|
-
await
|
|
30511
|
+
await sleep(Math.max(0, device.interval) * 1e3);
|
|
30386
30512
|
}
|
|
30387
30513
|
firstAttempt = false;
|
|
30388
30514
|
try {
|
|
@@ -30407,39 +30533,26 @@ async function requireYes(rl, prompt) {
|
|
|
30407
30533
|
throw new Error("Account deletion cancelled.");
|
|
30408
30534
|
}
|
|
30409
30535
|
}
|
|
30410
|
-
async function
|
|
30536
|
+
async function sleep(ms) {
|
|
30411
30537
|
await new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
30412
30538
|
}
|
|
30413
30539
|
|
|
30414
30540
|
// src/commands/account/commands.ts
|
|
30415
30541
|
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
|
-
}
|
|
30542
|
+
const account = program.command("account").description("Manage the signed-in Auto account.");
|
|
30543
|
+
account.command("list", { hidden: true }).description("Deprecated alias for `auth list`.").action(() => listAccounts2(context));
|
|
30544
|
+
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(
|
|
30545
|
+
async (accountRef, options) => await switchAccount(context, {
|
|
30546
|
+
accountRef,
|
|
30547
|
+
server: options.server
|
|
30548
|
+
})
|
|
30438
30549
|
);
|
|
30439
|
-
account.command("
|
|
30440
|
-
(accountRef, options) =>
|
|
30550
|
+
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(
|
|
30551
|
+
(accountRef, options) => removeAccount(context, {
|
|
30552
|
+
accountRef,
|
|
30553
|
+
server: options.server
|
|
30554
|
+
})
|
|
30441
30555
|
);
|
|
30442
|
-
account.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
|
|
30443
30556
|
account.command("delete").description("Permanently delete the signed-in Auto account.").option("--api-url <url>", "Auto web server URL").option(
|
|
30444
30557
|
"--delete-admin-orgs",
|
|
30445
30558
|
"delete organizations where this account is an admin"
|
|
@@ -30872,7 +30985,7 @@ import {
|
|
|
30872
30985
|
createCipheriv,
|
|
30873
30986
|
createDecipheriv,
|
|
30874
30987
|
hkdfSync,
|
|
30875
|
-
randomBytes
|
|
30988
|
+
randomBytes
|
|
30876
30989
|
} from "crypto";
|
|
30877
30990
|
var BOOTSTRAP_CRYPTO_INFO = "auto-agent-bridge-bootstrap/v1";
|
|
30878
30991
|
var BOOTSTRAP_KEY_LENGTH = 32;
|
|
@@ -31397,14 +31510,14 @@ function partialTextDeltaPayload(message, messageId) {
|
|
|
31397
31510
|
}
|
|
31398
31511
|
|
|
31399
31512
|
// src/commands/agent-bridge/harness/claude-code/resume-store.ts
|
|
31400
|
-
import { existsSync
|
|
31513
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
31401
31514
|
import { dirname as dirname3 } from "path";
|
|
31402
31515
|
var AGENT_BRIDGE_RUNTIME_DIR = "/tmp/auto-bridge-runtime";
|
|
31403
31516
|
var CLAUDE_SESSION_RESUME_PATH = `${AGENT_BRIDGE_RUNTIME_DIR}/claude-session-id`;
|
|
31404
31517
|
function fileClaudeSessionResumeStore(path2 = CLAUDE_SESSION_RESUME_PATH) {
|
|
31405
31518
|
return {
|
|
31406
31519
|
read(sessionId) {
|
|
31407
|
-
if (!
|
|
31520
|
+
if (!existsSync(path2)) {
|
|
31408
31521
|
return null;
|
|
31409
31522
|
}
|
|
31410
31523
|
const record2 = parseResumeRecord(readFileSync2(path2, "utf8"));
|
|
@@ -32482,7 +32595,7 @@ function statusAgents(input) {
|
|
|
32482
32595
|
// src/commands/agents/connect.ts
|
|
32483
32596
|
init_resources2();
|
|
32484
32597
|
init_browser();
|
|
32485
|
-
import { existsSync as
|
|
32598
|
+
import { existsSync as existsSync2, mkdtempSync, writeFileSync as writeFileSync3 } from "fs";
|
|
32486
32599
|
import { homedir as homedir3, tmpdir } from "os";
|
|
32487
32600
|
import { join as join4 } from "path";
|
|
32488
32601
|
|
|
@@ -32688,7 +32801,7 @@ async function stageAvatarImage(input) {
|
|
|
32688
32801
|
const contentType = response.headers.get("content-type") ?? "";
|
|
32689
32802
|
const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
|
|
32690
32803
|
const downloads = join4(homedir3(), "Downloads");
|
|
32691
|
-
const directory =
|
|
32804
|
+
const directory = existsSync2(downloads) ? downloads : mkdtempSync(join4(tmpdir(), "auto-avatar-"));
|
|
32692
32805
|
const path2 = join4(directory, `${input.agent}-avatar${extension}`);
|
|
32693
32806
|
writeFileSync3(path2, Buffer.from(await response.arrayBuffer()));
|
|
32694
32807
|
return path2;
|
|
@@ -32940,7 +33053,6 @@ function registerAuthCommands(program, context) {
|
|
|
32940
33053
|
await login({
|
|
32941
33054
|
options: {
|
|
32942
33055
|
...options,
|
|
32943
|
-
profile: globalOptions.profile,
|
|
32944
33056
|
apiUrl: resolveApiBaseUrl({
|
|
32945
33057
|
explicit: [options.apiUrl, globalOptions.apiUrl],
|
|
32946
33058
|
env: context.env
|
|
@@ -32955,7 +33067,7 @@ function registerAuthCommands(program, context) {
|
|
|
32955
33067
|
});
|
|
32956
33068
|
});
|
|
32957
33069
|
auth.command("status").description(
|
|
32958
|
-
"Show current
|
|
33070
|
+
"Show current authentication state and validate the token against the server."
|
|
32959
33071
|
).action(async () => {
|
|
32960
33072
|
await handleAuthStatus(context);
|
|
32961
33073
|
});
|
|
@@ -32963,13 +33075,21 @@ function registerAuthCommands(program, context) {
|
|
|
32963
33075
|
await handleWhoami(context);
|
|
32964
33076
|
});
|
|
32965
33077
|
auth.command("logout").description("Remove the local user refresh token.").action(() => logout(context));
|
|
32966
|
-
auth.command("list").description("List stored
|
|
33078
|
+
auth.command("list").description("List stored accounts.").action(() => listAccounts2(context));
|
|
32967
33079
|
auth.command("switch").description(
|
|
32968
|
-
"Switch the active account
|
|
32969
|
-
).argument("[account]", "
|
|
32970
|
-
(account, options) =>
|
|
33080
|
+
"Switch the active account, prompting interactively when no account is provided."
|
|
33081
|
+
).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(
|
|
33082
|
+
async (account, options) => {
|
|
33083
|
+
await switchAccount(context, {
|
|
33084
|
+
accountRef: account,
|
|
33085
|
+
user: options.user,
|
|
33086
|
+
server: options.server
|
|
33087
|
+
});
|
|
33088
|
+
}
|
|
33089
|
+
);
|
|
33090
|
+
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(
|
|
33091
|
+
(account, options) => removeAccount(context, { accountRef: account, server: options.server })
|
|
32971
33092
|
);
|
|
32972
|
-
auth.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
|
|
32973
33093
|
}
|
|
32974
33094
|
|
|
32975
33095
|
// src/lib/stdio/confirm.ts
|
|
@@ -36637,10 +36757,6 @@ function registerSyncCommands(program, context) {
|
|
|
36637
36757
|
});
|
|
36638
36758
|
}
|
|
36639
36759
|
|
|
36640
|
-
// src/cli/program.ts
|
|
36641
|
-
init_path();
|
|
36642
|
-
init_profiles();
|
|
36643
|
-
|
|
36644
36760
|
// src/lib/output/iostreams.ts
|
|
36645
36761
|
init_style();
|
|
36646
36762
|
function createIOStreams(flags) {
|
|
@@ -36734,12 +36850,9 @@ function createProgram(options = {}) {
|
|
|
36734
36850
|
"stream-json",
|
|
36735
36851
|
"tui"
|
|
36736
36852
|
])
|
|
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
|
-
);
|
|
36853
|
+
).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL");
|
|
36741
36854
|
program.configureHelp({ showGlobalOptions: true });
|
|
36742
|
-
program.hook("preAction", (
|
|
36855
|
+
program.hook("preAction", () => {
|
|
36743
36856
|
if (!options.io) {
|
|
36744
36857
|
const g = program.opts();
|
|
36745
36858
|
context.io = createIOStreams({
|
|
@@ -36749,22 +36862,6 @@ function createProgram(options = {}) {
|
|
|
36749
36862
|
writeError: options.writeError
|
|
36750
36863
|
});
|
|
36751
36864
|
}
|
|
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
36865
|
});
|
|
36769
36866
|
const launchTui = async () => {
|
|
36770
36867
|
const g = program.opts();
|