@dreamlake/ml-dash 0.1.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.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `ml-dash create` — create a project.
3
+ *
4
+ * `-p` accepts either `project` or `namespace/project`; anything deeper is a
5
+ * user error rather than a nested project, since projects do not nest.
6
+ * An existing project reports success (exit 0): creating what is already
7
+ * there is the outcome the caller asked for, and scripts that create-then-use
8
+ * should not have to special-case the second run.
9
+ */
10
+ import { HttpError } from "../client.js";
11
+ import { makeClient, notAuthenticatedMessage, resolveContext } from "../cli/context.js";
12
+ import { asId } from "../util/json.js";
13
+ import { bold, dim, green, red, yellow } from "../util/ansi.js";
14
+ export const spec = {
15
+ name: "create",
16
+ help: "Create a new project",
17
+ description: `Create a new project in ml-dash.
18
+
19
+ Examples:
20
+ ml-dash create -p new-project
21
+ ml-dash create -p geyang/new-project
22
+ ml-dash create -p geyang/tutorials -d "ML tutorials and examples"`,
23
+ options: [
24
+ { flags: ["-p", "--project"], dest: "project", required: true, metavar: "PROJECT", help: "Project name or namespace/project (e.g. 'my-project' or 'tom/my-project')" },
25
+ { flags: ["-d", "--description"], dest: "description", metavar: "TEXT", help: "Project description (optional)" },
26
+ { flags: ["--dash-url", "--api-url"], dest: "dash_url", metavar: "URL", help: "ML-Dash server URL (default: https://api.dash.ml)" },
27
+ ],
28
+ };
29
+ /** Split `-p` into (namespace, project). null namespace means "the caller's own". */
30
+ export function splitProjectArg(raw) {
31
+ const parts = raw.replace(/^\/+|\/+$/g, "").split("/");
32
+ if (parts.length > 2 || parts.some((p) => p === ""))
33
+ return null;
34
+ return parts.length === 1 ? { project: parts[0] } : { namespace: parts[0], project: parts[1] };
35
+ }
36
+ export async function run(args) {
37
+ const ctx = resolveContext(args);
38
+ const raw = String(args.project);
39
+ const split = splitProjectArg(raw);
40
+ if (!split) {
41
+ console.error(`${red("Error:")} Project can have at most 2 parts (namespace/project).\n` +
42
+ `Got: ${raw}\n\nExamples:\n ml-dash create -p new-project\n ml-dash create -p geyang/new-project`);
43
+ return 1;
44
+ }
45
+ if (!ctx.apiKey) {
46
+ console.error(`${red("Error:")} ${notAuthenticatedMessage(ctx)}`);
47
+ return 1;
48
+ }
49
+ const client = makeClient(ctx, split.namespace);
50
+ const description = typeof args.description === "string" ? args.description : undefined;
51
+ let namespace = split.namespace;
52
+ try {
53
+ namespace = await client.namespace();
54
+ if (!namespace) {
55
+ console.error(`${red("Error:")} Could not determine namespace. Please login first.`);
56
+ return 1;
57
+ }
58
+ console.log(dim(`Creating project '${split.project}' in namespace '${namespace}'`));
59
+ const result = await client.createProject(split.project, description);
60
+ const project = result?.project ?? result ?? {};
61
+ const projectSlug = project.slug ?? split.project;
62
+ const projectId = asId(project.id);
63
+ console.log(`${green("✓")} Project created successfully!`);
64
+ console.log(` Name: ${bold(projectSlug)}`);
65
+ console.log(` Namespace: ${bold(namespace)}`);
66
+ if (projectId)
67
+ console.log(` ID: ${projectId}`);
68
+ if (description)
69
+ console.log(` Description: ${description}`);
70
+ console.log(`\n View at: https://dash.ml/@${namespace}/${projectSlug}`);
71
+ return 0;
72
+ }
73
+ catch (e) {
74
+ if (e instanceof HttpError && e.status === 409) {
75
+ console.log(`${yellow("⚠")} Project '${bold(split.project)}' already exists in namespace ` +
76
+ `'${bold(namespace ?? "(current)")}'`);
77
+ return 0;
78
+ }
79
+ console.error(`${red("Error creating project:")} ${e.message}`);
80
+ return 1;
81
+ }
82
+ }