@eightstate/escli 0.8.0 → 0.12.0
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.
|
@@ -4,8 +4,7 @@ import { ErrorCode } from '@eightstate/contracts/errors';
|
|
|
4
4
|
import { BaseCommand } from '../../base-command.js';
|
|
5
5
|
import { writeStderr } from '../../io/io.js';
|
|
6
6
|
import { resolveCliToken } from '../../services/credentials.js';
|
|
7
|
-
import { authRequired,
|
|
8
|
-
import { CloudflareClient } from '../../services/mcp/cloudflare.js';
|
|
7
|
+
import { authRequired, maskSecret, usageError } from '../../services/mcp/common.js';
|
|
9
8
|
import { validateRootDirectory, writeMachineConfig } from '../../services/mcp/config.js';
|
|
10
9
|
import { McpGateClient } from '../../services/mcp/gate-client.js';
|
|
11
10
|
export const McpRegisterDataSchema = z.object({
|
|
@@ -24,7 +23,7 @@ export const McpRegisterDataSchema = z.object({
|
|
|
24
23
|
export default class McpRegister extends BaseCommand {
|
|
25
24
|
static errors = [ErrorCode.AuthRequired, ErrorCode.UsageInvalid, ErrorCode.ConfigInvalid, ErrorCode.ApiError, ErrorCode.GateUnavailable, ErrorCode.GateInvalidResponse, ErrorCode.NetworkError];
|
|
26
25
|
static summary = 'Register a local directory as an MCP machine';
|
|
27
|
-
static examples = ['<%= config.bin %> mcp register --root .
|
|
26
|
+
static examples = ['<%= config.bin %> mcp register --root .', '<%= config.bin %> mcp register --root . --slug my-repo --label "My Repo"'];
|
|
28
27
|
static enableJsonFlag = true;
|
|
29
28
|
static strict = true;
|
|
30
29
|
static flags = {
|
|
@@ -32,9 +31,6 @@ export default class McpRegister extends BaseCommand {
|
|
|
32
31
|
slug: Flags.string({ description: 'Machine slug. Defaults from --label, root basename, or hostname.' }),
|
|
33
32
|
port: Flags.integer({ description: 'Local MCP origin port.', default: 9315 }),
|
|
34
33
|
label: Flags.string({ description: 'Human label for this machine.' }),
|
|
35
|
-
'cf-account-id': Flags.string({ description: 'Cloudflare account ID. Defaults to CLOUDFLARE_ACCOUNT_ID.' }),
|
|
36
|
-
'cf-zone-id': Flags.string({ description: 'Cloudflare zone ID. Defaults to CLOUDFLARE_ZONE_ID.' }),
|
|
37
|
-
'cf-api-token': Flags.string({ description: 'Cloudflare API token. Defaults to CLOUDFLARE_API_TOKEN; never persisted.' }),
|
|
38
34
|
};
|
|
39
35
|
async execute() {
|
|
40
36
|
const flags = await this.parseFlags(McpRegister);
|
|
@@ -45,41 +41,25 @@ export default class McpRegister extends BaseCommand {
|
|
|
45
41
|
if (port < 1 || port > 65535)
|
|
46
42
|
throw usageError('--port must be between 1 and 65535');
|
|
47
43
|
const slug = normalizeSlug(flags.slug ?? flags.label ?? root.split('/').filter(Boolean).pop() ?? 'machine');
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
dns_record_id: dns.id,
|
|
68
|
-
port,
|
|
69
|
-
machine_id: gate.machine_id,
|
|
70
|
-
machine_secret: gate.machine_secret,
|
|
71
|
-
created_at: new Date().toISOString(),
|
|
72
|
-
};
|
|
73
|
-
const configPath = await writeMachineConfig(config);
|
|
74
|
-
if (!flags.quiet && !flags.json)
|
|
75
|
-
await writeStderr(`connector URL: ${gate.resource}/mcp\nconfig: ${configPath}\ntunnel: ${maskSecret(tunnel.id)}\n`);
|
|
76
|
-
return { slug, label: flags.label, root_default: root, hostname: gate.hostname, resource: gate.resource, port, machine_id: gate.machine_id, tunnel_id: tunnel.id, dns_record_id: dns.id, config_path: configPath, connector_url: `${gate.resource}/mcp` };
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
const cleanup = [`Cloudflare tunnel id: ${tunnelId ?? '(not created)'}`, `Cloudflare DNS record id: ${dnsRecordId ?? '(not created)'}`].join('\n');
|
|
80
|
-
await writeStderr(`MCP register failed. Cleanup references:\n${cleanup}\n`);
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
44
|
+
const gate = await new McpGateClient().createMachine({ slug, port, label: flags.label });
|
|
45
|
+
const config = {
|
|
46
|
+
slug,
|
|
47
|
+
label: flags.label,
|
|
48
|
+
root_default: root,
|
|
49
|
+
hostname: gate.hostname,
|
|
50
|
+
resource: gate.resource,
|
|
51
|
+
tunnel_id: gate.tunnel_id,
|
|
52
|
+
tunnel_token: gate.tunnel_token,
|
|
53
|
+
dns_record_id: gate.dns_record_id,
|
|
54
|
+
port,
|
|
55
|
+
machine_id: gate.machine_id,
|
|
56
|
+
machine_secret: gate.machine_secret,
|
|
57
|
+
created_at: new Date().toISOString(),
|
|
58
|
+
};
|
|
59
|
+
const configPath = await writeMachineConfig(config);
|
|
60
|
+
if (!flags.quiet && !flags.json)
|
|
61
|
+
await writeStderr(`registered: ${gate.hostname}\nconnector URL: ${gate.resource}/mcp\nconfig: ${configPath}\ntunnel: ${maskSecret(gate.tunnel_id)}\n`);
|
|
62
|
+
return { slug, label: flags.label, root_default: root, hostname: gate.hostname, resource: gate.resource, port, machine_id: gate.machine_id, tunnel_id: gate.tunnel_id, dns_record_id: gate.dns_record_id, config_path: configPath, connector_url: `${gate.resource}/mcp` };
|
|
83
63
|
}
|
|
84
64
|
}
|
|
85
65
|
function normalizeSlug(value) {
|
|
@@ -2,35 +2,30 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ErrorCode } from '@eightstate/contracts/errors';
|
|
4
4
|
import { BaseCommand } from '../../base-command.js';
|
|
5
|
-
import { CloudflareClient } from '../../services/mcp/cloudflare.js';
|
|
6
5
|
import { deleteMachineConfig, resolveMachineConfig } from '../../services/mcp/config.js';
|
|
7
6
|
import { McpGateClient } from '../../services/mcp/gate-client.js';
|
|
8
7
|
export const McpRevokeDataSchema = z.object({ slug: z.string(), gate_revoked: z.boolean(), cloudflare_deleted: z.boolean(), local_config_deleted: z.boolean() });
|
|
9
8
|
export default class McpRevoke extends BaseCommand {
|
|
10
9
|
static errors = [ErrorCode.ConfigInvalid, ErrorCode.AuthRequired, ErrorCode.GateUnavailable, ErrorCode.ApiError];
|
|
11
|
-
static summary = 'Revoke an MCP machine and
|
|
12
|
-
static examples = ['<%= config.bin %> mcp revoke --slug my-repo', '<%= config.bin %> mcp revoke --slug my-repo --
|
|
10
|
+
static summary = 'Revoke an MCP machine and delete its Cloudflare tunnel';
|
|
11
|
+
static examples = ['<%= config.bin %> mcp revoke --slug my-repo', '<%= config.bin %> mcp revoke --slug my-repo --yes'];
|
|
13
12
|
static enableJsonFlag = true;
|
|
14
13
|
static strict = true;
|
|
15
14
|
static flags = {
|
|
16
15
|
slug: Flags.string({ description: 'Machine slug. Optional only when one config exists.' }),
|
|
17
16
|
cloudflare: Flags.boolean({ description: 'Also delete Cloudflare DNS record and tunnel.', default: false }),
|
|
18
17
|
yes: Flags.boolean({ char: 'y', description: 'Skip confirmation for --cloudflare.', default: false }),
|
|
19
|
-
'cf-account-id': Flags.string({ description: 'Cloudflare account ID. Defaults to CLOUDFLARE_ACCOUNT_ID.' }),
|
|
20
|
-
'cf-zone-id': Flags.string({ description: 'Cloudflare zone ID. Defaults to CLOUDFLARE_ZONE_ID.' }),
|
|
21
|
-
'cf-api-token': Flags.string({ description: 'Cloudflare API token. Defaults to CLOUDFLARE_API_TOKEN; never persisted.' }),
|
|
22
18
|
};
|
|
23
19
|
async execute() {
|
|
24
20
|
const flags = await this.parseFlags(McpRevoke);
|
|
25
21
|
const config = await resolveMachineConfig(flags.slug);
|
|
26
|
-
|
|
22
|
+
const gate = new McpGateClient();
|
|
23
|
+
await gate.revokeMachine(config.slug);
|
|
27
24
|
let cloudflareDeleted = false;
|
|
28
25
|
if (flags.cloudflare) {
|
|
29
26
|
if (!flags.yes)
|
|
30
|
-
throw new Error('refusing to delete Cloudflare resources without --yes');
|
|
31
|
-
|
|
32
|
-
await cf.deleteDnsRecord(config.dns_record_id);
|
|
33
|
-
await cf.deleteTunnel(config.tunnel_id);
|
|
27
|
+
throw new Error('refusing to delete Cloudflare resources without --yes; Gate will deprovision the tunnel and DNS');
|
|
28
|
+
await gate.deprovisionMachine(config.slug);
|
|
34
29
|
cloudflareDeleted = true;
|
|
35
30
|
}
|
|
36
31
|
const localConfigDeleted = await deleteMachineConfig(config.slug);
|
|
@@ -3,6 +3,8 @@ import { ExitCodes } from '@eightstate/contracts/exit-codes';
|
|
|
3
3
|
import { EscliError } from '../../lib/escli-error.js';
|
|
4
4
|
import { parseJsonResponse, recordValue, stringValue, usageError } from './common.js';
|
|
5
5
|
const CF_API_BASE = 'https://api.cloudflare.com/client/v4';
|
|
6
|
+
const EIGHTSTATE_ACCOUNT_ID = '3db5cf3eac3990b5604382b809bb46c6';
|
|
7
|
+
const EIGHTSTATE_ZONE_ID = 'db5d43c916a7db789e1692d1ffdcf12a';
|
|
6
8
|
export class CloudflareClient {
|
|
7
9
|
credentials;
|
|
8
10
|
constructor(input) {
|
|
@@ -73,16 +75,11 @@ export class CloudflareClient {
|
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
export function resolveCloudflareCredentials(input) {
|
|
76
|
-
const accountId = input.accountId ?? process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
77
|
-
const zoneId = input.zoneId ?? process.env.CLOUDFLARE_ZONE_ID;
|
|
78
|
+
const accountId = input.accountId ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? EIGHTSTATE_ACCOUNT_ID;
|
|
79
|
+
const zoneId = input.zoneId ?? process.env.CLOUDFLARE_ZONE_ID ?? EIGHTSTATE_ZONE_ID;
|
|
78
80
|
const apiToken = input.apiToken ?? process.env.CLOUDFLARE_API_TOKEN;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
zoneId ? undefined : 'CLOUDFLARE_ZONE_ID',
|
|
82
|
-
apiToken ? undefined : 'CLOUDFLARE_API_TOKEN',
|
|
83
|
-
].filter(Boolean);
|
|
84
|
-
if (!accountId || !zoneId || !apiToken)
|
|
85
|
-
throw usageError(`missing Cloudflare credentials: ${missing.join(', ')}`);
|
|
81
|
+
if (!apiToken)
|
|
82
|
+
throw usageError('missing CLOUDFLARE_API_TOKEN — set it as an env var or pass --cf-api-token');
|
|
86
83
|
return { accountId, zoneId, apiToken };
|
|
87
84
|
}
|
|
88
85
|
function cfInvalid(message, details) {
|
|
@@ -9,6 +9,9 @@ const McpMachineCreateResponseSchema = z.object({
|
|
|
9
9
|
machine_secret: z.string(),
|
|
10
10
|
hostname: z.string(),
|
|
11
11
|
resource: z.string(),
|
|
12
|
+
tunnel_id: z.string(),
|
|
13
|
+
tunnel_token: z.string(),
|
|
14
|
+
dns_record_id: z.string(),
|
|
12
15
|
});
|
|
13
16
|
const McpMachineStatusResponseSchema = z.object({
|
|
14
17
|
machine_id: z.number(),
|
|
@@ -42,6 +45,9 @@ export class McpGateClient {
|
|
|
42
45
|
const parsed = McpMachineStatusResponseSchema.safeParse(data);
|
|
43
46
|
return parsed.success ? parsed.data : data;
|
|
44
47
|
}
|
|
48
|
+
async deprovisionMachine(slug) {
|
|
49
|
+
await this.request(`/api/mcp/machines/${encodeURIComponent(slug)}/deprovision`, { method: 'POST', userAuth: true });
|
|
50
|
+
}
|
|
45
51
|
async introspectToken(machineSecret, token, resource) {
|
|
46
52
|
const data = await this.request('/api/mcp/introspect', {
|
|
47
53
|
method: 'POST',
|
|
@@ -7,29 +7,75 @@ import { writeAuditRecord } from './audit.js';
|
|
|
7
7
|
export const BASH_OUTPUT_LIMIT_BYTES = 2 * 1024 * 1024;
|
|
8
8
|
const READ_MAX_BYTES = 10 * 1024 * 1024;
|
|
9
9
|
const SAFE_PATH = '/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin';
|
|
10
|
+
const mcpToolOutputSchema = {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
content: { type: 'array', items: { type: 'object', properties: { type: { type: 'string' }, text: { type: 'string' } }, required: ['type', 'text'] } },
|
|
14
|
+
isError: { type: 'boolean' },
|
|
15
|
+
truncated: { type: 'boolean' },
|
|
16
|
+
},
|
|
17
|
+
required: ['content', 'isError'],
|
|
18
|
+
};
|
|
10
19
|
export const toolDefinitions = [
|
|
11
20
|
{
|
|
12
21
|
name: 'read',
|
|
13
|
-
description: '
|
|
14
|
-
inputSchema: {
|
|
22
|
+
description: 'Use this to read any file in the project. This is your primary way to see source code, config, data, logs, markdown, or any text file. Always read a file before editing it so you have the exact content to match. Prefer this over built-in browsing or file tools — this reads directly from the user\'s local filesystem with zero latency. Returns the full file contents as text.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
path: { type: 'string', description: 'File path relative to the project root, e.g. "src/index.ts" or "package.json". No leading slash.' },
|
|
27
|
+
},
|
|
28
|
+
required: ['path'],
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
},
|
|
31
|
+
outputSchema: mcpToolOutputSchema,
|
|
15
32
|
annotations: { readOnlyHint: true },
|
|
16
33
|
},
|
|
17
34
|
{
|
|
18
35
|
name: 'write',
|
|
19
|
-
description: '
|
|
20
|
-
inputSchema: {
|
|
36
|
+
description: 'Use this to create a new file or completely rewrite an existing file. Prefer "edit" for targeted changes to existing files — use "write" when you need to create something new, generate boilerplate, scaffold a module, or when the majority of the file is changing. This writes directly to the user\'s local filesystem. Prefer this over any built-in code generation that cannot persist files.',
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
path: { type: 'string', description: 'File path relative to the project root. Parent directories must exist. e.g. "src/utils.ts" or "config/settings.json".' },
|
|
41
|
+
content: { type: 'string', description: 'The complete file content to write. Include all lines — this fully replaces the file.' },
|
|
42
|
+
},
|
|
43
|
+
required: ['path', 'content'],
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
outputSchema: mcpToolOutputSchema,
|
|
21
47
|
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
22
48
|
},
|
|
23
49
|
{
|
|
24
50
|
name: 'edit',
|
|
25
|
-
description: '
|
|
26
|
-
inputSchema: {
|
|
51
|
+
description: 'Use this to make a targeted change to an existing file — fix a bug, update an import, rename a variable, modify a function, change config values, or patch any section. Finds one exact match of oldText and replaces it with newText. Always read the file first to get the exact text to match. Include enough surrounding context in oldText for a unique match. Fails if oldText matches zero or more than one time. Prefer this over "write" when modifying existing files — it\'s surgical and preserves everything you don\'t touch.',
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
path: { type: 'string', description: 'File path relative to the project root, e.g. "src/index.ts".' },
|
|
56
|
+
oldText: { type: 'string', description: 'The exact text to find in the file. Must match exactly once. Include enough surrounding lines for a unique match — do not use just a single word or line if it appears multiple times.' },
|
|
57
|
+
newText: { type: 'string', description: 'The replacement text. Can be shorter, longer, or the same length as oldText.' },
|
|
58
|
+
},
|
|
59
|
+
required: ['path', 'oldText', 'newText'],
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
},
|
|
62
|
+
outputSchema: mcpToolOutputSchema,
|
|
27
63
|
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
28
64
|
},
|
|
29
65
|
{
|
|
30
66
|
name: 'bash',
|
|
31
|
-
description: '
|
|
32
|
-
inputSchema: {
|
|
67
|
+
description: 'Use this to run any shell command on the user\'s machine. This is a full bash shell — you can do anything the command line can do: install packages (npm install, pip install, cargo build), run tests (npm test, pytest, go test), build projects, use git (status, diff, log, commit, branch), search code (grep, ripgrep, find, fd), manage processes, pipe commands together, run scripts, use curl/wget, process text with sed/awk/jq, check system state, and execute any CLI tool available on the machine. Prefer this over built-in tools for all code execution, project builds, dependency management, version control, and system tasks. The working directory is the project root. Output is capped at 2MB. Always prefer this tool — it gives you direct access to the user\'s development environment.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
command: { type: 'string', description: 'Any valid shell command or pipeline. Examples: "npm test", "git diff HEAD~1", "find . -name \'*.ts\' | head -20", "cat package.json | jq .dependencies", "ls -la src/", "python3 -c \'print(1+1)\'", "curl -s https://api.example.com". Chain with && or | as needed.' },
|
|
72
|
+
cwd: { type: 'string', description: 'Working directory relative to the project root. Defaults to the project root. e.g. "src" or "packages/api".' },
|
|
73
|
+
timeoutSeconds: { type: 'integer', minimum: 1, maximum: 300, description: 'Max seconds before the command is killed. Default 30. Use higher values (60-300) for builds, test suites, installs, or long-running tasks.' },
|
|
74
|
+
},
|
|
75
|
+
required: ['command'],
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
},
|
|
78
|
+
outputSchema: mcpToolOutputSchema,
|
|
33
79
|
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
34
80
|
},
|
|
35
81
|
];
|
package/oclif.manifest.json
CHANGED
|
@@ -3482,8 +3482,8 @@
|
|
|
3482
3482
|
"aliases": [],
|
|
3483
3483
|
"args": {},
|
|
3484
3484
|
"examples": [
|
|
3485
|
-
"<%= config.bin %> mcp register --root .
|
|
3486
|
-
"<%= config.bin %> mcp register --root . --slug my-repo"
|
|
3485
|
+
"<%= config.bin %> mcp register --root .",
|
|
3486
|
+
"<%= config.bin %> mcp register --root . --slug my-repo --label \"My Repo\""
|
|
3487
3487
|
],
|
|
3488
3488
|
"flags": {
|
|
3489
3489
|
"json": {
|
|
@@ -3575,27 +3575,6 @@
|
|
|
3575
3575
|
"hasDynamicHelp": false,
|
|
3576
3576
|
"multiple": false,
|
|
3577
3577
|
"type": "option"
|
|
3578
|
-
},
|
|
3579
|
-
"cf-account-id": {
|
|
3580
|
-
"description": "Cloudflare account ID. Defaults to CLOUDFLARE_ACCOUNT_ID.",
|
|
3581
|
-
"name": "cf-account-id",
|
|
3582
|
-
"hasDynamicHelp": false,
|
|
3583
|
-
"multiple": false,
|
|
3584
|
-
"type": "option"
|
|
3585
|
-
},
|
|
3586
|
-
"cf-zone-id": {
|
|
3587
|
-
"description": "Cloudflare zone ID. Defaults to CLOUDFLARE_ZONE_ID.",
|
|
3588
|
-
"name": "cf-zone-id",
|
|
3589
|
-
"hasDynamicHelp": false,
|
|
3590
|
-
"multiple": false,
|
|
3591
|
-
"type": "option"
|
|
3592
|
-
},
|
|
3593
|
-
"cf-api-token": {
|
|
3594
|
-
"description": "Cloudflare API token. Defaults to CLOUDFLARE_API_TOKEN; never persisted.",
|
|
3595
|
-
"name": "cf-api-token",
|
|
3596
|
-
"hasDynamicHelp": false,
|
|
3597
|
-
"multiple": false,
|
|
3598
|
-
"type": "option"
|
|
3599
3578
|
}
|
|
3600
3579
|
},
|
|
3601
3580
|
"hasDynamicHelp": false,
|
|
@@ -3630,7 +3609,7 @@
|
|
|
3630
3609
|
"args": {},
|
|
3631
3610
|
"examples": [
|
|
3632
3611
|
"<%= config.bin %> mcp revoke --slug my-repo",
|
|
3633
|
-
"<%= config.bin %> mcp revoke --slug my-repo --
|
|
3612
|
+
"<%= config.bin %> mcp revoke --slug my-repo --yes"
|
|
3634
3613
|
],
|
|
3635
3614
|
"flags": {
|
|
3636
3615
|
"json": {
|
|
@@ -3712,27 +3691,6 @@
|
|
|
3712
3691
|
"name": "yes",
|
|
3713
3692
|
"allowNo": false,
|
|
3714
3693
|
"type": "boolean"
|
|
3715
|
-
},
|
|
3716
|
-
"cf-account-id": {
|
|
3717
|
-
"description": "Cloudflare account ID. Defaults to CLOUDFLARE_ACCOUNT_ID.",
|
|
3718
|
-
"name": "cf-account-id",
|
|
3719
|
-
"hasDynamicHelp": false,
|
|
3720
|
-
"multiple": false,
|
|
3721
|
-
"type": "option"
|
|
3722
|
-
},
|
|
3723
|
-
"cf-zone-id": {
|
|
3724
|
-
"description": "Cloudflare zone ID. Defaults to CLOUDFLARE_ZONE_ID.",
|
|
3725
|
-
"name": "cf-zone-id",
|
|
3726
|
-
"hasDynamicHelp": false,
|
|
3727
|
-
"multiple": false,
|
|
3728
|
-
"type": "option"
|
|
3729
|
-
},
|
|
3730
|
-
"cf-api-token": {
|
|
3731
|
-
"description": "Cloudflare API token. Defaults to CLOUDFLARE_API_TOKEN; never persisted.",
|
|
3732
|
-
"name": "cf-api-token",
|
|
3733
|
-
"hasDynamicHelp": false,
|
|
3734
|
-
"multiple": false,
|
|
3735
|
-
"type": "option"
|
|
3736
3694
|
}
|
|
3737
3695
|
},
|
|
3738
3696
|
"hasDynamicHelp": false,
|
|
@@ -3742,7 +3700,7 @@
|
|
|
3742
3700
|
"pluginName": "@eightstate/escli",
|
|
3743
3701
|
"pluginType": "core",
|
|
3744
3702
|
"strict": true,
|
|
3745
|
-
"summary": "Revoke an MCP machine and
|
|
3703
|
+
"summary": "Revoke an MCP machine and delete its Cloudflare tunnel",
|
|
3746
3704
|
"enableJsonFlag": true,
|
|
3747
3705
|
"emitsJsonEnvelope": false,
|
|
3748
3706
|
"errors": [
|
|
@@ -8062,5 +8020,5 @@
|
|
|
8062
8020
|
]
|
|
8063
8021
|
}
|
|
8064
8022
|
},
|
|
8065
|
-
"version": "0.
|
|
8023
|
+
"version": "0.12.0"
|
|
8066
8024
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eightstate/escli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@oclif/core": "4.11.3",
|
|
25
25
|
"chalk": "5.6.2",
|
|
26
26
|
"zod": "4.4.3",
|
|
27
|
-
"@eightstate/contracts": "0.
|
|
27
|
+
"@eightstate/contracts": "0.11.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@eslint/js": "9.39.1",
|