@autohq/cli 0.1.106 → 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 +5 -2
- package/dist/index.js +278 -85
- 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
|
@@ -20843,7 +20843,8 @@ var SecretAesGcmAuthTagSchema = external_exports.string().trim().regex(/^[A-Za-z
|
|
|
20843
20843
|
var SecretEnvValueSchema = external_exports.union([
|
|
20844
20844
|
external_exports.string(),
|
|
20845
20845
|
external_exports.object({
|
|
20846
|
-
$secret: ResourceNameSchema
|
|
20846
|
+
$secret: ResourceNameSchema,
|
|
20847
|
+
optional: external_exports.boolean().optional()
|
|
20847
20848
|
})
|
|
20848
20849
|
]);
|
|
20849
20850
|
var SecretEnvSchema = external_exports.record(
|
|
@@ -26205,7 +26206,7 @@ Object.assign(lookup, {
|
|
|
26205
26206
|
// package.json
|
|
26206
26207
|
var package_default = {
|
|
26207
26208
|
name: "@autohq/cli",
|
|
26208
|
-
version: "0.1.
|
|
26209
|
+
version: "0.1.108",
|
|
26209
26210
|
license: "SEE LICENSE IN README.md",
|
|
26210
26211
|
publishConfig: {
|
|
26211
26212
|
access: "public"
|
|
@@ -26229,6 +26230,7 @@ var package_default = {
|
|
|
26229
26230
|
"dev:tui": "node ../../scripts/cli-tui-dev-watch.mjs",
|
|
26230
26231
|
"skill:generate": "tsx scripts/generate-skill-content.ts",
|
|
26231
26232
|
test: "npm run test:unit",
|
|
26233
|
+
"test:mutation": "stryker run",
|
|
26232
26234
|
"test:unit": 'tsx --test "test/**/*.unit.test.ts"',
|
|
26233
26235
|
typecheck: "tsc --noEmit"
|
|
26234
26236
|
},
|
|
@@ -26249,6 +26251,7 @@ var package_default = {
|
|
|
26249
26251
|
devDependencies: {
|
|
26250
26252
|
"@auto/protocol": "*",
|
|
26251
26253
|
"@auto/schemas": "*",
|
|
26254
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
26252
26255
|
"@types/react": "^19",
|
|
26253
26256
|
tsup: "^8.5.1"
|
|
26254
26257
|
}
|
package/dist/index.js
CHANGED
|
@@ -16531,7 +16531,8 @@ var init_secrets = __esm({
|
|
|
16531
16531
|
SecretEnvValueSchema = external_exports.union([
|
|
16532
16532
|
external_exports.string(),
|
|
16533
16533
|
external_exports.object({
|
|
16534
|
-
$secret: ResourceNameSchema
|
|
16534
|
+
$secret: ResourceNameSchema,
|
|
16535
|
+
optional: external_exports.boolean().optional()
|
|
16535
16536
|
})
|
|
16536
16537
|
]);
|
|
16537
16538
|
SecretEnvSchema = external_exports.record(
|
|
@@ -18511,7 +18512,7 @@ var init_package = __esm({
|
|
|
18511
18512
|
"package.json"() {
|
|
18512
18513
|
package_default = {
|
|
18513
18514
|
name: "@autohq/cli",
|
|
18514
|
-
version: "0.1.
|
|
18515
|
+
version: "0.1.108",
|
|
18515
18516
|
license: "SEE LICENSE IN README.md",
|
|
18516
18517
|
publishConfig: {
|
|
18517
18518
|
access: "public"
|
|
@@ -18535,6 +18536,7 @@ var init_package = __esm({
|
|
|
18535
18536
|
"dev:tui": "node ../../scripts/cli-tui-dev-watch.mjs",
|
|
18536
18537
|
"skill:generate": "tsx scripts/generate-skill-content.ts",
|
|
18537
18538
|
test: "npm run test:unit",
|
|
18539
|
+
"test:mutation": "stryker run",
|
|
18538
18540
|
"test:unit": 'tsx --test "test/**/*.unit.test.ts"',
|
|
18539
18541
|
typecheck: "tsc --noEmit"
|
|
18540
18542
|
},
|
|
@@ -18555,6 +18557,7 @@ var init_package = __esm({
|
|
|
18555
18557
|
devDependencies: {
|
|
18556
18558
|
"@auto/protocol": "*",
|
|
18557
18559
|
"@auto/schemas": "*",
|
|
18560
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
18558
18561
|
"@types/react": "^19",
|
|
18559
18562
|
tsup: "^8.5.1"
|
|
18560
18563
|
}
|
|
@@ -18597,18 +18600,36 @@ var init_tokens = __esm({
|
|
|
18597
18600
|
// src/lib/config/path.ts
|
|
18598
18601
|
import { createHash } from "crypto";
|
|
18599
18602
|
import { homedir as homedir2 } from "os";
|
|
18600
|
-
import { join, normalize } from "path";
|
|
18603
|
+
import { basename, dirname as dirname2, join, normalize } from "path";
|
|
18601
18604
|
function defaultConfigPath() {
|
|
18602
18605
|
return process.env.AUTO_CLI_CONFIG ?? join(homedir2(), ".auto", "config.yaml");
|
|
18603
18606
|
}
|
|
18607
|
+
function isProfilePath(path2) {
|
|
18608
|
+
return basename(dirname2(path2)) === PROFILES_DIR_NAME;
|
|
18609
|
+
}
|
|
18604
18610
|
function profilesDir(configPath = defaultConfigPath()) {
|
|
18611
|
+
if (isProfilePath(configPath)) return dirname2(configPath);
|
|
18605
18612
|
return normalize(join(configPath, "..", PROFILES_DIR_NAME));
|
|
18606
18613
|
}
|
|
18607
|
-
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) {
|
|
18608
18629
|
const key = `${input.userEmail.toLowerCase()}
|
|
18609
18630
|
${serverHost(input.serverUrl)}`;
|
|
18610
18631
|
const hash2 = createHash("sha256").update(key).digest("hex").slice(0, 8);
|
|
18611
|
-
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}
|
|
18632
|
+
return `${slug(input.userEmail)}--${slug(serverHost(input.serverUrl))}-${hash2}`;
|
|
18612
18633
|
}
|
|
18613
18634
|
function serverHost(serverUrl) {
|
|
18614
18635
|
try {
|
|
@@ -18620,62 +18641,129 @@ function serverHost(serverUrl) {
|
|
|
18620
18641
|
function slug(value) {
|
|
18621
18642
|
return value.toLowerCase().replace(/[^a-z0-9._-]+/g, "_");
|
|
18622
18643
|
}
|
|
18623
|
-
var PROFILES_DIR_NAME;
|
|
18644
|
+
var PROFILES_DIR_NAME, PROFILE_NAME_PATTERN;
|
|
18624
18645
|
var init_path = __esm({
|
|
18625
18646
|
"src/lib/config/path.ts"() {
|
|
18626
18647
|
"use strict";
|
|
18627
18648
|
PROFILES_DIR_NAME = "profiles";
|
|
18649
|
+
PROFILE_NAME_PATTERN = /^[a-z0-9._-]+$/;
|
|
18628
18650
|
}
|
|
18629
18651
|
});
|
|
18630
18652
|
|
|
18631
18653
|
// src/lib/config/file.ts
|
|
18632
18654
|
import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
18633
|
-
import {
|
|
18655
|
+
import { dirname as dirname3 } from "path";
|
|
18634
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) {
|
|
18635
18732
|
try {
|
|
18636
18733
|
const text = readFileSync2(path2, "utf8");
|
|
18637
18734
|
const config2 = {};
|
|
18735
|
+
let currentProfile;
|
|
18638
18736
|
for (const line of text.split(/\r?\n/)) {
|
|
18639
18737
|
const match = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line.trim());
|
|
18640
18738
|
if (!match) continue;
|
|
18739
|
+
if (match[1] === CURRENT_PROFILE_KEY) {
|
|
18740
|
+
currentProfile = match[2] || void 0;
|
|
18741
|
+
continue;
|
|
18742
|
+
}
|
|
18641
18743
|
const key = CONFIG_KEYS.find((candidate) => candidate === match[1]);
|
|
18642
18744
|
if (key) config2[key] = match[2] ?? "";
|
|
18643
18745
|
}
|
|
18644
|
-
return config2;
|
|
18746
|
+
return { config: config2, currentProfile };
|
|
18645
18747
|
} catch (err) {
|
|
18646
|
-
if (err.code === "ENOENT")
|
|
18748
|
+
if (err.code === "ENOENT") {
|
|
18749
|
+
return { config: {} };
|
|
18750
|
+
}
|
|
18647
18751
|
throw err;
|
|
18648
18752
|
}
|
|
18649
18753
|
}
|
|
18650
|
-
function writeConfig(config2, path2 = defaultConfigPath()) {
|
|
18651
|
-
writeConfigFile(config2, path2);
|
|
18652
|
-
if (config2.userEmail && config2.serverUrl && basename(dirname2(path2)) !== PROFILES_DIR_NAME) {
|
|
18653
|
-
writeConfigFile(
|
|
18654
|
-
config2,
|
|
18655
|
-
join2(
|
|
18656
|
-
dirname2(path2),
|
|
18657
|
-
PROFILES_DIR_NAME,
|
|
18658
|
-
profileFileName({
|
|
18659
|
-
userEmail: config2.userEmail,
|
|
18660
|
-
serverUrl: config2.serverUrl
|
|
18661
|
-
})
|
|
18662
|
-
)
|
|
18663
|
-
);
|
|
18664
|
-
}
|
|
18665
|
-
}
|
|
18666
18754
|
function writeConfigFile(config2, path2) {
|
|
18667
|
-
mkdirSync2(dirname2(path2), { recursive: true });
|
|
18668
18755
|
const lines = CONFIG_KEYS.filter((key) => config2[key]).map(
|
|
18669
18756
|
(key) => `${key}: ${config2[key]}`
|
|
18670
18757
|
);
|
|
18671
|
-
|
|
18672
|
-
`,
|
|
18673
|
-
|
|
18674
|
-
|
|
18675
|
-
});
|
|
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 });
|
|
18676
18764
|
chmodSync(path2, 384);
|
|
18677
18765
|
}
|
|
18678
|
-
var CONFIG_KEYS;
|
|
18766
|
+
var CONFIG_KEYS, CURRENT_PROFILE_KEY;
|
|
18679
18767
|
var init_file = __esm({
|
|
18680
18768
|
"src/lib/config/file.ts"() {
|
|
18681
18769
|
"use strict";
|
|
@@ -18692,6 +18780,7 @@ var init_file = __esm({
|
|
|
18692
18780
|
"accessTokenOrganizationId",
|
|
18693
18781
|
"accessTokenProjectId"
|
|
18694
18782
|
];
|
|
18783
|
+
CURRENT_PROFILE_KEY = "currentProfile";
|
|
18695
18784
|
}
|
|
18696
18785
|
});
|
|
18697
18786
|
|
|
@@ -21115,10 +21204,10 @@ import {
|
|
|
21115
21204
|
} from "fs";
|
|
21116
21205
|
import {
|
|
21117
21206
|
basename as basename2,
|
|
21118
|
-
dirname as
|
|
21207
|
+
dirname as dirname4,
|
|
21119
21208
|
extname,
|
|
21120
21209
|
isAbsolute,
|
|
21121
|
-
join as
|
|
21210
|
+
join as join2,
|
|
21122
21211
|
resolve
|
|
21123
21212
|
} from "path";
|
|
21124
21213
|
import { parseAllDocuments as parseYamlDocuments } from "yaml";
|
|
@@ -21134,7 +21223,7 @@ function readProjectApplyRequest(options) {
|
|
|
21134
21223
|
);
|
|
21135
21224
|
return { ...request, assets: assets2 };
|
|
21136
21225
|
}
|
|
21137
|
-
const directory = options.directory ??
|
|
21226
|
+
const directory = options.directory ?? join2(process.cwd(), ".auto");
|
|
21138
21227
|
const files = applyFiles(directory);
|
|
21139
21228
|
if (files.length === 0) {
|
|
21140
21229
|
throw new Error(`No resource files found in ${directory}`);
|
|
@@ -21197,7 +21286,7 @@ function applyFiles(root) {
|
|
|
21197
21286
|
const files = [];
|
|
21198
21287
|
for (const kind of APPLY_RESOURCE_ORDER) {
|
|
21199
21288
|
const directory = APPLY_DIRECTORIES[kind];
|
|
21200
|
-
const path2 =
|
|
21289
|
+
const path2 = join2(root, directory);
|
|
21201
21290
|
let entries;
|
|
21202
21291
|
try {
|
|
21203
21292
|
entries = readdirSync(path2, { withFileTypes: true });
|
|
@@ -21341,15 +21430,15 @@ function validateSessionAvatarAsset(input) {
|
|
|
21341
21430
|
}
|
|
21342
21431
|
function applyProjectRoot(directory) {
|
|
21343
21432
|
const resolved = resolve(directory);
|
|
21344
|
-
return basename2(resolved) === ".auto" ?
|
|
21433
|
+
return basename2(resolved) === ".auto" ? dirname4(resolved) : resolved;
|
|
21345
21434
|
}
|
|
21346
21435
|
function applyFileProjectRoot(file2) {
|
|
21347
|
-
let dir =
|
|
21436
|
+
let dir = dirname4(resolve(file2));
|
|
21348
21437
|
while (true) {
|
|
21349
21438
|
if (basename2(dir) === ".auto") {
|
|
21350
|
-
return
|
|
21439
|
+
return dirname4(dir);
|
|
21351
21440
|
}
|
|
21352
|
-
const parent =
|
|
21441
|
+
const parent = dirname4(dir);
|
|
21353
21442
|
if (parent === dir) {
|
|
21354
21443
|
return process.cwd();
|
|
21355
21444
|
}
|
|
@@ -21383,7 +21472,7 @@ function isRecord(value) {
|
|
|
21383
21472
|
function resourceApplyFiles(directory, entries) {
|
|
21384
21473
|
const files = [];
|
|
21385
21474
|
for (const entry of entries) {
|
|
21386
|
-
const path2 =
|
|
21475
|
+
const path2 = join2(directory, entry.name);
|
|
21387
21476
|
if (entry.isDirectory()) {
|
|
21388
21477
|
files.push(
|
|
21389
21478
|
...resourceApplyFiles(path2, readdirSync(path2, { withFileTypes: true }))
|
|
@@ -21579,7 +21668,7 @@ var init_actions = __esm({
|
|
|
21579
21668
|
|
|
21580
21669
|
// src/lib/config/profiles.ts
|
|
21581
21670
|
import { readdirSync as readdirSync2 } from "fs";
|
|
21582
|
-
import { join as
|
|
21671
|
+
import { join as join3 } from "path";
|
|
21583
21672
|
function listProfiles(configPath = defaultConfigPath()) {
|
|
21584
21673
|
const dir = profilesDir(configPath);
|
|
21585
21674
|
let entries;
|
|
@@ -21590,8 +21679,12 @@ function listProfiles(configPath = defaultConfigPath()) {
|
|
|
21590
21679
|
throw err;
|
|
21591
21680
|
}
|
|
21592
21681
|
return entries.filter((entry) => entry.endsWith(".yaml")).sort().map((entry) => {
|
|
21593
|
-
const path2 =
|
|
21594
|
-
return {
|
|
21682
|
+
const path2 = join3(dir, entry);
|
|
21683
|
+
return {
|
|
21684
|
+
name: profileNameFromPath(path2),
|
|
21685
|
+
path: path2,
|
|
21686
|
+
config: readConfig(path2)
|
|
21687
|
+
};
|
|
21595
21688
|
}).filter((profile) => profile.config.userEmail);
|
|
21596
21689
|
}
|
|
21597
21690
|
function findAccountProfile(input) {
|
|
@@ -21630,6 +21723,9 @@ var init_pkce = __esm({
|
|
|
21630
21723
|
// src/commands/auth/login.ts
|
|
21631
21724
|
async function login(input) {
|
|
21632
21725
|
const style = input.style ?? plainStyle;
|
|
21726
|
+
if (input.options.profile !== void 0) {
|
|
21727
|
+
assertValidProfileName(input.options.profile);
|
|
21728
|
+
}
|
|
21633
21729
|
const serverUrl = resolveApiBaseUrl({ explicit: input.options.apiUrl });
|
|
21634
21730
|
if (input.options.device) {
|
|
21635
21731
|
const device = await postJson(
|
|
@@ -21673,6 +21769,7 @@ async function login(input) {
|
|
|
21673
21769
|
serverUrl,
|
|
21674
21770
|
fetch: input.fetch,
|
|
21675
21771
|
configPath: input.configPath,
|
|
21772
|
+
profileName: input.options.profile,
|
|
21676
21773
|
writeOutput: input.writeOutput,
|
|
21677
21774
|
style
|
|
21678
21775
|
});
|
|
@@ -21718,6 +21815,7 @@ async function login(input) {
|
|
|
21718
21815
|
serverUrl,
|
|
21719
21816
|
fetch: input.fetch,
|
|
21720
21817
|
configPath: input.configPath,
|
|
21818
|
+
profileName: input.options.profile,
|
|
21721
21819
|
writeOutput: input.writeOutput,
|
|
21722
21820
|
style
|
|
21723
21821
|
});
|
|
@@ -21738,6 +21836,7 @@ async function login(input) {
|
|
|
21738
21836
|
serverUrl,
|
|
21739
21837
|
fetch: input.fetch,
|
|
21740
21838
|
configPath: input.configPath,
|
|
21839
|
+
profileName: input.options.profile,
|
|
21741
21840
|
writeOutput: input.writeOutput,
|
|
21742
21841
|
style
|
|
21743
21842
|
});
|
|
@@ -21805,13 +21904,28 @@ async function finishLogin(input) {
|
|
|
21805
21904
|
);
|
|
21806
21905
|
}
|
|
21807
21906
|
}
|
|
21808
|
-
|
|
21907
|
+
persistLogin(config2, input);
|
|
21809
21908
|
input.writeOutput(
|
|
21810
21909
|
input.style.success(
|
|
21811
21910
|
token2.user ? `Logged in as ${token2.user.email}.` : "Logged in."
|
|
21812
21911
|
)
|
|
21813
21912
|
);
|
|
21814
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
|
+
}
|
|
21815
21929
|
async function sleep(ms) {
|
|
21816
21930
|
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
21817
21931
|
}
|
|
@@ -21823,6 +21937,7 @@ var init_login = __esm({
|
|
|
21823
21937
|
init_tokens();
|
|
21824
21938
|
init_browser();
|
|
21825
21939
|
init_file();
|
|
21940
|
+
init_path();
|
|
21826
21941
|
init_profiles2();
|
|
21827
21942
|
init_loopback();
|
|
21828
21943
|
init_style();
|
|
@@ -21874,9 +21989,9 @@ var init_resources3 = __esm({
|
|
|
21874
21989
|
|
|
21875
21990
|
// src/commands/edit/actions.ts
|
|
21876
21991
|
import { spawn as spawn2 } from "child_process";
|
|
21877
|
-
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";
|
|
21878
21993
|
import { tmpdir } from "os";
|
|
21879
|
-
import { join as
|
|
21994
|
+
import { join as join4 } from "path";
|
|
21880
21995
|
import { parseAllDocuments as parseYamlDocuments2, stringify as stringify2 } from "yaml";
|
|
21881
21996
|
async function editResource(input) {
|
|
21882
21997
|
const reference = parseProjectResourceReference(input.resource);
|
|
@@ -21891,8 +22006,8 @@ async function editResource(input) {
|
|
|
21891
22006
|
const document = editableResourceDocument(reference.kind, current);
|
|
21892
22007
|
const source = `${stringify2(document).trimEnd()}
|
|
21893
22008
|
`;
|
|
21894
|
-
const tempRoot = mkdtempSync(
|
|
21895
|
-
const filePath =
|
|
22009
|
+
const tempRoot = mkdtempSync(join4(tmpdir(), "auto-edit-"));
|
|
22010
|
+
const filePath = join4(tempRoot, `${reference.kind}-${reference.name}.yaml`);
|
|
21896
22011
|
writeFileSync3(filePath, source, "utf8");
|
|
21897
22012
|
let removeTempFile = false;
|
|
21898
22013
|
try {
|
|
@@ -21929,7 +22044,7 @@ async function editResource(input) {
|
|
|
21929
22044
|
Edited file retained at ${filePath}`);
|
|
21930
22045
|
} finally {
|
|
21931
22046
|
if (removeTempFile) {
|
|
21932
|
-
|
|
22047
|
+
rmSync2(tempRoot, { recursive: true, force: true });
|
|
21933
22048
|
}
|
|
21934
22049
|
}
|
|
21935
22050
|
}
|
|
@@ -26972,6 +27087,7 @@ async function launch(opts) {
|
|
|
26972
27087
|
const apiClient = createApiClient({
|
|
26973
27088
|
env: process.env,
|
|
26974
27089
|
fetch,
|
|
27090
|
+
configPath: opts.configPath,
|
|
26975
27091
|
writeError: (e) => process.stderr.write(`${e}
|
|
26976
27092
|
`)
|
|
26977
27093
|
});
|
|
@@ -27164,6 +27280,7 @@ var init_launcher = __esm({
|
|
|
27164
27280
|
});
|
|
27165
27281
|
|
|
27166
27282
|
// src/cli/program.ts
|
|
27283
|
+
import { existsSync as existsSync4 } from "fs";
|
|
27167
27284
|
import { Command, Option as Option3 } from "commander";
|
|
27168
27285
|
|
|
27169
27286
|
// src/commands/agent-bridge/git-credentials.ts
|
|
@@ -29135,7 +29252,9 @@ init_base_url();
|
|
|
29135
29252
|
init_login();
|
|
29136
29253
|
|
|
29137
29254
|
// src/commands/auth/profile.ts
|
|
29255
|
+
import { existsSync as existsSync2, rmSync } from "fs";
|
|
29138
29256
|
init_file();
|
|
29257
|
+
init_path();
|
|
29139
29258
|
init_profiles2();
|
|
29140
29259
|
async function handleAuthStatus(context) {
|
|
29141
29260
|
const result = await fetchAuthStatus(context);
|
|
@@ -29161,43 +29280,58 @@ function logout(context) {
|
|
|
29161
29280
|
);
|
|
29162
29281
|
context.writeOutput(context.io.style.success("Logged out."));
|
|
29163
29282
|
}
|
|
29164
|
-
function
|
|
29283
|
+
function listAccounts(context) {
|
|
29165
29284
|
const profiles = listProfiles(context.configPath);
|
|
29166
29285
|
if (profiles.length === 0) {
|
|
29167
29286
|
throw new Error("No stored accounts. Run `auto auth login` first.");
|
|
29168
29287
|
}
|
|
29169
|
-
const
|
|
29170
|
-
|
|
29171
|
-
|
|
29172
|
-
|
|
29173
|
-
|
|
29174
|
-
|
|
29175
|
-
|
|
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);
|
|
29176
29297
|
return;
|
|
29177
29298
|
}
|
|
29178
|
-
|
|
29179
|
-
|
|
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)
|
|
29180
29311
|
);
|
|
29181
29312
|
if (matches.length === 0) {
|
|
29182
29313
|
throw new Error(
|
|
29183
|
-
`No stored account for ${
|
|
29314
|
+
`No stored account for ${accountRef}. Run \`auto auth login\` to add it.`
|
|
29184
29315
|
);
|
|
29185
29316
|
}
|
|
29186
|
-
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
|
+
);
|
|
29187
29321
|
if (!match) {
|
|
29188
29322
|
throw new Error(
|
|
29189
|
-
`Multiple servers have a stored account for ${
|
|
29323
|
+
`Multiple servers have a stored account for ${accountRef}: ${matches.map((profile) => profile.config.serverUrl).join(
|
|
29190
29324
|
", "
|
|
29191
|
-
)}. Pick one with \`auto auth switch ${
|
|
29325
|
+
)}. Pick one with \`auto auth switch ${accountRef} --server <url>\`.`
|
|
29192
29326
|
);
|
|
29193
29327
|
}
|
|
29194
|
-
|
|
29328
|
+
setCurrentProfile(match.name, context.configPath);
|
|
29195
29329
|
context.writeOutput(
|
|
29196
29330
|
context.io.style.success(
|
|
29197
|
-
`Switched to ${match.userEmail} (${match.serverUrl}).`
|
|
29331
|
+
`Switched to ${match.config.userEmail} (${match.config.serverUrl}).`
|
|
29198
29332
|
)
|
|
29199
29333
|
);
|
|
29200
|
-
if (!match.refreshToken) {
|
|
29334
|
+
if (!match.config.refreshToken) {
|
|
29201
29335
|
context.writeOutput(
|
|
29202
29336
|
context.io.style.warn(
|
|
29203
29337
|
"This account has no stored credentials; run `auto auth login`."
|
|
@@ -29205,13 +29339,43 @@ function switchAccount(context, accountEmail, options = {}) {
|
|
|
29205
29339
|
);
|
|
29206
29340
|
}
|
|
29207
29341
|
}
|
|
29208
|
-
function
|
|
29209
|
-
|
|
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) {
|
|
29210
29373
|
return [
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
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
|
|
29215
29379
|
].filter(Boolean).join(" ");
|
|
29216
29380
|
}
|
|
29217
29381
|
async function fetchAuthStatus(context) {
|
|
@@ -29377,6 +29541,7 @@ function registerAuthCommands(program, context) {
|
|
|
29377
29541
|
await login({
|
|
29378
29542
|
options: {
|
|
29379
29543
|
...options,
|
|
29544
|
+
profile: globalOptions.profile,
|
|
29380
29545
|
apiUrl: resolveApiBaseUrl({
|
|
29381
29546
|
explicit: [options.apiUrl, globalOptions.apiUrl],
|
|
29382
29547
|
env: context.env
|
|
@@ -29399,11 +29564,13 @@ function registerAuthCommands(program, context) {
|
|
|
29399
29564
|
await handleWhoami(context);
|
|
29400
29565
|
});
|
|
29401
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));
|
|
29402
29568
|
auth.command("switch").description(
|
|
29403
29569
|
"Switch the active account to a stored profile, or list stored accounts."
|
|
29404
|
-
).argument("[
|
|
29405
|
-
(
|
|
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)
|
|
29406
29572
|
);
|
|
29573
|
+
auth.command("remove").description("Remove a stored account profile.").argument("<name>", "Profile name to remove").action((name) => removeProfile(context, name));
|
|
29407
29574
|
}
|
|
29408
29575
|
|
|
29409
29576
|
// src/lib/stdio/readline.ts
|
|
@@ -32204,9 +32371,9 @@ function withApiBaseUrl2(context, commandOptions) {
|
|
|
32204
32371
|
// src/commands/sessions/connect.ts
|
|
32205
32372
|
init_resources2();
|
|
32206
32373
|
init_browser();
|
|
32207
|
-
import { existsSync as
|
|
32374
|
+
import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
32208
32375
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
32209
|
-
import { join as
|
|
32376
|
+
import { join as join5 } from "path";
|
|
32210
32377
|
var POLL_INTERVAL_MS2 = 2e3;
|
|
32211
32378
|
var POLL_TIMEOUT_MS2 = 5 * 6e4;
|
|
32212
32379
|
var SLACK_APPS_URL = "https://api.slack.com/apps";
|
|
@@ -32377,7 +32544,7 @@ async function promptForIconUploads(input, options) {
|
|
|
32377
32544
|
}
|
|
32378
32545
|
}
|
|
32379
32546
|
function stagedLocationLabel(stagedPath) {
|
|
32380
|
-
return stagedPath.startsWith(`${
|
|
32547
|
+
return stagedPath.startsWith(`${join5(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
|
|
32381
32548
|
}
|
|
32382
32549
|
async function stageAvatarImage(input) {
|
|
32383
32550
|
try {
|
|
@@ -32389,9 +32556,9 @@ async function stageAvatarImage(input) {
|
|
|
32389
32556
|
}
|
|
32390
32557
|
const contentType = response.headers.get("content-type") ?? "";
|
|
32391
32558
|
const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
|
|
32392
|
-
const downloads =
|
|
32393
|
-
const directory =
|
|
32394
|
-
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}`);
|
|
32395
32562
|
writeFileSync4(path2, Buffer.from(await response.arrayBuffer()));
|
|
32396
32563
|
return path2;
|
|
32397
32564
|
} catch {
|
|
@@ -32534,6 +32701,10 @@ function registerToolCommands(program, context) {
|
|
|
32534
32701
|
});
|
|
32535
32702
|
}
|
|
32536
32703
|
|
|
32704
|
+
// src/cli/program.ts
|
|
32705
|
+
init_path();
|
|
32706
|
+
init_profiles2();
|
|
32707
|
+
|
|
32537
32708
|
// src/lib/output/iostreams.ts
|
|
32538
32709
|
init_style();
|
|
32539
32710
|
function createIOStreams(flags) {
|
|
@@ -32627,9 +32798,12 @@ function createProgram(options = {}) {
|
|
|
32627
32798
|
"stream-json",
|
|
32628
32799
|
"tui"
|
|
32629
32800
|
])
|
|
32630
|
-
).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
|
+
);
|
|
32631
32805
|
program.configureHelp({ showGlobalOptions: true });
|
|
32632
|
-
program.hook("preAction", () => {
|
|
32806
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
32633
32807
|
if (!options.io) {
|
|
32634
32808
|
const g = program.opts();
|
|
32635
32809
|
context.io = createIOStreams({
|
|
@@ -32639,15 +32813,34 @@ function createProgram(options = {}) {
|
|
|
32639
32813
|
writeError: options.writeError
|
|
32640
32814
|
});
|
|
32641
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
|
+
}
|
|
32642
32832
|
});
|
|
32643
32833
|
const launchTui = async () => {
|
|
32644
32834
|
const g = program.opts();
|
|
32645
32835
|
if (options.launchTui) {
|
|
32646
|
-
await options.launchTui({
|
|
32836
|
+
await options.launchTui({
|
|
32837
|
+
apiUrl: g.apiUrl,
|
|
32838
|
+
configPath: context.configPath
|
|
32839
|
+
});
|
|
32647
32840
|
return;
|
|
32648
32841
|
}
|
|
32649
32842
|
const { launch: launch2 } = await Promise.resolve().then(() => (init_launcher(), launcher_exports));
|
|
32650
|
-
await launch2({ apiUrl: g.apiUrl });
|
|
32843
|
+
await launch2({ apiUrl: g.apiUrl, configPath: context.configPath });
|
|
32651
32844
|
process.exit(0);
|
|
32652
32845
|
};
|
|
32653
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
|
}
|