@agentrhq/webcmd 0.3.0 → 0.3.2
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/README.md +13 -2
- package/cli-manifest.json +228 -24
- package/clis/chatgpt/ask.js +1 -1
- package/clis/chatgpt/ask.test.js +11 -0
- package/clis/chatgpt/commands.test.js +102 -1
- package/clis/chatgpt/deep-research-result.js +45 -3
- package/clis/chatgpt/image.js +22 -2
- package/clis/chatgpt/project-file-add.js +24 -1
- package/clis/chatgpt/utils.js +258 -52
- package/clis/chatgpt/utils.test.js +259 -1
- package/clis/claude/ask.js +21 -1
- package/clis/facebook/feed.js +110 -24
- package/clis/facebook/feed.test.js +62 -0
- package/clis/facebook/search.js +185 -38
- package/clis/facebook/search.test.js +95 -50
- package/clis/instagram/explore.js +30 -12
- package/clis/instagram/explore.test.js +77 -0
- package/clis/instagram/post.js +14 -1
- package/clis/instagram/reel.js +13 -1
- package/clis/linkedin/company.js +153 -0
- package/clis/linkedin/company.test.js +111 -0
- package/clis/linkedin/connections.js +135 -0
- package/clis/linkedin/connections.test.js +141 -0
- package/clis/mercury/reimbursement-draft.js +12 -1
- package/clis/producthunt/hot.js +1 -2
- package/clis/spotify/spotify.js +11 -11
- package/clis/twitter/article-evaluate.test.js +27 -0
- package/clis/twitter/article.js +73 -16
- package/clis/twitter/article.test.js +209 -0
- package/clis/twitter/download.js +6 -1
- package/clis/twitter/post.js +14 -1
- package/clis/twitter/profile.js +1 -1
- package/clis/twitter/profile.test.js +8 -0
- package/clis/twitter/quote.js +11 -1
- package/clis/twitter/reply.js +11 -1
- package/dist/src/browser/base-page.d.ts +26 -0
- package/dist/src/browser/base-page.js +23 -8
- package/dist/src/browser/base-page.test.js +45 -12
- package/dist/src/browser/command-catalog.d.ts +6 -0
- package/dist/src/browser/command-catalog.js +259 -0
- package/dist/src/browser/command-catalog.test.d.ts +1 -0
- package/dist/src/browser/command-catalog.test.js +79 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.d.ts +17 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.js +106 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/darwin-background-launch.test.js +65 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +7 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.js +23 -9
- package/dist/src/browser/runtime/local-cloak/session-manager.test.js +36 -0
- package/dist/src/browser/target-resolver.d.ts +1 -0
- package/dist/src/browser/target-resolver.js +99 -4
- package/dist/src/browser/target-resolver.test.js +141 -1
- package/dist/src/build-manifest.d.ts +6 -0
- package/dist/src/build-manifest.js +18 -1
- package/dist/src/build-manifest.test.js +57 -1
- package/dist/src/builtin-command-surface.d.ts +9 -0
- package/dist/src/builtin-command-surface.js +16 -0
- package/dist/src/check-hosted-contract.test.d.ts +1 -0
- package/dist/src/check-hosted-contract.test.js +56 -0
- package/dist/src/cli.js +58 -106
- package/dist/src/cli.test.js +71 -1
- package/dist/src/command-presentation.d.ts +95 -0
- package/dist/src/command-presentation.js +486 -0
- package/dist/src/command-presentation.test.d.ts +1 -0
- package/dist/src/command-presentation.test.js +97 -0
- package/dist/src/command-surface.d.ts +43 -0
- package/dist/src/command-surface.js +205 -0
- package/dist/src/command-surface.test.d.ts +1 -0
- package/dist/src/command-surface.test.js +406 -0
- package/dist/src/commanderAdapter.d.ts +5 -1
- package/dist/src/commanderAdapter.js +12 -55
- package/dist/src/commanderAdapter.test.js +15 -3
- package/dist/src/community-plugin-sync.d.ts +11 -0
- package/dist/src/community-plugin-sync.js +138 -0
- package/dist/src/community-plugin-sync.test.d.ts +1 -0
- package/dist/src/community-plugin-sync.test.js +123 -0
- package/dist/src/completion-fast.d.ts +4 -4
- package/dist/src/completion-fast.js +15 -34
- package/dist/src/completion-shared.d.ts +4 -0
- package/dist/src/completion-shared.js +38 -0
- package/dist/src/completion.js +3 -27
- package/dist/src/completion.test.js +31 -2
- package/dist/src/docs-sync-review-cli.test.d.ts +1 -0
- package/dist/src/docs-sync-review-cli.test.js +322 -0
- package/dist/src/docs-sync-review.d.ts +117 -0
- package/dist/src/docs-sync-review.js +475 -0
- package/dist/src/docs-sync-review.test.d.ts +1 -0
- package/dist/src/docs-sync-review.test.js +324 -0
- package/dist/src/errors.d.ts +12 -3
- package/dist/src/errors.js +11 -7
- package/dist/src/errors.test.js +14 -1
- package/dist/src/execution.d.ts +1 -2
- package/dist/src/execution.js +4 -45
- package/dist/src/generate-release-notes-cli.test.js +7 -2
- package/dist/src/help.d.ts +4 -0
- package/dist/src/help.js +50 -255
- package/dist/src/help.test.js +27 -1
- package/dist/src/hosted/args.d.ts +2 -7
- package/dist/src/hosted/args.js +2 -99
- package/dist/src/hosted/args.test.js +15 -1
- package/dist/src/hosted/availability.d.ts +13 -0
- package/dist/src/hosted/availability.js +16 -0
- package/dist/src/hosted/availability.test.d.ts +1 -0
- package/dist/src/hosted/availability.test.js +191 -0
- package/dist/src/hosted/browser-args.d.ts +18 -0
- package/dist/src/hosted/browser-args.js +152 -0
- package/dist/src/hosted/browser-args.test.d.ts +1 -0
- package/dist/src/hosted/browser-args.test.js +182 -0
- package/dist/src/hosted/client.d.ts +31 -2
- package/dist/src/hosted/client.js +441 -53
- package/dist/src/hosted/client.test.js +651 -32
- package/dist/src/hosted/config.d.ts +7 -2
- package/dist/src/hosted/config.js +26 -4
- package/dist/src/hosted/config.test.js +34 -1
- package/dist/src/hosted/contract.d.ts +94 -0
- package/dist/src/hosted/contract.js +208 -0
- package/dist/src/hosted/contract.test.d.ts +1 -0
- package/dist/src/hosted/contract.test.js +361 -0
- package/dist/src/hosted/credentials.d.ts +38 -0
- package/dist/src/hosted/credentials.js +248 -0
- package/dist/src/hosted/credentials.test.d.ts +1 -0
- package/dist/src/hosted/credentials.test.js +93 -0
- package/dist/src/hosted/file-contract.test.d.ts +1 -0
- package/dist/src/hosted/file-contract.test.js +174 -0
- package/dist/src/hosted/files.d.ts +29 -0
- package/dist/src/hosted/files.js +296 -0
- package/dist/src/hosted/files.test.d.ts +1 -0
- package/dist/src/hosted/files.test.js +231 -0
- package/dist/src/hosted/main-lifecycle.test.d.ts +1 -0
- package/dist/src/hosted/main-lifecycle.test.js +213 -0
- package/dist/src/hosted/manifest.d.ts +5 -0
- package/dist/src/hosted/manifest.js +21 -66
- package/dist/src/hosted/manifest.test.js +120 -2
- package/dist/src/hosted/output-parity.test.d.ts +1 -0
- package/dist/src/hosted/output-parity.test.js +108 -0
- package/dist/src/hosted/root-command-surface.test.d.ts +1 -0
- package/dist/src/hosted/root-command-surface.test.js +646 -0
- package/dist/src/hosted/runner.d.ts +7 -0
- package/dist/src/hosted/runner.js +554 -253
- package/dist/src/hosted/runner.test.js +1471 -53
- package/dist/src/hosted/setup.d.ts +3 -2
- package/dist/src/hosted/setup.js +45 -15
- package/dist/src/hosted/setup.test.js +133 -6
- package/dist/src/hosted/types.d.ts +101 -23
- package/dist/src/main.js +120 -108
- package/dist/src/manifest-types.d.ts +2 -0
- package/dist/src/output.d.ts +17 -2
- package/dist/src/output.js +88 -81
- package/dist/src/output.test.js +141 -51
- package/dist/src/pipeline/executor.test.js +1 -0
- package/dist/src/pipeline/steps/download.test.js +1 -0
- package/dist/src/plugin-create-cli.test.d.ts +1 -0
- package/dist/src/plugin-create-cli.test.js +37 -0
- package/dist/src/plugin-manifest.d.ts +12 -0
- package/dist/src/plugin-manifest.js +16 -0
- package/dist/src/plugin-manifest.test.js +19 -1
- package/dist/src/plugin-scaffold.d.ts +4 -1
- package/dist/src/plugin-scaffold.js +4 -1
- package/dist/src/plugin-scaffold.test.js +23 -8
- package/dist/src/plugin.js +4 -1
- package/dist/src/plugin.test.js +13 -0
- package/dist/src/registry.d.ts +2 -0
- package/dist/src/release-notes.js +12 -4
- package/dist/src/release-notes.test.js +27 -15
- package/dist/src/root-command-surface.d.ts +31 -0
- package/dist/src/root-command-surface.js +106 -0
- package/dist/src/serialization.d.ts +1 -16
- package/dist/src/serialization.js +5 -55
- package/dist/src/stream-write.d.ts +12 -0
- package/dist/src/stream-write.js +91 -0
- package/dist/src/stream-write.test.d.ts +1 -0
- package/dist/src/stream-write.test.js +186 -0
- package/dist/src/types.d.ts +5 -0
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.js +2 -8
- package/dist/src/utils.test.js +50 -0
- package/hosted-contract.json +36759 -0
- package/package.json +6 -1
- package/scripts/check-hosted-contract.mjs +108 -0
- package/scripts/docs-sync-review.ts +332 -0
- package/scripts/sync-community-plugins.ts +14 -0
- package/skills/webcmd-autofix/SKILL.md +26 -37
- package/skills/webcmd-usage/SKILL.md +7 -0
package/dist/src/main.js
CHANGED
|
@@ -16,7 +16,7 @@ import * as fs from 'node:fs';
|
|
|
16
16
|
import * as os from 'node:os';
|
|
17
17
|
import * as path from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { getCompletionsFromManifest, hasAllManifests
|
|
19
|
+
import { getCompletionScriptFast, getCompletionsFromManifest, hasAllManifests } from './completion-fast.js';
|
|
20
20
|
import { findPackageRoot, getCliManifestPath } from './package-paths.js';
|
|
21
21
|
import { PKG_VERSION } from './version.js';
|
|
22
22
|
import { EXIT_CODES } from './errors.js';
|
|
@@ -42,44 +42,111 @@ if (typeof globalThis.Bun === 'undefined' && !isSupportedNodeVersion(process.ver
|
|
|
42
42
|
}
|
|
43
43
|
// Fast path: --version (only when it's the top-level intent, not passed to a subcommand)
|
|
44
44
|
// e.g. `webcmd --version` or `webcmd -V`, but NOT `webcmd gh --version`
|
|
45
|
+
let fastPathHandled = false;
|
|
45
46
|
if (argv[0] === '--version' || argv[0] === '-V') {
|
|
46
47
|
process.stdout.write(PKG_VERSION + '\n');
|
|
47
48
|
process.exit(EXIT_CODES.SUCCESS);
|
|
48
49
|
}
|
|
49
50
|
// Fast path: completion <shell> — print shell script without discovery
|
|
50
|
-
if (argv[0] === 'completion' && argv.length >= 2) {
|
|
51
|
-
|
|
51
|
+
if (!fastPathHandled && argv[0] === 'completion' && argv.length >= 2) {
|
|
52
|
+
const script = getCompletionScriptFast(argv[1]);
|
|
53
|
+
if (script !== undefined) {
|
|
54
|
+
process.stdout.write(script);
|
|
52
55
|
process.exit(EXIT_CODES.SUCCESS);
|
|
53
56
|
}
|
|
54
57
|
// Unknown shell — fall through to full path for proper error handling
|
|
55
58
|
}
|
|
56
59
|
// Hosted setup and hosted dispatch run before local adapter discovery. This is
|
|
57
60
|
// the mode boundary: hosted mode must not read ~/.webcmd/clis or local site
|
|
58
|
-
// memory just to decide what commands exist.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
// memory just to decide what commands exist. Awaiting the selected branch and
|
|
62
|
+
// assigning exitCode lets Node flush pending stdout/stderr before shutdown.
|
|
63
|
+
if (!fastPathHandled) {
|
|
64
|
+
if (argv[0] === 'setup') {
|
|
65
|
+
const { runHostedSetup } = await import('./hosted/setup.js');
|
|
66
|
+
process.exitCode = await runHostedSetup();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const { shouldUseHostedMode } = await import('./hosted/config.js');
|
|
70
|
+
if (shouldUseHostedMode()) {
|
|
71
|
+
const { runHostedCli } = await import('./hosted/runner.js');
|
|
72
|
+
const result = await runHostedCli(argv);
|
|
73
|
+
process.exitCode = result.exitCode;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await runLocalMain();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
69
79
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
async function runLocalMain() {
|
|
81
|
+
// Fast path: --get-completions — read from manifest, skip discovery
|
|
82
|
+
const getCompIdx = process.argv.indexOf('--get-completions');
|
|
83
|
+
if (getCompIdx !== -1) {
|
|
84
|
+
// Only include manifests that actually exist on disk.
|
|
85
|
+
// With sparse override, the user clis dir may exist but have no manifest.
|
|
86
|
+
const manifestPaths = [getCliManifestPath(BUILTIN_CLIS)];
|
|
87
|
+
const userManifest = getCliManifestPath(USER_CLIS);
|
|
88
|
+
try {
|
|
89
|
+
fs.accessSync(userManifest);
|
|
90
|
+
manifestPaths.push(userManifest);
|
|
91
|
+
}
|
|
92
|
+
catch { /* no user manifest */ }
|
|
93
|
+
if (hasAllManifests(manifestPaths)) {
|
|
94
|
+
const rest = process.argv.slice(getCompIdx + 1);
|
|
95
|
+
let cursor;
|
|
96
|
+
const words = [];
|
|
97
|
+
for (let i = 0; i < rest.length; i++) {
|
|
98
|
+
if (rest[i] === '--cursor' && i + 1 < rest.length) {
|
|
99
|
+
cursor = parseInt(rest[i + 1], 10);
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
words.push(rest[i]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (cursor === undefined)
|
|
107
|
+
cursor = words.length;
|
|
108
|
+
const candidates = getCompletionsFromManifest(words, cursor, manifestPaths);
|
|
109
|
+
process.stdout.write(candidates.join('\n') + '\n');
|
|
110
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
111
|
+
}
|
|
112
|
+
// No manifest — fall through to full discovery path below
|
|
113
|
+
}
|
|
114
|
+
// ── Full startup path ───────────────────────────────────────────────────
|
|
115
|
+
// Dynamic imports: these are deferred so the fast path above never pays the cost.
|
|
116
|
+
const { discoverClis, discoverPlugins, ensureUserCliCompatShims, ensureUserAdapters } = await import('./discovery.js');
|
|
117
|
+
const { getCompletions } = await import('./completion.js');
|
|
118
|
+
const { runCli } = await import('./cli.js');
|
|
119
|
+
const { emitHook } = await import('./hooks.js');
|
|
120
|
+
const { installNodeNetwork } = await import('./node-network.js');
|
|
121
|
+
const { registerUpdateNoticeOnExit, checkForUpdateBackground } = await import('./update-check.js');
|
|
122
|
+
installNodeNetwork();
|
|
123
|
+
// Parallelise independent startup I/O:
|
|
124
|
+
// - Built-in adapter discovery has no dependency on user-dir setup.
|
|
125
|
+
// - ensureUserCliCompatShims and ensureUserAdapters operate on different paths
|
|
126
|
+
// (~/.webcmd/node_modules/ vs ~/.webcmd/clis/ + adapter-manifest.json).
|
|
127
|
+
// - registerCommand() overwrites on name collision (see registry.ts), so
|
|
128
|
+
// user-CLI discovery MUST run after built-in discovery to preserve the
|
|
129
|
+
// intended override order (user adapters override built-in ones).
|
|
130
|
+
// - discoverPlugins runs last: plugins may override both built-in and user CLIs.
|
|
131
|
+
const skipUserDiscovery = argv[0] === 'convention-audit';
|
|
132
|
+
if (skipUserDiscovery) {
|
|
133
|
+
await discoverClis(BUILTIN_CLIS);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const [, ,] = await Promise.all([
|
|
137
|
+
ensureUserCliCompatShims(),
|
|
138
|
+
ensureUserAdapters(),
|
|
139
|
+
discoverClis(BUILTIN_CLIS),
|
|
140
|
+
]);
|
|
141
|
+
await discoverClis(USER_CLIS);
|
|
142
|
+
await discoverPlugins();
|
|
80
143
|
}
|
|
81
|
-
|
|
82
|
-
|
|
144
|
+
// Register exit hook: notice appears after command output (same as npm/gh/yarn)
|
|
145
|
+
registerUpdateNoticeOnExit();
|
|
146
|
+
// Kick off background fetch for next run (non-blocking)
|
|
147
|
+
checkForUpdateBackground();
|
|
148
|
+
// ── Fallback completion: manifest unavailable, use full registry ─────────
|
|
149
|
+
if (getCompIdx !== -1) {
|
|
83
150
|
const rest = process.argv.slice(getCompIdx + 1);
|
|
84
151
|
let cursor;
|
|
85
152
|
const words = [];
|
|
@@ -94,93 +161,38 @@ if (getCompIdx !== -1) {
|
|
|
94
161
|
}
|
|
95
162
|
if (cursor === undefined)
|
|
96
163
|
cursor = words.length;
|
|
97
|
-
const candidates =
|
|
164
|
+
const candidates = getCompletions(words, cursor);
|
|
98
165
|
process.stdout.write(candidates.join('\n') + '\n');
|
|
99
166
|
process.exit(EXIT_CODES.SUCCESS);
|
|
100
167
|
}
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
const {
|
|
106
|
-
const { getCompletions } = await import('./completion.js');
|
|
107
|
-
const { runCli } = await import('./cli.js');
|
|
108
|
-
const { emitHook } = await import('./hooks.js');
|
|
109
|
-
const { installNodeNetwork } = await import('./node-network.js');
|
|
110
|
-
const { registerUpdateNoticeOnExit, checkForUpdateBackground } = await import('./update-check.js');
|
|
111
|
-
installNodeNetwork();
|
|
112
|
-
// Parallelise independent startup I/O:
|
|
113
|
-
// - Built-in adapter discovery has no dependency on user-dir setup.
|
|
114
|
-
// - ensureUserCliCompatShims and ensureUserAdapters operate on different paths
|
|
115
|
-
// (~/.webcmd/node_modules/ vs ~/.webcmd/clis/ + adapter-manifest.json).
|
|
116
|
-
// - registerCommand() overwrites on name collision (see registry.ts), so
|
|
117
|
-
// user-CLI discovery MUST run after built-in discovery to preserve the
|
|
118
|
-
// intended override order (user adapters override built-in ones).
|
|
119
|
-
// - discoverPlugins runs last: plugins may override both built-in and user CLIs.
|
|
120
|
-
const skipUserDiscovery = argv[0] === 'convention-audit';
|
|
121
|
-
if (skipUserDiscovery) {
|
|
122
|
-
await discoverClis(BUILTIN_CLIS);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
const [, ,] = await Promise.all([
|
|
126
|
-
ensureUserCliCompatShims(),
|
|
127
|
-
ensureUserAdapters(),
|
|
128
|
-
discoverClis(BUILTIN_CLIS),
|
|
129
|
-
]);
|
|
130
|
-
await discoverClis(USER_CLIS);
|
|
131
|
-
await discoverPlugins();
|
|
132
|
-
}
|
|
133
|
-
// Register exit hook: notice appears after command output (same as npm/gh/yarn)
|
|
134
|
-
registerUpdateNoticeOnExit();
|
|
135
|
-
// Kick off background fetch for next run (non-blocking)
|
|
136
|
-
checkForUpdateBackground();
|
|
137
|
-
// ── Fallback completion: manifest unavailable, use full registry ─────────
|
|
138
|
-
if (getCompIdx !== -1) {
|
|
139
|
-
const rest = process.argv.slice(getCompIdx + 1);
|
|
140
|
-
let cursor;
|
|
141
|
-
const words = [];
|
|
142
|
-
for (let i = 0; i < rest.length; i++) {
|
|
143
|
-
if (rest[i] === '--cursor' && i + 1 < rest.length) {
|
|
144
|
-
cursor = parseInt(rest[i + 1], 10);
|
|
145
|
-
i++;
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
words.push(rest[i]);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (cursor === undefined)
|
|
152
|
-
cursor = words.length;
|
|
153
|
-
const candidates = getCompletions(words, cursor);
|
|
154
|
-
process.stdout.write(candidates.join('\n') + '\n');
|
|
155
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
156
|
-
}
|
|
157
|
-
// Rewrite `webcmd browser <session> <subcommand> ...` so commander (which
|
|
158
|
-
// can't combine a parent positional with subcommand dispatch) sees the internal
|
|
159
|
-
// `--session <name>` flag form. Also refuses the retired `webcmd browser
|
|
160
|
-
// --session foo ...` user form with a friendly usage error.
|
|
161
|
-
const { rewriteBrowserArgv, BrowserSessionArgvError, escapeLeadingDashPositional } = await import('./cli-argv-preprocess.js');
|
|
162
|
-
try {
|
|
163
|
-
let rewritten = rewriteBrowserArgv(process.argv.slice(2));
|
|
164
|
-
// Insert a `--` separator before a required positional whose value starts
|
|
165
|
-
// with `-` (e.g. opaque securityId tokens; #1160). Skipped when the
|
|
166
|
-
// manifest is unavailable so the user-cli / dev paths still work.
|
|
168
|
+
// Rewrite `webcmd browser <session> <subcommand> ...` so commander (which
|
|
169
|
+
// can't combine a parent positional with subcommand dispatch) sees the internal
|
|
170
|
+
// `--session <name>` flag form. Also refuses the retired `webcmd browser
|
|
171
|
+
// --session foo ...` user form with a friendly usage error.
|
|
172
|
+
const { rewriteBrowserArgv, BrowserSessionArgvError, escapeLeadingDashPositional } = await import('./cli-argv-preprocess.js');
|
|
167
173
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
let rewritten = rewriteBrowserArgv(process.argv.slice(2));
|
|
175
|
+
// Insert a `--` separator before a required positional whose value starts
|
|
176
|
+
// with `-` (e.g. opaque securityId tokens; #1160). Skipped when the
|
|
177
|
+
// manifest is unavailable so the user-cli / dev paths still work.
|
|
178
|
+
try {
|
|
179
|
+
const manifestPath = getCliManifestPath(BUILTIN_CLIS);
|
|
180
|
+
if (fs.existsSync(manifestPath)) {
|
|
181
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
182
|
+
if (Array.isArray(manifest))
|
|
183
|
+
rewritten = escapeLeadingDashPositional(rewritten, manifest);
|
|
184
|
+
}
|
|
173
185
|
}
|
|
186
|
+
catch { /* manifest unavailable; skip the dash escape */ }
|
|
187
|
+
process.argv.splice(2, process.argv.length - 2, ...rewritten);
|
|
174
188
|
}
|
|
175
|
-
catch {
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
process.exit(EXIT_CODES.GENERIC_ERROR);
|
|
189
|
+
catch (err) {
|
|
190
|
+
if (err instanceof BrowserSessionArgvError) {
|
|
191
|
+
process.stderr.write(`error: ${err.message}\n`);
|
|
192
|
+
process.exit(EXIT_CODES.GENERIC_ERROR);
|
|
193
|
+
}
|
|
194
|
+
throw err;
|
|
182
195
|
}
|
|
183
|
-
|
|
196
|
+
await emitHook('onStartup', { command: '__startup__', args: {} });
|
|
197
|
+
runCli(BUILTIN_CLIS, USER_CLIS);
|
|
184
198
|
}
|
|
185
|
-
await emitHook('onStartup', { command: '__startup__', args: {} });
|
|
186
|
-
runCli(BUILTIN_CLIS, USER_CLIS);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* runtime code needs from build-manifest is the `ManifestEntry` type,
|
|
7
7
|
* and that lives here.
|
|
8
8
|
*/
|
|
9
|
+
import type { FileArgumentContract } from './hosted/contract.js';
|
|
9
10
|
export interface ManifestEntry {
|
|
10
11
|
site: string;
|
|
11
12
|
name: string;
|
|
@@ -25,6 +26,7 @@ export interface ManifestEntry {
|
|
|
25
26
|
positional?: boolean;
|
|
26
27
|
help?: string;
|
|
27
28
|
choices?: string[];
|
|
29
|
+
file?: FileArgumentContract;
|
|
28
30
|
}>;
|
|
29
31
|
columns?: string[];
|
|
30
32
|
pipeline?: Record<string, unknown>[];
|
package/dist/src/output.d.ts
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Output formatting: table, JSON, Markdown, CSV, YAML.
|
|
3
3
|
*/
|
|
4
|
+
import type { ErrorEnvelope } from './errors.js';
|
|
4
5
|
export interface RenderOptions {
|
|
5
6
|
fmt?: string;
|
|
6
|
-
/** True when the user explicitly passed -f on the command line */
|
|
7
|
+
/** True when the user explicitly passed -f on the command line. */
|
|
7
8
|
fmtExplicit?: boolean;
|
|
9
|
+
/** TTY state used by the pure formatter. `render` derives this from stdout. */
|
|
10
|
+
isTTY?: boolean;
|
|
8
11
|
columns?: string[];
|
|
9
12
|
title?: string;
|
|
10
13
|
elapsed?: number;
|
|
11
14
|
source?: string;
|
|
12
15
|
footerExtra?: string;
|
|
13
16
|
}
|
|
14
|
-
export
|
|
17
|
+
export interface ErrorRenderOptions {
|
|
18
|
+
cmdName?: string;
|
|
19
|
+
traceMode?: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface StreamRenderOptions extends RenderOptions {
|
|
22
|
+
stdout?: NodeJS.WritableStream;
|
|
23
|
+
}
|
|
24
|
+
/** Format output without writing to process-global streams. */
|
|
25
|
+
export declare function formatOutput(data: unknown, opts?: RenderOptions): string;
|
|
26
|
+
/** Render to an injected stream, with the legacy console path retained for local callers. */
|
|
27
|
+
export declare function render(data: unknown, opts?: StreamRenderOptions): Promise<void>;
|
|
28
|
+
/** Serialize the local error envelope without writing to process-global stderr. */
|
|
29
|
+
export declare function formatErrorEnvelope(envelope: ErrorEnvelope, opts?: ErrorRenderOptions): string;
|
package/dist/src/output.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import Table from 'cli-table3';
|
|
5
5
|
import yaml from 'js-yaml';
|
|
6
|
+
import { writeToStream } from './stream-write.js';
|
|
6
7
|
function normalizeRows(data) {
|
|
7
8
|
if (Array.isArray(data))
|
|
8
9
|
return data;
|
|
@@ -13,140 +14,146 @@ function normalizeRows(data) {
|
|
|
13
14
|
function resolveColumns(rows, opts) {
|
|
14
15
|
return opts.columns ?? Object.keys(rows[0] ?? {});
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
/** Format output without writing to process-global streams. */
|
|
18
|
+
export function formatOutput(data, opts = {}) {
|
|
17
19
|
let fmt = opts.fmt ?? 'table';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
if (data === null || data === undefined) {
|
|
24
|
-
console.log(data);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
20
|
+
if (!opts.fmtExplicit && fmt === 'table' && !opts.isTTY)
|
|
21
|
+
fmt = 'yaml';
|
|
22
|
+
if (data === null || data === undefined)
|
|
23
|
+
return `${String(data)}\n`;
|
|
27
24
|
switch (fmt) {
|
|
28
|
-
case 'json':
|
|
29
|
-
|
|
30
|
-
break;
|
|
31
|
-
case 'plain':
|
|
32
|
-
renderPlain(data, opts);
|
|
33
|
-
break;
|
|
25
|
+
case 'json': return `${JSON.stringify(data, null, 2)}\n`;
|
|
26
|
+
case 'plain': return formatPlain(data);
|
|
34
27
|
case 'md':
|
|
35
|
-
case 'markdown':
|
|
36
|
-
|
|
37
|
-
break;
|
|
38
|
-
case 'csv':
|
|
39
|
-
renderCsv(data, opts);
|
|
40
|
-
break;
|
|
28
|
+
case 'markdown': return formatMarkdown(data, opts);
|
|
29
|
+
case 'csv': return formatCsv(data, opts);
|
|
41
30
|
case 'yaml':
|
|
42
|
-
case 'yml':
|
|
43
|
-
|
|
44
|
-
break;
|
|
45
|
-
default:
|
|
46
|
-
renderTable(data, opts);
|
|
47
|
-
break;
|
|
31
|
+
case 'yml': return `${yaml.dump(data, { sortKeys: false, lineWidth: 120, noRefs: true })}\n`;
|
|
32
|
+
default: return formatTable(data, opts);
|
|
48
33
|
}
|
|
49
34
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
/** Render to an injected stream, with the legacy console path retained for local callers. */
|
|
36
|
+
export async function render(data, opts = {}) {
|
|
37
|
+
const { stdout, ...formatOptions } = opts;
|
|
38
|
+
const targetIsTTY = formatOptions.isTTY
|
|
39
|
+
?? (stdout ? stdout.isTTY === true : process.stdout.isTTY === true);
|
|
40
|
+
const output = formatOutput(data, { ...formatOptions, isTTY: targetIsTTY });
|
|
41
|
+
if (!output)
|
|
42
|
+
return;
|
|
43
|
+
if (stdout) {
|
|
44
|
+
await writeToStream(stdout, output);
|
|
54
45
|
return;
|
|
55
46
|
}
|
|
47
|
+
// Existing local command tests and embedders intercept console.log. Passing
|
|
48
|
+
// one string preserves the exact bytes console.log historically emitted.
|
|
49
|
+
console.log(output.endsWith('\n') ? output.slice(0, -1) : output);
|
|
50
|
+
}
|
|
51
|
+
/** Serialize the local error envelope without writing to process-global stderr. */
|
|
52
|
+
export function formatErrorEnvelope(envelope, opts = {}) {
|
|
53
|
+
let output = yaml.dump(envelope, { sortKeys: false, lineWidth: 120, noRefs: true });
|
|
54
|
+
const code = envelope.error.code;
|
|
55
|
+
if (opts.cmdName
|
|
56
|
+
&& opts.traceMode !== 'on'
|
|
57
|
+
&& opts.traceMode !== 'retain-on-failure'
|
|
58
|
+
&& (code === 'SELECTOR' || code === 'EMPTY_RESULT' || code === 'ADAPTER_LOAD' || code === 'UNKNOWN')) {
|
|
59
|
+
const runnable = opts.cmdName.replace('/', ' ');
|
|
60
|
+
output += '# AutoFix: re-run with --trace=retain-on-failure for trace artifact\n';
|
|
61
|
+
output += `# webcmd ${runnable} --trace retain-on-failure\n`;
|
|
62
|
+
}
|
|
63
|
+
return output;
|
|
64
|
+
}
|
|
65
|
+
function formatTable(data, opts) {
|
|
66
|
+
const rows = normalizeRows(data);
|
|
67
|
+
if (!rows.length)
|
|
68
|
+
return '(no data)\n';
|
|
56
69
|
const columns = resolveColumns(rows, opts);
|
|
57
|
-
const header = columns.map(c => capitalize(c));
|
|
58
70
|
const table = new Table({
|
|
59
|
-
head:
|
|
71
|
+
head: columns.map(capitalize),
|
|
60
72
|
style: { head: [], border: [] },
|
|
61
73
|
wordWrap: true,
|
|
62
74
|
wrapOnWordBoundary: true,
|
|
63
75
|
});
|
|
64
76
|
for (const row of rows) {
|
|
65
|
-
table.push(columns.map(
|
|
66
|
-
const
|
|
67
|
-
return
|
|
77
|
+
table.push(columns.map((column) => {
|
|
78
|
+
const value = row[column];
|
|
79
|
+
return value === null || value === undefined ? '' : String(value);
|
|
68
80
|
}));
|
|
69
81
|
}
|
|
70
|
-
|
|
82
|
+
const output = [''];
|
|
71
83
|
if (opts.title)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const footer = [];
|
|
75
|
-
footer.push(`${rows.length} items`);
|
|
84
|
+
output.push(` ${opts.title}`);
|
|
85
|
+
output.push(table.toString());
|
|
86
|
+
const footer = [`${rows.length} items`];
|
|
76
87
|
if (opts.elapsed !== undefined)
|
|
77
88
|
footer.push(`${opts.elapsed.toFixed(1)}s`);
|
|
78
89
|
if (opts.source)
|
|
79
90
|
footer.push(opts.source);
|
|
80
91
|
if (opts.footerExtra)
|
|
81
92
|
footer.push(opts.footerExtra);
|
|
82
|
-
|
|
93
|
+
output.push(footer.join(' | '));
|
|
94
|
+
return `${output.join('\n')}\n`;
|
|
83
95
|
}
|
|
84
|
-
function
|
|
85
|
-
console.log(JSON.stringify(data, null, 2));
|
|
86
|
-
}
|
|
87
|
-
function renderPlain(data, opts) {
|
|
96
|
+
function formatPlain(data) {
|
|
88
97
|
const rows = normalizeRows(data);
|
|
89
98
|
if (!rows.length)
|
|
90
|
-
return;
|
|
91
|
-
// Single-row single-field shortcuts for chat-style commands.
|
|
99
|
+
return '';
|
|
92
100
|
if (rows.length === 1) {
|
|
93
|
-
const
|
|
94
|
-
const entries = Object.entries(row);
|
|
101
|
+
const entries = Object.entries(rows[0]);
|
|
95
102
|
if (entries.length === 1) {
|
|
96
103
|
const [key, value] = entries[0];
|
|
97
104
|
if (key === 'response' || key === 'content' || key === 'markdown' || key === 'text' || key === 'value') {
|
|
98
|
-
|
|
99
|
-
return;
|
|
105
|
+
return `${String(value ?? '')}\n`;
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
}
|
|
109
|
+
const output = [];
|
|
103
110
|
rows.forEach((row, index) => {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
for (const [key, value] of Object.entries(row)) {
|
|
112
|
+
if (value === undefined || value === null || String(value) === '')
|
|
113
|
+
continue;
|
|
114
|
+
output.push(`${key}: ${value}`);
|
|
115
|
+
}
|
|
108
116
|
if (index < rows.length - 1)
|
|
109
|
-
|
|
117
|
+
output.push('');
|
|
110
118
|
});
|
|
119
|
+
return output.length > 0 ? `${output.join('\n')}\n` : '';
|
|
111
120
|
}
|
|
112
|
-
function
|
|
121
|
+
function formatMarkdown(data, opts) {
|
|
113
122
|
const rows = normalizeRows(data);
|
|
114
123
|
if (!rows.length)
|
|
115
|
-
return;
|
|
124
|
+
return '';
|
|
116
125
|
if (rows.length === 1) {
|
|
117
126
|
const entries = Object.entries(rows[0]);
|
|
118
127
|
if (entries.length === 1) {
|
|
119
128
|
const [key, value] = entries[0];
|
|
120
129
|
if (key === 'content' || key === 'markdown' || key === 'text' || key === 'value') {
|
|
121
|
-
|
|
122
|
-
return;
|
|
130
|
+
return `${String(value ?? '')}\n`;
|
|
123
131
|
}
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
const columns = resolveColumns(rows, opts);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
const output = [
|
|
136
|
+
`| ${columns.join(' | ')} |`,
|
|
137
|
+
`| ${columns.map(() => '---').join(' | ')} |`,
|
|
138
|
+
...rows.map(row => `| ${columns.map(column => String(row[column] ?? '')).join(' | ')} |`),
|
|
139
|
+
];
|
|
140
|
+
return `${output.join('\n')}\n`;
|
|
132
141
|
}
|
|
133
|
-
function
|
|
142
|
+
function formatCsv(data, opts) {
|
|
134
143
|
const rows = normalizeRows(data);
|
|
135
144
|
if (!rows.length)
|
|
136
|
-
return;
|
|
145
|
+
return '';
|
|
137
146
|
const columns = resolveColumns(rows, opts);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
? `"${v.replace(/"/g, '""')}"` : v;
|
|
144
|
-
}).join(','));
|
|
145
|
-
}
|
|
147
|
+
const output = [
|
|
148
|
+
columns.join(','),
|
|
149
|
+
...rows.map(row => columns.map(column => csvCell(row[column])).join(',')),
|
|
150
|
+
];
|
|
151
|
+
return `${output.join('\n')}\n`;
|
|
146
152
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
153
|
+
function csvCell(value) {
|
|
154
|
+
const text = String(value ?? '');
|
|
155
|
+
return /[,"\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
149
156
|
}
|
|
150
|
-
function capitalize(
|
|
151
|
-
return
|
|
157
|
+
function capitalize(value) {
|
|
158
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
152
159
|
}
|