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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { CliError, createClient } from '../../core/client.js';
|
|
5
|
+
import { heading, printJson, sym, table } from '../../core/format.js';
|
|
6
|
+
import { hardwareHints, listTemplates, prepareJobDefinition, resolveTemplate, topLevelTemplates, findTemplate, NeedsVariantError } from '../../core/templates.js';
|
|
7
|
+
export function registerTemplateCommands(program, globals) {
|
|
8
|
+
const templates = program.command('templates').alias('template').description('Browse the ready-to-run Nosana templates (MiniMax H3, Ollama models, ComfyUI, Jupyter, ...).');
|
|
9
|
+
templates
|
|
10
|
+
.command('list', { isDefault: true })
|
|
11
|
+
.description('List templates. Variants (e.g. minimax-h3-i2v-32gb) are folded under their parent unless --all is given.')
|
|
12
|
+
.option('--all', 'Show every variant template as its own row')
|
|
13
|
+
.option('--search <text>', 'Filter by id, name or category')
|
|
14
|
+
.action(async (cmd) => {
|
|
15
|
+
const options = globals();
|
|
16
|
+
const client = createClient(options);
|
|
17
|
+
let all = await listTemplates(client);
|
|
18
|
+
if (cmd.search) {
|
|
19
|
+
const q = cmd.search.toLowerCase();
|
|
20
|
+
all = all.filter((t) => t.id.toLowerCase().includes(q) || t.name.toLowerCase().includes(q) || t.category.join(' ').toLowerCase().includes(q));
|
|
21
|
+
}
|
|
22
|
+
const rows = cmd.all ? all : topLevelTemplates(all);
|
|
23
|
+
const vramLabel = (t) => {
|
|
24
|
+
const values = t.variants.length
|
|
25
|
+
? t.variants.map((v) => all.find((x) => x.id === `${t.id}-${v.id}`)?.vramRequirementGb ?? null).filter((n) => n !== null)
|
|
26
|
+
: t.vramRequirementGb !== null
|
|
27
|
+
? [t.vramRequirementGb]
|
|
28
|
+
: [];
|
|
29
|
+
if (!values.length)
|
|
30
|
+
return pc.dim('-');
|
|
31
|
+
const min = Math.min(...values);
|
|
32
|
+
const max = Math.max(...values);
|
|
33
|
+
return min === max ? `${min} GB` : `${min} to ${max} GB`;
|
|
34
|
+
};
|
|
35
|
+
if (options.json) {
|
|
36
|
+
printJson(rows.map(({ readme: _readme, jobDefinition: _def, ...rest }) => rest));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
console.log(table(['ID', 'Name', 'Category', 'Variants', 'VRAM'], rows.map((t) => [
|
|
40
|
+
t.id,
|
|
41
|
+
t.name,
|
|
42
|
+
t.category.filter((c) => c !== 'Official').join(', '),
|
|
43
|
+
t.variants.length ? t.variants.map((v) => v.id).join(', ') : pc.dim('-'),
|
|
44
|
+
vramLabel(t),
|
|
45
|
+
])));
|
|
46
|
+
console.log(`\n${rows.length} templates. Use \`nosana-deploy templates show <id>\` for details or \`nosana-deploy run --template <id>\` to deploy one.`);
|
|
47
|
+
});
|
|
48
|
+
templates
|
|
49
|
+
.command('show')
|
|
50
|
+
.description('Show a template: variants, hardware needs, exposed ports and the job definition it deploys.')
|
|
51
|
+
.argument('<id>', 'Template id or name, e.g. minimax-h3')
|
|
52
|
+
.option('--variant <id>', 'Variant id, e.g. i2v-32gb')
|
|
53
|
+
.option('--readme', 'Print the full README instead of a summary')
|
|
54
|
+
.action(async (id, cmd) => {
|
|
55
|
+
const options = globals();
|
|
56
|
+
const client = createClient(options);
|
|
57
|
+
const all = await listTemplates(client);
|
|
58
|
+
const parent = findTemplate(all, id);
|
|
59
|
+
if (!parent)
|
|
60
|
+
throw new CliError(`Unknown template "${id}".`);
|
|
61
|
+
let resolved;
|
|
62
|
+
try {
|
|
63
|
+
resolved = resolveTemplate(all, id, cmd.variant);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (!(error instanceof NeedsVariantError))
|
|
67
|
+
throw error;
|
|
68
|
+
resolved = null;
|
|
69
|
+
}
|
|
70
|
+
const template = resolved?.template ?? parent;
|
|
71
|
+
if (options.json) {
|
|
72
|
+
printJson({ parent: { ...parent, readme: undefined }, template: { ...template, readme: cmd.readme ? template.readme : undefined } });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const hints = hardwareHints(parent);
|
|
76
|
+
console.log(`${pc.bold(parent.name)} ${pc.dim(parent.id)}`);
|
|
77
|
+
if (parent.description)
|
|
78
|
+
console.log(parent.description);
|
|
79
|
+
console.log(`Category: ${parent.category.join(', ')}`);
|
|
80
|
+
if (parent.variants.length) {
|
|
81
|
+
heading('Variants');
|
|
82
|
+
console.log(table(['ID', 'Name', 'Description'], parent.variants.map((v) => [v.id, v.name, v.description ?? ''])));
|
|
83
|
+
if (!resolved)
|
|
84
|
+
console.log(`\n${sym.info} Add --variant <id> to see one variant's job definition.`);
|
|
85
|
+
}
|
|
86
|
+
heading('Hardware');
|
|
87
|
+
console.log(`VRAM needed: ${template.vramRequirementGb ? `${template.vramRequirementGb} GB` : 'not specified'}`);
|
|
88
|
+
for (const note of hints.notes)
|
|
89
|
+
console.log(`${sym.warn} ${note}`);
|
|
90
|
+
if (resolved) {
|
|
91
|
+
heading(`Job definition (${resolved.id})`);
|
|
92
|
+
console.log(JSON.stringify(prepareJobDefinition(resolved.jobDefinition), null, 2));
|
|
93
|
+
}
|
|
94
|
+
if (cmd.readme && parent.readme) {
|
|
95
|
+
heading('README');
|
|
96
|
+
console.log(parent.readme);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
templates
|
|
100
|
+
.command('export')
|
|
101
|
+
.description('Write a template job definition to a file so you can customise it and deploy with --file.')
|
|
102
|
+
.argument('<id>', 'Template id, e.g. minimax-h3')
|
|
103
|
+
.option('--variant <id>', 'Variant id, e.g. i2v-32gb')
|
|
104
|
+
.option('-o, --output <file>', 'Output path (defaults to <template-id>.json)')
|
|
105
|
+
.action(async (id, cmd) => {
|
|
106
|
+
const options = globals();
|
|
107
|
+
const client = createClient(options);
|
|
108
|
+
const resolved = resolveTemplate(await listTemplates(client), id, cmd.variant);
|
|
109
|
+
const target = path.resolve(cmd.output ?? `${resolved.id}.json`);
|
|
110
|
+
fs.writeFileSync(target, `${JSON.stringify(prepareJobDefinition(resolved.jobDefinition), null, 2)}\n`);
|
|
111
|
+
console.log(`${sym.ok} Wrote ${target}`);
|
|
112
|
+
console.log(`Deploy it with: nosana-deploy run --file ${path.relative(process.cwd(), target) || target} --gpu <slug>`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/commands/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAsB,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAElK,MAAM,UAAU,wBAAwB,CAAC,OAAgB,EAAE,OAA4B;IACrF,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,8FAA8F,CAAC,CAAC;IAE7K,SAAS;SACN,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SACpC,WAAW,CAAC,0GAA0G,CAAC;SACvH,MAAM,CAAC,OAAO,EAAE,4CAA4C,CAAC;SAC7D,MAAM,CAAC,iBAAiB,EAAE,gCAAgC,CAAC;SAC3D,MAAM,CAAC,KAAK,EAAE,GAAuC,EAAE,EAAE;QACxD,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,GAAG,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACnC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAChJ,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,CAAC,CAAwB,EAAU,EAAE;YACrD,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM;gBAC9B,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,iBAAiB,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;gBACtI,CAAC,CAAC,CAAC,CAAC,iBAAiB,KAAK,IAAI;oBAC5B,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC;oBACvB,CAAC,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;YAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;YAChC,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,GAAG,KAAK,CAAC;QAC3D,CAAC,CAAC;QACF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CACT,KAAK,CACH,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,EAC9C,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACd,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACrD,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;YACxE,SAAS,CAAC,CAAC,CAAC;SACb,CAAC,CACH,CACF,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,2HAA2H,CAAC,CAAC;IAC3J,CAAC,CAAC,CAAC;IAEL,SAAS;SACN,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,6FAA6F,CAAC;SAC1G,QAAQ,CAAC,MAAM,EAAE,sCAAsC,CAAC;SACxD,MAAM,CAAC,gBAAgB,EAAE,2BAA2B,CAAC;SACrD,MAAM,CAAC,UAAU,EAAE,4CAA4C,CAAC;SAChE,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,GAA2C,EAAE,EAAE;QACxE,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,QAAQ,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;QAC7D,IAAI,QAAQ,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,iBAAiB,CAAC;gBAAE,MAAM,KAAK,CAAC;YACvD,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,EAAE,QAAQ,IAAI,MAAM,CAAC;QAC9C,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACrI,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC3B,OAAO,CAAC,UAAU,CAAC,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACnH,IAAI,CAAC,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,0DAA0D,CAAC,CAAC;QACtG,CAAC;QACD,OAAO,CAAC,UAAU,CAAC,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,iBAAiB,KAAK,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QACjH,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;QACnE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,mBAAmB,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,SAAS;SACN,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2FAA2F,CAAC;SACxG,QAAQ,CAAC,MAAM,EAAE,8BAA8B,CAAC;SAChD,MAAM,CAAC,gBAAgB,EAAE,2BAA2B,CAAC;SACrD,MAAM,CAAC,qBAAqB,EAAE,8CAA8C,CAAC;SAC7E,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,GAA0C,EAAE,EAAE;QACvE,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QACjE,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACvG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,UAAU,MAAM,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,4CAA4C,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,MAAM,eAAe,CAAC,CAAC;IACzH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { Command, Option } from 'commander';
|
|
4
|
+
import { CliError } from '../core/client.js';
|
|
5
|
+
import { formatError, sym } from '../core/format.js';
|
|
6
|
+
import { registerAccountCommands } from './commands/account.js';
|
|
7
|
+
import { registerDeployCommands } from './commands/deploy.js';
|
|
8
|
+
import { registerGpuCommands } from './commands/gpus.js';
|
|
9
|
+
import { registerJobCommands } from './commands/job.js';
|
|
10
|
+
import { registerRunCommand } from './commands/run.js';
|
|
11
|
+
import { registerTemplateCommands } from './commands/templates.js';
|
|
12
|
+
const { version: VERSION } = createRequire(import.meta.url)('../../package.json');
|
|
13
|
+
const program = new Command()
|
|
14
|
+
.name('nosana-deploy')
|
|
15
|
+
.description('Nosana Deploy CLI (part of Nosana MCP): deploy GPU workloads on Nosana with account credits. Pick a template such as MiniMax H3, choose a GPU, run it.')
|
|
16
|
+
.version(VERSION)
|
|
17
|
+
.addOption(new Option('-n, --network <network>', 'Nosana network').choices(['mainnet', 'devnet']).default('mainnet'))
|
|
18
|
+
.option('--api-key <key>', 'Nosana API key (defaults to NOSANA_API_KEY or the key saved by `login`)')
|
|
19
|
+
.option('--rpc <url>', 'Solana RPC URL, only used for optional on-chain queue reads')
|
|
20
|
+
.option('--json', 'Machine-readable JSON output')
|
|
21
|
+
.showHelpAfterError('(add --help for usage)')
|
|
22
|
+
.addHelpText('after', `
|
|
23
|
+
Quick start:
|
|
24
|
+
nosana-deploy login store your API key from deploy.nosana.com
|
|
25
|
+
nosana-deploy templates list see what you can run (MiniMax H3, Qwen, ComfyUI, Jupyter, ...)
|
|
26
|
+
nosana-deploy gpus --template minimax-h3 --variant i2v-32gb
|
|
27
|
+
nosana-deploy run guided: template -> GPU -> timeout -> confirm -> live status
|
|
28
|
+
nosana-deploy deploy list everything you have deployed
|
|
29
|
+
nosana-deploy deploy stop <id> stop paying for a running deployment`);
|
|
30
|
+
const globals = () => program.opts();
|
|
31
|
+
registerAccountCommands(program, globals);
|
|
32
|
+
registerTemplateCommands(program, globals);
|
|
33
|
+
registerGpuCommands(program, globals);
|
|
34
|
+
registerRunCommand(program, globals);
|
|
35
|
+
registerDeployCommands(program, globals);
|
|
36
|
+
registerJobCommands(program, globals);
|
|
37
|
+
program.parseAsync().catch((error) => {
|
|
38
|
+
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
39
|
+
console.error('\nCancelled.');
|
|
40
|
+
process.exit(130);
|
|
41
|
+
}
|
|
42
|
+
console.error(`${sym.err} ${formatError(error)}`);
|
|
43
|
+
process.exit(error instanceof CliError ? error.exitCode : 1);
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAsB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAwB,CAAC;AAEzG,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,wJAAwJ,CAAC;KACrK,OAAO,CAAC,OAAO,CAAC;KAChB,SAAS,CAAC,IAAI,MAAM,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;KACpH,MAAM,CAAC,iBAAiB,EAAE,yEAAyE,CAAC;KACpG,MAAM,CAAC,aAAa,EAAE,6DAA6D,CAAC;KACpF,MAAM,CAAC,QAAQ,EAAE,8BAA8B,CAAC;KAChD,kBAAkB,CAAC,wBAAwB,CAAC;KAC5C,WAAW,CACV,OAAO,EACP;;;;;;;gFAO4E,CAC7E,CAAC;AAEJ,MAAM,OAAO,GAAG,GAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,EAAiB,CAAC;AAEnE,uBAAuB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1C,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC3C,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACtC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACrC,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACzC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAEtC,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC5C,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC/D,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NosanaNetwork, type NosanaClient } from '@nosana/kit';
|
|
2
|
+
import { type Network } from './config.js';
|
|
3
|
+
export interface GlobalOptions {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
network: Network;
|
|
6
|
+
rpc?: string;
|
|
7
|
+
json?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class CliError extends Error {
|
|
10
|
+
readonly exitCode: number;
|
|
11
|
+
constructor(message: string, exitCode?: number);
|
|
12
|
+
}
|
|
13
|
+
export declare const NO_KEY_MESSAGE: string;
|
|
14
|
+
export declare function requireApiKey(options: GlobalOptions): string;
|
|
15
|
+
export declare function toKitNetwork(network: Network): NosanaNetwork;
|
|
16
|
+
export type ClientLogLevel = 'none' | 'error';
|
|
17
|
+
export declare function createClient(options: GlobalOptions, apiKey?: string, logLevel?: ClientLogLevel): NosanaClient;
|
|
18
|
+
export declare function createPublicClient(options: GlobalOptions): NosanaClient;
|
|
19
|
+
export type Api = NosanaClient['api'];
|
|
20
|
+
export type ApiDeployment = Awaited<ReturnType<Api['deployments']['get']>>;
|
|
21
|
+
export type CreateDeploymentBody = Parameters<Api['deployments']['create']>[0];
|
|
22
|
+
export type CreditsBalance = Awaited<ReturnType<Api['credits']['balance']>>;
|
|
23
|
+
export type { JobDefinition } from '@nosana/kit';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createNosanaClient, NosanaNetwork } from '@nosana/kit';
|
|
2
|
+
import { resolveApiKey } from './config.js';
|
|
3
|
+
export class CliError extends Error {
|
|
4
|
+
exitCode;
|
|
5
|
+
constructor(message, exitCode = 1) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.exitCode = exitCode;
|
|
8
|
+
this.name = 'CliError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export const NO_KEY_MESSAGE = [
|
|
12
|
+
'No Nosana API key found.',
|
|
13
|
+
' Run `nosana-deploy login` to store one, set NOSANA_API_KEY, or pass --api-key.',
|
|
14
|
+
' Create a key at https://deploy.nosana.com (Account > API Keys).',
|
|
15
|
+
].join('\n');
|
|
16
|
+
export function requireApiKey(options) {
|
|
17
|
+
const resolved = resolveApiKey(options.apiKey);
|
|
18
|
+
if (!resolved)
|
|
19
|
+
throw new CliError(NO_KEY_MESSAGE);
|
|
20
|
+
return resolved.key;
|
|
21
|
+
}
|
|
22
|
+
export function toKitNetwork(network) {
|
|
23
|
+
return network === 'devnet' ? NosanaNetwork.DEVNET : NosanaNetwork.MAINNET;
|
|
24
|
+
}
|
|
25
|
+
export function createClient(options, apiKey = requireApiKey(options), logLevel = 'error') {
|
|
26
|
+
return createNosanaClient(toKitNetwork(options.network), {
|
|
27
|
+
api: { apiKey },
|
|
28
|
+
logLevel,
|
|
29
|
+
...(options.rpc ? { solana: { rpcEndpoint: options.rpc } } : {}),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function createPublicClient(options) {
|
|
33
|
+
return createNosanaClient(toKitNetwork(options.network), {
|
|
34
|
+
logLevel: 'error',
|
|
35
|
+
...(options.rpc ? { solana: { rpcEndpoint: options.rpc } } : {}),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAqB,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAS1D,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGtB;IAFX,YACE,OAAe,EACN,WAAW,CAAC;QAErB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,aAAQ,GAAR,QAAQ,CAAI;QAGrB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,0BAA0B;IAC1B,kFAAkF;IAClF,mEAAmE;CACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC;IAClD,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC;AAC7E,CAAC;AAID,MAAM,UAAU,YAAY,CAC1B,OAAsB,EACtB,SAAiB,aAAa,CAAC,OAAO,CAAC,EACvC,WAA2B,OAAO;IAElC,OAAO,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QACvD,GAAG,EAAE,EAAE,MAAM,EAAE;QACf,QAAQ;QACR,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,OAAO,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QACvD,QAAQ,EAAE,OAAO;QACjB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type Network = 'mainnet' | 'devnet';
|
|
2
|
+
export interface StoredConfig {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
network?: Network;
|
|
5
|
+
rpc?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const CONFIG_DIR: string;
|
|
8
|
+
export declare const CONFIG_FILE: string;
|
|
9
|
+
export declare function readConfig(): StoredConfig;
|
|
10
|
+
export declare function writeConfig(config: StoredConfig): void;
|
|
11
|
+
export declare function clearConfig(): boolean;
|
|
12
|
+
export type ApiKeySource = 'flag' | 'env' | 'config';
|
|
13
|
+
export declare function resolveApiKey(explicit?: string): {
|
|
14
|
+
key: string;
|
|
15
|
+
source: ApiKeySource;
|
|
16
|
+
} | undefined;
|
|
17
|
+
export declare function maskKey(key: string): string;
|
|
18
|
+
export declare function isInteractive(): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export const CONFIG_DIR = process.env.NOSANA_DEPLOY_CONFIG_DIR ?? path.join(os.homedir(), '.config', 'nosana-deploy');
|
|
5
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
+
export function readConfig() {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
9
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function writeConfig(config) {
|
|
16
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
17
|
+
fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
18
|
+
try {
|
|
19
|
+
fs.chmodSync(CONFIG_FILE, 0o600);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* best effort */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function clearConfig() {
|
|
26
|
+
try {
|
|
27
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function resolveApiKey(explicit) {
|
|
35
|
+
if (explicit)
|
|
36
|
+
return { key: explicit, source: 'flag' };
|
|
37
|
+
if (process.env.NOSANA_API_KEY)
|
|
38
|
+
return { key: process.env.NOSANA_API_KEY, source: 'env' };
|
|
39
|
+
const stored = readConfig().apiKey;
|
|
40
|
+
if (stored)
|
|
41
|
+
return { key: stored, source: 'config' };
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
export function maskKey(key) {
|
|
45
|
+
if (key.length <= 12)
|
|
46
|
+
return '****';
|
|
47
|
+
return `${key.slice(0, 8)}...${key.slice(-4)}`;
|
|
48
|
+
}
|
|
49
|
+
export function isInteractive() {
|
|
50
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,MAAM,CAAC,MAAM,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AAC9F,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEhE,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAY,CAAC;QAC3E,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;IACxG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvF,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAID,MAAM,UAAU,aAAa,CAAC,QAAiB;IAC7C,IAAI,QAAQ;QAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACvD,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1F,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,MAAM,CAAC;IACnC,IAAI,MAAM;QAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACrD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IACpC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CreditsBalance } from './client.js';
|
|
2
|
+
export interface BalanceSummary {
|
|
3
|
+
assigned: number;
|
|
4
|
+
reserved: number;
|
|
5
|
+
settled: number;
|
|
6
|
+
available: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function summarizeBalance(balance: CreditsBalance): BalanceSummary;
|
|
9
|
+
export declare const availableCredits: (balance: CreditsBalance) => number;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function summarizeBalance(balance) {
|
|
2
|
+
const b = balance;
|
|
3
|
+
const assigned = b.assignedCredits ?? 0;
|
|
4
|
+
const reserved = b.reservedCredits ?? 0;
|
|
5
|
+
const settled = b.settledCredits ?? 0;
|
|
6
|
+
return { assigned, reserved, settled, available: assigned - reserved - settled };
|
|
7
|
+
}
|
|
8
|
+
export const availableCredits = (balance) => summarizeBalance(balance).available;
|
|
9
|
+
//# sourceMappingURL=credits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credits.js","sourceRoot":"","sources":["../../src/core/credits.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IACtD,MAAM,CAAC,GAAG,OAAiC,CAAC;IAC5C,MAAM,QAAQ,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC;IACtC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAuB,EAAU,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { JobDefinition, NosanaClient } from '@nosana/kit';
|
|
2
|
+
import { type ApiDeployment, type CreateDeploymentBody } from './client.js';
|
|
3
|
+
import type { Network } from './config.js';
|
|
4
|
+
import { type GpuMarket, type GpuTableOptions } from './markets.js';
|
|
5
|
+
import { type WorkloadKind } from './templates.js';
|
|
6
|
+
export type Strategy = 'SIMPLE' | 'SIMPLE-EXTEND' | 'SCHEDULED' | 'INFINITE';
|
|
7
|
+
/** Nosana refuses credit-paid jobs shorter than 3600 seconds, so every deployment needs at least this timeout. */
|
|
8
|
+
export declare const MIN_TIMEOUT_MINUTES = 60;
|
|
9
|
+
export declare const STRATEGIES: readonly Strategy[];
|
|
10
|
+
export declare function parseStrategy(value: string | undefined): Strategy;
|
|
11
|
+
export interface DeployPlan {
|
|
12
|
+
name: string;
|
|
13
|
+
/** Human label of what is being deployed (template id or file name). */
|
|
14
|
+
workload: string;
|
|
15
|
+
jobDefinition: JobDefinition;
|
|
16
|
+
gpu: GpuMarket;
|
|
17
|
+
timeoutMinutes: number;
|
|
18
|
+
replicas: number;
|
|
19
|
+
strategy: Strategy;
|
|
20
|
+
schedule?: string;
|
|
21
|
+
rotationTime?: number;
|
|
22
|
+
startupTimeoutMinutes?: number;
|
|
23
|
+
confidential: boolean;
|
|
24
|
+
sshPublicKeys?: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare function defaultDeploymentName(label: string): string;
|
|
27
|
+
export interface PlanAssessment {
|
|
28
|
+
/** Problems that should stop the deployment unless the caller forces it. */
|
|
29
|
+
blocking: string[];
|
|
30
|
+
/** Things worth knowing that do not block. */
|
|
31
|
+
advisories: string[];
|
|
32
|
+
/** blocking followed by advisories, for printing. */
|
|
33
|
+
warnings: string[];
|
|
34
|
+
cost: CostEstimate;
|
|
35
|
+
}
|
|
36
|
+
export interface AssessOptions {
|
|
37
|
+
/** Treat "no idle host" as blocking (agents must get explicit consent to queue). */
|
|
38
|
+
requireIdle?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function assessPlan(plan: DeployPlan, fit: GpuTableOptions, creditsAvailable: number, options?: AssessOptions): PlanAssessment;
|
|
41
|
+
export interface CostEstimate {
|
|
42
|
+
perHour: number;
|
|
43
|
+
hours: number;
|
|
44
|
+
total: number;
|
|
45
|
+
recurring: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** 1 credit is priced like 1 USD on the dashboard; the estimate covers one full timeout window. */
|
|
48
|
+
export declare function estimateCost(plan: DeployPlan): CostEstimate;
|
|
49
|
+
export declare function validatePlan(plan: DeployPlan): void;
|
|
50
|
+
export declare function buildCreateBody(plan: DeployPlan): CreateDeploymentBody;
|
|
51
|
+
export declare function createDeployment(client: NosanaClient, plan: DeployPlan): Promise<ApiDeployment>;
|
|
52
|
+
export declare const dashboardUrl: (id: string, network: Network) => string;
|
|
53
|
+
export declare const explorerJobUrl: (job: string, network: Network) => string;
|
|
54
|
+
export declare const normalizeJobState: (state: unknown) => string;
|
|
55
|
+
export declare function summarizeDeployment(dep: ApiDeployment, network: Network): string[];
|
|
56
|
+
export declare function deploymentsTable(deps: ApiDeployment[]): string;
|
|
57
|
+
export interface WaitOptions {
|
|
58
|
+
timeoutMinutes: number;
|
|
59
|
+
intervalSeconds?: number;
|
|
60
|
+
network: Network;
|
|
61
|
+
quiet?: boolean;
|
|
62
|
+
/** Print the deployment's existing event log on the first pass (useful right after creation). */
|
|
63
|
+
replayEvents?: boolean;
|
|
64
|
+
/** Receive progress lines instead of printing them to stdout (used by the MCP server). */
|
|
65
|
+
onLog?: (line: string) => void;
|
|
66
|
+
}
|
|
67
|
+
export type WaitOutcome = {
|
|
68
|
+
kind: 'online';
|
|
69
|
+
deployment: ApiDeployment;
|
|
70
|
+
readyUrls: string[];
|
|
71
|
+
} | {
|
|
72
|
+
kind: 'completed';
|
|
73
|
+
deployment: ApiDeployment;
|
|
74
|
+
job: string;
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'stopped';
|
|
77
|
+
deployment: ApiDeployment;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'failed';
|
|
80
|
+
deployment: ApiDeployment;
|
|
81
|
+
reason: string;
|
|
82
|
+
error?: string;
|
|
83
|
+
} | {
|
|
84
|
+
kind: 'timeout';
|
|
85
|
+
deployment: ApiDeployment;
|
|
86
|
+
phase: DeploymentPhase;
|
|
87
|
+
};
|
|
88
|
+
export type DeploymentPhase = 'draft' | 'scheduling' | 'queued' | 'starting' | 'initializing' | 'ready' | 'completed' | 'stopped' | 'error';
|
|
89
|
+
export interface DefinitionFacts {
|
|
90
|
+
jobDefinition: JobDefinition | null;
|
|
91
|
+
exposes: boolean;
|
|
92
|
+
kind: WorkloadKind;
|
|
93
|
+
readinessPath: string;
|
|
94
|
+
bootMinutes: number;
|
|
95
|
+
opId: string | null;
|
|
96
|
+
}
|
|
97
|
+
export declare function inspectDefinition(dep: ApiDeployment): Promise<DefinitionFacts>;
|
|
98
|
+
/** Where a deployment is in its life, derived from status, jobs and endpoint probes. */
|
|
99
|
+
export declare function computePhase(dep: ApiDeployment, jobs: {
|
|
100
|
+
state: unknown;
|
|
101
|
+
node?: string | null;
|
|
102
|
+
}[], endpoints: {
|
|
103
|
+
tunnel_online: boolean;
|
|
104
|
+
service_ready: boolean;
|
|
105
|
+
}[], exposes: boolean): DeploymentPhase;
|
|
106
|
+
export declare const PHASE_HINTS: Record<DeploymentPhase, {
|
|
107
|
+
message: string;
|
|
108
|
+
pollAfterSeconds: number;
|
|
109
|
+
}>;
|
|
110
|
+
export interface EndpointSnapshot {
|
|
111
|
+
url: string;
|
|
112
|
+
op: string;
|
|
113
|
+
port: number | string;
|
|
114
|
+
tunnel_online: boolean;
|
|
115
|
+
service_ready: boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface DeploymentSnapshot {
|
|
118
|
+
deployment: ApiDeployment;
|
|
119
|
+
facts: DefinitionFacts;
|
|
120
|
+
phase: DeploymentPhase;
|
|
121
|
+
endpoints: EndpointSnapshot[];
|
|
122
|
+
readyUrls: string[];
|
|
123
|
+
jobs: {
|
|
124
|
+
job: string;
|
|
125
|
+
state: string;
|
|
126
|
+
node: string | null;
|
|
127
|
+
created_at?: string;
|
|
128
|
+
time_start?: number;
|
|
129
|
+
}[];
|
|
130
|
+
events: {
|
|
131
|
+
at?: string;
|
|
132
|
+
type?: string;
|
|
133
|
+
message?: string;
|
|
134
|
+
}[];
|
|
135
|
+
elapsedSeconds: number;
|
|
136
|
+
}
|
|
137
|
+
export declare function snapshotDeployment(client: NosanaClient, id: string, facts?: DefinitionFacts): Promise<DeploymentSnapshot>;
|
|
138
|
+
export type EndpointProbe = 'ready' | 'initializing' | 'unreachable';
|
|
139
|
+
/**
|
|
140
|
+
* The deployment manager marks an endpoint online as soon as the host's tunnel answers, which can be
|
|
141
|
+
* long before the container does: Nosana then serves a 503 "Service Initializing" page. Probe the URL
|
|
142
|
+
* so "online" means the workload itself responds.
|
|
143
|
+
*/
|
|
144
|
+
export declare function probeEndpoint(url: string, path?: string): Promise<EndpointProbe>;
|
|
145
|
+
export declare function waitForDeployment(client: NosanaClient, id: string, options: WaitOptions): Promise<WaitOutcome>;
|
|
146
|
+
export declare function formatJobResult(result: unknown): string[];
|