@autohq/cli 0.1.183 → 0.1.185

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