@noimosai/platform-mcp 0.1.3 → 0.1.5
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 +17 -48
- package/bin/noimos-platform-mcp.mjs +4 -5
- package/dist/cli/cli.js +565 -0
- package/dist/cli/command-help.js +395 -0
- package/dist/cli/download.js +79 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/input-error.js +48 -0
- package/dist/cli/local-agents.js +199 -0
- package/dist/cli/mcp-supervisor.js +136 -0
- package/dist/cli/noimos-cli.js +72 -0
- package/dist/cli/project-archive.js +27 -0
- package/dist/cli/project-files.js +25 -0
- package/dist/cli/project-sync.js +265 -0
- package/dist/cli/setup-lock.js +1 -0
- package/dist/cli/setup.js +347 -0
- package/dist/cli/skills.js +142 -0
- package/dist/cli/team-harness-targets.js +337 -0
- package/dist/cli/team-harness.js +1562 -0
- package/dist/cli/team-resources.js +222 -0
- package/dist/cli/team-skills.js +457 -0
- package/dist/cli/update-bootstrap.js +437 -0
- package/dist/cli/update-release.js +212 -0
- package/dist/cli/version.js +1 -0
- package/dist/cli/wallet.js +85 -0
- package/dist/client/agent-run.js +1425 -0
- package/dist/client/browser.js +911 -0
- package/dist/client/distribution.js +2 -0
- package/dist/client/evm-wallet.js +214 -0
- package/dist/client/execution-context.js +14 -0
- package/dist/client/failure-recovery.js +128 -0
- package/dist/client/index.js +4 -225
- package/dist/client/local-input.js +33 -12
- package/dist/client/local-profile-lock.js +52 -0
- package/dist/client/local-profile.js +181 -0
- package/dist/client/local-projects.js +131 -0
- package/dist/client/local-setup.js +93 -0
- package/dist/client/realtime-voice.js +184 -0
- package/dist/client/tool-name-hints.js +48 -0
- package/dist/client/wallet-access.js +214 -0
- package/dist/mcp/agent-server.js +345 -0
- package/dist/mcp/context.js +86 -0
- package/dist/mcp/host-schema.js +76 -0
- package/dist/mcp/icon.js +1 -0
- package/dist/mcp/instructions.js +26 -0
- package/dist/mcp/local-projects.js +42 -0
- package/dist/mcp/local-upload.js +7 -13
- package/dist/mcp/noimos-runtime.js +18 -0
- package/dist/mcp/noimos-server.js +38 -0
- package/dist/mcp/operations.js +41 -0
- package/dist/mcp/parallel-result.js +17 -0
- package/dist/mcp/project-sync.js +105 -0
- package/dist/mcp/results-reply.js +15 -0
- package/dist/mcp/runtime.js +65 -11
- package/dist/mcp/server.js +382 -40
- package/dist/mcp/team-skill.js +63 -0
- package/dist/mcp/update-notice.js +75 -0
- package/dist/mcp/version.js +1 -0
- package/dist/schema/accountRequestMethods.js +1 -0
- package/dist/schema/boundedBody.js +30 -0
- package/dist/schema/cronValidator.js +150 -0
- package/dist/schema/platformApiKey.js +14 -0
- package/dist/schema/platformAppAuth.js +11 -0
- package/dist/schema/platformBillingConstants.js +4 -0
- package/dist/schema/platformCapabilityFamilies.js +293 -0
- package/dist/schema/platformConnectionCatalog.js +39 -0
- package/dist/schema/platformConnectionPolicy.js +33 -0
- package/dist/schema/platformCustomTools.js +148 -0
- package/dist/schema/platformEndUserApi.js +62 -0
- package/dist/schema/platformFunding.js +10 -0
- package/dist/schema/platformHttpConstraints.js +12 -0
- package/dist/schema/platformLocalSetup.js +25 -0
- package/dist/schema/platformMcpTools.js +7 -0
- package/dist/schema/platformMetaTools.js +63 -0
- package/dist/schema/platformMonitorSchedule.js +123 -0
- package/dist/schema/platformMonitors.js +159 -0
- package/dist/schema/platformOAuth.js +13 -0
- package/dist/schema/platformProjectPermissions.js +33 -0
- package/dist/schema/platformPublicExecution.js +202 -21
- package/dist/schema/platformRoutes.js +122 -0
- package/dist/schema/platformSkills.js +11 -0
- package/dist/schema/platformTeamSecretName.js +3 -0
- package/dist/schema/platformTeamSkillSlug.js +1 -0
- package/dist/schema/platformTeamSkills.js +302 -0
- package/dist/schema/platformToolAccess.js +62 -0
- package/dist/schema/platformTriggers.js +153 -0
- package/dist/schema/platformWorkflows.js +100 -0
- package/dist/schema/scheduleTimeZone.js +15 -0
- package/dist/schema/toolDiscovery.js +71 -0
- package/dist/schema/toolInputRepair.js +122 -0
- package/dist/schema/toolResultContext.js +51 -0
- package/dist/schema/untrustedContent.js +35 -0
- package/npm-shrinkwrap.json +73 -0
- package/package.json +5 -3
package/dist/cli/cli.js
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { configFromEnvironment, PlatformClient, PLATFORM_DISTRIBUTIONS, walletAutoTopUpFromEnvironment, platformQuoteRequestSchema, platformCreateExecutionSchema, platformArtifactIdSchema, platformExecutionIdSchema, platformExecutionListQuerySchema, platformConnectionCatalogQuerySchema, toolDiscoveryInputSchema, toolResultSelectionSchema, TOOL_RESULT_PAGE_MAX_ITEMS, executionReceiptContext } from "../client/index.js";
|
|
5
|
+
import { downloadArtifact } from "./download.js";
|
|
6
|
+
import { commandHelp } from "./command-help.js";
|
|
7
|
+
import { CliInputError, cliInputReason, isCliInputFailure, jsonOption } from "./input-error.js";
|
|
8
|
+
import { uploadLocalInput } from "../client/local-input.js";
|
|
9
|
+
import { localProfilePath, runtimeConfig } from "../client/local-profile.js";
|
|
10
|
+
import { localProjectConfig } from "../client/local-projects.js";
|
|
11
|
+
import { isTeamResource, teamResourceCommand } from "./team-resources.js";
|
|
12
|
+
import { PLATFORM_CLI_VERSION } from "./version.js";
|
|
13
|
+
import { AgentRun, agentRunSchema, agentParallelSchema, agentResultSchema, agentResultsSchema, agentResumeSchema, resultFilesFor as resultFiles } from "../client/agent-run.js";
|
|
14
|
+
import { platformTeamQuerySchema } from "../client/index.js";
|
|
15
|
+
import { platformProjectStatusQuerySchema } from "../client/index.js";
|
|
16
|
+
import { PLATFORM_META_LIMITS, PLATFORM_TOOL_REQUEST_MAX_BYTES, platformMetaSearchSchema, platformMetaSchemasSchema, platformMetaConnectionsSchema, platformMetaExecuteSchema, platformMetaWorkbenchSchema, platformMetaBashSchema, platformIdempotencyKeySchema, platformJsonObjectSchema, withEvidentValues } from "../client/index.js";
|
|
17
|
+
function metaInput(schema, input) {
|
|
18
|
+
const json = platformJsonObjectSchema.safeParse(input);
|
|
19
|
+
return json.success ? withEvidentValues(schema, json.data) : input;
|
|
20
|
+
}
|
|
21
|
+
const READ_OPTIONS = { path: { type: "string" }, offset: { type: "string" }, limit: { type: "string" }, "max-chars": { type: "string" }, fields: { type: "string" },
|
|
22
|
+
find: { type: "string" }, pick: { type: "string" }, wait: { type: "string" } };
|
|
23
|
+
function findTerms(text) {
|
|
24
|
+
try {
|
|
25
|
+
const list = JSON.parse(text);
|
|
26
|
+
if (Array.isArray(list) && list.every(term => typeof term === "string"))
|
|
27
|
+
return list;
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
return [text];
|
|
31
|
+
}
|
|
32
|
+
const agentRead = (values) => ({ ...(values.path === undefined ? {} : { path: jsonOption("--path", values.path) }),
|
|
33
|
+
...(values.fields === undefined ? {} : { fields: jsonOption("--fields", values.fields) }), ...(values.offset === undefined ? {} : { offset: Number(values.offset) }),
|
|
34
|
+
...(values.limit === undefined ? {} : { limit: Number(values.limit) }), ...(values["max-chars"] === undefined ? {} : { maxChars: Number(values["max-chars"]) }),
|
|
35
|
+
...(values.find === undefined ? {} : { find: findTerms(values.find) }),
|
|
36
|
+
...(values.pick === undefined ? {} : { pick: jsonOption("--pick", values.pick) }), ...(values.wait === undefined ? {} : { waitSeconds: Number(values.wait) }) });
|
|
37
|
+
const missedRequestedOutput = (result) => "resultError" in result || "fileError" in result || "upstreamRefusal" in result;
|
|
38
|
+
function refuseDuplicates(tokens, message) {
|
|
39
|
+
const names = tokens.filter(token => token.kind === "option").map(token => token.name);
|
|
40
|
+
if (new Set(names).size !== names.length)
|
|
41
|
+
throw new CliInputError(message);
|
|
42
|
+
}
|
|
43
|
+
export const help = `Relvo CLI — discover and run project tools
|
|
44
|
+
|
|
45
|
+
tools --query <capability> --limit 1 --with-schemas
|
|
46
|
+
Find a tool and its input schema in one request; add --app for a connected service.
|
|
47
|
+
Search uses monthly free calls; overage is charged.
|
|
48
|
+
run <tool> '<arguments-json>' [--maximum-charge-micros <integer>] --idempotency-key <key>
|
|
49
|
+
Quote, save the exact purchase, submit, wait and select results in one call.
|
|
50
|
+
Optional: --provider <id> --path '<path-array>' --fields '<names-array>' --find '<terms>'
|
|
51
|
+
--pick '<label-to-paths-json>' --limit <n> --wait <1-60 seconds> --file
|
|
52
|
+
resume <run-id> [--wait <seconds>]
|
|
53
|
+
Recover the same saved purchase after a lost reply; never use a fresh key for unknown work.
|
|
54
|
+
parallel ('<request-json>' | - | --input-file <path>)
|
|
55
|
+
Start independent tools together; return every admission and execution ID without waiting.
|
|
56
|
+
result <execution-id> [--path '<path-array>'] [--fields '<names-array>'] [--find '<terms>'] [--wait <1-60 seconds>] [--file]
|
|
57
|
+
Read retained data without paying for another execution. --find keeps the lines of a
|
|
58
|
+
text that hold a term. --file writes the whole path as JSON Lines under ./.relvo/results.
|
|
59
|
+
results <execution-id>... [--path '<path-array>'] [--fields '<names-array>'] [--find '<terms>'] [--wait <1-60 seconds>]
|
|
60
|
+
Read several executions at once, such as every ID parallel returned.
|
|
61
|
+
setup Sign in and connect your installed agents.
|
|
62
|
+
project list | add | use <project-id>
|
|
63
|
+
Approve and switch projects. Add --project <id> to a tool command for one call.
|
|
64
|
+
help <command> | help all
|
|
65
|
+
Load detailed instructions only when needed.
|
|
66
|
+
|
|
67
|
+
1 USD = 1000000 micros. A budget does not authorize sending, publishing or account changes.
|
|
68
|
+
Scope comes from the developer API key. Provider keys remain server-side.
|
|
69
|
+
Environment: RELVO_API_KEY and RELVO_API_URL, or the login saved by setup.
|
|
70
|
+
Other commands: tool, quote, execute, execution, wait, executions, connections, connect,
|
|
71
|
+
project, usage, doctor, logout, update, skills, team-skills, custom-tools, monitors, triggers,
|
|
72
|
+
wallet, cancel, artifact-url, download, upload. Legacy NoimosAI Platform also supports access.
|
|
73
|
+
`;
|
|
74
|
+
export const fullHelp = `Relvo CLI
|
|
75
|
+
|
|
76
|
+
Usage: relvo <command>
|
|
77
|
+
relvo <command> --help | help <command>
|
|
78
|
+
|
|
79
|
+
setup --api-url <url> --console-url <origin> [--client auto|claude|codex|terminal]
|
|
80
|
+
[--mcp-runtime registry|installed] [--local] [--no-browser] [--new-project]
|
|
81
|
+
Sign in through the browser and configure every installed agent
|
|
82
|
+
(Claude Code and Codex) with one approval; auto is the default
|
|
83
|
+
doctor Verify the saved login, live catalog and owned MCP connection
|
|
84
|
+
logout Remove the local login; revoke the server key in the console
|
|
85
|
+
update [status|auto on|auto off|pin <version>|rollback]
|
|
86
|
+
Prepare a verified CLI/MCP release for the next process start
|
|
87
|
+
skills list | skills update [--client claude|codex|all]
|
|
88
|
+
Read or explicitly update current first-party guides
|
|
89
|
+
team-skills sync [--client all|<agent>[,<agent>…]] [--project <id>] [--allow-scripts] | team-skills push <SKILL.md, folder, AGENTS.md, CLAUDE.md, agent or rule file> [--kind skill|instructions|agent|rule] [--slug <slug>]
|
|
90
|
+
Copy the team skills, agents, rules and (with --allow-scripts) hooks you may use to every coding agent, or share a local one
|
|
91
|
+
team-skills check [--client <agents>] | apply <snapshot> --yes [--client <agents>] [--allow-scripts | --remove-executable-settings]
|
|
92
|
+
team-skills auto-sync on|off|status
|
|
93
|
+
Detect project changes, ask before importing, and configure local MCP notification checks
|
|
94
|
+
team-skills list | get <slug> | versions <slug> | restore <slug> --version <n> --expected-version <n> | delete <slug>
|
|
95
|
+
Read a team skill's kept versions, restore one, or delete the skill
|
|
96
|
+
login Alias for setup
|
|
97
|
+
wallet sign-in [--api-url <url>] [--local] [--key-file <path>] | wallet top-up --amount <usd> --max <usd> [--key-file <path>]
|
|
98
|
+
Sign in or add funds with an EVM wallet; help wallet explains each
|
|
99
|
+
access list | access set <member-id> (--tools '<json array>' | --all) [legacy NoimosAI Platform]
|
|
100
|
+
Read or change what each member may use, with a key made for it
|
|
101
|
+
|
|
102
|
+
project Show the API key's project; list/add/use approved local projects
|
|
103
|
+
usage Show the current project's monthly request count and spend
|
|
104
|
+
team [--member-offset <integer>] [--invitation-offset <integer>] [--limit <1-100>]
|
|
105
|
+
Read the selected team and your roles; owners/admins can page members and invitations
|
|
106
|
+
connections [--provider <id>] [--offset <integer>] [--limit <1-20>]
|
|
107
|
+
List project OAuth connections, states and observed grants
|
|
108
|
+
connect <app>
|
|
109
|
+
Print the console pages where you connect an app or add an account
|
|
110
|
+
tools [--query <text>] [--app <name-or-id>] [--category <id>]
|
|
111
|
+
[--provider <id>] [--available-only | --include-unavailable] [--summary | --with-schemas]
|
|
112
|
+
[--max-charge-micros <integer>] [--quote-arguments '<json>']
|
|
113
|
+
[--funding all|relvo|provider|mixed] [--price-unit <unit>] [--sort-by relevance|price_asc]
|
|
114
|
+
[--include '<filter-json>'] [--exclude '<filter-json>']
|
|
115
|
+
[--limit <1-12>] [--mode auto|keyword|semantic]
|
|
116
|
+
Search up to 12 tool summaries and USD prices (default 8)
|
|
117
|
+
tool <name>
|
|
118
|
+
Inspect one tool's arguments, example and price
|
|
119
|
+
meta <search|schemas|connections|execute|workbench|bash> '<request-json>'
|
|
120
|
+
Use six session-scoped meta tools; stdin/file input accepts 1 MiB
|
|
121
|
+
executions [--limit <1-100>] [--before <id>] [--status <state>] [--tool <name>]
|
|
122
|
+
Recover the API key's project execution history
|
|
123
|
+
monitors list | popular | tools | delete <id> | resume <id> [--maximum-charge-micros-per-run <n>]
|
|
124
|
+
monitors quote|create --tool <name> [--args '<json>'] [--provider <id>] [--interval 15m|1h|1d | --cron "<5 fields>" [--time-zone <IANA>]] [--name <text>] [--maximum-charge-micros-per-run <n>]
|
|
125
|
+
monitors events [--monitor <id>] [--after <cursor>] [--limit <1-100>]
|
|
126
|
+
monitors destination-add <id> --destination '<json>' | destination-remove <id> --destination-id <dst_id>
|
|
127
|
+
Watch tools for changes, each run charged in full; help monitors explains each
|
|
128
|
+
triggers list | types --app <app> [--connection <id>] [--query <text>] [--offset <n>] [--limit <1-100>] | delete <id> | update <id> [--enabled true|false] [--name <text>]
|
|
129
|
+
triggers create --connection <id> (--tool <name> [--args '<json>'] [--interval 1m|5m|15m|1h] [--items-path </json/pointer>] [--maximum-charge-micros-per-poll <n>] | --type <app.event> [--config '<json>']) [--name <text>] [--destinations '<json>']
|
|
130
|
+
triggers events [--trigger <id>] [--after <cursor>] [--limit <1-100>]
|
|
131
|
+
triggers destination-add <id> --destination '<json>' | destination-remove <id> --destination-id <dst_id>
|
|
132
|
+
Get events from a connected app; help triggers explains each
|
|
133
|
+
custom-tools list | get <name> | versions <name> | delete <name> | restore <name> --version <n> --expected-version <n>
|
|
134
|
+
custom-tools create <slug> | update <name> --expected-version <n> (definition from - or --input-file <path>)
|
|
135
|
+
Add Python or an API endpoint as a tool for the team; help custom-tools explains each
|
|
136
|
+
quote <tool> '<arguments-json>' [--provider <id>] [--session <id>] [--relevance-check auto|light] [--maximum-charge-micros <cap>]
|
|
137
|
+
Get a free quote without running the tool
|
|
138
|
+
run <tool> '<arguments-json>' [--maximum-charge-micros <integer>] --idempotency-key <key> [--provider <id>] [--session <id>]
|
|
139
|
+
[--path '<path-array>'] [--fields '<names-array>'] [--find '<terms>'] [--pick '<label-to-paths-json>'] [--limit <n>] [--offset <n>] [--max-chars <n>] [--wait <1-60>] [--file] [--accept-external-billing | --decline-external-billing]
|
|
140
|
+
Quote, save, submit and wait for already-authorized work
|
|
141
|
+
resume <run-id> [--wait <seconds>]
|
|
142
|
+
Recover the saved exact purchase; takes run's result selection, never a change to the purchase
|
|
143
|
+
parallel ('<request-json>' | - | --input-file <path>)
|
|
144
|
+
Quote, save and start up to 50 independent calls; read each accepted execution ID with result
|
|
145
|
+
execute <tool> '<arguments-json>' --maximum-charge-micros <integer>
|
|
146
|
+
--price-version <version> --idempotency-key <key> [--provider <id>] [--session <id>] [--relevance-check auto|light] [--raw]
|
|
147
|
+
Reserve up to the quoted amount and enqueue paid work
|
|
148
|
+
execution <id> [--raw]
|
|
149
|
+
Read status, a bounded output preview and the final USD charge
|
|
150
|
+
result <id> [--path '<JSON-array>'] [--fields '<JSON-array>'] [--offset <n>] [--limit <1-${TOOL_RESULT_PAGE_MAX_ITEMS}>] [--max-chars <1-8000>] [--find '<terms>'] [--pick '<label-to-paths-json>'] [--wait <1-60 seconds>] [--file]
|
|
151
|
+
Read a selected page of the retained full result
|
|
152
|
+
results <id>... [--path '<JSON-array>'] [--fields '<JSON-array>'] [--offset <n>] [--limit <1-${TOOL_RESULT_PAGE_MAX_ITEMS}>] [--max-chars <n>] [--find '<terms>'] [--pick '<label-to-paths-json>'] [--wait <1-60 seconds>]
|
|
153
|
+
Read the same selection of several retained results at once
|
|
154
|
+
wait <id> [--timeout <seconds>] [--raw]
|
|
155
|
+
Wait for completion (default 480 seconds, maximum 900)
|
|
156
|
+
cancel <id> [--raw]
|
|
157
|
+
Request cancellation of work that has not started
|
|
158
|
+
artifact-url <execution-id> <artifact-id>
|
|
159
|
+
Get a private output's download URL, valid for up to 5 minutes
|
|
160
|
+
download <execution-id> <artifact-id> --out <file>
|
|
161
|
+
Save an artifact after size/SHA-256 checks; never overwrite a file
|
|
162
|
+
upload <file>
|
|
163
|
+
Upload one explicit JPG/PNG/WebP/WAV/MP3/MP4/MOV input, at most 12 MiB
|
|
164
|
+
|
|
165
|
+
run quotes internally and charges the project's balance; quote separately only when
|
|
166
|
+
the user asks to approve a price. execute needs a quote first: use its price version
|
|
167
|
+
and maximum charge (1 USD = 1000000 micros). Keep the same body and idempotency key
|
|
168
|
+
when retrying a submission.
|
|
169
|
+
Auto routing is the default (--provider auto is equivalent). Use --provider to pin a provider for both quote
|
|
170
|
+
and execute; tools --provider only filters discovery.
|
|
171
|
+
Execution is asynchronous. Poll at intervals of at least 5 seconds.
|
|
172
|
+
For quote, execute, run and meta, use - in place of arguments-json to read JSON from stdin,
|
|
173
|
+
or --input-file <path> to read a JSON file (at most 1 MiB).
|
|
174
|
+
wait exits 0 on success, 1 on failed/cancelled/needs_review, 3 on timeout.
|
|
175
|
+
A timeout leaves accepted work running; read or wait on the same execution ID.
|
|
176
|
+
Large outputs stay in the execution. Use result to select data; --raw explicitly
|
|
177
|
+
returns the full output for execution, wait, execute or cancel.
|
|
178
|
+
|
|
179
|
+
Scope comes from the developer API key. OAuth connection IDs belong to this
|
|
180
|
+
project; inspect the selected tool's schema before using one. Workspace, brand,
|
|
181
|
+
knowledge base, dataset, workspace skill and chat-session commands are unavailable.
|
|
182
|
+
Use --project <id> on a tool command to select an already approved login for one call.
|
|
183
|
+
project list/add/use manages saved approvals; reconnect MCP after changing the default.
|
|
184
|
+
Start a managed MCP pinned to one approval with mcp --profile <managed-path> --project <id>.
|
|
185
|
+
|
|
186
|
+
Environment: RELVO_API_URL, RELVO_API_KEY
|
|
187
|
+
Project status: relvo project status [--member-offset <n>] [--invitation-offset <n>] [--limit <1-100>]
|
|
188
|
+
Local emulators only: RELVO_ENV=local
|
|
189
|
+
|
|
190
|
+
Manage teams, members, keys and billing in the Relvo console.
|
|
191
|
+
`;
|
|
192
|
+
export const RELVO_CLI_SURFACE = { distribution: PLATFORM_DISTRIBUTIONS.relvo, help, fullHelp, commandHelp, supports: () => true, savedLogins: true, clientNameHeader: "x-relvo-client-name" };
|
|
193
|
+
export async function runCli(args, env, output, error, transport = fetch, stdin = process.stdin, profile, surface = RELVO_CLI_SURFACE) {
|
|
194
|
+
const { cliCommand, name } = surface.distribution;
|
|
195
|
+
let projectId;
|
|
196
|
+
const projectOption = args.indexOf("--project");
|
|
197
|
+
if (projectOption !== -1) {
|
|
198
|
+
if (!surface.savedLogins) {
|
|
199
|
+
error(`${name} has no saved project logins; use the API key of the project you want. Nothing was sent.`);
|
|
200
|
+
return 2;
|
|
201
|
+
}
|
|
202
|
+
const value = args[projectOption + 1];
|
|
203
|
+
if (!value?.trim() || value.startsWith("-") || args.lastIndexOf("--project") !== projectOption) {
|
|
204
|
+
error("Provide one --project <id>. Nothing was sent.");
|
|
205
|
+
return 2;
|
|
206
|
+
}
|
|
207
|
+
projectId = value;
|
|
208
|
+
args = [...args.slice(0, projectOption), ...args.slice(projectOption + 2)];
|
|
209
|
+
}
|
|
210
|
+
if (args.length === 2 && args[0] === "help" && args[1] === "all") {
|
|
211
|
+
output(surface.fullHelp);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
if (!args.length || args[0] === "--help" || args[0] === "-h" || args.length === 1 && args[0] === "help") {
|
|
215
|
+
output(surface.help);
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
if (args[0] === "help" || args.slice(1).some((arg) => arg === "--help" || arg === "-h")) {
|
|
219
|
+
const detail = surface.commandHelp(args[0] === "help" ? args[1] : args[0]);
|
|
220
|
+
if (!detail || args[0] === "help" && args.length !== 2) {
|
|
221
|
+
error(`Unknown help command. Run ${cliCommand} --help.`);
|
|
222
|
+
return 2;
|
|
223
|
+
}
|
|
224
|
+
output(detail);
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
if (args.length === 1 && args[0] === "--version") {
|
|
228
|
+
output(PLATFORM_CLI_VERSION);
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
if (!surface.supports(args)) {
|
|
232
|
+
error(`${name} does not offer this command or option. Nothing was sent. Run ${cliCommand} --help.`);
|
|
233
|
+
return 2;
|
|
234
|
+
}
|
|
235
|
+
let command;
|
|
236
|
+
let exitCode = 0;
|
|
237
|
+
try {
|
|
238
|
+
const readInput = async (file, limit = PLATFORM_TOOL_REQUEST_MAX_BYTES) => {
|
|
239
|
+
if (!file || file.startsWith("--"))
|
|
240
|
+
throw new CliInputError("Provide an input file");
|
|
241
|
+
const chunks = [];
|
|
242
|
+
let bytes = 0;
|
|
243
|
+
for await (const chunk of file === "-" ? stdin : createReadStream(file)) {
|
|
244
|
+
const buffer = Buffer.from(chunk);
|
|
245
|
+
bytes += buffer.byteLength;
|
|
246
|
+
if (bytes > limit)
|
|
247
|
+
throw new CliInputError("Input exceeds its byte limit");
|
|
248
|
+
chunks.push(buffer);
|
|
249
|
+
}
|
|
250
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
251
|
+
};
|
|
252
|
+
if (["quote", "execute", "run", "meta"].includes(args[0]) && ["-", "--input-file"].includes(args[2])) {
|
|
253
|
+
const input = await readInput(args[2] === "--input-file" ? args[3] : "-", args[0] === "meta" ? PLATFORM_META_LIMITS.requestBytes : PLATFORM_TOOL_REQUEST_MAX_BYTES);
|
|
254
|
+
args = [args[0], args[1], input, ...args.slice(args[2] === "--input-file" ? 4 : 3)];
|
|
255
|
+
}
|
|
256
|
+
if (args[0] === "parallel") {
|
|
257
|
+
if (!(args.length === 2 || args.length === 3 && args[1] === "--input-file"))
|
|
258
|
+
throw new CliInputError("Provide one parallel request as JSON, - or --input-file <path>");
|
|
259
|
+
const source = args[1], text = source === "--input-file" ? await readInput(args[2]) : source === "-" ? await readInput("-") : source;
|
|
260
|
+
const input = agentParallelSchema.parse(metaInput(agentParallelSchema, JSON.parse(text)));
|
|
261
|
+
command = async (client, config) => {
|
|
262
|
+
const result = await new AgentRun(client, env.RELVO_RUN_DIRECTORY, resultFiles(env), walletAutoTopUpFromEnvironment(env, config)).parallel(input);
|
|
263
|
+
exitCode = result.allAccepted ? 0 : 1;
|
|
264
|
+
return result;
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
else if (args.length === 3 && args[0] === "meta") {
|
|
268
|
+
const input = JSON.parse(args[2]);
|
|
269
|
+
if (args[1] === "search") {
|
|
270
|
+
const value = platformMetaSearchSchema.parse(metaInput(platformMetaSearchSchema, input));
|
|
271
|
+
command = client => client.searchMetaTools(value);
|
|
272
|
+
}
|
|
273
|
+
else if (args[1] === "schemas") {
|
|
274
|
+
const value = platformMetaSchemasSchema.parse(metaInput(platformMetaSchemasSchema, input));
|
|
275
|
+
command = client => client.getToolSchemas(value);
|
|
276
|
+
}
|
|
277
|
+
else if (args[1] === "connections") {
|
|
278
|
+
const value = platformMetaConnectionsSchema.parse(metaInput(platformMetaConnectionsSchema, input));
|
|
279
|
+
command = client => client.manageConnections(value);
|
|
280
|
+
}
|
|
281
|
+
else if (args[1] === "execute") {
|
|
282
|
+
const value = platformMetaExecuteSchema.parse(metaInput(platformMetaExecuteSchema, input));
|
|
283
|
+
command = async (client) => { const result = await client.multiExecute(value); if (!result.data.allAccepted)
|
|
284
|
+
exitCode = 1; return result; };
|
|
285
|
+
}
|
|
286
|
+
else if (args[1] === "workbench") {
|
|
287
|
+
const value = platformMetaWorkbenchSchema.parse(metaInput(platformMetaWorkbenchSchema, input));
|
|
288
|
+
command = client => client.remoteWorkbench(value);
|
|
289
|
+
}
|
|
290
|
+
else if (args[1] === "bash") {
|
|
291
|
+
const value = platformMetaBashSchema.parse(metaInput(platformMetaBashSchema, input));
|
|
292
|
+
command = client => client.remoteBash(value);
|
|
293
|
+
}
|
|
294
|
+
else
|
|
295
|
+
throw new CliInputError("Unknown meta tool");
|
|
296
|
+
}
|
|
297
|
+
else if (args.length === 2 && args[0] === "upload" && args[1].trim())
|
|
298
|
+
command = (client) => uploadLocalInput(client, args[1]);
|
|
299
|
+
else if (args[0] === "project" && args[1] === "status") {
|
|
300
|
+
const { values, tokens } = parseArgs({ args: args.slice(2), tokens: true, options: {
|
|
301
|
+
"member-offset": { type: "string" }, "invitation-offset": { type: "string" }, limit: { type: "string" }
|
|
302
|
+
} });
|
|
303
|
+
refuseDuplicates(tokens, "Duplicate project status option");
|
|
304
|
+
const input = platformProjectStatusQuerySchema.parse({ memberOffset: values["member-offset"], invitationOffset: values["invitation-offset"], limit: values.limit });
|
|
305
|
+
command = client => client.projectStatus(input);
|
|
306
|
+
}
|
|
307
|
+
else if (args[0] === "team") {
|
|
308
|
+
const { values, tokens } = parseArgs({ args: args.slice(1), tokens: true, options: {
|
|
309
|
+
"member-offset": { type: "string" }, "invitation-offset": { type: "string" }, limit: { type: "string" }
|
|
310
|
+
} });
|
|
311
|
+
refuseDuplicates(tokens, "Duplicate team option");
|
|
312
|
+
const input = platformTeamQuerySchema.parse({ memberOffset: values["member-offset"], invitationOffset: values["invitation-offset"], limit: values.limit });
|
|
313
|
+
command = client => client.team(input);
|
|
314
|
+
}
|
|
315
|
+
else if (args.length === 1 && args[0] === "project")
|
|
316
|
+
command = (client) => client.project();
|
|
317
|
+
else if (args.length === 1 && args[0] === "usage")
|
|
318
|
+
command = (client) => client.usage();
|
|
319
|
+
else if (args.length === 2 && args[0] === "connect" && args[1].trim() && !args[1].startsWith("-"))
|
|
320
|
+
command = client => client.connectApp({ app: args[1] });
|
|
321
|
+
else if (args[0] === "connections") {
|
|
322
|
+
const { values, tokens } = parseArgs({ args: args.slice(1), tokens: true, options: { provider: { type: "string" }, offset: { type: "string" }, limit: { type: "string" } } });
|
|
323
|
+
refuseDuplicates(tokens, "Duplicate connection option");
|
|
324
|
+
const input = platformConnectionCatalogQuerySchema.parse({ provider: values.provider, offset: values.offset === undefined ? undefined : Number(values.offset), limit: values.limit === undefined ? undefined : Number(values.limit) });
|
|
325
|
+
command = client => client.connections(input);
|
|
326
|
+
}
|
|
327
|
+
else if (args[0] === "tools") {
|
|
328
|
+
const { values, tokens } = parseArgs({ args: args.slice(1), tokens: true, options: {
|
|
329
|
+
query: { type: "string" }, app: { type: "string" }, category: { type: "string" }, provider: { type: "string" }, "available-only": { type: "boolean" }, "include-unavailable": { type: "boolean" }, summary: { type: "boolean" }, "with-schemas": { type: "boolean" }, limit: { type: "string" }, mode: { type: "string" },
|
|
330
|
+
"max-charge-micros": { type: "string" }, "quote-arguments": { type: "string" }, funding: { type: "string" }, "price-unit": { type: "string" }, "sort-by": { type: "string" },
|
|
331
|
+
include: { type: "string" }, exclude: { type: "string" },
|
|
332
|
+
} });
|
|
333
|
+
refuseDuplicates(tokens, "Duplicate discovery option");
|
|
334
|
+
if (values["available-only"] && values["include-unavailable"])
|
|
335
|
+
throw new CliInputError("Conflicting availability filters");
|
|
336
|
+
if (values.summary && values["with-schemas"])
|
|
337
|
+
throw new CliInputError("Conflicting schema options");
|
|
338
|
+
if (values["max-charge-micros"] !== undefined && !values["max-charge-micros"].trim())
|
|
339
|
+
throw new CliInputError("Invalid cost cap");
|
|
340
|
+
const input = toolDiscoveryInputSchema.parse({ query: values.query, app: values.app, category: values.category, provider: values.provider,
|
|
341
|
+
maxChargeMicros: values["max-charge-micros"] === undefined ? undefined : Number(values["max-charge-micros"]),
|
|
342
|
+
quoteArguments: values["quote-arguments"] === undefined ? undefined : jsonOption("--quote-arguments", values["quote-arguments"]),
|
|
343
|
+
funding: values.funding, priceUnit: values["price-unit"], sortBy: values["sort-by"],
|
|
344
|
+
include: values.include === undefined ? undefined : jsonOption("--include", values.include), exclude: values.exclude === undefined ? undefined : jsonOption("--exclude", values.exclude),
|
|
345
|
+
availableOnly: values["include-unavailable"] ? false : true, summary: !values["with-schemas"], limit: values.limit === undefined ? undefined : Number(values.limit), mode: values.mode });
|
|
346
|
+
command = (client) => client.searchTools(input);
|
|
347
|
+
}
|
|
348
|
+
else if (args.length === 2 && args[0] === "tool" && /^[a-z][a-z0-9_]{1,63}$/.test(args[1]))
|
|
349
|
+
command = (client) => client.inspectTool(args[1]);
|
|
350
|
+
else if (args[0] === "executions") {
|
|
351
|
+
const { values, tokens } = parseArgs({ args: args.slice(1), tokens: true, options: { limit: { type: "string" }, before: { type: "string" }, status: { type: "string" }, tool: { type: "string" } } });
|
|
352
|
+
refuseDuplicates(tokens, "Duplicate history option");
|
|
353
|
+
const input = platformExecutionListQuerySchema.parse({ limit: values.limit === undefined ? undefined : Number(values.limit), before: values.before, status: values.status, tool: values.tool });
|
|
354
|
+
command = (client) => client.executions(input);
|
|
355
|
+
}
|
|
356
|
+
else if (args[0] === "result") {
|
|
357
|
+
const id = platformExecutionIdSchema.parse(args[1]);
|
|
358
|
+
const { values, tokens } = parseArgs({ args: args.slice(2), tokens: true, options: { ...READ_OPTIONS, file: { type: "boolean" } } });
|
|
359
|
+
refuseDuplicates(tokens, "Duplicate result option");
|
|
360
|
+
if (values.file && (values.offset !== undefined || values.limit !== undefined || values["max-chars"] !== undefined || values.find !== undefined))
|
|
361
|
+
throw new CliInputError("--file writes the whole path; omit paging options");
|
|
362
|
+
if (values.file || values.find !== undefined || values.pick !== undefined || values.wait !== undefined) {
|
|
363
|
+
const input = agentResultSchema.parse({ id, ...agentRead(values), ...(values.file ? { file: true } : {}) });
|
|
364
|
+
command = async (client) => {
|
|
365
|
+
const result = await new AgentRun(client, env.RELVO_RUN_DIRECTORY, resultFiles(env)).result(input);
|
|
366
|
+
exitCode = "timedOut" in result && result.timedOut ? 3 : result.status === "succeeded" && !missedRequestedOutput(result) ? 0 : 1;
|
|
367
|
+
return result;
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
const selection = toolResultSelectionSchema.parse({ path: values.path === undefined ? undefined : jsonOption("--path", values.path),
|
|
372
|
+
offset: values.offset === undefined ? undefined : Number(values.offset), limit: values.limit === undefined ? undefined : Number(values.limit),
|
|
373
|
+
maxChars: values["max-chars"] === undefined ? undefined : Number(values["max-chars"]),
|
|
374
|
+
fields: values.fields === undefined ? undefined : jsonOption("--fields", values.fields),
|
|
375
|
+
});
|
|
376
|
+
command = async (client) => {
|
|
377
|
+
const result = await client.readExecutionResult(id, selection);
|
|
378
|
+
if (result.data.status && result.data.status !== "succeeded")
|
|
379
|
+
exitCode = ["queued", "running", "waiting"].includes(result.data.status) ? 3 : 1;
|
|
380
|
+
else if (result.data.upstreamRefusal)
|
|
381
|
+
exitCode = 1;
|
|
382
|
+
return result;
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else if (args[0] === "results") {
|
|
387
|
+
const { values, positionals, tokens } = parseArgs({ args: args.slice(1), tokens: true, allowPositionals: true, options: READ_OPTIONS });
|
|
388
|
+
refuseDuplicates(tokens, "Duplicate results option");
|
|
389
|
+
const input = agentResultsSchema.parse({ ids: positionals, ...agentRead(values) });
|
|
390
|
+
command = async (client) => {
|
|
391
|
+
const reply = await new AgentRun(client, env.RELVO_RUN_DIRECTORY, resultFiles(env)).results(input);
|
|
392
|
+
const pending = reply.results.some(item => "status" in item && ["queued", "running", "waiting"].includes(String(item.status)));
|
|
393
|
+
const delivered = reply.results.every(item => "status" in item && item.status === "succeeded" && !missedRequestedOutput(item));
|
|
394
|
+
exitCode = delivered ? 0 : pending ? 3 : 1;
|
|
395
|
+
return reply;
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
else if (args[0] === "wait") {
|
|
399
|
+
const id = platformExecutionIdSchema.parse(args[1]);
|
|
400
|
+
const { values, tokens } = parseArgs({ args: args.slice(2), tokens: true, options: { timeout: { type: "string" }, raw: { type: "boolean" } } });
|
|
401
|
+
refuseDuplicates(tokens, "Duplicate wait option");
|
|
402
|
+
const seconds = values.timeout === undefined ? 480 : Number(values.timeout);
|
|
403
|
+
if (!Number.isInteger(seconds) || seconds < 1 || seconds > 900)
|
|
404
|
+
throw new CliInputError("Invalid wait timeout");
|
|
405
|
+
command = async (client) => {
|
|
406
|
+
const result = values.raw ? await client.waitExecution(id, seconds * 1000) : await client.waitExecutionContext(id, seconds * 1000);
|
|
407
|
+
exitCode = result.timedOut ? 3 : result.data?.status === "succeeded" && !result.data.upstreamRefusal ? 0 : 1;
|
|
408
|
+
return result;
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
else if (["execution", "cancel"].includes(args[0])) {
|
|
412
|
+
const id = platformExecutionIdSchema.parse(args[1]);
|
|
413
|
+
const { values, tokens } = parseArgs({ args: args.slice(2), tokens: true, options: { raw: { type: "boolean" } } });
|
|
414
|
+
if (tokens.length > 1)
|
|
415
|
+
throw new CliInputError("Duplicate output option");
|
|
416
|
+
command = async (client) => {
|
|
417
|
+
if (args[0] === "execution")
|
|
418
|
+
return values.raw ? client.execution(id) : client.executionContext(id);
|
|
419
|
+
const result = await client.cancelExecution(id);
|
|
420
|
+
return values.raw ? result : executionReceiptContext(result);
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
else if (args.length === 5 && args[0] === "download" && args[3] === "--out" && args[4].trim()) {
|
|
424
|
+
const id = platformExecutionIdSchema.parse(args[1]);
|
|
425
|
+
const artifactId = platformArtifactIdSchema.parse(args[2]);
|
|
426
|
+
command = (client) => downloadArtifact(client, id, artifactId, args[4], transport, cliCommand);
|
|
427
|
+
}
|
|
428
|
+
else if (args.length === 3 && args[0] === "artifact-url") {
|
|
429
|
+
const id = platformExecutionIdSchema.parse(args[1]);
|
|
430
|
+
const artifactId = platformArtifactIdSchema.parse(args[2]);
|
|
431
|
+
command = (client) => client.artifactDownload(id, artifactId);
|
|
432
|
+
}
|
|
433
|
+
else if ((args[0] === "run" && args.length >= 3) || (args[0] === "resume" && args.length >= 2)) {
|
|
434
|
+
const resume = args[0] === "resume";
|
|
435
|
+
const { values, tokens } = parseArgs({ args: args.slice(resume ? 2 : 3), tokens: true, options: { ...READ_OPTIONS,
|
|
436
|
+
"maximum-charge-micros": { type: "string" }, "idempotency-key": { type: "string" }, provider: { type: "string" }, session: { type: "string" },
|
|
437
|
+
file: { type: "boolean" }, "accept-external-billing": { type: "boolean" }, "decline-external-billing": { type: "boolean" },
|
|
438
|
+
} });
|
|
439
|
+
refuseDuplicates(tokens, "Duplicate run option");
|
|
440
|
+
if (values["accept-external-billing"] && values["decline-external-billing"])
|
|
441
|
+
throw new CliInputError("Conflicting external billing options");
|
|
442
|
+
const { waitSeconds, pick, ...selection } = agentRead(values);
|
|
443
|
+
const view = { waitSeconds, pick, file: values.file, selection };
|
|
444
|
+
if (resume) {
|
|
445
|
+
if (values["maximum-charge-micros"] || values["idempotency-key"] || values.provider || values.session || values["accept-external-billing"] || values["decline-external-billing"])
|
|
446
|
+
throw new CliInputError("Resume retains the original purchase");
|
|
447
|
+
const input = agentResumeSchema.parse({ runId: args[1], ...view });
|
|
448
|
+
command = async (client, config) => {
|
|
449
|
+
const result = await new AgentRun(client, env.RELVO_RUN_DIRECTORY, resultFiles(env), walletAutoTopUpFromEnvironment(env, config)).resume(input);
|
|
450
|
+
exitCode = "timedOut" in result && result.timedOut ? 3 : result.status === "succeeded" && !missedRequestedOutput(result) ? 0 : 1;
|
|
451
|
+
return result;
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const maximum = values["maximum-charge-micros"];
|
|
456
|
+
if (maximum !== undefined && !/^\d+$/.test(maximum))
|
|
457
|
+
throw new CliInputError("Invalid run ceiling");
|
|
458
|
+
const input = agentRunSchema.parse({ tool: args[1], arguments: jsonOption("<arguments-json>", args[2]), ...(maximum === undefined ? {} : { maximumChargeMicros: Number(maximum) }),
|
|
459
|
+
idempotencyKey: values["idempotency-key"], provider: values.provider, sessionId: values.session,
|
|
460
|
+
...(values["decline-external-billing"] ? { externalBillingAccepted: false } : {}), ...view });
|
|
461
|
+
command = async (client, config) => {
|
|
462
|
+
const result = await new AgentRun(client, env.RELVO_RUN_DIRECTORY, resultFiles(env), walletAutoTopUpFromEnvironment(env, config)).run(input);
|
|
463
|
+
exitCode = "status" in result ? result.timedOut ? 3 : result.status === "succeeded" && !missedRequestedOutput(result) ? 0 : 1 : 1;
|
|
464
|
+
return result;
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else if (args.length >= 3 && args[0] === "quote") {
|
|
469
|
+
const { values, tokens } = parseArgs({ args: args.slice(3), tokens: true, options: { provider: { type: "string" }, session: { type: "string" },
|
|
470
|
+
"relevance-check": { type: "string" }, "maximum-charge-micros": { type: "string" } } });
|
|
471
|
+
refuseDuplicates(tokens, "Duplicate quote option");
|
|
472
|
+
const maximum = values["maximum-charge-micros"];
|
|
473
|
+
if (maximum !== undefined && !/^\d+$/.test(maximum))
|
|
474
|
+
throw new CliInputError("Invalid quote cap");
|
|
475
|
+
const input = platformQuoteRequestSchema.parse({ tool: args[1], arguments: jsonOption("<arguments-json>", args[2]), sessionId: values.session, ...(values.provider === undefined ? {} : { provider: values.provider }),
|
|
476
|
+
...(values["relevance-check"] === undefined ? {} : { relevanceCheck: values["relevance-check"] }), ...(maximum === undefined ? {} : { maximumChargeMicros: Number(maximum) }) });
|
|
477
|
+
command = (client) => client.quote(input);
|
|
478
|
+
}
|
|
479
|
+
else if (args.length >= 3 && args[0] === "execute") {
|
|
480
|
+
const { values, tokens } = parseArgs({ args: args.slice(3), tokens: true, options: {
|
|
481
|
+
"maximum-charge-micros": { type: "string" }, "price-version": { type: "string" }, "idempotency-key": { type: "string" }, provider: { type: "string" }, session: { type: "string" }, raw: { type: "boolean" },
|
|
482
|
+
"relevance-check": { type: "string" },
|
|
483
|
+
} });
|
|
484
|
+
refuseDuplicates(tokens, "Duplicate execution option");
|
|
485
|
+
const maximum = values["maximum-charge-micros"], idempotencyKey = values["idempotency-key"];
|
|
486
|
+
if (!maximum || !/^\d+$/.test(maximum) || !idempotencyKey || !platformIdempotencyKeySchema.safeParse(idempotencyKey).success)
|
|
487
|
+
throw new CliInputError("Invalid execution confirmation");
|
|
488
|
+
const input = platformCreateExecutionSchema.parse({ tool: args[1], arguments: jsonOption("<arguments-json>", args[2]), sessionId: values.session, maximumChargeMicros: Number(maximum), priceVersion: values["price-version"], ...(values.provider === undefined ? {} : { provider: values.provider }),
|
|
489
|
+
...(values["relevance-check"] === undefined ? {} : { relevanceCheck: values["relevance-check"] }) });
|
|
490
|
+
command = async (client) => {
|
|
491
|
+
const result = await client.createExecution(input, idempotencyKey);
|
|
492
|
+
return values.raw ? result : executionReceiptContext(result);
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
else if (isTeamResource(args[0]))
|
|
496
|
+
command = await teamResourceCommand(args, source => readInput(source));
|
|
497
|
+
else
|
|
498
|
+
throw new CliInputError("Invalid command");
|
|
499
|
+
}
|
|
500
|
+
catch (failure) {
|
|
501
|
+
error(`${cliInputReason(failure)}. Nothing was sent. Run ${cliCommand} ${surface.commandHelp(args[0]) ? `help ${args[0]}` : "--help"}.`);
|
|
502
|
+
return 2;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
if (projectId && !profile)
|
|
506
|
+
throw new Error(`--project needs a saved login. Run ${cliCommand} setup first.`);
|
|
507
|
+
const config = projectId && profile ? await localProjectConfig(profile, projectId, env, "cli", transport)
|
|
508
|
+
: profile ? await runtimeConfig(env, "cli", profile) : configFromEnvironment(env, "cli", surface.distribution);
|
|
509
|
+
const header = surface.clientNameHeader, client = env.CLAUDECODE === "1" ? "claude-code" : `${cliCommand}-cli`;
|
|
510
|
+
const result = await command(new PlatformClient(config, header ? (input, init) => transport(input, { ...init, headers: { ...init?.headers, [header]: client } }) : transport), config);
|
|
511
|
+
output(JSON.stringify(result));
|
|
512
|
+
return exitCode;
|
|
513
|
+
}
|
|
514
|
+
catch (failure) {
|
|
515
|
+
error(failure instanceof Error ? failure.message : `${name} request failed.`);
|
|
516
|
+
return 1;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
export async function runRelvoCli(args, env, output, error, transport = fetch, stdin = process.stdin, options = {}) {
|
|
520
|
+
if (args[0] === "mcp") {
|
|
521
|
+
try {
|
|
522
|
+
const { values } = parseArgs({ args: args.slice(1), options: { profile: { type: "string" }, mode: { type: "string" }, project: { type: "string" } } });
|
|
523
|
+
if (!values.profile)
|
|
524
|
+
throw new Error("relvo mcp requires the managed --profile path.");
|
|
525
|
+
const [{ startPlatformMcp }, { deactivateProjectFiles }, { projectSyncFactory }] = await Promise.all([
|
|
526
|
+
import("../mcp/runtime.js"), import("./project-files.js"), import("./project-sync.js")
|
|
527
|
+
]);
|
|
528
|
+
const profileHome = dirname(dirname(dirname(values.profile)));
|
|
529
|
+
await startPlatformMcp(options.version, values.profile, values.mode, values.project, id => deactivateProjectFiles(id, env, profileHome), projectSyncFactory(env, profileHome));
|
|
530
|
+
return process.exitCode ? 1 : 0;
|
|
531
|
+
}
|
|
532
|
+
catch {
|
|
533
|
+
error("Relvo MCP could not start. Run relvo doctor to inspect the managed connection.");
|
|
534
|
+
return 1;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const localFailure = (failure, fallback) => {
|
|
538
|
+
if (!isCliInputFailure(failure)) {
|
|
539
|
+
error(failure instanceof Error ? failure.message : fallback);
|
|
540
|
+
return 1;
|
|
541
|
+
}
|
|
542
|
+
error(`${cliInputReason(failure).replace(/\.$/u, "")}. Nothing was changed. Run relvo help ${args[0]}.`);
|
|
543
|
+
return 2;
|
|
544
|
+
};
|
|
545
|
+
if (args[0] === "wallet" && !args.some((arg) => ["--help", "-h"].includes(arg))) {
|
|
546
|
+
try {
|
|
547
|
+
await (await import("./wallet.js")).runWalletCommand(args, env, output, transport, options);
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
550
|
+
catch (failure) {
|
|
551
|
+
return localFailure(failure, "Relvo wallet command failed.");
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const local = ["setup", "login", "doctor", "logout", "skills"].includes(args[0]) || args[0] === "team-skills" && ["sync", "push", "check", "apply", "dismiss", "auto-sync"].includes(args[1]) || args[0] === "project" && ["list", "add", "use"].includes(args[1]);
|
|
555
|
+
if (local && !args.some((arg) => ["--help", "-h"].includes(arg))) {
|
|
556
|
+
try {
|
|
557
|
+
await (await import("./setup.js")).runLocalCommand(args, env, output, transport, options);
|
|
558
|
+
return 0;
|
|
559
|
+
}
|
|
560
|
+
catch (failure) {
|
|
561
|
+
return localFailure(failure, "Relvo local setup failed.");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return runCli(args, env, output, error, transport, stdin, localProfilePath(options.home));
|
|
565
|
+
}
|