@autohq/cli 0.1.184 → 0.1.186

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 "&amp;";
585
- case "<":
586
- return "&lt;";
587
- case ">":
588
- return "&gt;";
589
- case '"':
590
- return "&quot;";
591
- case "'":
592
- return "&#39;";
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 createHash3 } from "crypto";
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: createHash3("sha256").update(body).digest("hex"), sizeBytes },
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 "&amp;";
21363
+ case "<":
21364
+ return "&lt;";
21365
+ case ">":
21366
+ return "&gt;";
21367
+ case '"':
21368
+ return "&quot;";
21369
+ case "'":
21370
+ return "&#39;";
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.184",
21415
+ version: "0.1.186",
21685
21416
  license: "SEE LICENSE IN README.md",
21686
21417
  publishConfig: {
21687
21418
  access: "public"
@@ -22450,6 +22181,47 @@ var init_progress = __esm({
22450
22181
  }
22451
22182
  });
22452
22183
 
22184
+ // src/lib/output/style.ts
22185
+ import { Chalk } from "chalk";
22186
+ function createStyle(flags) {
22187
+ const textual = flags.outputMode === "text" || flags.outputMode === "tui";
22188
+ if (!textual || flags.noColor || !flags.isTTY) {
22189
+ return plainStyle;
22190
+ }
22191
+ const chalk2 = new Chalk({ level: 1 });
22192
+ return {
22193
+ enabled: true,
22194
+ success: (text) => chalk2.green(text),
22195
+ warn: (text) => chalk2.yellow(text),
22196
+ error: (text) => chalk2.red(text),
22197
+ heading: (text) => chalk2.bold.underline(text),
22198
+ label: (text) => chalk2.bold(text),
22199
+ id: (text) => chalk2.cyan(text),
22200
+ url: (text) => chalk2.cyan.underline(text),
22201
+ dim: (text) => chalk2.dim(text)
22202
+ };
22203
+ }
22204
+ function identity(text) {
22205
+ return text;
22206
+ }
22207
+ var plainStyle;
22208
+ var init_style = __esm({
22209
+ "src/lib/output/style.ts"() {
22210
+ "use strict";
22211
+ plainStyle = {
22212
+ enabled: false,
22213
+ success: identity,
22214
+ warn: identity,
22215
+ error: identity,
22216
+ heading: identity,
22217
+ label: identity,
22218
+ id: identity,
22219
+ url: identity,
22220
+ dim: identity
22221
+ };
22222
+ }
22223
+ });
22224
+
22453
22225
  // src/commands/apply/agent-tool-connect.ts
22454
22226
  async function connectAgentTool(input) {
22455
22227
  if (input.manual) {
@@ -23429,7 +23201,7 @@ var init_apply_result = __esm({
23429
23201
  });
23430
23202
 
23431
23203
  // ../../packages/schemas/src/project-apply-files/assets.ts
23432
- import { createHash as createHash4 } from "crypto";
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: createHash4("sha256").update(bytes).digest("hex"),
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 existsSync4,
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 (!existsSync4(autoRoot)) {
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 createHash5 } from "crypto";
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: createHash5("sha256").update(body).digest("hex"),
23705
+ sha256: createHash4("sha256").update(body).digest("hex"),
23934
23706
  sizeBytes
23935
23707
  },
23936
23708
  { apiBaseUrl: input.commandOptions.apiBaseUrl }
@@ -24088,6 +23860,233 @@ var init_actions = __esm({
24088
23860
  }
24089
23861
  });
24090
23862
 
