padrone 1.7.0 → 1.8.0
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/CHANGELOG.md +22 -0
- package/README.md +2 -1
- package/dist/{args-Cnq0nwSM.mjs → args-DrCXxXeP.mjs} +20 -4
- package/dist/args-DrCXxXeP.mjs.map +1 -0
- package/dist/codegen/index.mjs +1 -1
- package/dist/{commands-B_gufyR9.mjs → commands-DLR0rFgq.mjs} +2 -2
- package/dist/{commands-B_gufyR9.mjs.map → commands-DLR0rFgq.mjs.map} +1 -1
- package/dist/{completion-BEuflbDO.mjs → completion-UnBKfGuk.mjs} +2 -2
- package/dist/{completion-BEuflbDO.mjs.map → completion-UnBKfGuk.mjs.map} +1 -1
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{formatter-DrvhDMrq.d.mts → formatter-CY3KrOEd.d.mts} +3 -2
- package/dist/formatter-CY3KrOEd.d.mts.map +1 -0
- package/dist/{help-BtxLgrF_.mjs → help-B-ZMYyn-.mjs} +16 -6
- package/dist/help-B-ZMYyn-.mjs.map +1 -0
- package/dist/{index-D6-7dz0l.d.mts → index-C2n3k4e8.d.mts} +332 -116
- package/dist/index-C2n3k4e8.d.mts.map +1 -0
- package/dist/index.d.mts +17 -160
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +78 -65
- package/dist/index.mjs.map +1 -1
- package/dist/{mcp-6-Jw4Bpq.mjs → mcp-D6PdtjIs.mjs} +4 -4
- package/dist/{mcp-6-Jw4Bpq.mjs.map → mcp-D6PdtjIs.mjs.map} +1 -1
- package/dist/{serve-YVTPzBCl.mjs → serve-PaCLsNoD.mjs} +4 -4
- package/dist/{serve-YVTPzBCl.mjs.map → serve-PaCLsNoD.mjs.map} +1 -1
- package/dist/test.d.mts +1 -1
- package/dist/zod.d.mts +1 -1
- package/package.json +3 -3
- package/src/core/args.ts +24 -1
- package/src/core/create.ts +21 -14
- package/src/core/exec.ts +4 -3
- package/src/core/interceptors.ts +12 -6
- package/src/core/program-methods.ts +12 -2
- package/src/core/validate.ts +26 -7
- package/src/extension/auto-output.ts +1 -1
- package/src/extension/config.ts +2 -1
- package/src/extension/env.ts +5 -4
- package/src/extension/index.ts +1 -0
- package/src/extension/interactive.ts +2 -1
- package/src/extension/logger.ts +1 -1
- package/src/extension/progress.ts +2 -2
- package/src/extension/tracing.ts +1 -1
- package/src/index.ts +5 -1
- package/src/output/formatter.ts +6 -1
- package/src/output/help.ts +15 -3
- package/src/types/args-meta.ts +10 -0
- package/src/types/builder.ts +140 -38
- package/src/types/index.ts +2 -0
- package/src/types/interceptor.ts +12 -12
- package/src/util/type-utils.ts +22 -0
- package/dist/args-Cnq0nwSM.mjs.map +0 -1
- package/dist/formatter-DrvhDMrq.d.mts.map +0 -1
- package/dist/help-BtxLgrF_.mjs.map +0 -1
- package/dist/index-D6-7dz0l.d.mts.map +0 -1
package/src/core/validate.ts
CHANGED
|
@@ -51,8 +51,8 @@ export function parseCommand(input: string | undefined, rootCommand: AnyPadroneC
|
|
|
51
51
|
const argsMeta = curCommand.meta?.fields;
|
|
52
52
|
const schemaMetadata = curCommand.argsSchema
|
|
53
53
|
? extractSchemaMetadata(curCommand.argsSchema, argsMeta, curCommand.meta?.autoAlias)
|
|
54
|
-
: { flags: {}, aliases: {} };
|
|
55
|
-
const { flags, aliases } = schemaMetadata;
|
|
54
|
+
: { flags: {}, aliases: {}, negatives: {}, customNegation: new Set<string>() };
|
|
55
|
+
const { flags, aliases, negatives, customNegation } = schemaMetadata;
|
|
56
56
|
|
|
57
57
|
const arrayArguments = new Set<string>();
|
|
58
58
|
if (curCommand.argsSchema) {
|
|
@@ -77,6 +77,10 @@ export function parseCommand(input: string | undefined, rootCommand: AnyPadroneC
|
|
|
77
77
|
key = [flags[arg.key[0]!]!];
|
|
78
78
|
} else if (arg.type === 'named' && arg.key.length === 1 && aliases[arg.key[0]!]) {
|
|
79
79
|
key = [aliases[arg.key[0]!]!];
|
|
80
|
+
} else if (arg.type === 'named' && arg.key.length === 1 && negatives[arg.key[0]!]) {
|
|
81
|
+
// Negative keyword: --remote sets local to false
|
|
82
|
+
setNestedValue(rawArgs, [negatives[arg.key[0]!]!], false);
|
|
83
|
+
continue;
|
|
80
84
|
} else {
|
|
81
85
|
key = arg.key;
|
|
82
86
|
}
|
|
@@ -84,6 +88,12 @@ export function parseCommand(input: string | undefined, rootCommand: AnyPadroneC
|
|
|
84
88
|
const rootKey = key[0]!;
|
|
85
89
|
|
|
86
90
|
if (arg.type === 'named' && arg.negated) {
|
|
91
|
+
// Skip --no- prefix negation for args with custom negation
|
|
92
|
+
if (customNegation.has(rootKey)) {
|
|
93
|
+
// Treat as unknown: put it back as `no-<key>` so detectUnknownArgs catches it
|
|
94
|
+
setNestedValue(rawArgs, [`no-${key.join('.')}`], false);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
87
97
|
setNestedValue(rawArgs, key, false);
|
|
88
98
|
continue;
|
|
89
99
|
}
|
|
@@ -132,8 +142,9 @@ export function buildCommandArgs(
|
|
|
132
142
|
command: AnyPadroneCommand,
|
|
133
143
|
rawArgs: Record<string, unknown>,
|
|
134
144
|
positionalArgs: string[],
|
|
135
|
-
): Record<string, unknown
|
|
145
|
+
): { args: Record<string, unknown>; issues?: StandardSchemaV1.Issue[] } {
|
|
136
146
|
let preprocessedArgs = preprocessArgs(rawArgs, { flags: {}, aliases: {} });
|
|
147
|
+
let issues: StandardSchemaV1.Issue[] | undefined;
|
|
137
148
|
|
|
138
149
|
const positionalConfig = command.meta?.positional ? parsePositionalConfig(command.meta.positional) : [];
|
|
139
150
|
|
|
@@ -143,6 +154,13 @@ export function buildCommandArgs(
|
|
|
143
154
|
const { name, variadic } = positionalConfig[i]!;
|
|
144
155
|
if (argIndex >= positionalArgs.length) break;
|
|
145
156
|
|
|
157
|
+
// Detect ambiguity: same arg provided both positionally and as a named option
|
|
158
|
+
if (name in preprocessedArgs) {
|
|
159
|
+
issues ??= [];
|
|
160
|
+
issues.push({ path: [name], message: `Ambiguous argument "${name}": provided both positionally and as a named option` });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
146
164
|
if (variadic) {
|
|
147
165
|
const remainingPositionals = positionalConfig.slice(i + 1);
|
|
148
166
|
const nonVariadicAfter = remainingPositionals.filter((p) => !p.variadic).length;
|
|
@@ -163,7 +181,7 @@ export function buildCommandArgs(
|
|
|
163
181
|
preprocessedArgs = coerceArgs(preprocessedArgs, command.argsSchema);
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
return preprocessedArgs;
|
|
184
|
+
return { args: preprocessedArgs, issues };
|
|
167
185
|
}
|
|
168
186
|
|
|
169
187
|
/**
|
|
@@ -180,9 +198,9 @@ export function checkUnknownArgs(command: AnyPadroneCommand, preprocessedArgs: R
|
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
const argsMeta = command.meta?.fields;
|
|
183
|
-
const { flags, aliases } = extractSchemaMetadata(command.argsSchema, argsMeta, command.meta?.autoAlias);
|
|
201
|
+
const { flags, aliases, negatives } = extractSchemaMetadata(command.argsSchema, argsMeta, command.meta?.autoAlias);
|
|
184
202
|
|
|
185
|
-
return detectUnknownArgs(preprocessedArgs, command.argsSchema, flags, aliases);
|
|
203
|
+
return detectUnknownArgs(preprocessedArgs, command.argsSchema, flags, aliases, negatives);
|
|
186
204
|
}
|
|
187
205
|
|
|
188
206
|
/**
|
|
@@ -241,7 +259,8 @@ export function coreValidateForParse(
|
|
|
241
259
|
rawArgs: Record<string, unknown>,
|
|
242
260
|
positionalArgs: string[],
|
|
243
261
|
): InterceptorValidateResult | Promise<InterceptorValidateResult> {
|
|
244
|
-
const preprocessedArgs = buildCommandArgs(command, rawArgs, positionalArgs);
|
|
262
|
+
const { args: preprocessedArgs, issues } = buildCommandArgs(command, rawArgs, positionalArgs);
|
|
263
|
+
if (issues) return { args: undefined, argsResult: { issues } as any };
|
|
245
264
|
const validated = validateCommandArgs(command, preprocessedArgs);
|
|
246
265
|
return thenMaybe(validated, (v) => v as InterceptorValidateResult);
|
|
247
266
|
}
|
|
@@ -88,7 +88,7 @@ function createAutoOutputInterceptor(outputConfig?: OutputConfig) {
|
|
|
88
88
|
return { result: collected };
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
const executedOrPromise = next({ context: {
|
|
91
|
+
const executedOrPromise = next({ context: { output: indicator } });
|
|
92
92
|
if (executedOrPromise instanceof Promise) return executedOrPromise.then(handleResult);
|
|
93
93
|
return handleResult(executedOrPromise);
|
|
94
94
|
},
|
package/src/extension/config.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ConfigError } from '../core/errors.ts';
|
|
|
4
4
|
import { defineInterceptor } from '../core/interceptors.ts';
|
|
5
5
|
import { thenMaybe } from '../core/results.ts';
|
|
6
6
|
import type { AnyPadroneBuilder, CommandTypesBase, InterceptorValidateContext } from '../types/index.ts';
|
|
7
|
+
import type { WithAsync } from '../util/type-utils.ts';
|
|
7
8
|
import { getRootCommand } from '../util/utils.ts';
|
|
8
9
|
|
|
9
10
|
// ── Types ────────────────────────────────────────────────────────────────
|
|
@@ -189,7 +190,7 @@ function loadConfig(
|
|
|
189
190
|
* }))
|
|
190
191
|
* ```
|
|
191
192
|
*/
|
|
192
|
-
export function padroneConfig(options?: PadroneConfigOptions): <T extends CommandTypesBase>(builder: T) => T {
|
|
193
|
+
export function padroneConfig(options?: PadroneConfigOptions): <T extends CommandTypesBase>(builder: T) => WithAsync<T> {
|
|
193
194
|
if (options?.disabled) {
|
|
194
195
|
const disabled = defineInterceptor({ id: 'padrone:config', name: 'padrone:config', order: -999, disabled: true }, () => ({}));
|
|
195
196
|
return ((builder: AnyPadroneBuilder) => builder.intercept(disabled)) as any;
|
package/src/extension/env.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { thenMaybe } from '../core/results.ts';
|
|
|
5
5
|
import type { AnyPadroneBuilder, CommandTypesBase, InterceptorValidateContext } from '../types/index.ts';
|
|
6
6
|
import type { LoadEnvFilesOptions } from '../util/dotenv.ts';
|
|
7
7
|
import { loadEnvFiles } from '../util/dotenv.ts';
|
|
8
|
+
import type { WithAsync } from '../util/type-utils.ts';
|
|
8
9
|
|
|
9
10
|
// ── Types ────────────────────────────────────────────────────────────────
|
|
10
11
|
|
|
@@ -53,13 +54,13 @@ function isSchema(value: unknown): value is StandardSchemaV1 {
|
|
|
53
54
|
*
|
|
54
55
|
* Env values have lower precedence than CLI args and stdin, but higher than config files.
|
|
55
56
|
*/
|
|
56
|
-
export function padroneEnv(schema: StandardSchemaV1): <T extends CommandTypesBase>(builder: T) => T
|
|
57
|
-
export function padroneEnv(schema: StandardSchemaV1, options: PadroneEnvOptions): <T extends CommandTypesBase>(builder: T) => T
|
|
58
|
-
export function padroneEnv(options: PadroneEnvOptions): <T extends CommandTypesBase>(builder: T) => T
|
|
57
|
+
export function padroneEnv(schema: StandardSchemaV1): <T extends CommandTypesBase>(builder: T) => WithAsync<T>;
|
|
58
|
+
export function padroneEnv(schema: StandardSchemaV1, options: PadroneEnvOptions): <T extends CommandTypesBase>(builder: T) => WithAsync<T>;
|
|
59
|
+
export function padroneEnv(options: PadroneEnvOptions): <T extends CommandTypesBase>(builder: T) => WithAsync<T>;
|
|
59
60
|
export function padroneEnv(
|
|
60
61
|
schemaOrOptions: StandardSchemaV1 | PadroneEnvOptions,
|
|
61
62
|
maybeOptions?: PadroneEnvOptions,
|
|
62
|
-
): <T extends CommandTypesBase>(builder: T) => T {
|
|
63
|
+
): <T extends CommandTypesBase>(builder: T) => WithAsync<T> {
|
|
63
64
|
const schema = isSchema(schemaOrOptions) ? schemaOrOptions : undefined;
|
|
64
65
|
const options = isSchema(schemaOrOptions) ? maybeOptions : schemaOrOptions;
|
|
65
66
|
const hasFiles = options?.modes !== undefined;
|
package/src/extension/index.ts
CHANGED
|
@@ -37,7 +37,8 @@ const interactiveInterceptor = defineInterceptor({ id: 'padrone:interactive', na
|
|
|
37
37
|
if (!willPrompt) return next();
|
|
38
38
|
|
|
39
39
|
// Preprocess args to determine what's missing
|
|
40
|
-
const preprocessedArgs = buildCommandArgs(command, ctx.rawArgs, ctx.positionalArgs);
|
|
40
|
+
const { args: preprocessedArgs, issues: positionalIssues } = buildCommandArgs(command, ctx.rawArgs, ctx.positionalArgs);
|
|
41
|
+
if (positionalIssues) return { args: undefined, argsResult: { issues: positionalIssues } } as any;
|
|
41
42
|
|
|
42
43
|
// Check for unknown args before prompting
|
|
43
44
|
const unknowns = checkUnknownArgs(command, preprocessedArgs);
|
package/src/extension/logger.ts
CHANGED
|
@@ -209,7 +209,7 @@ function loggerInterceptor(rawConfig?: PadroneLoggerConfig) {
|
|
|
209
209
|
timestamps: rawConfig?.timestamps ?? ctxCfg?.timestamps ?? false,
|
|
210
210
|
};
|
|
211
211
|
const logger = createLogger(ctx.runtime, resolved.level, resolved, ctx.context?.tracing);
|
|
212
|
-
return next({ context: {
|
|
212
|
+
return next({ context: { logger } });
|
|
213
213
|
},
|
|
214
214
|
};
|
|
215
215
|
});
|
|
@@ -201,7 +201,7 @@ function progressInterceptor(config: string | PadroneProgressConfig) {
|
|
|
201
201
|
return next();
|
|
202
202
|
},
|
|
203
203
|
|
|
204
|
-
execute(
|
|
204
|
+
execute(_ctx, next) {
|
|
205
205
|
// Transition from validation message to progress message
|
|
206
206
|
if (indicator && msgs!.validation) indicator.update(msgs!.progress);
|
|
207
207
|
|
|
@@ -221,7 +221,7 @@ function progressInterceptor(config: string | PadroneProgressConfig) {
|
|
|
221
221
|
|
|
222
222
|
let result: any;
|
|
223
223
|
try {
|
|
224
|
-
result = next({ context: {
|
|
224
|
+
result = next({ context: { progress: effectiveIndicator } });
|
|
225
225
|
} catch (err) {
|
|
226
226
|
onError(err);
|
|
227
227
|
}
|
package/src/extension/tracing.ts
CHANGED
|
@@ -109,7 +109,7 @@ function tracingInterceptor(config: ResolvedTracingConfig) {
|
|
|
109
109
|
},
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
-
return next({ context: {
|
|
112
|
+
return next({ context: { tracing: padroneTracer } });
|
|
113
113
|
},
|
|
114
114
|
|
|
115
115
|
error(ctx, next) {
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type {
|
|
|
38
38
|
PadroneTracer,
|
|
39
39
|
PadroneTracingConfig,
|
|
40
40
|
VersionCommand,
|
|
41
|
+
WithAsync,
|
|
41
42
|
WithCompletion,
|
|
42
43
|
WithHelp,
|
|
43
44
|
WithLogger,
|
|
@@ -79,7 +80,7 @@ export type { UpdateCheckConfig } from './feature/update-check.ts';
|
|
|
79
80
|
export type { WrapConfig, WrapResult } from './feature/wrap.ts';
|
|
80
81
|
export type { AnsiStyle, ColorConfig, ColorTheme } from './output/colorizer.ts';
|
|
81
82
|
export { colorThemes } from './output/colorizer.ts';
|
|
82
|
-
export type { HelpInfo } from './output/formatter.ts';
|
|
83
|
+
export type { HelpDetail, HelpFormat, HelpInfo } from './output/formatter.ts';
|
|
83
84
|
export type { PadroneOutputIndicator } from './output/output-indicator.ts';
|
|
84
85
|
export type { KeyValueOptions, ListItem, ListOptions, TableOptions, TreeNode, TreeOptions } from './output/primitives.ts';
|
|
85
86
|
export type { OutputContext, OutputFormat } from './output/styling.ts';
|
|
@@ -90,6 +91,8 @@ export type {
|
|
|
90
91
|
AsyncPadroneSchema,
|
|
91
92
|
CommandTypesBase,
|
|
92
93
|
DefineCommand,
|
|
94
|
+
DefineCommandBuilder,
|
|
95
|
+
DefineCommandContext,
|
|
93
96
|
ExtractInterceptorContext,
|
|
94
97
|
ExtractInterceptorRequires,
|
|
95
98
|
GetArgsMeta,
|
|
@@ -121,6 +124,7 @@ export type {
|
|
|
121
124
|
PadroneProgram,
|
|
122
125
|
PadroneProgramMeta,
|
|
123
126
|
PadroneSchema,
|
|
127
|
+
RegisteredInterceptor,
|
|
124
128
|
} from './types/index.ts';
|
|
125
129
|
export type { AsyncStreamMeta } from './util/stream.ts';
|
|
126
130
|
export { asyncStream } from './util/stream.ts';
|
package/src/output/formatter.ts
CHANGED
|
@@ -58,6 +58,8 @@ export type HelpArgumentInfo = {
|
|
|
58
58
|
variadic?: boolean;
|
|
59
59
|
/** Whether this arg is a boolean (shown as --[no-]arg) */
|
|
60
60
|
negatable?: boolean;
|
|
61
|
+
/** Custom negative keyword(s) that set this arg to false (e.g. `['remote']` for `--remote`) */
|
|
62
|
+
negative?: string[];
|
|
61
63
|
/** Config file key that maps to this arg */
|
|
62
64
|
configKey?: string;
|
|
63
65
|
/** Group name for organizing this option under a labeled section in help output */
|
|
@@ -316,7 +318,10 @@ function createGenericFormatter(styler: Styler, layout: LayoutConfig, showAllBui
|
|
|
316
318
|
const remainingAliases = arg.aliases?.filter((a) => a !== primaryName);
|
|
317
319
|
|
|
318
320
|
const flagsPlain = arg.flags?.length ? arg.flags.map((f) => `-${f}`).join(', ') : '';
|
|
319
|
-
const
|
|
321
|
+
const negPlain = arg.negative?.length ? arg.negative.map((n) => `--${n}`).join(', ') : '';
|
|
322
|
+
const namesPlain = [`--${primaryName}`, ...(remainingAliases?.map((a) => `--${a}`) || []), ...(negPlain ? [negPlain] : [])].join(
|
|
323
|
+
', ',
|
|
324
|
+
);
|
|
320
325
|
const typePlain = arg.type && arg.type !== 'boolean' ? (arg.optional ? `[${arg.type}]` : `<${arg.type}>`) : '';
|
|
321
326
|
|
|
322
327
|
const isDeprecated = !!arg.deprecated;
|
package/src/output/help.ts
CHANGED
|
@@ -128,9 +128,20 @@ function extractArgsInfo(schema: StandardJSONSchemaV1, meta?: PadroneArgsSchemaM
|
|
|
128
128
|
const optMeta = argsMeta?.[key];
|
|
129
129
|
const propType = prop.type as string;
|
|
130
130
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
const
|
|
131
|
+
// Resolve custom negative keywords from meta or schema
|
|
132
|
+
const rawNegative = optMeta?.negative ?? prop?.negative;
|
|
133
|
+
const hasCustomNegative = rawNegative !== undefined;
|
|
134
|
+
const negativeList = hasCustomNegative
|
|
135
|
+
? typeof rawNegative === 'string'
|
|
136
|
+
? rawNegative
|
|
137
|
+
? [rawNegative]
|
|
138
|
+
: []
|
|
139
|
+
: Array.from(rawNegative as readonly string[]).filter(Boolean)
|
|
140
|
+
: undefined;
|
|
141
|
+
|
|
142
|
+
// Booleans are negatable unless there's an explicit noArg property,
|
|
143
|
+
// this arg is itself a negation of another arg, or custom negative keywords are set
|
|
144
|
+
const isNegatable = propType === 'boolean' && !hasCustomNegative && !hasExplicitNegation(key) && !isNegationOf(key);
|
|
134
145
|
|
|
135
146
|
result.push({
|
|
136
147
|
name: key,
|
|
@@ -144,6 +155,7 @@ function extractArgsInfo(schema: StandardJSONSchemaV1, meta?: PadroneArgsSchemaM
|
|
|
144
155
|
examples: optMeta?.examples ?? prop?.examples,
|
|
145
156
|
variadic: propType === 'array',
|
|
146
157
|
negatable: isNegatable,
|
|
158
|
+
negative: negativeList?.length ? negativeList : undefined,
|
|
147
159
|
group: optMeta?.group,
|
|
148
160
|
});
|
|
149
161
|
}
|
package/src/types/args-meta.ts
CHANGED
|
@@ -35,6 +35,16 @@ export interface PadroneFieldMeta {
|
|
|
35
35
|
flags?: readonly SingleChar[] | SingleChar;
|
|
36
36
|
/** Multi-character alternative long names. Used with double dash (e.g. `--dry-run` for `--dryRun`). */
|
|
37
37
|
alias?: readonly string[] | string;
|
|
38
|
+
/**
|
|
39
|
+
* Custom negative keyword(s) for boolean options. When provided, `--<keyword>` sets this option to `false`.
|
|
40
|
+
* Disables the default `--no-<option>` negation prefix. Set to `''` or `[]` to only disable the prefix.
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* local: z.boolean().default(true).meta({ negative: 'remote' })
|
|
44
|
+
* // --remote sets local to false, --no-local is disabled
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
negative?: readonly string[] | string;
|
|
38
48
|
deprecated?: boolean | string;
|
|
39
49
|
hidden?: boolean;
|
|
40
50
|
examples?: readonly unknown[];
|
package/src/types/builder.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
import type { Tool } from 'ai';
|
|
3
|
-
import type { PadroneRuntime } from '../core/runtime.ts';
|
|
3
|
+
import type { PadroneProgressIndicator, PadroneRuntime } from '../core/runtime.ts';
|
|
4
|
+
import type { PadroneLogger } from '../extension/logger.ts';
|
|
5
|
+
import type { PadroneTracer } from '../extension/tracing.ts';
|
|
4
6
|
import type { PadroneMcpPreferences } from '../feature/mcp.ts';
|
|
5
7
|
import type { PadroneServePreferences } from '../feature/serve.ts';
|
|
6
8
|
import type { WrapConfig, WrapResult } from '../feature/wrap.ts';
|
|
@@ -340,21 +342,11 @@ export type PadroneBuilderMethods<
|
|
|
340
342
|
>;
|
|
341
343
|
|
|
342
344
|
/** Add or override a subcommand. Pass a builder function to define its schema, action, and nested commands. @category Builder */
|
|
343
|
-
command:
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
TNameNested,
|
|
349
|
-
FullCommandName<TName, TParentName>,
|
|
350
|
-
TArgs,
|
|
351
|
-
TCommands,
|
|
352
|
-
TContext & TContextProvided
|
|
353
|
-
>,
|
|
354
|
-
>(
|
|
355
|
-
name: TNameNested | readonly [TNameNested, ...TAliases],
|
|
356
|
-
builderFn?: (
|
|
357
|
-
builder: InitialCommandBuilder<
|
|
345
|
+
command: {
|
|
346
|
+
<
|
|
347
|
+
TNameNested extends string,
|
|
348
|
+
TAliases extends string[] = [],
|
|
349
|
+
TBuilder extends CommandTypesBase = DefaultCommandBuilder<
|
|
358
350
|
TProgramName,
|
|
359
351
|
TNameNested,
|
|
360
352
|
FullCommandName<TName, TParentName>,
|
|
@@ -362,28 +354,94 @@ export type PadroneBuilderMethods<
|
|
|
362
354
|
TCommands,
|
|
363
355
|
TContext & TContextProvided
|
|
364
356
|
>,
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
357
|
+
>(
|
|
358
|
+
name: TNameNested | readonly [TNameNested, ...TAliases],
|
|
359
|
+
builderFn?: (
|
|
360
|
+
builder: InitialCommandBuilder<
|
|
361
|
+
TProgramName,
|
|
362
|
+
TNameNested,
|
|
363
|
+
FullCommandName<TName, TParentName>,
|
|
364
|
+
TArgs,
|
|
365
|
+
TCommands,
|
|
366
|
+
TContext & TContextProvided
|
|
367
|
+
>,
|
|
368
|
+
) => TBuilder,
|
|
369
|
+
): BuilderOrProgram<
|
|
370
|
+
TReturn,
|
|
371
|
+
TProgramName,
|
|
372
|
+
TName,
|
|
373
|
+
TParentName,
|
|
374
|
+
TArgs,
|
|
375
|
+
TRes,
|
|
376
|
+
TCommands extends []
|
|
376
377
|
? [WithAliases<TBuilder['~types']['command'], TAliases>]
|
|
377
|
-
:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
378
|
+
: AnyPadroneCommand[] extends TCommands
|
|
379
|
+
? [WithAliases<TBuilder['~types']['command'], TAliases>]
|
|
380
|
+
: ReplaceOrAppendCommand<
|
|
381
|
+
TCommands,
|
|
382
|
+
TNameNested,
|
|
383
|
+
WithAliases<TBuilder['~types']['command'], ResolvedAliases<TCommands, TNameNested, TAliases>>
|
|
384
|
+
>,
|
|
385
|
+
TParentArgs,
|
|
386
|
+
TAsync,
|
|
387
|
+
TContext,
|
|
388
|
+
TContextProvided
|
|
389
|
+
>;
|
|
390
|
+
// Overload for defineCommand.requires() branded callbacks — validates context requirements
|
|
391
|
+
<TNameNested extends string, TAliases extends string[] = [], TBuilder extends CommandTypesBase = CommandTypesBase, TReq = unknown>(
|
|
392
|
+
name: TNameNested | readonly [TNameNested, ...TAliases],
|
|
393
|
+
builderFn: ((builder: any) => TBuilder) & { '~contextRequires': (ctx: TReq) => void },
|
|
394
|
+
): TContext & TContextProvided extends TReq
|
|
395
|
+
? BuilderOrProgram<
|
|
396
|
+
TReturn,
|
|
397
|
+
TProgramName,
|
|
398
|
+
TName,
|
|
399
|
+
TParentName,
|
|
400
|
+
TArgs,
|
|
401
|
+
TRes,
|
|
402
|
+
TCommands extends []
|
|
403
|
+
? [WithAliases<TBuilder['~types']['command'], TAliases>]
|
|
404
|
+
: AnyPadroneCommand[] extends TCommands
|
|
405
|
+
? [WithAliases<TBuilder['~types']['command'], TAliases>]
|
|
406
|
+
: ReplaceOrAppendCommand<
|
|
407
|
+
TCommands,
|
|
408
|
+
TNameNested,
|
|
409
|
+
WithAliases<TBuilder['~types']['command'], ResolvedAliases<TCommands, TNameNested, TAliases>>
|
|
410
|
+
>,
|
|
411
|
+
TParentArgs,
|
|
412
|
+
TAsync,
|
|
413
|
+
TContext,
|
|
414
|
+
TContextProvided
|
|
415
|
+
>
|
|
416
|
+
: DefineCommandRequiresError;
|
|
417
|
+
// Fallback overload: accepts DefineCommand-typed callbacks where the builder type is not structurally compatible
|
|
418
|
+
// (e.g., DefineCommand with unknown context used in a parent with specific context)
|
|
419
|
+
<TNameNested extends string, TAliases extends string[] = [], TBuilder extends CommandTypesBase = CommandTypesBase>(
|
|
420
|
+
name: TNameNested | readonly [TNameNested, ...TAliases],
|
|
421
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
422
|
+
builderFn?: (builder: any) => TBuilder,
|
|
423
|
+
): BuilderOrProgram<
|
|
424
|
+
TReturn,
|
|
425
|
+
TProgramName,
|
|
426
|
+
TName,
|
|
427
|
+
TParentName,
|
|
428
|
+
TArgs,
|
|
429
|
+
TRes,
|
|
430
|
+
TCommands extends []
|
|
431
|
+
? [WithAliases<TBuilder['~types']['command'], TAliases>]
|
|
432
|
+
: AnyPadroneCommand[] extends TCommands
|
|
433
|
+
? [WithAliases<TBuilder['~types']['command'], TAliases>]
|
|
434
|
+
: ReplaceOrAppendCommand<
|
|
435
|
+
TCommands,
|
|
436
|
+
TNameNested,
|
|
437
|
+
WithAliases<TBuilder['~types']['command'], ResolvedAliases<TCommands, TNameNested, TAliases>>
|
|
438
|
+
>,
|
|
439
|
+
TParentArgs,
|
|
440
|
+
TAsync,
|
|
441
|
+
TContext,
|
|
442
|
+
TContextProvided
|
|
443
|
+
>;
|
|
444
|
+
};
|
|
387
445
|
|
|
388
446
|
/** Mount an existing program as a subcommand, optionally transforming the context. @category Builder */
|
|
389
447
|
mount: {
|
|
@@ -694,6 +752,30 @@ export type AnyPadroneProgram = PadroneProgram<string, string, string, any, any,
|
|
|
694
752
|
*/
|
|
695
753
|
export type PadroneExtension<TIn extends CommandTypesBase = CommandTypesBase, TOut extends CommandTypesBase = TIn> = (builder: TIn) => TOut;
|
|
696
754
|
|
|
755
|
+
/**
|
|
756
|
+
* Default context type for commands defined with `defineCommand()`.
|
|
757
|
+
* Includes optional context properties provided by common extensions (logger, tracing, progress).
|
|
758
|
+
*
|
|
759
|
+
* Override globally via module augmentation to add your application's context:
|
|
760
|
+
* ```ts
|
|
761
|
+
* declare module 'padrone' {
|
|
762
|
+
* interface DefineCommandContext {
|
|
763
|
+
* db: Database;
|
|
764
|
+
* }
|
|
765
|
+
* }
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
export interface DefineCommandContext {
|
|
769
|
+
logger?: PadroneLogger;
|
|
770
|
+
tracing?: PadroneTracer;
|
|
771
|
+
progress?: PadroneProgressIndicator;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** Error brand returned by `.command()` when a `defineCommand.requires()` context requirement is not satisfied. */
|
|
775
|
+
export type DefineCommandRequiresError = {
|
|
776
|
+
readonly '~error': 'Required context not satisfied. Ensure required interceptors are registered on the program.';
|
|
777
|
+
};
|
|
778
|
+
|
|
697
779
|
/**
|
|
698
780
|
* Type for a command builder callback used with `.command()`.
|
|
699
781
|
* Use this when defining commands in separate files where full return type inference isn't needed.
|
|
@@ -712,7 +794,27 @@ export type PadroneExtension<TIn extends CommandTypesBase = CommandTypesBase, TO
|
|
|
712
794
|
* ```
|
|
713
795
|
*/
|
|
714
796
|
export type DefineCommand<TContext = unknown, TParentArgs extends PadroneSchema = PadroneSchema> = (
|
|
715
|
-
builder: PadroneBuilder<string, string, string, PadroneSchema<void>, void, [], TParentArgs, false, TContext>,
|
|
797
|
+
builder: PadroneBuilder<string, string, string, PadroneSchema<void>, void, [], TParentArgs, false, TContext, DefineCommandContext>,
|
|
716
798
|
) => CommandTypesBase;
|
|
717
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Builder returned by `defineCommand()` (no-arg form).
|
|
802
|
+
* Call `.requires<T>()` to declare context dependencies, then `.command()` to provide the builder callback.
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```ts
|
|
806
|
+
* const adminCommand = defineCommand()
|
|
807
|
+
* .requires<{ adminDb: AdminDB }>()
|
|
808
|
+
* .define((c) => c.action((_args, ctx) => ctx.context.adminDb.query(...)));
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
export type DefineCommandBuilder<TContextProvided = DefineCommandContext, TBrand = unknown> = {
|
|
812
|
+
/** Declare context types this command requires. Purely type-level — no runtime effect. */
|
|
813
|
+
requires: <TRequires>() => DefineCommandBuilder<DefineCommandContext & TRequires, { '~contextRequires': (ctx: TRequires) => void }>;
|
|
814
|
+
/** Provide the command builder callback. */
|
|
815
|
+
define: <TContext = unknown, TOut extends CommandTypesBase = CommandTypesBase>(
|
|
816
|
+
fn: (builder: PadroneBuilder<string, string, string, PadroneSchema<void>, void, [], any, false, TContext, TContextProvided>) => TOut,
|
|
817
|
+
) => typeof fn & TBrand;
|
|
818
|
+
};
|
|
819
|
+
|
|
718
820
|
type DefaultArgs = Record<string, unknown> | void;
|
package/src/types/index.ts
CHANGED
package/src/types/interceptor.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { AnyPadroneCommand, PadroneActionContext } from './command.ts';
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
/** Base context shared across all interceptor phases within a single execution. */
|
|
11
|
-
export type InterceptorBaseContext<TContext =
|
|
11
|
+
export type InterceptorBaseContext<TContext = object> = {
|
|
12
12
|
/** The resolved command for this execution. In the parse phase, this is the root program. */
|
|
13
13
|
command: AnyPadroneCommand;
|
|
14
14
|
/** The raw CLI input string (undefined when invoked without input). */
|
|
@@ -26,7 +26,7 @@ export type InterceptorBaseContext<TContext = unknown> = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
/** Context for the parse phase. */
|
|
29
|
-
export type InterceptorParseContext<TContext =
|
|
29
|
+
export type InterceptorParseContext<TContext = object> = InterceptorBaseContext<TContext>;
|
|
30
30
|
|
|
31
31
|
/** Result returned by the parse phase's `next()`. */
|
|
32
32
|
export type InterceptorParseResult = {
|
|
@@ -36,7 +36,7 @@ export type InterceptorParseResult = {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
/** Context for the validate phase. */
|
|
39
|
-
export type InterceptorValidateContext<TContext =
|
|
39
|
+
export type InterceptorValidateContext<TContext = object> = InterceptorBaseContext<TContext> & {
|
|
40
40
|
/** Raw named arguments extracted by the parser. Mutable — modify before `next()` to inject/override values. */
|
|
41
41
|
rawArgs: Record<string, unknown>;
|
|
42
42
|
/** Positional argument strings extracted by the parser. */
|
|
@@ -54,7 +54,7 @@ export type InterceptorValidateResult<TArgs = unknown> = {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
/** Context for the execute phase. Includes validate context fields (rawArgs, positionalArgs). */
|
|
57
|
-
export type InterceptorExecuteContext<TArgs = unknown, TContext =
|
|
57
|
+
export type InterceptorExecuteContext<TArgs = unknown, TContext = object> = InterceptorValidateContext<TContext> & {
|
|
58
58
|
/** Validated arguments that will be passed to the action. Mutable — modify before `next()` to override. */
|
|
59
59
|
args: TArgs;
|
|
60
60
|
};
|
|
@@ -65,10 +65,10 @@ export type InterceptorExecuteResult<TResult = unknown> = {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
/** Context for the start phase. Runs before parsing, wraps the entire pipeline. */
|
|
68
|
-
export type InterceptorStartContext<TContext =
|
|
68
|
+
export type InterceptorStartContext<TContext = object> = InterceptorBaseContext<TContext>;
|
|
69
69
|
|
|
70
70
|
/** Context for the error phase. Called when the pipeline throws. Includes pipeline state accumulated before the error. */
|
|
71
|
-
export type InterceptorErrorContext<TContext =
|
|
71
|
+
export type InterceptorErrorContext<TContext = object> = InterceptorBaseContext<TContext> & {
|
|
72
72
|
/** The error that was thrown. */
|
|
73
73
|
error: unknown;
|
|
74
74
|
/** Raw named arguments (available if parse completed). */
|
|
@@ -88,7 +88,7 @@ export type InterceptorErrorResult<TResult = unknown> = {
|
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
/** Context for the shutdown phase. Always runs after the pipeline (success or failure). Includes pipeline state accumulated before completion. */
|
|
91
|
-
export type InterceptorShutdownContext<TResult = unknown, TContext =
|
|
91
|
+
export type InterceptorShutdownContext<TResult = unknown, TContext = object> = InterceptorBaseContext<TContext> & {
|
|
92
92
|
/** The error, if the pipeline failed (after error phase processing). */
|
|
93
93
|
error?: unknown;
|
|
94
94
|
/** The pipeline result, if it succeeded. */
|
|
@@ -156,7 +156,7 @@ export type InterceptorMeta = {
|
|
|
156
156
|
* - `TArgs` — the validated arguments type (output of the args schema).
|
|
157
157
|
* - `TResult` — the command's return type.
|
|
158
158
|
*/
|
|
159
|
-
export type InterceptorPhases<TArgs = unknown, TResult = unknown, TContext =
|
|
159
|
+
export type InterceptorPhases<TArgs = unknown, TResult = unknown, TContext = object> = {
|
|
160
160
|
/**
|
|
161
161
|
* Runs before the pipeline (parse → validate → execute). `next()` proceeds to the pipeline.
|
|
162
162
|
* Root interceptors only. Use for startup tasks like telemetry, update checks, or global config loading.
|
|
@@ -188,7 +188,7 @@ export type InterceptorPhases<TArgs = unknown, TResult = unknown, TContext = unk
|
|
|
188
188
|
* Factory function that creates phase handlers for an interceptor.
|
|
189
189
|
* Called once per command execution — the closure provides typed, scoped cross-phase state across phases.
|
|
190
190
|
*/
|
|
191
|
-
export type InterceptorFactory<TArgs = unknown, TResult = unknown, TContext =
|
|
191
|
+
export type InterceptorFactory<TArgs = unknown, TResult = unknown, TContext = object> = () => InterceptorPhases<TArgs, TResult, TContext>;
|
|
192
192
|
|
|
193
193
|
/**
|
|
194
194
|
* A self-contained interceptor value: a factory function with static metadata as own properties.
|
|
@@ -198,7 +198,7 @@ export type InterceptorFactory<TArgs = unknown, TResult = unknown, TContext = un
|
|
|
198
198
|
* Also accepted directly by `.intercept()` as the single-argument form.
|
|
199
199
|
* Call `.provides<T>()` to brand it as a context-providing interceptor.
|
|
200
200
|
*/
|
|
201
|
-
export type PadroneInterceptorFn<TArgs = unknown, TResult = unknown, TContext =
|
|
201
|
+
export type PadroneInterceptorFn<TArgs = unknown, TResult = unknown, TContext = object> = InterceptorFactory<TArgs, TResult, TContext> &
|
|
202
202
|
InterceptorMeta & {
|
|
203
203
|
/** Brand this interceptor as providing additional context of type `TProvides`. No-op at runtime; purely a type-level cast. */
|
|
204
204
|
provides: <TProvides>() => PadroneContextInterceptor<TProvides, TArgs, TResult, TContext>;
|
|
@@ -217,7 +217,7 @@ export type PadroneInterceptorFn<TArgs = unknown, TResult = unknown, TContext =
|
|
|
217
217
|
*
|
|
218
218
|
* Create with `defineInterceptor(meta, factory)` or pass `(meta, factory)` directly to `.intercept()`.
|
|
219
219
|
*/
|
|
220
|
-
export type PadroneInterceptor<TArgs = unknown, TResult = unknown, TContext =
|
|
220
|
+
export type PadroneInterceptor<TArgs = unknown, TResult = unknown, TContext = object> = PadroneInterceptorFn<TArgs, TResult, TContext>;
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
223
|
* A context-providing interceptor. Carries a phantom `'~context'` brand declaring what it adds
|
|
@@ -227,7 +227,7 @@ export type PadroneInterceptor<TArgs = unknown, TResult = unknown, TContext = un
|
|
|
227
227
|
* Created by calling `.provides<T>()` on a `PadroneInterceptorFn`.
|
|
228
228
|
* Chain `.requires<T>()` to also declare context dependencies.
|
|
229
229
|
*/
|
|
230
|
-
export type PadroneContextInterceptor<TProvides = unknown, TArgs = unknown, TResult = unknown, TContext =
|
|
230
|
+
export type PadroneContextInterceptor<TProvides = unknown, TArgs = unknown, TResult = unknown, TContext = object> = Omit<
|
|
231
231
|
PadroneInterceptorFn<TArgs, TResult, TContext>,
|
|
232
232
|
'requires'
|
|
233
233
|
> &
|