@dreamlake/dreamlake-cli 0.2.0 → 0.9.2
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/bin/dreamlake.js +38 -26
- package/package.json +19 -27
- package/README.md +0 -171
- package/dist/cli/auth/commands.js +0 -240
- package/dist/cli/auth/constants.js +0 -16
- package/dist/cli/auth/credentials.js +0 -157
- package/dist/cli/auth/device-flow.js +0 -134
- package/dist/cli/auth/device-secret.js +0 -34
- package/dist/cli/client.js +0 -99
- package/dist/cli/config.js +0 -81
- package/dist/cli/create/index.js +0 -204
- package/dist/cli/delete/index.js +0 -228
- package/dist/cli/download/index.js +0 -128
- package/dist/cli/glob.js +0 -45
- package/dist/cli/graphql-helpers.js +0 -42
- package/dist/cli/graphql.js +0 -47
- package/dist/cli/helpers.js +0 -106
- package/dist/cli/index.js +0 -95
- package/dist/cli/list/index.js +0 -254
- package/dist/cli/org/index.js +0 -348
- package/dist/cli/pipeline/index.js +0 -606
- package/dist/cli/progress.js +0 -65
- package/dist/cli/prompt.js +0 -17
- package/dist/cli/resources.js +0 -134
- package/dist/cli/target.js +0 -85
- package/dist/cli/team/index.js +0 -411
- package/dist/cli/update/index.js +0 -256
- package/dist/cli/upload/index.js +0 -263
- package/dist/cli/upload/kinds.js +0 -85
- package/dist/cli/upload/multipart.js +0 -211
- package/dist/cli/workflow/index.js +0 -627
package/dist/cli/graphql.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// Minimal GraphQL client. Organization/team operations live on the
|
|
2
|
-
// dreamlake-server GraphQL endpoint (POST {server}/graphql) rather than
|
|
3
|
-
// the REST routes the rest of the CLI uses — same bearer token, same host.
|
|
4
|
-
import { resolveRemote, resolveToken } from "./config.js";
|
|
5
|
-
export class GraphQLClientError extends Error {
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Run a GraphQL query/mutation against {server}/graphql with the saved
|
|
9
|
-
* token. Throws GraphQLClientError on transport errors or a non-empty
|
|
10
|
-
* `errors` array (messages joined). Returns the `data` payload.
|
|
11
|
-
*/
|
|
12
|
-
export async function gql(query, variables = {}, opts = {}) {
|
|
13
|
-
const token = resolveToken();
|
|
14
|
-
if (!token) {
|
|
15
|
-
throw new GraphQLClientError("not authenticated. run 'dreamlake login' first");
|
|
16
|
-
}
|
|
17
|
-
const remote = resolveRemote();
|
|
18
|
-
let res;
|
|
19
|
-
try {
|
|
20
|
-
res = await fetch(`${remote}/graphql`, {
|
|
21
|
-
method: "POST",
|
|
22
|
-
headers: {
|
|
23
|
-
"Content-Type": "application/json",
|
|
24
|
-
Authorization: `Bearer ${token}`,
|
|
25
|
-
},
|
|
26
|
-
body: JSON.stringify({ query, variables }),
|
|
27
|
-
signal: AbortSignal.timeout(opts.timeoutMs ?? 30000),
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
catch (err) {
|
|
31
|
-
throw new GraphQLClientError(`request failed: ${err.message}`);
|
|
32
|
-
}
|
|
33
|
-
let body;
|
|
34
|
-
try {
|
|
35
|
-
body = (await res.json());
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
throw new GraphQLClientError(`(${res.status}) non-JSON response`);
|
|
39
|
-
}
|
|
40
|
-
if (body.errors && body.errors.length > 0) {
|
|
41
|
-
throw new GraphQLClientError(body.errors.map((e) => e.message).join("; "));
|
|
42
|
-
}
|
|
43
|
-
if (!res.ok) {
|
|
44
|
-
throw new GraphQLClientError(`(${res.status}) request failed`);
|
|
45
|
-
}
|
|
46
|
-
return body.data;
|
|
47
|
-
}
|
package/dist/cli/helpers.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// Small shared helpers for the dreamlake CLI subcommands. Adapted from
|
|
2
|
-
// lakeshore/src/cli/helpers.ts — zero dependencies beyond `yaml` (already
|
|
3
|
-
// a dep for the auth file). Keep this file presentation-only: glyphs,
|
|
4
|
-
// table rendering, JSON emit, CSV/tag parsing.
|
|
5
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import YAML from "yaml";
|
|
8
|
-
// ─── ✓ / ✗ / ⚠ glyph wrappers ────────────────────────────────────────
|
|
9
|
-
export function ok(line) {
|
|
10
|
-
process.stdout.write(`✓ ${line}\n`);
|
|
11
|
-
}
|
|
12
|
-
export function warn(line) {
|
|
13
|
-
process.stdout.write(`⚠ ${line}\n`);
|
|
14
|
-
}
|
|
15
|
-
export function fail(line) {
|
|
16
|
-
process.stderr.write(`✗ ${line}\n`);
|
|
17
|
-
}
|
|
18
|
-
// ─── splitCsv — comma-separated values → trimmed non-empty list ──────
|
|
19
|
-
//
|
|
20
|
-
// Used for `--tags a,b,c` and `--bindr name1,name2`. Mirrors the Python
|
|
21
|
-
// CLI's `[t.strip() for t in s.split(",") if t.strip()]`.
|
|
22
|
-
export function splitCsv(raw) {
|
|
23
|
-
if (!raw)
|
|
24
|
-
return [];
|
|
25
|
-
return raw
|
|
26
|
-
.split(",")
|
|
27
|
-
.map((s) => s.trim())
|
|
28
|
-
.filter((s) => s.length > 0);
|
|
29
|
-
}
|
|
30
|
-
// ─── renderTable ─────────────────────────────────────────────────────
|
|
31
|
-
//
|
|
32
|
-
// Minimal monospaced table renderer. Columns are exactly the `cols`
|
|
33
|
-
// names; each row may have arbitrary keys (others are ignored).
|
|
34
|
-
// Returns the rendered string (caller prints).
|
|
35
|
-
export function renderTable(rows, cols) {
|
|
36
|
-
if (rows.length === 0) {
|
|
37
|
-
return "(none)\n";
|
|
38
|
-
}
|
|
39
|
-
const stringRows = rows.map((r) => cols.map((c) => stringifyCell(r[c])));
|
|
40
|
-
const widths = cols.map((c, i) => Math.max(c.length, ...stringRows.map((r) => r[i].length)));
|
|
41
|
-
const lines = [];
|
|
42
|
-
// Header.
|
|
43
|
-
lines.push(cols
|
|
44
|
-
.map((c, i) => c.padEnd(widths[i]))
|
|
45
|
-
.join(" ")
|
|
46
|
-
.trimEnd());
|
|
47
|
-
// Body.
|
|
48
|
-
for (const r of stringRows) {
|
|
49
|
-
lines.push(r.map((cell, i) => cell.padEnd(widths[i])).join(" ").trimEnd());
|
|
50
|
-
}
|
|
51
|
-
return lines.join("\n") + "\n";
|
|
52
|
-
}
|
|
53
|
-
function stringifyCell(v) {
|
|
54
|
-
if (v == null)
|
|
55
|
-
return "—";
|
|
56
|
-
if (typeof v === "string")
|
|
57
|
-
return v;
|
|
58
|
-
if (typeof v === "number" || typeof v === "boolean")
|
|
59
|
-
return String(v);
|
|
60
|
-
if (Array.isArray(v))
|
|
61
|
-
return v.map(stringifyCell).join(", ");
|
|
62
|
-
return JSON.stringify(v);
|
|
63
|
-
}
|
|
64
|
-
// ─── emitJson — for `--json` / `--json-output` flags ─────────────────
|
|
65
|
-
//
|
|
66
|
-
// Single canonical JSON emitter. 2-space indent, trailing newline.
|
|
67
|
-
export function emitJson(value) {
|
|
68
|
-
process.stdout.write(JSON.stringify(value, null, 2) + "\n");
|
|
69
|
-
}
|
|
70
|
-
// ─── loadStructuredFile — YAML or JSON ───────────────────────────────
|
|
71
|
-
export function loadStructuredFile(filePath) {
|
|
72
|
-
const resolved = path.resolve(filePath);
|
|
73
|
-
if (!existsSync(resolved)) {
|
|
74
|
-
throw new Error(`file not found: ${resolved}`);
|
|
75
|
-
}
|
|
76
|
-
const text = readFileSync(resolved, "utf8");
|
|
77
|
-
const trimmed = text.trim();
|
|
78
|
-
if (trimmed.startsWith("{")) {
|
|
79
|
-
try {
|
|
80
|
-
const parsed = JSON.parse(trimmed);
|
|
81
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
82
|
-
return parsed;
|
|
83
|
-
}
|
|
84
|
-
throw new Error("expected an object at top level");
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
throw new Error(`failed to parse JSON ${resolved}: ${err.message}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
try {
|
|
91
|
-
const parsed = YAML.parse(text);
|
|
92
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
93
|
-
return parsed;
|
|
94
|
-
}
|
|
95
|
-
throw new Error("expected an object at top level");
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
throw new Error(`failed to parse YAML ${resolved}: ${err.message}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// ─── humanSize — bytes → "1.5 MB" / "12.3 KB" ───────────────────────
|
|
102
|
-
export function humanSize(bytes) {
|
|
103
|
-
if (bytes > 1024 * 1024)
|
|
104
|
-
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
105
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
106
|
-
}
|
package/dist/cli/index.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// `dreamlake` CLI entry. Wires Commander to subcommand modules. Mirrors
|
|
2
|
-
// the command surface of dreamlake-py's `dreamlake.cli` (the data-warehouse
|
|
3
|
-
// CLI), implemented in lakeshore's TypeScript conventions.
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import { authFilePath, BUILTIN_ENVS } from "./auth/credentials.js";
|
|
6
|
-
import { runLogin, runLogout, runProfile, runEnvList, runEnvUse, runEnvRemove, } from "./auth/commands.js";
|
|
7
|
-
import { registerUploadCommand } from "./upload/index.js";
|
|
8
|
-
import { registerDownloadCommand } from "./download/index.js";
|
|
9
|
-
import { registerListCommand } from "./list/index.js";
|
|
10
|
-
import { registerCreateCommand } from "./create/index.js";
|
|
11
|
-
import { registerUpdateCommand } from "./update/index.js";
|
|
12
|
-
import { registerDeleteCommand } from "./delete/index.js";
|
|
13
|
-
import { registerOrgCommand } from "./org/index.js";
|
|
14
|
-
import { registerTeamCommand } from "./team/index.js";
|
|
15
|
-
import { registerPipelineCommand } from "./pipeline/index.js";
|
|
16
|
-
import { registerWorkflowCommand } from "./workflow/index.js";
|
|
17
|
-
async function main(argv) {
|
|
18
|
-
const program = new Command();
|
|
19
|
-
program
|
|
20
|
-
.name("dreamlake")
|
|
21
|
-
.description("DreamLake CLI — upload/download assets and manage episodes, bindrs, and datasets.")
|
|
22
|
-
.showHelpAfterError();
|
|
23
|
-
// ─── auth ────────────────────────────────────────────────────────
|
|
24
|
-
//
|
|
25
|
-
// Credentials are stored at `$XDG_CONFIG_HOME/dreamlake/auth.yml`
|
|
26
|
-
// (default `~/.config/dreamlake/auth.yml`) with chmod 600.
|
|
27
|
-
program
|
|
28
|
-
.command("login")
|
|
29
|
-
.description(`authenticate with DreamLake (built-in envs: ${Object.keys(BUILTIN_ENVS).join(", ")})`)
|
|
30
|
-
.option("--env <name>", "environment to log into (built-in or custom; default: active, else staging)")
|
|
31
|
-
.option("--url <url>", "DreamLake server URL (for a custom env, or to override)")
|
|
32
|
-
.option("--bss <url>", "BSS server URL (for a custom env, or to override)")
|
|
33
|
-
.option("--auth <url>", "vuer-auth server URL (must match what the target server trusts)")
|
|
34
|
-
.option("--no-browser", "don't open the browser automatically")
|
|
35
|
-
.option("--token <jwt>", "save a token directly instead of the device flow")
|
|
36
|
-
.action(async (opts) => {
|
|
37
|
-
const rc = await runLogin({
|
|
38
|
-
env: opts.env,
|
|
39
|
-
url: opts.url,
|
|
40
|
-
bss: opts.bss,
|
|
41
|
-
auth: opts.auth,
|
|
42
|
-
noBrowser: opts.browser === false,
|
|
43
|
-
token: opts.token,
|
|
44
|
-
});
|
|
45
|
-
process.exit(rc);
|
|
46
|
-
});
|
|
47
|
-
program
|
|
48
|
-
.command("logout")
|
|
49
|
-
.description("log out of the active environment (removes its saved token)")
|
|
50
|
-
.action(async () => {
|
|
51
|
-
process.exit(await runLogout());
|
|
52
|
-
});
|
|
53
|
-
program
|
|
54
|
-
.command("profile")
|
|
55
|
-
.description("show the current authenticated user")
|
|
56
|
-
.option("--url <url>", "override the saved server URL")
|
|
57
|
-
.action(async (opts) => {
|
|
58
|
-
process.exit(await runProfile({ url: opts.url }));
|
|
59
|
-
});
|
|
60
|
-
// ─── env (switch between logged-in environments) ─────────────────
|
|
61
|
-
const env = program
|
|
62
|
-
.command("env")
|
|
63
|
-
.description(`switch between environments (saved at ${authFilePath()})`);
|
|
64
|
-
env
|
|
65
|
-
.command("list")
|
|
66
|
-
.description("list logged-in environments (* = active)")
|
|
67
|
-
.option("--json", "emit JSON")
|
|
68
|
-
.action(async (opts) => process.exit(await runEnvList(opts)));
|
|
69
|
-
env
|
|
70
|
-
.command("use")
|
|
71
|
-
.description("switch the active environment")
|
|
72
|
-
.argument("<name>", "environment name")
|
|
73
|
-
.action(async (name) => process.exit(await runEnvUse(name)));
|
|
74
|
-
env
|
|
75
|
-
.command("remove")
|
|
76
|
-
.description("remove a saved environment")
|
|
77
|
-
.argument("<name>", "environment name")
|
|
78
|
-
.action(async (name) => process.exit(await runEnvRemove(name)));
|
|
79
|
-
// ─── data-warehouse commands ─────────────────────────────────────
|
|
80
|
-
registerUploadCommand(program);
|
|
81
|
-
registerDownloadCommand(program);
|
|
82
|
-
registerListCommand(program);
|
|
83
|
-
registerCreateCommand(program);
|
|
84
|
-
registerUpdateCommand(program);
|
|
85
|
-
registerDeleteCommand(program);
|
|
86
|
-
registerOrgCommand(program);
|
|
87
|
-
registerTeamCommand(program);
|
|
88
|
-
registerPipelineCommand(program);
|
|
89
|
-
registerWorkflowCommand(program);
|
|
90
|
-
await program.parseAsync(argv);
|
|
91
|
-
}
|
|
92
|
-
main(process.argv).catch((err) => {
|
|
93
|
-
process.stderr.write(`${err.stack ?? err}\n`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
});
|
package/dist/cli/list/index.js
DELETED
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
// `dreamlake list ...` — assets (default), bindrs, datasets, episodes.
|
|
2
|
-
// Port of dreamlake-py's cli/commands/list.py. Where the Python CLI used
|
|
3
|
-
// an interactive 20-per-page pager, we fetch all pages and render one
|
|
4
|
-
// table (more script-friendly); `--json` emits raw JSON.
|
|
5
|
-
import { resolveNamespace, resolveRemote, resolveToken } from "../config.js";
|
|
6
|
-
import { emitJson, fail, humanSize, renderTable } from "../helpers.js";
|
|
7
|
-
import { fetchAllContents, fetchAllEpisodes, fetchAllPages, fetchProjects, nodePathToCommaPrefix, relPathUnder, resolveProjectCtx, } from "../resources.js";
|
|
8
|
-
import { formatProject, formatTarget, parseTarget } from "../target.js";
|
|
9
|
-
export async function runListAssets(opts) {
|
|
10
|
-
if (!opts.episode) {
|
|
11
|
-
fail("--episode is required");
|
|
12
|
-
return 1;
|
|
13
|
-
}
|
|
14
|
-
let t;
|
|
15
|
-
try {
|
|
16
|
-
t = parseTarget(opts.episode);
|
|
17
|
-
}
|
|
18
|
-
catch (err) {
|
|
19
|
-
fail(err.message);
|
|
20
|
-
return 1;
|
|
21
|
-
}
|
|
22
|
-
const remote = resolveRemote();
|
|
23
|
-
const token = resolveToken();
|
|
24
|
-
if (!token) {
|
|
25
|
-
fail("not authenticated. run 'dreamlake login' first");
|
|
26
|
-
return 1;
|
|
27
|
-
}
|
|
28
|
-
const namespace = await resolveNamespace(t.namespace, { token, remote });
|
|
29
|
-
if (!namespace) {
|
|
30
|
-
fail("namespace not specified and no authenticated user found. run 'dreamlake login'");
|
|
31
|
-
return 1;
|
|
32
|
-
}
|
|
33
|
-
t.namespace = namespace;
|
|
34
|
-
// Resolve the container node (episode root, or project root when the
|
|
35
|
-
// target has no episode), then list its files via /nodes/:id/contents.
|
|
36
|
-
let containerId;
|
|
37
|
-
let containerPrefix;
|
|
38
|
-
try {
|
|
39
|
-
if (t.episode) {
|
|
40
|
-
const episodes = await fetchAllEpisodes(remote, t.namespace, t.project, token);
|
|
41
|
-
const ep = episodes.find((e) => e.name === t.episode);
|
|
42
|
-
if (!ep) {
|
|
43
|
-
fail(`episode '${t.episode}' not found in ${t.project}@${t.namespace}`);
|
|
44
|
-
return 1;
|
|
45
|
-
}
|
|
46
|
-
containerId = ep.id;
|
|
47
|
-
containerPrefix = nodePathToCommaPrefix(ep.nodePath ?? `/${t.project}/${t.episode}`);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
const projects = await fetchProjects(remote, token, t.namespace);
|
|
51
|
-
const proj = projects.find((p) => p.slug === t.project);
|
|
52
|
-
if (!proj) {
|
|
53
|
-
fail(`project '${t.project}' not found in namespace ${t.namespace}`);
|
|
54
|
-
return 1;
|
|
55
|
-
}
|
|
56
|
-
containerId = proj.id;
|
|
57
|
-
containerPrefix = `,${t.project},`;
|
|
58
|
-
}
|
|
59
|
-
const files = await fetchAllContents(remote, token, containerId, opts.type);
|
|
60
|
-
let rows = files.map((f) => ({
|
|
61
|
-
path: relPathUnder(f, containerPrefix),
|
|
62
|
-
kind: f.kind,
|
|
63
|
-
size: f.metadata?.size != null ? humanSize(f.metadata.size) : "—",
|
|
64
|
-
created: String(f.createdAt ?? "").slice(0, 10),
|
|
65
|
-
}));
|
|
66
|
-
if (opts.prefix) {
|
|
67
|
-
const want = opts.prefix.replace(/^\/+/, "");
|
|
68
|
-
rows = rows.filter((r) => r.path.startsWith(want));
|
|
69
|
-
}
|
|
70
|
-
if (opts.json) {
|
|
71
|
-
emitJson(rows);
|
|
72
|
-
return 0;
|
|
73
|
-
}
|
|
74
|
-
process.stdout.write(`Listing ${opts.type ?? "all"} assets in ${formatTarget(t)}\n\n`);
|
|
75
|
-
if (rows.length === 0) {
|
|
76
|
-
process.stdout.write(" (no assets found)\n");
|
|
77
|
-
return 0;
|
|
78
|
-
}
|
|
79
|
-
process.stdout.write(renderTable(rows, ["path", "kind", "size", "created"]));
|
|
80
|
-
process.stdout.write(`\n ${rows.length} asset(s)\n`);
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
fail(err.message);
|
|
85
|
-
return 1;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// ─── projects ────────────────────────────────────────────────────────
|
|
89
|
-
export async function runListProjects(opts) {
|
|
90
|
-
const remote = resolveRemote();
|
|
91
|
-
const token = resolveToken();
|
|
92
|
-
if (!token) {
|
|
93
|
-
fail("not authenticated. run 'dreamlake login' first");
|
|
94
|
-
return 1;
|
|
95
|
-
}
|
|
96
|
-
const namespace = await resolveNamespace(opts.namespace, { token, remote });
|
|
97
|
-
if (!namespace) {
|
|
98
|
-
fail("namespace not specified and no authenticated user found. run 'dreamlake login'");
|
|
99
|
-
return 1;
|
|
100
|
-
}
|
|
101
|
-
const projects = await fetchProjects(remote, token, namespace);
|
|
102
|
-
if (opts.json) {
|
|
103
|
-
emitJson(projects);
|
|
104
|
-
return 0;
|
|
105
|
-
}
|
|
106
|
-
process.stdout.write(`Listing projects in namespace ${namespace}\n\n`);
|
|
107
|
-
const rows = projects.map((p) => ({
|
|
108
|
-
slug: p.slug,
|
|
109
|
-
name: p.name ?? "",
|
|
110
|
-
visibility: p.visibility ?? "",
|
|
111
|
-
bindrs: p.bindrCount ?? 0,
|
|
112
|
-
datasets: p.datasetCount ?? 0,
|
|
113
|
-
created: String(p.createdAt ?? "").slice(0, 10),
|
|
114
|
-
}));
|
|
115
|
-
process.stdout.write(renderTable(rows, ["slug", "name", "visibility", "bindrs", "datasets", "created"]));
|
|
116
|
-
process.stdout.write(`\n ${projects.length} project(s)\n`);
|
|
117
|
-
return 0;
|
|
118
|
-
}
|
|
119
|
-
// ─── bindrs / datasets / episodes ────────────────────────────────────
|
|
120
|
-
export async function runListBindrs(opts) {
|
|
121
|
-
if (!opts.project) {
|
|
122
|
-
fail("--project is required for listing bindrs");
|
|
123
|
-
return 1;
|
|
124
|
-
}
|
|
125
|
-
const ctx = await resolveProjectCtx(opts.project);
|
|
126
|
-
if (!ctx)
|
|
127
|
-
return 1;
|
|
128
|
-
const { s, token, remote } = ctx;
|
|
129
|
-
const result = await fetchAllPages(remote, `/namespaces/${s.namespace}/projects/${s.project}/bindrs`, token, "bindrs");
|
|
130
|
-
if (result === "not-found") {
|
|
131
|
-
process.stdout.write(" (project not found)\n");
|
|
132
|
-
return 0;
|
|
133
|
-
}
|
|
134
|
-
if (opts.json) {
|
|
135
|
-
emitJson(result.items);
|
|
136
|
-
return 0;
|
|
137
|
-
}
|
|
138
|
-
process.stdout.write(`Listing bindrs in ${formatProject(s)}\n\n`);
|
|
139
|
-
const rows = result.items.map((d) => ({
|
|
140
|
-
name: String(d.name ?? ""),
|
|
141
|
-
files: Array.isArray(d.members) ? d.members.length : 0,
|
|
142
|
-
tags: Array.isArray(d.tags) ? d.tags.join(", ") : "",
|
|
143
|
-
description: String(d.description ?? "").slice(0, 40),
|
|
144
|
-
created: String(d.createdAt ?? "").slice(0, 10),
|
|
145
|
-
}));
|
|
146
|
-
process.stdout.write(renderTable(rows, ["name", "files", "tags", "description", "created"]));
|
|
147
|
-
process.stdout.write(`\n ${result.total} bindr(s)\n`);
|
|
148
|
-
return 0;
|
|
149
|
-
}
|
|
150
|
-
export async function runListDatasets(opts) {
|
|
151
|
-
if (!opts.project) {
|
|
152
|
-
fail("--project is required for listing datasets");
|
|
153
|
-
return 1;
|
|
154
|
-
}
|
|
155
|
-
const ctx = await resolveProjectCtx(opts.project);
|
|
156
|
-
if (!ctx)
|
|
157
|
-
return 1;
|
|
158
|
-
const { s, token, remote } = ctx;
|
|
159
|
-
const result = await fetchAllPages(remote, `/namespaces/${s.namespace}/projects/${s.project}/datasets`, token, "datasets");
|
|
160
|
-
if (result === "not-found") {
|
|
161
|
-
process.stdout.write(" (project not found)\n");
|
|
162
|
-
return 0;
|
|
163
|
-
}
|
|
164
|
-
if (opts.json) {
|
|
165
|
-
emitJson(result.items);
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
process.stdout.write(`Listing datasets in ${formatProject(s)}\n\n`);
|
|
169
|
-
const rows = result.items.map((d) => ({
|
|
170
|
-
name: String(d.name ?? ""),
|
|
171
|
-
bindrs: Array.isArray(d.bindrs) ? d.bindrs.length : 0,
|
|
172
|
-
tags: Array.isArray(d.tags) ? d.tags.join(", ") : "",
|
|
173
|
-
description: String(d.description ?? "").slice(0, 40),
|
|
174
|
-
created: String(d.createdAt ?? "").slice(0, 10),
|
|
175
|
-
}));
|
|
176
|
-
process.stdout.write(renderTable(rows, ["name", "bindrs", "tags", "description", "created"]));
|
|
177
|
-
process.stdout.write(`\n ${result.total} dataset(s)\n`);
|
|
178
|
-
return 0;
|
|
179
|
-
}
|
|
180
|
-
export async function runListEpisodes(opts) {
|
|
181
|
-
if (!opts.project) {
|
|
182
|
-
fail("--project is required for listing episodes");
|
|
183
|
-
return 1;
|
|
184
|
-
}
|
|
185
|
-
const ctx = await resolveProjectCtx(opts.project);
|
|
186
|
-
if (!ctx)
|
|
187
|
-
return 1;
|
|
188
|
-
const { s, token, remote } = ctx;
|
|
189
|
-
const result = await fetchAllPages(remote, `/namespaces/${s.namespace}/projects/${s.project}/episodes`, token, "episodes");
|
|
190
|
-
if (result === "not-found") {
|
|
191
|
-
process.stdout.write(" (project not found)\n");
|
|
192
|
-
return 0;
|
|
193
|
-
}
|
|
194
|
-
if (opts.json) {
|
|
195
|
-
emitJson(result.items);
|
|
196
|
-
return 0;
|
|
197
|
-
}
|
|
198
|
-
process.stdout.write(`Listing episodes in ${formatProject(s)}\n\n`);
|
|
199
|
-
const rows = result.items.map((ep) => ({
|
|
200
|
-
name: String(ep.name ?? ""),
|
|
201
|
-
path: String(ep.nodePath ?? ""),
|
|
202
|
-
status: String(ep.status ?? ""),
|
|
203
|
-
tags: Array.isArray(ep.tags) ? ep.tags.join(", ") : "",
|
|
204
|
-
created: String(ep.createdAt ?? "").slice(0, 10),
|
|
205
|
-
}));
|
|
206
|
-
process.stdout.write(renderTable(rows, ["name", "path", "status", "tags", "created"]));
|
|
207
|
-
process.stdout.write(`\n ${result.total} episode(s)\n`);
|
|
208
|
-
return 0;
|
|
209
|
-
}
|
|
210
|
-
// ─── registration ────────────────────────────────────────────────────
|
|
211
|
-
export function registerListCommand(program) {
|
|
212
|
-
const list = program
|
|
213
|
-
.command("list")
|
|
214
|
-
.description("list assets (default), or projects / bindrs / datasets / episodes")
|
|
215
|
-
.option("--episode <target>", "episode scope: space[@namespace][:episode]")
|
|
216
|
-
.option("--prefix <path>", "filter assets by path prefix")
|
|
217
|
-
.option("--type <category>", "filter assets by category")
|
|
218
|
-
.option("--json", "emit JSON instead of a table")
|
|
219
|
-
.action(async (opts) => {
|
|
220
|
-
process.exit(await runListAssets(opts));
|
|
221
|
-
});
|
|
222
|
-
list
|
|
223
|
-
.command("project")
|
|
224
|
-
.description("list projects in a namespace")
|
|
225
|
-
.option("--namespace <slug>", "namespace (default: your own)")
|
|
226
|
-
.option("--json", "emit JSON instead of a table")
|
|
227
|
-
.action(async (opts) => {
|
|
228
|
-
process.exit(await runListProjects(opts));
|
|
229
|
-
});
|
|
230
|
-
list
|
|
231
|
-
.command("bindr")
|
|
232
|
-
.description("list bindrs in a project")
|
|
233
|
-
.requiredOption("--project <target>", "project scope: space[@namespace]")
|
|
234
|
-
.option("--json", "emit JSON instead of a table")
|
|
235
|
-
.action(async (opts) => {
|
|
236
|
-
process.exit(await runListBindrs(opts));
|
|
237
|
-
});
|
|
238
|
-
list
|
|
239
|
-
.command("dataset")
|
|
240
|
-
.description("list datasets in a project")
|
|
241
|
-
.requiredOption("--project <target>", "project scope: space[@namespace]")
|
|
242
|
-
.option("--json", "emit JSON instead of a table")
|
|
243
|
-
.action(async (opts) => {
|
|
244
|
-
process.exit(await runListDatasets(opts));
|
|
245
|
-
});
|
|
246
|
-
list
|
|
247
|
-
.command("episode")
|
|
248
|
-
.description("list episodes in a project")
|
|
249
|
-
.requiredOption("--project <target>", "project scope: space[@namespace]")
|
|
250
|
-
.option("--json", "emit JSON instead of a table")
|
|
251
|
-
.action(async (opts) => {
|
|
252
|
-
process.exit(await runListEpisodes(opts));
|
|
253
|
-
});
|
|
254
|
-
}
|