datagrok-tools 6.0.6 → 6.0.8

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.
@@ -2,36 +2,37 @@
2
2
 
3
3
  Isolated environment for JS/TS package development against a running Datagrok instance.
4
4
  All images are pre-built on Docker Hub — no local build step needed. Install `datagrok-tools`
5
- globally and run `grok claude` from any git repo.
5
+ globally and run `grok claude` from any directory.
6
6
 
7
7
  ## Quick start
8
8
 
9
9
  ```bash
10
10
  npm i -g datagrok-tools # one-time install
11
- export ANTHROPIC_API_KEY=... # or set in shell profile
12
11
 
13
12
  # From any package directory:
14
- grok claude
15
-
16
- # With options:
17
- grok claude --version 1.22.0 --profile full --task GROK-123
18
-
19
- # Stop containers:
20
- grok claude --stop --task GROK-123
13
+ grok claude GROK-12345 # start working on a task
14
+ grok claude GROK-12345 --version 1.22.0 # pin Datagrok version
15
+ grok claude GROK-12345 --profile full --keep # all services, keep running
16
+ grok claude GROK-12345 --prompt "fix the bug" # one-shot command
17
+ grok claude GROK-12345 --in-place # use current directory (no worktree)
18
+ grok claude destroy GROK-12345 # tear down a task
19
+ grok claude destroy-all # tear down everything
21
20
  ```
22
21
 
23
- The `grok claude` command is fully self-contained the compose configuration is
24
- embedded in the CLI, all container images are pulled from Docker Hub (`datagrok/tools-dev`,
25
- `datagrok/datagrok`, etc.). No files to copy, no Dockerfile to build.
26
- See `grok claude --help` for all options.
22
+ Authentication: Claude Code credentials are copied from the host `~/.claude/` into the
23
+ container on startup. Alternatively, set `ANTHROPIC_API_KEY` in the environment.
24
+
25
+ **Project name restrictions:** `master` and `main` are rejected.
27
26
 
28
27
  ### What it does
29
28
 
30
- 1. Generates a `docker-compose.yaml` and `.env` in a temp directory
31
- 2. Runs `docker compose up -d --wait` (pulls pre-built images)
32
- 3. Waits for Datagrok to be healthy
33
- 4. Launches `claude --dangerously-skip-permissions` inside the `tools-dev` container
34
- 5. On exit, stops containers (unless `--keep`)
29
+ 1. Creates a git worktree at `~/pkg-worktrees/<project>` (unless `--in-place` or not in a git repo)
30
+ 2. Generates `docker-compose.yaml`, `.env`, and optional `docker-compose.override.yaml` in `$TMPDIR/dg-pkg-<project>`
31
+ 3. Runs `docker compose up -d --wait` (pulls pre-built images from Docker Hub)
32
+ 4. Copies Claude credentials into the container and fixes ownership
33
+ 5. Detects working directory based on repo type (see below)
34
+ 6. Launches `claude --dangerously-skip-permissions` inside the `tools-dev` container
35
+ 7. On exit, stops containers (unless `--keep`)
35
36
 
36
37
  ### Manual setup
37
38
 
@@ -39,126 +40,162 @@ For manual setup or customization, use the `docker-compose.yaml` in this directo
39
40
  directly. The `Dockerfile.pkg_dev` and `entrypoint.sh` are the build recipe for the
40
41
  `datagrok/tools-dev` Docker Hub image — end users don't need them.
41
42
 
42
- ## What you can do
43
-
44
- | Workflow | How it works |
45
- |----------|-------------|
46
- | **Develop packages (public repo)** | Mount public repo worktree as `/workspace`. Agent reads js-api source, help docs, ApiSamples, and CLAUDE.md skills directly. |
47
- | **Develop packages (separate repo)** | Mount your repo as `/workspace`. The public repo is auto-cloned to `/workspace/public` on first start, branch matching `DG_VERSION`. |
48
- | **Learn from examples** | Agent reads `packages/ApiSamples/scripts/` for runnable code samples and `help/develop/` for guides. |
49
- | **Search Jira and GitHub** | MCP plugins for Atlassian Jira and GitHub — agent searches for similar issues, reads context, updates status. |
50
- | **Write and run tests** | Playwright for browser automation, `grok test` for Puppeteer-based package tests, Chrome remote debugging. |
51
- | **Publish packages** | `grok publish` to the local Datagrok instance or any external server. Agent handles build + publish. |
52
- | **Manage the Datagrok stand** | Agent changes `DG_VERSION`, pulls new images, resets DB, redeploys — all from inside the container via Docker socket. |
53
- | **Interactive setup** | Agent asks the user for tokens, auth keys, server URLs via Claude Code prompts. No pre-configuration required. |
54
-
55
43
  ## Architecture
56
44
 
