nosana-mcp 0.2.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.
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/cli/commands/account.d.ts +7 -0
- package/dist/cli/commands/account.js +108 -0
- package/dist/cli/commands/account.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +3 -0
- package/dist/cli/commands/deploy.js +311 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/gpus.d.ts +3 -0
- package/dist/cli/commands/gpus.js +38 -0
- package/dist/cli/commands/gpus.js.map +1 -0
- package/dist/cli/commands/job.d.ts +3 -0
- package/dist/cli/commands/job.js +31 -0
- package/dist/cli/commands/job.js.map +1 -0
- package/dist/cli/commands/run.d.ts +33 -0
- package/dist/cli/commands/run.js +365 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/templates.d.ts +3 -0
- package/dist/cli/commands/templates.js +115 -0
- package/dist/cli/commands/templates.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +45 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/client.d.ts +23 -0
- package/dist/core/client.js +38 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +52 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/credits.d.ts +9 -0
- package/dist/core/credits.js +9 -0
- package/dist/core/credits.js.map +1 -0
- package/dist/core/deploy.d.ts +146 -0
- package/dist/core/deploy.js +400 -0
- package/dist/core/deploy.js.map +1 -0
- package/dist/core/format.d.ts +25 -0
- package/dist/core/format.js +144 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/markets.d.ts +74 -0
- package/dist/core/markets.js +146 -0
- package/dist/core/markets.js.map +1 -0
- package/dist/core/templates.d.ts +61 -0
- package/dist/core/templates.js +190 -0
- package/dist/core/templates.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +73 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tools.d.ts +52 -0
- package/dist/mcp/tools.js +994 -0
- package/dist/mcp/tools.js.map +1 -0
- package/docs/CLI.md +144 -0
- package/docs/COST.md +32 -0
- package/docs/MINIMAX-H3.md +79 -0
- package/examples/hello-world.json +18 -0
- package/examples/minimax-h3-i2v-32gb.json +56 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Swatantra Sohni
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Nosana MCP
|
|
2
|
+
|
|
3
|
+
**Let your AI agent rent a GPU and deploy models on [Nosana](https://nosana.com).**
|
|
4
|
+
Nosana MCP is a [Model Context Protocol](https://modelcontextprotocol.io) server that gives Claude Code, Claude Desktop, Cursor, Codex, OpenClaw and any other MCP client the tools to browse Nosana's ready-to-run templates (MiniMax H3 video, Qwen and Gemma LLMs, ComfyUI, Jupyter, VS Code), compare GPU markets by live price and availability, estimate the cost, deploy, and hand back the running endpoint. Everything is paid with Nosana account credits through a single API key. No wallet, no tokens.
|
|
5
|
+
|
|
6
|
+
> "Deploy MiniMax H3 on an RTX 5090 and give me the ComfyUI link."
|
|
7
|
+
> The agent checks your credits, picks the 32 GB variant, shows you the price (about 0.40 credits per hour on a 5090), waits for your yes, deploys, polls until ComfyUI answers, and returns the URL. When you are done: "stop it".
|
|
8
|
+
|
|
9
|
+
A companion CLI, `nosana-deploy`, ships in the same package for humans, scripts and CI. See [docs/CLI.md](docs/CLI.md).
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
### 1. Credits and an API key
|
|
14
|
+
|
|
15
|
+
1. Sign in at [deploy.nosana.com](https://deploy.nosana.com).
|
|
16
|
+
2. **Billing**: add credits with a card or crypto.
|
|
17
|
+
3. **Account > API Keys > Create Key**. Copy it once.
|
|
18
|
+
|
|
19
|
+
### 2. Add the server to your client
|
|
20
|
+
|
|
21
|
+
Claude Code:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
claude mcp add nosana --env NOSANA_API_KEY=nos_your_key -- npx -y nosana-mcp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Claude Desktop, Cursor, Windsurf and most other clients take the same JSON:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"nosana": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "nosana-mcp"],
|
|
35
|
+
"env": { "NOSANA_API_KEY": "nos_your_key" }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Codex CLI:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
codex mcp add nosana --env NOSANA_API_KEY=nos_your_key -- npx -y nosana-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Running from a clone instead of npm:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/SohniSwatantra/nosana-mcp.git && cd nosana-mcp
|
|
51
|
+
npm install && npm run build
|
|
52
|
+
# then point your client at: node /absolute/path/to/nosana-mcp/dist/mcp/index.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Instead of the environment variable you can run `nosana-deploy login` once; the server also reads the key that command stores in `~/.config/nosana-deploy/config.json`.
|
|
56
|
+
|
|
57
|
+
### 3. Ask
|
|
58
|
+
|
|
59
|
+
- "Deploy Qwen 3.6 27B on the cheapest idle GPU and give me the OpenAI-compatible URL."
|
|
60
|
+
- "Deploy MiniMax H3 image-to-video. If no 5090 is idle, show me the alternatives."
|
|
61
|
+
- "What is running right now and what does it cost per hour?" / "Stop everything."
|
|
62
|
+
|
|
63
|
+
The agent must show you the estimate and get a yes before it can call `create_deployment`. Bundled prompts: `deploy_template`, `deploy_llm`, `deploy_comfy`, `deploy_minimax_h3`, `stop_when_done`.
|
|
64
|
+
|
|
65
|
+
After adding or changing the server configuration, restart the client or reconnect its MCP servers (Claude Code: `/mcp`); tools are discovered at session start.
|
|
66
|
+
|
|
67
|
+
## Tools
|
|
68
|
+
|
|
69
|
+
Every tool returns one JSON object, both as text and as MCP `structuredContent`: `{ok, message, next_tool, next_args, ...}`. `ok` is false only when the tool itself failed; deployment outcomes live in `outcome`. Agents never have to parse prose.
|
|
70
|
+
|
|
71
|
+
| Tool | What it does | Spends credits |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `doctor` | Checks the key, credits and every Nosana API; returns the exact fix (install command, restart hint). | no |
|
|
74
|
+
| `get_balance` | Assigned, reserved, settled and available credits. | no |
|
|
75
|
+
| `list_templates` / `get_template` | Catalog with kind (comfyui, llm, notebook, ide), VRAM from the template itself, variants, hardware notes, typical boot time. | no |
|
|
76
|
+
| `list_gpus` | Markets in buyer buckets: `ready_now` (fits, idle host), `fits_but_queued`, `idle_with_risk`, `unsupported`. Prices per hour include the fee and match the dashboard. | no |
|
|
77
|
+
| `recommend_plan` | One call from a workload ("minimax-h3/i2v-32gb", "qwen3-6-27b", "gemma3 12b", or a job definition) to the cheapest ready GPU, cost, similar deployments already running, and exact `create_deployment` arguments. Never picks a queued or unsupported GPU by itself. | no |
|
|
78
|
+
| `estimate_deployment` | Dry run of `create_deployment` with the same arguments, including `gpu: "auto"`. | no |
|
|
79
|
+
| `create_deployment` | Creates and starts. Needs `confirm=true`. Refuses to queue without `accept_queue`, to duplicate a running workload without `allow_duplicate`, and to use unfit hardware or overspend without `force`. | **yes** |
|
|
80
|
+
| `wait_for_deployment` | Watches up to 45 s per call (MCP clients time out at 60 s). Returns `online` with URLs and usage, `completed` with logs, `stopped` (user or timeout, not an error), `failed` with the scheduler error, or `pending` with phase (`queued`, `starting`, `initializing`), elapsed time and `poll_after_seconds`. | no |
|
|
81
|
+
| `get_deployment` / `list_deployments` | Status, phase, endpoints with a real readiness probe, price per hour, recent jobs and events. | no |
|
|
82
|
+
| `get_endpoint_usage` | How to use the running service: ComfyUI `/prompt` API, OpenAI-compatible base URL for Ollama/vLLM (`/v1/models`, `/v1/chat/completions`), Jupyter/VS Code URL, raw ports for custom jobs. | no |
|
|
83
|
+
| `get_job_result` / `get_deployment_events` | Logs and the scheduler's event log. | no |
|
|
84
|
+
| `stop_deployment` | Stops jobs; billing stops with them. | ends spending |
|
|
85
|
+
| `start_deployment` / `extend_deployment` | Restart a stopped deployment (needs `confirm=true`) or change its timeout. | yes |
|
|
86
|
+
|
|
87
|
+
Resources: `nosana://templates`, `nosana://gpus`. Prompts: `deploy_template`, `deploy_llm`, `deploy_comfy`, `deploy_minimax_h3`, `stop_when_done`.
|
|
88
|
+
|
|
89
|
+
### The happy path an agent follows
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
recommend_plan("qwen3-6-27b") -> ready_now GPU, cost, create_args
|
|
93
|
+
create_deployment(create_args, confirm=true) after the user says yes
|
|
94
|
+
wait_for_deployment(id) x N -> pending(phase) ... online
|
|
95
|
+
get_endpoint_usage(id) -> OPENAI_BASE_URL, /v1/models, curl example
|
|
96
|
+
stop_deployment(id) when the user is done
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Guard rails built in
|
|
100
|
+
|
|
101
|
+
- **Confirmation before spending.** `create_deployment` and `start_deployment` do nothing without `confirm=true`; their descriptions tell the agent to show the estimate first, and MCP annotations mark them non-read-only so clients prompt for approval.
|
|
102
|
+
- **No silent queueing.** With `gpu: "auto"` only a GPU that fits *and* has an idle host is chosen. If none is ready, the response lists `fits_but_queued` and `idle_with_risk` and asks for a decision (`accept_queue` or `force`). Unsupported hardware (a non-Blackwell card for MiniMax H3) is never offered, because it would download 45 GB of weights and then fail.
|
|
103
|
+
- **No accidental duplicates.** Creating a workload that is already running is refused unless `allow_duplicate=true`.
|
|
104
|
+
- **60-minute minimum.** Nosana only schedules credit-paid jobs of 3600 seconds or more; shorter values are rejected before anything is created. Workloads that download weights default to 120 minutes.
|
|
105
|
+
- **Real readiness.** Nosana marks an endpoint online when the host tunnel is up while the container may still be loading. The server probes the service itself (`/` for web UIs, `/api/tags` or `/v1/models` for LLMs) before reporting `online`.
|
|
106
|
+
- **Stopped is not failed.** A deployment stopped by the user or its timeout reports `outcome: "stopped"`, so agents do not "fix" it by restarting and paying the boot again.
|
|
107
|
+
- **Fail fast, retry the transient.** Repeated scheduler errors end the wait with the error text; transient 5xx and network blips are retried with backoff.
|
|
108
|
+
- **Short calls.** No tool blocks longer than 45 seconds. Pending results carry `poll_after_seconds` and `next_args` so the agent can loop safely.
|
|
109
|
+
|
|
110
|
+
## Costs to expect
|
|
111
|
+
|
|
112
|
+
Prices come from the live market list and include the network fee, so they match deploy.nosana.com. Every plan and every online result carries `usd_per_hour`, `estimated_credits`, `boot_minutes_typical` and the billing rules (idle time bills, there is no pause, stopping ends billing, restarting pays the boot again). See [docs/COST.md](docs/COST.md) for numbers from real runs.
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm install
|
|
118
|
+
npm run build
|
|
119
|
+
npm run test:mcp # read-only smoke test with assertions against the live API (needs NOSANA_API_KEY)
|
|
120
|
+
SMOKE_DEPLOY=1 npm run test:mcp # also deploys hello-world once (about 0.05 credits)
|
|
121
|
+
npm run inspect # MCP Inspector UI against the built server
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Layout: `src/core` (Nosana Kit wrappers: templates, markets, deployments, cost, readiness), `src/mcp` (server and tools), `src/cli` (the `nosana-deploy` command). Both surfaces share the core, so a fix in one place fixes both.
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT. Not affiliated with Nosana; built on the public `@nosana/kit` SDK and API.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { type CreditsBalance, type GlobalOptions } from '../../core/client.js';
|
|
3
|
+
import { availableCredits } from '../../core/credits.js';
|
|
4
|
+
export { availableCredits };
|
|
5
|
+
export declare function balanceTable(balance: CreditsBalance): string;
|
|
6
|
+
export declare function loginInteractive(options: GlobalOptions, provided?: string): Promise<string>;
|
|
7
|
+
export declare function registerAccountCommands(program: Command, globals: () => GlobalOptions): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { password } from '@inquirer/prompts';
|
|
2
|
+
import { CliError, createClient } from '../../core/client.js';
|
|
3
|
+
import { CONFIG_FILE, clearConfig, isInteractive, maskKey, readConfig, resolveApiKey, writeConfig } from '../../core/config.js';
|
|
4
|
+
import { formatError, pageSize, printJson, sym, table, withTimeout } from '../../core/format.js';
|
|
5
|
+
import { availableCredits, summarizeBalance } from '../../core/credits.js';
|
|
6
|
+
export { availableCredits };
|
|
7
|
+
export function balanceTable(balance) {
|
|
8
|
+
const b = summarizeBalance(balance);
|
|
9
|
+
return table(['Assigned', 'Reserved', 'Settled', 'Available'], [[b.assigned.toFixed(3), b.reserved.toFixed(3), b.settled.toFixed(3), b.available.toFixed(3)]]);
|
|
10
|
+
}
|
|
11
|
+
export async function loginInteractive(options, provided) {
|
|
12
|
+
let key = provided?.trim();
|
|
13
|
+
if (!key) {
|
|
14
|
+
if (!isInteractive())
|
|
15
|
+
throw new CliError('Pass --key <apiKey> or set NOSANA_API_KEY when not running in a terminal.', 2);
|
|
16
|
+
console.log('Create an API key at https://deploy.nosana.com (Account > API Keys) and paste it below.');
|
|
17
|
+
key = (await password({ message: 'Nosana API key', mask: '*' })).trim();
|
|
18
|
+
}
|
|
19
|
+
if (!key)
|
|
20
|
+
throw new CliError('Empty API key.', 2);
|
|
21
|
+
const client = createClient(options, key);
|
|
22
|
+
let balance;
|
|
23
|
+
try {
|
|
24
|
+
balance = await client.api.credits.balance();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new CliError(`Nosana rejected the API key: ${formatError(error)}`);
|
|
28
|
+
}
|
|
29
|
+
writeConfig({ ...readConfig(), apiKey: key, network: options.network });
|
|
30
|
+
console.log(`${sym.ok} API key ${maskKey(key)} verified and saved to ${CONFIG_FILE}`);
|
|
31
|
+
console.log(balanceTable(balance));
|
|
32
|
+
return key;
|
|
33
|
+
}
|
|
34
|
+
export function registerAccountCommands(program, globals) {
|
|
35
|
+
program
|
|
36
|
+
.command('login')
|
|
37
|
+
.description('Verify a Nosana API key and store it in ~/.config/nosana-deploy/config.json.')
|
|
38
|
+
.option('--key <apiKey>', 'API key (prompted when omitted)')
|
|
39
|
+
.action(async (cmd) => {
|
|
40
|
+
const options = globals();
|
|
41
|
+
await loginInteractive(options, cmd.key ?? options.apiKey);
|
|
42
|
+
});
|
|
43
|
+
program
|
|
44
|
+
.command('logout')
|
|
45
|
+
.description('Remove the stored API key.')
|
|
46
|
+
.action(() => {
|
|
47
|
+
console.log(clearConfig() ? `${sym.ok} Removed ${CONFIG_FILE}` : `${sym.info} No stored credentials at ${CONFIG_FILE}`);
|
|
48
|
+
});
|
|
49
|
+
program
|
|
50
|
+
.command('balance')
|
|
51
|
+
.description('Show the credit balance of the account behind the API key.')
|
|
52
|
+
.action(async () => {
|
|
53
|
+
const options = globals();
|
|
54
|
+
const client = createClient(options);
|
|
55
|
+
const balance = await client.api.credits.balance();
|
|
56
|
+
if (options.json) {
|
|
57
|
+
printJson({ ...balance, availableCredits: availableCredits(balance) });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
console.log(balanceTable(balance));
|
|
61
|
+
console.log(`\nTop up at https://deploy.nosana.com (Billing) with a card or crypto.`);
|
|
62
|
+
});
|
|
63
|
+
program
|
|
64
|
+
.command('doctor')
|
|
65
|
+
.description('Check Node.js, the API key, and connectivity to every Nosana API this CLI uses.')
|
|
66
|
+
.action(async () => {
|
|
67
|
+
const options = globals();
|
|
68
|
+
const checks = [];
|
|
69
|
+
const run = async (name, critical, fn) => {
|
|
70
|
+
try {
|
|
71
|
+
checks.push({ name, ok: true, detail: await fn(), critical });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
checks.push({ name, ok: false, detail: formatError(error), critical });
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const major = Number(process.versions.node.split('.')[0]);
|
|
78
|
+
checks.push({ name: 'Node.js', ok: major >= 20, detail: `v${process.versions.node}${major >= 20 ? '' : ' (need 20 or newer)'}`, critical: true });
|
|
79
|
+
const resolved = resolveApiKey(options.apiKey);
|
|
80
|
+
checks.push({
|
|
81
|
+
name: 'API key',
|
|
82
|
+
ok: Boolean(resolved),
|
|
83
|
+
detail: resolved ? `${maskKey(resolved.key)} (from ${resolved.source})` : 'missing: run `nosana-deploy login`',
|
|
84
|
+
critical: true,
|
|
85
|
+
});
|
|
86
|
+
if (resolved) {
|
|
87
|
+
const client = createClient(options, resolved.key);
|
|
88
|
+
await run('Credits API', true, async () => `${availableCredits(await client.api.credits.balance()).toFixed(3)} credits available`);
|
|
89
|
+
await run('Markets API', true, async () => `${(await client.api.markets.list()).length} GPU markets`);
|
|
90
|
+
await run('Templates API', true, async () => `${(await client.api.templates.list()).length} templates`);
|
|
91
|
+
await run('Host availability', false, async () => `${(await client.api.hosts.getQueuedNodes()).length} idle hosts network-wide`);
|
|
92
|
+
await run('Deployments API', true, async () => `${(await client.api.deployments.list({ limit: pageSize(10) })).total_items} deployments on this account`);
|
|
93
|
+
await run('Solana RPC (optional, for --queue)', false, async () => `${(await withTimeout(client.jobs.markets(), 15_000, 'RPC read')).length} on-chain markets`);
|
|
94
|
+
}
|
|
95
|
+
if (options.json) {
|
|
96
|
+
printJson(checks);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
for (const check of checks) {
|
|
100
|
+
const marker = check.ok ? sym.ok : check.critical ? sym.err : sym.warn;
|
|
101
|
+
console.log(`${marker} ${check.name.padEnd(36)} ${check.detail}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (checks.some((c) => !c.ok && c.critical))
|
|
105
|
+
throw new CliError('Some critical checks failed.');
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../../../src/cli/commands/account.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { confirm } from '@inquirer/prompts';
|
|
4
|
+
import { validateJobDefinition } from '@nosana/kit';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
import { CliError, createClient } from '../../core/client.js';
|
|
7
|
+
import { isInteractive } from '../../core/config.js';
|
|
8
|
+
import { dashboardUrl, deploymentsTable, explorerJobUrl, formatJobResult, normalizeJobState, summarizeDeployment, waitForDeployment } from '../../core/deploy.js';
|
|
9
|
+
import { fmtDate, pageSize, parseIntStrict, printJson, short, sleep, statusColor, sym, table } from '../../core/format.js';
|
|
10
|
+
import { addDeployFlags, DEFAULT_WAIT_MINUTES, runDeployFlow } from './run.js';
|
|
11
|
+
const DELETABLE_STATUSES = new Set(['STOPPED', 'ERROR', 'INSUFFICIENT_FUNDS', 'ARCHIVED']);
|
|
12
|
+
async function confirmDestructive(action, id, yes) {
|
|
13
|
+
if (yes)
|
|
14
|
+
return;
|
|
15
|
+
if (!isInteractive())
|
|
16
|
+
throw new CliError(`Add --yes to ${action} ${id} without a prompt.`, 2);
|
|
17
|
+
const ok = await confirm({ message: `Really ${action} deployment ${id}?`, default: false });
|
|
18
|
+
if (!ok)
|
|
19
|
+
throw new CliError('Aborted.', 130);
|
|
20
|
+
}
|
|
21
|
+
async function watch(client, id, minutes, options) {
|
|
22
|
+
console.log(`Watching ${id} for up to ${minutes} min. Ctrl+C only stops watching.`);
|
|
23
|
+
const outcome = await waitForDeployment(client, id, { timeoutMinutes: minutes, network: options.network, quiet: Boolean(options.json) });
|
|
24
|
+
const dep = outcome.deployment;
|
|
25
|
+
if (options.json) {
|
|
26
|
+
printJson({ outcome: outcome.kind, deployment: dep.id, status: dep.status, endpoints: dep.endpoints, ...(outcome.kind === 'online' ? { readyUrls: outcome.readyUrls } : {}) });
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.log();
|
|
30
|
+
if (outcome.kind === 'online') {
|
|
31
|
+
console.log(`${sym.ok} Online and answering:`);
|
|
32
|
+
for (const e of dep.endpoints.filter((x) => outcome.readyUrls.includes(x.url)))
|
|
33
|
+
console.log(` ${e.url} (${e.opId}:${e.port})`);
|
|
34
|
+
}
|
|
35
|
+
else if (outcome.kind === 'completed') {
|
|
36
|
+
console.log(`${sym.ok} Job ${outcome.job} completed. ${explorerJobUrl(outcome.job, options.network)}`);
|
|
37
|
+
try {
|
|
38
|
+
for (const line of formatJobResult((await dep.getJob(outcome.job)).jobResult))
|
|
39
|
+
console.log(` ${line}`);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
/* results may lag */
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (outcome.kind === 'stopped') {
|
|
46
|
+
console.log(`${sym.warn} Deployment is ${statusColor(dep.status)}: stopped by you or by its timeout, not failed.`);
|
|
47
|
+
}
|
|
48
|
+
else if (outcome.kind === 'failed') {
|
|
49
|
+
if (outcome.error)
|
|
50
|
+
console.log(`${sym.err} Scheduler error: ${outcome.error}`);
|
|
51
|
+
console.log(`${sym.err} Deployment is ${statusColor(outcome.reason)}. See: nosana-deploy deploy events ${dep.id}${outcome.error ? ` and stop it with: nosana-deploy deploy stop ${dep.id}` : ''}`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.log(`${sym.warn} Watch window elapsed; deployment status is ${statusColor(dep.status)}. Run the command again to keep watching.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (outcome.kind === 'failed')
|
|
58
|
+
throw new CliError(`Deployment ${dep.id} is ${dep.status}.`);
|
|
59
|
+
}
|
|
60
|
+
export function registerDeployCommands(program, globals) {
|
|
61
|
+
const deploy = program.command('deploy').alias('deployments').description('Create and manage deployments (the same objects you see on deploy.nosana.com).');
|
|
62
|
+
addDeployFlags(deploy
|
|
63
|
+
.command('create')
|
|
64
|
+
.description('Create a deployment from flags (non-interactive). Add --start to start it and --yes to skip the confirmation.'), { start: false }).action(async (flags) => runDeployFlow(globals(), flags, false, { start: false }));
|
|
65
|
+
deploy
|
|
66
|
+
.command('list', { isDefault: true })
|
|
67
|
+
.description('List deployments on this account.')
|
|
68
|
+
.option('--limit <count>', 'Page size (10, 20, 50 or 100)', '20')
|
|
69
|
+
.option('--status <status>', 'Filter by status, e.g. RUNNING or STOPPED,ERROR')
|
|
70
|
+
.option('--search <text>', 'Filter by partial id or name')
|
|
71
|
+
.action(async (cmd) => {
|
|
72
|
+
const options = globals();
|
|
73
|
+
const client = createClient(options);
|
|
74
|
+
const result = await client.api.deployments.list({
|
|
75
|
+
limit: pageSize(parseIntStrict(cmd.limit, '--limit')),
|
|
76
|
+
...(cmd.status ? { status: cmd.status.toUpperCase() } : {}),
|
|
77
|
+
...(cmd.search ? { search: cmd.search } : {}),
|
|
78
|
+
});
|
|
79
|
+
const deployments = result.deployments;
|
|
80
|
+
if (options.json) {
|
|
81
|
+
printJson({ total_items: result.total_items, deployments });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!deployments.length) {
|
|
85
|
+
console.log('No deployments found. Create one with `nosana-deploy run`.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(deploymentsTable(deployments));
|
|
89
|
+
console.log(`\n${deployments.length} of ${result.total_items} deployments shown.`);
|
|
90
|
+
});
|
|
91
|
+
deploy
|
|
92
|
+
.command('get')
|
|
93
|
+
.alias('show')
|
|
94
|
+
.description('Show one deployment with its endpoints.')
|
|
95
|
+
.argument('<id>', 'Deployment id')
|
|
96
|
+
.action(async (id) => {
|
|
97
|
+
const options = globals();
|
|
98
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
99
|
+
if (options.json)
|
|
100
|
+
printJson(dep);
|
|
101
|
+
else
|
|
102
|
+
console.log(summarizeDeployment(dep, options.network).join('\n'));
|
|
103
|
+
});
|
|
104
|
+
deploy
|
|
105
|
+
.command('start')
|
|
106
|
+
.description('Start a draft or stopped deployment.')
|
|
107
|
+
.argument('<id>', 'Deployment id')
|
|
108
|
+
.option('--wait', 'Watch until the workload is online or the job completes')
|
|
109
|
+
.option('--wait-timeout <minutes>', 'How long to watch', String(DEFAULT_WAIT_MINUTES))
|
|
110
|
+
.action(async (id, cmd) => {
|
|
111
|
+
const options = globals();
|
|
112
|
+
const client = createClient(options);
|
|
113
|
+
const dep = (await client.api.deployments.get(id));
|
|
114
|
+
await dep.start();
|
|
115
|
+
console.log(`${sym.ok} Started ${id}. Dashboard: ${dashboardUrl(id, options.network)}`);
|
|
116
|
+
if (cmd.wait)
|
|
117
|
+
await watch(client, id, parseIntStrict(cmd.waitTimeout, '--wait-timeout'), options);
|
|
118
|
+
});
|
|
119
|
+
deploy
|
|
120
|
+
.command('stop')
|
|
121
|
+
.description('Stop a deployment and its running jobs (billing stops with them).')
|
|
122
|
+
.argument('<id>', 'Deployment id')
|
|
123
|
+
.action(async (id) => {
|
|
124
|
+
const options = globals();
|
|
125
|
+
const client = createClient(options);
|
|
126
|
+
const dep = (await client.api.deployments.get(id));
|
|
127
|
+
await dep.stop();
|
|
128
|
+
const after = (await client.api.deployments.get(id));
|
|
129
|
+
if (options.json)
|
|
130
|
+
printJson({ id, status: after.status });
|
|
131
|
+
else
|
|
132
|
+
console.log(`${sym.ok} Stop requested for ${id}; status is now ${statusColor(after.status)}.`);
|
|
133
|
+
});
|
|
134
|
+
deploy
|
|
135
|
+
.command('watch')
|
|
136
|
+
.description('Follow a deployment until its endpoint is online or its job completes.')
|
|
137
|
+
.argument('<id>', 'Deployment id')
|
|
138
|
+
.option('--wait-timeout <minutes>', 'How long to watch', String(DEFAULT_WAIT_MINUTES))
|
|
139
|
+
.action(async (id, cmd) => {
|
|
140
|
+
const options = globals();
|
|
141
|
+
await watch(createClient(options), id, parseIntStrict(cmd.waitTimeout, '--wait-timeout'), options);
|
|
142
|
+
});
|
|
143
|
+
deploy
|
|
144
|
+
.command('jobs')
|
|
145
|
+
.description('List the jobs a deployment has run.')
|
|
146
|
+
.argument('<id>', 'Deployment id')
|
|
147
|
+
.option('--limit <count>', 'Page size', '20')
|
|
148
|
+
.action(async (id, cmd) => {
|
|
149
|
+
const options = globals();
|
|
150
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
151
|
+
const result = await dep.getJobs({ limit: pageSize(parseIntStrict(cmd.limit, '--limit')) });
|
|
152
|
+
const jobs = result.jobs;
|
|
153
|
+
if (options.json) {
|
|
154
|
+
printJson(result.jobs);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!jobs.length) {
|
|
158
|
+
console.log('No jobs yet.');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
console.log(table(['Job', 'State', 'Host', 'Rev', 'Created', 'Started'], jobs.map((j) => [j.job, statusColor(normalizeJobState(j.state)), j.node ? short(j.node, 6) : '-', String(j.revision ?? '-'), fmtDate(j.created_at), j.time_start ? fmtDate(j.time_start) : '-'])));
|
|
162
|
+
});
|
|
163
|
+
deploy
|
|
164
|
+
.command('events')
|
|
165
|
+
.description('Show the deployment event log (scheduling, starts, stops, errors).')
|
|
166
|
+
.argument('<id>', 'Deployment id')
|
|
167
|
+
.option('--limit <count>', 'Page size', '50')
|
|
168
|
+
.action(async (id, cmd) => {
|
|
169
|
+
const options = globals();
|
|
170
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
171
|
+
const result = await dep.getEvents({ limit: pageSize(parseIntStrict(cmd.limit, '--limit')), sort_order: 'desc' });
|
|
172
|
+
const events = result.events;
|
|
173
|
+
if (options.json) {
|
|
174
|
+
printJson(result.events);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!events.length) {
|
|
178
|
+
console.log('No events yet.');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
for (const e of [...events].reverse())
|
|
182
|
+
console.log(`${pc.dim(fmtDate(e.created_at))} ${pc.bold(e.type ?? e.event ?? '')} ${e.message ?? ''}`);
|
|
183
|
+
});
|
|
184
|
+
deploy
|
|
185
|
+
.command('result')
|
|
186
|
+
.description('Print the logs/results of a deployment job (latest job by default).')
|
|
187
|
+
.argument('<id>', 'Deployment id')
|
|
188
|
+
.option('--job <address>', 'Specific job address')
|
|
189
|
+
.action(async (id, cmd) => {
|
|
190
|
+
const options = globals();
|
|
191
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
192
|
+
let jobAddress = cmd.job;
|
|
193
|
+
if (!jobAddress) {
|
|
194
|
+
const jobs = (await dep.getJobs({ limit: 10 })).jobs;
|
|
195
|
+
if (!jobs.length)
|
|
196
|
+
throw new CliError('This deployment has not run any job yet.');
|
|
197
|
+
jobAddress = jobs[0].job;
|
|
198
|
+
}
|
|
199
|
+
const job = await dep.getJob(jobAddress);
|
|
200
|
+
if (options.json) {
|
|
201
|
+
printJson(job);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
console.log(`Job ${jobAddress} state ${statusColor(normalizeJobState(job.state))} host ${job.node ?? '-'} ${explorerJobUrl(jobAddress, options.network)}`);
|
|
205
|
+
for (const line of formatJobResult(job.jobResult))
|
|
206
|
+
console.log(line);
|
|
207
|
+
});
|
|
208
|
+
deploy
|
|
209
|
+
.command('extend')
|
|
210
|
+
.description('Change the timeout (minutes) of a deployment.')
|
|
211
|
+
.argument('<id>', 'Deployment id')
|
|
212
|
+
.requiredOption('--timeout <minutes>', 'New timeout in minutes')
|
|
213
|
+
.action(async (id, cmd) => {
|
|
214
|
+
const options = globals();
|
|
215
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
216
|
+
await dep.updateTimeout(parseIntStrict(cmd.timeout, '--timeout'));
|
|
217
|
+
console.log(`${sym.ok} Timeout of ${id} set to ${cmd.timeout} min.`);
|
|
218
|
+
});
|
|
219
|
+
deploy
|
|
220
|
+
.command('scale')
|
|
221
|
+
.description('Change the replica count of a deployment.')
|
|
222
|
+
.argument('<id>', 'Deployment id')
|
|
223
|
+
.requiredOption('--replicas <count>', 'New replica count')
|
|
224
|
+
.action(async (id, cmd) => {
|
|
225
|
+
const options = globals();
|
|
226
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
227
|
+
await dep.updateReplicaCount(parseIntStrict(cmd.replicas, '--replicas'));
|
|
228
|
+
console.log(`${sym.ok} Replicas of ${id} set to ${cmd.replicas}.`);
|
|
229
|
+
});
|
|
230
|
+
deploy
|
|
231
|
+
.command('rename')
|
|
232
|
+
.description('Rename a deployment.')
|
|
233
|
+
.argument('<id>', 'Deployment id')
|
|
234
|
+
.requiredOption('--name <name>', 'New name')
|
|
235
|
+
.action(async (id, cmd) => {
|
|
236
|
+
const options = globals();
|
|
237
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
238
|
+
await dep.updateName(cmd.name);
|
|
239
|
+
console.log(`${sym.ok} Renamed ${id} to "${cmd.name}".`);
|
|
240
|
+
});
|
|
241
|
+
deploy
|
|
242
|
+
.command('revision')
|
|
243
|
+
.description('Upload a new job definition revision and make it active.')
|
|
244
|
+
.argument('<id>', 'Deployment id')
|
|
245
|
+
.requiredOption('-f, --file <path>', 'Job definition JSON')
|
|
246
|
+
.action(async (id, cmd) => {
|
|
247
|
+
const options = globals();
|
|
248
|
+
const parsed = JSON.parse(fs.readFileSync(path.resolve(cmd.file), 'utf8'));
|
|
249
|
+
const validation = validateJobDefinition(parsed);
|
|
250
|
+
if (!validation.success)
|
|
251
|
+
throw new CliError(`Invalid job definition:\n${JSON.stringify(validation.errors, null, 2)}`, 2);
|
|
252
|
+
const client = createClient(options);
|
|
253
|
+
const dep = (await client.api.deployments.get(id));
|
|
254
|
+
await dep.createRevision(parsed);
|
|
255
|
+
const after = (await client.api.deployments.get(id));
|
|
256
|
+
console.log(`${sym.ok} New revision uploaded; active revision is now ${after.active_revision}. Restart the deployment to roll it out.`);
|
|
257
|
+
});
|
|
258
|
+
deploy
|
|
259
|
+
.command('auth-header')
|
|
260
|
+
.description('Print the Authorization header that grants access to a confidential deployment endpoint.')
|
|
261
|
+
.argument('<id>', 'Deployment id')
|
|
262
|
+
.action(async (id) => {
|
|
263
|
+
const options = globals();
|
|
264
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
265
|
+
const header = await dep.generateAuthHeader();
|
|
266
|
+
if (options.json)
|
|
267
|
+
printJson({ id, authorization: header });
|
|
268
|
+
else
|
|
269
|
+
console.log(`Authorization: ${header}`);
|
|
270
|
+
});
|
|
271
|
+
deploy
|
|
272
|
+
.command('archive')
|
|
273
|
+
.description('Archive a stopped deployment (hides it from the list).')
|
|
274
|
+
.argument('<id>', 'Deployment id')
|
|
275
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
276
|
+
.action(async (id, cmd) => {
|
|
277
|
+
const options = globals();
|
|
278
|
+
const dep = (await createClient(options).api.deployments.get(id));
|
|
279
|
+
await confirmDestructive('archive', id, cmd.yes);
|
|
280
|
+
await dep.archive();
|
|
281
|
+
console.log(`${sym.ok} Archived ${id}.`);
|
|
282
|
+
});
|
|
283
|
+
deploy
|
|
284
|
+
.command('delete')
|
|
285
|
+
.description('Delete a deployment permanently (stops it first if it is still running; drafts must be started and stopped once).')
|
|
286
|
+
.argument('<id>', 'Deployment id')
|
|
287
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
288
|
+
.action(async (id, cmd) => {
|
|
289
|
+
const options = globals();
|
|
290
|
+
const client = createClient(options);
|
|
291
|
+
let dep = (await client.api.deployments.get(id));
|
|
292
|
+
await confirmDestructive('delete', id, cmd.yes);
|
|
293
|
+
if (dep.status === 'DRAFT') {
|
|
294
|
+
throw new CliError(`${id} is a DRAFT. The Nosana API refuses to stop, archive or delete drafts. Start it and stop it first (\`deploy start ${id}\`, then \`deploy stop ${id}\`), or remove it on deploy.nosana.com.`);
|
|
295
|
+
}
|
|
296
|
+
if (!DELETABLE_STATUSES.has(dep.status)) {
|
|
297
|
+
await dep.stop();
|
|
298
|
+
for (let attempt = 0; attempt < 12 && !DELETABLE_STATUSES.has(dep.status); attempt += 1) {
|
|
299
|
+
await sleep(5000);
|
|
300
|
+
dep = (await client.api.deployments.get(id));
|
|
301
|
+
}
|
|
302
|
+
if (!DELETABLE_STATUSES.has(dep.status)) {
|
|
303
|
+
throw new CliError(`${id} is still ${dep.status}; wait for it to stop, then run delete again.`);
|
|
304
|
+
}
|
|
305
|
+
console.log(`${sym.ok} Stopped ${id}.`);
|
|
306
|
+
}
|
|
307
|
+
await dep.delete();
|
|
308
|
+
console.log(`${sym.ok} Deleted ${id}.`);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"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"}
|