@agentrhq/webcmd 0.2.5 → 0.3.1
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 +217 -13
- package/clis/_shared/site-auth.js +3 -3
- package/clis/_shared/site-auth.test.js +4 -4
- 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/slock/whoami.test.js +2 -2
- 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/daemon-client.d.ts +2 -1
- package/dist/src/browser/runtime/local-cloak/actions.js +4 -1
- 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/provider.test.js +36 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +10 -2
- package/dist/src/browser/runtime/local-cloak/session-manager.js +25 -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 +63 -110
- package/dist/src/cli.test.js +82 -6
- 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/commands/auth.js +3 -2
- package/dist/src/commands/auth.test.js +6 -6
- 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 +11 -1
- 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 +4 -0
- package/dist/src/hosted/args.js +4 -0
- package/dist/src/hosted/args.test.d.ts +1 -0
- package/dist/src/hosted/args.test.js +49 -0
- 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 +180 -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 +59 -0
- package/dist/src/hosted/client.js +510 -0
- package/dist/src/hosted/client.test.d.ts +1 -0
- package/dist/src/hosted/client.test.js +738 -0
- package/dist/src/hosted/config.d.ts +55 -0
- package/dist/src/hosted/config.js +112 -0
- package/dist/src/hosted/config.test.d.ts +1 -0
- package/dist/src/hosted/config.test.js +81 -0
- 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 +14 -0
- package/dist/src/hosted/manifest.js +47 -0
- package/dist/src/hosted/manifest.test.d.ts +1 -0
- package/dist/src/hosted/manifest.test.js +164 -0
- 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 +19 -0
- package/dist/src/hosted/runner.js +705 -0
- package/dist/src/hosted/runner.test.d.ts +1 -0
- package/dist/src/hosted/runner.test.js +1607 -0
- package/dist/src/hosted/setup.d.ts +10 -0
- package/dist/src/hosted/setup.js +79 -0
- package/dist/src/hosted/setup.test.d.ts +1 -0
- package/dist/src/hosted/setup.test.js +195 -0
- package/dist/src/hosted/types.d.ts +175 -0
- package/dist/src/hosted/types.js +1 -0
- package/dist/src/main.js +123 -97
- 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.d.ts +6 -1
- package/dist/src/release-notes.js +192 -4
- package/dist/src/release-notes.test.js +143 -4
- 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 +36748 -0
- package/package.json +6 -1
- package/scripts/check-hosted-contract.mjs +108 -0
- package/scripts/docs-sync-review.ts +332 -0
- package/scripts/generate-release-notes.ts +1 -1
- package/scripts/sync-community-plugins.ts +14 -0
- package/skills/smart-search/SKILL.md +14 -3
- package/skills/webcmd-autofix/SKILL.md +26 -37
- package/skills/webcmd-usage/SKILL.md +21 -7
- package/clis/antigravity/SKILL.md +0 -38
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
|
|
@@ -1022,11 +952,12 @@ Examples:
|
|
|
1022
952
|
const profileSelection = getBrowserProfileSelection(command);
|
|
1023
953
|
const contextId = profileSelection?.contextId;
|
|
1024
954
|
const routing = profileRouteParams(profileSelection);
|
|
955
|
+
const windowMode = getBrowserWindowMode(command, 'foreground');
|
|
1025
956
|
try {
|
|
1026
957
|
const { BrowserBridge } = await import('./browser/index.js');
|
|
1027
958
|
const bridge = new BrowserBridge();
|
|
1028
|
-
await bridge.connect({ timeout: DEFAULT_BROWSER_CONNECT_TIMEOUT, session, surface: 'browser', ...routing });
|
|
1029
|
-
await fn({ session, contextId, routing }, opts);
|
|
959
|
+
await bridge.connect({ timeout: DEFAULT_BROWSER_CONNECT_TIMEOUT, session, surface: 'browser', ...routing, windowMode });
|
|
960
|
+
await fn({ session, contextId, routing, windowMode }, opts);
|
|
1030
961
|
}
|
|
1031
962
|
catch (err) {
|
|
1032
963
|
if (err instanceof BrowserCommandError) {
|
|
@@ -1044,7 +975,7 @@ Examples:
|
|
|
1044
975
|
.description('Bind an existing Cloak runtime tab to the browser session named by <session>')
|
|
1045
976
|
.option('--page <id>', 'Cloak tab page id from `webcmd browser <session> tab list`')
|
|
1046
977
|
.option('--index <n>', 'Cloak tab index from `webcmd browser <session> tab list`')
|
|
1047
|
-
.action(browserSessionCommandAction(async ({ session, contextId, routing }, opts) => {
|
|
978
|
+
.action(browserSessionCommandAction(async ({ session, contextId, routing, windowMode }, opts) => {
|
|
1048
979
|
const page = typeof opts.page === 'string' && opts.page.trim() ? opts.page.trim() : undefined;
|
|
1049
980
|
const rawIndex = typeof opts.index === 'string' && opts.index.trim() ? opts.index.trim() : undefined;
|
|
1050
981
|
if ((page && rawIndex) || (!page && !rawIndex)) {
|
|
@@ -1054,7 +985,7 @@ Examples:
|
|
|
1054
985
|
throw new BrowserCommandError('--index must be a non-negative integer.', 'invalid_request');
|
|
1055
986
|
}
|
|
1056
987
|
const index = rawIndex === undefined ? undefined : Number.parseInt(rawIndex, 10);
|
|
1057
|
-
const data = await bindTab(session, { ...routing, ...(page && { page }), ...(index !== undefined && { index }) });
|
|
988
|
+
const data = await bindTab(session, { ...routing, ...(page && { page }), ...(index !== undefined && { index }), windowMode });
|
|
1058
989
|
saveBrowserTargetState(undefined, getBrowserScope(session, contextId));
|
|
1059
990
|
console.log(JSON.stringify({ session, ...((data && typeof data === 'object') ? data : { data }) }, null, 2));
|
|
1060
991
|
}));
|
|
@@ -1203,8 +1134,8 @@ Examples:
|
|
|
1203
1134
|
addBrowserTabOption(browser.command('screenshot').argument('[path]', 'Save to file (base64 if omitted)'))
|
|
1204
1135
|
.option('--full-page', 'Capture the full scrollable page, not just the viewport', false)
|
|
1205
1136
|
.option('--annotate', 'Overlay visible browser state ref labels on the screenshot', false)
|
|
1206
|
-
.option('--width <n>', 'Override viewport width in CSS pixels for this screenshot only', (
|
|
1207
|
-
.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))
|
|
1208
1139
|
.description('Take screenshot')
|
|
1209
1140
|
.action(browserAction(async (page, path, opts) => {
|
|
1210
1141
|
const shotOpts = {
|
|
@@ -1857,8 +1788,13 @@ Examples:
|
|
|
1857
1788
|
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
|
1858
1789
|
return;
|
|
1859
1790
|
}
|
|
1860
|
-
const { matches_n, match_level } = await page.click(resolvedTarget, parsed.opts);
|
|
1861
|
-
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));
|
|
1862
1798
|
}));
|
|
1863
1799
|
addBrowserTabOption(addSemanticLocatorOptions(browser.command('type'))
|
|
1864
1800
|
.argument('[targetOrText]', 'Numeric ref/CSS target, or text when using --role/--name/etc.')
|
|
@@ -2834,10 +2770,7 @@ cli({
|
|
|
2834
2770
|
const report = await runBrowserDoctor({ cliVersion: PKG_VERSION });
|
|
2835
2771
|
console.log(renderBrowserDoctorReport(report));
|
|
2836
2772
|
});
|
|
2837
|
-
program
|
|
2838
|
-
.command('completion')
|
|
2839
|
-
.description('Output shell completion script')
|
|
2840
|
-
.argument('<shell>', 'Shell type: bash, zsh, or fish')
|
|
2773
|
+
configureCompletionCommandSurface(program.command('completion'))
|
|
2841
2774
|
.action((shell) => {
|
|
2842
2775
|
printCompletionScript(shell);
|
|
2843
2776
|
});
|
|
@@ -3101,12 +3034,32 @@ cli({
|
|
|
3101
3034
|
.argument('<name>', 'Plugin name (lowercase, hyphens allowed)')
|
|
3102
3035
|
.option('-d, --dir <path>', 'Output directory (default: ./<name>)')
|
|
3103
3036
|
.option('--description <text>', 'Plugin description')
|
|
3037
|
+
.option('--author-name <name>', 'Author display name')
|
|
3038
|
+
.option('--author-handle <handle>', 'Author GitHub handle')
|
|
3104
3039
|
.action(async (name, opts) => {
|
|
3105
3040
|
const { createPluginScaffold } = await import('./plugin-scaffold.js');
|
|
3106
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
|
+
}
|
|
3107
3056
|
const result = createPluginScaffold(name, {
|
|
3108
3057
|
dir: opts.dir,
|
|
3109
3058
|
description: opts.description,
|
|
3059
|
+
author: {
|
|
3060
|
+
name: authorName ?? '',
|
|
3061
|
+
handle: authorHandle ?? '',
|
|
3062
|
+
},
|
|
3110
3063
|
});
|
|
3111
3064
|
console.log(`✅ Plugin scaffold created at ${result.dir}`);
|
|
3112
3065
|
console.log();
|
|
@@ -3479,7 +3432,7 @@ cli({
|
|
|
3479
3432
|
applyAncestorAwareUsage(sub);
|
|
3480
3433
|
}
|
|
3481
3434
|
applyAncestorAwareUsage(browser);
|
|
3482
|
-
|
|
3435
|
+
installRootPresentationHelp(program, () => rootHelpData(program, adapterGroups), buildRootHelpPresentation(program, adapterGroups));
|
|
3483
3436
|
// ── Unknown command fallback ──────────────────────────────────────────────
|
|
3484
3437
|
// Security: do NOT auto-discover and register arbitrary system binaries.
|
|
3485
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.
|
|
@@ -1109,8 +1159,8 @@ describe('browser tab targeting commands', () => {
|
|
|
1109
1159
|
it('binds an existing Cloak tab by page id into a browser session', async () => {
|
|
1110
1160
|
const program = createProgram('', '');
|
|
1111
1161
|
await program.parseAsync(['node', 'webcmd', 'browser', '--session', 'test', 'bind', '--page', 'tab-2']);
|
|
1112
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser' });
|
|
1113
|
-
expect(mockBindTab).toHaveBeenCalledWith('test', { page: 'tab-2' });
|
|
1162
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser', windowMode: 'foreground' });
|
|
1163
|
+
expect(mockBindTab).toHaveBeenCalledWith('test', { page: 'tab-2', windowMode: 'foreground' });
|
|
1114
1164
|
const out = lastJsonLog();
|
|
1115
1165
|
expect(out.session).toBe('test');
|
|
1116
1166
|
expect(out.url).toBe('https://user.example/inbox');
|
|
@@ -1118,11 +1168,17 @@ describe('browser tab targeting commands', () => {
|
|
|
1118
1168
|
it('binds an existing Cloak tab by index into a browser session', async () => {
|
|
1119
1169
|
const program = createProgram('', '');
|
|
1120
1170
|
await program.parseAsync(['node', 'webcmd', 'browser', '--session', 'test', 'bind', '--index', '1']);
|
|
1121
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser' });
|
|
1122
|
-
expect(mockBindTab).toHaveBeenCalledWith('test', { index: 1 });
|
|
1171
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser', windowMode: 'foreground' });
|
|
1172
|
+
expect(mockBindTab).toHaveBeenCalledWith('test', { index: 1, windowMode: 'foreground' });
|
|
1123
1173
|
const out = lastJsonLog();
|
|
1124
1174
|
expect(out.session).toBe('test');
|
|
1125
1175
|
});
|
|
1176
|
+
it('passes background window mode when binding a Cloak tab', async () => {
|
|
1177
|
+
const program = createProgram('', '');
|
|
1178
|
+
await program.parseAsync(['node', 'webcmd', 'browser', '--session', 'test', '--window', 'background', 'bind', '--page', 'tab-2']);
|
|
1179
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser', windowMode: 'background' });
|
|
1180
|
+
expect(mockBindTab).toHaveBeenCalledWith('test', { page: 'tab-2', windowMode: 'background' });
|
|
1181
|
+
});
|
|
1126
1182
|
it('requires an explicit Cloak tab target for bind', async () => {
|
|
1127
1183
|
const program = createProgram('', '');
|
|
1128
1184
|
await program.parseAsync(['node', 'webcmd', 'browser', '--session', 'test', 'bind']);
|
|
@@ -1237,7 +1293,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1237
1293
|
it('unbinds a session through the daemon close-window command', async () => {
|
|
1238
1294
|
const program = createProgram('', '');
|
|
1239
1295
|
await program.parseAsync(['node', 'webcmd', 'browser', '--session', 'test', 'unbind']);
|
|
1240
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser' });
|
|
1296
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 45, session: 'test', surface: 'browser', windowMode: 'foreground' });
|
|
1241
1297
|
expect(mockSendCommand).toHaveBeenCalledWith('close-window', { session: 'test', surface: 'browser' });
|
|
1242
1298
|
const out = lastJsonLog();
|
|
1243
1299
|
expect(out).toEqual({ unbound: true, session: 'test' });
|
|
@@ -2493,6 +2549,26 @@ describe('browser click/type commands', () => {
|
|
|
2493
2549
|
expect(browserState.page.click).toHaveBeenCalledWith('#save', {});
|
|
2494
2550
|
expect(lastJsonLog()).toEqual({ clicked: true, target: '#save', matches_n: 1, match_level: 'exact' });
|
|
2495
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
|
+
});
|
|
2496
2572
|
it('clicks a unique semantic locator without a prior state call', async () => {
|
|
2497
2573
|
browserState.page.evaluate.mockResolvedValueOnce({
|
|
2498
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;
|