@ornncompute/cli 0.2.18 → 0.2.19
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 +63 -21
- package/package.json +1 -1
- package/src/api-client.mjs +22 -4
- package/src/cli.mjs +1933 -404
- package/src/color.mjs +71 -0
- package/vendor/capabilities/capabilities/listings.d.ts +1 -0
- package/vendor/capabilities/capabilities/listings.js +30 -0
- package/vendor/capabilities/capability.d.ts +1 -1
- package/vendor/capabilities/capability.js +10 -5
- package/vendor/capabilities/index.d.ts +1 -1
- package/vendor/capabilities/index.js +1 -1
package/src/color.mjs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// SGR open/close pairs for the four styles the CLI uses.
|
|
2
|
+
const CODES = { red: [31, 39], green: [32, 39], bold: [1, 22], dim: [2, 22] };
|
|
3
|
+
|
|
4
|
+
function createColors(enabled) {
|
|
5
|
+
return Object.fromEntries(
|
|
6
|
+
Object.entries(CODES).map(([name, [open, close]]) => [
|
|
7
|
+
name,
|
|
8
|
+
enabled ? (text) => `\x1b[${open}m${text}\x1b[${close}m` : (text) => text,
|
|
9
|
+
])
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const NO_COLOR_FLAG = "--no-color";
|
|
14
|
+
|
|
15
|
+
// Structured-error label prefixes that get dim/bold styling. Matched at the
|
|
16
|
+
// start of a line; the remainder of the line (the value) is left untouched.
|
|
17
|
+
const LABEL_PREFIX_RE = /^(Code|Reason|Next steps?):/;
|
|
18
|
+
|
|
19
|
+
// `--no-color` is global: strip it anywhere in argv so per-command parsing
|
|
20
|
+
// never sees it as an unknown option.
|
|
21
|
+
export function extractNoColorFlag(argv) {
|
|
22
|
+
return {
|
|
23
|
+
argv: argv.filter((arg) => arg !== NO_COLOR_FLAG),
|
|
24
|
+
noColor: argv.includes(NO_COLOR_FLAG),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function streamSupportsColor(stream, env, noColorFlag) {
|
|
29
|
+
if (noColorFlag || Object.hasOwn(env ?? {}, "NO_COLOR")) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (Object.hasOwn(env ?? {}, "FORCE_COLOR")) {
|
|
33
|
+
return String(env.FORCE_COLOR) !== "0";
|
|
34
|
+
}
|
|
35
|
+
return Boolean(stream.isTTY);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// stdout and stderr are evaluated independently: one can be a TTY while the
|
|
39
|
+
// other is piped.
|
|
40
|
+
export function attachStreamColors({ env, noColorFlag, stdout, stderr }) {
|
|
41
|
+
stdout.pc = createColors(streamSupportsColor(stdout, env, noColorFlag));
|
|
42
|
+
stderr.pc = createColors(streamSupportsColor(stderr, env, noColorFlag));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function writeColored(stream, variant, text) {
|
|
46
|
+
const colorize = stream.pc?.[variant];
|
|
47
|
+
stream.write(`${colorize ? colorize(text) : text}\n`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// First line is the error summary (red); later `Code:`/`Reason:`/`Next step:`
|
|
51
|
+
// label prefixes are dim/bold, their values left plain.
|
|
52
|
+
export function colorizeErrorMessage(message, stream) {
|
|
53
|
+
const pc = stream?.pc;
|
|
54
|
+
if (!pc) {
|
|
55
|
+
return message;
|
|
56
|
+
}
|
|
57
|
+
return message
|
|
58
|
+
.split("\n")
|
|
59
|
+
.map((line, index) => {
|
|
60
|
+
if (index === 0) {
|
|
61
|
+
return line ? pc.red(line) : line;
|
|
62
|
+
}
|
|
63
|
+
const match = line.match(LABEL_PREFIX_RE);
|
|
64
|
+
if (!match) {
|
|
65
|
+
return line;
|
|
66
|
+
}
|
|
67
|
+
const label = match[0];
|
|
68
|
+
return `${pc.bold(pc.dim(label))}${line.slice(label.length)}`;
|
|
69
|
+
})
|
|
70
|
+
.join("\n");
|
|
71
|
+
}
|
|
@@ -44,6 +44,7 @@ export declare const listingCreateInput: z.ZodObject<{
|
|
|
44
44
|
}, z.core.$strip>;
|
|
45
45
|
export declare const listCatalogTermsCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
46
46
|
export declare const listListingsCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
47
|
+
export declare const forecastListingsCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
47
48
|
export declare const createListingCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
48
49
|
export declare const listingCapabilities: import("../capability.ts").Capability<unknown, unknown>[];
|
|
49
50
|
/** @deprecated Use createListingCapability. */
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { defineCapability, preview } from "../capability.js";
|
|
3
3
|
import { bindHardwareFields, HARDWARE_LISTING_FIELDS, HARDWARE_LISTING_FILTERS, loadHardwareCatalog, projectCatalogTerms, } from "../spec-catalog.js";
|
|
4
|
+
function queryPath(path, params) {
|
|
5
|
+
const parts = [];
|
|
6
|
+
for (const [key, value] of Object.entries(params)) {
|
|
7
|
+
if (value?.trim())
|
|
8
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.trim())}`);
|
|
9
|
+
}
|
|
10
|
+
return parts.length ? `${path}?${parts.join("&")}` : path;
|
|
11
|
+
}
|
|
4
12
|
export const listingCreateInput = z.object({
|
|
5
13
|
operatorId: z.string().optional(),
|
|
6
14
|
operator_id: z.string().optional(),
|
|
@@ -46,6 +54,10 @@ export const listingCreateInput = z.object({
|
|
|
46
54
|
confirm: z.boolean().optional(),
|
|
47
55
|
});
|
|
48
56
|
const catalogTermsInput = z.object({ kind: z.enum(["gpu", "cpu", "node", "link"]).optional() });
|
|
57
|
+
const listingForecastInput = z.object({
|
|
58
|
+
limit: z.number().int().min(1).max(500).optional(),
|
|
59
|
+
cursor: z.string().min(1).optional(),
|
|
60
|
+
});
|
|
49
61
|
const slackListingCreateParams = {
|
|
50
62
|
type: "object",
|
|
51
63
|
properties: {
|
|
@@ -135,6 +147,23 @@ export const listListingsCapability = defineCapability({
|
|
|
135
147
|
return ctx.fetch("/listings/available", { method: "GET", body: bound.payload });
|
|
136
148
|
},
|
|
137
149
|
});
|
|
150
|
+
export const forecastListingsCapability = defineCapability({
|
|
151
|
+
id: "listings.forecast",
|
|
152
|
+
domain: "listings",
|
|
153
|
+
roles: ["user"],
|
|
154
|
+
description: "List the next 48 hourly availability buckets for customer-visible compute listings.",
|
|
155
|
+
mutation: "read",
|
|
156
|
+
http: { method: "GET", path: "/v1/cli/compute/listings/forecast" },
|
|
157
|
+
slack: false,
|
|
158
|
+
input: listingForecastInput,
|
|
159
|
+
execute: async (ctx, input) => {
|
|
160
|
+
const parsed = listingForecastInput.parse(input);
|
|
161
|
+
return ctx.fetch(queryPath("/v1/cli/compute/listings/forecast", {
|
|
162
|
+
limit: parsed.limit?.toString(),
|
|
163
|
+
cursor: parsed.cursor,
|
|
164
|
+
}), { method: "GET" });
|
|
165
|
+
},
|
|
166
|
+
});
|
|
138
167
|
/** Commerce parses listing windows as RFC3339 timestamps, not bare dates. */
|
|
139
168
|
function rfc3339(value) {
|
|
140
169
|
const trimmed = value.trim();
|
|
@@ -317,6 +346,7 @@ export const createListingCapability = defineCapability({
|
|
|
317
346
|
export const listingCapabilities = [
|
|
318
347
|
listCatalogTermsCapability,
|
|
319
348
|
listListingsCapability,
|
|
349
|
+
forecastListingsCapability,
|
|
320
350
|
createListingCapability,
|
|
321
351
|
];
|
|
322
352
|
/** @deprecated Use createListingCapability. */
|
|
@@ -65,7 +65,7 @@ export type Capability<I = unknown, O = unknown> = CapabilityBindings & {
|
|
|
65
65
|
execute: (ctx: CapabilityContext, input: I) => Promise<O>;
|
|
66
66
|
};
|
|
67
67
|
export type CapabilitySpec<I = unknown, O = unknown> = Omit<Capability<I, O>, "cli" | "mcp" | "slack"> & {
|
|
68
|
-
slack?: {
|
|
68
|
+
slack?: false | {
|
|
69
69
|
parameters?: JsonSchema;
|
|
70
70
|
};
|
|
71
71
|
};
|
|
@@ -31,14 +31,19 @@ export function preview(action, payload) {
|
|
|
31
31
|
/** Fill CLI/MCP/Slack names from `id`. Slack parameters stay optional. */
|
|
32
32
|
export function defineCapability(spec) {
|
|
33
33
|
const names = namesFromId(spec.id);
|
|
34
|
+
const { slack, ...rest } = spec;
|
|
34
35
|
return {
|
|
35
|
-
...
|
|
36
|
+
...rest,
|
|
36
37
|
cli: { command: names.cli },
|
|
37
38
|
mcp: { name: names.mcp },
|
|
38
|
-
slack
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
...(slack === false
|
|
40
|
+
? {}
|
|
41
|
+
: {
|
|
42
|
+
slack: {
|
|
43
|
+
name: names.slack,
|
|
44
|
+
...(slack?.parameters ? { parameters: slack.parameters } : {}),
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
42
47
|
};
|
|
43
48
|
}
|
|
44
49
|
/**
|
|
@@ -4,7 +4,7 @@ export { catalog } from "./catalog.ts";
|
|
|
4
4
|
export { fleetCapabilities, listFacilitiesCapability, listNodesCapability, listOperatorsCapability, nodeExecCapability, nodeOffGridCapability, nodeOnGridCapability, nodeTerminateCapability, } from "./capabilities/fleet.ts";
|
|
5
5
|
export { buildEnrollmentInstallCommand, tokenCapabilities, tokensCreateCapability, tokensListCapability, } from "./capabilities/tokens.ts";
|
|
6
6
|
export { identityCapabilities, statusCapability, whoamiCapability } from "./capabilities/identity.ts";
|
|
7
|
-
export { createListingCapability, listingCreateInput, listCatalogTermsCapability, listingCapabilities, listListingsCapability, proposeListingCapability, } from "./capabilities/listings.ts";
|
|
7
|
+
export { createListingCapability, forecastListingsCapability, listingCreateInput, listCatalogTermsCapability, listingCapabilities, listListingsCapability, proposeListingCapability, } from "./capabilities/listings.ts";
|
|
8
8
|
export { userCapabilities, usersQueryCapability } from "./capabilities/users.ts";
|
|
9
9
|
export { kubernetesCapabilities, slurmCapabilities } from "./capabilities/clusters.ts";
|
|
10
10
|
export { logsLatestCapability, logsTailCapability, observabilityCapabilities, telemetryLatestCapability, telemetryTailCapability, } from "./capabilities/observability.ts";
|
|
@@ -4,7 +4,7 @@ export { catalog } from "./catalog.js";
|
|
|
4
4
|
export { fleetCapabilities, listFacilitiesCapability, listNodesCapability, listOperatorsCapability, nodeExecCapability, nodeOffGridCapability, nodeOnGridCapability, nodeTerminateCapability, } from "./capabilities/fleet.js";
|
|
5
5
|
export { buildEnrollmentInstallCommand, tokenCapabilities, tokensCreateCapability, tokensListCapability, } from "./capabilities/tokens.js";
|
|
6
6
|
export { identityCapabilities, statusCapability, whoamiCapability } from "./capabilities/identity.js";
|
|
7
|
-
export { createListingCapability, listingCreateInput, listCatalogTermsCapability, listingCapabilities, listListingsCapability, proposeListingCapability, } from "./capabilities/listings.js";
|
|
7
|
+
export { createListingCapability, forecastListingsCapability, listingCreateInput, listCatalogTermsCapability, listingCapabilities, listListingsCapability, proposeListingCapability, } from "./capabilities/listings.js";
|
|
8
8
|
export { userCapabilities, usersQueryCapability } from "./capabilities/users.js";
|
|
9
9
|
export { kubernetesCapabilities, slurmCapabilities } from "./capabilities/clusters.js";
|
|
10
10
|
export { logsLatestCapability, logsTailCapability, observabilityCapabilities, telemetryLatestCapability, telemetryTailCapability, } from "./capabilities/observability.js";
|