@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
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { cli, getRegistry, Strategy } from './registry.js';
|
|
6
|
-
import { ManifestImportError, diffRemovedEntries, findManifestMetadataIssues, loadManifestEntries, normalizeManifestPath, parseBuildManifestArgs, scanClisDir, serializeManifest, } from './build-manifest.js';
|
|
6
|
+
import { ManifestImportError, buildManifestArtifacts, diffRemovedEntries, findManifestMetadataIssues, loadManifestEntries, normalizeManifestPath, parseBuildManifestArgs, scanClisDir, serializeManifest, } from './build-manifest.js';
|
|
7
7
|
describe('manifest helper rules', () => {
|
|
8
8
|
const tempDirs = [];
|
|
9
9
|
afterEach(() => {
|
|
@@ -81,6 +81,62 @@ describe('manifest helper rules', () => {
|
|
|
81
81
|
expect(entries[0].sourceFile).not.toContain('\\');
|
|
82
82
|
getRegistry().delete(key);
|
|
83
83
|
});
|
|
84
|
+
it('preserves file metadata and builds both artifacts from one normalized snapshot', async () => {
|
|
85
|
+
const site = `manifest-files-${Date.now()}`;
|
|
86
|
+
const key = `${site}/upload`;
|
|
87
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'webcmd-manifest-'));
|
|
88
|
+
tempDirs.push(dir);
|
|
89
|
+
const file = path.join(dir, `${site}.ts`);
|
|
90
|
+
fs.writeFileSync(file, `export const command = cli({ site: '${site}', name: 'upload', access: 'write' });`);
|
|
91
|
+
const entries = await loadManifestEntries(file, site, async () => ({
|
|
92
|
+
command: cli({
|
|
93
|
+
site,
|
|
94
|
+
name: 'upload',
|
|
95
|
+
access: 'write',
|
|
96
|
+
description: 'upload a document',
|
|
97
|
+
strategy: Strategy.PUBLIC,
|
|
98
|
+
browser: false,
|
|
99
|
+
args: [{
|
|
100
|
+
name: 'path',
|
|
101
|
+
type: 'str',
|
|
102
|
+
positional: true,
|
|
103
|
+
required: true,
|
|
104
|
+
help: 'Document path',
|
|
105
|
+
file: {
|
|
106
|
+
direction: 'input',
|
|
107
|
+
pathKind: 'file',
|
|
108
|
+
contentTypes: ['application/pdf'],
|
|
109
|
+
maxBytes: 5_000_000,
|
|
110
|
+
},
|
|
111
|
+
}],
|
|
112
|
+
}),
|
|
113
|
+
}));
|
|
114
|
+
expect(entries[0].args[0].file).toEqual({
|
|
115
|
+
direction: 'input',
|
|
116
|
+
pathKind: 'file',
|
|
117
|
+
contentTypes: ['application/pdf'],
|
|
118
|
+
maxBytes: 5_000_000,
|
|
119
|
+
});
|
|
120
|
+
const artifacts = buildManifestArtifacts(entries, '7.6.5', []);
|
|
121
|
+
expect(JSON.parse(artifacts.manifestJson)).toEqual(entries);
|
|
122
|
+
expect(JSON.parse(artifacts.hostedContractJson)).toMatchObject({
|
|
123
|
+
schemaVersion: 1,
|
|
124
|
+
webcmdVersion: '7.6.5',
|
|
125
|
+
commands: [{
|
|
126
|
+
command: `${site}/upload`,
|
|
127
|
+
fileArguments: [{
|
|
128
|
+
name: 'path',
|
|
129
|
+
direction: 'input',
|
|
130
|
+
pathKind: 'file',
|
|
131
|
+
multiple: false,
|
|
132
|
+
required: true,
|
|
133
|
+
contentTypes: ['application/pdf'],
|
|
134
|
+
maxBytes: 5_000_000,
|
|
135
|
+
}],
|
|
136
|
+
}],
|
|
137
|
+
});
|
|
138
|
+
getRegistry().delete(key);
|
|
139
|
+
});
|
|
84
140
|
it('serializes freshPage for persistent browser commands', async () => {
|
|
85
141
|
const site = `manifest-fresh-page-${Date.now()}`;
|
|
86
142
|
const key = `${site}/checkout`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export declare const LIST_COMMAND_DESCRIPTION = "List all available CLI commands";
|
|
3
|
+
export declare const LIST_FORMAT_DESCRIPTION = "Output format: table, json, yaml, md, csv";
|
|
4
|
+
export declare const COMPLETION_COMMAND_DESCRIPTION = "Output shell completion script";
|
|
5
|
+
export declare const COMPLETION_SHELL_DESCRIPTION = "Shell type: bash, zsh, or fish";
|
|
6
|
+
/** Configure built-in grammar shared by the local and hosted runtimes. */
|
|
7
|
+
export declare function configureListCommandSurface(command: Command): Command;
|
|
8
|
+
/** Configure completion grammar shared by the local and hosted runtimes. */
|
|
9
|
+
export declare function configureCompletionCommandSurface(command: Command): Command;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const LIST_COMMAND_DESCRIPTION = 'List all available CLI commands';
|
|
2
|
+
export const LIST_FORMAT_DESCRIPTION = 'Output format: table, json, yaml, md, csv';
|
|
3
|
+
export const COMPLETION_COMMAND_DESCRIPTION = 'Output shell completion script';
|
|
4
|
+
export const COMPLETION_SHELL_DESCRIPTION = 'Shell type: bash, zsh, or fish';
|
|
5
|
+
/** Configure built-in grammar shared by the local and hosted runtimes. */
|
|
6
|
+
export function configureListCommandSurface(command) {
|
|
7
|
+
return command
|
|
8
|
+
.description(LIST_COMMAND_DESCRIPTION)
|
|
9
|
+
.option('-f, --format <fmt>', LIST_FORMAT_DESCRIPTION, 'table');
|
|
10
|
+
}
|
|
11
|
+
/** Configure completion grammar shared by the local and hosted runtimes. */
|
|
12
|
+
export function configureCompletionCommandSurface(command) {
|
|
13
|
+
return command
|
|
14
|
+
.description(COMPLETION_COMMAND_DESCRIPTION)
|
|
15
|
+
.argument('<shell>', COMPLETION_SHELL_DESCRIPTION);
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
8
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const checkerPath = path.join(packageRoot, 'scripts/check-hosted-contract.mjs');
|
|
10
|
+
const committedArtifactNames = ['cli-manifest.json'];
|
|
11
|
+
const fixtureRoots = [];
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
for (const fixtureRoot of fixtureRoots.splice(0)) {
|
|
14
|
+
rmSync(fixtureRoot, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
function createCommittedArtifactFixture() {
|
|
18
|
+
const fixtureRoot = mkdtempSync(path.join(tmpdir(), 'webcmd-contract-committed-'));
|
|
19
|
+
fixtureRoots.push(fixtureRoot);
|
|
20
|
+
for (const artifactName of committedArtifactNames) {
|
|
21
|
+
copyFileSync(path.join(packageRoot, artifactName), path.join(fixtureRoot, artifactName));
|
|
22
|
+
}
|
|
23
|
+
return fixtureRoot;
|
|
24
|
+
}
|
|
25
|
+
function rootArtifactHashes() {
|
|
26
|
+
return Object.fromEntries(committedArtifactNames.map((artifactName) => [
|
|
27
|
+
artifactName,
|
|
28
|
+
createHash('sha256').update(readFileSync(path.join(packageRoot, artifactName))).digest('hex'),
|
|
29
|
+
]));
|
|
30
|
+
}
|
|
31
|
+
function runChecker(committedRoot) {
|
|
32
|
+
return spawnSync(process.execPath, [checkerPath], {
|
|
33
|
+
cwd: packageRoot,
|
|
34
|
+
encoding: 'utf8',
|
|
35
|
+
env: { ...process.env, WEBCMD_CONTRACT_COMMITTED_ROOT: committedRoot },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
describe('hosted contract reproducibility checker', () => {
|
|
39
|
+
it('accepts a byte-identical committed manifest and generates the package contract', () => {
|
|
40
|
+
const result = runChecker(createCommittedArtifactFixture());
|
|
41
|
+
expect(result.status).toBe(0);
|
|
42
|
+
expect(result.stdout).toContain('hosted-contract.json generated successfully.');
|
|
43
|
+
}, 10_000);
|
|
44
|
+
it('rejects one stale byte without mutating root artifacts', () => {
|
|
45
|
+
const before = rootArtifactHashes();
|
|
46
|
+
const fixtureRoot = createCommittedArtifactFixture();
|
|
47
|
+
const stalePath = path.join(fixtureRoot, 'cli-manifest.json');
|
|
48
|
+
const staleBytes = readFileSync(stalePath);
|
|
49
|
+
staleBytes[0] ^= 1;
|
|
50
|
+
writeFileSync(stalePath, staleBytes);
|
|
51
|
+
const result = runChecker(fixtureRoot);
|
|
52
|
+
expect(result.status).toBe(1);
|
|
53
|
+
expect(result.stderr).toContain('cli-manifest.json');
|
|
54
|
+
expect(rootArtifactHashes()).toEqual(before);
|
|
55
|
+
}, 10_000);
|
|
56
|
+
});
|
package/dist/src/cli.js
CHANGED
|
@@ -9,17 +9,18 @@ import * as os from 'node:os';
|
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
import * as readline from 'node:readline/promises';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { Command,
|
|
12
|
+
import { Command, Option } from 'commander';
|
|
13
13
|
import { findPackageRoot, getBuiltEntryCandidates } from './package-paths.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { getRegistry } from './registry.js';
|
|
15
|
+
import { commandListPresentation, toPresentableCommand } from './command-presentation.js';
|
|
16
|
+
import { configureCompletionCommandSurface, configureListCommandSurface } from './builtin-command-surface.js';
|
|
16
17
|
import { render as renderOutput } from './output.js';
|
|
17
18
|
import { PKG_VERSION } from './version.js';
|
|
18
19
|
import { printCompletionScript } from './completion.js';
|
|
19
20
|
import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled, formatExternalCliLabel } from './external.js';
|
|
20
21
|
import { installWebcmdSkill, listWebcmdSkills, updateWebcmdSkill } from './skills.js';
|
|
21
22
|
import { registerAllCommands } from './commanderAdapter.js';
|
|
22
|
-
import {
|
|
23
|
+
import { buildRootHelpPresentation, classifyAdapter, installCommanderNamespaceStructuredHelp, installRootPresentationHelp, leadingPositionalFromUsage, rootHelpData } from './help.js';
|
|
23
24
|
import { EXIT_CODES, getErrorMessage, BrowserConnectError, CliError, ArgumentError } from './errors.js';
|
|
24
25
|
import { TargetError } from './browser/target-errors.js';
|
|
25
26
|
import { resolveTargetJs, getTextResolvedJs, getValueResolvedJs, getAttributesResolvedJs, selectResolvedJs, isAutocompleteResolvedJs } from './browser/target-resolver.js';
|
|
@@ -32,6 +33,7 @@ import { parseFilter, shapeMatchesFilter } from './browser/shape-filter.js';
|
|
|
32
33
|
import { buildHtmlTreeJs } from './browser/html-tree.js';
|
|
33
34
|
import { buildExtractHtmlJs, runExtractFromHtml } from './browser/extract.js';
|
|
34
35
|
import { analyzeSite } from './browser/analyze.js';
|
|
36
|
+
import { browserOptionValueParser } from './browser/command-catalog.js';
|
|
35
37
|
import { registerAuthCommands } from './commands/auth.js';
|
|
36
38
|
import { daemonRestart, daemonStatus, daemonStop } from './commands/daemon.js';
|
|
37
39
|
import { log } from './logger.js';
|
|
@@ -41,6 +43,7 @@ import { aliasForContextId, loadProfileConfig, profileRouteParams, renameProfile
|
|
|
41
43
|
import { formatDaemonVersion, isDaemonStale } from './browser/daemon-version.js';
|
|
42
44
|
import { DEFAULT_BROWSER_CONNECT_TIMEOUT } from './browser/config.js';
|
|
43
45
|
import { CLI_COMMAND } from './brand.js';
|
|
46
|
+
import { configureRootCommandSurface } from './root-command-surface.js';
|
|
44
47
|
const CLI_FILE = fileURLToPath(import.meta.url);
|
|
45
48
|
const BROWSER_TAB_OPTION_DESCRIPTION = 'Target tab/page identity returned by "browser open", "browser tab new", or "browser tab list"';
|
|
46
49
|
const FOLLOW_POLL_MS = 1_000;
|
|
@@ -632,16 +635,6 @@ function parsePositiveIntOption(val, label, fallback) {
|
|
|
632
635
|
}
|
|
633
636
|
return parsed;
|
|
634
637
|
}
|
|
635
|
-
function parseScreenshotDim(val, label) {
|
|
636
|
-
if (!/^\d+$/.test(val)) {
|
|
637
|
-
throw new InvalidArgumentError(`--${label} must be a positive integer (got "${val}")`);
|
|
638
|
-
}
|
|
639
|
-
const parsed = parseInt(val, 10);
|
|
640
|
-
if (parsed <= 0) {
|
|
641
|
-
throw new InvalidArgumentError(`--${label} must be a positive integer (got "${val}")`);
|
|
642
|
-
}
|
|
643
|
-
return parsed;
|
|
644
|
-
}
|
|
645
638
|
function applyVerbose(opts) {
|
|
646
639
|
if (opts.verbose)
|
|
647
640
|
process.env.WEBCMD_VERBOSE = '1';
|
|
@@ -666,93 +659,30 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
|
|
|
666
659
|
// prerequisite for passThroughOptions to forward --help/--version to external binaries
|
|
667
660
|
program
|
|
668
661
|
.name('webcmd')
|
|
669
|
-
.description('Make any website your CLI. Zero setup. AI-powered.')
|
|
670
|
-
|
|
671
|
-
.option('--profile <name>', 'Chrome profile/context alias for browser runtime commands')
|
|
672
|
-
.enablePositionalOptions();
|
|
662
|
+
.description('Make any website your CLI. Zero setup. AI-powered.');
|
|
663
|
+
configureRootCommandSurface(program);
|
|
673
664
|
// ── Built-in: list ────────────────────────────────────────────────────────
|
|
674
|
-
program
|
|
675
|
-
.command('list')
|
|
676
|
-
.description('List all available CLI commands')
|
|
677
|
-
.option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table')
|
|
665
|
+
configureListCommandSurface(program.command('list'))
|
|
678
666
|
.action((opts) => {
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
aliases: c.aliases?.join(', ') ?? '',
|
|
691
|
-
description: c.description,
|
|
692
|
-
access: c.access,
|
|
693
|
-
strategy: strategyLabel(c),
|
|
694
|
-
browser: !!c.browser,
|
|
695
|
-
args: formatArgSummary(c.args),
|
|
696
|
-
}));
|
|
697
|
-
renderOutput(rows, {
|
|
698
|
-
fmt,
|
|
699
|
-
columns: ['command', 'site', 'name', 'aliases', 'description', 'access', 'strategy', 'browser', 'args',
|
|
700
|
-
...(isStructured ? ['columns', 'domain'] : [])],
|
|
701
|
-
title: 'webcmd/list',
|
|
702
|
-
source: 'webcmd list',
|
|
703
|
-
});
|
|
667
|
+
const externalClis = opts.format === 'table' ? loadExternalClis() : [];
|
|
668
|
+
const presentation = commandListPresentation([...new Set(getRegistry().values())].map(toPresentableCommand), opts.format, {
|
|
669
|
+
externalClis: externalClis.map((external) => ({
|
|
670
|
+
label: formatExternalCliLabel(external),
|
|
671
|
+
installed: isBinaryInstalled(external.binary),
|
|
672
|
+
...(external.description ? { description: external.description } : {}),
|
|
673
|
+
})),
|
|
674
|
+
});
|
|
675
|
+
if (presentation.displayLines) {
|
|
676
|
+
for (const line of presentation.displayLines)
|
|
677
|
+
console.log(line);
|
|
704
678
|
return;
|
|
705
679
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const target = classifyAdapter(cmd.domain) === 'app' ? appsBySite : sitesBySite;
|
|
713
|
-
const g = target.get(cmd.site) ?? [];
|
|
714
|
-
g.push(cmd);
|
|
715
|
-
target.set(cmd.site, g);
|
|
716
|
-
}
|
|
717
|
-
const renderSiteGroup = (site, cmds) => {
|
|
718
|
-
console.log(` ${site}`);
|
|
719
|
-
for (const cmd of cmds) {
|
|
720
|
-
const label = strategyLabel(cmd);
|
|
721
|
-
const tag = label === 'public'
|
|
722
|
-
? '[public]'
|
|
723
|
-
: `[${label}]`;
|
|
724
|
-
const aliases = cmd.aliases?.length ? ` (aliases: ${cmd.aliases.join(', ')})` : '';
|
|
725
|
-
console.log(` ${cmd.name} ${tag}${aliases}${cmd.description ? ` — ${cmd.description}` : ''}`);
|
|
726
|
-
}
|
|
727
|
-
console.log();
|
|
728
|
-
};
|
|
729
|
-
console.log();
|
|
730
|
-
console.log(' webcmd' + ' — available commands');
|
|
731
|
-
console.log();
|
|
732
|
-
if (appsBySite.size > 0) {
|
|
733
|
-
console.log(' App adapters');
|
|
734
|
-
console.log();
|
|
735
|
-
for (const [site, cmds] of appsBySite)
|
|
736
|
-
renderSiteGroup(site, cmds);
|
|
737
|
-
}
|
|
738
|
-
if (sitesBySite.size > 0) {
|
|
739
|
-
console.log(' Site adapters');
|
|
740
|
-
console.log();
|
|
741
|
-
for (const [site, cmds] of sitesBySite)
|
|
742
|
-
renderSiteGroup(site, cmds);
|
|
743
|
-
}
|
|
744
|
-
const externalClis = loadExternalClis();
|
|
745
|
-
if (externalClis.length > 0) {
|
|
746
|
-
console.log(' external CLIs');
|
|
747
|
-
for (const ext of externalClis) {
|
|
748
|
-
const isInstalled = isBinaryInstalled(ext.binary);
|
|
749
|
-
const tag = isInstalled ? '[installed]' : '[auto-install]';
|
|
750
|
-
console.log(` ${formatExternalCliLabel(ext)} ${tag}${ext.description ? ` — ${ext.description}` : ''}`);
|
|
751
|
-
}
|
|
752
|
-
console.log();
|
|
753
|
-
}
|
|
754
|
-
console.log(` ${commands.length} built-in commands across ${appsBySite.size} apps + ${sitesBySite.size} sites, ${externalClis.length} external CLIs`);
|
|
755
|
-
console.log();
|
|
680
|
+
renderOutput(presentation.rows, {
|
|
681
|
+
fmt: opts.format,
|
|
682
|
+
columns: presentation.columns,
|
|
683
|
+
title: 'webcmd/list',
|
|
684
|
+
source: 'webcmd list',
|
|
685
|
+
});
|
|
756
686
|
});
|
|
757
687
|
// ── Built-in: validate / verify ───────────────────────────────────────────
|
|
758
688
|
program
|
|
@@ -1204,8 +1134,8 @@ Examples:
|
|
|
1204
1134
|
addBrowserTabOption(browser.command('screenshot').argument('[path]', 'Save to file (base64 if omitted)'))
|
|
1205
1135
|
.option('--full-page', 'Capture the full scrollable page, not just the viewport', false)
|
|
1206
1136
|
.option('--annotate', 'Overlay visible browser state ref labels on the screenshot', false)
|
|
1207
|
-
.option('--width <n>', 'Override viewport width in CSS pixels for this screenshot only', (
|
|
1208
|
-
.option('--height <n>', 'Override viewport height in CSS pixels for this screenshot only (ignored with --full-page)', (
|
|
1137
|
+
.option('--width <n>', 'Override viewport width in CSS pixels for this screenshot only', (value) => browserOptionValueParser('screenshot', 'width')(value))
|
|
1138
|
+
.option('--height <n>', 'Override viewport height in CSS pixels for this screenshot only (ignored with --full-page)', (value) => browserOptionValueParser('screenshot', 'height')(value))
|
|
1209
1139
|
.description('Take screenshot')
|
|
1210
1140
|
.action(browserAction(async (page, path, opts) => {
|
|
1211
1141
|
const shotOpts = {
|
|
@@ -1858,8 +1788,13 @@ Examples:
|
|
|
1858
1788
|
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
|
1859
1789
|
return;
|
|
1860
1790
|
}
|
|
1861
|
-
const { matches_n, match_level } = await page.click(resolvedTarget, parsed.opts);
|
|
1862
|
-
console.log(JSON.stringify({
|
|
1791
|
+
const { matches_n, match_level, click_method, hit, retargeted } = await page.click(resolvedTarget, parsed.opts);
|
|
1792
|
+
console.log(JSON.stringify({
|
|
1793
|
+
clicked: true, target: resolvedTarget, matches_n, match_level,
|
|
1794
|
+
...(click_method && { click_method }),
|
|
1795
|
+
...(hit && { hit }),
|
|
1796
|
+
...(retargeted && { retargeted }),
|
|
1797
|
+
}, null, 2));
|
|
1863
1798
|
}));
|
|
1864
1799
|
addBrowserTabOption(addSemanticLocatorOptions(browser.command('type'))
|
|
1865
1800
|
.argument('[targetOrText]', 'Numeric ref/CSS target, or text when using --role/--name/etc.')
|
|
@@ -2835,10 +2770,7 @@ cli({
|
|
|
2835
2770
|
const report = await runBrowserDoctor({ cliVersion: PKG_VERSION });
|
|
2836
2771
|
console.log(renderBrowserDoctorReport(report));
|
|
2837
2772
|
});
|
|
2838
|
-
program
|
|
2839
|
-
.command('completion')
|
|
2840
|
-
.description('Output shell completion script')
|
|
2841
|
-
.argument('<shell>', 'Shell type: bash, zsh, or fish')
|
|
2773
|
+
configureCompletionCommandSurface(program.command('completion'))
|
|
2842
2774
|
.action((shell) => {
|
|
2843
2775
|
printCompletionScript(shell);
|
|
2844
2776
|
});
|
|
@@ -3102,12 +3034,32 @@ cli({
|
|
|
3102
3034
|
.argument('<name>', 'Plugin name (lowercase, hyphens allowed)')
|
|
3103
3035
|
.option('-d, --dir <path>', 'Output directory (default: ./<name>)')
|
|
3104
3036
|
.option('--description <text>', 'Plugin description')
|
|
3037
|
+
.option('--author-name <name>', 'Author display name')
|
|
3038
|
+
.option('--author-handle <handle>', 'Author GitHub handle')
|
|
3105
3039
|
.action(async (name, opts) => {
|
|
3106
3040
|
const { createPluginScaffold } = await import('./plugin-scaffold.js');
|
|
3107
3041
|
try {
|
|
3042
|
+
let authorName = opts.authorName?.trim();
|
|
3043
|
+
let authorHandle = opts.authorHandle?.trim();
|
|
3044
|
+
if ((!authorName || !authorHandle) && process.stdin.isTTY && process.stdout.isTTY) {
|
|
3045
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3046
|
+
try {
|
|
3047
|
+
if (!authorName)
|
|
3048
|
+
authorName = (await rl.question('Author name: ')).trim();
|
|
3049
|
+
if (!authorHandle)
|
|
3050
|
+
authorHandle = (await rl.question('GitHub handle: ')).trim();
|
|
3051
|
+
}
|
|
3052
|
+
finally {
|
|
3053
|
+
rl.close();
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3108
3056
|
const result = createPluginScaffold(name, {
|
|
3109
3057
|
dir: opts.dir,
|
|
3110
3058
|
description: opts.description,
|
|
3059
|
+
author: {
|
|
3060
|
+
name: authorName ?? '',
|
|
3061
|
+
handle: authorHandle ?? '',
|
|
3062
|
+
},
|
|
3111
3063
|
});
|
|
3112
3064
|
console.log(`✅ Plugin scaffold created at ${result.dir}`);
|
|
3113
3065
|
console.log();
|
|
@@ -3480,7 +3432,7 @@ cli({
|
|
|
3480
3432
|
applyAncestorAwareUsage(sub);
|
|
3481
3433
|
}
|
|
3482
3434
|
applyAncestorAwareUsage(browser);
|
|
3483
|
-
|
|
3435
|
+
installRootPresentationHelp(program, () => rootHelpData(program, adapterGroups), buildRootHelpPresentation(program, adapterGroups));
|
|
3484
3436
|
// ── Unknown command fallback ──────────────────────────────────────────────
|
|
3485
3437
|
// Security: do NOT auto-discover and register arbitrary system binaries.
|
|
3486
3438
|
// Only explicitly registered external CLIs are allowed.
|
package/dist/src/cli.test.js
CHANGED
|
@@ -7,7 +7,9 @@ import { cli, getRegistry, Strategy } from './registry.js';
|
|
|
7
7
|
import { BrowserCommandError } from './browser/daemon-client.js';
|
|
8
8
|
import { TargetError } from './browser/target-errors.js';
|
|
9
9
|
import { PKG_VERSION } from './version.js';
|
|
10
|
-
import { classifyAdapter } from './help.js';
|
|
10
|
+
import { classifyAdapter, getInstalledRootHelpPresentation } from './help.js';
|
|
11
|
+
import { commandListPresentation, formatRootHelp, toPresentableCommand, } from './command-presentation.js';
|
|
12
|
+
import { render as renderOutput } from './output.js';
|
|
11
13
|
const { mockBrowserConnect, mockBrowserClose, mockBindTab, mockSendCommand, mockExecFileSync, browserState, } = vi.hoisted(() => ({
|
|
12
14
|
mockBrowserConnect: vi.fn(),
|
|
13
15
|
mockBrowserClose: vi.fn(),
|
|
@@ -94,6 +96,14 @@ describe('createProgram root help descriptions', () => {
|
|
|
94
96
|
expect(descriptionFor(program, 'list')).toBe('List all available CLI commands');
|
|
95
97
|
expect(descriptionFor(program, 'doctor')).toBe('Diagnose webcmd browser bridge connectivity');
|
|
96
98
|
});
|
|
99
|
+
it('renders the actual local root through the shared root presentation seam', () => {
|
|
100
|
+
const program = createProgram('', '');
|
|
101
|
+
const presentation = getInstalledRootHelpPresentation(program);
|
|
102
|
+
const commanderHelp = program.createHelp();
|
|
103
|
+
expect(presentation).toBeDefined();
|
|
104
|
+
expect(presentation.baseText).toBe(commanderHelp.formatHelp(program, commanderHelp));
|
|
105
|
+
expect(program.helpInformation()).toBe(formatRootHelp(presentation));
|
|
106
|
+
});
|
|
97
107
|
it('keeps site adapters out of root commands and lists sites in the root help tail', () => {
|
|
98
108
|
const registry = getRegistry();
|
|
99
109
|
const snapshot = new Map(registry);
|
|
@@ -274,6 +284,45 @@ describe('createProgram root help descriptions', () => {
|
|
|
274
284
|
registry.set(key, value);
|
|
275
285
|
}
|
|
276
286
|
});
|
|
287
|
+
it.each(['json', 'yaml', 'yml'])('renders local list %s through the shared list presentation', async (format) => {
|
|
288
|
+
const registry = getRegistry();
|
|
289
|
+
const snapshot = new Map(registry);
|
|
290
|
+
registry.clear();
|
|
291
|
+
try {
|
|
292
|
+
const command = cli({
|
|
293
|
+
site: 'github',
|
|
294
|
+
name: 'issues',
|
|
295
|
+
aliases: ['issue-list'],
|
|
296
|
+
access: 'read',
|
|
297
|
+
description: 'List repository issues',
|
|
298
|
+
strategy: Strategy.PUBLIC,
|
|
299
|
+
browser: false,
|
|
300
|
+
args: [{ name: 'limit', type: 'int', default: 20, help: 'Maximum issues' }],
|
|
301
|
+
columns: ['number', 'title'],
|
|
302
|
+
});
|
|
303
|
+
const presentation = commandListPresentation([toPresentableCommand(command)], format);
|
|
304
|
+
const outputSpy = vi.mocked(console.log);
|
|
305
|
+
outputSpy.mockClear();
|
|
306
|
+
const program = createProgram('', '');
|
|
307
|
+
await program.parseAsync(['node', 'webcmd', 'list', '-f', format]);
|
|
308
|
+
const actual = outputSpy.mock.calls.flat().join('\n');
|
|
309
|
+
outputSpy.mockClear();
|
|
310
|
+
renderOutput(presentation.rows, {
|
|
311
|
+
fmt: format,
|
|
312
|
+
columns: presentation.columns,
|
|
313
|
+
title: 'webcmd/list',
|
|
314
|
+
source: 'webcmd list',
|
|
315
|
+
});
|
|
316
|
+
const expected = outputSpy.mock.calls.flat().join('\n');
|
|
317
|
+
outputSpy.mockClear();
|
|
318
|
+
expect(actual).toBe(expected);
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
registry.clear();
|
|
322
|
+
for (const [key, value] of snapshot)
|
|
323
|
+
registry.set(key, value);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
277
326
|
it('exposes external_clis / app_adapters / site_adapters in structured help', () => {
|
|
278
327
|
const registry = getRegistry();
|
|
279
328
|
const snapshot = new Map(registry);
|
|
@@ -489,6 +538,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
489
538
|
expect(data.command).toBe('webcmd browser');
|
|
490
539
|
expect(data.description).toBe('Browser control — navigate, click, type, extract, wait (no LLM needed)');
|
|
491
540
|
expect(data.command_count).toBeGreaterThan(20);
|
|
541
|
+
expect(data.commands.map((cmd) => cmd.name)).toContain('bind');
|
|
492
542
|
// `--session` is now a hidden internal option; user-facing surface is the
|
|
493
543
|
// <session> positional declared via `.usage()`. Structured help drops
|
|
494
544
|
// hidden options, so namespace_options shouldn't expose it.
|
|
@@ -2499,6 +2549,26 @@ describe('browser click/type commands', () => {
|
|
|
2499
2549
|
expect(browserState.page.click).toHaveBeenCalledWith('#save', {});
|
|
2500
2550
|
expect(lastJsonLog()).toEqual({ clicked: true, target: '#save', matches_n: 1, match_level: 'exact' });
|
|
2501
2551
|
});
|
|
2552
|
+
it('surfaces click hit-testing diagnostics', async () => {
|
|
2553
|
+
browserState.page.click.mockResolvedValueOnce({
|
|
2554
|
+
matches_n: 1,
|
|
2555
|
+
match_level: 'exact',
|
|
2556
|
+
click_method: 'js',
|
|
2557
|
+
hit: 'other',
|
|
2558
|
+
retargeted: true,
|
|
2559
|
+
});
|
|
2560
|
+
const program = createProgram('', '');
|
|
2561
|
+
await program.parseAsync(['node', 'webcmd', 'browser', '--session', 'test', 'click', '#covered']);
|
|
2562
|
+
expect(lastJsonLog()).toEqual({
|
|
2563
|
+
clicked: true,
|
|
2564
|
+
target: '#covered',
|
|
2565
|
+
matches_n: 1,
|
|
2566
|
+
match_level: 'exact',
|
|
2567
|
+
click_method: 'js',
|
|
2568
|
+
hit: 'other',
|
|
2569
|
+
retargeted: true,
|
|
2570
|
+
});
|
|
2571
|
+
});
|
|
2502
2572
|
it('clicks a unique semantic locator without a prior state call', async () => {
|
|
2503
2573
|
browserState.page.evaluate.mockResolvedValueOnce({
|
|
2504
2574
|
matches_n: 1,
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Arg } from './registry.js';
|
|
2
|
+
export interface PresentableCommand {
|
|
3
|
+
site: string;
|
|
4
|
+
name: string;
|
|
5
|
+
aliases: string[];
|
|
6
|
+
description: string;
|
|
7
|
+
access: 'read' | 'write';
|
|
8
|
+
strategy: string;
|
|
9
|
+
browser: boolean;
|
|
10
|
+
args: readonly Arg[];
|
|
11
|
+
columns: readonly string[];
|
|
12
|
+
defaultFormat?: string;
|
|
13
|
+
domain?: string;
|
|
14
|
+
example?: string;
|
|
15
|
+
siteSession?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PresentableCommandSource {
|
|
18
|
+
site: string;
|
|
19
|
+
name: string;
|
|
20
|
+
aliases?: readonly string[];
|
|
21
|
+
description: string;
|
|
22
|
+
access: string;
|
|
23
|
+
strategy?: string;
|
|
24
|
+
browser?: boolean;
|
|
25
|
+
args: readonly Arg[];
|
|
26
|
+
columns?: readonly string[];
|
|
27
|
+
defaultFormat?: string | null;
|
|
28
|
+
domain?: string | null;
|
|
29
|
+
example?: string;
|
|
30
|
+
siteSession?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface RootHelpCommand {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
}
|
|
36
|
+
export interface RootHelpOption {
|
|
37
|
+
flags: string;
|
|
38
|
+
description: string;
|
|
39
|
+
}
|
|
40
|
+
export interface RootHelpGroup {
|
|
41
|
+
label: string;
|
|
42
|
+
items: readonly string[];
|
|
43
|
+
}
|
|
44
|
+
export interface RootHelpPresentation {
|
|
45
|
+
description: string;
|
|
46
|
+
usage?: readonly string[];
|
|
47
|
+
commands: readonly RootHelpCommand[];
|
|
48
|
+
options: readonly RootHelpOption[];
|
|
49
|
+
/** Commander-generated body retained by local mode for byte-compatible layout. */
|
|
50
|
+
baseText?: string;
|
|
51
|
+
groups?: readonly RootHelpGroup[];
|
|
52
|
+
footer?: readonly string[];
|
|
53
|
+
localOnlyCommands?: readonly RootHelpCommand[];
|
|
54
|
+
localOnlyExplanation?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface CommandListPresentation {
|
|
57
|
+
rows: Record<string, unknown>[];
|
|
58
|
+
columns: string[];
|
|
59
|
+
structured: boolean;
|
|
60
|
+
displayLines?: string[];
|
|
61
|
+
}
|
|
62
|
+
export interface PresentableExternalCli {
|
|
63
|
+
label: string;
|
|
64
|
+
installed: boolean;
|
|
65
|
+
description?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface CompletableCommand {
|
|
68
|
+
site: string;
|
|
69
|
+
name: string;
|
|
70
|
+
aliases?: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
export declare function toPresentableCommand(command: PresentableCommandSource): PresentableCommand;
|
|
73
|
+
export declare function formatRootHelp(presentation: RootHelpPresentation): string;
|
|
74
|
+
export declare function commandListRows(commands: readonly PresentableCommand[], structured: boolean): Record<string, unknown>[];
|
|
75
|
+
export declare function commandListPresentation(commands: readonly PresentableCommand[], format: string, options?: {
|
|
76
|
+
externalClis?: readonly PresentableExternalCli[];
|
|
77
|
+
}): CommandListPresentation;
|
|
78
|
+
export type AdapterKind = 'site' | 'app';
|
|
79
|
+
export declare function classifyAdapterDomain(domain: string | undefined): AdapterKind;
|
|
80
|
+
export declare function getCommandCompletionCandidates(commands: readonly CompletableCommand[], words: readonly string[], cursor: number, builtins: readonly string[]): string[];
|
|
81
|
+
export declare function formatCommandListTerm(command: PresentableCommand): string;
|
|
82
|
+
export declare function siteHelpData(site: string, commands: readonly PresentableCommand[]): Record<string, unknown>;
|
|
83
|
+
export declare function commandHelpData(command: PresentableCommand): Record<string, unknown>;
|
|
84
|
+
export declare function formatCommonOptionsHelp(): string;
|
|
85
|
+
export declare function formatBrowserCommonOptionsHelp(): string;
|
|
86
|
+
export declare function formatSiteHelp(site: string, commands: readonly PresentableCommand[]): string;
|
|
87
|
+
export declare function formatCommandHelp(command: PresentableCommand): string;
|
|
88
|
+
export declare function formatSiteCommandDescription(command: PresentableCommand): string;
|
|
89
|
+
export declare function wrapCommaList(items: readonly string[], opts?: {
|
|
90
|
+
width?: number;
|
|
91
|
+
indent?: string;
|
|
92
|
+
}): string;
|
|
93
|
+
export declare function serializePresentableArg(arg: Arg): Record<string, unknown>;
|
|
94
|
+
export declare function formatArgumentSummary(args: readonly Arg[]): string;
|
|
95
|
+
export declare function formatPresentableCommandExample(command: PresentableCommand): string;
|