@jackwener/opencli 1.5.4 → 1.5.5
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/dist/cli-manifest.json +55 -0
- package/dist/cli.js +14 -14
- package/dist/clis/antigravity/serve.js +2 -2
- package/dist/clis/sinafinance/rolling-news.d.ts +4 -0
- package/dist/clis/sinafinance/rolling-news.js +40 -0
- package/dist/clis/sinafinance/stock.d.ts +8 -0
- package/dist/clis/sinafinance/stock.js +117 -0
- package/dist/commanderAdapter.js +26 -3
- package/dist/daemon.js +5 -4
- package/dist/errors.d.ts +29 -1
- package/dist/errors.js +49 -11
- package/dist/external.js +3 -3
- package/dist/main.js +2 -1
- package/dist/tui.js +2 -1
- package/docs/adapters/browser/sinafinance.md +56 -6
- package/extension/dist/background.js +1 -2
- package/extension/manifest.json +1 -1
- package/extension/package.json +1 -1
- package/extension/src/background.ts +2 -1
- package/package.json +1 -1
- package/src/cli.ts +14 -14
- package/src/clis/antigravity/serve.ts +2 -2
- package/src/clis/sinafinance/rolling-news.ts +42 -0
- package/src/clis/sinafinance/stock.ts +127 -0
- package/src/commanderAdapter.ts +25 -2
- package/src/daemon.ts +5 -4
- package/src/errors.ts +71 -10
- package/src/external.ts +3 -3
- package/src/main.ts +2 -1
- package/src/tui.ts +2 -1
package/src/errors.ts
CHANGED
|
@@ -4,48 +4,96 @@
|
|
|
4
4
|
* All errors thrown by the framework should extend CliError so that
|
|
5
5
|
* the top-level handler in commanderAdapter.ts can render consistent,
|
|
6
6
|
* helpful output with emoji-coded severity and actionable hints.
|
|
7
|
+
*
|
|
8
|
+
* ## Exit codes
|
|
9
|
+
*
|
|
10
|
+
* opencli follows Unix conventions (sysexits.h) for process exit codes:
|
|
11
|
+
*
|
|
12
|
+
* 0 Success
|
|
13
|
+
* 1 Generic / unexpected error
|
|
14
|
+
* 2 Argument / usage error (ArgumentError)
|
|
15
|
+
* 66 No input / empty result (EmptyResultError)
|
|
16
|
+
* 69 Service unavailable (BrowserConnectError, AdapterLoadError)
|
|
17
|
+
* 75 Temporary failure, retry later (TimeoutError) EX_TEMPFAIL
|
|
18
|
+
* 77 Permission denied / auth needed (AuthRequiredError)
|
|
19
|
+
* 78 Configuration error (ConfigError)
|
|
20
|
+
* 130 Interrupted by Ctrl-C (set by tui.ts SIGINT handler)
|
|
7
21
|
*/
|
|
8
22
|
|
|
23
|
+
// ── Exit code table ──────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export const EXIT_CODES = {
|
|
26
|
+
SUCCESS: 0,
|
|
27
|
+
GENERIC_ERROR: 1,
|
|
28
|
+
USAGE_ERROR: 2, // Bad arguments / command misuse
|
|
29
|
+
EMPTY_RESULT: 66, // No data / not found (EX_NOINPUT)
|
|
30
|
+
SERVICE_UNAVAIL:69, // Daemon / browser unavailable (EX_UNAVAILABLE)
|
|
31
|
+
TEMPFAIL: 75, // Timeout — try again later (EX_TEMPFAIL)
|
|
32
|
+
NOPERM: 77, // Auth required / permission (EX_NOPERM)
|
|
33
|
+
CONFIG_ERROR: 78, // Missing / invalid config (EX_CONFIG)
|
|
34
|
+
INTERRUPTED: 130, // Ctrl-C / SIGINT
|
|
35
|
+
} as const;
|
|
36
|
+
|
|
37
|
+
export type ExitCode = typeof EXIT_CODES[keyof typeof EXIT_CODES];
|
|
38
|
+
|
|
39
|
+
// ── Base class ───────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
9
41
|
export class CliError extends Error {
|
|
10
42
|
/** Machine-readable error code (e.g. 'BROWSER_CONNECT', 'AUTH_REQUIRED') */
|
|
11
43
|
readonly code: string;
|
|
12
44
|
/** Human-readable hint on how to fix the problem */
|
|
13
45
|
readonly hint?: string;
|
|
46
|
+
/** Unix process exit code — defaults to 1 (generic error) */
|
|
47
|
+
readonly exitCode: ExitCode;
|
|
14
48
|
|
|
15
|
-
constructor(code: string, message: string, hint?: string) {
|
|
49
|
+
constructor(code: string, message: string, hint?: string, exitCode: ExitCode = EXIT_CODES.GENERIC_ERROR) {
|
|
16
50
|
super(message);
|
|
17
51
|
this.name = new.target.name;
|
|
18
52
|
this.code = code;
|
|
19
53
|
this.hint = hint;
|
|
54
|
+
this.exitCode = exitCode;
|
|
20
55
|
}
|
|
21
56
|
}
|
|
22
57
|
|
|
58
|
+
// ── Typed subclasses ─────────────────────────────────────────────────────────
|
|
59
|
+
|
|
23
60
|
export type BrowserConnectKind = 'daemon-not-running' | 'extension-not-connected' | 'command-failed' | 'unknown';
|
|
24
61
|
|
|
25
62
|
export class BrowserConnectError extends CliError {
|
|
26
63
|
readonly kind: BrowserConnectKind;
|
|
27
64
|
constructor(message: string, hint?: string, kind: BrowserConnectKind = 'unknown') {
|
|
28
|
-
super('BROWSER_CONNECT', message, hint);
|
|
65
|
+
super('BROWSER_CONNECT', message, hint, EXIT_CODES.SERVICE_UNAVAIL);
|
|
29
66
|
this.kind = kind;
|
|
30
67
|
}
|
|
31
68
|
}
|
|
32
69
|
|
|
33
70
|
export class AdapterLoadError extends CliError {
|
|
34
|
-
constructor(message: string, hint?: string) {
|
|
71
|
+
constructor(message: string, hint?: string) {
|
|
72
|
+
super('ADAPTER_LOAD', message, hint, EXIT_CODES.SERVICE_UNAVAIL);
|
|
73
|
+
}
|
|
35
74
|
}
|
|
36
75
|
|
|
37
76
|
export class CommandExecutionError extends CliError {
|
|
38
|
-
constructor(message: string, hint?: string) {
|
|
77
|
+
constructor(message: string, hint?: string) {
|
|
78
|
+
super('COMMAND_EXEC', message, hint, EXIT_CODES.GENERIC_ERROR);
|
|
79
|
+
}
|
|
39
80
|
}
|
|
40
81
|
|
|
41
82
|
export class ConfigError extends CliError {
|
|
42
|
-
constructor(message: string, hint?: string) {
|
|
83
|
+
constructor(message: string, hint?: string) {
|
|
84
|
+
super('CONFIG', message, hint, EXIT_CODES.CONFIG_ERROR);
|
|
85
|
+
}
|
|
43
86
|
}
|
|
44
87
|
|
|
45
88
|
export class AuthRequiredError extends CliError {
|
|
46
89
|
readonly domain: string;
|
|
47
90
|
constructor(domain: string, message?: string) {
|
|
48
|
-
super(
|
|
91
|
+
super(
|
|
92
|
+
'AUTH_REQUIRED',
|
|
93
|
+
message ?? `Not logged in to ${domain}`,
|
|
94
|
+
`Please open Chrome and log in to https://${domain}`,
|
|
95
|
+
EXIT_CODES.NOPERM,
|
|
96
|
+
);
|
|
49
97
|
this.domain = domain;
|
|
50
98
|
}
|
|
51
99
|
}
|
|
@@ -56,27 +104,40 @@ export class TimeoutError extends CliError {
|
|
|
56
104
|
'TIMEOUT',
|
|
57
105
|
`${label} timed out after ${seconds}s`,
|
|
58
106
|
hint ?? 'Try again, or increase timeout with OPENCLI_BROWSER_COMMAND_TIMEOUT env var',
|
|
107
|
+
EXIT_CODES.TEMPFAIL,
|
|
59
108
|
);
|
|
60
109
|
}
|
|
61
110
|
}
|
|
62
111
|
|
|
63
112
|
export class ArgumentError extends CliError {
|
|
64
|
-
constructor(message: string, hint?: string) {
|
|
113
|
+
constructor(message: string, hint?: string) {
|
|
114
|
+
super('ARGUMENT', message, hint, EXIT_CODES.USAGE_ERROR);
|
|
115
|
+
}
|
|
65
116
|
}
|
|
66
117
|
|
|
67
118
|
export class EmptyResultError extends CliError {
|
|
68
119
|
constructor(command: string, hint?: string) {
|
|
69
|
-
super(
|
|
120
|
+
super(
|
|
121
|
+
'EMPTY_RESULT',
|
|
122
|
+
`${command} returned no data`,
|
|
123
|
+
hint ?? 'The page structure may have changed, or you may need to log in',
|
|
124
|
+
EXIT_CODES.EMPTY_RESULT,
|
|
125
|
+
);
|
|
70
126
|
}
|
|
71
127
|
}
|
|
72
128
|
|
|
73
129
|
export class SelectorError extends CliError {
|
|
74
130
|
constructor(selector: string, hint?: string) {
|
|
75
|
-
super(
|
|
131
|
+
super(
|
|
132
|
+
'SELECTOR',
|
|
133
|
+
`Could not find element: ${selector}`,
|
|
134
|
+
hint ?? 'The page UI may have changed. Please report this issue.',
|
|
135
|
+
EXIT_CODES.GENERIC_ERROR,
|
|
136
|
+
);
|
|
76
137
|
}
|
|
77
138
|
}
|
|
78
139
|
|
|
79
|
-
// ── Utilities
|
|
140
|
+
// ── Utilities ───────────────────────────────────────────────────────────────
|
|
80
141
|
|
|
81
142
|
/** Extract a human-readable message from an unknown caught value. */
|
|
82
143
|
export function getErrorMessage(error: unknown): string {
|
package/src/external.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { spawnSync, execFileSync } from 'node:child_process';
|
|
|
6
6
|
import yaml from 'js-yaml';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { log } from './logger.js';
|
|
9
|
-
import { getErrorMessage } from './errors.js';
|
|
9
|
+
import { EXIT_CODES, getErrorMessage } from './errors.js';
|
|
10
10
|
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
|
|
@@ -180,7 +180,7 @@ export function executeExternalCli(name: string, args: string[], preloaded?: Ext
|
|
|
180
180
|
// 2. Try to auto install
|
|
181
181
|
const success = installExternalCli(cli);
|
|
182
182
|
if (!success) {
|
|
183
|
-
process.exitCode =
|
|
183
|
+
process.exitCode = EXIT_CODES.SERVICE_UNAVAIL;
|
|
184
184
|
return;
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -189,7 +189,7 @@ export function executeExternalCli(name: string, args: string[], preloaded?: Ext
|
|
|
189
189
|
const result = spawnSync(cli.binary, args, { stdio: 'inherit' });
|
|
190
190
|
if (result.error) {
|
|
191
191
|
console.error(chalk.red(`Failed to execute '${cli.binary}': ${result.error.message}`));
|
|
192
|
-
process.exitCode =
|
|
192
|
+
process.exitCode = EXIT_CODES.GENERIC_ERROR;
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
195
|
|
package/src/main.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { runCli } from './cli.js';
|
|
|
22
22
|
import { emitHook } from './hooks.js';
|
|
23
23
|
import { installNodeNetwork } from './node-network.js';
|
|
24
24
|
import { registerUpdateNoticeOnExit, checkForUpdateBackground } from './update-check.js';
|
|
25
|
+
import { EXIT_CODES } from './errors.js';
|
|
25
26
|
|
|
26
27
|
installNodeNetwork();
|
|
27
28
|
|
|
@@ -57,7 +58,7 @@ if (getCompIdx !== -1) {
|
|
|
57
58
|
if (cursor === undefined) cursor = words.length;
|
|
58
59
|
const candidates = getCompletions(words, cursor);
|
|
59
60
|
process.stdout.write(candidates.join('\n') + '\n');
|
|
60
|
-
process.exit(
|
|
61
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
await emitHook('onStartup', { command: '__startup__', args: {} });
|
package/src/tui.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Uses raw stdin mode + ANSI escape codes for interactive prompts.
|
|
5
5
|
*/
|
|
6
6
|
import chalk from 'chalk';
|
|
7
|
+
import { EXIT_CODES } from './errors.js';
|
|
7
8
|
|
|
8
9
|
export interface CheckboxItem {
|
|
9
10
|
label: string;
|
|
@@ -161,7 +162,7 @@ export async function checkboxPrompt(
|
|
|
161
162
|
// Ctrl+C — exit process
|
|
162
163
|
if (key === '\x03') {
|
|
163
164
|
cleanup();
|
|
164
|
-
process.exit(
|
|
165
|
+
process.exit(EXIT_CODES.INTERRUPTED);
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
|