fix(cli): read commander's camelCase keys for kebab-case flags

commander stores --code-file as opts.codeFile (kebab keys are never set),
so 'action update --code-file' silently no-opped (workflow.ts read
opts['code-file']) and 'auth token create --base-ids/--full-access'
never applied (auth.ts read the kebab keys). The 'action add' path was
unaffected because buildActionConfig's pick() checks both forms.

- workflow.ts action update: opts['code-file'] ?? opts.codeFile
- auth.ts token create: baseIds/fullAccess dual-form read
- verified live: action update --code-file now persists the new script
  (was silently keeping old code); commander dual-key behavior confirmed
  by direct parse (kebab keys absent, camelCase present, and this
  commander version drops the --base-ids VALUE entirely — the dual read
  covers both)
This commit is contained in:
yeuimu
2026-08-16 17:47:17 +00:00
parent e1a91dfa84
commit 3a03e0753c
4 changed files with 33 additions and 11 deletions
+7 -3
View File
@@ -95,16 +95,20 @@ function authCommand() {
.option('--full-access', 'Grant full access') .option('--full-access', 'Grant full access')
.option('--expires <date>', 'Expiry date YYYY-MM-DD (default 2099-12-31)', '2099-12-31') .option('--expires <date>', 'Expiry date YYYY-MM-DD (default 2099-12-31)', '2099-12-31')
.action((0, ctx_1.run)(async (ctx, _args, o) => { .action((0, ctx_1.run)(async (ctx, _args, o) => {
// commander stores --kebab-case flags camelCased (--base-ids ->
// opts.baseIds, --full-access -> opts.fullAccess); accept both.
const opts = o; const opts = o;
const scopes = opts.scopes.split(',').map((s) => s.trim()).filter(Boolean); const scopes = opts.scopes.split(',').map((s) => s.trim()).filter(Boolean);
const baseIds = opts['base-ids'] const rawBaseIds = opts['base-ids'] ?? opts.baseIds;
? opts['base-ids'].split(',').map((s) => s.trim()) const baseIds = rawBaseIds
? rawBaseIds.split(',').map((s) => s.trim())
: undefined; : undefined;
const fullAccess = Boolean(opts['full-access'] ?? opts.fullAccess);
const created = (await ctx.client.createAccessToken({ const created = (await ctx.client.createAccessToken({
name: opts.name, name: opts.name,
scopes, scopes,
baseIds, baseIds,
hasFullAccess: !!opts['full-access'], hasFullAccess: fullAccess,
expiredTime: opts.expires, expiredTime: opts.expires,
})); }));
if (ctx.json) if (ctx.json)
+5 -2
View File
@@ -463,8 +463,11 @@ function actionCommand() {
else { else {
// Granular update: merge only the flags the user actually passed. // Granular update: merge only the flags the user actually passed.
config = { ...config }; config = { ...config };
if (opts.code !== undefined || opts['code-file'] !== undefined) { // commander stores --kebab-case flags camelCased (--code-file ->
config.code = opts['code-file'] ? readFile(opts['code-file']) : opts.code; // opts.codeFile); accept both forms like buildActionConfig's pick().
const codeFile = opts['code-file'] ?? opts.codeFile;
if (opts.code !== undefined || codeFile !== undefined) {
config.code = codeFile ? readFile(codeFile) : opts.code;
} }
if (opts.timeout !== undefined) if (opts.timeout !== undefined)
config.timeout = Number(opts.timeout); config.timeout = Number(opts.timeout);
+16 -4
View File
@@ -107,16 +107,28 @@ export function authCommand(): Command {
.option('--expires <date>', 'Expiry date YYYY-MM-DD (default 2099-12-31)', '2099-12-31') .option('--expires <date>', 'Expiry date YYYY-MM-DD (default 2099-12-31)', '2099-12-31')
.action( .action(
run(async (ctx, _args, o) => { run(async (ctx, _args, o) => {
const opts = o as { name: string; scopes: string; 'base-ids'?: string; 'full-access'?: boolean; expires: string }; // commander stores --kebab-case flags camelCased (--base-ids ->
// opts.baseIds, --full-access -> opts.fullAccess); accept both.
const opts = o as {
name: string;
scopes: string;
'base-ids'?: string;
baseIds?: string;
'full-access'?: boolean;
fullAccess?: boolean;
expires: string;
};
const scopes = opts.scopes.split(',').map((s) => s.trim()).filter(Boolean); const scopes = opts.scopes.split(',').map((s) => s.trim()).filter(Boolean);
const baseIds = opts['base-ids'] const rawBaseIds = opts['base-ids'] ?? opts.baseIds;
? opts['base-ids'].split(',').map((s) => s.trim()) const baseIds = rawBaseIds
? rawBaseIds.split(',').map((s) => s.trim())
: undefined; : undefined;
const fullAccess = Boolean(opts['full-access'] ?? opts.fullAccess);
const created = (await ctx.client.createAccessToken({ const created = (await ctx.client.createAccessToken({
name: opts.name, name: opts.name,
scopes, scopes,
baseIds, baseIds,
hasFullAccess: !!opts['full-access'], hasFullAccess: fullAccess,
expiredTime: opts.expires, expiredTime: opts.expires,
})) as { id: string; token: string }; })) as { id: string; token: string };
if (ctx.json) printJson(created); if (ctx.json) printJson(created);
+5 -2
View File
@@ -523,8 +523,11 @@ export function actionCommand(): Command {
else { else {
// Granular update: merge only the flags the user actually passed. // Granular update: merge only the flags the user actually passed.
config = { ...config }; config = { ...config };
if (opts.code !== undefined || opts['code-file'] !== undefined) { // commander stores --kebab-case flags camelCased (--code-file ->
config.code = opts['code-file'] ? readFile(opts['code-file']) : opts.code; // opts.codeFile); accept both forms like buildActionConfig's pick().
const codeFile = opts['code-file'] ?? opts.codeFile;
if (opts.code !== undefined || codeFile !== undefined) {
config.code = codeFile ? readFile(codeFile) : opts.code;
} }
if (opts.timeout !== undefined) config.timeout = Number(opts.timeout); if (opts.timeout !== undefined) config.timeout = Number(opts.timeout);
if (opts.dependencies !== undefined) { if (opts.dependencies !== undefined) {