@autohq/cli 0.1.107 → 0.1.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/agent-bridge.js +3 -1
- package/dist/index.js +276 -84
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -44,6 +44,24 @@ auto tui # interactive dashboard
|
|
|
44
44
|
|
|
45
45
|
Run `auto --help` for the full command tree.
|
|
46
46
|
|
|
47
|
+
## Accounts and profiles
|
|
48
|
+
|
|
49
|
+
Each signed-in account is stored as a named profile; the active one is a
|
|
50
|
+
pointer in `~/.auto/config.yaml`. Logging in to a second account never
|
|
51
|
+
discards the first.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
auto auth login --profile staging # sign in under a chosen profile name
|
|
55
|
+
auto auth list # list stored accounts
|
|
56
|
+
auto auth switch staging # make a profile (or email) active
|
|
57
|
+
auto auth remove staging # delete a stored profile
|
|
58
|
+
auto --profile staging runs list # use a profile for one invocation
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`AUTO_PROFILE=<name>` pins a whole shell session to a profile without
|
|
62
|
+
moving the active pointer, so different terminals can act as different
|
|
63
|
+
accounts at the same time.
|
|
64
|
+
|
|
47
65
|
## License
|
|
48
66
|
|
|
49
67
|
Copyright Fractal Works. All rights reserved.
|
package/dist/agent-bridge.js
CHANGED
|
@@ -26206,7 +26206,7 @@ Object.assign(lookup, {
|
|
|
26206
26206
|
// package.json
|
|
26207
26207
|
var package_default = {
|
|
26208
26208
|
name: "@autohq/cli",
|
|
26209
|
-
version: "0.1.
|
|
26209
|
+
version: "0.1.108",
|
|
26210
26210
|
license: "SEE LICENSE IN README.md",
|
|
26211
26211
|
publishConfig: {
|
|
26212
26212
|
access: "public"
|
|
@@ -26230,6 +26230,7 @@ var package_default = {
|
|
|
26230
26230
|
"dev:tui": "node ../../scripts/cli-tui-dev-watch.mjs",
|
|
26231
26231
|
"skill:generate": "tsx scripts/generate-skill-content.ts",
|
|
26232
26232
|
test: "npm run test:unit",
|
|
26233
|
+
"test:mutation": "stryker run",
|
|
26233
26234
|
"test:unit": 'tsx --test "test/**/*.unit.test.ts"',
|
|
26234
26235
|
typecheck: "tsc --noEmit"
|
|
26235
26236
|
},
|
|
@@ -26250,6 +26251,7 @@ var package_default = {
|
|
|
26250
26251
|
devDependencies: {
|
|
26251
26252
|
"@auto/protocol": "*",
|
|
26252
26253
|
"@auto/schemas": "*",
|
|
26254
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
26253
26255
|
"@types/react": "^19",
|
|
26254
26256
|
tsup: "^8.5.1"
|
|
26255
26257
|
}
|
package/dist/index.js
CHANGED
|
@@ -18512,7 +18512,7 @@ var init_package = __esm({
|
|
|
18512
18512
|
"package.json"() {
|
|
18513
18513
|
package_default = {
|
|
18514
18514
|
name: "@autohq/cli",
|
|
18515
|
-
version: "0.1.
|
|
18515
|
+
version: "0.1.108",
|
|
18516
18516
|
license: "SEE LICENSE IN README.md",
|
|
18517
18517
|
publishConfig: {
|
|
18518
18518
|
access: "public"
|
|
@@ -18536,6 +18536,7 @@ var init_package = __esm({
|
|
|
18536
18536
|
"dev:tui": "node ../../scripts/cli-tui-dev-watch.mjs",
|
|
18537
18537
|
"skill:generate": "tsx scripts/generate-skill-content.ts",
|
|
18538
18538
|
test: "npm run test:unit",
|
|
18539
|
+
"test:mutation": "stryker run",
|
|
18539
18540
|
"test:unit": 'tsx --test "test/**/*.unit.test.ts"',
|
|
18540
18541
|
typecheck: "tsc --noEmit"
|
|
18541
18542
|
},
|
|
@@ -18556,6 +18557,7 @@ var init_package = __esm({
|
|
|
18556
18557
|
devDependencies: {
|
|
18557
18558
|
"@auto/protocol": "*",
|
|
18558
18559
|
"@auto/schemas": "*",
|
|
18560
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
18559
18561
|
"@types/react": "^19",
|
|
18560
18562
|
tsup: "^8.5.1"
|
|
18561
18563
|
}
|
|
@@ -18598,18 +18600,36 @@ var init_tokens = __esm({
|
|
|
18598
18600
|
// src/lib/config/path.ts
|
|
18599
18601
|
import { createHash } from "crypto";
|
|
18600
18602
|
import { homedir as homedir2 } from "os";
|
|
18601
|
-
import { join, normalize } from "path";
|
|
18603
|
+
import { basename, dirname as dirname2, join, normalize } from "path";
|
|
18602
18604
|
function defaultConfigPath() {
|
|
18603
18605
|
return process.env.AUTO_CLI_CONFIG ?? join(homedir2(), ".auto", "config.yaml");
|
|
18604
18606
|
}
|
|
18607
|
+
function isProfilePath(path2) {
|
|
18608
|
+
return basename(dirname2(path2)) === PROFILES_DIR_NAME;
|
|
18609
|
+
}
|
|
18605
18610
|
function profilesDir(configPath = defaultConfigPath()) {
|
|
18611
|
+
if (isProfilePath(configPath)) return dirname2(configPath);
|
|
18606
18612
|
return normalize(join(configPath, "..", PROFILES_DIR_NAME));
|
|
18607
18613
|
}
|
|
18608
|
-
function
|
|
18614
|
+
function profileFilePath(configPath, name) {
|
|
18615
|
+
return join(profilesDir(configPath), `${name}.yaml`);
|
|
18616
|
+
}
|
|
18617
|
+
function profileNameFromPath(path2) {
|
|
18618
|
+
return basename(path2).replace(/\.yaml$/, "");
|
|
18619
|
+
}
|
|
18620
|
+
function assertValidProfileName(name) {
|
|
18621
|
+
if (!PROFILE_NAME_PATTERN.test(name)) {
|
|
18622
|
+
throw new Error(
|
|
18623
|
+
`Invalid profile name "${name}". Profile names use lowercase letters, digits, dots, dashes, and underscores.`
|
|
18624
|
+
);
|
|
18625
|
+
}
|
|
18626
|
+
return name;
|
|
18627
|
+
}
|
|
18628
|
+
function derivedProfileName(input) {
|
|
18609
18629
|
const key = `${input.userEmail.toLowerCase()}
|
|
18610
18630
|
${serverHost(input.serverUrl)}`;
|
|
18611
18631
|
const hash2 = createHash("sha256").update(key).digest("hex").slice(0, 8);
|
|
18612
|
-
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}
|
|
18632
|
+
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}`;
|
|
18613
18633
|
}
|
|
18614
18634
|
function serverHost(serverUrl) {
|
|
18615
18635
|
try {
|
|
@@ -18621,62 +18641,129 @@ function serverHost(serverUrl) {
|
|
|
18621
18641
|
function slug(value) {
|
|
18622
18642
|
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "_");
|
|
18623
18643
|
}
|
|
18624
|
-
var PROFILES_DIR_NAME;
|
|
18644
|
+
var PROFILES_DIR_NAME, PROFILE_NAME_PATTERN;
|
|
18625
18645
|
var init_path = __esm({
|
|
18626
18646
|
"src/lib/config/path.ts"() {
|
|
18627
18647
|
"use strict";
|
|
18628
18648
|
PROFILES_DIR_NAME = "profiles";
|
|
18649
|
+
PROFILE_NAME_PATTERN = /^[a-z0-9._-]+$/;
|
|
18629
18650
|
}
|
|
18630
18651
|
});
|
|
18631
18652
|
|
|
18632
18653
|
// src/lib/config/file.ts
|
|
18633
18654
|
import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
18634
|
-
import {
|
|
18655
|
+
import { dirname as dirname3 } from "path";
|
|
18635
18656
|
function readConfig(path2 = defaultConfigPath()) {
|
|
18657
|
+
const raw = readRawConfigFile(path2);
|
|
18658
|
+
if (isProfilePath(path2) || !raw.currentProfile) return raw.config;
|
|
18659
|
+
return readRawConfigFile(profileFilePath(path2, raw.currentProfile)).config;
|
|
18660
|
+
}
|
|
18661
|
+
function currentProfileName(path2 = defaultConfigPath()) {
|
|
18662
|
+
if (isProfilePath(path2)) return profileNameFromPath(path2);
|
|
18663
|
+
return readRawConfigFile(path2).currentProfile;
|
|
18664
|
+
}
|
|
18665
|
+
function writeConfig(config2, path2 = defaultConfigPath()) {
|
|
18666
|
+
if (isProfilePath(path2)) {
|
|
18667
|
+
writeConfigFile(config2, path2);
|
|
18668
|
+
return;
|
|
18669
|
+
}
|
|
18670
|
+
const raw = readRawConfigFile(path2);
|
|
18671
|
+
if (raw.currentProfile) {
|
|
18672
|
+
const targetPath = profileFilePath(path2, raw.currentProfile);
|
|
18673
|
+
const target = readRawConfigFile(targetPath).config;
|
|
18674
|
+
if (hasAccountIdentity(config2) && !sameAccount(config2, target)) {
|
|
18675
|
+
setCurrentProfile(saveProfile({ config: config2, configPath: path2 }), path2);
|
|
18676
|
+
return;
|
|
18677
|
+
}
|
|
18678
|
+
writeConfigFile(config2, targetPath);
|
|
18679
|
+
return;
|
|
18680
|
+
}
|
|
18681
|
+
if (hasAccountIdentity(config2)) {
|
|
18682
|
+
setCurrentProfile(saveProfile({ config: config2, configPath: path2 }), path2);
|
|
18683
|
+
return;
|
|
18684
|
+
}
|
|
18685
|
+
writeConfigFile(config2, path2);
|
|
18686
|
+
}
|
|
18687
|
+
function saveProfile(input) {
|
|
18688
|
+
const configPath = input.configPath ?? defaultConfigPath();
|
|
18689
|
+
const name = resolveProfileName(input);
|
|
18690
|
+
writeConfigFile(input.config, profileFilePath(configPath, name));
|
|
18691
|
+
return name;
|
|
18692
|
+
}
|
|
18693
|
+
function setCurrentProfile(name, path2 = defaultConfigPath()) {
|
|
18694
|
+
if (isProfilePath(path2)) {
|
|
18695
|
+
throw new Error(
|
|
18696
|
+
"Cannot change the active profile while pinned to a profile."
|
|
18697
|
+
);
|
|
18698
|
+
}
|
|
18699
|
+
writeFile2(`${CURRENT_PROFILE_KEY}: ${assertValidProfileName(name)}
|
|
18700
|
+
`, path2);
|
|
18701
|
+
}
|
|
18702
|
+
function clearCurrentProfile(path2 = defaultConfigPath()) {
|
|
18703
|
+
if (isProfilePath(path2)) {
|
|
18704
|
+
throw new Error(
|
|
18705
|
+
"Cannot change the active profile while pinned to a profile."
|
|
18706
|
+
);
|
|
18707
|
+
}
|
|
18708
|
+
writeFile2("", path2);
|
|
18709
|
+
}
|
|
18710
|
+
function resolveProfileName(input) {
|
|
18711
|
+
if (input.name !== void 0) return assertValidProfileName(input.name);
|
|
18712
|
+
if (!input.config.userEmail || !input.config.serverUrl) {
|
|
18713
|
+
throw new Error(
|
|
18714
|
+
"A profile name is required for a config without a signed-in account."
|
|
18715
|
+
);
|
|
18716
|
+
}
|
|
18717
|
+
return derivedProfileName({
|
|
18718
|
+
userEmail: input.config.userEmail,
|
|
18719
|
+
serverUrl: input.config.serverUrl
|
|
18720
|
+
});
|
|
18721
|
+
}
|
|
18722
|
+
function hasAccountIdentity(config2) {
|
|
18723
|
+
return Boolean(config2.userEmail && config2.serverUrl);
|
|
18724
|
+
}
|
|
18725
|
+
function sameAccount(next, current) {
|
|
18726
|
+
if (!current.userEmail) return true;
|
|
18727
|
+
if (next.serverUrl !== current.serverUrl) return false;
|
|
18728
|
+
if (next.userId && current.userId) return next.userId === current.userId;
|
|
18729
|
+
return next.userEmail?.toLowerCase() === current.userEmail.toLowerCase();
|
|
18730
|
+
}
|
|
18731
|
+
function readRawConfigFile(path2) {
|
|
18636
18732
|
try {
|
|
18637
18733
|
const text = readFileSync2(path2, "utf8");
|
|
18638
18734
|
const config2 = {};
|
|
18735
|
+
let currentProfile;
|
|
18639
18736
|
for (const line of text.split(/\r?\n/)) {
|
|
18640
18737
|
const match = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line.trim());
|
|
18641
18738
|
if (!match) continue;
|
|
18739
|
+
if (match[1] === CURRENT_PROFILE_KEY) {
|
|
18740
|
+
currentProfile = match[2] || void 0;
|
|
18741
|
+
continue;
|
|
18742
|
+
}
|
|
18642
18743
|
const key = CONFIG_KEYS.find((candidate) => candidate === match[1]);
|
|
18643
18744
|
if (key) config2[key] = match[2] ?? "";
|
|
18644
18745
|
}
|
|
18645
|
-
return config2;
|
|
18746
|
+
return { config: config2, currentProfile };
|
|
18646
18747
|
} catch (err) {
|
|
18647
|
-
if (err.code === "ENOENT")
|
|
18748
|
+
if (err.code === "ENOENT") {
|
|
18749
|
+
return { config: {} };
|
|
18750
|
+
}
|
|
18648
18751
|
throw err;
|
|
18649
18752
|
}
|
|
18650
18753
|
}
|
|
18651
|
-
function writeConfig(config2, path2 = defaultConfigPath()) {
|
|
18652
|
-
writeConfigFile(config2, path2);
|
|
18653
|
-
if (config2.userEmail && config2.serverUrl && basename(dirname2(path2)) !== PROFILES_DIR_NAME) {
|
|
18654
|
-
writeConfigFile(
|
|
18655
|
-
config2,
|
|
18656
|
-
join2(
|
|
18657
|
-
dirname2(path2),
|
|
18658
|
-
PROFILES_DIR_NAME,
|
|
18659
|
-
profileFileName({
|
|
18660
|
-
userEmail: config2.userEmail,
|
|
18661
|
-
serverUrl: config2.serverUrl
|
|
18662
|
-
})
|
|
18663
|
-
)
|
|
18664
|
-
);
|
|
18665
|
-
}
|
|
18666
|
-
}
|
|
18667
18754
|
function writeConfigFile(config2, path2) {
|
|
18668
|
-
mkdirSync2(dirname2(path2), { recursive: true });
|
|
18669
18755
|
const lines = CONFIG_KEYS.filter((key) => config2[key]).map(
|
|
18670
18756
|
(key) => `${key}: ${config2[key]}`
|
|
18671
18757
|
);
|
|
18672
|
-
|
|
18673
|
-
`,
|
|
18674
|
-
|
|
18675
|
-
|
|
18676
|
-
});
|
|
18758
|
+
writeFile2(`${lines.join("\n")}
|
|
18759
|
+
`, path2);
|
|
18760
|
+
}
|
|
18761
|
+
function writeFile2(content, path2) {
|
|
18762
|
+
mkdirSync2(dirname3(path2), { recursive: true });
|
|
18763
|
+
writeFileSync2(path2, content, { encoding: "utf8", mode: 384 });
|
|
18677
18764
|
chmodSync(path2, 384);
|
|
18678
18765
|
}
|
|
18679
|
-
var CONFIG_KEYS;
|
|
18766
|
+
var CONFIG_KEYS, CURRENT_PROFILE_KEY;
|
|
18680
18767
|
var init_file = __esm({
|
|
18681
18768
|
"src/lib/config/file.ts"() {
|
|
18682
18769
|
"use strict";
|
|
@@ -18693,6 +18780,7 @@ var init_file = __esm({
|
|
|
18693
18780
|
"accessTokenOrganizationId",
|
|
18694
18781
|
"accessTokenProjectId"
|
|
18695
18782
|
];
|
|
18783
|
+
CURRENT_PROFILE_KEY = "currentProfile";
|
|
18696
18784
|
}
|
|
18697
18785
|
});
|
|
18698
18786
|
|
|
@@ -21116,10 +21204,10 @@ import {
|
|
|
21116
21204
|
} from "fs";
|
|
21117
21205
|
import {
|
|
21118
21206
|
basename as basename2,
|
|
21119
|
-
dirname as
|
|
21207
|
+
dirname as dirname4,
|
|
21120
21208
|
extname,
|
|
21121
21209
|
isAbsolute,
|
|
21122
|
-
join as
|
|
21210
|
+
join as join2,
|
|
21123
21211
|
resolve
|
|
21124
21212
|
} from "path";
|
|
21125
21213
|
import { parseAllDocuments as parseYamlDocuments } from "yaml";
|
|
@@ -21135,7 +21223,7 @@ function readProjectApplyRequest(options) {
|
|
|
21135
21223
|
);
|
|
21136
21224
|
return { ...request, assets: assets2 };
|
|
21137
21225
|
}
|
|
21138
|
-
const directory = options.directory ??
|
|
21226
|
+
const directory = options.directory ?? join2(process.cwd(), ".auto");
|
|
21139
21227
|
const files = applyFiles(directory);
|
|
21140
21228
|
if (files.length === 0) {
|
|
21141
21229
|
throw new Error(`No resource files found in ${directory}`);
|
|
@@ -21198,7 +21286,7 @@ function applyFiles(root) {
|
|
|
21198
21286
|
const files = [];
|
|
21199
21287
|
for (const kind of APPLY_RESOURCE_ORDER) {
|
|
21200
21288
|
const directory = APPLY_DIRECTORIES[kind];
|
|
21201
|
-
const path2 =
|
|
21289
|
+
const path2 = join2(root, directory);
|
|
21202
21290
|
let entries;
|
|
21203
21291
|
try {
|
|
21204
21292
|
entries = readdirSync(path2, { withFileTypes: true });
|
|
@@ -21342,15 +21430,15 @@ function validateSessionAvatarAsset(input) {
|
|
|
21342
21430
|
}
|
|
21343
21431
|
function applyProjectRoot(directory) {
|
|
21344
21432
|
const resolved = resolve(directory);
|
|
21345
|
-
return basename2(resolved) === ".auto" ?
|
|
21433
|
+
return basename2(resolved) === ".auto" ? dirname4(resolved) : resolved;
|
|
21346
21434
|
}
|
|
21347
21435
|
function applyFileProjectRoot(file2) {
|
|
21348
|
-
let dir =
|
|
21436
|
+
let dir = dirname4(resolve(file2));
|
|
21349
21437
|
while (true) {
|
|
21350
21438
|
if (basename2(dir) === ".auto") {
|
|
21351
|
-
return
|
|
21439
|
+
return dirname4(dir);
|
|
21352
21440
|
}
|
|
21353
|
-
const parent =
|
|
21441
|
+
const parent = dirname4(dir);
|
|
21354
21442
|
if (parent === dir) {
|
|
21355
21443
|
return process.cwd();
|
|
21356
21444
|
}
|
|
@@ -21384,7 +21472,7 @@ function isRecord(value) {
|
|
|
21384
21472
|
function resourceApplyFiles(directory, entries) {
|
|
21385
21473
|
const files = [];
|
|
21386
21474
|
for (const entry of entries) {
|
|
21387
|
-
const path2 =
|
|
21475
|
+
const path2 = join2(directory, entry.name);
|
|
21388
21476
|
if (entry.isDirectory()) {
|
|
21389
21477
|
files.push(
|
|
21390
21478
|
...resourceApplyFiles(path2, readdirSync(path2, { withFileTypes: true }))
|
|
@@ -21580,7 +21668,7 @@ var init_actions = __esm({
|
|
|
21580
21668
|
|
|
21581
21669
|
// src/lib/config/profiles.ts
|
|
21582
21670
|
import { readdirSync as readdirSync2 } from "fs";
|
|
21583
|
-
import { join as
|
|
21671
|
+
import { join as join3 } from "path";
|
|
21584
21672
|
function listProfiles(configPath = defaultConfigPath()) {
|
|
21585
21673
|
const dir = profilesDir(configPath);
|
|
21586
21674
|
let entries;
|
|
@@ -21591,8 +21679,12 @@ function listProfiles(configPath = defaultConfigPath()) {
|
|
|
21591
21679
|
throw err;
|
|
21592
21680
|
}
|
|
21593
21681
|
return entries.filter((entry) => entry.endsWith(".yaml")).sort().map((entry) => {
|
|
21594
|
-
const path2 =
|
|
21595
|
-
return {
|
|
21682
|
+
const path2 = join3(dir, entry);
|
|
21683
|
+
return {
|
|
21684
|
+
name: profileNameFromPath(path2),
|
|
21685
|
+
path: path2,
|
|
21686
|
+
config: readConfig(path2)
|
|
21687
|
+
};
|
|
21596
21688
|
}).filter((profile) => profile.config.userEmail);
|
|
21597
21689
|
}
|
|
21598
21690
|
function findAccountProfile(input) {
|
|
@@ -21631,6 +21723,9 @@ var init_pkce = __esm({
|
|
|
21631
21723
|
// src/commands/auth/login.ts
|
|
21632
21724
|
async function login(input) {
|
|
21633
21725
|
const style = input.style ?? plainStyle;
|
|
21726
|
+
if (input.options.profile !== void 0) {
|
|
21727
|
+
assertValidProfileName(input.options.profile);
|
|
21728
|
+
}
|
|
21634
21729
|
const serverUrl = resolveApiBaseUrl({ explicit: input.options.apiUrl });
|
|
21635
21730
|
if (input.options.device) {
|
|
21636
21731
|
const device = await postJson(
|
|
@@ -21674,6 +21769,7 @@ async function login(input) {
|
|
|
21674
21769
|
serverUrl,
|
|
21675
21770
|
fetch: input.fetch,
|
|
21676
21771
|
configPath: input.configPath,
|
|
21772
|
+
profileName: input.options.profile,
|
|
21677
21773
|
writeOutput: input.writeOutput,
|
|
21678
21774
|
style
|
|
21679
21775
|
});
|
|
@@ -21719,6 +21815,7 @@ async function login(input) {
|
|
|
21719
21815
|
serverUrl,
|
|
21720
21816
|
fetch: input.fetch,
|
|
21721
21817
|
configPath: input.configPath,
|
|
21818
|
+
profileName: input.options.profile,
|
|
21722
21819
|
writeOutput: input.writeOutput,
|
|
21723
21820
|
style
|
|
21724
21821
|
});
|
|
@@ -21739,6 +21836,7 @@ async function login(input) {
|
|
|
21739
21836
|
serverUrl,
|
|
21740
21837
|
fetch: input.fetch,
|
|
21741
21838
|
configPath: input.configPath,
|
|
21839
|
+
profileName: input.options.profile,
|
|
21742
21840
|
writeOutput: input.writeOutput,
|
|
21743
21841
|
style
|
|
21744
21842
|
});
|
|
@@ -21806,13 +21904,28 @@ async function finishLogin(input) {
|
|
|
21806
21904
|
);
|
|
21807
21905
|
}
|
|
21808
21906
|
}
|
|
21809
|
-
|
|
21907
|
+
persistLogin(config2, input);
|
|
21810
21908
|
input.writeOutput(
|
|
21811
21909
|
input.style.success(
|
|
21812
21910
|
token2.user ? `Logged in as ${token2.user.email}.` : "Logged in."
|
|
21813
21911
|
)
|
|
21814
21912
|
);
|
|
21815
21913
|
}
|
|
21914
|
+
function persistLogin(config2, input) {
|
|
21915
|
+
if (!input.token.user) {
|
|
21916
|
+
writeConfig(config2, input.configPath);
|
|
21917
|
+
return;
|
|
21918
|
+
}
|
|
21919
|
+
const pinned = input.configPath !== void 0 && isProfilePath(input.configPath);
|
|
21920
|
+
const name = saveProfile({
|
|
21921
|
+
config: config2,
|
|
21922
|
+
name: input.profileName ?? (pinned && input.configPath ? profileNameFromPath(input.configPath) : void 0),
|
|
21923
|
+
configPath: input.configPath
|
|
21924
|
+
});
|
|
21925
|
+
if (!pinned) {
|
|
21926
|
+
setCurrentProfile(name, input.configPath);
|
|
21927
|
+
}
|
|
21928
|
+
}
|
|
21816
21929
|
async function sleep(ms) {
|
|
21817
21930
|
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
21818
21931
|
}
|
|
@@ -21824,6 +21937,7 @@ var init_login = __esm({
|
|
|
21824
21937
|
init_tokens();
|
|
21825
21938
|
init_browser();
|
|
21826
21939
|
init_file();
|
|
21940
|
+
init_path();
|
|
21827
21941
|
init_profiles2();
|
|
21828
21942
|
init_loopback();
|
|
21829
21943
|
init_style();
|
|
@@ -21875,9 +21989,9 @@ var init_resources3 = __esm({
|
|
|
21875
21989
|
|
|
21876
21990
|
// src/commands/edit/actions.ts
|
|
21877
21991
|
import { spawn as spawn2 } from "child_process";
|
|
21878
|
-
import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
21992
|
+
import { mkdtempSync, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
21879
21993
|
import { tmpdir } from "os";
|
|
21880
|
-
import { join as
|
|
21994
|
+
import { join as join4 } from "path";
|
|
21881
21995
|
import { parseAllDocuments as parseYamlDocuments2, stringify as stringify2 } from "yaml";
|
|
21882
21996
|
async function editResource(input) {
|
|
21883
21997
|
const reference = parseProjectResourceReference(input.resource);
|
|
@@ -21892,8 +22006,8 @@ async function editResource(input) {
|
|
|
21892
22006
|
const document = editableResourceDocument(reference.kind, current);
|
|
21893
22007
|
const source = `${stringify2(document).trimEnd()}
|
|
21894
22008
|
`;
|
|
21895
|
-
const tempRoot = mkdtempSync(
|
|
21896
|
-
const filePath =
|
|
22009
|
+
const tempRoot = mkdtempSync(join4(tmpdir(), "auto-edit-"));
|
|
22010
|
+
const filePath = join4(tempRoot, `${reference.kind}-${reference.name}.yaml`);
|
|
21897
22011
|
writeFileSync3(filePath, source, "utf8");
|
|
21898
22012
|
let removeTempFile = false;
|
|
21899
22013
|
try {
|
|
@@ -21930,7 +22044,7 @@ async function editResource(input) {
|
|
|
21930
22044
|
Edited file retained at ${filePath}`);
|
|
21931
22045
|
} finally {
|
|
21932
22046
|
if (removeTempFile) {
|
|
21933
|
-
|
|
22047
|
+
rmSync2(tempRoot, { recursive: true, force: true });
|
|
21934
22048
|
}
|
|
21935
22049
|
}
|
|
21936
22050
|
}
|
|
@@ -26973,6 +27087,7 @@ async function launch(opts) {
|
|
|
26973
27087
|
const apiClient = createApiClient({
|
|
26974
27088
|
env: process.env,
|
|
26975
27089
|
fetch,
|
|
27090
|
+
configPath: opts.configPath,
|
|
26976
27091
|
writeError: (e) => process.stderr.write(`${e}
|
|
26977
27092
|
`)
|
|
26978
27093
|
});
|
|
@@ -27165,6 +27280,7 @@ var init_launcher = __esm({
|
|
|
27165
27280
|
});
|
|
27166
27281
|
|
|
27167
27282
|
// src/cli/program.ts
|
|
27283
|
+
import { existsSync as existsSync4 } from "fs";
|
|
27168
27284
|
import { Command, Option as Option3 } from "commander";
|
|
27169
27285
|
|
|
27170
27286
|
// src/commands/agent-bridge/git-credentials.ts
|
|
@@ -29136,7 +29252,9 @@ init_base_url();
|
|
|
29136
29252
|
init_login();
|
|
29137
29253
|
|
|
29138
29254
|
// src/commands/auth/profile.ts
|
|
29255
|
+
import { existsSync as existsSync2, rmSync } from "fs";
|
|
29139
29256
|
init_file();
|
|
29257
|
+
init_path();
|
|
29140
29258
|
init_profiles2();
|
|
29141
29259
|
async function handleAuthStatus(context) {
|
|
29142
29260
|
const result = await fetchAuthStatus(context);
|
|
@@ -29162,43 +29280,58 @@ function logout(context) {
|
|
|
29162
29280
|
);
|
|
29163
29281
|
context.writeOutput(context.io.style.success("Logged out."));
|
|
29164
29282
|
}
|
|
29165
|
-
function
|
|
29283
|
+
function listAccounts(context) {
|
|
29166
29284
|
const profiles = listProfiles(context.configPath);
|
|
29167
29285
|
if (profiles.length === 0) {
|
|
29168
29286
|
throw new Error("No stored accounts. Run `auto auth login` first.");
|
|
29169
29287
|
}
|
|
29170
|
-
const
|
|
29171
|
-
|
|
29172
|
-
|
|
29173
|
-
|
|
29174
|
-
|
|
29175
|
-
|
|
29176
|
-
|
|
29288
|
+
for (const profile of profiles) {
|
|
29289
|
+
context.writeOutput(
|
|
29290
|
+
accountLine(profile, activeProfileName(context), context.io.style)
|
|
29291
|
+
);
|
|
29292
|
+
}
|
|
29293
|
+
}
|
|
29294
|
+
function switchAccount(context, accountRef, options = {}) {
|
|
29295
|
+
if (!accountRef) {
|
|
29296
|
+
listAccounts(context);
|
|
29177
29297
|
return;
|
|
29178
29298
|
}
|
|
29179
|
-
|
|
29180
|
-
|
|
29299
|
+
if (context.pinnedProfile) {
|
|
29300
|
+
throw new Error(
|
|
29301
|
+
"Cannot switch the active account while pinned to a profile via --profile or AUTO_PROFILE."
|
|
29302
|
+
);
|
|
29303
|
+
}
|
|
29304
|
+
const profiles = listProfiles(context.configPath);
|
|
29305
|
+
if (profiles.length === 0) {
|
|
29306
|
+
throw new Error("No stored accounts. Run `auto auth login` first.");
|
|
29307
|
+
}
|
|
29308
|
+
const byName = profiles.find((profile) => profile.name === accountRef);
|
|
29309
|
+
const matches = byName ? [byName] : profiles.filter(
|
|
29310
|
+
(profile) => profile.config.userEmail?.toLowerCase() === accountRef.toLowerCase() && (!options.server || profile.config.serverUrl === options.server)
|
|
29181
29311
|
);
|
|
29182
29312
|
if (matches.length === 0) {
|
|
29183
29313
|
throw new Error(
|
|
29184
|
-
`No stored account for ${
|
|
29314
|
+
`No stored account for ${accountRef}. Run \`auto auth login\` to add it.`
|
|
29185
29315
|
);
|
|
29186
29316
|
}
|
|
29187
|
-
const
|
|
29317
|
+
const active = readConfig(context.configPath);
|
|
29318
|
+
const match = matches.length === 1 ? matches[0] : matches.find(
|
|
29319
|
+
(profile) => profile.config.serverUrl === active.serverUrl
|
|
29320
|
+
);
|
|
29188
29321
|
if (!match) {
|
|
29189
29322
|
throw new Error(
|
|
29190
|
-
`Multiple servers have a stored account for ${
|
|
29323
|
+
`Multiple servers have a stored account for ${accountRef}: ${matches.map((profile) => profile.config.serverUrl).join(
|
|
29191
29324
|
", "
|
|
29192
|
-
)}. Pick one with \`auto auth switch ${
|
|
29325
|
+
)}. Pick one with \`auto auth switch ${accountRef} --server <url>\`.`
|
|
29193
29326
|
);
|
|
29194
29327
|
}
|
|
29195
|
-
|
|
29328
|
+
setCurrentProfile(match.name, context.configPath);
|
|
29196
29329
|
context.writeOutput(
|
|
29197
29330
|
context.io.style.success(
|
|
29198
|
-
`Switched to ${match.userEmail} (${match.serverUrl}).`
|
|
29331
|
+
`Switched to ${match.config.userEmail} (${match.config.serverUrl}).`
|
|
29199
29332
|
)
|
|
29200
29333
|
);
|
|
29201
|
-
if (!match.refreshToken) {
|
|
29334
|
+
if (!match.config.refreshToken) {
|
|
29202
29335
|
context.writeOutput(
|
|
29203
29336
|
context.io.style.warn(
|
|
29204
29337
|
"This account has no stored credentials; run `auto auth login`."
|
|
@@ -29206,13 +29339,43 @@ function switchAccount(context, accountEmail, options = {}) {
|
|
|
29206
29339
|
);
|
|
29207
29340
|
}
|
|
29208
29341
|
}
|
|
29209
|
-
function
|
|
29210
|
-
|
|
29342
|
+
function removeProfile(context, name) {
|
|
29343
|
+
assertValidProfileName(name);
|
|
29344
|
+
if (context.pinnedProfile === name) {
|
|
29345
|
+
throw new Error(`Cannot remove profile "${name}" while pinned to it.`);
|
|
29346
|
+
}
|
|
29347
|
+
const configPath = context.configPath ?? defaultConfigPath();
|
|
29348
|
+
const path2 = profileFilePath(configPath, name);
|
|
29349
|
+
if (!existsSync2(path2)) {
|
|
29350
|
+
const names = listProfiles(context.configPath).map(
|
|
29351
|
+
(profile) => profile.name
|
|
29352
|
+
);
|
|
29353
|
+
throw new Error(
|
|
29354
|
+
`No stored profile "${name}". Stored profiles: ${names.join(", ") || "(none)"}.`
|
|
29355
|
+
);
|
|
29356
|
+
}
|
|
29357
|
+
if (currentProfileName(context.configPath) === name) {
|
|
29358
|
+
clearCurrentProfile(context.configPath);
|
|
29359
|
+
}
|
|
29360
|
+
rmSync(path2);
|
|
29361
|
+
context.writeOutput(context.io.style.success(`Removed profile "${name}".`));
|
|
29362
|
+
}
|
|
29363
|
+
function activeProfileName(context) {
|
|
29364
|
+
const name = currentProfileName(context.configPath);
|
|
29365
|
+
if (name) return name;
|
|
29366
|
+
const active = readConfig(context.configPath);
|
|
29367
|
+
if (!active.userEmail) return void 0;
|
|
29368
|
+
return listProfiles(context.configPath).find(
|
|
29369
|
+
(profile) => profile.config.userEmail === active.userEmail && profile.config.serverUrl === active.serverUrl
|
|
29370
|
+
)?.name;
|
|
29371
|
+
}
|
|
29372
|
+
function accountLine(profile, activeName, style) {
|
|
29211
29373
|
return [
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29215
|
-
|
|
29374
|
+
profile.name,
|
|
29375
|
+
profile.config.userEmail,
|
|
29376
|
+
style.dim(`server=${profile.config.serverUrl ?? "(unset)"}`),
|
|
29377
|
+
profile.config.refreshToken ? void 0 : style.warn("logged_out"),
|
|
29378
|
+
profile.name === activeName ? style.success("(active)") : void 0
|
|
29216
29379
|
].filter(Boolean).join(" ");
|
|
29217
29380
|
}
|
|
29218
29381
|
async function fetchAuthStatus(context) {
|
|
@@ -29378,6 +29541,7 @@ function registerAuthCommands(program, context) {
|
|
|
29378
29541
|
await login({
|
|
29379
29542
|
options: {
|
|
29380
29543
|
...options,
|
|
29544
|
+
profile: globalOptions.profile,
|
|
29381
29545
|
apiUrl: resolveApiBaseUrl({
|
|
29382
29546
|
explicit: [options.apiUrl, globalOptions.apiUrl],
|
|
29383
29547
|
env: context.env
|
|
@@ -29400,11 +29564,13 @@ function registerAuthCommands(program, context) {
|
|
|
29400
29564
|
await handleWhoami(context);
|
|
29401
29565
|
});
|
|
29402
29566
|
auth.command("logout").description("Remove the local user refresh token.").action(() => logout(context));
|
|
29567
|
+
auth.command("list").description("List stored account profiles.").action(() => listAccounts(context));
|
|
29403
29568
|
auth.command("switch").description(
|
|
29404
29569
|
"Switch the active account to a stored profile, or list stored accounts."
|
|
29405
|
-
).argument("[
|
|
29406
|
-
(
|
|
29570
|
+
).argument("[account]", "Profile name or email of a stored account").option("--server <url>", "Auto web server URL of the stored account").action(
|
|
29571
|
+
(account, options) => switchAccount(context, account, options)
|
|
29407
29572
|
);
|
|
29573
|
+
auth.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
|
|
29408
29574
|
}
|
|
29409
29575
|
|
|
29410
29576
|
// src/lib/stdio/readline.ts
|
|
@@ -32205,9 +32371,9 @@ function withApiBaseUrl2(context, commandOptions) {
|
|
|
32205
32371
|
// src/commands/sessions/connect.ts
|
|
32206
32372
|
init_resources2();
|
|
32207
32373
|
init_browser();
|
|
32208
|
-
import { existsSync as
|
|
32374
|
+
import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
32209
32375
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
32210
|
-
import { join as
|
|
32376
|
+
import { join as join5 } from "path";
|
|
32211
32377
|
var POLL_INTERVAL_MS2 = 2e3;
|
|
32212
32378
|
var POLL_TIMEOUT_MS2 = 5 * 6e4;
|
|
32213
32379
|
var SLACK_APPS_URL = "https://api.slack.com/apps";
|
|
@@ -32378,7 +32544,7 @@ async function promptForIconUploads(input, options) {
|
|
|
32378
32544
|
}
|
|
32379
32545
|
}
|
|
32380
32546
|
function stagedLocationLabel(stagedPath) {
|
|
32381
|
-
return stagedPath.startsWith(`${
|
|
32547
|
+
return stagedPath.startsWith(`${join5(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
|
|
32382
32548
|
}
|
|
32383
32549
|
async function stageAvatarImage(input) {
|
|
32384
32550
|
try {
|
|
@@ -32390,9 +32556,9 @@ async function stageAvatarImage(input) {
|
|
|
32390
32556
|
}
|
|
32391
32557
|
const contentType = response.headers.get("content-type") ?? "";
|
|
32392
32558
|
const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
|
|
32393
|
-
const downloads =
|
|
32394
|
-
const directory =
|
|
32395
|
-
const path2 =
|
|
32559
|
+
const downloads = join5(homedir3(), "Downloads");
|
|
32560
|
+
const directory = existsSync3(downloads) ? downloads : mkdtempSync2(join5(tmpdir2(), "auto-avatar-"));
|
|
32561
|
+
const path2 = join5(directory, `${input.session}-avatar${extension}`);
|
|
32396
32562
|
writeFileSync4(path2, Buffer.from(await response.arrayBuffer()));
|
|
32397
32563
|
return path2;
|
|
32398
32564
|
} catch {
|
|
@@ -32535,6 +32701,10 @@ function registerToolCommands(program, context) {
|
|
|
32535
32701
|
});
|
|
32536
32702
|
}
|
|
32537
32703
|
|
|
32704
|
+
// src/cli/program.ts
|
|
32705
|
+
init_path();
|
|
32706
|
+
init_profiles2();
|
|
32707
|
+
|
|
32538
32708
|
// src/lib/output/iostreams.ts
|
|
32539
32709
|
init_style();
|
|
32540
32710
|
function createIOStreams(flags) {
|
|
@@ -32628,9 +32798,12 @@ function createProgram(options = {}) {
|
|
|
32628
32798
|
"stream-json",
|
|
32629
32799
|
"tui"
|
|
32630
32800
|
])
|
|
32631
|
-
).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL")
|
|
32801
|
+
).option("--no-color", "disable color output").option("--api-url <url>", "override API base URL").option(
|
|
32802
|
+
"--profile <name>",
|
|
32803
|
+
"use this stored profile for the invocation (or set AUTO_PROFILE); for `auth login`, the profile name to store the login under"
|
|
32804
|
+
);
|
|
32632
32805
|
program.configureHelp({ showGlobalOptions: true });
|
|
32633
|
-
program.hook("preAction", () => {
|
|
32806
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
32634
32807
|
if (!options.io) {
|
|
32635
32808
|
const g = program.opts();
|
|
32636
32809
|
context.io = createIOStreams({
|
|
@@ -32640,15 +32813,34 @@ function createProgram(options = {}) {
|
|
|
32640
32813
|
writeError: options.writeError
|
|
32641
32814
|
});
|
|
32642
32815
|
}
|
|
32816
|
+
const isLogin = actionCommand.name() === "login" && actionCommand.parent?.name() === "auth";
|
|
32817
|
+
const flagPin = isLogin ? void 0 : program.opts().profile;
|
|
32818
|
+
const pin = flagPin ?? context.env.AUTO_PROFILE;
|
|
32819
|
+
if (pin && context.pinnedProfile !== pin) {
|
|
32820
|
+
assertValidProfileName(pin);
|
|
32821
|
+
const base = options.configPath ?? defaultConfigPath();
|
|
32822
|
+
const pinnedPath = profileFilePath(base, pin);
|
|
32823
|
+
if (!existsSync4(pinnedPath)) {
|
|
32824
|
+
const names = listProfiles(base).map((profile) => profile.name);
|
|
32825
|
+
throw new Error(
|
|
32826
|
+
`No stored profile "${pin}". Stored profiles: ${names.join(", ") || "(none)"}.`
|
|
32827
|
+
);
|
|
32828
|
+
}
|
|
32829
|
+
context.configPath = pinnedPath;
|
|
32830
|
+
context.pinnedProfile = pin;
|
|
32831
|
+
}
|
|
32643
32832
|
});
|
|
32644
32833
|
const launchTui = async () => {
|
|
32645
32834
|
const g = program.opts();
|
|
32646
32835
|
if (options.launchTui) {
|
|
32647
|
-
await options.launchTui({
|
|
32836
|
+
await options.launchTui({
|
|
32837
|
+
apiUrl: g.apiUrl,
|
|
32838
|
+
configPath: context.configPath
|
|
32839
|
+
});
|
|
32648
32840
|
return;
|
|
32649
32841
|
}
|
|
32650
32842
|
const { launch: launch2 } = await Promise.resolve().then(() => (init_launcher(), launcher_exports));
|
|
32651
|
-
await launch2({ apiUrl: g.apiUrl });
|
|
32843
|
+
await launch2({ apiUrl: g.apiUrl, configPath: context.configPath });
|
|
32652
32844
|
process.exit(0);
|
|
32653
32845
|
};
|
|
32654
32846
|
program.action(async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autohq/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.108",
|
|
4
4
|
"license": "SEE LICENSE IN README.md",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dev:tui": "node ../../scripts/cli-tui-dev-watch.mjs",
|
|
25
25
|
"skill:generate": "tsx scripts/generate-skill-content.ts",
|
|
26
26
|
"test": "npm run test:unit",
|
|
27
|
+
"test:mutation": "stryker run",
|
|
27
28
|
"test:unit": "tsx --test \"test/**/*.unit.test.ts\"",
|
|
28
29
|
"typecheck": "tsc --noEmit"
|
|
29
30
|
},
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@auto/protocol": "*",
|
|
46
47
|
"@auto/schemas": "*",
|
|
48
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
47
49
|
"@types/react": "^19",
|
|
48
50
|
"tsup": "^8.5.1"
|
|
49
51
|
}
|