Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d51ca38cee |
@@ -83,7 +83,7 @@ Use OpenCLI directly when you want a reliable command instead of a live browser
|
||||
|
||||
- `opencli list` shows every registered command.
|
||||
- `opencli <site> <command>` runs a built-in or generated adapter.
|
||||
- `opencli register mycli` exposes a local CLI through the same discovery surface.
|
||||
- `opencli external register mycli` exposes a local CLI through the same discovery surface.
|
||||
- `opencli doctor` helps diagnose browser connectivity.
|
||||
|
||||
## For AI Agents
|
||||
@@ -282,7 +282,7 @@ OpenCLI acts as a universal hub for your existing command-line tools — unified
|
||||
**Register your own** — add any local CLI so AI agents can discover it via `opencli list`:
|
||||
|
||||
```bash
|
||||
opencli register mycli
|
||||
opencli external register mycli
|
||||
```
|
||||
|
||||
### Desktop App Adapters
|
||||
|
||||
@@ -13,7 +13,7 @@ OpenCLI turns any website, Electron desktop app, or external CLI into a uniform
|
||||
- **Adapter commands** — `opencli <site> <command> [...]`. Built-in adapters live in `clis/`, user adapters in `~/.opencli/clis/`. Each is backed by a strategy (`PUBLIC | COOKIE | HEADER | INTERCEPT | UI | LOCAL`) that tells you whether a Chrome session is needed.
|
||||
- **Browser driving** — `opencli browser *` subcommands (`open`, `state`, `click`, `type`, `select`, `find`, `extract`, `network`, …) for ad-hoc interaction and scraping when no adapter covers the task. See `opencli-browser`.
|
||||
- **Current-tab binding** — `opencli browser bind --domain <host>` attaches a `bound:*` workspace to the Chrome tab the user already opened/logged into. Follow-up commands use `opencli browser --workspace bound:default ...`. See `opencli-browser` before using it; bound workspaces have stricter navigation/tab-mutation safety rules.
|
||||
- **External CLI passthrough** — `opencli gh`, `opencli docker`, `opencli vercel`, etc. Registered via `opencli install <name>` (auto-install from `external-clis.yaml`) or `opencli register <name>` (bring your own).
|
||||
- **External CLI passthrough** — `opencli gh`, `opencli docker`, `opencli vercel`, etc. Managed via `opencli external install <name>` (auto-install from `external-clis.yaml`) or `opencli external register <name>` (bring your own).
|
||||
|
||||
## Install
|
||||
|
||||
@@ -126,11 +126,12 @@ opencli plugin create <name> # scaffold a new plugin
|
||||
Wraps external command-line tools so you can discover + invoke them through the same `opencli …` entrypoint:
|
||||
|
||||
```bash
|
||||
opencli install gh # auto-install via brew/apt/npm per external-clis.yaml
|
||||
opencli register my-tool \
|
||||
opencli external install gh # auto-install via brew/apt/npm per external-clis.yaml
|
||||
opencli external register my-tool \
|
||||
--binary my-tool \
|
||||
--install "npm i -g my-tool" \
|
||||
--desc "My internal CLI"
|
||||
opencli external list
|
||||
opencli gh pr list --limit 5 # passthrough; stdio is inherited, exit code propagated
|
||||
opencli docker ps
|
||||
```
|
||||
|
||||
+30
-6
@@ -427,11 +427,10 @@ export function createProgram(BUILTIN_CLIS: string, USER_CLIS: string): Command
|
||||
.command('list')
|
||||
.description('List all available CLI commands')
|
||||
.option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table')
|
||||
.option('--json', 'JSON output (deprecated)')
|
||||
.action((opts) => {
|
||||
const registry = getRegistry();
|
||||
const commands = [...new Set(registry.values())].sort((a, b) => fullName(a).localeCompare(fullName(b)));
|
||||
const fmt = opts.json && opts.format === 'table' ? 'json' : opts.format;
|
||||
const fmt = opts.format;
|
||||
const isStructured = fmt === 'json' || fmt === 'yaml';
|
||||
|
||||
if (fmt !== 'table') {
|
||||
@@ -2301,7 +2300,11 @@ cli({
|
||||
|
||||
const externalClis = loadExternalClis();
|
||||
|
||||
program
|
||||
const externalCmd = program
|
||||
.command('external')
|
||||
.description('Manage external CLI passthrough commands');
|
||||
|
||||
externalCmd
|
||||
.command('install')
|
||||
.description('Install an external CLI')
|
||||
.argument('<name>', 'Name of the external CLI')
|
||||
@@ -2315,7 +2318,7 @@ cli({
|
||||
installExternalCli(ext);
|
||||
});
|
||||
|
||||
program
|
||||
externalCmd
|
||||
.command('register')
|
||||
.description('Register an external CLI')
|
||||
.argument('<name>', 'Name of the CLI')
|
||||
@@ -2326,6 +2329,27 @@ cli({
|
||||
registerExternalCli(name, { binary: opts.binary, install: opts.install, description: opts.desc });
|
||||
});
|
||||
|
||||
externalCmd
|
||||
.command('list')
|
||||
.description('List registered external CLIs')
|
||||
.option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table')
|
||||
.action((opts) => {
|
||||
const rows = loadExternalClis().map((ext) => ({
|
||||
name: ext.name,
|
||||
binary: ext.binary,
|
||||
installed: isBinaryInstalled(ext.binary),
|
||||
description: ext.description ?? '',
|
||||
homepage: ext.homepage ?? '',
|
||||
tags: ext.tags?.join(', ') ?? '',
|
||||
}));
|
||||
renderOutput(rows, {
|
||||
fmt: opts.format,
|
||||
columns: ['name', 'binary', 'installed', 'description', 'homepage', 'tags'],
|
||||
title: 'opencli/external/list',
|
||||
source: 'opencli external list',
|
||||
});
|
||||
});
|
||||
|
||||
function passthroughExternal(name: string, parsedArgs?: string[]) {
|
||||
const args = parsedArgs ?? (() => {
|
||||
const idx = process.argv.indexOf(name);
|
||||
@@ -2376,13 +2400,13 @@ cli({
|
||||
|
||||
// ── Unknown command fallback ──────────────────────────────────────────────
|
||||
// Security: do NOT auto-discover and register arbitrary system binaries.
|
||||
// Only explicitly registered external CLIs (via `opencli register`) are allowed.
|
||||
// Only explicitly registered external CLIs are allowed.
|
||||
|
||||
program.on('command:*', (operands: string[]) => {
|
||||
const binary = operands[0];
|
||||
console.error(styleText('red', `error: unknown command '${binary}'`));
|
||||
if (isBinaryInstalled(binary)) {
|
||||
console.error(styleText('dim', ` Tip: '${binary}' exists on your PATH. Use 'opencli register ${binary}' to add it as an external CLI.`));
|
||||
console.error(styleText('dim', ` Tip: '${binary}' exists on your PATH. Use 'opencli external register ${binary}' to add it as an external CLI.`));
|
||||
}
|
||||
program.outputHelp();
|
||||
process.exitCode = EXIT_CODES.USAGE_ERROR;
|
||||
|
||||
@@ -16,8 +16,7 @@ export const BUILTIN_COMMANDS = [
|
||||
'tab',
|
||||
'doctor',
|
||||
'plugin',
|
||||
'install',
|
||||
'register',
|
||||
'external',
|
||||
'completion',
|
||||
];
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ describe('getCompletions', () => {
|
||||
const completions = getCompletions([], 1);
|
||||
|
||||
expect(completions).toContain('plugin');
|
||||
expect(completions).toContain('install');
|
||||
expect(completions).toContain('register');
|
||||
expect(completions).toContain('external');
|
||||
expect(completions).not.toContain('install');
|
||||
expect(completions).not.toContain('register');
|
||||
expect(completions).not.toContain('setup');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user