57
45
  ```
58
46
  Host machine
59
47
  ┌──────────────────────────────────────────────────────────────────┐
60
- Worktree: ~/pkg-worktrees/TASK-123/
61
- │ └── public/ (auto-cloned if workspace is not public repo) │
62
- │ │
63
- │ Docker network: dg-pkg-TASK-123-net │
48
+ Docker network: dg-pkg-<project>-net
64
49
  │ ┌────────────────────────────────────────────────────────────┐ │
65
- │ │ datagrok (datagrok/datagrok:${VERSION}) :8080 │ │
66
- │ │ postgres (pgvector/pgvector:pg17) │ │
67
- │ │ rabbitmq (rabbitmq:4.0.5-management) │ │
68
- │ │ grok_pipe (datagrok/grok_pipe:${VERSION}) │ │
69
- │ │ grok_connect, grok_spawner, jkg (optional profiles) │ │
70
- │ │ world, test_db, northwind (optional demo DBs) │ │
71
- │ │ │ │
72
- │ │ tools-dev (datagrok/tools-dev:latest) │ │
73
- │ │ ├── /workspace ← bind mount of worktree │ │
74
- │ │ ├── /var/run/docker.sock host Docker for stand mgmt │ │
75
- │ │ ├── ~/.claude/ bind mount from host │ │
50
+ │ │ datagrok (datagrok/datagrok:${VERSION}) :${PORT} │ │
51
+ │ │ postgres (pgvector/pgvector:pg17) │ │
52
+ │ │ rabbitmq (rabbitmq:4.0.5-management) │ │
53
+ │ │ grok_pipe (datagrok/grok_pipe:latest) │ │
54
+ │ │ grok_connect (datagrok/grok_connect:latest) │ │
55
+ │ │ grok_spawner, jkg (optional profiles) │ │
56
+ │ │ world, test_db, northwind (optional demo DBs) │ │
57
+ │ │ │ │
58
+ │ │ tools-dev (datagrok/tools-dev:latest, runs as root) │ │
59
+ │ │ ├── /workspace/repo bind mount of worktree │ │
60
+ │ │ ├── /workspace/datagrok/ sparse clone of public repo │ │
61
+ │ │ │ ├── .claude/, CLAUDE.md │ │
62
+ │ │ │ ├── js-api/ │ │
63
+ │ │ │ ├── libraries/ │ │
64
+ │ │ │ └── packages/ │ │
65
+ │ │ │ ├── ApiSamples/ ← checked out │ │
66
+ │ │ │ └── <folder>/ ← bind mount of user's repo │ │
67
+ │ │ ├── /var/run/docker.sock ← host Docker for stand mgmt │ │
68
+ │ │ ├── entrypoint.sh ← bind-mounted from tools/ │ │
76
69
  │ │ ├── Claude Code + MCP plugins (Jira, GitHub) │ │
77
70
  │ │ ├── Playwright + Chrome for testing │ │
78
71
  │ │ └── grok CLI for build/publish/test │ │
79
72
  │ └────────────────────────────────────────────────────────────┘ │
80
-
81
- │ Host browser → http://localhost:8080 (Datagrok UI)
82
- │ → chrome://inspect → localhost:9222 (Debug)
83
- └──────────────────────────────────────────────────────────────────┘
73
+
74
+ │ Host browser → http://localhost:${PORT} (Datagrok UI)
75
+ │ → chrome://inspect → localhost:9222 (Debug)
76
+ └───────────────────────────────────────────────────────────────────┘
84
77
  ```
85
78
 
86
- ## docker-compose.yaml
79
+ ## Working directory detection
87
80
 
88
- The `docker-compose.yaml` in this directory uses pre-built images from Docker Hub.
89
- The `grok claude` command embeds this same configuration and generates it automatically
90
- — you don't need this file unless you want manual control.
81
+ Claude Code launches in different directories depending on the repo type:
91
82
 
92
- For manual use, set variables in `.env` or export them, then:
83
+ | Repo type | Working directory | How detected |
84
+ |-----------|------------------|--------------|
85
+ | Public repo root | `/workspace/repo` | Host has `js-api/` at git root |
86
+ | Monorepo | `/workspace/repo/public` | Host has `public/js-api/` at git root |
87
+ | External repo | `/workspace/datagrok/packages/<folder>` | Everything else |
93
88
 
94
- ```bash
95
- docker compose up -d
96
- ```
89
+ For **external repos**, the user's directory is bind-mounted at both `/workspace/repo`
90
+ and `/workspace/datagrok/packages/<folder>`. Claude Code starts in the latter so it can
91
+ walk up the directory tree to find `CLAUDE.md`, `.claude/`, and `js-api/` from the
92
+ sparse-cloned public repo.
97
93
 
98
- ### Building the tools-dev image
94
+ ## Sparse clone (external repos)
99
95
 
100
- The `Dockerfile.pkg_dev` and `entrypoint.sh` are the build recipe for the
101
- `datagrok/tools-dev` image published to Docker Hub. To build locally:
96
+ When the workspace is not the public repo, the entrypoint sparse-clones it into
97
+ `/workspace/datagrok/` using **cone mode** with `--filter=blob:none`:
102
98
 
103
- ```bash
104
- cd public/tools/.devcontainer
105
- docker build -f Dockerfile.pkg_dev -t datagrok/tools-dev:latest .
99
+ ```
100
+ git sparse-checkout set --cone .claude js-api libraries packages/ApiSamples
106
101
  ```
107
102
 
108
- ## Working with repos
103
+ This fetches only ~3 MB (vs 1.67 GB for the full repo) and completes in ~10 seconds.
104
+ The checked-out directories provide:
105
+ - `.claude/` — Claude Code skills and settings
106
+ - `CLAUDE.md` — project instructions for the agent
107
+ - `js-api/` — JS API source for reference
108
+ - `libraries/` — shared library source
109
+ - `packages/ApiSamples/` — runnable code examples
109
110
 
110
- ### Public repo (default)
111
+ ### Branch resolution
111
112
 
112
- ```bash
113
- cd /path/to/public
114
- git worktree add ~/pkg-worktrees/TASK-123 -b TASK-123
113
+ 1. `DG_PUBLIC_BRANCH` (explicit override)
114
+ 2. Current branch of the host repo (if inside the public repo)
115
+ 3. `DG_VERSION` mapped to a branch (version tags like `latest`/`bleeding-edge` → `master`)
116
+ 4. Falls back to `master`
115
117
 
116
- # Set WORKTREE_PATH in .env or export
117
- echo "WORKTREE_PATH=$HOME/pkg-worktrees/TASK-123" >> .env
118
- docker compose up -d
119
- ```
118
+ To use a private fork: `DG_PUBLIC_REPO=https://github.com/myorg/public-fork.git`
120
119
 
121
- Inside the container, the agent sees:
122
- - `/workspace/js-api/` — JS API source (read directly, no build needed for reference)
123
- - `/workspace/packages/ApiSamples/scripts/` — runnable code examples
124
- - `/workspace/help/develop/` — development guides
125
- - `/workspace/packages/` — all existing packages as reference
120
+ ## Entrypoint
126
121
 
