@ait-co/console-cli 0.1.24 → 0.1.26
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 +17 -3
- package/dist/cli.mjs +383 -102
- package/dist/cli.mjs.map +1 -1
- package/package.json +5 -5
package/dist/cli.mjs
CHANGED
|
@@ -13,6 +13,10 @@ import { promisify } from "node:util";
|
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
14
|
//#region src/api/http.ts
|
|
15
15
|
var TossApiError = class extends Error {
|
|
16
|
+
status;
|
|
17
|
+
errorCode;
|
|
18
|
+
reason;
|
|
19
|
+
errorType;
|
|
16
20
|
constructor(status, errorCode, reason, errorType) {
|
|
17
21
|
super(`Toss API error ${errorCode}: ${reason} (HTTP ${status})`);
|
|
18
22
|
this.status = status;
|
|
@@ -27,6 +31,7 @@ var TossApiError = class extends Error {
|
|
|
27
31
|
}
|
|
28
32
|
};
|
|
29
33
|
var NetworkError = class extends Error {
|
|
34
|
+
url;
|
|
30
35
|
constructor(url, cause) {
|
|
31
36
|
super(`Network request to ${url} failed: ${cause.message}`);
|
|
32
37
|
this.url = url;
|
|
@@ -35,6 +40,9 @@ var NetworkError = class extends Error {
|
|
|
35
40
|
}
|
|
36
41
|
};
|
|
37
42
|
var MalformedResponseError = class extends Error {
|
|
43
|
+
url;
|
|
44
|
+
status;
|
|
45
|
+
bodyPreview;
|
|
38
46
|
constructor(url, status, message, bodyPreview) {
|
|
39
47
|
const suffix = bodyPreview ? ` (body: ${bodyPreview})` : "";
|
|
40
48
|
super(`Malformed response from ${url} (HTTP ${status}): ${message}${suffix}`);
|
|
@@ -2593,7 +2601,7 @@ async function runAppInit(args) {
|
|
|
2593
2601
|
workspaceId = await pickWorkspace(session.cookies);
|
|
2594
2602
|
categoryIds = await pickCategories(session.cookies);
|
|
2595
2603
|
} catch (err) {
|
|
2596
|
-
if (isPromptCancelled$
|
|
2604
|
+
if (isPromptCancelled$2(err)) {
|
|
2597
2605
|
process.stderr.write("Aborted.\n");
|
|
2598
2606
|
return exitAfterFlush(ExitCode.Usage);
|
|
2599
2607
|
}
|
|
@@ -2636,7 +2644,7 @@ async function runAppInit(args) {
|
|
|
2636
2644
|
categoryIds
|
|
2637
2645
|
};
|
|
2638
2646
|
} catch (err) {
|
|
2639
|
-
if (isPromptCancelled$
|
|
2647
|
+
if (isPromptCancelled$2(err)) {
|
|
2640
2648
|
process.stderr.write("Aborted.\n");
|
|
2641
2649
|
return exitAfterFlush(ExitCode.Usage);
|
|
2642
2650
|
}
|
|
@@ -2695,7 +2703,7 @@ async function fileExists(path) {
|
|
|
2695
2703
|
return false;
|
|
2696
2704
|
}
|
|
2697
2705
|
}
|
|
2698
|
-
function isPromptCancelled$
|
|
2706
|
+
function isPromptCancelled$2(err) {
|
|
2699
2707
|
return err instanceof Error && err.name === "ExitPromptError";
|
|
2700
2708
|
}
|
|
2701
2709
|
async function pickWorkspace(cookies) {
|
|
@@ -5810,6 +5818,8 @@ const appCommand = defineCommand({
|
|
|
5810
5818
|
//#region src/auth/backend.ts
|
|
5811
5819
|
const CREDENTIAL_SERVICE = "aitcc.credentials";
|
|
5812
5820
|
var CredentialBackendUnsupportedError = class extends Error {
|
|
5821
|
+
platform;
|
|
5822
|
+
hint;
|
|
5813
5823
|
constructor(platform, hint) {
|
|
5814
5824
|
super(`No supported credential backend for platform "${platform}". ${hint}`);
|
|
5815
5825
|
this.platform = platform;
|
|
@@ -5818,6 +5828,9 @@ var CredentialBackendUnsupportedError = class extends Error {
|
|
|
5818
5828
|
}
|
|
5819
5829
|
};
|
|
5820
5830
|
var CredentialBackendCommandError = class extends Error {
|
|
5831
|
+
command;
|
|
5832
|
+
exitCode;
|
|
5833
|
+
redactedStderr;
|
|
5821
5834
|
constructor(command, exitCode, redactedStderr) {
|
|
5822
5835
|
super(`Credential backend command "${command}" failed (exit=${exitCode ?? "null"}): ${redactedStderr}`);
|
|
5823
5836
|
this.command = command;
|
|
@@ -5856,7 +5869,7 @@ async function runCommand(command, opts) {
|
|
|
5856
5869
|
function isCommandNotFound(err) {
|
|
5857
5870
|
return err.code === "ENOENT";
|
|
5858
5871
|
}
|
|
5859
|
-
function stripTrailingNewline(s) {
|
|
5872
|
+
function stripTrailingNewline$1(s) {
|
|
5860
5873
|
return s.replace(/\r?\n$/, "");
|
|
5861
5874
|
}
|
|
5862
5875
|
function redactStderr(stderr) {
|
|
@@ -5890,7 +5903,7 @@ const LINUX_BACKEND = {
|
|
|
5890
5903
|
throw new CredentialBackendCommandError("secret-tool lookup", result.exitCode, redactStderr(result.stderr));
|
|
5891
5904
|
}
|
|
5892
5905
|
if (result.stdout.length === 0) return null;
|
|
5893
|
-
const password = stripTrailingNewline(result.stdout);
|
|
5906
|
+
const password = stripTrailingNewline$1(result.stdout);
|
|
5894
5907
|
return password.length > 0 ? password : null;
|
|
5895
5908
|
},
|
|
5896
5909
|
async set(account, password) {
|
|
@@ -5954,7 +5967,7 @@ const MACOS_BACKEND = {
|
|
|
5954
5967
|
}
|
|
5955
5968
|
if (result.exitCode === 44) return null;
|
|
5956
5969
|
if (result.exitCode !== 0) return null;
|
|
5957
|
-
const password = stripTrailingNewline(result.stdout);
|
|
5970
|
+
const password = stripTrailingNewline$1(result.stdout);
|
|
5958
5971
|
return password.length > 0 ? password : null;
|
|
5959
5972
|
},
|
|
5960
5973
|
async set(account, password) {
|
|
@@ -6489,13 +6502,17 @@ const authImportCommand = defineCommand({
|
|
|
6489
6502
|
});
|
|
6490
6503
|
//#endregion
|
|
6491
6504
|
//#region src/commands/auth.ts
|
|
6505
|
+
function emitDeprecation(replacement) {
|
|
6506
|
+
process.stderr.write(`warning: this command is deprecated and will be removed in 1.0; ${replacement}\n`);
|
|
6507
|
+
}
|
|
6492
6508
|
async function runAuthSet(args, deps = {}) {
|
|
6509
|
+
emitDeprecation("use `aitcc login` (interactive prompt offers a save option).");
|
|
6493
6510
|
const env = deps.env ?? process.env;
|
|
6494
6511
|
let email = args.email?.trim();
|
|
6495
|
-
let password$
|
|
6496
|
-
const argvPasswordUsed = password$
|
|
6512
|
+
let password$1 = args.password;
|
|
6513
|
+
const argvPasswordUsed = password$1 !== void 0;
|
|
6497
6514
|
if (!email && env.AITCC_EMAIL) email = env.AITCC_EMAIL;
|
|
6498
|
-
if (password$
|
|
6515
|
+
if (password$1 === void 0 && env.AITCC_PASSWORD) password$1 = env.AITCC_PASSWORD;
|
|
6499
6516
|
if (argvPasswordUsed) process.stderr.write("Warning: --password on argv is visible in `ps`/Task Manager. Prefer the AITCC_PASSWORD environment variable for scripted use.\n");
|
|
6500
6517
|
const interactive = process.stdout.isTTY && process.stdin.isTTY && !args.json;
|
|
6501
6518
|
if (!email) {
|
|
@@ -6509,7 +6526,7 @@ async function runAuthSet(args, deps = {}) {
|
|
|
6509
6526
|
validate: (raw) => raw.trim().length > 0 ? true : "email is required"
|
|
6510
6527
|
})).trim();
|
|
6511
6528
|
} catch (err) {
|
|
6512
|
-
if (isPromptCancelled(err)) {
|
|
6529
|
+
if (isPromptCancelled$1(err)) {
|
|
6513
6530
|
process.stderr.write("Aborted.\n");
|
|
6514
6531
|
return exitAfterFlush(ExitCode.Usage);
|
|
6515
6532
|
}
|
|
@@ -6525,26 +6542,26 @@ async function runAuthSet(args, deps = {}) {
|
|
|
6525
6542
|
else process.stderr.write(`Invalid email: ${email}\n`);
|
|
6526
6543
|
return exitAfterFlush(ExitCode.Usage);
|
|
6527
6544
|
}
|
|
6528
|
-
if (password$
|
|
6545
|
+
if (password$1 === void 0) {
|
|
6529
6546
|
if (!interactive) {
|
|
6530
6547
|
emitInteractiveRequired(args.json, "password");
|
|
6531
6548
|
return exitAfterFlush(ExitCode.Usage);
|
|
6532
6549
|
}
|
|
6533
6550
|
try {
|
|
6534
|
-
password$
|
|
6551
|
+
password$1 = await password({
|
|
6535
6552
|
message: "Password:",
|
|
6536
6553
|
mask: true,
|
|
6537
6554
|
validate: (raw) => raw.length > 0 ? true : "password is required"
|
|
6538
6555
|
});
|
|
6539
6556
|
} catch (err) {
|
|
6540
|
-
if (isPromptCancelled(err)) {
|
|
6557
|
+
if (isPromptCancelled$1(err)) {
|
|
6541
6558
|
process.stderr.write("Aborted.\n");
|
|
6542
6559
|
return exitAfterFlush(ExitCode.Usage);
|
|
6543
6560
|
}
|
|
6544
6561
|
throw err;
|
|
6545
6562
|
}
|
|
6546
6563
|
}
|
|
6547
|
-
if (password$
|
|
6564
|
+
if (password$1.length === 0) {
|
|
6548
6565
|
if (args.json) emitJson({
|
|
6549
6566
|
ok: false,
|
|
6550
6567
|
reason: "invalid-password",
|
|
@@ -6555,7 +6572,7 @@ async function runAuthSet(args, deps = {}) {
|
|
|
6555
6572
|
}
|
|
6556
6573
|
let result;
|
|
6557
6574
|
try {
|
|
6558
|
-
result = await saveCredentials(email, password$
|
|
6575
|
+
result = await saveCredentials(email, password$1, deps.backend ? { override: deps.backend } : {});
|
|
6559
6576
|
} catch (err) {
|
|
6560
6577
|
const message = err.message;
|
|
6561
6578
|
if (args.json) emitJson({
|
|
@@ -6576,6 +6593,7 @@ async function runAuthSet(args, deps = {}) {
|
|
|
6576
6593
|
return exitAfterFlush(ExitCode.Ok);
|
|
6577
6594
|
}
|
|
6578
6595
|
async function runAuthClear(args, deps = {}) {
|
|
6596
|
+
emitDeprecation("use `aitcc logout --purge` to remove session and saved credentials together.");
|
|
6579
6597
|
const interactive = process.stdout.isTTY && process.stdin.isTTY && !args.json;
|
|
6580
6598
|
const active = await getActiveCredentialEmail(deps.env ? { env: deps.env } : {}).catch(() => null);
|
|
6581
6599
|
if (!args.yes) {
|
|
@@ -6596,7 +6614,7 @@ async function runAuthClear(args, deps = {}) {
|
|
|
6596
6614
|
default: false
|
|
6597
6615
|
});
|
|
6598
6616
|
} catch (err) {
|
|
6599
|
-
if (isPromptCancelled(err)) {
|
|
6617
|
+
if (isPromptCancelled$1(err)) {
|
|
6600
6618
|
process.stderr.write("Aborted.\n");
|
|
6601
6619
|
return exitAfterFlush(ExitCode.Usage);
|
|
6602
6620
|
}
|
|
@@ -6634,6 +6652,7 @@ async function runAuthClear(args, deps = {}) {
|
|
|
6634
6652
|
return exitAfterFlush(ExitCode.Ok);
|
|
6635
6653
|
}
|
|
6636
6654
|
async function runAuthStatus(args, deps = {}) {
|
|
6655
|
+
emitDeprecation("use `aitcc whoami` (now reports credential source).");
|
|
6637
6656
|
const active = await getActiveCredentialEmail(deps.env ? { env: deps.env } : {}).catch(() => null);
|
|
6638
6657
|
const session = await readSession();
|
|
6639
6658
|
if (args.json) {
|
|
@@ -6672,7 +6691,7 @@ function emitInteractiveRequired(json, missing) {
|
|
|
6672
6691
|
});
|
|
6673
6692
|
else process.stderr.write(`Cannot prompt for ${missing} in non-interactive mode. Use --${missing} or set AITCC_${missing.toUpperCase()}.\n`);
|
|
6674
6693
|
}
|
|
6675
|
-
function isPromptCancelled(err) {
|
|
6694
|
+
function isPromptCancelled$1(err) {
|
|
6676
6695
|
return err instanceof Error && err.name === "ExitPromptError";
|
|
6677
6696
|
}
|
|
6678
6697
|
const authCommand = defineCommand({
|
|
@@ -6684,7 +6703,7 @@ const authCommand = defineCommand({
|
|
|
6684
6703
|
set: defineCommand({
|
|
6685
6704
|
meta: {
|
|
6686
6705
|
name: "set",
|
|
6687
|
-
description: "
|
|
6706
|
+
description: "[deprecated] Use `aitcc login` instead — the prompt now offers a save option."
|
|
6688
6707
|
},
|
|
6689
6708
|
args: {
|
|
6690
6709
|
json: {
|
|
@@ -6712,7 +6731,7 @@ const authCommand = defineCommand({
|
|
|
6712
6731
|
clear: defineCommand({
|
|
6713
6732
|
meta: {
|
|
6714
6733
|
name: "clear",
|
|
6715
|
-
description: "
|
|
6734
|
+
description: "[deprecated] Use `aitcc logout --purge` instead."
|
|
6716
6735
|
},
|
|
6717
6736
|
args: {
|
|
6718
6737
|
json: {
|
|
@@ -6737,7 +6756,7 @@ const authCommand = defineCommand({
|
|
|
6737
6756
|
status: defineCommand({
|
|
6738
6757
|
meta: {
|
|
6739
6758
|
name: "status",
|
|
6740
|
-
description: "
|
|
6759
|
+
description: "[deprecated] Use `aitcc whoami` (now reports credential source)."
|
|
6741
6760
|
},
|
|
6742
6761
|
args: { json: {
|
|
6743
6762
|
type: "boolean",
|
|
@@ -7226,6 +7245,8 @@ function isResponse(m) {
|
|
|
7226
7245
|
return "id" in m;
|
|
7227
7246
|
}
|
|
7228
7247
|
var CdpProtocolError = class extends Error {
|
|
7248
|
+
method;
|
|
7249
|
+
code;
|
|
7229
7250
|
constructor(method, code, message) {
|
|
7230
7251
|
super(`CDP error for ${method}: ${message} (code=${code})`);
|
|
7231
7252
|
this.method = method;
|
|
@@ -7470,6 +7491,7 @@ function validateCookie(raw, index) {
|
|
|
7470
7491
|
//#endregion
|
|
7471
7492
|
//#region src/chrome.ts
|
|
7472
7493
|
var ChromeNotFoundError = class extends Error {
|
|
7494
|
+
candidates;
|
|
7473
7495
|
constructor(candidates) {
|
|
7474
7496
|
super(`Could not find Chrome or a Chromium-family browser. Tried: ${candidates.join(", ")}.\nInstall Chrome, or set AITCC_BROWSER to an executable path.`);
|
|
7475
7497
|
this.candidates = candidates;
|
|
@@ -7477,6 +7499,7 @@ var ChromeNotFoundError = class extends Error {
|
|
|
7477
7499
|
}
|
|
7478
7500
|
};
|
|
7479
7501
|
var ChromeLaunchError = class extends Error {
|
|
7502
|
+
executable;
|
|
7480
7503
|
constructor(executable, cause) {
|
|
7481
7504
|
super(`Failed to launch ${executable}: ${cause.message}`);
|
|
7482
7505
|
this.executable = executable;
|
|
@@ -7485,6 +7508,7 @@ var ChromeLaunchError = class extends Error {
|
|
|
7485
7508
|
}
|
|
7486
7509
|
};
|
|
7487
7510
|
var ChromeEndpointTimeoutError = class extends Error {
|
|
7511
|
+
executable;
|
|
7488
7512
|
constructor(executable) {
|
|
7489
7513
|
super(`${executable} did not print a DevTools endpoint within the timeout. It may have been blocked by the OS or launched a GUI-less variant.`);
|
|
7490
7514
|
this.executable = executable;
|
|
@@ -7897,10 +7921,38 @@ function chooseLoginMode(input) {
|
|
|
7897
7921
|
if (input.interactiveFlag) return "interactive";
|
|
7898
7922
|
return input.hasCredentials ? "headless" : "interactive";
|
|
7899
7923
|
}
|
|
7924
|
+
const defaultPromptDeps = {
|
|
7925
|
+
email: (defaultValue) => input({
|
|
7926
|
+
message: "Email:",
|
|
7927
|
+
...defaultValue !== void 0 ? { default: defaultValue } : {},
|
|
7928
|
+
validate: (raw) => {
|
|
7929
|
+
const trimmed = raw.trim();
|
|
7930
|
+
if (trimmed.length === 0) return "email is required";
|
|
7931
|
+
if (!trimmed.includes("@")) return "must contain \"@\"";
|
|
7932
|
+
return true;
|
|
7933
|
+
}
|
|
7934
|
+
}).then((s) => s.trim()),
|
|
7935
|
+
password: () => password({
|
|
7936
|
+
message: "Password:",
|
|
7937
|
+
mask: true,
|
|
7938
|
+
validate: (raw) => raw.length > 0 ? true : "password is required"
|
|
7939
|
+
}),
|
|
7940
|
+
saveTarget: () => select({
|
|
7941
|
+
message: "Where would you like to save the credentials?",
|
|
7942
|
+
default: "keychain",
|
|
7943
|
+
choices: [{
|
|
7944
|
+
name: "OS keychain (recommended) — next login runs headlessly",
|
|
7945
|
+
value: "keychain"
|
|
7946
|
+
}, {
|
|
7947
|
+
name: "Do not save — one-shot. (Tip: AITCC_EMAIL/AITCC_PASSWORD env for CI.)",
|
|
7948
|
+
value: "none"
|
|
7949
|
+
}]
|
|
7950
|
+
})
|
|
7951
|
+
};
|
|
7900
7952
|
const loginCommand = defineCommand({
|
|
7901
7953
|
meta: {
|
|
7902
7954
|
name: "login",
|
|
7903
|
-
description: "
|
|
7955
|
+
description: "Sign in to the Apps in Toss console and capture the session cookies."
|
|
7904
7956
|
},
|
|
7905
7957
|
args: {
|
|
7906
7958
|
json: {
|
|
@@ -7918,9 +7970,26 @@ const loginCommand = defineCommand({
|
|
|
7918
7970
|
description: "Force the visible-browser flow even if credentials are configured.",
|
|
7919
7971
|
default: false
|
|
7920
7972
|
},
|
|
7973
|
+
email: {
|
|
7974
|
+
type: "string",
|
|
7975
|
+
description: "Email (skip prompt; required for non-interactive use)."
|
|
7976
|
+
},
|
|
7977
|
+
password: {
|
|
7978
|
+
type: "string",
|
|
7979
|
+
description: "Password (skip prompt; visible in `ps`/Task Manager — prefer --password-stdin or AITCC_PASSWORD env)."
|
|
7980
|
+
},
|
|
7981
|
+
"password-stdin": {
|
|
7982
|
+
type: "boolean",
|
|
7983
|
+
description: "Read the password from stdin (recommended for non-interactive use).",
|
|
7984
|
+
default: false
|
|
7985
|
+
},
|
|
7986
|
+
save: {
|
|
7987
|
+
type: "string",
|
|
7988
|
+
description: "Where to persist credentials when --email/--password* are passed: \"keychain\" or \"none\" (default)."
|
|
7989
|
+
},
|
|
7921
7990
|
"skip-onboarding": {
|
|
7922
7991
|
type: "boolean",
|
|
7923
|
-
description: "
|
|
7992
|
+
description: "Deprecated no-op; kept so existing scripts do not break.",
|
|
7924
7993
|
default: false
|
|
7925
7994
|
}
|
|
7926
7995
|
},
|
|
@@ -7929,7 +7998,10 @@ const loginCommand = defineCommand({
|
|
|
7929
7998
|
json: args.json,
|
|
7930
7999
|
timeout: args.timeout,
|
|
7931
8000
|
interactive: args.interactive,
|
|
7932
|
-
|
|
8001
|
+
email: typeof args.email === "string" ? args.email : void 0,
|
|
8002
|
+
password: typeof args.password === "string" ? args.password : void 0,
|
|
8003
|
+
passwordStdin: args["password-stdin"],
|
|
8004
|
+
save: typeof args.save === "string" ? args.save : void 0
|
|
7933
8005
|
}, {
|
|
7934
8006
|
getCredentials: loadCredentials,
|
|
7935
8007
|
saveCredentials
|
|
@@ -7973,17 +8045,41 @@ async function runLoginCommand(args, deps) {
|
|
|
7973
8045
|
}
|
|
7974
8046
|
process.stderr.write(`Using custom authorize URL from AITCC_OAUTH_URL: ${authorizeUrl}\n`);
|
|
7975
8047
|
}
|
|
7976
|
-
|
|
7977
|
-
if (
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
8048
|
+
const resolved = await resolveCredentialsForLogin(args, deps);
|
|
8049
|
+
if (resolved.kind === "error") {
|
|
8050
|
+
emitError({
|
|
8051
|
+
reason: resolved.reason,
|
|
8052
|
+
message: resolved.message
|
|
8053
|
+
}, resolved.message);
|
|
8054
|
+
return exitAfterFlush(resolved.exitCode);
|
|
8055
|
+
}
|
|
8056
|
+
let saved = "skipped";
|
|
8057
|
+
if (resolved.saveTarget === "keychain" && resolved.credentials !== null) {
|
|
8058
|
+
const save = deps.saveCredentials;
|
|
8059
|
+
if (!save) {
|
|
8060
|
+
emitError({
|
|
8061
|
+
reason: "save-unavailable",
|
|
8062
|
+
message: "no save backend configured"
|
|
8063
|
+
}, "Cannot save credentials: no backend configured.");
|
|
8064
|
+
return exitAfterFlush(ExitCode.Generic);
|
|
8065
|
+
}
|
|
8066
|
+
try {
|
|
8067
|
+
const result = await save(resolved.credentials.email, resolved.credentials.password);
|
|
8068
|
+
saved = result.status;
|
|
8069
|
+
if (!args.json) if (result.status === "unchanged") process.stderr.write("Credentials already saved (no change).\n");
|
|
8070
|
+
else process.stderr.write(`Credentials saved to OS keychain (${resolved.credentials.email}).\n`);
|
|
8071
|
+
} catch (err) {
|
|
8072
|
+
const message = err.message;
|
|
8073
|
+
emitError({
|
|
8074
|
+
reason: "keychain-save-failed",
|
|
8075
|
+
message
|
|
8076
|
+
}, `Failed to save credentials to the OS keychain: ${message}\nOn Linux, install libsecret (\`secret-tool\`) and retry. Re-run with \`--save none\` to skip persistence.`);
|
|
8077
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
8078
|
+
}
|
|
7983
8079
|
}
|
|
7984
8080
|
const initialMode = chooseLoginMode({
|
|
7985
8081
|
interactiveFlag: args.interactive,
|
|
7986
|
-
hasCredentials: credentials !== null
|
|
8082
|
+
hasCredentials: resolved.credentials !== null
|
|
7987
8083
|
});
|
|
7988
8084
|
const endpointTimeoutMs = Math.min(6e4, Math.max(3e4, Math.floor(timeoutMs / 2)));
|
|
7989
8085
|
const firstAttemptStart = Date.now();
|
|
@@ -7993,9 +8089,9 @@ async function runLoginCommand(args, deps) {
|
|
|
7993
8089
|
endpointTimeoutMs,
|
|
7994
8090
|
authorizeUrl,
|
|
7995
8091
|
mode: initialMode,
|
|
7996
|
-
credentials,
|
|
7997
|
-
|
|
7998
|
-
|
|
8092
|
+
credentials: resolved.credentials,
|
|
8093
|
+
saved,
|
|
8094
|
+
emitError
|
|
7999
8095
|
});
|
|
8000
8096
|
if (result.status === "fallback-to-interactive") {
|
|
8001
8097
|
process.stderr.write(`${result.message}\n`);
|
|
@@ -8006,8 +8102,8 @@ async function runLoginCommand(args, deps) {
|
|
|
8006
8102
|
authorizeUrl,
|
|
8007
8103
|
mode: "interactive",
|
|
8008
8104
|
credentials: null,
|
|
8009
|
-
|
|
8010
|
-
|
|
8105
|
+
saved,
|
|
8106
|
+
emitError
|
|
8011
8107
|
});
|
|
8012
8108
|
if (second.status === "exit") return exitAfterFlush(second.code);
|
|
8013
8109
|
second.status;
|
|
@@ -8015,8 +8111,167 @@ async function runLoginCommand(args, deps) {
|
|
|
8015
8111
|
}
|
|
8016
8112
|
return exitAfterFlush(result.code);
|
|
8017
8113
|
}
|
|
8114
|
+
/**
|
|
8115
|
+
* Resolve credentials and the requested save target for `aitcc login`.
|
|
8116
|
+
* Pure-ish: only side-effect is reading stdin via `deps.readStdin` (when
|
|
8117
|
+
* `--password-stdin` is set) and prompting via `deps.prompts` (when TTY).
|
|
8118
|
+
*/
|
|
8119
|
+
async function resolveCredentialsForLogin(args, deps, opts = {}) {
|
|
8120
|
+
const env = opts.env ?? process.env;
|
|
8121
|
+
const stdoutIsTTY = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
|
|
8122
|
+
const stdinIsTTY = opts.stdinIsTTY ?? Boolean(process.stdin.isTTY);
|
|
8123
|
+
const interactiveTty = stdoutIsTTY && stdinIsTTY && !args.json;
|
|
8124
|
+
if (args.password !== void 0 && args.passwordStdin) return {
|
|
8125
|
+
kind: "error",
|
|
8126
|
+
reason: "conflicting-password-source",
|
|
8127
|
+
message: "--password and --password-stdin cannot be used together.",
|
|
8128
|
+
exitCode: ExitCode.Usage
|
|
8129
|
+
};
|
|
8130
|
+
if (args.interactive && (args.email !== void 0 || args.password !== void 0 || args.passwordStdin || args.save !== void 0)) return {
|
|
8131
|
+
kind: "error",
|
|
8132
|
+
reason: "conflicting-interactive-flags",
|
|
8133
|
+
message: "--interactive cannot be combined with --email/--password/--password-stdin/--save. Drop --interactive to use credentials, or drop the credential flags to type in the browser.",
|
|
8134
|
+
exitCode: ExitCode.Usage
|
|
8135
|
+
};
|
|
8136
|
+
let saveTarget;
|
|
8137
|
+
if (args.save !== void 0) {
|
|
8138
|
+
if (args.save !== "keychain" && args.save !== "none") return {
|
|
8139
|
+
kind: "error",
|
|
8140
|
+
reason: "invalid-save",
|
|
8141
|
+
message: `--save must be "keychain" or "none" (got "${args.save}").`,
|
|
8142
|
+
exitCode: ExitCode.Usage
|
|
8143
|
+
};
|
|
8144
|
+
saveTarget = args.save;
|
|
8145
|
+
}
|
|
8146
|
+
if (args.email !== void 0 || args.password !== void 0 || args.passwordStdin) {
|
|
8147
|
+
if (args.email === void 0 || args.email.trim().length === 0) return {
|
|
8148
|
+
kind: "error",
|
|
8149
|
+
reason: "missing-email",
|
|
8150
|
+
message: "--email is required when --password / --password-stdin is passed.",
|
|
8151
|
+
exitCode: ExitCode.Usage
|
|
8152
|
+
};
|
|
8153
|
+
if (!args.email.includes("@")) return {
|
|
8154
|
+
kind: "error",
|
|
8155
|
+
reason: "invalid-email",
|
|
8156
|
+
message: `Invalid email: ${args.email}`,
|
|
8157
|
+
exitCode: ExitCode.Usage
|
|
8158
|
+
};
|
|
8159
|
+
let password;
|
|
8160
|
+
if (args.passwordStdin) {
|
|
8161
|
+
password = stripTrailingNewline(await (deps.readStdin ?? readStdinAll)());
|
|
8162
|
+
if (password.length === 0) return {
|
|
8163
|
+
kind: "error",
|
|
8164
|
+
reason: "invalid-password",
|
|
8165
|
+
message: "--password-stdin received an empty password on stdin.",
|
|
8166
|
+
exitCode: ExitCode.Usage
|
|
8167
|
+
};
|
|
8168
|
+
} else if (args.password !== void 0) {
|
|
8169
|
+
process.stderr.write("Warning: --password on argv is visible in `ps`/Task Manager. Prefer --password-stdin or the AITCC_PASSWORD environment variable.\n");
|
|
8170
|
+
password = args.password;
|
|
8171
|
+
if (password.length === 0) return {
|
|
8172
|
+
kind: "error",
|
|
8173
|
+
reason: "invalid-password",
|
|
8174
|
+
message: "--password value is empty.",
|
|
8175
|
+
exitCode: ExitCode.Usage
|
|
8176
|
+
};
|
|
8177
|
+
} else return {
|
|
8178
|
+
kind: "error",
|
|
8179
|
+
reason: "missing-password",
|
|
8180
|
+
message: "--email passed without a password. Add --password-stdin (recommended) or --password.",
|
|
8181
|
+
exitCode: ExitCode.Usage
|
|
8182
|
+
};
|
|
8183
|
+
return {
|
|
8184
|
+
kind: "ok",
|
|
8185
|
+
credentials: {
|
|
8186
|
+
source: "argv",
|
|
8187
|
+
email: args.email.trim(),
|
|
8188
|
+
password
|
|
8189
|
+
},
|
|
8190
|
+
saveTarget: saveTarget ?? "none"
|
|
8191
|
+
};
|
|
8192
|
+
}
|
|
8193
|
+
if (args.interactive) return {
|
|
8194
|
+
kind: "ok",
|
|
8195
|
+
credentials: null,
|
|
8196
|
+
saveTarget: saveTarget ?? "none"
|
|
8197
|
+
};
|
|
8198
|
+
if (env.AITCC_EMAIL && env.AITCC_PASSWORD) return {
|
|
8199
|
+
kind: "ok",
|
|
8200
|
+
credentials: {
|
|
8201
|
+
source: "env",
|
|
8202
|
+
email: env.AITCC_EMAIL,
|
|
8203
|
+
password: env.AITCC_PASSWORD
|
|
8204
|
+
},
|
|
8205
|
+
saveTarget: saveTarget ?? "none"
|
|
8206
|
+
};
|
|
8207
|
+
const getCredentials = deps.getCredentials;
|
|
8208
|
+
if (getCredentials) {
|
|
8209
|
+
const fromStore = await getCredentials().catch((err) => {
|
|
8210
|
+
process.stderr.write(`Credential lookup failed (${err.message}); ignoring.\n`);
|
|
8211
|
+
return null;
|
|
8212
|
+
});
|
|
8213
|
+
if (fromStore) {
|
|
8214
|
+
if (!args.json) process.stderr.write(`Using credentials from OS keychain for ${fromStore.email}. Pass --interactive to type a different account.
|
|
8215
|
+
`);
|
|
8216
|
+
return {
|
|
8217
|
+
kind: "ok",
|
|
8218
|
+
credentials: {
|
|
8219
|
+
source: fromStore.kind,
|
|
8220
|
+
email: fromStore.email,
|
|
8221
|
+
password: fromStore.password
|
|
8222
|
+
},
|
|
8223
|
+
saveTarget: saveTarget ?? "none"
|
|
8224
|
+
};
|
|
8225
|
+
}
|
|
8226
|
+
}
|
|
8227
|
+
if (interactiveTty) {
|
|
8228
|
+
const prompts = deps.prompts ?? defaultPromptDeps;
|
|
8229
|
+
let email;
|
|
8230
|
+
let password;
|
|
8231
|
+
try {
|
|
8232
|
+
email = await prompts.email();
|
|
8233
|
+
password = await prompts.password();
|
|
8234
|
+
} catch (err) {
|
|
8235
|
+
if (isPromptCancelled(err)) return {
|
|
8236
|
+
kind: "error",
|
|
8237
|
+
reason: "aborted",
|
|
8238
|
+
message: "Aborted.",
|
|
8239
|
+
exitCode: ExitCode.Usage
|
|
8240
|
+
};
|
|
8241
|
+
throw err;
|
|
8242
|
+
}
|
|
8243
|
+
let promptedSave;
|
|
8244
|
+
if (saveTarget !== void 0) promptedSave = saveTarget;
|
|
8245
|
+
else try {
|
|
8246
|
+
promptedSave = await prompts.saveTarget();
|
|
8247
|
+
} catch (err) {
|
|
8248
|
+
if (isPromptCancelled(err)) return {
|
|
8249
|
+
kind: "error",
|
|
8250
|
+
reason: "aborted",
|
|
8251
|
+
message: "Aborted.",
|
|
8252
|
+
exitCode: ExitCode.Usage
|
|
8253
|
+
};
|
|
8254
|
+
throw err;
|
|
8255
|
+
}
|
|
8256
|
+
return {
|
|
8257
|
+
kind: "ok",
|
|
8258
|
+
credentials: {
|
|
8259
|
+
source: "prompt",
|
|
8260
|
+
email,
|
|
8261
|
+
password
|
|
8262
|
+
},
|
|
8263
|
+
saveTarget: promptedSave
|
|
8264
|
+
};
|
|
8265
|
+
}
|
|
8266
|
+
return {
|
|
8267
|
+
kind: "error",
|
|
8268
|
+
reason: "interactive-required",
|
|
8269
|
+
message: "No credentials configured and stdin is not a TTY. Pass --email + --password-stdin (or set AITCC_EMAIL + AITCC_PASSWORD).",
|
|
8270
|
+
exitCode: ExitCode.Usage
|
|
8271
|
+
};
|
|
8272
|
+
}
|
|
8018
8273
|
async function attemptLogin(opts) {
|
|
8019
|
-
const { args, timeoutMs, endpointTimeoutMs, authorizeUrl, mode, credentials,
|
|
8274
|
+
const { args, timeoutMs, endpointTimeoutMs, authorizeUrl, mode, credentials, saved, emitError } = opts;
|
|
8020
8275
|
const launched = await launchChrome({
|
|
8021
8276
|
initialUrl: authorizeUrl,
|
|
8022
8277
|
endpointTimeoutMs,
|
|
@@ -8055,8 +8310,8 @@ async function attemptLogin(opts) {
|
|
|
8055
8310
|
}
|
|
8056
8311
|
if (mode === "interactive") process.stderr.write("Opened a browser window — complete the sign-in there. The CLI will capture the session automatically.\n");
|
|
8057
8312
|
else {
|
|
8058
|
-
const
|
|
8059
|
-
process.stderr.write(`Signing in headlessly with credentials from ${
|
|
8313
|
+
const sourceLabel = credentials?.source ?? "configured store";
|
|
8314
|
+
process.stderr.write(`Signing in headlessly with credentials from ${sourceLabel}…\n`);
|
|
8060
8315
|
}
|
|
8061
8316
|
let client = null;
|
|
8062
8317
|
const disposeAll = async () => {
|
|
@@ -8223,57 +8478,29 @@ async function attemptLogin(opts) {
|
|
|
8223
8478
|
capturedAt: session.capturedAt,
|
|
8224
8479
|
cookieCount: cookies.length,
|
|
8225
8480
|
mode,
|
|
8481
|
+
credentialSource: credentials?.source ?? "browser",
|
|
8482
|
+
saved,
|
|
8226
8483
|
stepUp
|
|
8227
8484
|
})}\n`);
|
|
8228
8485
|
else process.stdout.write(`Logged in as ${user.name} <${user.email}>\n`);
|
|
8229
8486
|
await disposeAll();
|
|
8230
|
-
if (mode === "interactive" && credentials === null && !args.json && !args.skipOnboarding && process.stdout.isTTY && process.stdin.isTTY && deps.saveCredentials) await runOnboardingPrompt(user.email, deps.saveCredentials);
|
|
8231
8487
|
return {
|
|
8232
8488
|
status: "exit",
|
|
8233
8489
|
code: ExitCode.Ok
|
|
8234
8490
|
};
|
|
8235
8491
|
}
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
agreed = await confirm({
|
|
8249
|
-
message: "Save credentials?",
|
|
8250
|
-
default: true
|
|
8251
|
-
});
|
|
8252
|
-
} catch (err) {
|
|
8253
|
-
if (err instanceof Error && err.name === "ExitPromptError") return;
|
|
8254
|
-
process.stderr.write(`Onboarding prompt failed: ${err.message}\n`);
|
|
8255
|
-
return;
|
|
8256
|
-
}
|
|
8257
|
-
if (!agreed) return;
|
|
8258
|
-
let password$1;
|
|
8259
|
-
try {
|
|
8260
|
-
password$1 = await password({
|
|
8261
|
-
message: "Password:",
|
|
8262
|
-
mask: true,
|
|
8263
|
-
validate: (raw) => raw.length > 0 ? true : "password is required"
|
|
8264
|
-
});
|
|
8265
|
-
} catch (err) {
|
|
8266
|
-
if (err instanceof Error && err.name === "ExitPromptError") return;
|
|
8267
|
-
process.stderr.write(`Could not read password: ${err.message}\n`);
|
|
8268
|
-
return;
|
|
8269
|
-
}
|
|
8270
|
-
try {
|
|
8271
|
-
await save(email, password$1);
|
|
8272
|
-
process.stdout.write("Saved. Next `aitcc login` will run headlessly.\n");
|
|
8273
|
-
} catch (err) {
|
|
8274
|
-
process.stderr.write(`Could not save credentials: ${err.message}. You can retry later with \`aitcc auth set\`.
|
|
8275
|
-
`);
|
|
8276
|
-
}
|
|
8492
|
+
async function readStdinAll() {
|
|
8493
|
+
if (process.stdin.isTTY) throw new Error("--password-stdin requires stdin to be a pipe, not a TTY.");
|
|
8494
|
+
process.stdin.setEncoding("utf8");
|
|
8495
|
+
let buf = "";
|
|
8496
|
+
for await (const chunk of process.stdin) buf += chunk;
|
|
8497
|
+
return buf;
|
|
8498
|
+
}
|
|
8499
|
+
function stripTrailingNewline(s) {
|
|
8500
|
+
return s.replace(/\r?\n$/, "");
|
|
8501
|
+
}
|
|
8502
|
+
function isPromptCancelled(err) {
|
|
8503
|
+
return err instanceof Error && err.name === "ExitPromptError";
|
|
8277
8504
|
}
|
|
8278
8505
|
async function waitForLanding(client, sessionId, timeoutMs) {
|
|
8279
8506
|
return await new Promise((resolve) => {
|
|
@@ -8337,18 +8564,25 @@ async function resolveUserWithRetry(cookies, opts = {}) {
|
|
|
8337
8564
|
const logoutCommand = defineCommand({
|
|
8338
8565
|
meta: {
|
|
8339
8566
|
name: "logout",
|
|
8340
|
-
description: "Delete the local session file."
|
|
8567
|
+
description: "Delete the local session file (and optionally the saved credentials)."
|
|
8568
|
+
},
|
|
8569
|
+
args: {
|
|
8570
|
+
json: {
|
|
8571
|
+
type: "boolean",
|
|
8572
|
+
description: "Emit machine-readable JSON to stdout.",
|
|
8573
|
+
default: false
|
|
8574
|
+
},
|
|
8575
|
+
purge: {
|
|
8576
|
+
type: "boolean",
|
|
8577
|
+
description: "Also delete saved keychain credentials and the auth-state pointer.",
|
|
8578
|
+
default: false
|
|
8579
|
+
}
|
|
8341
8580
|
},
|
|
8342
|
-
args: { json: {
|
|
8343
|
-
type: "boolean",
|
|
8344
|
-
description: "Emit machine-readable JSON to stdout.",
|
|
8345
|
-
default: false
|
|
8346
|
-
} },
|
|
8347
8581
|
async run({ args }) {
|
|
8348
8582
|
const path = sessionPathForDiagnostics();
|
|
8349
|
-
let
|
|
8583
|
+
let sessionRemoved;
|
|
8350
8584
|
try {
|
|
8351
|
-
|
|
8585
|
+
sessionRemoved = (await clearSession()).existed;
|
|
8352
8586
|
} catch (err) {
|
|
8353
8587
|
const message = err.message;
|
|
8354
8588
|
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
@@ -8360,13 +8594,29 @@ const logoutCommand = defineCommand({
|
|
|
8360
8594
|
process.stderr.write(`Failed to remove session file at ${path}: ${message}\n`);
|
|
8361
8595
|
return exitAfterFlush(ExitCode.Generic);
|
|
8362
8596
|
}
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
})
|
|
8368
|
-
|
|
8369
|
-
|
|
8597
|
+
let credentialsPurged = false;
|
|
8598
|
+
let purgeError = null;
|
|
8599
|
+
if (args.purge) try {
|
|
8600
|
+
credentialsPurged = (await deleteCredentials()).existed;
|
|
8601
|
+
} catch (err) {
|
|
8602
|
+
purgeError = err.message;
|
|
8603
|
+
}
|
|
8604
|
+
if (args.json) {
|
|
8605
|
+
const payload = {
|
|
8606
|
+
ok: true,
|
|
8607
|
+
sessionRemoved,
|
|
8608
|
+
credentialsPurged,
|
|
8609
|
+
path
|
|
8610
|
+
};
|
|
8611
|
+
if (purgeError !== null) payload.purgeError = purgeError;
|
|
8612
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
8613
|
+
} else {
|
|
8614
|
+
if (sessionRemoved) process.stdout.write(`Logged out. Session removed from ${path}\n`);
|
|
8615
|
+
else process.stdout.write(`No active session at ${path}.\n`);
|
|
8616
|
+
if (args.purge) if (purgeError !== null) process.stderr.write(`Could not delete saved credentials: ${purgeError}\n`);
|
|
8617
|
+
else if (credentialsPurged) process.stdout.write("Saved credentials deleted from the OS keychain.\n");
|
|
8618
|
+
else process.stdout.write("No saved credentials to delete.\n");
|
|
8619
|
+
}
|
|
8370
8620
|
return exitAfterFlush(ExitCode.Ok);
|
|
8371
8621
|
}
|
|
8372
8622
|
});
|
|
@@ -8901,7 +9151,7 @@ function resolveVersion() {
|
|
|
8901
9151
|
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
8902
9152
|
} catch {}
|
|
8903
9153
|
try {
|
|
8904
|
-
return "0.1.
|
|
9154
|
+
return "0.1.26";
|
|
8905
9155
|
} catch {}
|
|
8906
9156
|
return "0.0.0-dev";
|
|
8907
9157
|
}
|
|
@@ -9273,6 +9523,22 @@ function maybeEmitNotice(entry, env) {
|
|
|
9273
9523
|
}
|
|
9274
9524
|
//#endregion
|
|
9275
9525
|
//#region src/commands/whoami.ts
|
|
9526
|
+
async function describeCredentialSource() {
|
|
9527
|
+
const active = await getActiveCredentialEmail().catch(() => null);
|
|
9528
|
+
if (!active) return {
|
|
9529
|
+
source: "none",
|
|
9530
|
+
email: null
|
|
9531
|
+
};
|
|
9532
|
+
return {
|
|
9533
|
+
source: active.kind,
|
|
9534
|
+
email: active.email
|
|
9535
|
+
};
|
|
9536
|
+
}
|
|
9537
|
+
function formatCredentials(cred) {
|
|
9538
|
+
if (cred.source === "none") return "none (run `aitcc login` to save)";
|
|
9539
|
+
if (cred.source === "env") return `env (AITCC_EMAIL${cred.email ? ` = ${cred.email}` : ""})`;
|
|
9540
|
+
return `keychain${cred.email ? ` (${cred.email})` : ""}`;
|
|
9541
|
+
}
|
|
9276
9542
|
async function runBackgroundUpdateCheck(json) {
|
|
9277
9543
|
if (json) return;
|
|
9278
9544
|
const timeoutMs = 500;
|
|
@@ -9300,14 +9566,18 @@ const whoamiCommand = defineCommand({
|
|
|
9300
9566
|
},
|
|
9301
9567
|
async run({ args }) {
|
|
9302
9568
|
const session = await readSession();
|
|
9569
|
+
const cred = await describeCredentialSource();
|
|
9303
9570
|
if (!session) {
|
|
9304
9571
|
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
9305
9572
|
ok: true,
|
|
9306
|
-
authenticated: false
|
|
9573
|
+
authenticated: false,
|
|
9574
|
+
credentialSource: cred.source,
|
|
9575
|
+
...cred.email ? { credentialEmail: cred.email } : {}
|
|
9307
9576
|
})}\n`);
|
|
9308
9577
|
else {
|
|
9309
9578
|
process.stderr.write("Not logged in. Run `aitcc login` to start a session.\n");
|
|
9310
9579
|
process.stderr.write(`Session file checked: ${sessionPathForDiagnostics()}\n`);
|
|
9580
|
+
process.stderr.write(`Credentials: ${formatCredentials(cred)}\n`);
|
|
9311
9581
|
}
|
|
9312
9582
|
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
9313
9583
|
}
|
|
@@ -9318,12 +9588,15 @@ const whoamiCommand = defineCommand({
|
|
|
9318
9588
|
authenticated: true,
|
|
9319
9589
|
source: "cache",
|
|
9320
9590
|
user: session.user,
|
|
9321
|
-
capturedAt: session.capturedAt
|
|
9591
|
+
capturedAt: session.capturedAt,
|
|
9592
|
+
credentialSource: cred.source,
|
|
9593
|
+
...cred.email ? { credentialEmail: cred.email } : {}
|
|
9322
9594
|
})}\n`);
|
|
9323
9595
|
return exitAfterFlush(ExitCode.Ok);
|
|
9324
9596
|
}
|
|
9325
9597
|
const label = session.user.displayName ? `${session.user.displayName} <${session.user.email}>` : session.user.email;
|
|
9326
9598
|
process.stdout.write(`Logged in as ${label} (cached)\n`);
|
|
9599
|
+
process.stdout.write(`Credentials: ${formatCredentials(cred)}\n`);
|
|
9327
9600
|
process.stdout.write(`Session captured: ${session.capturedAt}\n`);
|
|
9328
9601
|
await runBackgroundUpdateCheck(args.json);
|
|
9329
9602
|
return exitAfterFlush(ExitCode.Ok);
|
|
@@ -9347,11 +9620,14 @@ const whoamiCommand = defineCommand({
|
|
|
9347
9620
|
workspaceName: w.workspaceName,
|
|
9348
9621
|
role: w.role
|
|
9349
9622
|
})),
|
|
9350
|
-
capturedAt: session.capturedAt
|
|
9623
|
+
capturedAt: session.capturedAt,
|
|
9624
|
+
credentialSource: cred.source,
|
|
9625
|
+
...cred.email ? { credentialEmail: cred.email } : {}
|
|
9351
9626
|
})}\n`);
|
|
9352
9627
|
return exitAfterFlush(ExitCode.Ok);
|
|
9353
9628
|
}
|
|
9354
9629
|
process.stdout.write(`Logged in as ${info.name} <${info.email}> (${info.role})\n`);
|
|
9630
|
+
process.stdout.write(`Credentials: ${formatCredentials(cred)}\n`);
|
|
9355
9631
|
if (info.workspaces.length > 0) {
|
|
9356
9632
|
process.stdout.write("Workspaces:\n");
|
|
9357
9633
|
for (const w of info.workspaces) process.stdout.write(` - ${w.workspaceName} (id ${w.workspaceId}, ${w.role})\n`);
|
|
@@ -9364,9 +9640,14 @@ const whoamiCommand = defineCommand({
|
|
|
9364
9640
|
ok: true,
|
|
9365
9641
|
authenticated: false,
|
|
9366
9642
|
reason: "session-expired",
|
|
9367
|
-
errorCode: err.errorCode
|
|
9643
|
+
errorCode: err.errorCode,
|
|
9644
|
+
credentialSource: cred.source,
|
|
9645
|
+
...cred.email ? { credentialEmail: cred.email } : {}
|
|
9368
9646
|
})}\n`);
|
|
9369
|
-
else
|
|
9647
|
+
else {
|
|
9648
|
+
process.stderr.write("Session is no longer valid. Run `aitcc login` again.\n");
|
|
9649
|
+
process.stderr.write(`Credentials: ${formatCredentials(cred)}\n`);
|
|
9650
|
+
}
|
|
9370
9651
|
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
9371
9652
|
}
|
|
9372
9653
|
if (err instanceof NetworkError) {
|