@dreamlake/dreamlake-cli 0.1.0 → 0.9.1
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 +30 -34
- package/package.json +18 -47
- package/LICENSE +0 -21
- package/README.md +0 -191
- 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 -97
- package/dist/cli/list/index.js +0 -254
- package/dist/cli/org/index.js +0 -348
- package/dist/cli/pipeline/index.js +0 -481
- 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/bin/dreamlake.js
CHANGED
|
@@ -1,41 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// (workspace dev — no `pnpm build` required).
|
|
2
|
+
"use strict";
|
|
3
|
+
const { spawnSync } = require("node:child_process");
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
function isMusl() {
|
|
6
|
+
if (process.platform !== "linux") return false;
|
|
7
|
+
try {
|
|
8
|
+
return !process.report.getReport().header.glibcVersionRuntime;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
9
13
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
14
|
+
const key = process.platform + "-" + process.arch + (isMusl() ? "-musl" : "");
|
|
15
|
+
const pkg = "@dreamlake/dreamlake-cli-" + key;
|
|
16
|
+
const bin = process.platform === "win32" ? "dreamlake.exe" : "dreamlake";
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"dreamlake: dist/ is missing and tsx is not installed — run `pnpm install && pnpm build` first",
|
|
27
|
-
);
|
|
28
|
-
process.exit(2);
|
|
29
|
-
}
|
|
30
|
-
tsx.register();
|
|
31
|
-
await import(pathToFileURL(srcEntry).href);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
console.error("dreamlake: neither dist/cli/index.js nor src/cli/index.ts found");
|
|
35
|
-
process.exit(2);
|
|
18
|
+
let binPath;
|
|
19
|
+
try {
|
|
20
|
+
binPath = require.resolve(pkg + "/" + bin);
|
|
21
|
+
} catch {
|
|
22
|
+
process.stderr.write(
|
|
23
|
+
"dreamlake: no binary for " + key + ".\n" +
|
|
24
|
+
"The platform package " + pkg + " is not installed. npm skips it when\n" +
|
|
25
|
+
"os/cpu do not match, and --no-optional skips it always. Reinstall without\n" +
|
|
26
|
+
"--no-optional, or use the native installer:\n" +
|
|
27
|
+
" curl -fsSL https://dl.dreamlake.ai/install.sh | sh\n",
|
|
28
|
+
);
|
|
29
|
+
process.exit(1);
|
|
36
30
|
}
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
const r = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
|
|
33
|
+
if (r.error) {
|
|
34
|
+
process.stderr.write("dreamlake: " + r.error.message + "\n");
|
|
40
35
|
process.exit(1);
|
|
41
|
-
}
|
|
36
|
+
}
|
|
37
|
+
process.exit(r.status === null ? 1 : r.status);
|
package/package.json
CHANGED
|
@@ -1,58 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreamlake/dreamlake-cli",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "dreamlake — data-warehouse CLI. The `dreamlake` command uploads/downloads assets and manages episodes, bindrs, and datasets against a DreamLake server + BSS.",
|
|
5
|
-
"private": false,
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"dreamlake": "bin/dreamlake.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"bin",
|
|
13
|
-
"dist",
|
|
14
|
-
"!dist/**/*.map"
|
|
15
|
-
],
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=20"
|
|
18
|
-
},
|
|
3
|
+
"version": "0.9.1",
|
|
19
4
|
"repository": {
|
|
20
5
|
"type": "git",
|
|
21
6
|
"url": "git+https://github.com/dreamlake-ai/dreamlake-cli.git"
|
|
22
7
|
},
|
|
23
|
-
"homepage": "https://
|
|
24
|
-
"
|
|
25
|
-
|
|
8
|
+
"homepage": "https://cli.dreamlake.ai",
|
|
9
|
+
"description": "dreamlake — data-warehouse CLI. The `dreamlake` command uploads/downloads assets, manages episodes, bindrs, datasets, organizations and teams, and versions artifacts, workflow specs and pipelines against a DreamLake server + BSS.",
|
|
10
|
+
"bin": {
|
|
11
|
+
"dreamlake": "bin/dreamlake.js"
|
|
26
12
|
},
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"cli",
|
|
30
|
-
"data-warehouse",
|
|
31
|
-
"dataset",
|
|
32
|
-
"robotics"
|
|
13
|
+
"files": [
|
|
14
|
+
"bin"
|
|
33
15
|
],
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
"access": "public"
|
|
37
|
-
},
|
|
38
|
-
"scripts": {
|
|
39
|
-
"build": "tsc",
|
|
40
|
-
"cli": "tsx src/cli/index.ts",
|
|
41
|
-
"test": "tsc -p tsconfig.json --noEmit && node --import tsx --test src/cli/__tests__/*.test.ts",
|
|
42
|
-
"prepublishOnly": "npm test",
|
|
43
|
-
"prepack": "npm run build",
|
|
44
|
-
"docs:dev": "pnpm -C docs dev",
|
|
45
|
-
"docs:build": "pnpm -C docs build",
|
|
46
|
-
"docs:preview": "pnpm -C docs preview"
|
|
47
|
-
},
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"@types/node": "^22.10.0",
|
|
50
|
-
"tsx": "^4.19.0",
|
|
51
|
-
"typescript": "^5.7.0"
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
52
18
|
},
|
|
53
|
-
"
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
56
|
-
"
|
|
19
|
+
"optionalDependencies": {
|
|
20
|
+
"@dreamlake/dreamlake-cli-darwin-arm64": "0.9.1",
|
|
21
|
+
"@dreamlake/dreamlake-cli-darwin-x64": "0.9.1",
|
|
22
|
+
"@dreamlake/dreamlake-cli-linux-x64": "0.9.1",
|
|
23
|
+
"@dreamlake/dreamlake-cli-linux-arm64": "0.9.1",
|
|
24
|
+
"@dreamlake/dreamlake-cli-linux-x64-musl": "0.9.1",
|
|
25
|
+
"@dreamlake/dreamlake-cli-linux-arm64-musl": "0.9.1",
|
|
26
|
+
"@dreamlake/dreamlake-cli-win32-x64": "0.9.1",
|
|
27
|
+
"@dreamlake/dreamlake-cli-win32-arm64": "0.9.1"
|
|
57
28
|
}
|
|
58
29
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 DreamLake AI
|
|
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
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
# dreamlake-cli
|
|
2
|
-
|
|
3
|
-
`dreamlake` — a data-warehouse CLI for DreamLake. Uploads/downloads assets and
|
|
4
|
-
manages episodes, bindrs, and datasets against a DreamLake server + BSS
|
|
5
|
-
(big-streaming-server).
|
|
6
|
-
|
|
7
|
-
It is a TypeScript port of the CLI shipped in `dreamlake-py` (`dreamlake.cli`),
|
|
8
|
-
built on Commander + native `fetch`, pure ESM, with the token saved to a flat
|
|
9
|
-
YAML file instead of the system keyring.
|
|
10
|
-
|
|
11
|
-
> The `dreamlake` binary shares its name with the Python `dreamlake` entry
|
|
12
|
-
> point — this CLI is the TypeScript replacement. When both are on a machine,
|
|
13
|
-
> `PATH` order decides which one runs.
|
|
14
|
-
|
|
15
|
-
## Install
|
|
16
|
-
|
|
17
|
-
Requires Node.js >= 20.
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install -g @dreamlake/dreamlake-cli # or: pnpm add -g @dreamlake/dreamlake-cli
|
|
21
|
-
|
|
22
|
-
dreamlake --version
|
|
23
|
-
dreamlake --help
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Or run it without installing:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npx @dreamlake/dreamlake-cli --help
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Then authenticate and go:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
dreamlake login --env prod
|
|
36
|
-
dreamlake list project
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Auth & environments
|
|
40
|
-
|
|
41
|
-
The CLI knows two built-in environments — **`staging`** and **`prod`** — so you
|
|
42
|
-
never type their URLs. Each environment keeps its own token; switch between them
|
|
43
|
-
with one command.
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
dreamlake login --env staging # device-authorization flow (opens browser)
|
|
47
|
-
dreamlake login --env prod # built-in URLs; no --url/--bss needed
|
|
48
|
-
dreamlake login # re-auth the active env (or staging if none)
|
|
49
|
-
|
|
50
|
-
dreamlake env list # show logged-in envs (* = active)
|
|
51
|
-
dreamlake env use prod # switch active env — no URLs, no re-login
|
|
52
|
-
dreamlake env remove staging # forget a saved env
|
|
53
|
-
|
|
54
|
-
dreamlake profile # current user (shows [env: ...])
|
|
55
|
-
dreamlake logout # log out of the active env only
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Custom / self-hosted environments — define once, then switch by name forever:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
dreamlake login --env dev --url https://dev-api.example.com --bss https://dev-bss.example.com
|
|
62
|
-
dreamlake env use dev
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Flags / env vars (`DREAMLAKE_REMOTE`, `DREAMLAKE_BSS_URL`, `DREAMLAKE_API_KEY`)
|
|
66
|
-
still override the active environment for one-off commands. `--token <jwt>` saves
|
|
67
|
-
a token directly (skip the device flow). `--auth <url>` overrides the vuer-auth
|
|
68
|
-
server (built-ins set this automatically; staging needs `staging-auth.vuer.ai`).
|
|
69
|
-
|
|
70
|
-
Credentials live in `~/.config/dreamlake/auth.yml`
|
|
71
|
-
(`$XDG_CONFIG_HOME/dreamlake/auth.yml`, `chmod 600`) as a multi-environment file:
|
|
72
|
-
|
|
73
|
-
```yaml
|
|
74
|
-
current: prod
|
|
75
|
-
envs:
|
|
76
|
-
staging: { server, bss, auth, namespace, token }
|
|
77
|
-
prod: { server, bss, auth, namespace, token }
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Targets
|
|
81
|
-
|
|
82
|
-
Most commands take a target string:
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
--episode space[@namespace][:episode] # e.g. robotics@alice:2026/q1/run-042
|
|
86
|
-
--project space[@namespace] # e.g. robotics@alice
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
When `@namespace` is omitted it is resolved from your token (`/auth/me`).
|
|
90
|
-
|
|
91
|
-
## Commands
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
# Upload — single file, or a flat directory (resumable). Type auto-detected.
|
|
95
|
-
dreamlake upload ./mic.wav --episode robotics@alice:run-042 --to /microphone/front
|
|
96
|
-
dreamlake upload ./clip.mp4 --episode robotics:run-042 --to /camera/front --bindr cam-set
|
|
97
|
-
dreamlake upload ./capture/ --episode robotics@alice:run-042 --to /sensors --yes
|
|
98
|
-
|
|
99
|
-
# Download — a single file, or a whole folder/episode recursively.
|
|
100
|
-
dreamlake download --episode robotics@alice:run-042 --from /camera/front/clip.mp4 -o ./clip.mp4
|
|
101
|
-
dreamlake download --episode robotics@alice:run-042 --from /camera -o ./out/
|
|
102
|
-
|
|
103
|
-
# Projects. Private by default — pass --public (or --visibility public) to share.
|
|
104
|
-
dreamlake create project my-robots --description "robot captures" # private
|
|
105
|
-
dreamlake create project shared-set --public # public
|
|
106
|
-
dreamlake update project my-robots --public # flip visibility later
|
|
107
|
-
dreamlake list project
|
|
108
|
-
dreamlake delete project my-robots # HARD-deletes the whole subtree
|
|
109
|
-
|
|
110
|
-
# List — assets (default), or projects / bindrs / datasets / episodes.
|
|
111
|
-
dreamlake list --episode robotics@alice:run-042 --type video
|
|
112
|
-
dreamlake list project
|
|
113
|
-
dreamlake list bindr --project robotics@alice
|
|
114
|
-
dreamlake list dataset --project robotics@alice --json
|
|
115
|
-
dreamlake list episode --project robotics@alice
|
|
116
|
-
|
|
117
|
-
# Delete uploaded data (all HARD deletes — confirm prompt unless --yes).
|
|
118
|
-
dreamlake delete file /camera/front/clip.mp4 --episode robotics@alice:run-042
|
|
119
|
-
dreamlake delete episode run-042 --project robotics@alice
|
|
120
|
-
|
|
121
|
-
# Bindrs (members are episodes, matched by node-path glob).
|
|
122
|
-
dreamlake create bindr front-cam --project robotics@alice --episode "camera/front/*"
|
|
123
|
-
dreamlake update bindr front-cam --project robotics@alice --add "2026/04/*"
|
|
124
|
-
dreamlake delete bindr front-cam --project robotics@alice
|
|
125
|
-
|
|
126
|
-
# Datasets (members are bindrs, matched by name glob).
|
|
127
|
-
dreamlake create dataset train-v1 --project robotics@alice --tags robotics,training
|
|
128
|
-
dreamlake update dataset train-v1 --project robotics@alice --add "front-*"
|
|
129
|
-
dreamlake delete dataset train-v1 --project robotics@alice
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
> Videos are just files — upload/list/download them with `upload` / `list --type video`
|
|
133
|
-
> / `download`, no dedicated `video` command needed.
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
# Organizations (GraphQL-backed; same token). Members resolve by email/name.
|
|
137
|
-
dreamlake org list
|
|
138
|
-
dreamlake org show my-org
|
|
139
|
-
dreamlake org create my-org --name "My Org" --description "..." # you become OWNER + a namespace is created
|
|
140
|
-
dreamlake org update my-org --description "..."
|
|
141
|
-
dreamlake org delete my-org # must have no remaining projects
|
|
142
|
-
dreamlake org leave my-org
|
|
143
|
-
dreamlake org member list my-org
|
|
144
|
-
dreamlake org member add my-org alice@example.com --role member
|
|
145
|
-
dreamlake org member role my-org alice@example.com --role owner
|
|
146
|
-
dreamlake org member remove my-org alice@example.com
|
|
147
|
-
|
|
148
|
-
# Teams (scoped to an org; nestable via --parent; visible|secret).
|
|
149
|
-
dreamlake team list # all teams you belong to
|
|
150
|
-
dreamlake team list my-org # teams in one org
|
|
151
|
-
dreamlake team show my-org backend
|
|
152
|
-
dreamlake team create my-org backend --name "Backend" --visibility secret
|
|
153
|
-
dreamlake team create my-org api --parent backend # nested team
|
|
154
|
-
dreamlake team update my-org backend --visibility visible
|
|
155
|
-
dreamlake team delete my-org backend # must have no child teams
|
|
156
|
-
dreamlake team leave my-org backend
|
|
157
|
-
dreamlake team member list my-org backend
|
|
158
|
-
dreamlake team member add my-org backend alice@example.com --role maintainer
|
|
159
|
-
dreamlake team member role my-org backend alice@example.com --role member
|
|
160
|
-
dreamlake team member remove my-org backend alice@example.com
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Configuration
|
|
164
|
-
|
|
165
|
-
Resolution precedence (high → low): flag → env var → saved `auth.yml` → default.
|
|
166
|
-
|
|
167
|
-
| Setting | Env var | Default |
|
|
168
|
-
| --- | --- | --- |
|
|
169
|
-
| DreamLake server | `DREAMLAKE_REMOTE` | `http://localhost:10334` |
|
|
170
|
-
| BSS server | `DREAMLAKE_BSS_URL` | `http://localhost:10234` |
|
|
171
|
-
| Token | `DREAMLAKE_API_KEY` | (from `auth.yml`) |
|
|
172
|
-
|
|
173
|
-
## Not implemented
|
|
174
|
-
|
|
175
|
-
By design, the CLI excludes the parts of `dreamlake-py` that aren't
|
|
176
|
-
command-line-shaped: `vectorize` (CLIP/LLaVA inference + Zaku + Qdrant), the
|
|
177
|
-
SDK-only rich types (`Video`/`TextTrack`/`VectorIndex`), and the deprecated
|
|
178
|
-
Episode-tracking stack.
|
|
179
|
-
|
|
180
|
-
## Development
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
pnpm install
|
|
184
|
-
pnpm cli --help # dev — runs the TS source through tsx
|
|
185
|
-
pnpm test # tsc --noEmit + node --test (target/glob/kinds/credentials)
|
|
186
|
-
pnpm build # tsc → dist/
|
|
187
|
-
node bin/dreamlake.js … # runs compiled dist/ (falls back to tsx when dist/ is absent)
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Publishing: `npm version patch|minor` → `npm publish` (tests and build run
|
|
191
|
-
automatically via `prepublishOnly`/`prepack`) → `git push --follow-tags`.
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
// `dreamlake login | logout | profile`.
|
|
2
|
-
//
|
|
3
|
-
// login: runs the device-authorization flow (default) or saves a token
|
|
4
|
-
// passed via --token (lakeshore-style escape hatch). On success the
|
|
5
|
-
// dreamlake token + resolved server/bss/namespace are written to
|
|
6
|
-
// ~/.config/dreamlake/auth.yml (NOT the system keyring — see credentials.ts).
|
|
7
|
-
//
|
|
8
|
-
// profile: hits GET /auth/me, falling back to a local JWT decode when the
|
|
9
|
-
// server is unreachable. logout: deletes the auth file.
|
|
10
|
-
import { spawn } from "node:child_process";
|
|
11
|
-
import { resolveRemote, resolveToken, decodeJwtPayload } from "../config.js";
|
|
12
|
-
import { emitJson, renderTable } from "../helpers.js";
|
|
13
|
-
import { authFilePath, builtinEnv, BUILTIN_ENVS, currentEnvName, listEnvs, removeEnv, saveEnv, useEnv, } from "./credentials.js";
|
|
14
|
-
import { getOrCreateDeviceSecret } from "./device-secret.js";
|
|
15
|
-
import { AuthorizationDeniedError, DeviceCodeExpiredError, DeviceFlowClient, TokenExchangeError, } from "./device-flow.js";
|
|
16
|
-
async function fetchAuthMe(remote, token) {
|
|
17
|
-
try {
|
|
18
|
-
const res = await fetch(`${remote}/auth/me`, {
|
|
19
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
20
|
-
signal: AbortSignal.timeout(5000),
|
|
21
|
-
});
|
|
22
|
-
if (res.ok)
|
|
23
|
-
return (await res.json());
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
// unreachable
|
|
27
|
-
}
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
function openBrowser(url) {
|
|
31
|
-
const cmd = process.platform === "darwin"
|
|
32
|
-
? "open"
|
|
33
|
-
: process.platform === "win32"
|
|
34
|
-
? "start"
|
|
35
|
-
: "xdg-open";
|
|
36
|
-
try {
|
|
37
|
-
const child = spawn(cmd, [url], { stdio: "ignore", detached: true });
|
|
38
|
-
child.on("error", () => { });
|
|
39
|
-
child.unref();
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
// best-effort
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export async function runLogin(args) {
|
|
46
|
-
// Resolve which environment we're logging into and its URLs.
|
|
47
|
-
const envName = args.env ?? currentEnvName() ?? "staging";
|
|
48
|
-
const builtin = builtinEnv(envName);
|
|
49
|
-
const remote = (args.url ?? builtin?.server)?.replace(/\/+$/, "");
|
|
50
|
-
const bss = (args.bss ?? builtin?.bss)?.replace(/\/+$/, "");
|
|
51
|
-
const auth = args.auth ?? builtin?.auth;
|
|
52
|
-
if (!remote || !bss) {
|
|
53
|
-
process.stderr.write(`✗ unknown environment '${envName}'. Either use a built-in (${Object.keys(BUILTIN_ENVS).join(", ")}) ` +
|
|
54
|
-
`or define one: dreamlake login --env ${envName} --url <api> --bss <bss>\n`);
|
|
55
|
-
return 1;
|
|
56
|
-
}
|
|
57
|
-
process.stderr.write(`Logging into environment '${envName}' (${remote})\n`);
|
|
58
|
-
// ── Escape hatch: direct token (lakeshore style) ──────────────────
|
|
59
|
-
if (args.token) {
|
|
60
|
-
const me = await fetchAuthMe(remote, args.token);
|
|
61
|
-
if (!me) {
|
|
62
|
-
process.stderr.write(`✗ token validation failed against ${remote}/auth/me\n`);
|
|
63
|
-
return 1;
|
|
64
|
-
}
|
|
65
|
-
const namespace = me.namespace?.slug ?? "";
|
|
66
|
-
const p = saveEnv(envName, { server: remote, bss, auth, namespace, token: args.token });
|
|
67
|
-
process.stdout.write(`✓ Saved env '${envName}' to ${p}\n` +
|
|
68
|
-
` server: ${remote}\n` +
|
|
69
|
-
` bss: ${bss}\n` +
|
|
70
|
-
` namespace: ${namespace || "(none)"}\n`);
|
|
71
|
-
return 0;
|
|
72
|
-
}
|
|
73
|
-
// ── Device authorization flow ─────────────────────────────────────
|
|
74
|
-
const deviceSecret = getOrCreateDeviceSecret();
|
|
75
|
-
const client = new DeviceFlowClient(deviceSecret, remote, auth);
|
|
76
|
-
let flow;
|
|
77
|
-
try {
|
|
78
|
-
process.stderr.write("Initializing device authorization...\n\n");
|
|
79
|
-
flow = await client.startDeviceFlow();
|
|
80
|
-
}
|
|
81
|
-
catch (err) {
|
|
82
|
-
process.stderr.write(`✗ ${err.message}\n`);
|
|
83
|
-
return 1;
|
|
84
|
-
}
|
|
85
|
-
process.stdout.write(`DEVICE AUTHORIZATION REQUIRED\n\n` +
|
|
86
|
-
` 1. Visit: ${flow.verificationUri}\n` +
|
|
87
|
-
` 2. Enter code: ${flow.userCode}\n\n` +
|
|
88
|
-
` (code expires in ${Math.floor(flow.expiresIn / 60)} minutes)\n\n`);
|
|
89
|
-
if (!args.noBrowser) {
|
|
90
|
-
openBrowser(flow.verificationUriComplete);
|
|
91
|
-
}
|
|
92
|
-
process.stderr.write("Waiting for authorization...\n");
|
|
93
|
-
let vuerToken;
|
|
94
|
-
try {
|
|
95
|
-
vuerToken = await client.pollForToken(120, (elapsed) => {
|
|
96
|
-
if (process.stderr.isTTY) {
|
|
97
|
-
process.stderr.write(`\r waiting (${elapsed}s)`);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
if (process.stderr.isTTY)
|
|
101
|
-
process.stderr.write("\n");
|
|
102
|
-
}
|
|
103
|
-
catch (err) {
|
|
104
|
-
if (process.stderr.isTTY)
|
|
105
|
-
process.stderr.write("\n");
|
|
106
|
-
if (err instanceof DeviceCodeExpiredError) {
|
|
107
|
-
process.stderr.write("✗ Device code expired. Run 'dreamlake login' again.\n");
|
|
108
|
-
}
|
|
109
|
-
else if (err instanceof AuthorizationDeniedError) {
|
|
110
|
-
process.stderr.write("✗ Authorization denied.\n");
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
process.stderr.write(`✗ ${err.message}\n`);
|
|
114
|
-
}
|
|
115
|
-
return 1;
|
|
116
|
-
}
|
|
117
|
-
let token;
|
|
118
|
-
try {
|
|
119
|
-
process.stderr.write("Exchanging token with DreamLake server...\n");
|
|
120
|
-
token = await client.exchangeToken(vuerToken);
|
|
121
|
-
}
|
|
122
|
-
catch (err) {
|
|
123
|
-
const msg = err instanceof TokenExchangeError ? err.message : err.message;
|
|
124
|
-
process.stderr.write(`✗ ${msg}\n`);
|
|
125
|
-
return 1;
|
|
126
|
-
}
|
|
127
|
-
const me = await fetchAuthMe(remote, token);
|
|
128
|
-
const namespace = me?.namespace?.slug ?? "";
|
|
129
|
-
saveEnv(envName, { server: remote, bss, auth, namespace, token });
|
|
130
|
-
process.stdout.write(`✓ Logged in to '${envName}' (now active)\n` +
|
|
131
|
-
` server: ${remote}\n` +
|
|
132
|
-
` bss: ${bss}\n` +
|
|
133
|
-
` namespace: ${namespace || "(none)"}\n`);
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
// ─── logout ──────────────────────────────────────────────────────────
|
|
137
|
-
export async function runLogout() {
|
|
138
|
-
const name = currentEnvName();
|
|
139
|
-
if (!name) {
|
|
140
|
-
process.stdout.write(`No active environment (${authFilePath()})\n`);
|
|
141
|
-
return 0;
|
|
142
|
-
}
|
|
143
|
-
removeEnv(name);
|
|
144
|
-
const next = currentEnvName();
|
|
145
|
-
process.stdout.write(`✓ Logged out of '${name}'.` +
|
|
146
|
-
(next ? ` Active environment is now '${next}'.\n` : " No environments left.\n"));
|
|
147
|
-
return 0;
|
|
148
|
-
}
|
|
149
|
-
// ─── env (environment switching) ──────────────────────────────────────
|
|
150
|
-
export async function runEnvList(opts) {
|
|
151
|
-
const envs = listEnvs();
|
|
152
|
-
if (opts.json) {
|
|
153
|
-
emitJson(envs.map((e) => ({
|
|
154
|
-
name: e.name,
|
|
155
|
-
current: e.current,
|
|
156
|
-
server: e.env.server,
|
|
157
|
-
bss: e.env.bss,
|
|
158
|
-
namespace: e.env.namespace,
|
|
159
|
-
})));
|
|
160
|
-
return 0;
|
|
161
|
-
}
|
|
162
|
-
process.stdout.write("Environments\n\n");
|
|
163
|
-
if (envs.length === 0) {
|
|
164
|
-
process.stdout.write(` (none logged in)\n hint: dreamlake login --env staging (built-in: ${Object.keys(BUILTIN_ENVS).join(", ")})\n`);
|
|
165
|
-
return 0;
|
|
166
|
-
}
|
|
167
|
-
const rows = envs.map((e) => ({
|
|
168
|
-
"": e.current ? "*" : "",
|
|
169
|
-
name: e.name,
|
|
170
|
-
server: e.env.server,
|
|
171
|
-
namespace: e.env.namespace || "(none)",
|
|
172
|
-
}));
|
|
173
|
-
process.stdout.write(renderTable(rows, ["", "name", "server", "namespace"]));
|
|
174
|
-
return 0;
|
|
175
|
-
}
|
|
176
|
-
export async function runEnvUse(name) {
|
|
177
|
-
if (useEnv(name)) {
|
|
178
|
-
process.stdout.write(`✓ Active environment is now '${name}'\n`);
|
|
179
|
-
return 0;
|
|
180
|
-
}
|
|
181
|
-
const known = listEnvs().map((e) => e.name);
|
|
182
|
-
process.stderr.write(`✗ environment '${name}' is not logged in.\n` +
|
|
183
|
-
(known.length ? ` logged in: ${known.join(", ")}\n` : "") +
|
|
184
|
-
` log in with: dreamlake login --env ${name}\n`);
|
|
185
|
-
return 1;
|
|
186
|
-
}
|
|
187
|
-
export async function runEnvRemove(name) {
|
|
188
|
-
if (removeEnv(name)) {
|
|
189
|
-
process.stdout.write(`✓ Removed environment '${name}'\n`);
|
|
190
|
-
return 0;
|
|
191
|
-
}
|
|
192
|
-
process.stderr.write(`✗ environment '${name}' not found\n`);
|
|
193
|
-
return 1;
|
|
194
|
-
}
|
|
195
|
-
export async function runProfile(args) {
|
|
196
|
-
const token = resolveToken();
|
|
197
|
-
if (!token) {
|
|
198
|
-
process.stdout.write("Not logged in. Run: dreamlake login\n");
|
|
199
|
-
return 1;
|
|
200
|
-
}
|
|
201
|
-
const remote = resolveRemote(args.url);
|
|
202
|
-
const me = await fetchAuthMe(remote, token);
|
|
203
|
-
if (me) {
|
|
204
|
-
const envName = currentEnvName();
|
|
205
|
-
process.stdout.write(`Current DreamLake user${envName ? ` [env: ${envName}]` : ""}\n`);
|
|
206
|
-
for (const field of ["id", "sub", "username", "name", "email"]) {
|
|
207
|
-
if (me[field] != null)
|
|
208
|
-
process.stdout.write(` ${field}: ${me[field]}\n`);
|
|
209
|
-
}
|
|
210
|
-
if (me.namespace?.slug) {
|
|
211
|
-
process.stdout.write(` namespace: ${me.namespace.slug} (id: ${me.namespace.id ?? "?"})\n`);
|
|
212
|
-
}
|
|
213
|
-
if (me.createdAt) {
|
|
214
|
-
process.stdout.write(` member since: ${String(me.createdAt).slice(0, 10)}\n`);
|
|
215
|
-
}
|
|
216
|
-
return 0;
|
|
217
|
-
}
|
|
218
|
-
// Fallback: decode JWT locally.
|
|
219
|
-
process.stderr.write("⚠ could not reach server, showing local token info.\n");
|
|
220
|
-
const payload = decodeJwtPayload(token);
|
|
221
|
-
if (Object.keys(payload).length === 0) {
|
|
222
|
-
process.stdout.write("Failed to decode token. Please log in again.\n");
|
|
223
|
-
return 1;
|
|
224
|
-
}
|
|
225
|
-
process.stdout.write("Current DreamLake user (from token)\n");
|
|
226
|
-
for (const field of ["sub", "username", "name", "email"]) {
|
|
227
|
-
if (payload[field] != null) {
|
|
228
|
-
process.stdout.write(` ${field}: ${payload[field]}\n`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const exp = payload.exp;
|
|
232
|
-
if (typeof exp === "number") {
|
|
233
|
-
const expired = exp < Date.now() / 1000;
|
|
234
|
-
process.stdout.write(` expires: ${new Date(exp * 1000).toISOString()}${expired ? " (EXPIRED)" : ""}\n`);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
process.stdout.write(" expires: never (long-lived token)\n");
|
|
238
|
-
}
|
|
239
|
-
return 0;
|
|
240
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Authentication constants — port of dreamlake-py's auth/constants.py.
|
|
2
|
-
/**
|
|
3
|
-
* Vuer-auth server URL (device-flow provider). Resolved lazily so a
|
|
4
|
-
* `--auth` flag / late env assignment can override it. NOTE: this must
|
|
5
|
-
* match the auth server the TARGET dreamlake-server trusts — e.g.
|
|
6
|
-
* staging-api.dreamlake.ai validates tokens from staging-auth.vuer.ai,
|
|
7
|
-
* not the production auth.vuer.ai (mismatched keys → "No matching key
|
|
8
|
-
* found in JWKS").
|
|
9
|
-
*/
|
|
10
|
-
export function vuerAuthUrl(override) {
|
|
11
|
-
return (override || process.env.VUER_AUTH_URL || "https://auth.vuer.ai").replace(/\/+$/, "");
|
|
12
|
-
}
|
|
13
|
-
/** OAuth client ID for dreamlake. */
|
|
14
|
-
export const CLIENT_ID = "dreamlake-client";
|
|
15
|
-
/** Default OAuth scopes. */
|
|
16
|
-
export const DEFAULT_SCOPE = "openid profile email";
|