127
- ### Separate repo (e.g., private packages)
122
+ The entrypoint (`entrypoint.sh`) runs as **root** (via `user: root` in compose) to
123
+ handle bind-mount permission issues, then drops to the `node` user (UID 1000) via
124
+ `setpriv` for the main process (`sleep infinity`).
128
125
 
129
- ```bash
130
- git -C /path/to/my-repo worktree add ~/pkg-worktrees/TASK-123 -b TASK-123
126
+ Steps:
127
+ 1. Fix ownership of `/workspace/datagrok` (bind-mounts create parents as root)
128
+ 2. Detect repo type (public, monorepo, or external)
129
+ 3. Sparse-clone public repo if needed (cone mode, ~10s)
130
+ 4. `chown -R node:node` on cloned files
131
+ 5. Link workspace into `packages/` (skipped if bind-mount already exists)
132
+ 6. Create symlinks at `/workspace/` for CLAUDE.md and .claude/
133
+ 7. Auto-configure `~/.grok/config.yaml` pointing to `http://datagrok:8080/api`
134
+ 8. Drop privileges: `exec setpriv --reuid=node --regid=node --init-groups "$@"`
131
135
 
132
- echo "WORKTREE_PATH=$HOME/pkg-worktrees/TASK-123" >> .env
133
- docker compose up -d
134
- ```
136
+ The entrypoint is **bind-mounted from the host repo** (`tools/.devcontainer/entrypoint.sh`)
137
+ via the compose override, so changes take effect without rebuilding the Docker image.
135
138
 
136
- On first start, the entrypoint detects that `/workspace` is not the public repo
137
- (no `js-api/` at root) and automatically clones it to `/workspace/public`:
139
+ ## Compose template
138
140
 
139
- ```
140
- [tools-dev] Cloning public repo (bleeding-edge) into /workspace/public...
141
- [tools-dev] Public repo ready at /workspace/public (branch: bleeding-edge).
142
- ```
141
+ The compose configuration is embedded in `bin/commands/claude.ts` — no external files
142
+ needed. It generates three files in `$TMPDIR/dg-pkg-<project>/`:
143
143
 
144
- The branch is resolved in this order:
145
- 1. `DG_PUBLIC_BRANCH` (explicit override in `.env`)
146
- 2. `DG_VERSION` (matches the Datagrok image tag keeps API and server in sync)
147
- 3. Falls back to `master` if the branch/tag doesn't exist
144
+ - `docker-compose.yaml` main compose template
145
+ - `.env` — resolved variables (paths, versions, ports, tokens)
146
+ - `docker-compose.override.yaml` host-specific volumes (entrypoint, credentials mount for external repos)
148
147
 
149
- Inside the container:
150
- - `/workspace/` — your repo
151
- - `/workspace/public/js-api/` — JS API source (matching Datagrok version)
152
- - `/workspace/public/packages/ApiSamples/scripts/` — code examples
153
- - `/workspace/public/help/` — docs
154
- - `/workspace/public/packages/` — reference packages
148
+ ### Key differences from `.devcontainer/docker-compose.yaml`
155
149
 
156
- To use a private fork of public, set `DG_PUBLIC_REPO` in `.env`:
157
- ```bash
158
- DG_PUBLIC_REPO=https://github.com/myorg/public-fork.git
159
- ```
150
+ | | Embedded (grok claude) | .devcontainer/ |
151
+ |---|---|---|
152
+ | Version vars | Separate per service (`DATAGROK_VERSION`, `GROK_CONNECT_VERSION`, etc.) | Single `DG_VERSION` |
153
+ | Workspace mount | `/workspace/repo` | `/workspace` |
154
+ | grok_connect | Always on | Profile `full` only |
155
+ | grok_pipe | `grok_pipe:latest` | `grok_pipe:${DG_VERSION}` |
156
+ | Host ~/.grok | Not mounted (auto-created by entrypoint) | Mounted from host |
157
+ | Entrypoint | Bind-mounted from repo via override | Baked into image |
158
+ | User | `root` (drops to `node` via setpriv) | `node` (from Dockerfile) |
160
159
 
161
- Worktrees keep full git connectivity — commit, push, fetch, PR all work.
160
+ ## Credential handling
161
+
162
+ Claude Code credentials are **copied** into the container after startup (not
163
+ bind-mounted) to avoid permission issues — host files may have different UID/GID
164
+ than the container's `node` user.
165
+
166
+ Files copied from `~/.claude/`:
167
+ - `.credentials.json` — OAuth credentials
168
+ - `settings.json` — Claude Code settings
169
+ - `settings.local.json` — local settings
170
+
171
+ After copying, ownership is fixed: `chown -R node:node /home/node/.claude`
172
+
173
+ The host `~/.claude` directory is found by searching (in order):
174
+ 1. `CLAUDE_HOME` environment variable
175
+ 2. `$HOME/.claude`
176
+ 3. `$USERPROFILE/.claude` (Windows)
177
+ 4. `$APPDATA/../.claude` (Windows fallback)
178
+
179
+ ## Profiles
180
+
181
+ | Profile | Services added | Use case |
182
+ |---------|---------------|----------|
183
+ | (none) | postgres, rabbitmq, grok_pipe, datagrok, grok_connect, tools-dev | Basic package dev |
184
+ | `scripting` | + jupyter_kernel_gateway | Python/R/Julia scripts |
185
+ | `demo` | + world, test_db, northwind | Demo databases |
186
+ | `full` | + grok_spawner, JKG, demo DBs | Everything |
187
+
188
+ ## tools-dev container
189
+
190
+ Based on `node:22-bookworm-slim`. Pre-installed:
191
+ - Google Chrome stable (for Puppeteer)
192
+ - Playwright + Chromium
193
+ - `datagrok-tools` (grok CLI) — global npm
194
+ - `@anthropic-ai/claude-code` — global npm
195
+ - git, curl, jq, docker CLI
196
+ - Node.js 22
197
+
198
+ Runs as **root** in compose (entrypoint drops to `node` user for the main process).
162
199
 
163
200
  ## MCP plugins: Jira and GitHub
164
201
 
@@ -176,100 +213,42 @@ claude mcp add mcp-atlassian -s user -- \
176
213
  --jira-token "$JIRA_TOKEN"
177
214
  ```