23863
+ // src/commands/auth/pkce.ts
23864
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
23865
+ function pkceVerifier() {
23866
+ return randomBytes2(32).toString("base64url");
23867
+ }
23868
+ function pkceChallenge(verifier) {
23869
+ return createHash5("sha256").update(verifier).digest("base64url");
23870
+ }
23871
+ var init_pkce = __esm({
23872
+ "src/commands/auth/pkce.ts"() {
23873
+ "use strict";
23874
+ }
23875
+ });
23876
+
23877
+ // src/commands/auth/login.ts
23878
+ async function login(input) {
23879
+ const style = input.style ?? plainStyle;
23880
+ const serverUrl = resolveApiBaseUrl({ explicit: input.options.apiUrl });
23881
+ if (input.options.device) {
23882
+ const device = await postJson(
23883
+ input.fetch,
23884
+ `${serverUrl}/api/v1/auth/device/code`,
23885
+ {}
23886
+ );
23887
+ input.writeOutput(
23888
+ `Open ${style.url(device.verification_uri)} and enter ${style.label(
23889
+ device.user_code
23890
+ )}`
23891
+ );
23892
+ input.writeOutput(
23893
+ `${style.label("Device code:")} ${style.id(device.device_code)}`
23894
+ );
23895
+ const deadline = Date.now() + device.expires_in * 1e3;
23896
+ let firstAttempt = true;
23897
+ while (Date.now() < deadline) {
23898
+ if (!firstAttempt) {
23899
+ await sleep2(Math.max(0, device.interval) * 1e3);
23900
+ }
23901
+ firstAttempt = false;
23902
+ let token3;
23903
+ try {
23904
+ token3 = await postJson(
23905
+ input.fetch,
23906
+ `${serverUrl}/api/v1/auth/cli/token`,
23907
+ {
23908
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
23909
+ device_code: device.device_code
23910
+ }
23911
+ );
23912
+ } catch (error51) {
23913
+ if (error51 instanceof Error && error51.message === "authorization_pending") {
23914
+ continue;
23915
+ }
23916
+ throw error51;
23917
+ }
23918
+ await finishLogin({
23919
+ token: token3,
23920
+ serverUrl,
23921
+ fetch: input.fetch,
23922
+ configPath: input.configPath,
23923
+ writeOutput: input.writeOutput,
23924
+ style
23925
+ });
23926
+ return;
23927
+ }
23928
+ throw new Error("Device authorization expired before approval.");
23929
+ }
23930
+ const verifier = input.options.verifier ?? pkceVerifier();
23931
+ if (!input.options.code) {
23932
+ const callback = await createOAuthLoopbackCallback({
23933
+ successHtml: () => renderOAuthLoopbackPage({
23934
+ status: "success",
23935
+ eyebrow: "Auto CLI",
23936
+ title: "Login authorized",
23937
+ message: "Auto received the browser authorization. The CLI will finish signing you in from your terminal.",
23938
+ details: [{ label: "Server", value: serverUrl }]
23939
+ }),
23940
+ failureHtml: () => renderOAuthLoopbackPage({
23941
+ status: "failure",
23942
+ eyebrow: "Auto CLI",
23943
+ title: "Login failed",
23944
+ message: "The browser authorization did not complete. Return to your terminal to retry or inspect the error.",
23945
+ details: [{ label: "Server", value: serverUrl }]
23946
+ })
23947
+ });
23948
+ try {
23949
+ const authorizeUrl = new URL("/auth/cli", serverUrl);
23950
+ authorizeUrl.searchParams.set("pkce_challenge", pkceChallenge(verifier));
23951
+ authorizeUrl.searchParams.set("redirect_uri", callback.redirectUri);
23952
+ input.writeOutput(`Opening ${style.url(authorizeUrl.toString())}`);
23953
+ input.writeOutput(style.dim("Waiting for browser authorization..."));
23954
+ openBrowser(authorizeUrl.toString());
23955
+ const { code } = await callback.result;
23956
+ const token3 = await exchangeAuthorizationCode({
23957
+ code,
23958
+ fetch: input.fetch,
23959
+ redirectUri: callback.redirectUri,
23960
+ serverUrl,
23961
+ verifier
23962
+ });
23963
+ await finishLogin({
23964
+ token: token3,
23965
+ serverUrl,
23966
+ fetch: input.fetch,
23967
+ configPath: input.configPath,
23968
+ writeOutput: input.writeOutput,
23969
+ style
23970
+ });
23971
+ return;
23972
+ } finally {
23973
+ callback.close();
23974
+ }
23975
+ }
23976
+ const token2 = await exchangeAuthorizationCode({
23977
+ code: input.options.code,
23978
+ fetch: input.fetch,
23979
+ redirectUri: "http://127.0.0.1/callback",
23980
+ serverUrl,
23981
+ verifier
23982
+ });
23983
+ await finishLogin({
23984
+ token: token2,
23985
+ serverUrl,
23986
+ fetch: input.fetch,
23987
+ configPath: input.configPath,
23988
+ writeOutput: input.writeOutput,
23989
+ style
23990
+ });
23991
+ }
23992
+ async function exchangeAuthorizationCode(input) {
23993
+ return postJson(
23994
+ input.fetch,
23995
+ `${input.serverUrl}/api/v1/auth/cli/token`,
23996
+ {
23997
+ grant_type: "authorization_code",
23998
+ code: input.code,
23999
+ code_verifier: input.verifier,
24000
+ redirect_uri: input.redirectUri
24001
+ }
24002
+ );
24003
+ }
24004
+ async function finishLogin(input) {
24005
+ const { token: token2, serverUrl } = input;
24006
+ const storedAccount = token2.user ? findStoredAccountConfig({
24007
+ configPath: input.configPath,
24008
+ serverUrl,
24009
+ userId: token2.user.id,
24010
+ userEmail: token2.user.email
24011
+ }) : void 0;
24012
+ const selection = storedAccount?.organizationId && storedAccount.projectId ? {
24013
+ organizationId: storedAccount.organizationId,
24014
+ projectId: storedAccount.projectId
24015
+ } : void 0;
24016
+ let config2 = {
24017
+ serverUrl,
24018
+ userId: token2.user?.id,
24019
+ userEmail: token2.user?.email,
24020
+ refreshToken: token2.refresh_token,
24021
+ accessToken: token2.access_token,
24022
+ accessTokenExpiresAt: token2.access_token ? accessTokenExpiresAt(token2) : void 0
24023
+ };
24024
+ if (selection) {
24025
+ try {
24026
+ const scoped = await postJson(
24027
+ input.fetch,
24028
+ `${serverUrl}/api/v1/auth/cli/token`,
24029
+ {
24030
+ grant_type: "refresh_token",
24031
+ refresh_token: token2.refresh_token,
24032
+ organization_id: selection.organizationId,
24033
+ project_id: selection.projectId
24034
+ }
24035
+ );
24036
+ config2 = {
24037
+ ...config2,
24038
+ ...selection,
24039
+ refreshToken: scoped.refresh_token,
24040
+ accessToken: scoped.access_token,
24041
+ accessTokenExpiresAt: scoped.access_token ? accessTokenExpiresAt(scoped) : void 0,
24042
+ accessTokenOrganizationId: scoped.access_token ? selection.organizationId : void 0,
24043
+ accessTokenProjectId: scoped.access_token ? selection.projectId : void 0
24044
+ };
24045
+ } catch {
24046
+ input.writeOutput(
24047
+ input.style.warn(
24048
+ "The saved organization/project selection is not available for this account; run `auto orgs list` to pick a new one."
24049
+ )
24050
+ );
24051
+ }
24052
+ }
24053
+ persistLogin(config2, input);
24054
+ input.writeOutput(
24055
+ input.style.success(
24056
+ token2.user ? `Logged in as ${token2.user.email}.` : "Logged in."
24057
+ )
24058
+ );
24059
+ }
24060
+ function persistLogin(config2, input) {
24061
+ const directAccountPath = input.configPath !== void 0 && isAccountPath(input.configPath);
24062
+ const accountKey = saveAccount({
24063
+ config: config2,
24064
+ accountKey: directAccountPath && input.configPath ? accountKeyFromPath(input.configPath) : void 0,
24065
+ configPath: input.configPath
24066
+ });
24067
+ if (!directAccountPath) {
24068
+ setActiveAccount(accountKey, input.configPath);
24069
+ }
24070
+ }
24071
+ async function sleep2(ms) {
24072
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
24073
+ }
24074
+ var init_login = __esm({
24075
+ "src/commands/auth/login.ts"() {
24076
+ "use strict";
24077
+ init_base_url();
24078
+ init_http();
24079
+ init_tokens();
24080
+ init_browser();
24081
+ init_accounts();
24082
+ init_file();
24083
+ init_path();
24084
+ init_loopback();
24085
+ init_style();
24086
+ init_pkce();
24087
+ }
24088
+ });
24089
+
24091
24090
  // src/lib/resources.ts
