@vigilbase/cli 0.1.0 → 0.1.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 +30 -79
- package/dist/bin.js +0 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +36 -376
- package/dist/cli.js.map +1 -1
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.d.ts.map +1 -0
- package/dist/commands/account.js +171 -0
- package/dist/commands/account.js.map +1 -0
- package/dist/commands/auth.d.ts +5 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +185 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/billing.d.ts +3 -0
- package/dist/commands/billing.d.ts.map +1 -0
- package/dist/commands/billing.js +47 -0
- package/dist/commands/billing.js.map +1 -0
- package/dist/commands/cloudflare.d.ts +3 -0
- package/dist/commands/cloudflare.d.ts.map +1 -0
- package/dist/commands/cloudflare.js +53 -0
- package/dist/commands/cloudflare.js.map +1 -0
- package/dist/commands/helpers.d.ts +49 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +36 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +37 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/public-tools.d.ts +9 -0
- package/dist/commands/public-tools.d.ts.map +1 -0
- package/dist/commands/public-tools.js +171 -0
- package/dist/commands/public-tools.js.map +1 -0
- package/dist/commands/runs.d.ts +3 -0
- package/dist/commands/runs.d.ts.map +1 -0
- package/dist/commands/runs.js +69 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/watchdog.d.ts +3 -0
- package/dist/commands/watchdog.d.ts.map +1 -0
- package/dist/commands/watchdog.js +59 -0
- package/dist/commands/watchdog.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +2 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/format.d.ts +3 -0
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +4 -3
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/help.d.ts +1 -1
- package/dist/lib/help.d.ts.map +1 -1
- package/dist/lib/help.js +34 -9
- package/dist/lib/help.js.map +1 -1
- package/dist/lib/one-client.d.ts +42 -3
- package/dist/lib/one-client.d.ts.map +1 -1
- package/dist/lib/one-client.js +121 -28
- package/dist/lib/one-client.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# @vigilbase/cli
|
|
2
2
|
|
|
3
|
-
Terminal client for Vigilbase — public scanners
|
|
3
|
+
Terminal client for Vigilbase — public scanners and tools without login, plus Vigilbase One after device authorization.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Binary name: **`vigilbase`**
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
vigilbase help
|
|
9
9
|
vigilbase run security-headers https://vigilbase.com
|
|
10
|
+
vigilbase login
|
|
11
|
+
vigilbase account --json
|
|
10
12
|
```
|
|
11
13
|
|
|
12
14
|
## Install
|
|
@@ -27,25 +29,11 @@ npx @vigilbase/cli help
|
|
|
27
29
|
npx @vigilbase/cli run email-security vigilbase.com --json
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
pnpm install
|
|
34
|
-
pnpm link:vigilbase
|
|
35
|
-
# builds dist/ and symlinks `vigilbase` into PATH
|
|
36
|
-
|
|
37
|
-
vigilbase help
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Unlink later with `pnpm unlink:vigilbase`.
|
|
41
|
-
|
|
42
|
-
## Commands
|
|
43
|
-
|
|
44
|
-
### Public tools (no auth)
|
|
32
|
+
## Public tools (no auth)
|
|
45
33
|
|
|
46
34
|
| Command | Purpose |
|
|
47
35
|
| --- | --- |
|
|
48
|
-
| `vigilbase list` |
|
|
36
|
+
| `vigilbase list` | Public catalog + auth-required tool names |
|
|
49
37
|
| `vigilbase run <tool> <target>` | Execute a scan |
|
|
50
38
|
| `vigilbase headers <url>` | Shortcut for `security-headers` |
|
|
51
39
|
| `vigilbase email <domain>` | Shortcut for `email-security` |
|
|
@@ -53,17 +41,9 @@ Unlink later with `pnpm unlink:vigilbase`.
|
|
|
53
41
|
| `vigilbase badge <domain>` | Public badge URL |
|
|
54
42
|
| `vigilbase doctor` | Connectivity + auth status |
|
|
55
43
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
### Auth (Vigilbase One)
|
|
44
|
+
Public allowlist: `security-headers`, `email-security`.
|
|
59
45
|
|
|
60
|
-
|
|
61
|
-
| --- | --- |
|
|
62
|
-
| `vigilbase login` | Device-code sign-in (RFC 8628) at `one.vigilbase.com/device` |
|
|
63
|
-
| `vigilbase logout` | Clear `~/.config/vigilbase/credentials.json` |
|
|
64
|
-
| `vigilbase whoami` | Show local / live One session |
|
|
65
|
-
|
|
66
|
-
Sensitive tools unlocked after login:
|
|
46
|
+
## Auth-required tools (after login)
|
|
67
47
|
|
|
68
48
|
```bash
|
|
69
49
|
vigilbase login
|
|
@@ -72,14 +52,34 @@ vigilbase run react2shell https://app.example.com
|
|
|
72
52
|
vigilbase run fortibleed-check example.com
|
|
73
53
|
```
|
|
74
54
|
|
|
75
|
-
|
|
55
|
+
## Account & One management (login required)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
vigilbase account
|
|
59
|
+
vigilbase account services
|
|
60
|
+
vigilbase account org
|
|
61
|
+
vigilbase account endpoints
|
|
62
|
+
vigilbase account sessions
|
|
63
|
+
vigilbase account invites
|
|
64
|
+
vigilbase account invites create --email user@example.com --confirm
|
|
65
|
+
vigilbase billing summary
|
|
66
|
+
vigilbase billing portal
|
|
67
|
+
vigilbase runs list
|
|
68
|
+
vigilbase runs get <id>
|
|
69
|
+
vigilbase cf zones
|
|
70
|
+
vigilbase cf checkup status
|
|
71
|
+
vigilbase watchdog list
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Every command supports `-j` / `--json`. Async work shows a spinner on TTY (disabled for `--json`, CI, and non-TTY).
|
|
76
75
|
|
|
77
76
|
## Agent-friendly mode
|
|
78
77
|
|
|
79
78
|
```bash
|
|
80
79
|
vigilbase list --json
|
|
81
80
|
vigilbase run security-headers https://vigilbase.com --json
|
|
82
|
-
vigilbase
|
|
81
|
+
vigilbase account --json
|
|
82
|
+
vigilbase runs list --json
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
| Exit code | Meaning |
|
|
@@ -87,52 +87,3 @@ vigilbase email vigilbase.com --json
|
|
|
87
87
|
| `0` | Success |
|
|
88
88
|
| `1` | Error |
|
|
89
89
|
| `2` | Rate limited (`HTTP 429`) |
|
|
90
|
-
|
|
91
|
-
## Local development
|
|
92
|
-
|
|
93
|
-
For pointing the CLI at local servers (not shown in `vigilbase help`):
|
|
94
|
-
|
|
95
|
-
| Env / flag | Description |
|
|
96
|
-
| --- | --- |
|
|
97
|
-
| `VIGILBASE_BASE_URL` / `--base-url` | Public API (default `https://vigilbase.com`) |
|
|
98
|
-
| `VIGILBASE_ONE_URL` / `--one-url` | One API (default `https://one.vigilbase.com`) |
|
|
99
|
-
| `VIGILBASE_CLIENT_ID` / `--client-id` | OAuth client id for device auth |
|
|
100
|
-
| `VIGILBASE_CONFIG_DIR` | Override `~/.config/vigilbase` |
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
vigilbase doctor --base-url http://localhost:3001
|
|
104
|
-
vigilbase login --one-url http://localhost:3000
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Architecture notes
|
|
108
|
-
|
|
109
|
-
- **Public surface** (`VigilbaseClient`) talks only to vigilbase.com agent/tool endpoints — no credentials.
|
|
110
|
-
- **One surface** stores tokens from Better Auth `POST /api/auth/device/code` + `/device/token` under `~/.config/vigilbase/credentials.json`.
|
|
111
|
-
- Headless / CI: `vigilbase login --token "$VIGILBASE_ONE_TOKEN"` or `--no-open` and approve the printed URL elsewhere.
|
|
112
|
-
|
|
113
|
-
## Smoke test
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
VIGILBASE_BASE_URL=https://vigilbase.com pnpm --filter @vigilbase/cli smoke:e2e
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Publishing
|
|
120
|
-
|
|
121
|
-
See [PUBLISH.md](./PUBLISH.md) for npm org setup and `npm publish` steps.
|
|
122
|
-
|
|
123
|
-
## Library API
|
|
124
|
-
|
|
125
|
-
```ts
|
|
126
|
-
import { runCli, VigilbaseClient, createConfig } from '@vigilbase/cli'
|
|
127
|
-
|
|
128
|
-
await runCli({ argv: ['list', '--json'] })
|
|
129
|
-
|
|
130
|
-
const client = new VigilbaseClient(createConfig())
|
|
131
|
-
await client.listTools()
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Related
|
|
135
|
-
|
|
136
|
-
- OpenAPI: https://vigilbase.com/openapi.json
|
|
137
|
-
- Skills index: https://vigilbase.com/.well-known/agent-skills/index.json
|
|
138
|
-
- Device auth docs: https://better-auth.com/docs/plugins/device-authorization
|
package/dist/bin.js
CHANGED
|
File without changes
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createConfig } from './lib/config.js';
|
|
2
2
|
import { type ExitCode } from './types.js';
|
|
3
|
-
|
|
3
|
+
import { CLI_VERSION } from './version.js';
|
|
4
|
+
export { CLI_VERSION };
|
|
4
5
|
export type RunCliOptions = {
|
|
5
6
|
argv?: string[];
|
|
6
7
|
stdout?: NodeJS.WritableStream;
|
|
7
8
|
stderr?: NodeJS.WritableStream;
|
|
8
9
|
env?: NodeJS.ProcessEnv;
|
|
9
|
-
/** Injected for tests — skip real device-auth polling. */
|
|
10
10
|
loginHandler?: (config: ReturnType<typeof createConfig>) => Promise<ExitCode>;
|
|
11
11
|
};
|
|
12
12
|
export declare function runCli(options?: RunCliOptions): Promise<ExitCode>;
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAM9C,OAAO,EAA4B,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,WAAW,EAAE,CAAA;AAmFtB,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAA;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC9E,CAAA;AAED,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6G3E"}
|
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
-
import {
|
|
2
|
+
import { DeviceAuthError } from './auth/index.js';
|
|
3
3
|
import { VigilbaseApiError, VigilbaseClient } from './lib/client.js';
|
|
4
4
|
import { createConfig } from './lib/config.js';
|
|
5
|
-
import { isOneAuthRequiredTool, isOneExecutableTool, ONE_AUTH_REQUIRED_TOOLS, resolveToolDefinition, VigilbaseOneClient, } from './lib/one-client.js';
|
|
6
5
|
import { renderBanner, shouldShowBanner } from './lib/banner.js';
|
|
7
|
-
import { createWriter, formatError
|
|
6
|
+
import { createWriter, formatError } from './lib/format.js';
|
|
8
7
|
import { HELP_TAGLINE, HELP_TEXT, helpForCommand } from './lib/help.js';
|
|
9
|
-
import {
|
|
10
|
-
import { shouldShowProgress, startProgress, withProgress } from './lib/progress.js';
|
|
8
|
+
import { getCommandHandler } from './commands/index.js';
|
|
11
9
|
import { EXIT } from './types.js';
|
|
12
|
-
|
|
10
|
+
import { CLI_VERSION } from './version.js';
|
|
11
|
+
export { CLI_VERSION };
|
|
13
12
|
function parseCli(argv) {
|
|
14
13
|
const { values, positionals } = parseArgs({
|
|
15
14
|
args: argv,
|
|
@@ -29,9 +28,15 @@ function parseCli(argv) {
|
|
|
29
28
|
public: { type: 'boolean', default: false },
|
|
30
29
|
token: { type: 'string' },
|
|
31
30
|
'no-open': { type: 'boolean', default: false },
|
|
31
|
+
email: { type: 'string' },
|
|
32
|
+
role: { type: 'string' },
|
|
33
|
+
confirm: { type: 'boolean', default: false },
|
|
34
|
+
'account-id': { type: 'string' },
|
|
35
|
+
tool: { type: 'string' },
|
|
36
|
+
limit: { type: 'string' },
|
|
37
|
+
'target-id': { type: 'string' },
|
|
32
38
|
},
|
|
33
39
|
});
|
|
34
|
-
// Map parseArgs flag names onto is*/has* props (CLI flags stay --json/--help/…).
|
|
35
40
|
return {
|
|
36
41
|
positionals,
|
|
37
42
|
values: {
|
|
@@ -48,6 +53,15 @@ function parseCli(argv) {
|
|
|
48
53
|
isPublic: Boolean(values.public),
|
|
49
54
|
token: typeof values.token === 'string' ? values.token : undefined,
|
|
50
55
|
isNoOpen: Boolean(values['no-open']),
|
|
56
|
+
email: typeof values.email === 'string' ? values.email : undefined,
|
|
57
|
+
role: typeof values.role === 'string' ? values.role : undefined,
|
|
58
|
+
isConfirm: Boolean(values.confirm),
|
|
59
|
+
'account-id': typeof values['account-id'] === 'string'
|
|
60
|
+
? values['account-id']
|
|
61
|
+
: undefined,
|
|
62
|
+
tool: typeof values.tool === 'string' ? values.tool : undefined,
|
|
63
|
+
limit: typeof values.limit === 'string' ? values.limit : undefined,
|
|
64
|
+
'target-id': typeof values['target-id'] === 'string' ? values['target-id'] : undefined,
|
|
51
65
|
},
|
|
52
66
|
};
|
|
53
67
|
}
|
|
@@ -117,376 +131,22 @@ export async function runCli(options = {}) {
|
|
|
117
131
|
clientId: parsed.values['client-id'],
|
|
118
132
|
timeoutMs: parseTimeout(parsed.values.timeout),
|
|
119
133
|
});
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const catalog = await client.listTools();
|
|
125
|
-
const accessToken = await getOneAccessToken(config);
|
|
126
|
-
const authenticated = Boolean(accessToken);
|
|
127
|
-
if (mode === 'json') {
|
|
128
|
-
writer.stdout(JSON.stringify({
|
|
129
|
-
...catalog,
|
|
130
|
-
authenticated,
|
|
131
|
-
oneAuthRequiredTools: [...ONE_AUTH_REQUIRED_TOOLS],
|
|
132
|
-
oneExecutableTools: authenticated
|
|
133
|
-
? [
|
|
134
|
-
...new Set([
|
|
135
|
-
...catalog.tools.map((tool) => tool.toolName),
|
|
136
|
-
...ONE_AUTH_REQUIRED_TOOLS,
|
|
137
|
-
]),
|
|
138
|
-
]
|
|
139
|
-
: catalog.tools.map((tool) => tool.toolName),
|
|
140
|
-
}, null, 2));
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
formatToolsList(catalog, mode, writer, {
|
|
144
|
-
sensitiveTools: ONE_AUTH_REQUIRED_TOOLS,
|
|
145
|
-
isAuthenticated: authenticated,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return EXIT.OK;
|
|
149
|
-
}
|
|
150
|
-
case 'run':
|
|
151
|
-
case 'headers':
|
|
152
|
-
case 'email': {
|
|
153
|
-
let toolName;
|
|
154
|
-
let target;
|
|
155
|
-
if (command === 'headers') {
|
|
156
|
-
toolName = 'security-headers';
|
|
157
|
-
target = rest[0] ?? '';
|
|
158
|
-
}
|
|
159
|
-
else if (command === 'email') {
|
|
160
|
-
toolName = 'email-security';
|
|
161
|
-
target = rest[0] ?? '';
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
toolName = rest[0] ?? '';
|
|
165
|
-
target = rest[1] ?? '';
|
|
166
|
-
}
|
|
167
|
-
if (!toolName || !target) {
|
|
168
|
-
throw new Error(command === 'run'
|
|
169
|
-
? 'Usage: vigilbase run <tool> <target>'
|
|
170
|
-
: `Usage: vigilbase ${command} <target>`);
|
|
171
|
-
}
|
|
172
|
-
const via = parsed.values.via;
|
|
173
|
-
if (via && via !== 'one' && via !== 'public') {
|
|
174
|
-
throw new Error('--via must be one of: public, one');
|
|
175
|
-
}
|
|
176
|
-
const accessToken = await getOneAccessToken(config);
|
|
177
|
-
const forcePublic = Boolean(parsed.values.isPublic) || via === 'public';
|
|
178
|
-
const forceOne = via === 'one';
|
|
179
|
-
// Default routing:
|
|
180
|
-
// - sensitive tools → One (requires login)
|
|
181
|
-
// - public tools → public agent API unless --via one
|
|
182
|
-
const useOne = !forcePublic && (forceOne || isOneAuthRequiredTool(toolName));
|
|
183
|
-
if (useOne) {
|
|
184
|
-
if (!accessToken) {
|
|
185
|
-
throw new Error(`Tool "${toolName}" requires Vigilbase One auth. Run \`vigilbase login\` first.`);
|
|
186
|
-
}
|
|
187
|
-
if (!isOneExecutableTool(toolName)) {
|
|
188
|
-
throw new Error(`Tool "${toolName}" is not executable via Vigilbase One CLI yet.`);
|
|
189
|
-
}
|
|
190
|
-
const oneClient = new VigilbaseOneClient(config, accessToken);
|
|
191
|
-
const oneResult = await withProgress(`Running ${toolName} against ${target}`, mode, () => oneClient.executeTool(toolName, target));
|
|
192
|
-
formatExecuteResult({
|
|
193
|
-
success: Boolean(oneResult.success),
|
|
194
|
-
toolName,
|
|
195
|
-
target,
|
|
196
|
-
grade: oneResult.data?.grade ?? null,
|
|
197
|
-
gradeScore: oneResult.data?.gradeScore ?? null,
|
|
198
|
-
result: oneResult.data?.result ?? null,
|
|
199
|
-
error: oneResult.error ?? null,
|
|
200
|
-
uiUrl: `${config.oneBaseUrl}/dashboard/tools/${toolName}`,
|
|
201
|
-
}, mode, writer);
|
|
202
|
-
return oneResult.success ? EXIT.OK : EXIT.ERROR;
|
|
203
|
-
}
|
|
204
|
-
const result = await withProgress(`Running ${toolName} against ${target}`, mode, () => client.executeTool(toolName, target));
|
|
205
|
-
formatExecuteResult(result, mode, writer);
|
|
206
|
-
return result.success ? EXIT.OK : EXIT.ERROR;
|
|
207
|
-
}
|
|
208
|
-
case 'describe': {
|
|
209
|
-
const toolName = rest[0];
|
|
210
|
-
if (!toolName)
|
|
211
|
-
throw new Error('Usage: vigilbase describe <tool>');
|
|
212
|
-
const catalog = await client.listTools();
|
|
213
|
-
const tool = resolveToolDefinition(toolName, catalog.tools, config.oneBaseUrl);
|
|
214
|
-
if (!tool) {
|
|
215
|
-
throw new Error(`Unknown tool "${toolName}". Run \`vigilbase list\` for the allowlist.`);
|
|
216
|
-
}
|
|
217
|
-
let skillMarkdown;
|
|
218
|
-
if (!parsed.values.isNoSkill) {
|
|
219
|
-
try {
|
|
220
|
-
skillMarkdown = await client.fetchSkillMarkdown(tool.skillUrl);
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
// Sensitive tools may not publish public skill docs yet.
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
formatToolDescription(tool, mode, writer, skillMarkdown);
|
|
227
|
-
return EXIT.OK;
|
|
228
|
-
}
|
|
229
|
-
case 'skill': {
|
|
230
|
-
const toolName = rest[0];
|
|
231
|
-
if (!toolName)
|
|
232
|
-
throw new Error('Usage: vigilbase skill <tool>');
|
|
233
|
-
const catalog = await client.listTools();
|
|
234
|
-
const definition = resolveToolDefinition(toolName, catalog.tools, config.oneBaseUrl);
|
|
235
|
-
if (!definition) {
|
|
236
|
-
throw new Error(`Unknown tool "${toolName}". Run \`vigilbase list\` for the allowlist.`);
|
|
237
|
-
}
|
|
238
|
-
const skillMarkdown = await client.fetchSkillMarkdown(definition.skillUrl);
|
|
239
|
-
if (mode === 'json') {
|
|
240
|
-
writer.stdout(JSON.stringify({
|
|
241
|
-
toolName: definition.toolName,
|
|
242
|
-
skillUrl: definition.skillUrl,
|
|
243
|
-
content: skillMarkdown,
|
|
244
|
-
}, null, 2));
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
writer.stdout(skillMarkdown.trimEnd());
|
|
248
|
-
}
|
|
249
|
-
return EXIT.OK;
|
|
250
|
-
}
|
|
251
|
-
case 'badge': {
|
|
252
|
-
const domain = rest[0];
|
|
253
|
-
if (!domain)
|
|
254
|
-
throw new Error('Usage: vigilbase badge <domain>');
|
|
255
|
-
const styleRaw = parsed.values.style ?? 'default';
|
|
256
|
-
if (styleRaw !== 'default' && styleRaw !== 'compact' && styleRaw !== 'mini') {
|
|
257
|
-
throw new Error('--style must be one of: default, compact, mini');
|
|
258
|
-
}
|
|
259
|
-
const url = client.badgeUrl(domain, styleRaw);
|
|
260
|
-
if (mode === 'json') {
|
|
261
|
-
writer.stdout(JSON.stringify({ domain, style: styleRaw, url }, null, 2));
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
writer.stdout(url);
|
|
265
|
-
}
|
|
266
|
-
return EXIT.OK;
|
|
267
|
-
}
|
|
268
|
-
case 'doctor': {
|
|
269
|
-
try {
|
|
270
|
-
const catalog = await client.listTools();
|
|
271
|
-
const accessToken = await getOneAccessToken(config);
|
|
272
|
-
const payload = {
|
|
273
|
-
ok: true,
|
|
274
|
-
publicBaseUrl: config.publicBaseUrl,
|
|
275
|
-
oneBaseUrl: config.oneBaseUrl,
|
|
276
|
-
clientId: config.clientId,
|
|
277
|
-
authenticated: Boolean(accessToken),
|
|
278
|
-
toolCount: catalog.tools.length,
|
|
279
|
-
tools: catalog.tools.map((tool) => tool.toolName),
|
|
280
|
-
openapi: catalog.openapi ?? null,
|
|
281
|
-
skillsIndex: catalog.skillsIndex ?? null,
|
|
282
|
-
rateLimit: catalog.rateLimit ?? null,
|
|
283
|
-
};
|
|
284
|
-
if (mode === 'json') {
|
|
285
|
-
writer.stdout(JSON.stringify(payload, null, 2));
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
writer.stdout(`ok public=${config.publicBaseUrl} one=${config.oneBaseUrl} auth=${payload.authenticated ? 'in' : 'out'} tools=${payload.tools.join(',')}`);
|
|
289
|
-
}
|
|
290
|
-
return EXIT.OK;
|
|
291
|
-
}
|
|
292
|
-
catch (error) {
|
|
293
|
-
formatError(error, mode, writer);
|
|
294
|
-
return error instanceof VigilbaseApiError && error.status === 429
|
|
295
|
-
? EXIT.RATE_LIMITED
|
|
296
|
-
: EXIT.ERROR;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
case 'login': {
|
|
300
|
-
if (options.loginHandler) {
|
|
301
|
-
return options.loginHandler(config);
|
|
302
|
-
}
|
|
303
|
-
const explicitToken = parsed.values.token?.trim() ||
|
|
304
|
-
process.env.VIGILBASE_ONE_TOKEN?.trim() ||
|
|
305
|
-
'';
|
|
306
|
-
// Headless / CI: accept a bearer session token directly.
|
|
307
|
-
if (explicitToken) {
|
|
308
|
-
const oneClient = new VigilbaseOneClient(config, explicitToken);
|
|
309
|
-
let user;
|
|
310
|
-
try {
|
|
311
|
-
const session = await oneClient.getSession();
|
|
312
|
-
if (!session.user) {
|
|
313
|
-
throw new Error('Token did not resolve to a Vigilbase One session.');
|
|
314
|
-
}
|
|
315
|
-
user = {
|
|
316
|
-
id: session.user.id,
|
|
317
|
-
email: session.user.email ?? undefined,
|
|
318
|
-
name: session.user.name ?? undefined,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
catch (error) {
|
|
322
|
-
throw new Error(error instanceof Error
|
|
323
|
-
? `Invalid --token / VIGILBASE_ONE_TOKEN: ${error.message}`
|
|
324
|
-
: 'Invalid --token / VIGILBASE_ONE_TOKEN');
|
|
325
|
-
}
|
|
326
|
-
await writeOneCredentials(config, {
|
|
327
|
-
accessToken: explicitToken,
|
|
328
|
-
tokenType: 'Bearer',
|
|
329
|
-
user,
|
|
330
|
-
obtainedAt: new Date().toISOString(),
|
|
331
|
-
});
|
|
332
|
-
if (mode === 'json') {
|
|
333
|
-
writer.stdout(JSON.stringify({
|
|
334
|
-
success: true,
|
|
335
|
-
mode: 'token',
|
|
336
|
-
oneBaseUrl: config.oneBaseUrl,
|
|
337
|
-
user: user ?? null,
|
|
338
|
-
sensitiveTools: [...ONE_AUTH_REQUIRED_TOOLS],
|
|
339
|
-
}, null, 2));
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
writer.stdout(`signed in${user?.email ? ` ${user.email}` : ''} (token)`);
|
|
343
|
-
}
|
|
344
|
-
return EXIT.OK;
|
|
345
|
-
}
|
|
346
|
-
const preferNoOpen = Boolean(parsed.values.isNoOpen) || isHeadlessEnvironment(process.env);
|
|
347
|
-
writer.stderr(`login ${config.oneBaseUrl}`);
|
|
348
|
-
const device = await requestDeviceCode(config);
|
|
349
|
-
const verifyUrl = resolveDeviceVerificationUrl(config.oneBaseUrl, device);
|
|
350
|
-
const prettyCode = formatUserCode(device.user_code);
|
|
351
|
-
writer.stderr(`code ${prettyCode}`);
|
|
352
|
-
writer.stderr(`url ${verifyUrl}`);
|
|
353
|
-
if (preferNoOpen) {
|
|
354
|
-
writer.stderr('headless approve the URL on any signed-in browser, or re-run with --token');
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
const opened = openUrlInBrowser(verifyUrl);
|
|
358
|
-
if (opened.isOpened) {
|
|
359
|
-
writer.stderr('browser opened — approve the device code');
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
writer.stderr(`browser not opened (${opened.reason ?? 'failed'}) — paste the URL above`);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
const showSpinner = shouldShowProgress(mode);
|
|
366
|
-
const progress = startProgress('waiting for approval', {
|
|
367
|
-
isEnabled: showSpinner,
|
|
368
|
-
expectedMs: Math.min(Math.max(device.expires_in, 30) * 1000, 180_000),
|
|
369
|
-
});
|
|
370
|
-
if (!showSpinner && mode !== 'json') {
|
|
371
|
-
writer.stderr('waiting for approval…');
|
|
372
|
-
}
|
|
373
|
-
let token;
|
|
374
|
-
try {
|
|
375
|
-
token = await waitForDeviceAuthorization(config, device);
|
|
376
|
-
progress.stop('✓ approved');
|
|
377
|
-
}
|
|
378
|
-
catch (error) {
|
|
379
|
-
progress.stop('✗ login failed');
|
|
380
|
-
throw error;
|
|
381
|
-
}
|
|
382
|
-
const expiresAt = typeof token.expires_in === 'number'
|
|
383
|
-
? new Date(Date.now() + token.expires_in * 1000).toISOString()
|
|
384
|
-
: undefined;
|
|
385
|
-
let user;
|
|
386
|
-
try {
|
|
387
|
-
const oneClient = new VigilbaseOneClient(config, token.access_token);
|
|
388
|
-
const session = await oneClient.getSession();
|
|
389
|
-
if (session.user) {
|
|
390
|
-
user = {
|
|
391
|
-
id: session.user.id,
|
|
392
|
-
email: session.user.email ?? undefined,
|
|
393
|
-
name: session.user.name ?? undefined,
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
// Session enrichment is best-effort; token storage still succeeds.
|
|
399
|
-
}
|
|
400
|
-
await writeOneCredentials(config, {
|
|
401
|
-
accessToken: token.access_token,
|
|
402
|
-
refreshToken: token.refresh_token,
|
|
403
|
-
tokenType: token.token_type,
|
|
404
|
-
scope: token.scope,
|
|
405
|
-
expiresAt,
|
|
406
|
-
user,
|
|
407
|
-
obtainedAt: new Date().toISOString(),
|
|
408
|
-
});
|
|
409
|
-
if (mode === 'json') {
|
|
410
|
-
writer.stdout(JSON.stringify({
|
|
411
|
-
success: true,
|
|
412
|
-
mode: 'device',
|
|
413
|
-
oneBaseUrl: config.oneBaseUrl,
|
|
414
|
-
expiresAt: expiresAt ?? null,
|
|
415
|
-
scope: token.scope ?? null,
|
|
416
|
-
user: user ?? null,
|
|
417
|
-
sensitiveTools: [...ONE_AUTH_REQUIRED_TOOLS],
|
|
418
|
-
}, null, 2));
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
writer.stdout(`signed in${user?.email ? ` ${user.email}` : ''}`);
|
|
422
|
-
}
|
|
423
|
-
return EXIT.OK;
|
|
424
|
-
}
|
|
425
|
-
case 'logout': {
|
|
426
|
-
const cleared = await clearOneCredentials(config);
|
|
427
|
-
if (mode === 'json') {
|
|
428
|
-
writer.stdout(JSON.stringify({ success: true, cleared }, null, 2));
|
|
429
|
-
}
|
|
430
|
-
else if (cleared) {
|
|
431
|
-
writer.stdout('Signed out. Local One credentials removed.');
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
writer.stdout('Already signed out.');
|
|
435
|
-
}
|
|
436
|
-
return EXIT.OK;
|
|
437
|
-
}
|
|
438
|
-
case 'whoami': {
|
|
439
|
-
const credentials = await readCredentials(config);
|
|
440
|
-
// Honor expiry the same way list/run do (getOneAccessToken).
|
|
441
|
-
const accessToken = await getOneAccessToken(config);
|
|
442
|
-
let liveUser = accessToken ? (credentials.one?.user ?? null) : null;
|
|
443
|
-
if (accessToken) {
|
|
444
|
-
try {
|
|
445
|
-
const session = await new VigilbaseOneClient(config, accessToken).getSession();
|
|
446
|
-
if (session.user) {
|
|
447
|
-
liveUser = {
|
|
448
|
-
id: session.user.id,
|
|
449
|
-
email: session.user.email ?? undefined,
|
|
450
|
-
name: session.user.name ?? undefined,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
catch {
|
|
455
|
-
// Keep stored user if live session probe fails.
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
const signedIn = Boolean(accessToken);
|
|
459
|
-
const payload = {
|
|
460
|
-
authenticated: signedIn,
|
|
461
|
-
oneBaseUrl: config.oneBaseUrl,
|
|
462
|
-
user: liveUser,
|
|
463
|
-
expiresAt: credentials.one?.expiresAt ?? null,
|
|
464
|
-
scope: credentials.one?.scope ?? null,
|
|
465
|
-
obtainedAt: credentials.one?.obtainedAt ?? null,
|
|
466
|
-
sensitiveTools: signedIn ? [...ONE_AUTH_REQUIRED_TOOLS] : [],
|
|
467
|
-
};
|
|
468
|
-
if (mode === 'json') {
|
|
469
|
-
writer.stdout(JSON.stringify(payload, null, 2));
|
|
470
|
-
}
|
|
471
|
-
else if (!signedIn) {
|
|
472
|
-
writer.stdout('Not signed in. Public tools still work.');
|
|
473
|
-
writer.stdout('Run `vigilbase login` to unlock website-security, react2shell, and fortibleed-check.');
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
writer.stdout(`Signed in to ${config.oneBaseUrl}`);
|
|
477
|
-
if (liveUser?.email) {
|
|
478
|
-
writer.stdout(`User: ${liveUser.email}`);
|
|
479
|
-
}
|
|
480
|
-
if (credentials.one?.expiresAt) {
|
|
481
|
-
writer.stdout(`Expires: ${credentials.one.expiresAt}`);
|
|
482
|
-
}
|
|
483
|
-
writer.stdout(`Sensitive tools: ${ONE_AUTH_REQUIRED_TOOLS.join(', ')}`);
|
|
484
|
-
}
|
|
485
|
-
return EXIT.OK;
|
|
486
|
-
}
|
|
487
|
-
default:
|
|
488
|
-
throw new Error(`Unknown command "${command}". Run \`vigilbase help\` for usage.`);
|
|
134
|
+
const publicClient = new VigilbaseClient(config);
|
|
135
|
+
const handler = getCommandHandler(command);
|
|
136
|
+
if (!handler) {
|
|
137
|
+
throw new Error(`Unknown command "${command}". Run \`vigilbase help\` for usage.`);
|
|
489
138
|
}
|
|
139
|
+
return await handler({
|
|
140
|
+
command,
|
|
141
|
+
rest,
|
|
142
|
+
values: parsed.values,
|
|
143
|
+
mode,
|
|
144
|
+
config,
|
|
145
|
+
writer,
|
|
146
|
+
publicClient,
|
|
147
|
+
env: process.env,
|
|
148
|
+
loginHandler: options.loginHandler,
|
|
149
|
+
});
|
|
490
150
|
}
|
|
491
151
|
catch (error) {
|
|
492
152
|
const mode = argv.includes('--json') || argv.includes('-j') ? 'json' : 'text';
|