178
215
 
179
- The agent can then:
180
- - Search for issues: "find similar bugs to GROK-12345"
181
- - Read issue details, comments, attachments
182
- - Update issue status, add comments
183
- - Create new issues
184
-
185
216
  ### GitHub
186
217
 
187
218
  ```bash
188
- # Inside tools-dev, register the MCP server:
189
219
  claude mcp add github -s user -- \
190
220
  npx -y @modelcontextprotocol/server-github
191
221
  ```
192
222
 
193
- Requires `GITHUB_TOKEN` in the environment (already passed from `.env`). The agent can:
194
- - Search issues and PRs across repos
195
- - Read issue comments and PR diffs
196
- - Create issues, PRs, and comments
197
- - Check CI status
198
-
199
- ### Interactive token setup
200
-
201
- If tokens are not pre-configured, the agent asks the user:
202
-
203
- ```
204
- Agent: I need a Jira API token to search for similar issues.
205
- Go to https://id.atlassian.com/manage-profile/security/api-tokens
206
- and create a token. Paste it here.
207
- User: <pastes token>
208
- Agent: <configures MCP server and proceeds>
209
- ```
210
-
211
- The same flow works for GitHub tokens and grok dev keys.
223
+ Requires `GITHUB_TOKEN` in the environment (already passed from `.env`).
212
224
 
213
225
  ## Testing
214
226
 
215
- ### Playwright (browser automation)
216
-
217
- Playwright is pre-installed with Chromium. Use it for custom browser automation,
218
- E2E tests, and UI interaction scenarios.
227
+ ### grok test (Puppeteer-based)
219
228
 
220
229
  ```bash
221
- # Inside tools-dev:
222
- cd /workspace/packages/MyPkg
223
-
224
- # Run Playwright tests (if the package has them)
225
- npx playwright test --project chromium
226
-
227
- # Or write ad-hoc automation
228
- npx playwright codegen http://datagrok:8080
229
- ```
230
-
231
- ### grok test (Puppeteer-based package tests)
232
-
233
- Standard Datagrok package testing via the `grok` CLI:
234
-
235
- ```bash
236
- # Inside tools-dev:
237
- cd /workspace/packages/MyPkg
230
+ cd /workspace/datagrok/packages/MyPkg
238
231
  grok test --host local # headless
239
232
  grok test --host local --gui # visible browser
240
233
  grok test --host local --verbose # detailed output
241
- grok test --host local --category "MyCategory" # filter tests
242
234
  ```
243
235
 
244
- ### Chrome remote debugging
245
-
246
- `grok test --gui` runs Chrome with `--remote-debugging-port=9222`. Port 9222 is
247
- exposed to the host.
248
-
249
- On host: open `chrome://inspect` → Configure → add `localhost:9222` → click "inspect"
250
- on the test session. Set breakpoints in package source during test execution.
251
-
252
- ### Developing test scenarios
253
-
254
- The agent can:
255
- 1. Read existing tests in `packages/ApiTests/` and other packages for patterns
256
- 2. Create new test files following the `package-test.ts` template
257
- 3. Run tests and analyze failures
258
- 4. Generate test cases from Jira issue descriptions or GitHub issues
236
+ ### Playwright
259
237
 
260
238
  ```bash
261
- # Scaffold a test file in a package
262
- cd /workspace/packages/MyPkg
263
- grok add test
239
+ cd /workspace/datagrok/packages/MyPkg
240
+ npx playwright test --project chromium
264
241
  ```
265
242
 
266
- ## Publishing packages
243
+ ### Chrome remote debugging
267
244
 
268
- ### To the local Datagrok instance
245
+ `grok test --gui` runs Chrome with `--remote-debugging-port=9222`.
246
+ On host: `chrome://inspect` → Configure → add `localhost:9222`
247
+
248
+ ## Publishing packages
269
249
 
270
250
  ```bash
271
- # Inside tools-dev:
272
- cd /workspace/packages/MyPkg
251
+ cd /workspace/datagrok/packages/MyPkg
273
252
  grok publish # debug mode (visible only to dev)
274
253
  grok publish --release # public release
275
254
  grok publish --build # build webpack first
@@ -278,184 +257,17 @@ grok publish --build # build webpack first
278
257
  ### To an external server
279
258
 
280
259
  ```bash
281
- # Add a server config
282
260
  grok config add --alias prod \
283
261
  --server https://example.datagrok.ai/api \
284
262
  --key <dev-key>
285
-
286
- # Publish to it
287
263
  grok publish prod --release --build
