Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/commands/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@ import { BaseCommand } from "../lib/base-command";
export default class CurrentCommand extends BaseCommand {
static description = "Show the currently active account name";

static flags = {
...BaseCommand.jsonFlag,
} as const;

async run(): Promise<void> {
const { flags } = await this.parse(CurrentCommand);
this.setJsonMode(flags);

await this.runSafe(async () => {
const name = await this.accounts.getCurrentAccountName();
this.log(name ?? "No Codex account is active yet.");
this.emit({ active: name ?? null }, (data) => {
this.log(data.active ?? "No Codex account is active yet.");
});
});
}
}
53 changes: 26 additions & 27 deletions src/commands/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,46 +21,45 @@ export default class ListCommand extends BaseCommand {
description: "Show per-account mapping metadata (email/account/user/type/usage)",
default: false,
}),
...BaseCommand.jsonFlag,
} as const;

async run(): Promise<void> {
const { flags } = await this.parse(ListCommand);
this.setJsonMode(flags);
const detailed = Boolean(flags.details);

await this.runSafe(async () => {
const { flags } = await this.parse(ListCommand);
const detailed = Boolean(flags.details);
await this.maybeOfferGlobalUpdate();
// Interactive update prompt would corrupt JSON stdout; skip it.
if (!this.jsonMode) {
await this.maybeOfferGlobalUpdate();
}

const accounts = await this.accounts.listAccountMappings({ refreshUsage: "missing" });

if (!detailed) {
const accounts = await this.accounts.listAccountMappings({ refreshUsage: "missing" });
if (!accounts.length) {
this.emit({ accounts, detailed }, (payload) => {
if (!payload.accounts.length) {
this.log("No saved Codex accounts yet. Run `authmux save <name>`.");
return;
}

for (const account of accounts) {
for (const account of payload.accounts) {
const mark = account.active ? "*" : " ";
if (!payload.detailed) {
this.log(
`${mark} ${account.name} type=${formatAccountType(account.planType)} 5h=${this.formatRemaining(account.remaining5hPercent)} weekly=${this.formatRemaining(account.remainingWeeklyPercent)}`,
);
continue;
}
this.log(`${mark} ${account.name}`);
this.log(
` email=${account.email ?? "-"} account=${account.accountId ?? "-"} user=${account.userId ?? "-"}`,
);
this.log(
`${mark} ${account.name} type=${formatAccountType(account.planType)} 5h=${this.formatRemaining(account.remaining5hPercent)} weekly=${this.formatRemaining(account.remainingWeeklyPercent)}`,
` type=${formatAccountType(account.planType)} plan=${account.planType ?? "-"} usage=${account.usageSource ?? "-"} 5h=${this.formatRemaining(account.remaining5hPercent)} weekly=${this.formatRemaining(account.remainingWeeklyPercent)} lastUsageAt=${account.lastUsageAt ?? "-"}`,
);
}
return;
}

const accounts = await this.accounts.listAccountMappings({ refreshUsage: "missing" });
if (!accounts.length) {
this.log("No saved Codex accounts yet. Run `authmux save <name>`.");
return;
}

for (const account of accounts) {
const mark = account.active ? "*" : " ";
this.log(`${mark} ${account.name}`);
this.log(
` email=${account.email ?? "-"} account=${account.accountId ?? "-"} user=${account.userId ?? "-"}`,
);
this.log(
` type=${formatAccountType(account.planType)} plan=${account.planType ?? "-"} usage=${account.usageSource ?? "-"} 5h=${this.formatRemaining(account.remaining5hPercent)} weekly=${this.formatRemaining(account.remainingWeeklyPercent)} lastUsageAt=${account.lastUsageAt ?? "-"}`,
);
}
});
});
}

Expand Down
33 changes: 23 additions & 10 deletions src/commands/save.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,40 @@ export default class SaveCommand extends BaseCommand {
"Force overwrite when the existing snapshot name belongs to a different email account",
default: false,
}),
...BaseCommand.jsonFlag,
} as const;

