padrone 1.5.0 → 1.7.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 +44 -0
- package/README.md +15 -11
- package/dist/{args-D5PNDyNu.mjs → args-Cnq0nwSM.mjs} +91 -41
- package/dist/args-Cnq0nwSM.mjs.map +1 -0
- package/dist/codegen/index.mjs +4 -4
- package/dist/codegen/index.mjs.map +1 -1
- package/dist/commands-B_gufyR9.mjs +514 -0
- package/dist/commands-B_gufyR9.mjs.map +1 -0
- package/dist/{completion.mjs → completion-BEuflbDO.mjs} +12 -82
- package/dist/completion-BEuflbDO.mjs.map +1 -0
- package/dist/docs/index.d.mts +4 -4
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +10 -12
- package/dist/docs/index.mjs.map +1 -1
- package/dist/{errors-BiVrBgi6.mjs → errors-DA4KzK1M.mjs} +26 -3
- package/dist/errors-DA4KzK1M.mjs.map +1 -0
- package/dist/{formatter-DtHzbP22.d.mts → formatter-DrvhDMrq.d.mts} +3 -3
- package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
- package/dist/{help-bbmu9-qd.mjs → help-BtxLgrF_.mjs} +190 -43
- package/dist/help-BtxLgrF_.mjs.map +1 -0
- package/dist/{types-Ch8Mk6Qb.d.mts → index-D6-7dz0l.d.mts} +634 -745
- package/dist/index-D6-7dz0l.d.mts.map +1 -0
- package/dist/index.d.mts +869 -36
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3884 -1699
- package/dist/index.mjs.map +1 -1
- package/dist/{mcp-mLWIdUIu.mjs → mcp-6-Jw4Bpq.mjs} +13 -15
- package/dist/mcp-6-Jw4Bpq.mjs.map +1 -0
- package/dist/{serve-B0u43DK7.mjs → serve-YVTPzBCl.mjs} +12 -14
- package/dist/serve-YVTPzBCl.mjs.map +1 -0
- package/dist/{stream-BcC146Ud.mjs → stream-DC4H8YTx.mjs} +24 -3
- package/dist/stream-DC4H8YTx.mjs.map +1 -0
- package/dist/test.d.mts +5 -8
- package/dist/test.d.mts.map +1 -1
- package/dist/test.mjs +2 -13
- package/dist/test.mjs.map +1 -1
- package/dist/{update-check-CFX1FV3v.mjs → update-check-CZ2VqjnV.mjs} +16 -17
- package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
- package/dist/zod.d.mts +2 -2
- package/dist/zod.d.mts.map +1 -1
- package/dist/zod.mjs +2 -2
- package/dist/zod.mjs.map +1 -1
- package/package.json +15 -12
- package/src/cli/completions.ts +14 -11
- package/src/cli/docs.ts +13 -10
- package/src/cli/doctor.ts +22 -18
- package/src/cli/index.ts +28 -82
- package/src/cli/init.ts +10 -7
- package/src/cli/link.ts +20 -16
- package/src/cli/wrap.ts +14 -11
- package/src/codegen/schema-to-code.ts +2 -2
- package/src/{args.ts → core/args.ts} +32 -225
- package/src/core/commands.ts +373 -0
- package/src/core/create.ts +301 -0
- package/src/core/default-runtime.ts +239 -0
- package/src/{errors.ts → core/errors.ts} +22 -0
- package/src/core/exec.ts +259 -0
- package/src/core/interceptors.ts +302 -0
- package/src/{parse.ts → core/parse.ts} +36 -89
- package/src/core/program-methods.ts +301 -0
- package/src/core/results.ts +229 -0
- package/src/core/runtime.ts +246 -0
- package/src/core/validate.ts +247 -0
- package/src/docs/index.ts +12 -13
- package/src/extension/auto-output.ts +146 -0
- package/src/extension/color.ts +38 -0
- package/src/extension/completion.ts +49 -0
- package/src/extension/config.ts +262 -0
- package/src/extension/env.ts +101 -0
- package/src/extension/help.ts +192 -0
- package/src/extension/index.ts +44 -0
- package/src/extension/ink.ts +93 -0
- package/src/extension/interactive.ts +106 -0
- package/src/extension/logger.ts +262 -0
- package/src/extension/man.ts +51 -0
- package/src/extension/mcp.ts +52 -0
- package/src/extension/progress-renderer.ts +338 -0
- package/src/extension/progress.ts +299 -0
- package/src/extension/repl.ts +94 -0
- package/src/extension/serve.ts +48 -0
- package/src/extension/signal.ts +87 -0
- package/src/extension/stdin.ts +62 -0
- package/src/extension/suggestions.ts +114 -0
- package/src/extension/timing.ts +81 -0
- package/src/extension/tracing.ts +175 -0
- package/src/extension/update-check.ts +77 -0
- package/src/extension/utils.ts +51 -0
- package/src/extension/version.ts +63 -0
- package/src/{completion.ts → feature/completion.ts} +12 -12
- package/src/{interactive.ts → feature/interactive.ts} +4 -4
- package/src/{mcp.ts → feature/mcp.ts} +12 -15
- package/src/{repl-loop.ts → feature/repl-loop.ts} +10 -13
- package/src/{serve.ts → feature/serve.ts} +11 -15
- package/src/feature/test.ts +262 -0
- package/src/{update-check.ts → feature/update-check.ts} +16 -16
- package/src/{wrap.ts → feature/wrap.ts} +10 -8
- package/src/index.ts +115 -30
- package/src/{formatter.ts → output/formatter.ts} +124 -176
- package/src/{help.ts → output/help.ts} +22 -8
- package/src/output/output-indicator.ts +87 -0
- package/src/output/primitives.ts +335 -0
- package/src/output/styling.ts +221 -0
- package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
- package/src/schema/zod.ts +50 -0
- package/src/test.ts +2 -276
- package/src/types/args-meta.ts +151 -0
- package/src/types/builder.ts +718 -0
- package/src/types/command.ts +157 -0
- package/src/types/index.ts +60 -0
- package/src/types/interceptor.ts +296 -0
- package/src/types/preferences.ts +83 -0
- package/src/types/result.ts +71 -0
- package/src/types/schema.ts +19 -0
- package/src/util/dotenv.ts +244 -0
- package/src/{shell-utils.ts → util/shell-utils.ts} +26 -9
- package/src/{stream.ts → util/stream.ts} +27 -1
- package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
- package/src/{type-utils.ts → util/type-utils.ts} +71 -33
- package/src/util/utils.ts +51 -0
- package/src/zod.ts +1 -50
- package/dist/args-D5PNDyNu.mjs.map +0 -1
- package/dist/chunk-CjcI7cDX.mjs +0 -15
- package/dist/command-utils-B1D-HqCd.mjs +0 -1117
- package/dist/command-utils-B1D-HqCd.mjs.map +0 -1
- package/dist/completion.d.mts +0 -64
- package/dist/completion.d.mts.map +0 -1
- package/dist/completion.mjs.map +0 -1
- package/dist/errors-BiVrBgi6.mjs.map +0 -1
- package/dist/formatter-DtHzbP22.d.mts.map +0 -1
- package/dist/help-bbmu9-qd.mjs.map +0 -1
- package/dist/mcp-mLWIdUIu.mjs.map +0 -1
- package/dist/serve-B0u43DK7.mjs.map +0 -1
- package/dist/stream-BcC146Ud.mjs.map +0 -1
- package/dist/types-Ch8Mk6Qb.d.mts.map +0 -1
- package/dist/update-check-CFX1FV3v.mjs.map +0 -1
- package/src/command-utils.ts +0 -882
- package/src/create.ts +0 -1829
- package/src/runtime.ts +0 -497
- package/src/types.ts +0 -1291
- package/src/utils.ts +0 -140
- /package/src/{colorizer.ts → output/colorizer.ts} +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { thenMaybe } from '#src/core/results.ts';
|
|
2
|
+
import { defineInterceptor } from '../core/interceptors.ts';
|
|
3
|
+
import type { UpdateCheckConfig } from '../feature/update-check.ts';
|
|
4
|
+
import type { AnyPadroneBuilder, CommandTypesBase } from '../types/index.ts';
|
|
5
|
+
import { getVersion } from '../util/utils.ts';
|
|
6
|
+
|
|
7
|
+
// ── Interceptor ─────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
function createUpdateCheckInterceptor(config: UpdateCheckConfig) {
|
|
10
|
+
return defineInterceptor({ id: 'padrone:update-check', name: 'padrone:update-check', order: 1000 }, () => {
|
|
11
|
+
let checkPromise: Promise<(() => void) | undefined> | undefined;
|
|
12
|
+
let suppressed = false;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
start(ctx, next) {
|
|
16
|
+
const rootCommand = ctx.command;
|
|
17
|
+
const runtime = ctx.runtime;
|
|
18
|
+
|
|
19
|
+
checkPromise = Promise.resolve(getVersion(rootCommand.version)).then((currentVersion) =>
|
|
20
|
+
import('../feature/update-check.ts').then(({ createUpdateChecker }) =>
|
|
21
|
+
createUpdateChecker(rootCommand.name, currentVersion, config, runtime),
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return next();
|
|
26
|
+
},
|
|
27
|
+
parse(_ctx, next) {
|
|
28
|
+
return thenMaybe(next(), (res) => {
|
|
29
|
+
if ('update-check' in res.rawArgs) {
|
|
30
|
+
if (res.rawArgs['update-check'] === false) suppressed = true;
|
|
31
|
+
delete res.rawArgs['update-check'];
|
|
32
|
+
}
|
|
33
|
+
return res;
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
shutdown(_ctx, next) {
|
|
37
|
+
const result = next();
|
|
38
|
+
if (suppressed || !checkPromise) return result;
|
|
39
|
+
|
|
40
|
+
// Try to show notification synchronously if the check already resolved
|
|
41
|
+
let resolved: (() => void) | undefined | null = null;
|
|
42
|
+
checkPromise.then(
|
|
43
|
+
(fn) => {
|
|
44
|
+
resolved = fn;
|
|
45
|
+
},
|
|
46
|
+
() => {
|
|
47
|
+
resolved = undefined;
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (resolved !== null) {
|
|
52
|
+
(resolved as (() => void) | undefined)?.();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Extension ────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extension that adds background update checking:
|
|
65
|
+
* - Checks for newer versions on npm (or custom registry) in the background
|
|
66
|
+
* - Shows an update notification after command execution
|
|
67
|
+
* - Respects `--no-update-check` flag to suppress
|
|
68
|
+
*
|
|
69
|
+
* Usage:
|
|
70
|
+
* ```ts
|
|
71
|
+
* createPadrone('my-cli')
|
|
72
|
+
* .extend(padroneUpdateCheck({ packageName: 'my-cli' }))
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export function padroneUpdateCheck(config: UpdateCheckConfig = {}): <T extends CommandTypesBase>(builder: T) => T {
|
|
76
|
+
return ((builder: AnyPadroneBuilder) => builder.intercept(createUpdateCheckInterceptor(config))) as any;
|
|
77
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AnyPadroneCommand, PadroneSchema } from '../types/index.ts';
|
|
2
|
+
|
|
3
|
+
type SchemaShape = Record<string, 'string' | 'string[]' | 'boolean'>;
|
|
4
|
+
|
|
5
|
+
type InferPassthroughSchema<T extends SchemaShape> = {
|
|
6
|
+
[K in keyof T]: T[K] extends 'string' ? string : T[K] extends 'string[]' ? string[] : T[K] extends 'boolean' ? boolean : never;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/** Minimal Standard Schema that passes through known fields, ignoring unknown ones. */
|
|
10
|
+
export function passthroughSchema<TShape extends SchemaShape>(fields: TShape): PadroneSchema<InferPassthroughSchema<TShape>> {
|
|
11
|
+
return {
|
|
12
|
+
'~standard': {
|
|
13
|
+
version: 1 as const,
|
|
14
|
+
vendor: 'padrone' as const,
|
|
15
|
+
jsonSchema: {
|
|
16
|
+
input: () => ({}),
|
|
17
|
+
output: () => ({}),
|
|
18
|
+
},
|
|
19
|
+
validate: (value) => {
|
|
20
|
+
const input = value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
|
|
21
|
+
const result: Record<string, unknown> = {};
|
|
22
|
+
for (const [name, type] of Object.entries(fields)) {
|
|
23
|
+
const v = input[name];
|
|
24
|
+
if (v === undefined) continue;
|
|
25
|
+
if (type === 'string[]') {
|
|
26
|
+
if (Array.isArray(v)) result[name] = v.map(String);
|
|
27
|
+
else if (typeof v === 'string') result[name] = [v];
|
|
28
|
+
} else if (type === 'string') {
|
|
29
|
+
if (typeof v === 'string') result[name] = v;
|
|
30
|
+
else if (Array.isArray(v) && v.length > 0) result[name] = String(v[0]);
|
|
31
|
+
} else if (type === 'boolean') {
|
|
32
|
+
result[name] = v === true || v === 'true';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { value: result as InferPassthroughSchema<TShape> };
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Find a command by space-separated name in the command tree. */
|
|
42
|
+
export function findCommandInTree(name: string, rootCommand: AnyPadroneCommand): AnyPadroneCommand | undefined {
|
|
43
|
+
const parts = name.split(' ').filter(Boolean);
|
|
44
|
+
let current = rootCommand;
|
|
45
|
+
for (const part of parts) {
|
|
46
|
+
const found = current.commands?.find((c) => c.name === part || c.aliases?.includes(part));
|
|
47
|
+
if (!found) return undefined;
|
|
48
|
+
current = found;
|
|
49
|
+
}
|
|
50
|
+
return current;
|
|
51
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { thenMaybe } from '#src/core/results.ts';
|
|
2
|
+
import { resolveCommand } from '../core/commands.ts';
|
|
3
|
+
import { defineInterceptor } from '../core/interceptors.ts';
|
|
4
|
+
import type { AnyPadroneBuilder, CommandTypesBase, PadroneCommand } from '../types/index.ts';
|
|
5
|
+
import type { PadroneSchema } from '../types/schema.ts';
|
|
6
|
+
import type { WithCommand } from '../util/type-utils.ts';
|
|
7
|
+
import { getRootCommand, getVersion } from '../util/utils.ts';
|
|
8
|
+
|
|
9
|
+
// ── Types ────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export type VersionCommand = PadroneCommand<'version', '', PadroneSchema<void>, string, [], [], false>;
|
|
12
|
+
|
|
13
|
+
export type WithVersion<T> = WithCommand<T, 'version', VersionCommand>;
|
|
14
|
+
|
|
15
|
+
// ── Interceptor ─────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
const versionInterceptor = defineInterceptor({ id: 'padrone:version', name: 'padrone:version', order: -1000 }, () => ({
|
|
18
|
+
parse(_ctx, next) {
|
|
19
|
+
return thenMaybe(next(), (res) => {
|
|
20
|
+
const hasVersionFlag = res.rawArgs.version || res.rawArgs.v || res.rawArgs.V;
|
|
21
|
+
|
|
22
|
+
// Only show version for root command (no subcommand matched)
|
|
23
|
+
if (hasVersionFlag && !res.command.parent) {
|
|
24
|
+
delete res.rawArgs.version;
|
|
25
|
+
delete res.rawArgs.v;
|
|
26
|
+
delete res.rawArgs.V;
|
|
27
|
+
|
|
28
|
+
// Route to the version command so its action handles the rest
|
|
29
|
+
const versionCmd = res.command.commands?.find((c) => c.name === 'version');
|
|
30
|
+
if (versionCmd) {
|
|
31
|
+
resolveCommand(versionCmd);
|
|
32
|
+
return { ...res, command: versionCmd, rawArgs: {}, positionalArgs: [] };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return res;
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
// ── Extension ────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Extension that adds version support:
|
|
45
|
+
* - `version` command
|
|
46
|
+
* - `--version` / `-v` / `-V` flags (root command only)
|
|
47
|
+
*
|
|
48
|
+
* Usage:
|
|
49
|
+
* ```ts
|
|
50
|
+
* createPadrone('my-cli').extend(padroneVersion())
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function padroneVersion(): <T extends CommandTypesBase>(builder: T) => WithVersion<T> {
|
|
54
|
+
return ((builder: AnyPadroneBuilder) =>
|
|
55
|
+
builder
|
|
56
|
+
.command('version', (c) =>
|
|
57
|
+
c.configure({ description: 'Display the version number', hidden: true }).action((_args, ctx) => {
|
|
58
|
+
const rootCommand = getRootCommand(ctx.command);
|
|
59
|
+
return getVersion(rootCommand.version);
|
|
60
|
+
}),
|
|
61
|
+
)
|
|
62
|
+
.intercept(versionInterceptor)) as any;
|
|
63
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { extractSchemaMetadata,
|
|
2
|
-
import
|
|
3
|
-
import type
|
|
1
|
+
import { extractSchemaMetadata, getJsonSchema } from '../core/args.ts';
|
|
2
|
+
import type { AnyPadroneCommand } from '../types/index.ts';
|
|
3
|
+
import { detectShell, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';
|
|
4
4
|
|
|
5
|
-
export { detectShell, escapeRegExp, getRcFile, type ShellType, writeToRcFile } from '
|
|
5
|
+
export { detectShell, escapeRegExp, getRcFile, type ShellType, writeToRcFile } from '../util/shell-utils.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Collects all commands from a program recursively.
|
|
@@ -48,7 +48,7 @@ function extractArguments(cmd: AnyPadroneCommand): ExtractedArg[] {
|
|
|
48
48
|
if (!argToAlias[argName]) argToAlias[argName] = aliasName;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const jsonSchema = cmd.argsSchema
|
|
51
|
+
const jsonSchema = getJsonSchema(cmd.argsSchema) as Record<string, any>;
|
|
52
52
|
|
|
53
53
|
if (jsonSchema.type === 'object' && jsonSchema.properties) {
|
|
54
54
|
for (const [key, prop] of Object.entries(jsonSchema.properties as Record<string, any>)) {
|
|
@@ -439,7 +439,7 @@ ${programName} completion powershell >> $PROFILE`;
|
|
|
439
439
|
* Generates the completion output with automatic shell detection.
|
|
440
440
|
* If shell is not specified, detects the current shell and provides instructions.
|
|
441
441
|
*/
|
|
442
|
-
export function generateCompletionOutput(program: AnyPadroneCommand, shell?: ShellType): string {
|
|
442
|
+
export async function generateCompletionOutput(program: AnyPadroneCommand, shell?: ShellType): Promise<string> {
|
|
443
443
|
const programName = program.name;
|
|
444
444
|
|
|
445
445
|
if (shell) {
|
|
@@ -447,7 +447,7 @@ export function generateCompletionOutput(program: AnyPadroneCommand, shell?: She
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
// Auto-detect shell and provide instructions
|
|
450
|
-
const detectedShell = detectShell();
|
|
450
|
+
const detectedShell = await detectShell();
|
|
451
451
|
|
|
452
452
|
if (detectedShell) {
|
|
453
453
|
const instructions = getCompletionInstallInstructions(programName, detectedShell);
|
|
@@ -492,10 +492,10 @@ export interface SetupCompletionsResult {
|
|
|
492
492
|
* Sets up shell completions by writing an eval snippet to the appropriate shell config file.
|
|
493
493
|
* Uses marker comments for idempotency — re-running replaces the existing block.
|
|
494
494
|
*/
|
|
495
|
-
export function setupCompletions(programName: string, shell: ShellType): SetupCompletionsResult {
|
|
496
|
-
const { existsSync, mkdirSync, writeFileSync } =
|
|
497
|
-
const { join } =
|
|
498
|
-
const { homedir } =
|
|
495
|
+
export async function setupCompletions(programName: string, shell: ShellType): Promise<SetupCompletionsResult> {
|
|
496
|
+
const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');
|
|
497
|
+
const { join } = await import('node:path');
|
|
498
|
+
const { homedir } = await import('node:os');
|
|
499
499
|
|
|
500
500
|
const beginMarker = `###-begin-${programName}-completion-###`;
|
|
501
501
|
const endMarker = `###-end-${programName}-completion-###`;
|
|
@@ -510,7 +510,7 @@ export function setupCompletions(programName: string, shell: ShellType): SetupCo
|
|
|
510
510
|
return { file: filePath, updated: existed };
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
-
const rcFile = getRcFile(shell);
|
|
513
|
+
const rcFile = await getRcFile(shell);
|
|
514
514
|
if (!rcFile) {
|
|
515
515
|
throw new Error(`Could not determine config file for ${shell}.`);
|
|
516
516
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { InteractivePromptConfig, ResolvedPadroneRuntime } from '
|
|
3
|
-
import type { AnyPadroneCommand } from '
|
|
1
|
+
import { getJsonSchema } from '../core/args.ts';
|
|
2
|
+
import type { InteractivePromptConfig, ResolvedPadroneRuntime } from '../core/runtime.ts';
|
|
3
|
+
import type { AnyPadroneCommand } from '../types/index.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Auto-detect the prompt type for a field based on its JSON schema property definition.
|
|
@@ -110,7 +110,7 @@ export async function promptInteractiveFields(
|
|
|
110
110
|
let requiredFields: Set<string> = new Set();
|
|
111
111
|
if (command.argsSchema) {
|
|
112
112
|
try {
|
|
113
|
-
const jsonSchema = command.argsSchema
|
|
113
|
+
const jsonSchema = getJsonSchema(command.argsSchema) as Record<string, any>;
|
|
114
114
|
if (jsonSchema.type === 'object' && jsonSchema.properties) {
|
|
115
115
|
jsonProperties = jsonSchema.properties;
|
|
116
116
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { buildInputSchema, collectEndpoints, serializeArgsToFlags } from '
|
|
2
|
-
import { generateHelp } from '
|
|
3
|
-
import type { AnyPadroneCommand, AnyPadroneProgram } from '
|
|
1
|
+
import { buildInputSchema, collectEndpoints, serializeArgsToFlags } from '../core/commands.ts';
|
|
2
|
+
import { generateHelp } from '../output/help.ts';
|
|
3
|
+
import type { AnyPadroneCommand, AnyPadroneProgram } from '../types/index.ts';
|
|
4
|
+
import { readStreamAsText } from '../util/stream.ts';
|
|
4
5
|
|
|
5
6
|
export type PadroneMcpPreferences = {
|
|
6
7
|
/** Server name. Defaults to the program name. */
|
|
@@ -158,7 +159,7 @@ export function createMcpHandler(
|
|
|
158
159
|
const output: string[] = [];
|
|
159
160
|
const errors: string[] = [];
|
|
160
161
|
const result = await evalCommand(input as any, {
|
|
161
|
-
|
|
162
|
+
caller: 'mcp',
|
|
162
163
|
runtime: {
|
|
163
164
|
output: (...outArgs: unknown[]) => output.push(outArgs.map(String).join(' ')),
|
|
164
165
|
error: (text: string) => errors.push(text),
|
|
@@ -237,6 +238,7 @@ async function startHttpTransport(
|
|
|
237
238
|
handleRequest: (req: JsonRpcRequest) => Promise<JsonRpcResponse | undefined>,
|
|
238
239
|
prefs: PadroneMcpPreferences,
|
|
239
240
|
log: (msg: string) => void,
|
|
241
|
+
onSignal?: (callback: () => void) => () => void,
|
|
240
242
|
): Promise<void> {
|
|
241
243
|
const http = await import('node:http');
|
|
242
244
|
const crypto = await import('node:crypto');
|
|
@@ -317,11 +319,7 @@ async function startHttpTransport(
|
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
// Read request body
|
|
320
|
-
const
|
|
321
|
-
for await (const chunk of req) {
|
|
322
|
-
chunks.push(chunk);
|
|
323
|
-
}
|
|
324
|
-
const body = Buffer.concat(chunks).toString('utf-8');
|
|
322
|
+
const body = await readStreamAsText(req as AsyncIterable<Uint8Array>);
|
|
325
323
|
|
|
326
324
|
let rpcRequest: JsonRpcRequest;
|
|
327
325
|
try {
|
|
@@ -363,11 +361,10 @@ async function startHttpTransport(
|
|
|
363
361
|
log(`MCP server listening on http://${host}:${port}${endpoint}`);
|
|
364
362
|
});
|
|
365
363
|
server.on('error', reject);
|
|
366
|
-
const
|
|
364
|
+
const unsubscribe = onSignal?.(() => {
|
|
367
365
|
server.close(() => resolve());
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
process.on('SIGTERM', onSignal);
|
|
366
|
+
});
|
|
367
|
+
server.on('close', () => unsubscribe?.());
|
|
371
368
|
});
|
|
372
369
|
}
|
|
373
370
|
|
|
@@ -384,7 +381,7 @@ export async function startMcpServer(
|
|
|
384
381
|
return startStdioTransport(handleRequest);
|
|
385
382
|
}
|
|
386
383
|
|
|
387
|
-
const { getCommandRuntime } = await import('
|
|
384
|
+
const { getCommandRuntime } = await import('../core/commands.ts');
|
|
388
385
|
const runtime = getCommandRuntime(existingCommand);
|
|
389
|
-
return startHttpTransport(handleRequest, prefs ?? {}, (msg) => runtime.error(msg));
|
|
386
|
+
return startHttpTransport(handleRequest, prefs ?? {}, (msg) => runtime.error(msg), runtime.onSignal);
|
|
390
387
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
-
import { buildReplCompleter, findCommandByName, getCommandRuntime } from '
|
|
3
|
-
import { createTerminalReplSession
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
2
|
+
import { buildReplCompleter, findCommandByName, getCommandRuntime } from '../core/commands.ts';
|
|
3
|
+
import { createTerminalReplSession } from '../core/default-runtime.ts';
|
|
4
|
+
import { REPL_SIGINT, type ReplSessionConfig } from '../core/runtime.ts';
|
|
5
|
+
import type { AnyPadroneCommand, PadroneEvalPreferences, PadroneReplPreferences } from '../types/index.ts';
|
|
6
|
+
import { getVersion } from '../util/utils.ts';
|
|
6
7
|
|
|
7
8
|
export type ReplDeps = {
|
|
8
9
|
existingCommand: AnyPadroneCommand;
|
|
@@ -25,9 +26,8 @@ export function createReplIterator(deps: ReplDeps, options?: PadroneReplPreferen
|
|
|
25
26
|
const runtime = getCommandRuntime(existingCommand);
|
|
26
27
|
|
|
27
28
|
const programName = existingCommand.name || 'padrone';
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
(runtime.format === 'auto' && typeof process !== 'undefined' && !process.env.NO_COLOR && !process.env.CI && process.stdout?.isTTY);
|
|
29
|
+
const env = runtime.env();
|
|
30
|
+
const useAnsi = runtime.format === 'ansi' || (runtime.format === 'auto' && !env.NO_COLOR && !env.CI && runtime.terminal?.isTTY === true);
|
|
31
31
|
|
|
32
32
|
// Track command history for .history built-in
|
|
33
33
|
const commandHistory: string[] = [];
|
|
@@ -60,7 +60,7 @@ export function createReplIterator(deps: ReplDeps, options?: PadroneReplPreferen
|
|
|
60
60
|
runtime.output(options.greeting);
|
|
61
61
|
} else {
|
|
62
62
|
const displayName = existingCommand.title || programName;
|
|
63
|
-
const version = existingCommand.version ? getVersion(existingCommand.version) : undefined;
|
|
63
|
+
const version = existingCommand.version ? await getVersion(existingCommand.version) : undefined;
|
|
64
64
|
const greeting = version ? `Welcome to ${displayName} v${version}` : `Welcome to ${displayName}`;
|
|
65
65
|
runtime.output(greeting);
|
|
66
66
|
}
|
|
@@ -264,10 +264,7 @@ export function createReplIterator(deps: ReplDeps, options?: PadroneReplPreferen
|
|
|
264
264
|
|
|
265
265
|
const emitSpacingLine = (value: boolean | string) => {
|
|
266
266
|
if (typeof value === 'string') {
|
|
267
|
-
const sep =
|
|
268
|
-
value.length === 1
|
|
269
|
-
? value.repeat(typeof process !== 'undefined' && process.stdout?.columns ? process.stdout.columns : 80)
|
|
270
|
-
: value;
|
|
267
|
+
const sep = value.length === 1 ? value.repeat(runtime.terminal?.columns ?? 80) : value;
|
|
271
268
|
runtime.output(sep);
|
|
272
269
|
} else if (value) {
|
|
273
270
|
runtime.output('');
|
|
@@ -289,7 +286,7 @@ export function createReplIterator(deps: ReplDeps, options?: PadroneReplPreferen
|
|
|
289
286
|
const scopedInput = scopePath ? (evalInput ? `${scopePath} ${evalInput}` : scopePath) : evalInput;
|
|
290
287
|
|
|
291
288
|
try {
|
|
292
|
-
const replEvalPrefs: PadroneEvalPreferences | undefined =
|
|
289
|
+
const replEvalPrefs: PadroneEvalPreferences | undefined = { caller: 'repl' };
|
|
293
290
|
const result = await evalCommand(scopedInput, replEvalPrefs);
|
|
294
291
|
if (result.error) {
|
|
295
292
|
const msg = result.error instanceof Error ? result.error.message : String(result.error);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { buildInputSchema, type CollectedEndpoint, collectEndpoints, serializeArgsToFlags } from '
|
|
2
|
-
import { RoutingError, ValidationError } from '
|
|
3
|
-
import { generateHelp } from '
|
|
4
|
-
import type { AnyPadroneCommand, AnyPadroneProgram } from '
|
|
1
|
+
import { buildInputSchema, type CollectedEndpoint, collectEndpoints, serializeArgsToFlags } from '../core/commands.ts';
|
|
2
|
+
import { RoutingError, ValidationError } from '../core/errors.ts';
|
|
3
|
+
import { generateHelp } from '../output/help.ts';
|
|
4
|
+
import type { AnyPadroneCommand, AnyPadroneProgram } from '../types/index.ts';
|
|
5
|
+
import { readStreamAsText } from '../util/stream.ts';
|
|
5
6
|
|
|
6
7
|
export type PadroneServePreferences = {
|
|
7
8
|
/** Port to listen on. Default: 3000 */
|
|
@@ -229,7 +230,7 @@ export function createServeHandler(
|
|
|
229
230
|
const output: string[] = [];
|
|
230
231
|
const errors: string[] = [];
|
|
231
232
|
const result = await evalCommand(commandString || (undefined as any), {
|
|
232
|
-
|
|
233
|
+
caller: 'serve',
|
|
233
234
|
runtime: {
|
|
234
235
|
output: (...args: unknown[]) => output.push(args.map(String).join(' ')),
|
|
235
236
|
error: (text: string) => errors.push(text),
|
|
@@ -414,7 +415,7 @@ export async function startServeServer(
|
|
|
414
415
|
res.end(body);
|
|
415
416
|
});
|
|
416
417
|
|
|
417
|
-
const { getCommandRuntime } = await import('
|
|
418
|
+
const { getCommandRuntime } = await import('../core/commands.ts');
|
|
418
419
|
const runtime = getCommandRuntime(existingCommand);
|
|
419
420
|
|
|
420
421
|
return new Promise<void>((resolve, reject) => {
|
|
@@ -424,19 +425,14 @@ export async function startServeServer(
|
|
|
424
425
|
if (builtins.docs) runtime.error(`API docs: http://${host}:${port}${basePath}_docs`);
|
|
425
426
|
});
|
|
426
427
|
server.on('error', reject);
|
|
427
|
-
const
|
|
428
|
+
const unsubscribe = runtime.onSignal?.(() => {
|
|
428
429
|
server.close(() => resolve());
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
process.on('SIGTERM', onSignal);
|
|
430
|
+
});
|
|
431
|
+
server.on('close', () => unsubscribe?.());
|
|
432
432
|
});
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
/** Read the full body from a Node.js IncomingMessage. */
|
|
436
436
|
async function readBody(req: import('node:http').IncomingMessage): Promise<string> {
|
|
437
|
-
|
|
438
|
-
for await (const chunk of req) {
|
|
439
|
-
chunks.push(chunk as Buffer);
|
|
440
|
-
}
|
|
441
|
-
return Buffer.concat(chunks).toString('utf-8');
|
|
437
|
+
return readStreamAsText(req as AsyncIterable<Uint8Array>);
|
|
442
438
|
}
|