288
264
  ```
289
265
 
290
- The agent handles the full cycle: build, check, publish, verify in the UI.
291
-
292
- ## Managing the Datagrok stand
293
-
294
- The tools-dev container has the Docker socket mounted, so the agent can manage the
295
- entire compose stack from inside.
296
-
297
- ### Version switching
298
-
299
- ```bash
300
- # Inside tools-dev (or from host):
301
- DG_VERSION=1.22.0 docker compose up -d --pull always
302
- ```
303
-
304
- When the workspace is a separate repo (not public), the auto-cloned public repo
305
- should also be updated to match the new version:
306
-
307
- ```bash
308
- # Inside tools-dev — update the public clone to match new DG_VERSION:
309
- cd /workspace/public && git fetch && git checkout 1.22.0
310
- ```
311
-
312
- The agent does this autonomously when asked:
313
- ```
314
- User: Switch to Datagrok 1.22.0
315
- Agent: <updates DG_VERSION, pulls new images, updates public branch>
316
- Datagrok 1.22.0 is running. Public repo updated to 1.22.0.
317
- ```
318
-
319
- ### DB reset / redeploy
320
-
321
- ```bash
322
- docker compose down -v && docker compose up -d
323
- ```
324
-
325
- ### Adding profiles on the fly
326
-
327
- ```bash
328
- # Need demo databases now
329
- docker compose --profile demo up -d
330
-
331
- # Need everything
332
- docker compose --profile full up -d
333
- ```
334
-
335
- ## Profiles
336
-
337
- | Profile | Services added | Use case |
338
- |---------|---------------|----------|
339
- | (none) | postgres, rabbitmq, grok_pipe, datagrok, tools-dev | Basic package dev |
340
- | `demo` | + world, test_db, northwind | Need demo databases |
341
- | `scripting` | + jupyter_kernel_gateway | Need Python/R/Julia scripts |
342
- | `full` | + grok_connect, grok_spawner, demo DBs, JKG | Everything |
343
-
344
- ## JS API and code reference
345
-
346
- The agent reads JS API source and documentation directly from the workspace — no
347
- generated docs needed.
348
-
349
- ### Key paths (public repo at `/workspace`)
350
-
351
- | What | Path |
352
- |------|------|
353
- | JS API source (types, classes, methods) | `js-api/src/` |
354
- | JS API entry points (grok, ui, dg) | `js-api/grok.ts`, `js-api/ui.ts`, `js-api/dg.ts` |
355
- | JS API CLAUDE.md (module map, patterns) | `js-api/CLAUDE.md` |
356
- | Runnable code samples | `packages/ApiSamples/scripts/` |
357
- | Package development guide | `help/develop/packages/` |
358
- | All platform help docs | `help/` |
359
- | Existing packages (patterns) | `packages/` |
360
- | CLI tool source | `tools/` |
361
-
362
- ### Key paths (separate repo — public auto-cloned)
363
-
364
- Same as above, prefixed with `public/`:
365
- - `public/js-api/src/`, `public/packages/ApiSamples/scripts/`, etc.
366
-
367
- The public branch matches `DG_VERSION` by default, so js-api types stay in sync
368
- with the running Datagrok server.
369
-
370
- ### CLAUDE.md for the agent
371
-
372
- Add to your project's CLAUDE.md so the agent knows where to look:
373
-
374
- ```markdown
375
- ## Reference
376
-
377
- - JS API: read source in `js-api/src/` — see `js-api/CLAUDE.md` for module map
378
- - Code samples: `packages/ApiSamples/scripts/` — runnable examples for eval
379
- - Help docs: `help/develop/` — guides for packages, viewers, functions
380
- - Existing packages: `packages/` — real-world patterns
381
- ```
382
-
383
266
  ## Exposing the client
384
267
 
385
- - Datagrok UI: `http://localhost:${DG_PORT:-8080}`
386
- - Default credentials: **admin / admin** (created on first deploy)
387
-
388
- ### grok CLI config inside the container
389
-
390
- The entrypoint auto-creates `~/.grok/config.yaml` with the local Datagrok instance
391
- (dev key `admin`). The grok CLI is ready to use immediately — no manual config needed.
392
-
393
- If you need to reconfigure:
394
-
395
- ```bash
396
- # Inside tools-dev:
397
- grok config add --alias local \
398
- --server http://datagrok:8080/api \
399
- --key admin --default
400
- ```
401
-
402
- ## Claude Code inside the container
403
-
404
- Mount `~/.claude/` from host (already in the compose file) for credentials and
405
- config. Pass `ANTHROPIC_API_KEY` via `.env` or shell export.
406
-
407
- ```bash
408
- # Single entry point — launch Claude Code interactively
409
- docker exec -it <tools-dev_container> claude --dangerously-skip-permissions
410
-
411
- # One-shot command
412
- docker exec <tools-dev_container> claude -p "publish the Chem package" \
413
- --dangerously-skip-permissions
414
- ```
415
-
416
- The agent can:
417
- - Build, test, and publish packages
418
- - Search Jira/GitHub for context (via MCP plugins)
419
- - Manage the Datagrok stand (version switch, redeploy, DB reset)
420
- - Ask the user for tokens and auth when needed
421
- - Read js-api source and ApiSamples for reference
422
-
423
- ## Quick reference
424
-
425
- ```bash
426
- # Start (basic)
427
- docker compose up -d
428
-
429
- # Start (everything)
430
- docker compose --profile full up -d
431
-
432
- # Launch Claude agent (single entry point)
433
- docker exec -it <tools-dev> claude --dangerously-skip-permissions
434
-
435
- # Shell into tools-dev
436
- docker exec -it <tools-dev> bash
437
-
438
- # Publish a package
439
- docker exec <tools-dev> bash -c "cd /workspace/packages/MyPkg && grok publish"
440
-
441
- # Run grok tests
442
- docker exec <tools-dev> bash -c "cd /workspace/packages/MyPkg && grok test --host local"
443
-
444
- # Run Playwright tests
445
- docker exec <tools-dev> bash -c "cd /workspace/packages/MyPkg && npx playwright test"
446
-
447
- # Version switch
448
- DG_VERSION=1.22.0 docker compose up -d --pull always
449
-
450
- # DB reset
451
- docker compose down -v && docker compose up -d
452
-
453
- # Logs
454
- docker compose logs -f datagrok
455
-
456
- # Stop
457
- docker compose down
458
- ```
268
+ - Datagrok UI: `http://localhost:${DG_PORT}` (port shown at startup, or use `--port` to fix it)
269
+ - Default credentials: **admin / admin**
270
+ - grok CLI inside container: auto-configured to `http://datagrok:8080/api` with key `admin`
459
271
 