async run(): Promise<void> {
const { args, flags } = await this.parse(SaveCommand);
this.setJsonMode(flags);

await this.runSafe(async () => {
const { args, flags } = await this.parse(SaveCommand);
const providedName = args.name as string | undefined;
const resolvedName = providedName
? { name: providedName, source: "explicit" as const, forceOverwrite: false }
: await this.accounts.resolveDefaultAccountNameFromCurrentAuth();
const savedName = await this.accounts.saveAccount(resolvedName.name, {
force: Boolean(flags.force || resolvedName.forceOverwrite),
});
const suffix =
resolvedName.source === "explicit"
? ""
: resolvedName.source === "active"
? " (reused active account name)"
: resolvedName.source === "existing"
? " (reused saved account name)"
: " (inferred from auth email)";
this.log(`Saved current Codex auth tokens as "${savedName}"${suffix}.`);

this.emit(
{
saved: savedName,
source: resolvedName.source,
forced: Boolean(flags.force || resolvedName.forceOverwrite),
},
(data) => {
const suffix =
data.source === "explicit"
? ""
: data.source === "active"
? " (reused active account name)"
: data.source === "existing"
? " (reused saved account name)"
: " (inferred from auth email)";
this.log(`Saved current Codex auth tokens as "${data.saved}"${suffix}.`);
},
);
});
}
}
17 changes: 13 additions & 4 deletions src/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@ import { BaseCommand } from "../lib/base-command";
export default class StatusCommand extends BaseCommand {
static description = "Show auto-switch, service, and usage status";

static flags = {
...BaseCommand.jsonFlag,
} as const;

async run(): Promise<void> {
const { flags } = await this.parse(StatusCommand);
this.setJsonMode(flags);

await this.runSafe(async () => {
const status = await this.accounts.getStatus();
this.log(`auto-switch: ${status.autoSwitchEnabled ? "ON" : "OFF"}`);
this.log(`service: ${status.serviceState}`);
this.log(`thresholds: 5h<${status.threshold5hPercent}%, weekly<${status.thresholdWeeklyPercent}%`);
this.log(`usage: ${status.usageMode}`);
this.emit(status, (data) => {
this.log(`auto-switch: ${data.autoSwitchEnabled ? "ON" : "OFF"}`);
this.log(`service: ${data.serviceState}`);
this.log(`thresholds: 5h<${data.threshold5hPercent}%, weekly<${data.thresholdWeeklyPercent}%`);
this.log(`usage: ${data.usageMode}`);
});
});
}
}
44 changes: 35 additions & 9 deletions src/commands/use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,22 @@ export default class UseCommand extends BaseCommand {

static flags = {
"no-kiro": Flags.boolean({ description: "Skip Kiro CLI mirror even if a matching snapshot exists" }),
...BaseCommand.jsonFlag,
} as const;

async run(): Promise<void> {
const { args, flags } = await this.parse(UseCommand);
this.setJsonMode(flags);

await this.runSafe(async () => {
const { args, flags } = await this.parse(UseCommand);
let account = args.account as string | undefined;

if (!account) {
if (this.jsonMode) {
// No interactive prompt allowed under --json: stdout would
// be corrupted by the prompt UI.
throw new PromptCancelledError();
}
account = await this.promptForAccount();
}

Expand All @@ -37,20 +45,38 @@ export default class UseCommand extends BaseCommand {
activated = await this.accounts.useAccount(account);
recordSuccess(activated);
recordSwitch();
this.log(`Switched Codex auth to "${activated}".`);
} catch (err) {
recordFailure(account);
throw err;
}

if (flags["no-kiro"]) return;

const mirror = switchKiroSnapshot(activated);
if (mirror.switched) {
this.log(`Mirrored Kiro CLI to "${mirror.active}".`);
} else if (mirror.attempted) {
this.warn(`Kiro mirror skipped: ${mirror.reason}`);
let mirror: { switched: boolean; attempted: boolean; active?: string; reason?: string } = {
switched: false,
attempted: false,
};
if (!flags["no-kiro"]) {
mirror = switchKiroSnapshot(activated);
}

this.emit(
{
activated,
kiro: {
attempted: mirror.attempted,
switched: mirror.switched,
active: mirror.active ?? null,
reason: mirror.reason ?? null,
},
},
(data) => {
this.log(`Switched Codex auth to "${data.activated}".`);
if (data.kiro.switched && data.kiro.active) {
this.log(`Mirrored Kiro CLI to "${data.kiro.active}".`);
} else if (data.kiro.attempted && data.kiro.reason) {
this.warn(`Kiro mirror skipped: ${data.kiro.reason}`);
}
},
);
});
}

Expand Down
Loading
Loading