@autohq/cli 0.1.87 → 0.1.88
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 +252 -167
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18158,20 +18158,42 @@ var init_active_project = __esm({
|
|
|
18158
18158
|
});
|
|
18159
18159
|
|
|
18160
18160
|
// src/lib/config/path.ts
|
|
18161
|
+
import { createHash } from "crypto";
|
|
18161
18162
|
import { homedir } from "os";
|
|
18162
|
-
import { join } from "path";
|
|
18163
|
+
import { join, normalize } from "path";
|
|
18163
18164
|
function defaultConfigPath() {
|
|
18164
18165
|
return process.env.AUTO_CLI_CONFIG ?? join(homedir(), ".auto", "config.yaml");
|
|
18165
18166
|
}
|
|
18167
|
+
function profilesDir(configPath = defaultConfigPath()) {
|
|
18168
|
+
return normalize(join(configPath, "..", PROFILES_DIR_NAME));
|
|
18169
|
+
}
|
|
18170
|
+
function profileFileName(input) {
|
|
18171
|
+
const key = `${input.userEmail.toLowerCase()}
|
|
18172
|
+
${serverHost(input.serverUrl)}`;
|
|
18173
|
+
const hash2 = createHash("sha256").update(key).digest("hex").slice(0, 8);
|
|
18174
|
+
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}.yaml`;
|
|
18175
|
+
}
|
|
18176
|
+
function serverHost(serverUrl) {
|
|
18177
|
+
try {
|
|
18178
|
+
return new URL(serverUrl).host;
|
|
18179
|
+
} catch {
|
|
18180
|
+
return serverUrl;
|
|
18181
|
+
}
|
|
18182
|
+
}
|
|
18183
|
+
function slug(value) {
|
|
18184
|
+
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "_");
|
|
18185
|
+
}
|
|
18186
|
+
var PROFILES_DIR_NAME;
|
|
18166
18187
|
var init_path = __esm({
|
|
18167
18188
|
"src/lib/config/path.ts"() {
|
|
18168
18189
|
"use strict";
|
|
18190
|
+
PROFILES_DIR_NAME = "profiles";
|
|
18169
18191
|
}
|
|
18170
18192
|
});
|
|
18171
18193
|
|
|
18172
18194
|
// src/lib/config/file.ts
|
|
18173
18195
|
import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
18174
|
-
import { dirname as dirname2 } from "path";
|
|
18196
|
+
import { basename, dirname as dirname2, join as join2 } from "path";
|
|
18175
18197
|
function readConfig(path = defaultConfigPath()) {
|
|
18176
18198
|
try {
|
|
18177
18199
|
const text = readFileSync2(path, "utf8");
|
|
@@ -18179,21 +18201,8 @@ function readConfig(path = defaultConfigPath()) {
|
|
|
18179
18201
|
for (const line of text.split(/\r?\n/)) {
|
|
18180
18202
|
const match = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line.trim());
|
|
18181
18203
|
if (!match) continue;
|
|
18182
|
-
const
|
|
18183
|
-
if (match[
|
|
18184
|
-
if (match[1] === "organizationId") config2.organizationId = value;
|
|
18185
|
-
if (match[1] === "projectId") config2.projectId = value;
|
|
18186
|
-
if (match[1] === "refreshToken") config2.refreshToken = value;
|
|
18187
|
-
if (match[1] === "accessToken") config2.accessToken = value;
|
|
18188
|
-
if (match[1] === "accessTokenExpiresAt") {
|
|
18189
|
-
config2.accessTokenExpiresAt = value;
|
|
18190
|
-
}
|
|
18191
|
-
if (match[1] === "accessTokenOrganizationId") {
|
|
18192
|
-
config2.accessTokenOrganizationId = value;
|
|
18193
|
-
}
|
|
18194
|
-
if (match[1] === "accessTokenProjectId") {
|
|
18195
|
-
config2.accessTokenProjectId = value;
|
|
18196
|
-
}
|
|
18204
|
+
const key = CONFIG_KEYS.find((candidate) => candidate === match[1]);
|
|
18205
|
+
if (key) config2[key] = match[2] ?? "";
|
|
18197
18206
|
}
|
|
18198
18207
|
return config2;
|
|
18199
18208
|
} catch (err) {
|
|
@@ -18202,17 +18211,26 @@ function readConfig(path = defaultConfigPath()) {
|
|
|
18202
18211
|
}
|
|
18203
18212
|
}
|
|
18204
18213
|
function writeConfig(config2, path = defaultConfigPath()) {
|
|
18214
|
+
writeConfigFile(config2, path);
|
|
18215
|
+
if (config2.userEmail && config2.serverUrl && basename(dirname2(path)) !== PROFILES_DIR_NAME) {
|
|
18216
|
+
writeConfigFile(
|
|
18217
|
+
config2,
|
|
18218
|
+
join2(
|
|
18219
|
+
dirname2(path),
|
|
18220
|
+
PROFILES_DIR_NAME,
|
|
18221
|
+
profileFileName({
|
|
18222
|
+
userEmail: config2.userEmail,
|
|
18223
|
+
serverUrl: config2.serverUrl
|
|
18224
|
+
})
|
|
18225
|
+
)
|
|
18226
|
+
);
|
|
18227
|
+
}
|
|
18228
|
+
}
|
|
18229
|
+
function writeConfigFile(config2, path) {
|
|
18205
18230
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
18206
|
-
const lines = [
|
|
18207
|
-
|
|
18208
|
-
|
|
18209
|
-
["projectId", config2.projectId],
|
|
18210
|
-
["refreshToken", config2.refreshToken],
|
|
18211
|
-
["accessToken", config2.accessToken],
|
|
18212
|
-
["accessTokenExpiresAt", config2.accessTokenExpiresAt],
|
|
18213
|
-
["accessTokenOrganizationId", config2.accessTokenOrganizationId],
|
|
18214
|
-
["accessTokenProjectId", config2.accessTokenProjectId]
|
|
18215
|
-
].filter(([, value]) => value).map(([key, value]) => `${key}: ${value}`);
|
|
18231
|
+
const lines = CONFIG_KEYS.filter((key) => config2[key]).map(
|
|
18232
|
+
(key) => `${key}: ${config2[key]}`
|
|
18233
|
+
);
|
|
18216
18234
|
writeFileSync2(path, `${lines.join("\n")}
|
|
18217
18235
|
`, {
|
|
18218
18236
|
encoding: "utf8",
|
|
@@ -18220,10 +18238,23 @@ function writeConfig(config2, path = defaultConfigPath()) {
|
|
|
18220
18238
|
});
|
|
18221
18239
|
chmodSync(path, 384);
|
|
18222
18240
|
}
|
|
18241
|
+
var CONFIG_KEYS;
|
|
18223
18242
|
var init_file = __esm({
|
|
18224
18243
|
"src/lib/config/file.ts"() {
|
|
18225
18244
|
"use strict";
|
|
18226
18245
|
init_path();
|
|
18246
|
+
CONFIG_KEYS = [
|
|
18247
|
+
"serverUrl",
|
|
18248
|
+
"userId",
|
|
18249
|
+
"userEmail",
|
|
18250
|
+
"organizationId",
|
|
18251
|
+
"projectId",
|
|
18252
|
+
"refreshToken",
|
|
18253
|
+
"accessToken",
|
|
18254
|
+
"accessTokenExpiresAt",
|
|
18255
|
+
"accessTokenOrganizationId",
|
|
18256
|
+
"accessTokenProjectId"
|
|
18257
|
+
];
|
|
18227
18258
|
}
|
|
18228
18259
|
});
|
|
18229
18260
|
|
|
@@ -20213,7 +20244,7 @@ var init_connect = __esm({
|
|
|
20213
20244
|
});
|
|
20214
20245
|
|
|
20215
20246
|
// src/commands/apply/files.ts
|
|
20216
|
-
import { createHash } from "crypto";
|
|
20247
|
+
import { createHash as createHash2 } from "crypto";
|
|
20217
20248
|
import {
|
|
20218
20249
|
readFileSync as readFileSync3,
|
|
20219
20250
|
readdirSync,
|
|
@@ -20221,11 +20252,11 @@ import {
|
|
|
20221
20252
|
statSync
|
|
20222
20253
|
} from "fs";
|
|
20223
20254
|
import {
|
|
20224
|
-
basename,
|
|
20255
|
+
basename as basename2,
|
|
20225
20256
|
dirname as dirname3,
|
|
20226
20257
|
extname,
|
|
20227
20258
|
isAbsolute,
|
|
20228
|
-
join as
|
|
20259
|
+
join as join3,
|
|
20229
20260
|
resolve
|
|
20230
20261
|
} from "path";
|
|
20231
20262
|
import { parseAllDocuments as parseYamlDocuments } from "yaml";
|
|
@@ -20241,7 +20272,7 @@ function readProjectApplyRequest(options) {
|
|
|
20241
20272
|
);
|
|
20242
20273
|
return { ...request, assets: assets2 };
|
|
20243
20274
|
}
|
|
20244
|
-
const directory = options.directory ??
|
|
20275
|
+
const directory = options.directory ?? join3(process.cwd(), ".auto");
|
|
20245
20276
|
const files = applyFiles(directory);
|
|
20246
20277
|
if (files.length === 0) {
|
|
20247
20278
|
throw new Error(`No resource files found in ${directory}`);
|
|
@@ -20304,7 +20335,7 @@ function applyFiles(root) {
|
|
|
20304
20335
|
const files = [];
|
|
20305
20336
|
for (const kind of APPLY_RESOURCE_ORDER) {
|
|
20306
20337
|
const directory = APPLY_DIRECTORIES[kind];
|
|
20307
|
-
const path =
|
|
20338
|
+
const path = join3(root, directory);
|
|
20308
20339
|
let entries;
|
|
20309
20340
|
try {
|
|
20310
20341
|
entries = readdirSync(path, { withFileTypes: true });
|
|
@@ -20378,7 +20409,7 @@ function readApplyAssets(resources, projectRoot) {
|
|
|
20378
20409
|
});
|
|
20379
20410
|
const bytes = readFileSync3(resolvedPath);
|
|
20380
20411
|
assets[avatarAsset] = {
|
|
20381
|
-
sha256:
|
|
20412
|
+
sha256: createHash2("sha256").update(bytes).digest("hex"),
|
|
20382
20413
|
contentType: extname(resolvedPath).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
|
|
20383
20414
|
dataBase64: bytes.toString("base64")
|
|
20384
20415
|
};
|
|
@@ -20442,12 +20473,12 @@ function validateSessionAvatarAsset(input) {
|
|
|
20442
20473
|
}
|
|
20443
20474
|
function applyProjectRoot(directory) {
|
|
20444
20475
|
const resolved = resolve(directory);
|
|
20445
|
-
return
|
|
20476
|
+
return basename2(resolved) === ".auto" ? dirname3(resolved) : resolved;
|
|
20446
20477
|
}
|
|
20447
20478
|
function applyFileProjectRoot(file2) {
|
|
20448
20479
|
let dir = dirname3(resolve(file2));
|
|
20449
20480
|
while (true) {
|
|
20450
|
-
if (
|
|
20481
|
+
if (basename2(dir) === ".auto") {
|
|
20451
20482
|
return dirname3(dir);
|
|
20452
20483
|
}
|
|
20453
20484
|
const parent = dirname3(dir);
|
|
@@ -20484,7 +20515,7 @@ function isRecord(value) {
|
|
|
20484
20515
|
function resourceApplyFiles(directory, entries) {
|
|
20485
20516
|
const files = [];
|
|
20486
20517
|
for (const entry of entries) {
|
|
20487
|
-
const path =
|
|
20518
|
+
const path = join3(directory, entry.name);
|
|
20488
20519
|
if (entry.isDirectory()) {
|
|
20489
20520
|
files.push(
|
|
20490
20521
|
...resourceApplyFiles(path, readdirSync(path, { withFileTypes: true }))
|
|
@@ -20633,13 +20664,49 @@ var init_actions = __esm({
|
|
|
20633
20664
|
}
|
|
20634
20665
|
});
|
|
20635
20666
|
|
|
20667
|
+
// src/lib/config/profiles.ts
|
|
20668
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
20669
|
+
import { join as join4 } from "path";
|
|
20670
|
+
function listProfiles(configPath = defaultConfigPath()) {
|
|
20671
|
+
const dir = profilesDir(configPath);
|
|
20672
|
+
let entries;
|
|
20673
|
+
try {
|
|
20674
|
+
entries = readdirSync2(dir);
|
|
20675
|
+
} catch (err) {
|
|
20676
|
+
if (err.code === "ENOENT") return [];
|
|
20677
|
+
throw err;
|
|
20678
|
+
}
|
|
20679
|
+
return entries.filter((entry) => entry.endsWith(".yaml")).sort().map((entry) => {
|
|
20680
|
+
const path = join4(dir, entry);
|
|
20681
|
+
return { path, config: readConfig(path) };
|
|
20682
|
+
}).filter((profile) => profile.config.userEmail);
|
|
20683
|
+
}
|
|
20684
|
+
function findAccountProfile(input) {
|
|
20685
|
+
const candidates = listProfiles(input.configPath).map((profile) => profile.config).filter((config2) => config2.serverUrl === input.serverUrl);
|
|
20686
|
+
return candidates.find(
|
|
20687
|
+
(config2) => input.userId && config2.userId === input.userId
|
|
20688
|
+
) ?? // Email is only a fallback for when a user id is missing on either side;
|
|
20689
|
+
// it must never override a known user-id mismatch (emails can be
|
|
20690
|
+
// reassigned to a different user).
|
|
20691
|
+
candidates.find(
|
|
20692
|
+
(config2) => input.userEmail && config2.userEmail?.toLowerCase() === input.userEmail.toLowerCase() && !(input.userId && config2.userId && config2.userId !== input.userId)
|
|
20693
|
+
);
|
|
20694
|
+
}
|
|
20695
|
+
var init_profiles2 = __esm({
|
|
20696
|
+
"src/lib/config/profiles.ts"() {
|
|
20697
|
+
"use strict";
|
|
20698
|
+
init_file();
|
|
20699
|
+
init_path();
|
|
20700
|
+
}
|
|
20701
|
+
});
|
|
20702
|
+
|
|
20636
20703
|
// src/commands/auth/pkce.ts
|
|
20637
|
-
import { createHash as
|
|
20704
|
+
import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
|
|
20638
20705
|
function pkceVerifier() {
|
|
20639
20706
|
return randomBytes2(32).toString("base64url");
|
|
20640
20707
|
}
|
|
20641
20708
|
function pkceChallenge(verifier) {
|
|
20642
|
-
return
|
|
20709
|
+
return createHash3("sha256").update(verifier).digest("base64url");
|
|
20643
20710
|
}
|
|
20644
20711
|
var init_pkce = __esm({
|
|
20645
20712
|
"src/commands/auth/pkce.ts"() {
|
|
@@ -20650,7 +20717,6 @@ var init_pkce = __esm({
|
|
|
20650
20717
|
// src/commands/auth/login.ts
|
|
20651
20718
|
async function login(input) {
|
|
20652
20719
|
const serverUrl = resolveApiBaseUrl({ explicit: input.options.apiUrl });
|
|
20653
|
-
const previous = readConfig(input.configPath);
|
|
20654
20720
|
if (input.options.device) {
|
|
20655
20721
|
const device = await postJson(
|
|
20656
20722
|
input.fetch,
|
|
@@ -20668,194 +20734,158 @@ async function login(input) {
|
|
|
20668
20734
|
await sleep(Math.max(0, device.interval) * 1e3);
|
|
20669
20735
|
}
|
|
20670
20736
|
firstAttempt = false;
|
|
20737
|
+
let token3;
|
|
20671
20738
|
try {
|
|
20672
|
-
|
|
20739
|
+
token3 = await postJson(
|
|
20673
20740
|
input.fetch,
|
|
20674
20741
|
`${serverUrl}/api/v1/auth/cli/token`,
|
|
20675
20742
|
{
|
|
20676
20743
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
20677
|
-
device_code: device.device_code
|
|
20678
|
-
...previous.organizationId && previous.projectId ? {
|
|
20679
|
-
organization_id: previous.organizationId,
|
|
20680
|
-
project_id: previous.projectId
|
|
20681
|
-
} : {}
|
|
20744
|
+
device_code: device.device_code
|
|
20682
20745
|
}
|
|
20683
20746
|
);
|
|
20684
|
-
writeConfig(
|
|
20685
|
-
{
|
|
20686
|
-
...previous,
|
|
20687
|
-
serverUrl,
|
|
20688
|
-
refreshToken: token2.refresh_token,
|
|
20689
|
-
accessToken: token2.access_token,
|
|
20690
|
-
accessTokenExpiresAt: token2.access_token ? accessTokenExpiresAt(token2) : void 0,
|
|
20691
|
-
accessTokenOrganizationId: token2.access_token ? previous.organizationId : void 0,
|
|
20692
|
-
accessTokenProjectId: token2.access_token ? previous.projectId : void 0
|
|
20693
|
-
},
|
|
20694
|
-
input.configPath
|
|
20695
|
-
);
|
|
20696
|
-
input.writeOutput("Logged in.");
|
|
20697
|
-
return;
|
|
20698
20747
|
} catch (error51) {
|
|
20699
20748
|
if (error51 instanceof Error && error51.message === "authorization_pending") {
|
|
20700
20749
|
continue;
|
|
20701
20750
|
}
|
|
20702
20751
|
throw error51;
|
|
20703
20752
|
}
|
|
20753
|
+
await finishLogin({
|
|
20754
|
+
token: token3,
|
|
20755
|
+
serverUrl,
|
|
20756
|
+
fetch: input.fetch,
|
|
20757
|
+
configPath: input.configPath,
|
|
20758
|
+
writeOutput: input.writeOutput
|
|
20759
|
+
});
|
|
20760
|
+
return;
|
|
20704
20761
|
}
|
|
20705
20762
|
throw new Error("Device authorization expired before approval.");
|
|
20706
20763
|
}
|
|
20707
20764
|
const verifier = input.options.verifier ?? pkceVerifier();
|
|
20708
20765
|
if (!input.options.code) {
|
|
20709
|
-
const selectionDetails = await resolveLoginSelectionDetails({
|
|
20710
|
-
configPath: input.configPath,
|
|
20711
|
-
env: input.env ?? process.env,
|
|
20712
|
-
fetch: input.fetch,
|
|
20713
|
-
serverUrl,
|
|
20714
|
-
previous,
|
|
20715
|
-
writeError: input.writeError ?? (() => {
|
|
20716
|
-
})
|
|
20717
|
-
});
|
|
20718
20766
|
const callback = await createOAuthLoopbackCallback({
|
|
20719
|
-
successHtml: (
|
|
20767
|
+
successHtml: () => renderOAuthLoopbackPage({
|
|
20720
20768
|
status: "success",
|
|
20721
20769
|
eyebrow: "Auto CLI",
|
|
20722
20770
|
title: "Login authorized",
|
|
20723
20771
|
message: "Auto received the browser authorization. The CLI will finish signing you in from your terminal.",
|
|
20724
|
-
details: [
|
|
20725
|
-
{ label: "Server", value: serverUrl },
|
|
20726
|
-
{
|
|
20727
|
-
label: "Organization",
|
|
20728
|
-
value: result.organizationName ?? selectionDetails.organization
|
|
20729
|
-
},
|
|
20730
|
-
{
|
|
20731
|
-
label: "Project",
|
|
20732
|
-
value: result.projectName ?? selectionDetails.project
|
|
20733
|
-
}
|
|
20734
|
-
]
|
|
20772
|
+
details: [{ label: "Server", value: serverUrl }]
|
|
20735
20773
|
}),
|
|
20736
20774
|
failureHtml: () => renderOAuthLoopbackPage({
|
|
20737
20775
|
status: "failure",
|
|
20738
20776
|
eyebrow: "Auto CLI",
|
|
20739
20777
|
title: "Login failed",
|
|
20740
20778
|
message: "The browser authorization did not complete. Return to your terminal to retry or inspect the error.",
|
|
20741
|
-
details: [
|
|
20742
|
-
{ label: "Server", value: serverUrl },
|
|
20743
|
-
{ label: "Organization", value: selectionDetails.organization },
|
|
20744
|
-
{ label: "Project", value: selectionDetails.project }
|
|
20745
|
-
]
|
|
20779
|
+
details: [{ label: "Server", value: serverUrl }]
|
|
20746
20780
|
})
|
|
20747
20781
|
});
|
|
20748
20782
|
try {
|
|
20749
20783
|
const authorizeUrl = new URL("/auth/cli", serverUrl);
|
|
20750
20784
|
authorizeUrl.searchParams.set("pkce_challenge", pkceChallenge(verifier));
|
|
20751
20785
|
authorizeUrl.searchParams.set("redirect_uri", callback.redirectUri);
|
|
20752
|
-
if (previous.organizationId && previous.projectId) {
|
|
20753
|
-
authorizeUrl.searchParams.set(
|
|
20754
|
-
"organization_id",
|
|
20755
|
-
previous.organizationId
|
|
20756
|
-
);
|
|
20757
|
-
authorizeUrl.searchParams.set("project_id", previous.projectId);
|
|
20758
|
-
}
|
|
20759
20786
|
input.writeOutput(`Opening ${authorizeUrl.toString()}`);
|
|
20760
20787
|
input.writeOutput("Waiting for browser authorization...");
|
|
20761
20788
|
openBrowser(authorizeUrl.toString());
|
|
20762
20789
|
const { code } = await callback.result;
|
|
20763
|
-
await
|
|
20790
|
+
const token3 = await exchangeAuthorizationCode({
|
|
20764
20791
|
code,
|
|
20765
|
-
configPath: input.configPath,
|
|
20766
20792
|
fetch: input.fetch,
|
|
20767
|
-
previous,
|
|
20768
20793
|
redirectUri: callback.redirectUri,
|
|
20769
20794
|
serverUrl,
|
|
20770
20795
|
verifier
|
|
20771
20796
|
});
|
|
20772
|
-
|
|
20797
|
+
await finishLogin({
|
|
20798
|
+
token: token3,
|
|
20799
|
+
serverUrl,
|
|
20800
|
+
fetch: input.fetch,
|
|
20801
|
+
configPath: input.configPath,
|
|
20802
|
+
writeOutput: input.writeOutput
|
|
20803
|
+
});
|
|
20773
20804
|
return;
|
|
20774
20805
|
} finally {
|
|
20775
20806
|
callback.close();
|
|
20776
20807
|
}
|
|
20777
20808
|
}
|
|
20778
|
-
await
|
|
20809
|
+
const token2 = await exchangeAuthorizationCode({
|
|
20779
20810
|
code: input.options.code,
|
|
20780
|
-
configPath: input.configPath,
|
|
20781
20811
|
fetch: input.fetch,
|
|
20782
|
-
previous,
|
|
20783
20812
|
redirectUri: "http://127.0.0.1/callback",
|
|
20784
20813
|
serverUrl,
|
|
20785
20814
|
verifier
|
|
20786
20815
|
});
|
|
20787
|
-
|
|
20816
|
+
await finishLogin({
|
|
20817
|
+
token: token2,
|
|
20818
|
+
serverUrl,
|
|
20819
|
+
fetch: input.fetch,
|
|
20820
|
+
configPath: input.configPath,
|
|
20821
|
+
writeOutput: input.writeOutput
|
|
20822
|
+
});
|
|
20788
20823
|
}
|
|
20789
|
-
async function
|
|
20790
|
-
|
|
20824
|
+
async function exchangeAuthorizationCode(input) {
|
|
20825
|
+
return postJson(
|
|
20791
20826
|
input.fetch,
|
|
20792
20827
|
`${input.serverUrl}/api/v1/auth/cli/token`,
|
|
20793
20828
|
{
|
|
20794
20829
|
grant_type: "authorization_code",
|
|
20795
20830
|
code: input.code,
|
|
20796
20831
|
code_verifier: input.verifier,
|
|
20797
|
-
redirect_uri: input.redirectUri
|
|
20798
|
-
...input.previous.organizationId && input.previous.projectId ? {
|
|
20799
|
-
organization_id: input.previous.organizationId,
|
|
20800
|
-
project_id: input.previous.projectId
|
|
20801
|
-
} : {}
|
|
20832
|
+
redirect_uri: input.redirectUri
|
|
20802
20833
|
}
|
|
20803
20834
|
);
|
|
20804
|
-
writeConfig(
|
|
20805
|
-
{
|
|
20806
|
-
...input.previous,
|
|
20807
|
-
serverUrl: input.serverUrl,
|
|
20808
|
-
refreshToken: token2.refresh_token,
|
|
20809
|
-
accessToken: token2.access_token,
|
|
20810
|
-
accessTokenExpiresAt: token2.access_token ? accessTokenExpiresAt(token2) : void 0,
|
|
20811
|
-
accessTokenOrganizationId: token2.access_token ? input.previous.organizationId : void 0,
|
|
20812
|
-
accessTokenProjectId: token2.access_token ? input.previous.projectId : void 0
|
|
20813
|
-
},
|
|
20814
|
-
input.configPath
|
|
20815
|
-
);
|
|
20816
20835
|
}
|
|
20817
|
-
async function
|
|
20818
|
-
const
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
};
|
|
20822
|
-
if (!input.previous.organizationId) {
|
|
20823
|
-
return fallback;
|
|
20824
|
-
}
|
|
20825
|
-
const client = createApiClient({
|
|
20836
|
+
async function finishLogin(input) {
|
|
20837
|
+
const { token: token2, serverUrl } = input;
|
|
20838
|
+
const previous = readConfig(input.configPath);
|
|
20839
|
+
const profile = token2.user ? findAccountProfile({
|
|
20826
20840
|
configPath: input.configPath,
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
});
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
|
|
20837
|
-
|
|
20841
|
+
serverUrl,
|
|
20842
|
+
userId: token2.user.id,
|
|
20843
|
+
userEmail: token2.user.email
|
|
20844
|
+
}) : void 0;
|
|
20845
|
+
const selectionSource = profile ?? previous;
|
|
20846
|
+
const selection = selectionSource.organizationId && selectionSource.projectId ? {
|
|
20847
|
+
organizationId: selectionSource.organizationId,
|
|
20848
|
+
projectId: selectionSource.projectId
|
|
20849
|
+
} : void 0;
|
|
20850
|
+
let config2 = {
|
|
20851
|
+
serverUrl,
|
|
20852
|
+
userId: token2.user?.id,
|
|
20853
|
+
userEmail: token2.user?.email,
|
|
20854
|
+
refreshToken: token2.refresh_token,
|
|
20855
|
+
accessToken: token2.access_token,
|
|
20856
|
+
accessTokenExpiresAt: token2.access_token ? accessTokenExpiresAt(token2) : void 0
|
|
20857
|
+
};
|
|
20858
|
+
if (selection) {
|
|
20859
|
+
try {
|
|
20860
|
+
const scoped = await postJson(
|
|
20861
|
+
input.fetch,
|
|
20862
|
+
`${serverUrl}/api/v1/auth/cli/token`,
|
|
20863
|
+
{
|
|
20864
|
+
grant_type: "refresh_token",
|
|
20865
|
+
refresh_token: token2.refresh_token,
|
|
20866
|
+
organization_id: selection.organizationId,
|
|
20867
|
+
project_id: selection.projectId
|
|
20868
|
+
}
|
|
20869
|
+
);
|
|
20870
|
+
config2 = {
|
|
20871
|
+
...config2,
|
|
20872
|
+
...selection,
|
|
20873
|
+
refreshToken: scoped.refresh_token,
|
|
20874
|
+
accessToken: scoped.access_token,
|
|
20875
|
+
accessTokenExpiresAt: scoped.access_token ? accessTokenExpiresAt(scoped) : void 0,
|
|
20876
|
+
accessTokenOrganizationId: scoped.access_token ? selection.organizationId : void 0,
|
|
20877
|
+
accessTokenProjectId: scoped.access_token ? selection.projectId : void 0
|
|
20878
|
+
};
|
|
20879
|
+
} catch {
|
|
20880
|
+
input.writeOutput(
|
|
20881
|
+
"The saved organization/project selection is not available for this account; run `auto orgs list` to pick a new one."
|
|
20838
20882
|
);
|
|
20839
|
-
if (project) {
|
|
20840
|
-
return {
|
|
20841
|
-
organization: project.organizationName,
|
|
20842
|
-
project: project.projectName
|
|
20843
|
-
};
|
|
20844
|
-
}
|
|
20845
20883
|
}
|
|
20846
|
-
const organizations = await client.listOrganizations({
|
|
20847
|
-
apiBaseUrl: input.serverUrl
|
|
20848
|
-
});
|
|
20849
|
-
const organization = organizations.organizations.find(
|
|
20850
|
-
(candidate) => candidate.organizationId === input.previous.organizationId
|
|
20851
|
-
);
|
|
20852
|
-
return {
|
|
20853
|
-
organization: organization?.organizationName ?? fallback.organization,
|
|
20854
|
-
project: fallback.project
|
|
20855
|
-
};
|
|
20856
|
-
} catch {
|
|
20857
|
-
return fallback;
|
|
20858
20884
|
}
|
|
20885
|
+
writeConfig(config2, input.configPath);
|
|
20886
|
+
input.writeOutput(
|
|
20887
|
+
token2.user ? `Logged in as ${token2.user.email}.` : "Logged in."
|
|
20888
|
+
);
|
|
20859
20889
|
}
|
|
20860
20890
|
async function sleep(ms) {
|
|
20861
20891
|
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
@@ -20864,11 +20894,11 @@ var init_login = __esm({
|
|
|
20864
20894
|
"src/commands/auth/login.ts"() {
|
|
20865
20895
|
"use strict";
|
|
20866
20896
|
init_base_url();
|
|
20867
|
-
init_client();
|
|
20868
20897
|
init_http();
|
|
20869
20898
|
init_tokens();
|
|
20870
20899
|
init_browser();
|
|
20871
20900
|
init_file();
|
|
20901
|
+
init_profiles2();
|
|
20872
20902
|
init_loopback();
|
|
20873
20903
|
init_pkce();
|
|
20874
20904
|
}
|
|
@@ -20878,7 +20908,7 @@ var init_login = __esm({
|
|
|
20878
20908
|
import { spawn as spawn2 } from "child_process";
|
|
20879
20909
|
import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
20880
20910
|
import { tmpdir } from "os";
|
|
20881
|
-
import { join as
|
|
20911
|
+
import { join as join5 } from "path";
|
|
20882
20912
|
import { parseAllDocuments as parseYamlDocuments2, stringify as stringify2 } from "yaml";
|
|
20883
20913
|
async function editResource(input) {
|
|
20884
20914
|
const reference = parseEditableResource(input.resource);
|
|
@@ -20893,8 +20923,8 @@ async function editResource(input) {
|
|
|
20893
20923
|
const document = editableResourceDocument(reference.kind, current);
|
|
20894
20924
|
const source = `${stringify2(document).trimEnd()}
|
|
20895
20925
|
`;
|
|
20896
|
-
const tempRoot = mkdtempSync(
|
|
20897
|
-
const filePath =
|
|
20926
|
+
const tempRoot = mkdtempSync(join5(tmpdir(), "auto-edit-"));
|
|
20927
|
+
const filePath = join5(tempRoot, `${reference.kind}-${reference.name}.yaml`);
|
|
20898
20928
|
writeFileSync3(filePath, source, "utf8");
|
|
20899
20929
|
let removeTempFile = false;
|
|
20900
20930
|
try {
|
|
@@ -21153,7 +21183,7 @@ var init_package = __esm({
|
|
|
21153
21183
|
"package.json"() {
|
|
21154
21184
|
package_default = {
|
|
21155
21185
|
name: "@autohq/cli",
|
|
21156
|
-
version: "0.1.
|
|
21186
|
+
version: "0.1.88",
|
|
21157
21187
|
license: "SEE LICENSE IN README.md",
|
|
21158
21188
|
publishConfig: {
|
|
21159
21189
|
access: "public"
|
|
@@ -28006,6 +28036,7 @@ init_login();
|
|
|
28006
28036
|
|
|
28007
28037
|
// src/commands/auth/profile.ts
|
|
28008
28038
|
init_file();
|
|
28039
|
+
init_profiles2();
|
|
28009
28040
|
async function handleAuthStatus(context) {
|
|
28010
28041
|
const result = await fetchAuthStatus(context);
|
|
28011
28042
|
context.io.writeResult(result, formatAuthStatusText);
|
|
@@ -28030,12 +28061,58 @@ function logout(context) {
|
|
|
28030
28061
|
);
|
|
28031
28062
|
context.writeOutput("Logged out.");
|
|
28032
28063
|
}
|
|
28064
|
+
function switchAccount(context, accountEmail, options = {}) {
|
|
28065
|
+
const profiles = listProfiles(context.configPath);
|
|
28066
|
+
if (profiles.length === 0) {
|
|
28067
|
+
throw new Error("No stored accounts. Run `auto auth login` first.");
|
|
28068
|
+
}
|
|
28069
|
+
const active = readConfig(context.configPath);
|
|
28070
|
+
if (!accountEmail) {
|
|
28071
|
+
for (const profile of profiles) {
|
|
28072
|
+
context.writeOutput(accountLine(profile.config, active));
|
|
28073
|
+
}
|
|
28074
|
+
return;
|
|
28075
|
+
}
|
|
28076
|
+
const matches = profiles.map((profile) => profile.config).filter(
|
|
28077
|
+
(config2) => config2.userEmail?.toLowerCase() === accountEmail.toLowerCase() && (!options.server || config2.serverUrl === options.server)
|
|
28078
|
+
);
|
|
28079
|
+
if (matches.length === 0) {
|
|
28080
|
+
throw new Error(
|
|
28081
|
+
`No stored account for ${accountEmail}. Run \`auto auth login\` to add it.`
|
|
28082
|
+
);
|
|
28083
|
+
}
|
|
28084
|
+
const match = matches.length === 1 ? matches[0] : matches.find((config2) => config2.serverUrl === active.serverUrl);
|
|
28085
|
+
if (!match) {
|
|
28086
|
+
throw new Error(
|
|
28087
|
+
`Multiple servers have a stored account for ${accountEmail}: ${matches.map((config2) => config2.serverUrl).join(
|
|
28088
|
+
", "
|
|
28089
|
+
)}. Pick one with \`auto auth switch ${accountEmail} --server <url>\`.`
|
|
28090
|
+
);
|
|
28091
|
+
}
|
|
28092
|
+
writeConfig(match, context.configPath);
|
|
28093
|
+
context.writeOutput(`Switched to ${match.userEmail} (${match.serverUrl}).`);
|
|
28094
|
+
if (!match.refreshToken) {
|
|
28095
|
+
context.writeOutput(
|
|
28096
|
+
"This account has no stored credentials; run `auto auth login`."
|
|
28097
|
+
);
|
|
28098
|
+
}
|
|
28099
|
+
}
|
|
28100
|
+
function accountLine(config2, active) {
|
|
28101
|
+
const isActive = Boolean(config2.userEmail) && config2.userEmail === active.userEmail && config2.serverUrl === active.serverUrl;
|
|
28102
|
+
return [
|
|
28103
|
+
config2.userEmail,
|
|
28104
|
+
`server=${config2.serverUrl ?? "(unset)"}`,
|
|
28105
|
+
config2.refreshToken ? void 0 : "logged_out",
|
|
28106
|
+
isActive ? "(active)" : void 0
|
|
28107
|
+
].filter(Boolean).join(" ");
|
|
28108
|
+
}
|
|
28033
28109
|
async function fetchAuthStatus(context) {
|
|
28034
28110
|
const config2 = readConfig(context.configPath);
|
|
28035
28111
|
const client = createContextApiClient(context);
|
|
28036
28112
|
const operator = client.getOperatorInfo();
|
|
28037
28113
|
const base = {
|
|
28038
28114
|
serverUrl: config2.serverUrl ?? context.env.AUTO_API_BASE_URL ?? null,
|
|
28115
|
+
account: config2.userEmail ?? null,
|
|
28039
28116
|
organizationId: config2.organizationId ?? null,
|
|
28040
28117
|
projectId: config2.projectId ?? null,
|
|
28041
28118
|
authSource: operator.authType
|
|
@@ -28077,6 +28154,9 @@ async function fetchAuthStatus(context) {
|
|
|
28077
28154
|
}
|
|
28078
28155
|
function formatAuthStatusText(result, writeLine) {
|
|
28079
28156
|
writeLine(`server: ${result.serverUrl ?? "(unset)"}`);
|
|
28157
|
+
if (result.account) {
|
|
28158
|
+
writeLine(`account: ${result.account}`);
|
|
28159
|
+
}
|
|
28080
28160
|
writeLine(`organization: ${result.organizationId ?? "(unset)"}`);
|
|
28081
28161
|
writeLine(`project: ${result.projectId ?? "(unset)"}`);
|
|
28082
28162
|
writeLine(
|
|
@@ -28189,6 +28269,11 @@ function registerAuthCommands(program, context) {
|
|
|
28189
28269
|
await handleWhoami(context);
|
|
28190
28270
|
});
|
|
28191
28271
|
auth.command("logout").description("Remove the local user refresh token.").action(() => logout(context));
|
|
28272
|
+
auth.command("switch").description(
|
|
28273
|
+
"Switch the active account to a stored profile, or list stored accounts."
|
|
28274
|
+
).argument("[email]", "Email of a stored account").option("--server <url>", "Auto web server URL of the stored account").action(
|
|
28275
|
+
(email3, options) => switchAccount(context, email3, options)
|
|
28276
|
+
);
|
|
28192
28277
|
}
|
|
28193
28278
|
|
|
28194
28279
|
// src/lib/stdio/readline.ts
|
|
@@ -30745,7 +30830,7 @@ init_resources2();
|
|
|
30745
30830
|
init_browser();
|
|
30746
30831
|
import { existsSync as existsSync2, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
30747
30832
|
import { homedir as homedir2, tmpdir as tmpdir2 } from "os";
|
|
30748
|
-
import { join as
|
|
30833
|
+
import { join as join6 } from "path";
|
|
30749
30834
|
var POLL_INTERVAL_MS = 2e3;
|
|
30750
30835
|
var POLL_TIMEOUT_MS = 5 * 6e4;
|
|
30751
30836
|
var SLACK_APPS_URL = "https://api.slack.com/apps";
|
|
@@ -30906,7 +30991,7 @@ async function promptForIconUploads(input, options) {
|
|
|
30906
30991
|
}
|
|
30907
30992
|
}
|
|
30908
30993
|
function stagedLocationLabel(stagedPath) {
|
|
30909
|
-
return stagedPath.startsWith(`${
|
|
30994
|
+
return stagedPath.startsWith(`${join6(homedir2(), "Downloads")}/`) ? "Downloads" : "the printed path";
|
|
30910
30995
|
}
|
|
30911
30996
|
async function stageAvatarImage(input) {
|
|
30912
30997
|
try {
|
|
@@ -30918,9 +31003,9 @@ async function stageAvatarImage(input) {
|
|
|
30918
31003
|
}
|
|
30919
31004
|
const contentType = response.headers.get("content-type") ?? "";
|
|
30920
31005
|
const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
|
|
30921
|
-
const downloads =
|
|
30922
|
-
const directory = existsSync2(downloads) ? downloads : mkdtempSync2(
|
|
30923
|
-
const path =
|
|
31006
|
+
const downloads = join6(homedir2(), "Downloads");
|
|
31007
|
+
const directory = existsSync2(downloads) ? downloads : mkdtempSync2(join6(tmpdir2(), "auto-avatar-"));
|
|
31008
|
+
const path = join6(directory, `${input.session}-avatar${extension}`);
|
|
30924
31009
|
writeFileSync4(path, Buffer.from(await response.arrayBuffer()));
|
|
30925
31010
|
return path;
|
|
30926
31011
|
} catch {
|