24092
24091
  function parseProjectResourceReference(resource) {
24093
24092
  const [kind, name, extra] = resource.split("/");
@@ -29501,9 +29500,11 @@ function SplashView({
29501
29500
  const { stdout } = useStdout8();
29502
29501
  const termWidth = stdout?.columns ?? 120;
29503
29502
  const termHeight = stdout?.rows ?? 24;
29504
- const logo = autoLogo(void 0, Math.min(termWidth, SPLASH_LOGO_MAX_WIDTH));
29503
+ const logo = splashLogo(termWidth);
29505
29504
  const logoWidth2 = Math.max(...logo.map((line) => line.length));
29506
29505
  const logoRows = keyedLogoRows(logo);
29506
+ const shimmerStep = splashLogoShimmerStep(logoWidth2);
29507
+ const shimmerRadius = splashLogoShimmerRadius(logoWidth2);
29507
29508
  const [step, setStep] = useState4(0);
29508
29509
  const [frame, setFrame] = useState4(0);
29509
29510
  useEffect4(() => {
@@ -29559,7 +29560,9 @@ function SplashView({
29559
29560
  {
29560
29561
  frame,
29561
29562
  line: row.line,
29562
- rowIndex: row.rowIndex
29563
+ rowIndex: row.rowIndex,
29564
+ shimmerStep,
29565
+ shimmerRadius
29563
29566
  },
29564
29567
  row.key
29565
29568
  ))
@@ -29586,6 +29589,27 @@ function SplashView({
29586
29589
  }
29587
29590
  );
29588
29591
  }
29592
+ function splashLogo(termWidth) {
29593
+ return autoLogo(
29594
+ void 0,
29595
+ Math.max(0, termWidth - SPLASH_LOGO_OPTICAL_OFFSET)
29596
+ );
29597
+ }
29598
+ function splashLogoShimmerStep(logoWidth2) {
29599
+ if (splashUsesLargeLogoTreatment(logoWidth2)) {
29600
+ return LARGE_LOGO_SHIMMER_STEP;
29601
+ }
29602
+ if (logoWidth2 === MEDIUM_LOGO_WIDTH) {
29603
+ return MEDIUM_LOGO_SHIMMER_STEP;
29604
+ }
29605
+ return DEFAULT_SHIMMER_STEP;
29606
+ }
29607
+ function splashLogoShimmerRadius(logoWidth2) {
29608
+ return splashUsesLargeLogoTreatment(logoWidth2) ? LARGE_LOGO_SHIMMER_RADIUS : DEFAULT_SHIMMER_RADIUS;
29609
+ }
29610
+ function splashUsesLargeLogoTreatment(logoWidth2) {
29611
+ return logoWidth2 >= LARGE_LOGO_WIDTH;
29612
+ }
29589
29613
  function keyedLogoRows(logo) {
29590
29614
  const seen = /* @__PURE__ */ new Map();
29591
29615
  return logo.map((line, rowIndex) => {
@@ -29597,9 +29621,11 @@ function keyedLogoRows(logo) {
29597
29621
  function ShimmerLogoLine({
29598
29622
  frame,
29599
29623
  line,
29600
- rowIndex
29624
+ rowIndex,
29625
+ shimmerStep,
29626
+ shimmerRadius
29601
29627
  }) {
29602
- const highlight = frame * 2 % (line.length + 8);
29628
+ const highlight = frame * shimmerStep % (line.length + 8);
29603
29629
  return /* @__PURE__ */ jsx15(Text15, { children: Array.from(line).map((char, index) => {
29604
29630
  const distance = Math.abs(index - highlight + rowIndex);
29605
29631
  const key = `${index}:${char}`;
@@ -29609,16 +29635,16 @@ function ShimmerLogoLine({
29609
29635
  return /* @__PURE__ */ jsx15(
29610
29636
  Text15,
29611
29637
  {
29612
- color: distance <= 1 ? "white" : "cyan",
29613
- bold: distance <= 1,
29614
- dimColor: distance > 4,
29638
+ color: distance <= shimmerRadius ? "white" : "cyan",
29639
+ bold: distance <= shimmerRadius,
29640
+ dimColor: distance > shimmerRadius + 3,
29615
29641
  children: char
29616
29642
  },
29617
29643
  key
29618
29644
  );
29619
29645
  }) });
29620
29646
  }
29621
- var STEPS, STEP_MS, FRAME_MS, SPLASH_LOGO_MAX_WIDTH, SPLASH_LOGO_OPTICAL_OFFSET, SPLASH_STATUS_OPTICAL_OFFSET;
29647
+ var STEPS, STEP_MS, FRAME_MS, SPLASH_LOGO_OPTICAL_OFFSET, SPLASH_STATUS_OPTICAL_OFFSET, LARGE_LOGO_WIDTH, MEDIUM_LOGO_WIDTH, DEFAULT_SHIMMER_STEP, LARGE_LOGO_SHIMMER_STEP, MEDIUM_LOGO_SHIMMER_STEP, DEFAULT_SHIMMER_RADIUS, LARGE_LOGO_SHIMMER_RADIUS;
29622
29648
  var init_SplashView = __esm({
29623
29649
  "src/tui/SplashView.tsx"() {
29624
29650
  "use strict";
@@ -29631,9 +29657,15 @@ var init_SplashView = __esm({
29631
29657
  ];
29632
29658
  STEP_MS = 380;
29633
29659
  FRAME_MS = 90;
29634
- SPLASH_LOGO_MAX_WIDTH = 36;
29635
29660
  SPLASH_LOGO_OPTICAL_OFFSET = 4;
29636
29661
  SPLASH_STATUS_OPTICAL_OFFSET = 2;
29662
+ LARGE_LOGO_WIDTH = 100;
29663
+ MEDIUM_LOGO_WIDTH = 66;
29664
+ DEFAULT_SHIMMER_STEP = 2;
29665
+ LARGE_LOGO_SHIMMER_STEP = 6;
29666
+ MEDIUM_LOGO_SHIMMER_STEP = 4;
29667
+ DEFAULT_SHIMMER_RADIUS = 1;
29668
+ LARGE_LOGO_SHIMMER_RADIUS = 3;
29637
29669
  }
29638
29670
  });
29639
29671
 
@@ -29643,7 +29675,7 @@ __export(launcher_exports, {
29643
29675
  launch: () => launch
29644
29676
  });
29645
29677
  import { spawnSync } from "child_process";
29646
- import { existsSync as existsSync5 } from "fs";
29678
+ import { existsSync as existsSync4 } from "fs";
29647
29679
  import { dirname as dirname6, resolve as resolve3 } from "path";
29648
29680
  import { fileURLToPath } from "url";
29649
29681
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
@@ -29833,7 +29865,7 @@ function resolveLatestReleaseVersionFromCheckout() {
29833
29865
  function findRepoRoot(startDirectory) {
29834
29866
  let directory = startDirectory;
29835
29867
  while (true) {
29836
- if (existsSync5(resolve3(directory, ".git")) && existsSync5(resolve3(directory, "apps/cli/package.json"))) {
29868
+ if (existsSync4(resolve3(directory, ".git")) && existsSync4(resolve3(directory, "apps/cli/package.json"))) {
29837
29869
  return directory;
29838
29870
  }
29839
29871
  const parent = dirname6(directory);
@@ -29900,16 +29932,13 @@ var init_launcher = __esm({
29900
29932
  });
29901
29933
 
29902
29934
  // src/cli/program.ts
29903
- import { existsSync as existsSync6 } from "fs";
29904
29935
  import { Command, Option as Option4 } from "commander";
29905
29936
 
29906
29937
  // src/commands/account/commands.ts
29907
- init_base_url();
29908
- init_login();
29909
29938
  import { Option } from "commander";
29910
29939
 
29911
- // src/commands/auth/profile.ts
29912
- import { existsSync, rmSync } from "fs";
29940
+ // src/commands/auth/accounts.ts
29941
+ import { rmSync } from "fs";
29913
29942
 
29914
29943
  // src/lib/api/context.ts
29915
29944
  init_client();
@@ -29932,10 +29961,102 @@ function createContextApiClient(context) {
29932
29961
  });
29933
29962
  }
29934
29963
 
29935
- // src/commands/auth/profile.ts
29964
+ // src/commands/auth/accounts.ts
29965
+ init_accounts();
29936
29966
  init_file();
29937
- init_path();
29938
- init_profiles();
29967
+
29968
+ // src/lib/stdio/select.ts
29969
+ async function selectFromList(context, input) {
29970
+ if (input.items.length === 0) {
29971
+ throw new Error("No options available.");
29972
+ }
29973
+ if (!context.io.canPrompt()) {
29974
+ throw new Error("Cannot prompt in a non-interactive terminal.");
29975
+ }
29976
+ const stdin = context.stdin;
29977
+ const stdout = context.stdout;
29978
+ let selected = clamp(input.initialIndex ?? 0, input.items.length);
29979
+ let renderedLines = 0;
29980
+ let rawModeWasEnabled = Boolean(stdin.isRaw);
29981
+ return await new Promise((resolve4, reject) => {
29982
+ let settled = false;
29983
+ const settle = (callback) => {
29984
+ if (settled) return;
29985
+ settled = true;
29986
+ cleanup();
29987
+ callback();
29988
+ };
29989
+ const render2 = () => {
29990
+ clearRendered(stdout, renderedLines);
29991
+ const lines = [
29992
+ input.title,
29993
+ "",
29994
+ ...input.items.map(
29995
+ (item, index) => input.renderItem(item, index === selected)
29996
+ ),
29997
+ "",
29998
+ "up/down navigate enter select esc cancel"
29999
+ ];
30000
+ renderedLines = lines.length;
30001
+ stdout.write(`${lines.join("\n")}
30002
+ `);
30003
+ };
30004
+ const onData = (chunk) => {
30005
+ const key = chunk.toString("utf8");
30006
+ if (key === "") {
30007
+ settle(() => reject(new Error("Cancelled.")));
30008
+ return;
30009
+ }
30010
+ if (key === "\x1B") {
30011
+ settle(() => reject(new Error("Cancelled.")));
30012
+ return;
30013
+ }
30014
+ if (key === "\r" || key === "\n") {
30015
+ settle(() => resolve4(input.items[selected]));
30016
+ return;
30017
+ }
30018
+ if (key === "\x1B[A" || key === "k") {
30019
+ selected = selected === 0 ? input.items.length - 1 : selected - 1;
30020
+ render2();
30021
+ return;
30022
+ }
30023
+ if (key === "\x1B[B" || key === "j") {
30024
+ selected = selected === input.items.length - 1 ? 0 : selected + 1;
30025
+ render2();
30026
+ }
30027
+ };
30028
+ const cleanup = () => {
30029
+ stdin.off?.("data", onData);
30030
+ clearRendered(stdout, renderedLines);
30031
+ renderedLines = 0;
30032
+ stdout.write("\x1B[?25h");
30033
+ if (stdin.setRawMode) {
30034
+ stdin.setRawMode(rawModeWasEnabled);
30035
+ }
30036
+ stdin.pause?.();
30037
+ };
30038
+ try {
30039
+ rawModeWasEnabled = Boolean(stdin.isRaw);
30040
+ stdin.setEncoding?.("utf8");
30041
+ stdin.setRawMode?.(true);
30042
+ stdin.resume?.();
30043
+ stdout.write("\x1B[?25l");
30044
+ stdin.on("data", onData);
30045
+ render2();
30046
+ } catch (error51) {
30047
+ settle(() => reject(error51));
30048
+ }
30049
+ });
30050
+ }
30051
+ function clamp(value, length) {
30052
+ return Math.min(Math.max(0, value), length - 1);
30053
+ }
30054
+ function clearRendered(stdout, lines) {
30055
+ if (lines === 0) return;
30056
+ stdout.write(`\x1B[${lines}A\x1B[J`);
30057
+ }
30058
+
30059
+ // src/commands/auth/accounts.ts
29939
30060
  async function handleAuthStatus(context) {
29940
30061
  const result = await fetchAuthStatus(context);
29941
30062
  context.io.writeResult(result, formatAuthStatusText);
@@ -29946,7 +30067,7 @@ async function handleWhoami(context) {
29946
30067
  context.io.writeResult(whoami, formatWhoamiText);
29947
30068
  }
29948
30069
  function logout(context) {
29949
- if (currentProfileName(context.configPath) !== void 0) {
30070
+ if (activeAccountKey(context.configPath) !== void 0) {
29950
30071
  const config2 = readConfig(context.configPath);
29951
30072
  writeConfig(
29952
30073
  {
@@ -29962,62 +30083,103 @@ function logout(context) {
29962
30083
  }
29963
30084
  context.writeOutput(context.io.style.success("Logged out."));
29964
30085
  }
29965
- function listAccounts(context) {
29966
- const profiles = listProfiles(context.configPath);
29967
- if (profiles.length === 0) {
30086
+ function listAccounts2(context) {
30087
+ const accounts = listAccounts(context.configPath);
30088
+ if (accounts.length === 0) {
29968
30089
  throw new Error("No stored accounts. Session `auto auth login` first.");
29969
30090
  }
29970
- for (const profile of profiles) {
30091
+ for (const account of accounts) {
29971
30092
  context.writeOutput(
29972
30093
  accountLine(
29973
- profile,
29974
- currentProfileName(context.configPath),
30094
+ account,
30095
+ activeAccountKey(context.configPath),
29975
30096
  context.io.style
29976
30097
  )
29977
30098
  );
29978
30099
  }
29979
30100
  }
29980
- function switchAccount(context, accountRef, options = {}) {
30101
+ async function switchAccount(context, input = {}) {
30102
+ const accountRef = input.user ?? input.accountRef;
30103
+ if (input.user && input.accountRef) {
30104
+ throw new Error("Choose either an account argument or --user, not both.");
30105
+ }
29981
30106
  if (!accountRef) {
29982
- listAccounts(context);
30107
+ await switchAccountInteractive(context);
29983
30108
  return;
29984
30109
  }
29985
- if (context.pinnedProfile) {
29986
- throw new Error(
29987
- "Cannot switch the active account while pinned to a profile via --profile or AUTO_PROFILE."
29988
- );
30110
+ useAccount(
30111
+ context,
30112
+ resolveAccountByEmail(context, accountRef, {
30113
+ server: input.server,
30114
+ preferActiveServer: true,
30115
+ command: "switch"
30116
+ })
30117
+ );
30118
+ }
30119
+ function removeAccount(context, input) {
30120
+ const account = resolveAccountByEmail(context, input.accountRef, {
30121
+ server: input.server,
30122
+ preferActiveServer: false,
30123
+ command: "remove"
30124
+ });
30125
+ if (activeAccountKey(context.configPath) === account.key) {
30126
+ clearActiveAccount(context.configPath);
29989
30127
  }
29990
- const profiles = listProfiles(context.configPath);
29991
- if (profiles.length === 0) {
30128
+ rmSync(account.path);
30129
+ context.writeOutput(
30130
+ context.io.style.success(
30131
+ `Removed account ${account.config.userEmail} (${account.config.serverUrl}).`
30132
+ )
30133
+ );
30134
+ }
30135
+ function accountLine(account, activeKey, style) {
30136
+ return [
30137
+ account.config.userEmail,
30138
+ style.dim(`server=${account.config.serverUrl ?? "(unset)"}`),
30139
+ account.config.refreshToken ? void 0 : style.warn("logged_out"),
30140
+ account.key === activeKey ? style.success("(active)") : void 0
30141
+ ].filter(Boolean).join(" ");
30142
+ }
30143
+ async function switchAccountInteractive(context) {
30144
+ const accounts = listAccounts(context.configPath);
30145
+ if (accounts.length === 0) {
29992
30146
  throw new Error("No stored accounts. Session `auto auth login` first.");
29993
30147
  }
29994
- const byName = profiles.find((profile) => profile.name === accountRef);
29995
- const matches = byName ? [byName] : profiles.filter(
29996
- (profile) => profile.config.userEmail?.toLowerCase() === accountRef.toLowerCase() && (!options.server || profile.config.serverUrl === options.server)
29997
- );
29998
- if (matches.length === 0) {
30148
+ if (!context.io.canPrompt()) {
29999
30149
  throw new Error(
30000
- `No stored account for ${accountRef}. Session \`auto auth login\` to add it.`
30150
+ "Cannot prompt for an account in a non-interactive terminal. Rerun with `auto auth switch --user <email>`."
30001
30151
  );
30002
30152
  }
30003
- const active = readConfig(context.configPath);
30004
- const match = matches.length === 1 ? matches[0] : matches.find(
30005
- (profile) => profile.config.serverUrl === active.serverUrl
30153
+ const activeKey = activeAccountKey(context.configPath);
30154
+ const initialIndex = Math.max(
30155
+ 0,
30156
+ accounts.findIndex((account) => account.key === activeKey)
30006
30157
  );
30007
- if (!match) {
30008
- throw new Error(
30009
- `Multiple servers have a stored account for ${accountRef}: ${matches.map((profile) => profile.config.serverUrl).join(
30010
- ", "
30011
- )}. Pick one with \`auto auth switch ${accountRef} --server <url>\`.`
30012
- );
30013
- }
30014
- setCurrentProfile(match.name, context.configPath);
30158
+ const selected = await selectFromList(context, {
30159
+ title: "Select account",
30160
+ items: accounts,
30161
+ initialIndex,
30162
+ renderItem: (account, selected2) => interactiveAccountLine(account, activeKey, selected2, context.io.style)
30163
+ });
30164
+ useAccount(context, selected);
30165
+ }
30166
+ function interactiveAccountLine(account, activeKey, selected, style) {
30167
+ return [
30168
+ selected ? ">" : " ",
30169
+ account.config.userEmail,
30170
+ style.dim(account.config.serverUrl ?? "(unset)"),
30171
+ account.config.refreshToken ? void 0 : style.warn("logged_out"),
30172
+ account.key === activeKey ? style.success("active") : void 0
30173
+ ].filter(Boolean).join(" ");
30174
+ }
30175
+ function useAccount(context, account) {
30176
+ setActiveAccount(account.key, context.configPath);
30015
30177
  context.writeOutput(
30016
30178
  context.io.style.success(
30017
- `Switched to ${match.config.userEmail} (${match.config.serverUrl}).`
30179
+ `Switched to ${account.config.userEmail} (${account.config.serverUrl}).`
30018
30180
  )
30019
30181
  );
30020
- if (!match.config.refreshToken) {
30182
+ if (!account.config.refreshToken) {
30021
30183
  context.writeOutput(
30022
30184
  context.io.style.warn(
30023
30185
  "This account has no stored credentials; run `auto auth login`."
@@ -30025,35 +30187,32 @@ function switchAccount(context, accountRef, options = {}) {
30025
30187
  );
30026
30188
  }
30027
30189
  }
30028
- function removeProfile(context, name) {
30029
- assertValidProfileName(name);
30030
- if (context.pinnedProfile === name) {
30031
- throw new Error(`Cannot remove profile "${name}" while pinned to it.`);
30190
+ function resolveAccountByEmail(context, accountRef, options) {
30191
+ const accounts = listAccounts(context.configPath);
30192
+ if (accounts.length === 0) {
30193
+ throw new Error("No stored accounts. Session `auto auth login` first.");
30032
30194
  }
30033
- const configPath = context.configPath ?? defaultConfigPath();
30034
- const path2 = profileFilePath(configPath, name);
30035
- if (!existsSync(path2)) {
30036
- const names = listProfiles(context.configPath).map(
30037
- (profile) => profile.name
30038
- );
30195
+ const matches = accounts.filter(
30196
+ (account) => account.config.userEmail?.toLowerCase() === accountRef.toLowerCase() && (!options.server || account.config.serverUrl === options.server)
30197
+ );
30198
+ if (matches.length === 0) {
30039
30199
  throw new Error(
30040
- `No stored profile "${name}". Stored profiles: ${names.join(", ") || "(none)"}.`
30200
+ `No stored account for ${accountRef}. Session \`auto auth login\` to add it.`
30041
30201
  );
30042
30202
  }
30043
- if (currentProfileName(context.configPath) === name) {
30044
- clearCurrentProfile(context.configPath);
30203
+ if (matches.length === 1) return matches[0];
30204
+ if (options.preferActiveServer) {
30205
+ const active = readConfig(context.configPath);
30206
+ const activeServerMatch = matches.find(
30207
+ (account) => account.config.serverUrl === active.serverUrl
30208
+ );
30209
+ if (activeServerMatch) return activeServerMatch;
30045
30210
  }
30046
- rmSync(path2);
30047
- context.writeOutput(context.io.style.success(`Removed profile "${name}".`));
30048
- }
30049
- function accountLine(profile, activeName, style) {
30050
- return [
30051
- profile.name,
30052
- profile.config.userEmail,
30053
- style.dim(`server=${profile.config.serverUrl ?? "(unset)"}`),
30054
- profile.config.refreshToken ? void 0 : style.warn("logged_out"),
30055
- profile.name === activeName ? style.success("(active)") : void 0
30056
- ].filter(Boolean).join(" ");
30211
+ throw new Error(
30212
+ `Multiple servers have a stored account for ${accountRef}: ${matches.map((account) => account.config.serverUrl).join(
30213
+ ", "
30214
+ )}. Pick one with \`auto auth ${options.command} ${accountRef} --server <url>\`.`
30215
+ );
30057
30216
  }
30058
30217
  async function fetchAuthStatus(context) {
30059
30218
  const config2 = readConfig(context.configPath);
@@ -30382,7 +30541,7 @@ async function authorizeSensitiveActionDevice(input) {
30382
30541
  let firstAttempt = true;
30383
30542
  while (Date.now() < deadline) {
30384
30543
  if (!firstAttempt) {
30385
- await sleep2(Math.max(0, device.interval) * 1e3);
30544
+ await sleep(Math.max(0, device.interval) * 1e3);
30386
30545
  }
30387
30546
  firstAttempt = false;
30388
30547
  try {
@@ -30407,39 +30566,26 @@ async function requireYes(rl, prompt) {
30407
30566
  throw new Error("Account deletion cancelled.");
30408
30567
  }
30409
30568
  }
30410
- async function sleep2(ms) {
30569
+ async function sleep(ms) {
30411
30570
  await new Promise((resolve4) => setTimeout(resolve4, ms));
30412
30571
  }
30413
30572
 
30414
30573
  // src/commands/account/commands.ts
30415
30574
  function registerAccountCommands(program, context) {
30416
- const account = program.command("account").description("Manage stored account profiles.");
30417
- account.command("list").description("List stored account profiles.").action(() => listAccounts(context));
30418
- account.command("create").description("Log in and store the account under a profile name.").argument("<name>", "Profile name for the new account").option("--api-url <url>", "Auto web server URL").option("--device", "Use device authorization flow").option("--code <code>", "Exchange an existing authorization code").option("--verifier <verifier>", "PKCE verifier for --code").action(
30419
- async (name, options) => {
30420
- const globalOptions = program.opts();
30421
- await login({
30422
- options: {
30423
- ...options,
30424
- profile: name,
30425
- apiUrl: resolveApiBaseUrl({
30426
- explicit: [options.apiUrl, globalOptions.apiUrl],
30427
- env: context.env
30428
- })
30429
- },
30430
- env: context.env,
30431
- fetch: context.fetch,
30432
- configPath: context.configPath,
30433
- writeOutput: context.writeOutput,
30434
- writeError: context.writeError,
30435
- style: context.io.style
30436
- });
30437
- }
30575
+ const account = program.command("account").description("Manage the signed-in Auto account.");
30576
+ account.command("list", { hidden: true }).description("Deprecated alias for `auth list`.").action(() => listAccounts2(context));
30577
+ account.command("switch", { hidden: true }).description("Deprecated alias for `auth switch`.").argument("<account>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
30578
+ async (accountRef, options) => await switchAccount(context, {
30579
+ accountRef,
30580
+ server: options.server
30581
+ })
30438
30582
  );
30439
- account.command("switch").description("Switch the active account to a stored profile.").argument("<account>", "Profile name or email of a stored account").option("--server <url>", "Auto web server URL of the stored account").action(
30440
- (accountRef, options) => switchAccount(context, accountRef, options)
30583
+ account.command("remove", { hidden: true }).description("Deprecated alias for `auth remove`.").argument("<account>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
30584
+ (accountRef, options) => removeAccount(context, {
30585
+ accountRef,
30586
+ server: options.server
30587
+ })
30441
30588
  );
30442
- account.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
30443
30589
  account.command("delete").description("Permanently delete the signed-in Auto account.").option("--api-url <url>", "Auto web server URL").option(
30444
30590
  "--delete-admin-orgs",
30445
30591
  "delete organizations where this account is an admin"
@@ -30872,7 +31018,7 @@ import {
30872
31018
  createCipheriv,
30873
31019
  createDecipheriv,
30874
31020
  hkdfSync,
30875
- randomBytes as randomBytes2
31021
+ randomBytes
30876
31022
  } from "crypto";
30877
31023
  var BOOTSTRAP_CRYPTO_INFO = "auto-agent-bridge-bootstrap/v1";
30878
31024
  var BOOTSTRAP_KEY_LENGTH = 32;
@@ -31397,14 +31543,14 @@ function partialTextDeltaPayload(message, messageId) {
31397
31543
  }
31398
31544
 
31399
31545
  // src/commands/agent-bridge/harness/claude-code/resume-store.ts
31400
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
31546
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
31401
31547
  import { dirname as dirname3 } from "path";
31402
31548
  var AGENT_BRIDGE_RUNTIME_DIR = "/tmp/auto-bridge-runtime";
31403
31549
  var CLAUDE_SESSION_RESUME_PATH = `${AGENT_BRIDGE_RUNTIME_DIR}/claude-session-id`;
31404
31550
  function fileClaudeSessionResumeStore(path2 = CLAUDE_SESSION_RESUME_PATH) {
31405
31551
  return {
31406
31552
  read(sessionId) {
31407
- if (!existsSync2(path2)) {
31553
+ if (!existsSync(path2)) {
31408
31554
  return null;
31409
31555
  }
31410
31556
  const record2 = parseResumeRecord(readFileSync2(path2, "utf8"));
@@ -32482,7 +32628,7 @@ function statusAgents(input) {
32482
32628
  // src/commands/agents/connect.ts
32483
32629
  init_resources2();
32484
32630
  init_browser();
32485
- import { existsSync as existsSync3, mkdtempSync, writeFileSync as writeFileSync3 } from "fs";
32631
+ import { existsSync as existsSync2, mkdtempSync, writeFileSync as writeFileSync3 } from "fs";
32486
32632
  import { homedir as homedir3, tmpdir } from "os";
32487
32633
  import { join as join4 } from "path";
32488
32634
 
@@ -32688,7 +32834,7 @@ async function stageAvatarImage(input) {
32688
32834
  const contentType = response.headers.get("content-type") ?? "";
32689
32835
  const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
32690
32836
  const downloads = join4(homedir3(), "Downloads");
32691
- const directory = existsSync3(downloads) ? downloads : mkdtempSync(join4(tmpdir(), "auto-avatar-"));
32837
+ const directory = existsSync2(downloads) ? downloads : mkdtempSync(join4(tmpdir(), "auto-avatar-"));
32692
32838
  const path2 = join4(directory, `${input.agent}-avatar${extension}`);
32693
32839
  writeFileSync3(path2, Buffer.from(await response.arrayBuffer()));
32694
32840
  return path2;
@@ -32940,7 +33086,6 @@ function registerAuthCommands(program, context) {
32940
33086
  await login({
32941
33087
  options: {
32942
33088
  ...options,
32943
- profile: globalOptions.profile,
32944
33089
  apiUrl: resolveApiBaseUrl({
32945
33090
  explicit: [options.apiUrl, globalOptions.apiUrl],
32946
33091
  env: context.env
@@ -32955,7 +33100,7 @@ function registerAuthCommands(program, context) {
32955
33100
  });
32956
33101
  });
32957
33102
  auth.command("status").description(
32958
- "Show current auth profile and validate the token against the server."
33103
+ "Show current authentication state and validate the token against the server."
32959
33104
  ).action(async () => {
32960
33105
  await handleAuthStatus(context);
32961
33106
  });
@@ -32963,13 +33108,21 @@ function registerAuthCommands(program, context) {
32963
33108
  await handleWhoami(context);
32964
33109
  });
32965
33110
  auth.command("logout").description("Remove the local user refresh token.").action(() => logout(context));
32966
- auth.command("list").description("List stored account profiles.").action(() => listAccounts(context));
33111
+ auth.command("list").description("List stored accounts.").action(() => listAccounts2(context));
32967
33112
  auth.command("switch").description(
32968
- "Switch the active account to a stored profile, or list stored accounts."
32969
- ).argument("[account]", "Profile name or email of a stored account").option("--server <url>", "Auto web server URL of the stored account").action(
32970
- (account, options) => switchAccount(context, account, options)
33113
+ "Switch the active account, prompting interactively when no account is provided."
33114
+ ).argument("[account]", "Email address of a stored account").option("--user <email>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
33115
+ async (account, options) => {
33116
+ await switchAccount(context, {
33117
+ accountRef: account,
33118
+ user: options.user,
33119
+ server: options.server
33120
+ });
33121
+ }
33122
+ );
33123
+ auth.command("remove").description("Remove a stored account.").argument("<account>", "Email address of the stored account").option("--server <url>", "Auto web server URL of the stored account").action(
33124
+ (account, options) => removeAccount(context, { accountRef: account, server: options.server })
32971
33125
  );
32972
- auth.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
32973
33126
  }
32974
33127
 
32975
33128
  // src/lib/stdio/confirm.ts
@@ -36637,10 +36790,6 @@ function registerSyncCommands(program, context) {
36637
36790
  });
36638
36791
  }
36639
36792
 
36640
- // src/cli/program.ts
36641
- init_path();
36642
- init_profiles();
36643
-
36644
36793
  // src/lib/output/iostreams.ts
36645
36794
  init_style();
36646
36795
  function createIOStreams(flags) {
@@ -36734,12 +36883,9 @@ function createProgram(options = {}) {
36734
36883
  "stream-json",
36735
36884
  "tui"
36736
36885
  ])
36737
- ).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL").option(
36738
- "--profile <name>",
36739
- "use this stored profile for the invocation (or set AUTO_PROFILE); for `auth login`, the profile name to store the login under"
36740
- );
36886
+ ).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL");
36741
36887
  program.configureHelp({ showGlobalOptions: true });
36742
- program.hook("preAction", (_thisCommand, actionCommand) => {
36888
+ program.hook("preAction", () => {
36743
36889
  if (!options.io) {
36744
36890
  const g = program.opts();
36745
36891
  context.io = createIOStreams({
@@ -36749,22 +36895,6 @@ function createProgram(options = {}) {
36749
36895
  writeError: options.writeError
36750
36896
  });
36751
36897
  }
36752
- const isLoginLike = actionCommand.name() === "login" && actionCommand.parent?.name() === "auth" || actionCommand.name() === "create" && actionCommand.parent?.name() === "account";
36753
- const flagPin = isLoginLike ? void 0 : program.opts().profile;
36754
- const pin = flagPin ?? context.env.AUTO_PROFILE;
36755
- if (pin && context.pinnedProfile !== pin) {
36756
- assertValidProfileName(pin);
36757
- const base = options.configPath ?? defaultConfigPath();
36758
- const pinnedPath = profileFilePath(base, pin);
36759
- if (!existsSync6(pinnedPath)) {
36760
- const names = listProfiles(base).map((profile) => profile.name);
36761
- throw new Error(
36762
- `No stored profile "${pin}". Stored profiles: ${names.join(", ") || "(none)"}.`
36763
- );
36764
- }
36765
- context.configPath = pinnedPath;
36766
- context.pinnedProfile = pin;
36767
- }
36768
36898
  });
36769
36899
  const launchTui = async () => {
36770
36900
  const g = program.opts();