460
272
  ## Troubleshooting
461
273
 
@@ -463,39 +275,21 @@ docker compose down
463
275
  ```bash
464
276
  docker compose logs datagrok
465
277
  # Wait for DB migrations — first start takes 1-2 minutes
466
- # Look for: "Server started on port 8080"
467
278
  ```
468
279
 
469
280
  ### grok publish fails
470
- Ensure `~/.grok/config.yaml` inside the container points to `http://datagrok:8080/api`
471
- with a valid dev key. The container reaches Datagrok via Docker DNS, not `localhost`.
472
-
473
- ### Chrome / Playwright not working
474
- ```bash
475
- docker exec <tools-dev> google-chrome-stable --version
476
- docker exec <tools-dev> npx playwright --version
477
- ```
478
- If missing, pull the latest image: `docker pull datagrok/tools-dev:latest`
281
+ Ensure `~/.grok/config.yaml` inside the container points to `http://datagrok:8080/api`.
282
+ The container reaches Datagrok via Docker DNS (`datagrok`), not `localhost`.
479
283
 
480
- ### MCP plugins not connecting
481
- ```bash
482
- # Verify env vars are set
483
- docker exec <tools-dev> env | grep -E 'JIRA|GITHUB'
484
- # Re-register if needed
485
- docker exec -it <tools-dev> claude mcp add mcp-atlassian -s user -- \
486
- npx -y mcp-atlassian --jira-url "$JIRA_URL" \
487
- --jira-username "$JIRA_USERNAME" --jira-token "$JIRA_TOKEN"
488
- ```
284
+ ### Permission denied on /workspace/datagrok
285
+ The entrypoint runs as root and fixes ownership. If using the image directly (not via
286
+ `grok claude`), add `user: root` to the compose service definition.
489
287
 
490
- ### Agent can't manage Docker (version switch, redeploy)
491
- The Docker socket must be mounted and the `dev` user must be in the `docker` group:
492
- ```bash
493
- docker exec <tools-dev> docker ps
494
- ```
495
- If permission denied, check that `/var/run/docker.sock` is mounted and accessible.
288
+ ### Agent can't manage Docker
289
+ The Docker socket must be mounted. Check: `docker exec <tools-dev> docker ps`
496
290
 
497
291
  ### Container can't resolve `datagrok` hostname
498
292
  All services must be on the same Docker network:
499
293
  ```bash
500
- docker network inspect dg-pkg-${TASK_KEY:-default}-net
294
+ docker network inspect dg-pkg-<project>-net
501
295
  ```
@@ -13,6 +13,11 @@ resolve_branch() {
13
13
  }
14
14
 
15
15
  PUBLIC_DIR="${DG_PUBLIC_DIR:-/workspace/datagrok}"
16
+ # Ensure workspace dirs are writable by node user (bind-mounts create parents as root)
17
+ chown node:node /workspace "$PUBLIC_DIR" 2>/dev/null || true
18
+ # Allow git to operate on dirs owned by different users (root runs git, dir owned by node)
19
+ git config --global --add safe.directory "$PUBLIC_DIR" 2>/dev/null || true
20
+ git config --global init.defaultBranch master 2>/dev/null || true
16
21
  # Detect if /workspace/repo IS the public repo.
17
22
  # Require .git to avoid false positives from packages that have public/js-api via npm link.
