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,262 @@
|
|
|
1
|
+
import type { InteractivePromptConfig, PadroneRuntime } from '../core/runtime.ts';
|
|
2
|
+
import type { AnyPadroneCommand, PadroneCommandResult } from '../types/index.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Result from a single command execution in test mode.
|
|
6
|
+
* Extends the standard PadroneCommandResult with captured I/O.
|
|
7
|
+
*/
|
|
8
|
+
export type TestCliResult = {
|
|
9
|
+
/** The matched command. */
|
|
10
|
+
command: AnyPadroneCommand;
|
|
11
|
+
/** Validated arguments (undefined if validation failed). */
|
|
12
|
+
args: unknown;
|
|
13
|
+
/** Action handler return value (undefined if validation failed or no action). */
|
|
14
|
+
result: unknown;
|
|
15
|
+
/** Validation issues, if any. */
|
|
16
|
+
issues: { message: string; path?: PropertyKey[] }[] | undefined;
|
|
17
|
+
/** All values passed to `runtime.output()`. */
|
|
18
|
+
stdout: unknown[];
|
|
19
|
+
/** All strings passed to `runtime.error()`. */
|
|
20
|
+
stderr: string[];
|
|
21
|
+
/** The thrown error, if the command threw (routing error, action error, etc.). */
|
|
22
|
+
error?: unknown;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Result from a REPL test session.
|
|
27
|
+
*/
|
|
28
|
+
export type TestReplResult = {
|
|
29
|
+
/** One entry per successfully executed command (validation errors are captured in stderr, not here). */
|
|
30
|
+
results: Omit<TestCliResult, 'stdout' | 'stderr'>[];
|
|
31
|
+
/** All output from the entire REPL session. */
|
|
32
|
+
stdout: unknown[];
|
|
33
|
+
/** All errors from the entire REPL session. */
|
|
34
|
+
stderr: string[];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Fluent builder for setting up CLI test scenarios.
|
|
39
|
+
*/
|
|
40
|
+
export type TestCliBuilder = {
|
|
41
|
+
/** Set the CLI input string (e.g. `'deploy --env production'`). */
|
|
42
|
+
args(input: string): TestCliBuilder;
|
|
43
|
+
/** Set environment variables visible to the command. */
|
|
44
|
+
env(vars: Record<string, string | undefined>): TestCliBuilder;
|
|
45
|
+
/** Provide mock answers for interactive prompts. Keys are field names. */
|
|
46
|
+
prompt(answers: Record<string, unknown>): TestCliBuilder;
|
|
47
|
+
/** Provide mock stdin data (simulates piped input). */
|
|
48
|
+
stdin(data: string): TestCliBuilder;
|
|
49
|
+
/**
|
|
50
|
+
* Execute a single command via `eval()` and return the result with captured I/O.
|
|
51
|
+
* @param input - Optional CLI input string. Overrides `.args()` if provided.
|
|
52
|
+
*/
|
|
53
|
+
run(input?: string): Promise<TestCliResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Run a REPL session with the given sequence of inputs.
|
|
56
|
+
* Each string in the array is fed as one line of input.
|
|
57
|
+
* The session ends after all inputs are consumed (EOF).
|
|
58
|
+
*/
|
|
59
|
+
repl(inputs: string[]): Promise<TestReplResult>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a fluent test builder for a Padrone program.
|
|
64
|
+
* Captures all I/O and provides a clean interface for assertions.
|
|
65
|
+
*
|
|
66
|
+
* Works with any test framework (bun:test, vitest, jest, node:test, etc.).
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { testCli } from 'padrone/test'
|
|
71
|
+
*
|
|
72
|
+
* const result = await testCli(myProgram)
|
|
73
|
+
* .args('deploy --env production')
|
|
74
|
+
* .env({ API_KEY: 'xxx' })
|
|
75
|
+
* .run()
|
|
76
|
+
*
|
|
77
|
+
* expect(result.result).toBe('Deployed')
|
|
78
|
+
* expect(result.stdout).toContain('Deploying...')
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* // Shorthand: pass input directly to run()
|
|
84
|
+
* const result = await testCli(myProgram).run('deploy --env production')
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* // Test interactive prompts
|
|
90
|
+
* const result = await testCli(myProgram)
|
|
91
|
+
* .args('init')
|
|
92
|
+
* .prompt({ name: 'myapp', template: 'react' })
|
|
93
|
+
* .run()
|
|
94
|
+
*
|
|
95
|
+
* expect(result.args).toEqual({ name: 'myapp', template: 'react' })
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // Test REPL sessions
|
|
101
|
+
* const { results } = await testCli(myProgram)
|
|
102
|
+
* .repl(['greet World', 'add --a=2 --b=3'])
|
|
103
|
+
*
|
|
104
|
+
* expect(results[0].result).toBe('Hello, World!')
|
|
105
|
+
* expect(results[1].result).toBe(5)
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
/**
|
|
109
|
+
* Any program-like object that has `eval`, `runtime`, and `repl` methods.
|
|
110
|
+
* Avoids strict variance issues with `AnyPadroneProgram`.
|
|
111
|
+
*/
|
|
112
|
+
type TestableProgram = {
|
|
113
|
+
eval: (input: string, prefs?: Record<string, unknown>) => any;
|
|
114
|
+
runtime: (runtime: PadroneRuntime) => TestableProgram;
|
|
115
|
+
repl: (options?: { greeting?: false; hint?: false }) => AsyncIterable<any>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export function testCli(program: TestableProgram): TestCliBuilder {
|
|
119
|
+
let input: string | undefined;
|
|
120
|
+
let envVars: Record<string, string | undefined> | undefined;
|
|
121
|
+
let promptAnswers: Record<string, unknown> | undefined;
|
|
122
|
+
let stdinData: string | undefined;
|
|
123
|
+
|
|
124
|
+
const builder: TestCliBuilder = {
|
|
125
|
+
args(args: string) {
|
|
126
|
+
input = args;
|
|
127
|
+
return builder;
|
|
128
|
+
},
|
|
129
|
+
env(vars) {
|
|
130
|
+
envVars = vars;
|
|
131
|
+
return builder;
|
|
132
|
+
},
|
|
133
|
+
prompt(answers) {
|
|
134
|
+
promptAnswers = answers;
|
|
135
|
+
return builder;
|
|
136
|
+
},
|
|
137
|
+
stdin(data: string) {
|
|
138
|
+
stdinData = data;
|
|
139
|
+
return builder;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
async run(runInput?: string) {
|
|
143
|
+
const stdout: unknown[] = [];
|
|
144
|
+
const stderr: string[] = [];
|
|
145
|
+
|
|
146
|
+
const runtime = buildRuntime(stdout, stderr, { envVars, promptAnswers, stdinData });
|
|
147
|
+
const testProgram = program.runtime(runtime);
|
|
148
|
+
|
|
149
|
+
const evalResult = await testProgram.eval(runInput ?? input ?? '', {});
|
|
150
|
+
if (evalResult.error) {
|
|
151
|
+
stderr.push(evalResult.error instanceof Error ? evalResult.error.message : String(evalResult.error));
|
|
152
|
+
}
|
|
153
|
+
return toTestResult(evalResult, stdout, stderr);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async repl(inputs: string[]) {
|
|
157
|
+
const stdout: unknown[] = [];
|
|
158
|
+
const stderr: string[] = [];
|
|
159
|
+
|
|
160
|
+
const runtime = buildRuntime(stdout, stderr, {
|
|
161
|
+
envVars,
|
|
162
|
+
promptAnswers,
|
|
163
|
+
readLine: createMockReadLine(inputs),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const testProgram = program.runtime(runtime);
|
|
167
|
+
const results: Omit<TestCliResult, 'stdout' | 'stderr'>[] = [];
|
|
168
|
+
|
|
169
|
+
for await (const r of testProgram.repl({ greeting: false, hint: false })) {
|
|
170
|
+
results.push({
|
|
171
|
+
command: r.command!,
|
|
172
|
+
args: r.args,
|
|
173
|
+
result: r.result,
|
|
174
|
+
issues: r.argsResult?.issues as TestCliResult['issues'],
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { results, stdout, stderr };
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return builder;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function toTestResult(evalResult: PadroneCommandResult, stdout: unknown[], stderr: string[]): TestCliResult {
|
|
186
|
+
return {
|
|
187
|
+
command: evalResult.command!,
|
|
188
|
+
args: evalResult.args,
|
|
189
|
+
result: evalResult.result,
|
|
190
|
+
error: evalResult.error,
|
|
191
|
+
issues: evalResult.argsResult?.issues as TestCliResult['issues'],
|
|
192
|
+
stdout,
|
|
193
|
+
stderr,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function buildRuntime(
|
|
198
|
+
stdout: unknown[],
|
|
199
|
+
stderr: string[],
|
|
200
|
+
opts: {
|
|
201
|
+
envVars?: Record<string, string | undefined>;
|
|
202
|
+
promptAnswers?: Record<string, unknown>;
|
|
203
|
+
readLine?: (prompt: string) => Promise<string | null>;
|
|
204
|
+
stdinData?: string;
|
|
205
|
+
},
|
|
206
|
+
): PadroneRuntime {
|
|
207
|
+
const runtime: PadroneRuntime = {
|
|
208
|
+
output: (...args: unknown[]) => stdout.push(...args),
|
|
209
|
+
error: (text: string) => stderr.push(text),
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
if (opts.envVars) {
|
|
213
|
+
runtime.env = () => opts.envVars!;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (opts.promptAnswers) {
|
|
217
|
+
runtime.interactive = 'supported';
|
|
218
|
+
runtime.prompt = async (config: InteractivePromptConfig) => opts.promptAnswers![config.name];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (opts.readLine) {
|
|
222
|
+
runtime.readLine = opts.readLine;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (opts.stdinData !== undefined) {
|
|
226
|
+
runtime.stdin = {
|
|
227
|
+
isTTY: false,
|
|
228
|
+
async text() {
|
|
229
|
+
return opts.stdinData!;
|
|
230
|
+
},
|
|
231
|
+
async *lines() {
|
|
232
|
+
const lines = opts.stdinData!.split('\n');
|
|
233
|
+
// Remove trailing empty line from final newline (matches readline behavior)
|
|
234
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
|
|
235
|
+
for (const line of lines) {
|
|
236
|
+
yield line;
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
} else {
|
|
241
|
+
// No stdin data: simulate a TTY (no piped input) to avoid reading from process.stdin
|
|
242
|
+
runtime.stdin = {
|
|
243
|
+
isTTY: true,
|
|
244
|
+
async text() {
|
|
245
|
+
return '';
|
|
246
|
+
},
|
|
247
|
+
async *lines() {
|
|
248
|
+
// no lines
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return runtime;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function createMockReadLine(inputs: string[]): (prompt: string) => Promise<string | null> {
|
|
257
|
+
let index = 0;
|
|
258
|
+
return async (_prompt: string): Promise<string | null> => {
|
|
259
|
+
if (index >= inputs.length) return null;
|
|
260
|
+
return inputs[index++] ?? null;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResolvedPadroneRuntime } from '
|
|
1
|
+
import type { ResolvedPadroneRuntime } from '../core/runtime.ts';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Configuration for the update check feature.
|
|
@@ -92,9 +92,9 @@ export function isNewerVersion(current: string, latest: string): boolean {
|
|
|
92
92
|
/**
|
|
93
93
|
* Reads the update check cache file.
|
|
94
94
|
*/
|
|
95
|
-
function readCache(cachePath: string): CacheData | undefined {
|
|
95
|
+
async function readCache(cachePath: string): Promise<CacheData | undefined> {
|
|
96
96
|
try {
|
|
97
|
-
const { existsSync, readFileSync } =
|
|
97
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
98
98
|
if (!existsSync(cachePath)) return undefined;
|
|
99
99
|
const data = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
100
100
|
if (typeof data.lastCheck === 'number' && typeof data.latestVersion === 'string') {
|
|
@@ -109,10 +109,10 @@ function readCache(cachePath: string): CacheData | undefined {
|
|
|
109
109
|
/**
|
|
110
110
|
* Writes the update check cache file.
|
|
111
111
|
*/
|
|
112
|
-
function writeCache(cachePath: string, data: CacheData): void {
|
|
112
|
+
async function writeCache(cachePath: string, data: CacheData): Promise<void> {
|
|
113
113
|
try {
|
|
114
|
-
const { existsSync, mkdirSync, writeFileSync } =
|
|
115
|
-
const { dirname } =
|
|
114
|
+
const { existsSync, mkdirSync, writeFileSync } = await import('node:fs');
|
|
115
|
+
const { dirname } = await import('node:path');
|
|
116
116
|
const dir = dirname(cachePath);
|
|
117
117
|
if (!existsSync(dir)) {
|
|
118
118
|
mkdirSync(dir, { recursive: true });
|
|
@@ -126,9 +126,9 @@ function writeCache(cachePath: string, data: CacheData): void {
|
|
|
126
126
|
/**
|
|
127
127
|
* Resolves the cache path, expanding `~` to the home directory.
|
|
128
128
|
*/
|
|
129
|
-
function resolveCachePath(cachePath: string): string {
|
|
130
|
-
const { homedir } =
|
|
131
|
-
const { resolve } =
|
|
129
|
+
async function resolveCachePath(cachePath: string): Promise<string> {
|
|
130
|
+
const { homedir } = await import('node:os');
|
|
131
|
+
const { resolve } = await import('node:path');
|
|
132
132
|
if (cachePath.startsWith('~')) {
|
|
133
133
|
return cachePath.replace('~', homedir());
|
|
134
134
|
}
|
|
@@ -173,28 +173,28 @@ export function formatUpdateMessage(currentVersion: string, latestVersion: strin
|
|
|
173
173
|
* This is designed to be non-blocking: the check starts immediately but the
|
|
174
174
|
* result is only consumed after command execution completes.
|
|
175
175
|
*/
|
|
176
|
-
export function createUpdateChecker(
|
|
176
|
+
export async function createUpdateChecker(
|
|
177
177
|
programName: string,
|
|
178
178
|
currentVersion: string,
|
|
179
179
|
config: UpdateCheckConfig,
|
|
180
180
|
runtime: ResolvedPadroneRuntime,
|
|
181
|
-
): () => void {
|
|
181
|
+
): Promise<() => void> {
|
|
182
182
|
const packageName = config.packageName ?? programName;
|
|
183
183
|
const registry = config.registry ?? 'npm';
|
|
184
184
|
const intervalMs = parseInterval(config.interval ?? '1d');
|
|
185
185
|
const disableEnvVar = config.disableEnvVar ?? `${programName.toUpperCase().replace(/-/g, '_')}_NO_UPDATE_CHECK`;
|
|
186
186
|
|
|
187
187
|
const defaultCachePath = `~/.config/${programName}-update-check.json`;
|
|
188
|
-
const cachePath = resolveCachePath(config.cache ?? defaultCachePath);
|
|
188
|
+
const cachePath = await resolveCachePath(config.cache ?? defaultCachePath);
|
|
189
189
|
|
|
190
190
|
// Check if disabled
|
|
191
191
|
const env = runtime.env();
|
|
192
192
|
if (env.CI || env.CONTINUOUS_INTEGRATION) return noop;
|
|
193
193
|
if (env[disableEnvVar]) return noop;
|
|
194
|
-
if (
|
|
194
|
+
if (runtime.terminal && !runtime.terminal.isTTY) return noop;
|
|
195
195
|
|
|
196
196
|
// Check cache — if we checked recently, use cached result
|
|
197
|
-
const cached = readCache(cachePath);
|
|
197
|
+
const cached = await readCache(cachePath);
|
|
198
198
|
if (cached && Date.now() - cached.lastCheck < intervalMs) {
|
|
199
199
|
// Use cached version for display
|
|
200
200
|
if (isNewerVersion(currentVersion, cached.latestVersion)) {
|
|
@@ -206,9 +206,9 @@ export function createUpdateChecker(
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// Start background fetch
|
|
209
|
-
const fetchPromise = fetchLatestVersion(packageName, registry).then((latestVersion) => {
|
|
209
|
+
const fetchPromise = fetchLatestVersion(packageName, registry).then(async (latestVersion) => {
|
|
210
210
|
if (latestVersion) {
|
|
211
|
-
writeCache(cachePath, { lastCheck: Date.now(), latestVersion });
|
|
211
|
+
await writeCache(cachePath, { lastCheck: Date.now(), latestVersion });
|
|
212
212
|
if (isNewerVersion(currentVersion, latestVersion)) {
|
|
213
213
|
return latestVersion;
|
|
214
214
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
-
import { ValidationError } from '
|
|
3
|
-
import type { PadroneSchema } from '
|
|
2
|
+
import { ValidationError } from '../core/errors.ts';
|
|
3
|
+
import type { PadroneSchema } from '../types/index.ts';
|
|
4
|
+
import { concatBytes } from '../util/stream.ts';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Configuration for wrapping an external CLI tool.
|
|
@@ -160,21 +161,22 @@ export function createWrapHandler<TCommandArgs extends PadroneSchema, TWrapArgs
|
|
|
160
161
|
stdio: inheritStdio ? 'inherit' : ['ignore', 'pipe', 'pipe'],
|
|
161
162
|
});
|
|
162
163
|
|
|
163
|
-
const stdoutChunks:
|
|
164
|
-
const stderrChunks:
|
|
164
|
+
const stdoutChunks: Uint8Array[] = [];
|
|
165
|
+
const stderrChunks: Uint8Array[] = [];
|
|
165
166
|
|
|
166
167
|
if (!inheritStdio) {
|
|
167
|
-
proc.stdout!.on('data', (chunk:
|
|
168
|
-
proc.stderr!.on('data', (chunk:
|
|
168
|
+
proc.stdout!.on('data', (chunk: Uint8Array) => stdoutChunks.push(chunk));
|
|
169
|
+
proc.stderr!.on('data', (chunk: Uint8Array) => stderrChunks.push(chunk));
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
const decoder = new TextDecoder();
|
|
171
173
|
proc.on('error', reject);
|
|
172
174
|
proc.on('close', (code) => {
|
|
173
175
|
const exitCode = code ?? 1;
|
|
174
176
|
resolve({
|
|
175
177
|
exitCode,
|
|
176
|
-
stdout: inheritStdio ? undefined :
|
|
177
|
-
stderr: inheritStdio ? undefined :
|
|
178
|
+
stdout: inheritStdio ? undefined : decoder.decode(concatBytes(stdoutChunks)),
|
|
179
|
+
stderr: inheritStdio ? undefined : decoder.decode(concatBytes(stderrChunks)),
|
|
178
180
|
success: exitCode === 0,
|
|
179
181
|
});
|
|
180
182
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,51 +1,136 @@
|
|
|
1
|
-
export
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export type { PadroneErrorOptions } from './errors.ts';
|
|
5
|
-
export { ActionError, ConfigError, PadroneError, RoutingError, ValidationError } from './errors.ts';
|
|
6
|
-
export
|
|
7
|
-
export
|
|
1
|
+
export { buildReplCompleter } from './core/commands.ts';
|
|
2
|
+
export type { PadroneOptions } from './core/create.ts';
|
|
3
|
+
export { createPadrone, defineCommand } from './core/create.ts';
|
|
4
|
+
export type { PadroneErrorOptions } from './core/errors.ts';
|
|
5
|
+
export { ActionError, ConfigError, PadroneError, RoutingError, SignalError, ValidationError } from './core/errors.ts';
|
|
6
|
+
export { defineInterceptor } from './core/interceptors.ts';
|
|
7
|
+
export { asyncSchema } from './core/results.ts';
|
|
8
8
|
export type {
|
|
9
9
|
InteractiveMode,
|
|
10
10
|
InteractivePromptConfig,
|
|
11
|
+
PadroneBarAnimation,
|
|
12
|
+
PadroneBarChar,
|
|
13
|
+
PadroneBarConfig,
|
|
11
14
|
PadroneProgressIndicator,
|
|
12
15
|
PadroneProgressOptions,
|
|
16
|
+
PadroneProgressShow,
|
|
17
|
+
PadroneProgressUpdate,
|
|
13
18
|
PadroneRuntime,
|
|
19
|
+
PadroneSignal,
|
|
14
20
|
PadroneSpinnerConfig,
|
|
15
21
|
PadroneSpinnerPreset,
|
|
16
|
-
} from './runtime.ts';
|
|
17
|
-
export { REPL_SIGINT } from './runtime.ts';
|
|
18
|
-
export type {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
} from './core/runtime.ts';
|
|
23
|
+
export { REPL_SIGINT } from './core/runtime.ts';
|
|
24
|
+
export type {
|
|
25
|
+
HelpCommand,
|
|
26
|
+
InkOptions,
|
|
27
|
+
OtelSpan,
|
|
28
|
+
OtelTracer,
|
|
29
|
+
OtelTracerProvider,
|
|
30
|
+
PadroneLogger,
|
|
31
|
+
PadroneLoggerConfig,
|
|
32
|
+
PadroneLogLevel,
|
|
33
|
+
PadroneProgressConfig,
|
|
34
|
+
PadroneProgressDefaults,
|
|
35
|
+
PadroneProgressMessage,
|
|
36
|
+
PadroneProgressMessages,
|
|
37
|
+
PadroneProgressRenderer,
|
|
38
|
+
PadroneTracer,
|
|
39
|
+
PadroneTracingConfig,
|
|
40
|
+
VersionCommand,
|
|
41
|
+
WithCompletion,
|
|
42
|
+
WithHelp,
|
|
43
|
+
WithLogger,
|
|
44
|
+
WithMan,
|
|
45
|
+
WithMcp,
|
|
46
|
+
WithProgress,
|
|
47
|
+
WithRepl,
|
|
48
|
+
WithServe,
|
|
49
|
+
WithTracing,
|
|
50
|
+
WithVersion,
|
|
51
|
+
} from './extension/index.ts';
|
|
52
|
+
export {
|
|
53
|
+
createTerminalProgress,
|
|
54
|
+
isReactElement,
|
|
55
|
+
padroneAutoOutput,
|
|
56
|
+
padroneColor,
|
|
57
|
+
padroneCompletion,
|
|
58
|
+
padroneConfig,
|
|
59
|
+
padroneEnv,
|
|
60
|
+
padroneHelp,
|
|
61
|
+
padroneInk,
|
|
62
|
+
padroneInteractive,
|
|
63
|
+
padroneLogger,
|
|
64
|
+
padroneMan,
|
|
65
|
+
padroneMcp,
|
|
66
|
+
padroneProgress,
|
|
67
|
+
padroneRepl,
|
|
68
|
+
padroneServe,
|
|
69
|
+
padroneSignalHandling,
|
|
70
|
+
padroneStdin,
|
|
71
|
+
padroneSuggestions,
|
|
72
|
+
padroneTiming,
|
|
73
|
+
padroneTracing,
|
|
74
|
+
padroneUpdateCheck,
|
|
75
|
+
padroneVersion,
|
|
76
|
+
} from './extension/index.ts';
|
|
77
|
+
export type { PadroneMcpPreferences } from './feature/mcp.ts';
|
|
78
|
+
export type { UpdateCheckConfig } from './feature/update-check.ts';
|
|
79
|
+
export type { WrapConfig, WrapResult } from './feature/wrap.ts';
|
|
80
|
+
export type { AnsiStyle, ColorConfig, ColorTheme } from './output/colorizer.ts';
|
|
81
|
+
export { colorThemes } from './output/colorizer.ts';
|
|
82
|
+
export type { HelpInfo } from './output/formatter.ts';
|
|
83
|
+
export type { PadroneOutputIndicator } from './output/output-indicator.ts';
|
|
84
|
+
export type { KeyValueOptions, ListItem, ListOptions, TableOptions, TreeNode, TreeOptions } from './output/primitives.ts';
|
|
85
|
+
export type { OutputContext, OutputFormat } from './output/styling.ts';
|
|
22
86
|
export type {
|
|
23
87
|
AnyPadroneBuilder,
|
|
24
88
|
AnyPadroneCommand,
|
|
25
89
|
AnyPadroneProgram,
|
|
26
90
|
AsyncPadroneSchema,
|
|
91
|
+
CommandTypesBase,
|
|
92
|
+
DefineCommand,
|
|
93
|
+
ExtractInterceptorContext,
|
|
94
|
+
ExtractInterceptorRequires,
|
|
95
|
+
GetArgsMeta,
|
|
96
|
+
InterceptorBaseContext,
|
|
97
|
+
InterceptorDefBuilder,
|
|
98
|
+
InterceptorErrorContext,
|
|
99
|
+
InterceptorErrorResult,
|
|
100
|
+
InterceptorExecuteContext,
|
|
101
|
+
InterceptorExecuteResult,
|
|
102
|
+
InterceptorFactory,
|
|
103
|
+
InterceptorMeta,
|
|
104
|
+
InterceptorParseContext,
|
|
105
|
+
InterceptorParseResult,
|
|
106
|
+
InterceptorPhases,
|
|
107
|
+
InterceptorShutdownContext,
|
|
108
|
+
InterceptorStartContext,
|
|
109
|
+
InterceptorValidateContext,
|
|
110
|
+
InterceptorValidateResult,
|
|
27
111
|
PadroneActionContext,
|
|
28
112
|
PadroneBuilder,
|
|
29
113
|
PadroneCommand,
|
|
30
114
|
PadroneCommandResult,
|
|
115
|
+
PadroneContextInterceptor,
|
|
31
116
|
PadroneDrainResult,
|
|
117
|
+
PadroneExtension,
|
|
118
|
+
PadroneInterceptor,
|
|
119
|
+
PadroneInterceptorFn,
|
|
32
120
|
PadroneParseResult,
|
|
33
|
-
PadronePlugin,
|
|
34
121
|
PadroneProgram,
|
|
35
|
-
|
|
36
|
-
PadroneProgressPrefs as PadroneProgressConfig,
|
|
122
|
+
PadroneProgramMeta,
|
|
37
123
|
PadroneSchema,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} from './
|
|
50
|
-
export type {
|
|
51
|
-
export type { WrapConfig, WrapResult } from './wrap.ts';
|
|
124
|
+
} from './types/index.ts';
|
|
125
|
+
export type { AsyncStreamMeta } from './util/stream.ts';
|
|
126
|
+
export { asyncStream } from './util/stream.ts';
|
|
127
|
+
export type {
|
|
128
|
+
InferArgsInput,
|
|
129
|
+
InferArgsOutput,
|
|
130
|
+
InferCommand,
|
|
131
|
+
InferContext,
|
|
132
|
+
InferContextProvided,
|
|
133
|
+
InferInterceptorContext,
|
|
134
|
+
InferInterceptorRequires,
|
|
135
|
+
} from './util/type-helpers.ts';
|
|
136
|
+
export type { Drained } from './util/type-utils.ts';
|