@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/help.js
CHANGED
|
@@ -1,54 +1,6 @@
|
|
|
1
1
|
import yaml from 'js-yaml';
|
|
2
|
-
import { fullName } from './registry.js';
|
|
3
|
-
import { formatCommandExample } from './serialization.js';
|
|
4
2
|
import { CLI_COMMAND } from './brand.js';
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
flags: '-f, --format <fmt>',
|
|
8
|
-
name: 'format',
|
|
9
|
-
help: 'Output format: table, plain, json, yaml, md, csv',
|
|
10
|
-
default: 'table',
|
|
11
|
-
choices: ['table', 'plain', 'json', 'yaml', 'md', 'csv'],
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
flags: '--trace <mode>',
|
|
15
|
-
name: 'trace',
|
|
16
|
-
help: 'Trace capture: off, on, retain-on-failure',
|
|
17
|
-
default: 'off',
|
|
18
|
-
choices: ['off', 'on', 'retain-on-failure'],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
flags: '-v, --verbose',
|
|
22
|
-
name: 'verbose',
|
|
23
|
-
help: 'Debug output',
|
|
24
|
-
default: false,
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
flags: '-h, --help',
|
|
28
|
-
name: 'help',
|
|
29
|
-
help: 'display help for command',
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
const BROWSER_COMMON_OPTIONS = [
|
|
33
|
-
{
|
|
34
|
-
flags: '--window <mode>',
|
|
35
|
-
name: 'window',
|
|
36
|
-
help: 'Browser window mode: foreground or background',
|
|
37
|
-
choices: ['foreground', 'background'],
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
flags: '--site-session <mode>',
|
|
41
|
-
name: 'site-session',
|
|
42
|
-
help: 'Adapter site session lifecycle: ephemeral or persistent',
|
|
43
|
-
choices: ['ephemeral', 'persistent'],
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
flags: '--keep-tab <bool>',
|
|
47
|
-
name: 'keep-tab',
|
|
48
|
-
help: 'Keep the browser tab lease after the command finishes',
|
|
49
|
-
choices: ['true', 'false'],
|
|
50
|
-
},
|
|
51
|
-
];
|
|
3
|
+
import { classifyAdapterDomain, commandHelpData as buildCommandHelpData, formatBrowserCommonOptionsHelp, formatCommandHelp, formatCommandListTerm as buildCommandListTerm, formatCommonOptionsHelp, formatSiteCommandDescription as buildSiteCommandDescription, formatSiteHelp, formatRootHelp, siteHelpData as buildSiteHelpData, toPresentableCommand, wrapCommaList as wrapPresentationCommaList, } from './command-presentation.js';
|
|
52
4
|
function normalizeStructuredHelpFormat(value) {
|
|
53
5
|
const normalized = value?.toLowerCase();
|
|
54
6
|
if (normalized === 'yaml' || normalized === 'yml')
|
|
@@ -78,41 +30,49 @@ export function renderStructuredHelp(data, format) {
|
|
|
78
30
|
return yaml.dump(data, { sortKeys: false, lineWidth: 120, noRefs: true });
|
|
79
31
|
}
|
|
80
32
|
export function wrapCommaList(items, opts = {}) {
|
|
81
|
-
|
|
82
|
-
const indent = opts.indent ?? ' ';
|
|
83
|
-
const sorted = [...items].sort((a, b) => a.localeCompare(b));
|
|
84
|
-
const lines = [];
|
|
85
|
-
let line = indent;
|
|
86
|
-
sorted.forEach((item, index) => {
|
|
87
|
-
const token = `${item}${index < sorted.length - 1 ? ',' : ''}`;
|
|
88
|
-
const prefix = line === indent ? '' : ' ';
|
|
89
|
-
if (line.length + prefix.length + token.length > width && line.trim()) {
|
|
90
|
-
lines.push(line);
|
|
91
|
-
line = `${indent}${token}`;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
line += `${prefix}${token}`;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
if (line.trim())
|
|
98
|
-
lines.push(line);
|
|
99
|
-
return lines.join('\n');
|
|
100
|
-
}
|
|
101
|
-
function isLocalIpDomain(domain) {
|
|
102
|
-
if (domain === '::1' || domain === '[::1]')
|
|
103
|
-
return true;
|
|
104
|
-
const parts = domain.split('.');
|
|
105
|
-
if (parts.length !== 4)
|
|
106
|
-
return false;
|
|
107
|
-
return parts.every(part => /^\d+$/.test(part) && Number(part) >= 0 && Number(part) <= 255)
|
|
108
|
-
&& Number(parts[0]) === 127;
|
|
33
|
+
return wrapPresentationCommaList(items, opts);
|
|
109
34
|
}
|
|
110
35
|
export function classifyAdapter(domain) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
36
|
+
return classifyAdapterDomain(domain);
|
|
37
|
+
}
|
|
38
|
+
const installedRootHelpPresentations = new WeakMap();
|
|
39
|
+
export function buildRootHelpPresentation(program, groups) {
|
|
40
|
+
const adapterNames = new Set([...groups.external.map((cli) => cli.name), ...groups.apps, ...groups.sites]);
|
|
41
|
+
const commanderHelp = program.createHelp();
|
|
42
|
+
const options = commanderHelp.visibleOptions(program).map((option) => ({
|
|
43
|
+
flags: option.flags,
|
|
44
|
+
description: option.description,
|
|
45
|
+
}));
|
|
46
|
+
return {
|
|
47
|
+
description: program.description(),
|
|
48
|
+
usage: [`${program.name()} [options] [command]`],
|
|
49
|
+
baseText: commanderHelp.formatHelp(program, commanderHelp),
|
|
50
|
+
options,
|
|
51
|
+
commands: program.commands
|
|
52
|
+
.filter((command) => !adapterNames.has(command.name()))
|
|
53
|
+
.map((command) => ({ name: command.name(), description: command.description() })),
|
|
54
|
+
groups: [
|
|
55
|
+
{ label: `External CLIs (${groups.external.length})`, items: groups.external.map((cli) => cli.label) },
|
|
56
|
+
{ label: `App adapters (${groups.apps.length})`, items: groups.apps },
|
|
57
|
+
{ label: `Site adapters (${groups.sites.length})`, items: groups.sites },
|
|
58
|
+
],
|
|
59
|
+
footer: [
|
|
60
|
+
`Run '${CLI_COMMAND} list' for full command details, or '${CLI_COMMAND} <site> --help' to inspect one site.`,
|
|
61
|
+
`Agent tip: use '${CLI_COMMAND} <site> --help -f yaml' for all command args/options in one structured response.`,
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function getInstalledRootHelpPresentation(command) {
|
|
66
|
+
return installedRootHelpPresentations.get(command);
|
|
67
|
+
}
|
|
68
|
+
export function installRootPresentationHelp(command, data, presentation) {
|
|
69
|
+
installedRootHelpPresentations.set(command, presentation);
|
|
70
|
+
command.helpInformation = (() => {
|
|
71
|
+
const format = getRequestedHelpFormat();
|
|
72
|
+
if (format)
|
|
73
|
+
return renderStructuredHelp(data(), format);
|
|
74
|
+
return formatRootHelp(presentation);
|
|
75
|
+
});
|
|
116
76
|
}
|
|
117
77
|
function formatGroupSection(label, names) {
|
|
118
78
|
if (names.length === 0)
|
|
@@ -136,27 +96,6 @@ export function formatRootAdapterHelpText(groups) {
|
|
|
136
96
|
lines.push('');
|
|
137
97
|
return lines.join('\n');
|
|
138
98
|
}
|
|
139
|
-
function compactArg(arg) {
|
|
140
|
-
return {
|
|
141
|
-
name: arg.name,
|
|
142
|
-
...(arg.type && arg.type !== 'string' ? { type: arg.type } : {}),
|
|
143
|
-
...(arg.positional ? { positional: true } : {}),
|
|
144
|
-
...(arg.required ? { required: true } : {}),
|
|
145
|
-
...(arg.valueRequired ? { valueRequired: true } : {}),
|
|
146
|
-
...(arg.default !== undefined ? { default: arg.default } : {}),
|
|
147
|
-
...(arg.choices?.length ? { choices: arg.choices } : {}),
|
|
148
|
-
...(arg.help ? { help: arg.help } : {}),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
function compactCommonOption(option) {
|
|
152
|
-
return {
|
|
153
|
-
name: option.name,
|
|
154
|
-
flags: option.flags,
|
|
155
|
-
help: option.help,
|
|
156
|
-
...('default' in option ? { default: option.default } : {}),
|
|
157
|
-
...('choices' in option ? { choices: option.choices } : {}),
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
99
|
function compactCommanderArgument(arg) {
|
|
161
100
|
return {
|
|
162
101
|
name: arg.name(),
|
|
@@ -343,49 +282,8 @@ export function installCommanderNamespaceStructuredHelp(namespaceRoot, opts = {}
|
|
|
343
282
|
}
|
|
344
283
|
}
|
|
345
284
|
}
|
|
346
|
-
function positionals(cmd) {
|
|
347
|
-
return cmd.args.filter(arg => arg.positional);
|
|
348
|
-
}
|
|
349
|
-
function commandOptions(cmd) {
|
|
350
|
-
return cmd.args.filter(arg => !arg.positional);
|
|
351
|
-
}
|
|
352
|
-
function formatPositionals(args) {
|
|
353
|
-
return args
|
|
354
|
-
.map(arg => arg.required ? `<${arg.name}>` : `[${arg.name}]`)
|
|
355
|
-
.join(' ');
|
|
356
|
-
}
|
|
357
|
-
function formatCommandOptionTerm(arg) {
|
|
358
|
-
if (arg.required || arg.valueRequired)
|
|
359
|
-
return `--${arg.name} <value>`;
|
|
360
|
-
return `--${arg.name} [value]`;
|
|
361
|
-
}
|
|
362
285
|
export function formatCommandListTerm(cmd) {
|
|
363
|
-
|
|
364
|
-
const optionText = commandOptions(cmd).length > 0 ? ' [options]' : '';
|
|
365
|
-
return `${cmd.name}${positionalText ? ` ${positionalText}` : ''}${optionText}`;
|
|
366
|
-
}
|
|
367
|
-
function formatUsage(cmd) {
|
|
368
|
-
const positionalText = formatPositionals(positionals(cmd));
|
|
369
|
-
return `${CLI_COMMAND} ${cmd.site} ${cmd.name}${positionalText ? ` ${positionalText}` : ''} [options]`;
|
|
370
|
-
}
|
|
371
|
-
function compactCommand(cmd) {
|
|
372
|
-
return {
|
|
373
|
-
name: cmd.name,
|
|
374
|
-
command: `${CLI_COMMAND} ${cmd.site} ${cmd.name}`,
|
|
375
|
-
usage: formatUsage(cmd),
|
|
376
|
-
access: cmd.access,
|
|
377
|
-
description: cmd.description,
|
|
378
|
-
browser: !!cmd.browser,
|
|
379
|
-
...(cmd.domain ? { domain: cmd.domain } : {}),
|
|
380
|
-
...(cmd.aliases?.length ? { aliases: cmd.aliases } : {}),
|
|
381
|
-
positionals: positionals(cmd).map(compactArg),
|
|
382
|
-
command_options: commandOptions(cmd).map(compactArg),
|
|
383
|
-
...(cmd.browser ? { browser_common_options: BROWSER_COMMON_OPTIONS.map(compactCommonOption) } : {}),
|
|
384
|
-
example: formatCommandExample(cmd),
|
|
385
|
-
...(cmd.siteSession ? { siteSession: cmd.siteSession } : {}),
|
|
386
|
-
...(cmd.defaultFormat ? { defaultFormat: cmd.defaultFormat } : {}),
|
|
387
|
-
...(cmd.columns?.length ? { columns: cmd.columns } : {}),
|
|
388
|
-
};
|
|
286
|
+
return buildCommandListTerm(toPresentableCommand(cmd));
|
|
389
287
|
}
|
|
390
288
|
export function rootHelpData(program, groups) {
|
|
391
289
|
const adapterNames = new Set([...groups.external.map(cli => cli.name), ...groups.apps, ...groups.sites]);
|
|
@@ -421,124 +319,22 @@ export function rootHelpData(program, groups) {
|
|
|
421
319
|
};
|
|
422
320
|
}
|
|
423
321
|
export function siteHelpData(site, commands) {
|
|
424
|
-
|
|
425
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
426
|
-
return {
|
|
427
|
-
site,
|
|
428
|
-
command_count: unique.length,
|
|
429
|
-
commands: unique.map(cmd => compactCommand(cmd)),
|
|
430
|
-
common_options: COMMON_OPTIONS.map(compactCommonOption),
|
|
431
|
-
...(unique.some(cmd => cmd.browser) ? { browser_common_options: BROWSER_COMMON_OPTIONS.map(compactCommonOption) } : {}),
|
|
432
|
-
next: [
|
|
433
|
-
`${CLI_COMMAND} ${site} <command> --help -f yaml`,
|
|
434
|
-
`${CLI_COMMAND} ${site} <command> -f yaml`,
|
|
435
|
-
],
|
|
436
|
-
};
|
|
322
|
+
return buildSiteHelpData(site, commands.map(toPresentableCommand));
|
|
437
323
|
}
|
|
438
324
|
export function commandHelpData(cmd) {
|
|
439
|
-
return
|
|
440
|
-
site: cmd.site,
|
|
441
|
-
...compactCommand(cmd),
|
|
442
|
-
common_options: COMMON_OPTIONS.map(compactCommonOption),
|
|
443
|
-
...(cmd.browser ? { browser_common_options: BROWSER_COMMON_OPTIONS.map(compactCommonOption) } : {}),
|
|
444
|
-
output_formats: ['table', 'plain', 'yaml', 'json', 'md', 'csv'],
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
function formatRows(rows) {
|
|
448
|
-
if (rows.length === 0)
|
|
449
|
-
return [];
|
|
450
|
-
const width = Math.min(Math.max(...rows.map(([left]) => left.length)), 34);
|
|
451
|
-
return rows.map(([left, right]) => ` ${left.padEnd(width + 2)}${right}`);
|
|
452
|
-
}
|
|
453
|
-
function formatArgHelp(arg) {
|
|
454
|
-
const parts = [];
|
|
455
|
-
if (arg.help)
|
|
456
|
-
parts.push(arg.help);
|
|
457
|
-
if (arg.default !== undefined)
|
|
458
|
-
parts.push(`default: ${arg.default}`);
|
|
459
|
-
if (arg.choices?.length)
|
|
460
|
-
parts.push(`choices: ${arg.choices.join(', ')}`);
|
|
461
|
-
return parts.join(' ');
|
|
325
|
+
return buildCommandHelpData(toPresentableCommand(cmd));
|
|
462
326
|
}
|
|
463
327
|
export function formatCommonOptionsHelpText() {
|
|
464
|
-
|
|
465
|
-
const details = [option.help];
|
|
466
|
-
if ('default' in option)
|
|
467
|
-
details.push(`default: ${option.default}`);
|
|
468
|
-
if ('choices' in option)
|
|
469
|
-
details.push(`choices: ${option.choices.join(', ')}`);
|
|
470
|
-
return [option.flags, details.join(' ')];
|
|
471
|
-
});
|
|
472
|
-
return ['Common options:', ...formatRows(rows)].join('\n');
|
|
328
|
+
return formatCommonOptionsHelp();
|
|
473
329
|
}
|
|
474
330
|
export function formatBrowserCommonOptionsHelpText() {
|
|
475
|
-
|
|
476
|
-
const details = [option.help];
|
|
477
|
-
if ('choices' in option)
|
|
478
|
-
details.push(`choices: ${option.choices.join(', ')}`);
|
|
479
|
-
return [option.flags, details.join(' ')];
|
|
480
|
-
});
|
|
481
|
-
return ['Browser common options:', ...formatRows(rows)].join('\n');
|
|
331
|
+
return formatBrowserCommonOptionsHelp();
|
|
482
332
|
}
|
|
483
333
|
export function formatSiteHelpText(site, commands) {
|
|
484
|
-
|
|
485
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
486
|
-
const lines = [
|
|
487
|
-
`Usage: ${CLI_COMMAND} ${site} <command> [args] [options]`,
|
|
488
|
-
'',
|
|
489
|
-
wrapCommaList(unique.map(cmd => cmd.name), { indent: '' }),
|
|
490
|
-
'',
|
|
491
|
-
'Commands:',
|
|
492
|
-
...formatRows(unique.map(cmd => [formatCommandListTerm(cmd), formatSiteCommandDescription(cmd)])),
|
|
493
|
-
'',
|
|
494
|
-
formatCommonOptionsHelpText(),
|
|
495
|
-
...(unique.some(cmd => cmd.browser) ? ['', formatBrowserCommonOptionsHelpText()] : []),
|
|
496
|
-
'',
|
|
497
|
-
`Agent tip: use '${CLI_COMMAND} ${site} --help -f yaml' to get all command args/options in one structured response.`,
|
|
498
|
-
'',
|
|
499
|
-
];
|
|
500
|
-
return lines.join('\n');
|
|
334
|
+
return formatSiteHelp(site, commands.map(toPresentableCommand));
|
|
501
335
|
}
|
|
502
336
|
export function formatCommandHelpText(cmd) {
|
|
503
|
-
|
|
504
|
-
`Usage: ${formatUsage(cmd)}`,
|
|
505
|
-
'',
|
|
506
|
-
cmd.description,
|
|
507
|
-
'',
|
|
508
|
-
];
|
|
509
|
-
const positionalRows = positionals(cmd).map(arg => [
|
|
510
|
-
arg.name,
|
|
511
|
-
formatArgHelp(arg),
|
|
512
|
-
]);
|
|
513
|
-
if (positionalRows.length) {
|
|
514
|
-
lines.push('Arguments:', ...formatRows(positionalRows), '');
|
|
515
|
-
}
|
|
516
|
-
const optionRows = commandOptions(cmd).map(arg => [
|
|
517
|
-
formatCommandOptionTerm(arg),
|
|
518
|
-
formatArgHelp(arg),
|
|
519
|
-
]);
|
|
520
|
-
if (optionRows.length) {
|
|
521
|
-
lines.push('Command options:', ...formatRows(optionRows), '');
|
|
522
|
-
}
|
|
523
|
-
lines.push(formatCommonOptionsHelpText(), '');
|
|
524
|
-
if (cmd.browser)
|
|
525
|
-
lines.push(formatBrowserCommonOptionsHelpText(), '');
|
|
526
|
-
const meta = [];
|
|
527
|
-
meta.push(`Access: ${cmd.access}`);
|
|
528
|
-
meta.push(`Browser: ${cmd.browser ? 'yes' : 'no'}`);
|
|
529
|
-
if (cmd.domain)
|
|
530
|
-
meta.push(`Domain: ${cmd.domain}`);
|
|
531
|
-
if (cmd.defaultFormat)
|
|
532
|
-
meta.push(`Default format: ${cmd.defaultFormat}`);
|
|
533
|
-
if (cmd.aliases?.length)
|
|
534
|
-
meta.push(`Aliases: ${cmd.aliases.join(', ')}`);
|
|
535
|
-
lines.push(meta.join(' | '));
|
|
536
|
-
lines.push(`Example: ${formatCommandExample(cmd)}`);
|
|
537
|
-
if (cmd.columns?.length)
|
|
538
|
-
lines.push(`Output columns: ${cmd.columns.join(', ')}`);
|
|
539
|
-
lines.push("Agent tip: use '--help -f yaml' for structured args/options.");
|
|
540
|
-
lines.push('');
|
|
541
|
-
return lines.join('\n');
|
|
337
|
+
return formatCommandHelp(toPresentableCommand(cmd));
|
|
542
338
|
}
|
|
543
339
|
export function installStructuredHelp(command, data, textSuffix) {
|
|
544
340
|
const original = command.helpInformation.bind(command);
|
|
@@ -551,6 +347,5 @@ export function installStructuredHelp(command, data, textSuffix) {
|
|
|
551
347
|
});
|
|
552
348
|
}
|
|
553
349
|
export function formatSiteCommandDescription(cmd) {
|
|
554
|
-
|
|
555
|
-
return `${access} ${cmd.description}`;
|
|
350
|
+
return buildSiteCommandDescription(toPresentableCommand(cmd));
|
|
556
351
|
}
|
package/dist/src/help.test.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { classifyAdapter, formatRootAdapterHelpText } from './help.js';
|
|
2
|
+
import { classifyAdapter, commandHelpData, formatCommandHelpText, formatRootAdapterHelpText, formatSiteHelpText, siteHelpData, } from './help.js';
|
|
3
|
+
import { commandHelpData as sharedCommandHelpData, formatCommandHelp, formatSiteHelp, siteHelpData as sharedSiteHelpData, toPresentableCommand, } from './command-presentation.js';
|
|
4
|
+
import { Strategy } from './registry.js';
|
|
5
|
+
const presentableFixture = {
|
|
6
|
+
site: 'github',
|
|
7
|
+
name: 'issues',
|
|
8
|
+
aliases: ['issue-list'],
|
|
9
|
+
description: 'List repository issues',
|
|
10
|
+
access: 'read',
|
|
11
|
+
strategy: Strategy.COOKIE,
|
|
12
|
+
browser: true,
|
|
13
|
+
args: [{ name: 'owner', positional: true, required: true, help: 'Repository owner' }],
|
|
14
|
+
columns: ['number', 'title'],
|
|
15
|
+
domain: 'github.com',
|
|
16
|
+
};
|
|
3
17
|
describe('classifyAdapter', () => {
|
|
4
18
|
it('classifies DNS-style domains as site', () => {
|
|
5
19
|
expect(classifyAdapter('www.youtube.com')).toBe('site');
|
|
@@ -56,3 +70,15 @@ describe('formatRootAdapterHelpText', () => {
|
|
|
56
70
|
expect(text).toContain("'webcmd <site> --help -f yaml'");
|
|
57
71
|
});
|
|
58
72
|
});
|
|
73
|
+
describe('shared presentation delegation', () => {
|
|
74
|
+
it('keeps local site and command text byte-identical to the pure model', () => {
|
|
75
|
+
const presentable = toPresentableCommand(presentableFixture);
|
|
76
|
+
expect(formatSiteHelpText('github', [presentableFixture])).toBe(formatSiteHelp('github', [presentable]));
|
|
77
|
+
expect(formatCommandHelpText(presentableFixture)).toBe(formatCommandHelp(presentable));
|
|
78
|
+
});
|
|
79
|
+
it('keeps local structured help byte-identical to the pure model', () => {
|
|
80
|
+
const presentable = toPresentableCommand(presentableFixture);
|
|
81
|
+
expect(siteHelpData('github', [presentableFixture])).toEqual(sharedSiteHelpData('github', [presentable]));
|
|
82
|
+
expect(commandHelpData(presentableFixture)).toEqual(sharedCommandHelpData(presentable));
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
+
import { type ParsedCommandSurface } from '../command-surface.js';
|
|
1
2
|
import type { HostedCommand } from './types.js';
|
|
2
|
-
export
|
|
3
|
-
args: Record<string, unknown>;
|
|
4
|
-
format: string;
|
|
5
|
-
trace: string;
|
|
6
|
-
profile?: string;
|
|
7
|
-
help: boolean;
|
|
8
|
-
}
|
|
3
|
+
export type ParsedHostedInvocation = ParsedCommandSurface;
|
|
9
4
|
export declare function parseHostedInvocation(command: HostedCommand, argv: string[]): ParsedHostedInvocation;
|
package/dist/src/hosted/args.js
CHANGED
|
@@ -1,101 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseCommandSurface } from '../command-surface.js';
|
|
2
2
|
export function parseHostedInvocation(command, argv) {
|
|
3
|
-
|
|
4
|
-
const positional = command.args.filter((arg) => arg.positional);
|
|
5
|
-
const named = new Map(command.args.filter((arg) => !arg.positional).map((arg) => [arg.name, arg]));
|
|
6
|
-
let positionalIndex = 0;
|
|
7
|
-
let format = String(command.defaultFormat || 'table');
|
|
8
|
-
let trace = 'off';
|
|
9
|
-
let profile;
|
|
10
|
-
let help = false;
|
|
11
|
-
for (let i = 0; i < argv.length; i++) {
|
|
12
|
-
const token = argv[i];
|
|
13
|
-
if (token === '--') {
|
|
14
|
-
for (const rest of argv.slice(i + 1)) {
|
|
15
|
-
const arg = positional[positionalIndex++];
|
|
16
|
-
if (!arg)
|
|
17
|
-
throw new ArgumentError(`Unexpected positional argument: ${rest}`);
|
|
18
|
-
args[arg.name] = coerceValue(rest, arg.type);
|
|
19
|
-
}
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
if (token === '-h' || token === '--help') {
|
|
23
|
-
help = true;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (token === '-f' || token === '--format') {
|
|
27
|
-
format = readValue(argv, ++i, token);
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (token === '--trace') {
|
|
31
|
-
trace = readValue(argv, ++i, token);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (token === '--profile') {
|
|
35
|
-
profile = readValue(argv, ++i, token);
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (token.startsWith('--')) {
|
|
39
|
-
const eqIndex = token.indexOf('=');
|
|
40
|
-
const rawName = token.slice(2, eqIndex === -1 ? undefined : eqIndex);
|
|
41
|
-
const arg = named.get(rawName);
|
|
42
|
-
if (!arg)
|
|
43
|
-
throw new ArgumentError(`Unknown option for hosted command ${command.command}: --${rawName}`);
|
|
44
|
-
const inlineValue = eqIndex === -1 ? undefined : token.slice(eqIndex + 1);
|
|
45
|
-
if (arg.type === 'bool' || arg.type === 'boolean') {
|
|
46
|
-
args[arg.name] = inlineValue === undefined ? true : coerceValue(inlineValue, arg.type);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
args[arg.name] = coerceValue(inlineValue ?? readValue(argv, ++i, token), arg.type);
|
|
50
|
-
}
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const arg = positional[positionalIndex++];
|
|
54
|
-
if (!arg)
|
|
55
|
-
throw new ArgumentError(`Unexpected positional argument: ${token}`);
|
|
56
|
-
args[arg.name] = coerceValue(token, arg.type);
|
|
57
|
-
}
|
|
58
|
-
for (const arg of command.args) {
|
|
59
|
-
if (args[arg.name] === undefined && arg.default !== undefined)
|
|
60
|
-
args[arg.name] = arg.default;
|
|
61
|
-
if (arg.required && args[arg.name] === undefined) {
|
|
62
|
-
throw new ArgumentError(`Missing required argument for hosted command ${command.command}: ${arg.name}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
args,
|
|
67
|
-
format,
|
|
68
|
-
trace,
|
|
69
|
-
...(profile ? { profile } : {}),
|
|
70
|
-
help,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function readValue(argv, index, flag) {
|
|
74
|
-
const value = argv[index];
|
|
75
|
-
if (value === undefined || value.startsWith('--')) {
|
|
76
|
-
throw new ArgumentError(`${flag} requires a value.`);
|
|
77
|
-
}
|
|
78
|
-
return value;
|
|
79
|
-
}
|
|
80
|
-
function coerceValue(value, type) {
|
|
81
|
-
if (type === 'int') {
|
|
82
|
-
const parsed = Number.parseInt(value, 10);
|
|
83
|
-
if (!Number.isFinite(parsed))
|
|
84
|
-
throw new ArgumentError(`Expected integer value, got "${value}".`);
|
|
85
|
-
return parsed;
|
|
86
|
-
}
|
|
87
|
-
if (type === 'number') {
|
|
88
|
-
const parsed = Number(value);
|
|
89
|
-
if (!Number.isFinite(parsed))
|
|
90
|
-
throw new ArgumentError(`Expected number value, got "${value}".`);
|
|
91
|
-
return parsed;
|
|
92
|
-
}
|
|
93
|
-
if (type === 'bool' || type === 'boolean') {
|
|
94
|
-
if (value === 'true' || value === '1')
|
|
95
|
-
return true;
|
|
96
|
-
if (value === 'false' || value === '0')
|
|
97
|
-
return false;
|
|
98
|
-
throw new ArgumentError(`Expected boolean value, got "${value}".`);
|
|
99
|
-
}
|
|
100
|
-
return value;
|
|
3
|
+
return parseCommandSurface(command, argv);
|
|
101
4
|
}
|
|
@@ -12,6 +12,7 @@ const command = {
|
|
|
12
12
|
{ name: 'query', positional: true, required: true, type: 'string' },
|
|
13
13
|
{ name: 'limit', type: 'int', default: 10 },
|
|
14
14
|
{ name: 'include-forks', type: 'boolean', default: false },
|
|
15
|
+
{ name: 'sort', type: 'string', choices: ['updated', 'stars'] },
|
|
15
16
|
],
|
|
16
17
|
columns: ['name'],
|
|
17
18
|
};
|
|
@@ -24,12 +25,25 @@ describe('parseHostedInvocation', () => {
|
|
|
24
25
|
limit: 5,
|
|
25
26
|
'include-forks': true,
|
|
26
27
|
},
|
|
28
|
+
optionSources: {
|
|
29
|
+
query: 'cli',
|
|
30
|
+
limit: 'cli',
|
|
31
|
+
'include-forks': 'cli',
|
|
32
|
+
},
|
|
27
33
|
format: 'json',
|
|
34
|
+
formatExplicit: true,
|
|
28
35
|
trace: 'on',
|
|
36
|
+
verbose: false,
|
|
29
37
|
help: false,
|
|
30
38
|
});
|
|
31
39
|
});
|
|
32
40
|
it('rejects missing required positional args', () => {
|
|
33
|
-
expect(() => parseHostedInvocation(command, [])).toThrow(
|
|
41
|
+
expect(() => parseHostedInvocation(command, [])).toThrow("error: missing required argument 'query'");
|
|
42
|
+
});
|
|
43
|
+
it.each([
|
|
44
|
+
{ name: 'partial integers', argv: ['webcmd', '--limit', '12x'], message: /limit.*number/i },
|
|
45
|
+
{ name: 'invalid choices', argv: ['webcmd', '--sort', 'recent'], message: /sort.*one of.*updated.*stars/i },
|
|
46
|
+
])('rejects $name', ({ argv, message }) => {
|
|
47
|
+
expect(() => parseHostedInvocation(command, argv)).toThrow(message);
|
|
34
48
|
});
|
|
35
49
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Strategy } from '../registry.js';
|
|
2
|
+
export type HostedAvailability = {
|
|
3
|
+
mode: 'hosted';
|
|
4
|
+
} | {
|
|
5
|
+
mode: 'local-only';
|
|
6
|
+
reason: 'desktop-app' | 'local-tool' | 'browser-bind';
|
|
7
|
+
};
|
|
8
|
+
export interface HostedAvailabilityMetadata {
|
|
9
|
+
strategy?: Strategy | string;
|
|
10
|
+
domain?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function deriveHostedAvailability(command: HostedAvailabilityMetadata): HostedAvailability;
|
|
13
|
+
export declare function deriveBrowserAvailability(command: string): HostedAvailability;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { classifyAdapter } from '../help.js';
|
|
2
|
+
import { Strategy } from '../registry.js';
|
|
3
|
+
export function deriveHostedAvailability(command) {
|
|
4
|
+
if (String(command.strategy).toLowerCase() === Strategy.LOCAL) {
|
|
5
|
+
return { mode: 'local-only', reason: 'local-tool' };
|
|
6
|
+
}
|
|
7
|
+
if (classifyAdapter(command.domain) === 'app') {
|
|
8
|
+
return { mode: 'local-only', reason: 'desktop-app' };
|
|
9
|
+
}
|
|
10
|
+
return { mode: 'hosted' };
|
|
11
|
+
}
|
|
12
|
+
export function deriveBrowserAvailability(command) {
|
|
13
|
+
return command === 'bind'
|
|
14
|
+
? { mode: 'local-only', reason: 'browser-bind' }
|
|
15
|
+
: { mode: 'hosted' };
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|