18
23
  if [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/js-api" ]; then
@@ -29,31 +34,40 @@ else
29
34
  else
30
35
  BRANCH=$(resolve_branch "${DG_VERSION:-latest}")
31
36
  fi
32
- # Sparse checkout: only fetch dirs needed for package context (js-api, libraries, packages)
33
- # plus root files (CLAUDE.md, .claude/, etc.). Uses --depth 1 without blob filter so all
34
- # blobs arrive in one pack avoids slow on-demand fetching during checkout.
37
+ # Sparse checkout (cone mode) with partial clone: only fetch js-api, libraries, and
38
+ # ApiSamples. Cone mode integrates with --filter=blob:none so the server only sends
39
+ # blobs for the included directories (~3 MB vs 1.67 GB for the full tree).
35
40
  sparse_clone() {
36
41
  local branch="$1"
37
42
  # Use init+fetch instead of clone to handle pre-existing directories (e.g. mount points)
38
- git init "$PUBLIC_DIR" \
39
- && git -C "$PUBLIC_DIR" remote add origin "$REPO" \
40
- && git -C "$PUBLIC_DIR" sparse-checkout set --no-cone \
41
- '/*' '!connectors/' '!docker/' '!docusaurus/' '!docusaurus-static/' \
42
- '!environments/' '!hooks/' '!misc/' 'python-api/' '!datagrok-celery-task/' \
43
- '/js-api/**' '/libraries/**' '/packages/**' \
44
- && git -C "$PUBLIC_DIR" fetch --depth 1 origin "$branch" \
45
- && git -C "$PUBLIC_DIR" checkout -B "$branch" FETCH_HEAD
43
+ git init -q "$PUBLIC_DIR" \
44
+ && (git -C "$PUBLIC_DIR" remote add origin "$REPO" 2>/dev/null || git -C "$PUBLIC_DIR" remote set-url origin "$REPO") \
45
+ && git -C "$PUBLIC_DIR" config remote.origin.promisor true \
46
+ && git -C "$PUBLIC_DIR" config remote.origin.partialclonefilter blob:none \
47
+ && git -C "$PUBLIC_DIR" sparse-checkout set --cone .claude js-api libraries packages/ApiSamples \
48
+ && git -C "$PUBLIC_DIR" fetch -q --depth 1 --filter=blob:none origin "$branch" \
49
+ && git -C "$PUBLIC_DIR" checkout -q -B "$branch" FETCH_HEAD
46
50
  }
47
- # Clear directory contents without removing the dir itself (may be a mount point)
48
- clear_dir() {
49
- find "$1" -mindepth 1 -delete 2>/dev/null || rm -rf "$1"/* "$1"/.[!.]* 2>/dev/null || true
51
+ # Clear git state without touching bind-mounted subdirs (e.g. packages/awesome)
52
+ clear_git() {
53
+ rm -rf "$1/.git" 2>/dev/null || true
50
54
  }
51
55
  echo "[tools-dev] Cloning public repo ($BRANCH, sparse) into $PUBLIC_DIR..."
52
- sparse_clone "$BRANCH" \
53
- || { echo "[tools-dev] Branch '$BRANCH' clone failed, falling back to master."
54
- clear_dir "$PUBLIC_DIR"
55
- sparse_clone "master"; }
56
- echo "[tools-dev] Public repo ready at $PUBLIC_DIR (branch: $(git -C "$PUBLIC_DIR" branch --show-current))."
56
+ if sparse_clone "$BRANCH"; then
57
+ echo "[tools-dev] Public repo ready."
58
+ elif [ "$BRANCH" != "master" ]; then
59
+ echo "[tools-dev] Branch '$BRANCH' clone failed, falling back to master."
60
+ clear_git "$PUBLIC_DIR"
61
+ if sparse_clone "master"; then
62
+ echo "[tools-dev] Public repo ready (branch: master)."
63
+ else
64
+ echo "[tools-dev] WARNING: Failed to clone public repo."
65
+ fi
66
+ else
67
+ echo "[tools-dev] WARNING: Failed to clone public repo."
68
+ fi
69
+ # Ensure node user can read cloned files (entrypoint runs as root)
70
+ chown -R node:node "$PUBLIC_DIR" 2>/dev/null || true
57
71
  fi
58
72
 
59
73
  # ── Mount workspace inside public repo for non-public workspaces ──
@@ -64,21 +78,20 @@ if [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/js-api" ]; then
64
78
  elif [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/public/js-api" ]; then
65
79
  echo "[tools-dev] Monorepo detected — public context at /workspace/repo/public/."
66
80
  elif [ -d "$PUBLIC_DIR/js-api" ]; then
67
- # Cloned public repo — link workspace into packages/ and expose context at /workspace/
68
81
  LINK_NAME="${FOLDER_NAME:-$TASK_KEY}"
69
82
  if [ -n "$LINK_NAME" ]; then
70
83
  mkdir -p "$PUBLIC_DIR/packages" 2>/dev/null || true
71
- [ ! -e "$PUBLIC_DIR/packages/$LINK_NAME" ] && ln -s /workspace/repo "$PUBLIC_DIR/packages/$LINK_NAME"
72
- echo "[tools-dev] Workspace linked at $PUBLIC_DIR/packages/$LINK_NAME"
84
+ if [ ! -d "$PUBLIC_DIR/packages/$LINK_NAME" ]; then
85
+ ln -sfn /workspace/repo "$PUBLIC_DIR/packages/$LINK_NAME"
86
+ fi
87
+ echo "[tools-dev] Workspace at $PUBLIC_DIR/packages/$LINK_NAME"
73
88
  fi
74
89
  PUBLIC_BASENAME=$(basename "$PUBLIC_DIR")
75
- [ ! -e /workspace/.claude ] && ln -s "$PUBLIC_BASENAME/.claude" /workspace/.claude
76
- [ ! -e /workspace/CLAUDE.md ] && ln -s "$PUBLIC_BASENAME/CLAUDE.md" /workspace/CLAUDE.md
77
- echo "[tools-dev] Linked public repo context at /workspace/"
90
+ ln -sfn "$PUBLIC_BASENAME/.claude" /workspace/.claude 2>/dev/null || true
91
+ ln -sfn "$PUBLIC_BASENAME/CLAUDE.md" /workspace/CLAUDE.md 2>/dev/null || true
78
92
  fi
79
93
 
80
94
  # Auto-configure grok CLI to point to the local Datagrok instance.
81
- # Only creates the config if it doesn't already exist (preserves host config).
82
95
  GROK_DIR="/home/node/.grok"
83
96
  GROK_CFG="$GROK_DIR/config.yaml"
84
97
  if [ ! -f "$GROK_CFG" ] || [ ! -s "$GROK_CFG" ]; then
@@ -91,9 +104,10 @@ servers:
91
104
  key: admin
92
105
  YAML
93
106
  [ -s "$GROK_CFG" ] && echo "[tools-dev] Created grok config at $GROK_CFG"
94
- elif ! grep -q "datagrok:8080" "$GROK_CFG" 2>/dev/null; then
95
- echo "[tools-dev] Note: existing grok config found. Add 'local' server with:"
96
- echo " grok config add --alias local --server http://datagrok:8080/api --key admin --default"
97
107
  fi
98
108
 
109
+ # Drop to node user for the main process (entrypoint runs as root for permission fixes)
110
+ if [ "$(id -u)" = "0" ]; then
111
+ exec setpriv --reuid=node --regid=node --init-groups "$@"
112
+ fi
99
113
  exec "$@"
@@ -259,6 +259,7 @@ services:
259
259
  networks:
260
260
  dg:
261
261
  aliases: [tools-dev]
262
+ user: root
262
263
  stdin_open: true
263
264
  tty: true
264
265
  restart: unless-stopped
@@ -321,7 +322,7 @@ function findFreePort() {
321
322
  srv.on('error', reject);
322
323
  });
323
324
  }
324
- function writeProjectFiles(taskKey, args, worktreeRoot, dgPort) {
325
+ function writeProjectFiles(taskKey, args, worktreeRoot, dgPort, repoRoot) {
325
326
  const projectDir = getProjectDir(taskKey);
326
327
  if (!_fs.default.existsSync(projectDir)) _fs.default.mkdirSync(projectDir, {
327
328
  recursive: true
@@ -349,14 +350,19 @@ function writeProjectFiles(taskKey, args, worktreeRoot, dgPort) {
349
350
  // Bind-mount local entrypoint so fixes take effect without rebuilding the image
350
351
  if (hasLocalEntrypoint) volumes.push(` - "${toDockerPath(entrypointPath)}:/usr/local/bin/entrypoint.sh"`);
351
352
 
352
- // Claude profile (~/.claude + ~/.claude.json)
353
+ // For external repos, also mount workspace inside the public repo tree so Claude Code
354
+ // sees the real path (not a symlink that resolves back to /workspace/repo).
355
+ const isPublic = repoRoot ? isPublicRepo(repoRoot) : false;
356
+ if (!isPublic) {
357
+ const folderName = _path.default.basename(worktreeRoot);
358
+ volumes.push(` - "${toDockerPath(worktreeRoot)}:/workspace/datagrok/packages/${folderName}"`);
359
+ }
360
+
361
+ // Claude profile: credentials are copied into the container after startup (not bind-mounted)
362
+ // to avoid permission issues — host files may have different UID/GID than the container's
363
+ // node user.
353
364
  const claudeHome = findClaudeHome();
354
- if (claudeHome) {
355
- volumes.push(` - "${toDockerPath(claudeHome)}:/home/node/.claude"`);
356
- const claudeState = _path.default.join(_path.default.dirname(claudeHome), '.claude.json');
357
- if (_fs.default.existsSync(claudeState) && _fs.default.statSync(claudeState).isFile()) volumes.push(` - "${toDockerPath(claudeState)}:/home/node/.claude.json"`);
358
- color.info(`Claude profile: ${claudeHome}`);
359
- } else color.warn('No Claude profile found. Set CLAUDE_HOME or run "claude" locally to log in.');
365
+ if (claudeHome) color.info(`Claude profile: ${claudeHome}`);else color.warn('No Claude profile found. Set CLAUDE_HOME or run "claude" locally to log in.');
360
366
  const overridePath = _path.default.join(projectDir, 'docker-compose.override.yaml');
361
367
  if (volumes.length > 0) {
362
368
  const override = ['services:', ' tools-dev:', ' volumes:', ...volumes].join('\n') + '\n';
@@ -539,7 +545,7 @@ async function claude(args) {
539
545
  color.info(`Datagrok UI will be at: http://localhost:${dgPort}`);
540
546
 
541
547
  // Write compose + .env to temp dir (no external file dependencies)
542
- const projectDir = writeProjectFiles(taskKey, args, worktreeRoot, dgPort);
548
+ const projectDir = writeProjectFiles(taskKey, args, worktreeRoot, dgPort, repoRoot);
543
549
  color.info(`Workspace: ${worktreeRoot}`);
544
550
 
545
551
  // Start containers
@@ -549,11 +555,25 @@ async function claude(args) {
549
555
  color.error('Failed to start containers.');
550
556
  return false;
551
557
  }
558
+ const containerName = `dg-pkg-${taskKey.toLowerCase()}-tools-dev-1`;
559
+
560
+ // Copy Claude credentials into the container (avoids bind-mount permission issues)
561
+ const claudeHomePath = findClaudeHome();
562
+ if (claudeHomePath) {
563
+ const claudeFiles = ['.credentials.json', 'settings.json', 'settings.local.json'];
564
+ for (const file of claudeFiles) {
565
+ const filePath = _path.default.join(claudeHomePath, file);
566
+ if (_fs.default.existsSync(filePath) && _fs.default.statSync(filePath).isFile()) (0, _child_process.spawnSync)('docker', ['cp', filePath, `${containerName}:/home/node/.claude/${file}`]);
567
+ }
568
+ const claudeState = _path.default.join(_path.default.dirname(claudeHomePath), '.claude.json');
569
+ if (_fs.default.existsSync(claudeState) && _fs.default.statSync(claudeState).isFile()) (0, _child_process.spawnSync)('docker', ['cp', claudeState, `${containerName}:/home/node/.claude.json`]);
570
+ // Fix ownership — docker cp creates files as root
571
+ (0, _child_process.spawnSync)('docker', ['exec', '-u', 'root', containerName, 'chown', '-R', 'node:node', '/home/node/.claude']);
572
+ }
552
573
 
553
574
  // Determine Claude working directory based on repo type
554
575
  // Only trust public-repo markers when we are inside a real git repo — packages can have
555
576
  // public/js-api via node_modules symlinks, which would cause a false positive.
556
- const containerName = `dg-pkg-${taskKey.toLowerCase()}-tools-dev-1`;
557
577
  let claudeWorkDir;
558
578
  if (repoRoot && _fs.default.existsSync(_path.default.join(repoRoot, 'js-api'))) claudeWorkDir = '/workspace/repo';else if (repoRoot && _fs.default.existsSync(_path.default.join(repoRoot, 'public', 'js-api'))) claudeWorkDir = '/workspace/repo/public';else {
559
579
  // External repo: entrypoint clones public repo and creates symlink — wait for it
@@ -587,7 +607,7 @@ async function claude(args) {
587
607
  const claudeArgs = ['--dangerously-skip-permissions'];
588
608
  if (args.prompt) claudeArgs.push('-p', args.prompt);
589
609
  color.info(`Launching Claude Code in container ${containerName}...`);
590
- (0, _child_process.spawnSync)('docker', ['exec', '-it', '-w', claudeWorkDir, containerName, 'claude', ...claudeArgs], {
610
+ (0, _child_process.spawnSync)('docker', ['exec', '-it', '-u', 'node', '-w', claudeWorkDir, containerName, 'claude', ...claudeArgs], {
591
611
  stdio: 'inherit'
592
612
  });
593
613
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "6.0.6",
3
+ "version": "6.0.8",
4
4
  "description": "Utility to upload and publish packages to Datagrok",
5
5
  "homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
6
6
  "dependencies": {