grok-studio 0.1.1 → 0.1.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/README.md +29 -96
- package/dist/server/cli.js +8 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,117 +1,50 @@
|
|
|
1
1
|
# Grok Studio
|
|
2
2
|
|
|
3
|
-
Self-hosted Grok
|
|
3
|
+
Self-hosted Grok image-to-video studio. Your xAI credentials stay on the server; turn an image into video from a web UI or the CLI, with outputs stored on disk. Built for local / LAN use.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
The UI is organized around generated outputs, active work, and source-scoped lineage.
|
|
8
|
-
|
|
9
|
-
- **Left rail**: `+` creates a new source image. Active Jobs appears above the output gallery when queued or running work exists; it is global and not tied to the currently selected source. Outputs are typed as `Frame` or `Video`; status text is only for exceptional or in-progress states.
|
|
10
|
-
- **Result Graph**: the center canvas shows lineage for the current source. Nodes are concrete artifacts/runs:
|
|
11
|
-
- `Source` is the root image.
|
|
12
|
-
- each Prep run creates a separate `Frame` node.
|
|
13
|
-
- each Video run creates a separate `Video` node.
|
|
14
|
-
- **Inspector**: the right panel is the only place for details and actions. Select a node to inspect it, download video output, rerun, prepare another frame, or animate the selected frame.
|
|
15
|
-
|
|
16
|
-
Lineage is data-driven:
|
|
17
|
-
|
|
18
|
-
- Source-linked video jobs attach only when `inputFrame.source === "source"` and the job's `clientSourceId` matches the current source.
|
|
19
|
-
- Frame-linked video jobs attach only when `inputFrame.source === "prep"` and `preparedImageId` matches a frame in the current graph.
|
|
20
|
-
- UI cache must not decide graph edges. If lineage fields are missing, do not guess by recent history.
|
|
21
|
-
|
|
22
|
-
## Run
|
|
5
|
+
## Install & run
|
|
23
6
|
|
|
24
7
|
```bash
|
|
25
|
-
|
|
26
|
-
cp .env.example .env
|
|
27
|
-
vpr build
|
|
28
|
-
vpr start
|
|
8
|
+
npx grok-studio
|
|
29
9
|
```
|
|
30
10
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
vpr launch
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
`launch` builds the app, checks xAI auth, opens the browser login flow when no token exists, then starts the web app. Open <http://127.0.0.1:8787>.
|
|
38
|
-
|
|
39
|
-
## LAN Access
|
|
40
|
-
|
|
41
|
-
For local-network access, set:
|
|
42
|
-
|
|
43
|
-
```dotenv
|
|
44
|
-
HOST=0.0.0.0
|
|
45
|
-
ACCESS_TOKEN=
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Then restart the service and open `http://<mac-lan-ip>:8787` or `http://<mac-local-hostname>.local:8787` from another device on the same LAN.
|
|
49
|
-
|
|
50
|
-
`ACCESS_TOKEN` can be left empty for trusted local/LAN use. Set it before exposing the app outside a trusted network.
|
|
11
|
+
That's it. First run opens the xAI browser login if needed, then serves on <http://127.0.0.1:8787>. (`npm i -g grok-studio` also works.)
|
|
51
12
|
|
|
52
|
-
##
|
|
53
|
-
|
|
54
|
-
- `HOST` / `PORT` control where the server binds.
|
|
55
|
-
- `ACCESS_TOKEN` optionally protects browser/API access outside loopback.
|
|
56
|
-
- `WORKSPACE_DIR` stores `images/`, `videos/`, `jobs/`, and `prepared-images/`.
|
|
57
|
-
- `XAI_AUTH_MODE=oauth` reads `XAI_OAUTH_TOKEN_FILE`, refreshes it when needed, and `vpr launch` can bootstrap it interactively.
|
|
58
|
-
- `XAI_AUTH_MODE=api_key` uses `XAI_API_KEY`.
|
|
59
|
-
- `XAI_VIDEO_MODEL` controls image-to-video generation.
|
|
60
|
-
- `XAI_IMAGE_MODEL` controls first-frame image editing.
|
|
61
|
-
- `DEFAULT_DURATION_SECONDS` supports up to 15 seconds.
|
|
62
|
-
- `MAX_VARIATIONS` limits sequential variations per video job.
|
|
63
|
-
|
|
64
|
-
The app binds to `127.0.0.1` by default. Use `HOST=0.0.0.0` only for LAN/tunnel exposure.
|
|
65
|
-
|
|
66
|
-
## Grok Login Test
|
|
67
|
-
|
|
68
|
-
To test the xAI/Grok browser login without touching the token file used by the running service:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
vpr login
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
The command opens the xAI authorization page, waits for the local callback, saves the token state to a test path, and verifies it with the API. If xAI shows a fallback code instead of redirecting, paste that code into the terminal prompt.
|
|
75
|
-
|
|
76
|
-
Useful variants:
|
|
13
|
+
## CLI
|
|
77
14
|
|
|
78
15
|
```bash
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
16
|
+
grok-studio serve [--open] # start the web app (default command)
|
|
17
|
+
grok-studio gen --image a.png --prompt "slow head turn" \
|
|
18
|
+
[--prep --duration 6 --resolution 720p --aspect 9:16 --count 2 --out clip.mp4]
|
|
19
|
+
grok-studio login # xAI OAuth login (browser)
|
|
20
|
+
grok-studio status # config + xAI auth + server health
|
|
82
21
|
```
|
|
83
22
|
|
|
84
|
-
`
|
|
23
|
+
`gen` is headless: image →(optional `--prep` first frame)→ video, no UI; result paths print to stdout.
|
|
85
24
|
|
|
86
|
-
|
|
25
|
+
## Web UI
|
|
87
26
|
|
|
88
|
-
|
|
27
|
+
- **Left rail** — `+` adds a source image; **Active Jobs** lists running work globally; **Outputs** is a gallery of every `Frame` / `Video`.
|
|
28
|
+
- **Result Graph** (center) — every node is one concrete run: `Source` → `Frame` (each prep) → `Video` (each run). Forks are sibling nodes. Lineage is derived purely from data (`inputFrame.preparedImageId` / `clientSourceId`), never guessed from history.
|
|
29
|
+
- **Inspector** (right) — the only place to view a result, download, rerun, prep, or animate the selected node.
|
|
89
30
|
|
|
90
|
-
|
|
31
|
+
## Configure (`.env`)
|
|
91
32
|
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
vpr smoke
|
|
33
|
+
```dotenv
|
|
34
|
+
HOST=127.0.0.1 # 0.0.0.0 for LAN access
|
|
35
|
+
PORT=8787
|
|
36
|
+
ACCESS_TOKEN= # set to gate non-loopback access (empty = open on loopback/LAN)
|
|
37
|
+
WORKSPACE_DIR=./workspace # holds images/ videos/ jobs/ prepared-images/
|
|
38
|
+
XAI_AUTH_MODE=oauth # or: api_key (+ XAI_API_KEY)
|
|
39
|
+
XAI_VIDEO_MODEL=grok-imagine-video
|
|
40
|
+
XAI_IMAGE_MODEL=grok-imagine-image-quality
|
|
101
41
|
```
|
|
102
42
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- React-specific lint rules for hooks, effect dependencies, JSX keys, nested components, unsafe JSX, and button types.
|
|
106
|
-
- Type-aware linting and TypeScript checks.
|
|
107
|
-
- Architecture guardrails in `tests/architecture.test.ts` to keep the main app shell thin, limit local React state in `App.tsx`, split stylesheet ownership, and keep server route files focused.
|
|
108
|
-
- Browser smoke coverage in `scripts/smoke.mjs`, including paste input, image lightbox, video-card scroll behavior, and legacy history not restoring unrelated Prep outputs.
|
|
109
|
-
|
|
110
|
-
## Local Service
|
|
111
|
-
|
|
112
|
-
If installed as the local launchd service, restart it after building:
|
|
43
|
+
## Develop
|
|
113
44
|
|
|
114
45
|
```bash
|
|
115
|
-
|
|
116
|
-
|
|
46
|
+
vp install
|
|
47
|
+
vpr check && vp test && vpr build && vpr smoke
|
|
117
48
|
```
|
|
49
|
+
|
|
50
|
+
Quality gates: React/a11y lint, type checks, an architecture-size test (`tests/architecture.test.ts`), and a Playwright smoke (`scripts/smoke.mjs`).
|
package/dist/server/cli.js
CHANGED
|
@@ -1683,14 +1683,15 @@ function cleanOptionalString(value) {
|
|
|
1683
1683
|
//#endregion
|
|
1684
1684
|
//#region src/server/serve.ts
|
|
1685
1685
|
async function startServer(options = {}) {
|
|
1686
|
+
clack.intro("Grok Studio");
|
|
1686
1687
|
const config = loadConfig();
|
|
1687
1688
|
await ensureAuthReady(config);
|
|
1688
1689
|
const { app } = createServer(config);
|
|
1689
1690
|
app.listen(config.port, config.host, () => {
|
|
1690
|
-
const url = `http://${config.host}:${config.port}`;
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1691
|
+
const url = `http://${config.host === "0.0.0.0" || config.host === "::" ? "127.0.0.1" : config.host}:${config.port}`;
|
|
1692
|
+
const access = config.accessToken ? "Access: token required (set in .env)" : "Access: open — no ACCESS_TOKEN set";
|
|
1693
|
+
clack.note(`${url}\n${access}`, "Running");
|
|
1694
|
+
clack.outro("Press Ctrl+C to stop.");
|
|
1694
1695
|
if (options.open) openBrowser(url);
|
|
1695
1696
|
});
|
|
1696
1697
|
}
|
|
@@ -1701,13 +1702,11 @@ async function ensureAuthReady(config) {
|
|
|
1701
1702
|
}
|
|
1702
1703
|
try {
|
|
1703
1704
|
await resolveXaiAuth(config);
|
|
1704
|
-
|
|
1705
|
+
clack.log.success(`xAI OAuth ready (${config.xai.oauthTokenFile})`);
|
|
1705
1706
|
return;
|
|
1706
1707
|
} catch (error) {
|
|
1707
|
-
if (fs.existsSync(config.xai.oauthTokenFile)) {
|
|
1708
|
-
|
|
1709
|
-
console.warn("Starting OAuth login to repair the token state.");
|
|
1710
|
-
} else console.log(`No xAI OAuth token found at ${config.xai.oauthTokenFile}. Starting login.`);
|
|
1708
|
+
if (fs.existsSync(config.xai.oauthTokenFile)) clack.log.warn(`xAI token check failed: ${error instanceof Error ? error.message : String(error)} — re-running login.`);
|
|
1709
|
+
else clack.log.info("No xAI token found — starting login.");
|
|
1711
1710
|
}
|
|
1712
1711
|
await runXaiOauthLogin({ outputPath: config.xai.oauthTokenFile });
|
|
1713
1712
|
await resolveXaiAuth(config);
|