datagrok-tools 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(find /c/repos/Datagrok -type f \\\\\\( -name \"Dockerfile*\" -o -name \"docker-compose*\" \\\\\\) 2>/dev/null | grep -v node_modules | head -20)",
5
+ "Bash(npm run:*)",
6
+ "Bash(docker build:*)",
7
+ "Bash(cd C:/repos/awesome && node C:/repos/Datagrok/public/tools/bin/grok.js claude destroy GROK-PLAYGROUND 2>&1)",
8
+ "Bash(cd C:/repos/awesome && node C:/repos/Datagrok/public/tools/bin/grok.js claude GROK-PLAYGROUND --in-place --keep --prompt \"echo hello and exit\" 2>&1)",
9
+ "Bash(cd C:/repos/awesome && node C:/repos/Datagrok/public/tools/bin/grok.js claude GROK-PLAYGROUND --in-place --keep --prompt \"echo done\" 2>&1)",
10
+ "Bash(git -C C:/repos/awesome rev-parse --show-toplevel 2>&1)",
11
+ "Bash(cd C:/repos/awesome && node C:/repos/Datagrok/public/tools/bin/grok.js claude GROK-PLAYGROUND 2>&1)",
12
+ "Bash(cd C:/repos/awesome && node C:/repos/Datagrok/public/tools/bin/grok.js claude GROK-PLAYGROUND --keep 2>&1)",
13
+ "Bash(MSYS_NO_PATHCONV=1 docker exec dg-pkg-grok-playground-tools-dev-1 ls /workspace/datagrok/ 2>&1)",
14
+ "Bash(MSYS_NO_PATHCONV=1 docker exec dg-pkg-grok-playground-tools-dev-1 ls -la /workspace/datagrok/packages/awesome 2>&1)",
15
+ "Bash(MSYS_NO_PATHCONV=1 docker exec dg-pkg-grok-playground-tools-dev-1 ls /workspace/datagrok/packages/awesome/ 2>&1)",
16
+ "Bash(MSYS_NO_PATHCONV=1 docker exec dg-pkg-grok-playground-tools-dev-1 bash -c 'echo \"=== Symlink ===\" && ls -la /workspace/datagrok/packages/awesome && echo \"=== Package contents ===\" && ls /workspace/datagrok/packages/awesome/ && echo \"=== Public repo root ===\" && ls /workspace/datagrok/js-api /workspace/datagrok/packages /workspace/datagrok/CLAUDE.md 2>&1 | head -5' 2>&1)",
17
+ "Bash(grep -r \"tools-dev\" /c/repos/Datagrok --include=\"*\" 2>/dev/null | head -20)",
18
+ "Bash(find /c/repos/Datagrok/public/tools/.devcontainer -type f -name \".*\" 2>/dev/null | sort)",
19
+ "Bash(grep -r \"tools-dev\" /c/repos/Datagrok/public/tools/bin --include=\"*.ts\" 2>/dev/null | head -20)"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ *.sh text eol=lf
@@ -1,39 +1,23 @@
1
1
  #!/bin/bash
2
2
  set -e
3
3
 
4
- # Resolve the actual version when DG_VERSION is "latest" needed for git branch matching.
5
- resolve_version() {
4
+ # Map DG_VERSION to a git branch name for cloning the public repo.
5
+ # Docker tags like "latest" or "bleeding-edge" aren't git branches — use master.
6
+ # Explicit version numbers (e.g. "0.151.9") are tried as-is with a master fallback.
7
+ resolve_branch() {
6
8
  local ver="${1:-latest}"
7
- if [ "$ver" = "latest" ]; then
8
- echo "[tools-dev] Resolving latest Datagrok version from Docker Hub..." >&2
9
- local resolved
10
- resolved=$(curl -sf "https://hub.docker.com/v2/repositories/datagrok/datagrok/tags?page_size=100&ordering=-name" \
11
- | node -e "
12
- const data = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
13
- const tags = (data.results||[]).map(t=>t.name).filter(n=>/^\d+\.\d+\.\d+$/.test(n));
14
- tags.sort((a,b)=>{
15
- const pa=a.split('.').map(Number), pb=b.split('.').map(Number);
16
- return pb[0]-pa[0]||pb[1]-pa[1]||pb[2]-pa[2];
17
- });
18
- if(tags[0]) process.stdout.write(tags[0]);
19
- " 2>/dev/null) || true
20
- if [ -n "$resolved" ]; then
21
- echo "[tools-dev] Resolved latest version: $resolved" >&2
22
- echo "$resolved"
23
- else
24
- echo "[tools-dev] Could not resolve latest version, falling back to master" >&2
25
- echo "master"
26
- fi
27
- else
28
- echo "$ver"
29
- fi
9
+ case "$ver" in
10
+ latest|bleeding-edge) echo "master" ;;
11
+ *) echo "$ver" ;;
12
+ esac
30
13
  }
31
14
 
32
- PUBLIC_DIR="/workspace/public"
33
- # Detect if /workspace/repo IS the public repo (has js-api/ at root)
34
- if [ -d "/workspace/repo/js-api" ]; then
15
+ PUBLIC_DIR="${DG_PUBLIC_DIR:-/workspace/datagrok}"
16
+ # Detect if /workspace/repo IS the public repo.
17
+ # Require .git to avoid false positives from packages that have public/js-api via npm link.
18
+ if [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/js-api" ]; then
35
19
  echo "[tools-dev] Public repo detected at /workspace/repo — skipping clone."
36
- elif [ -d "/workspace/repo/public/js-api" ]; then
20
+ elif [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/public/js-api" ]; then
37
21
  echo "[tools-dev] Monorepo detected — public repo at /workspace/repo/public."
38
22
  elif [ -d "$PUBLIC_DIR/js-api" ]; then
39
23
  echo "[tools-dev] Public repo already at $PUBLIC_DIR."
@@ -43,31 +27,45 @@ else
43
27
  if [ -n "$DG_PUBLIC_BRANCH" ]; then
44
28
  BRANCH="$DG_PUBLIC_BRANCH"
45
29
  else
46
- BRANCH=$(resolve_version "${DG_VERSION:-latest}")
30
+ BRANCH=$(resolve_branch "${DG_VERSION:-latest}")
47
31
  fi
48
- echo "[tools-dev] Cloning public repo ($BRANCH) into $PUBLIC_DIR..."
49
- git clone --depth 1 --branch "$BRANCH" "$REPO" "$PUBLIC_DIR" 2>/dev/null \
32
+ # Sparse checkout: only fetch dirs needed for package context (js-api, libraries, help, packages)
33
+ # plus root files (CLAUDE.md, .claude/, etc.). Much faster than a full clone.
34
+ sparse_clone() {
35
+ local branch="$1"
36
+ git clone --depth 1 --branch "$branch" --filter=blob:none --no-checkout "$REPO" "$PUBLIC_DIR" \
37
+ && git -C "$PUBLIC_DIR" sparse-checkout set --no-cone \
38
+ '/*' '!connectors/' '!docker/' '!docusaurus/' '!docusaurus-static/' \
39
+ '!environments/' '!hooks/' '!misc/' 'python-api/' '!datagrok-celery-task/' \
40
+ '/js-api/**' '/libraries/**' '/help/**' '/packages/**' '!help/uploads/**' \
41
+ && git -C "$PUBLIC_DIR" checkout
42
+ }
43
+ echo "[tools-dev] Cloning public repo ($BRANCH, sparse) into $PUBLIC_DIR..."
44
+ sparse_clone "$BRANCH" \
50
45
  || { echo "[tools-dev] Branch '$BRANCH' not found, falling back to master."
51
- git clone --depth 1 --branch master "$REPO" "$PUBLIC_DIR"; }
46
+ rm -rf "$PUBLIC_DIR"
47
+ sparse_clone "master"; }
52
48
  echo "[tools-dev] Public repo ready at $PUBLIC_DIR (branch: $(git -C "$PUBLIC_DIR" branch --show-current))."
53
49
  fi
54
50
 
55
51
  # ── Mount workspace inside public repo for non-public workspaces ──
56
52
  # When TASK_KEY is set and workspace is not the public repo, link it into packages/
57
53
  # so Claude Code walks up to find all public repo context (CLAUDE.md, .claude/, js-api/, etc.)
58
- if [ -d "/workspace/repo/js-api" ]; then
54
+ if [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/js-api" ]; then
59
55
  echo "[tools-dev] Public repo IS the workspace — no linking needed."
60
- elif [ -d "/workspace/repo/public/js-api" ]; then
56
+ elif [ -e "/workspace/repo/.git" ] && [ -d "/workspace/repo/public/js-api" ]; then
61
57
  echo "[tools-dev] Monorepo detected — public context at /workspace/repo/public/."
62
58
  elif [ -d "$PUBLIC_DIR/js-api" ]; then
63
59
  # Cloned public repo — link workspace into packages/ and expose context at /workspace/
64
- if [ -n "$TASK_KEY" ]; then
60
+ LINK_NAME="${FOLDER_NAME:-$TASK_KEY}"
61
+ if [ -n "$LINK_NAME" ]; then
65
62
  mkdir -p "$PUBLIC_DIR/packages" 2>/dev/null || true
66
- [ ! -e "$PUBLIC_DIR/packages/$TASK_KEY" ] && ln -s /workspace/repo "$PUBLIC_DIR/packages/$TASK_KEY"
67
- echo "[tools-dev] Workspace linked at $PUBLIC_DIR/packages/$TASK_KEY"
63
+ [ ! -e "$PUBLIC_DIR/packages/$LINK_NAME" ] && ln -s /workspace/repo "$PUBLIC_DIR/packages/$LINK_NAME"
64
+ echo "[tools-dev] Workspace linked at $PUBLIC_DIR/packages/$LINK_NAME"
68
65
  fi
69
- [ ! -e /workspace/.claude ] && ln -s public/.claude /workspace/.claude
70
- [ ! -e /workspace/CLAUDE.md ] && ln -s public/CLAUDE.md /workspace/CLAUDE.md
66
+ PUBLIC_BASENAME=$(basename "$PUBLIC_DIR")
67
+ [ ! -e /workspace/.claude ] && ln -s "$PUBLIC_BASENAME/.claude" /workspace/.claude
68
+ [ ! -e /workspace/CLAUDE.md ] && ln -s "$PUBLIC_BASENAME/CLAUDE.md" /workspace/CLAUDE.md
71
69
  echo "[tools-dev] Linked public repo context at /workspace/"
72
70
  fi
73
71
 
package/CLAUDE.md CHANGED
@@ -14,7 +14,7 @@ npm run build # Transpile TypeScript to JavaScript using Babe
14
14
  npm run debug-source-map # Build with source maps for debugging
15
15
  ```
16
16
 
17
- The build process uses Babel with `@babel/preset-typescript` to transpile TypeScript files from `bin/` (TypeScript source) to `bin/` (JavaScript output). The source TypeScript files are transpiled in-place.
17
+ The build process uses Babel with `@babel/preset-typescript` to transpile TypeScript files from `bin/` to `bin/` (in-place). **Important:** `.ts` source and `.js` output coexist in the same `bin/` directory — `grok.js` requires the transpiled `.js` files, not the `.ts` sources. After editing any `.ts` file, you must run `npm run build` before testing.
18
18
 
19
19
  ### Link for Local Development
20
20
  ```bash
@@ -42,13 +42,18 @@ The CLI uses a modular command pattern. Each command is a separate module that:
42
42
  - `add.ts` - Add entities (functions, scripts, queries, etc.) to packages
43
43
  - `publish.ts` - Upload and deploy packages to Datagrok servers
44
44
  - `check.ts` - Validate package structure, signatures, imports
45
+ - `build.ts` - Build one package or recursively build all packages in a directory
45
46
  - `test.ts` - Run Puppeteer-based tests for a single package
46
47
  - `test-all.ts` - Run tests across multiple packages
48
+ - `stress-tests.ts` - Run stress tests (must be run from ApiTests package)
47
49
  - `api.ts` - Auto-generate TypeScript wrappers for scripts/queries
48
50
  - `link.ts` - Link libraries for plugin development
51
+ - `claude.ts` - Launch a Dockerized dev environment with Datagrok + Claude Code
49
52
  - `migrate.ts` - Update legacy packages
50
53
  - `init.ts` - Apply configuration to existing packages
51
54
 
55
+ The commands `api`, `check`, `link`, `publish`, and `test` support the `--all` flag to run recursively across all packages in the current directory.
56
+
52
57
  ### Template System
53
58
 
54
59
  **Package Templates** (`package-template/`)
@@ -132,6 +137,90 @@ Tests use Puppeteer for headless browser automation:
132
137
  - `--catchUnhandled` - Catch unhandled exceptions
133
138
  - `--debug` - Debug breakpoints (requires `--gui`)
134
139
 
140
+ ### `grok build` Command
141
+
142
+ Builds packages with `npm install` + `npm run build`. Supports:
143
+ - Single package: `grok build` (from package directory)
144
+ - Recursive: `grok build --recursive` (discovers and builds all packages in subdirectories)
145
+ - `--filter "name:Chem"` - Filter packages by package.json fields (supports regex, `&&` for multiple conditions)
146
+ - `--parallel N` - Max parallel build jobs (default 4)
147
+ - `--no-incremental` - Force full rebuild (default uses `--env incremental`)
148
+
149
+ ### `grok claude` Command
150
+
151
+ Launches a full Dockerized development environment with Datagrok + Claude Code. The compose configuration is embedded in `claude.ts` — no external files needed.
152
+
153
+ ```bash
154
+ grok claude <project> # Create worktree + start containers + launch Claude
155
+ grok claude <project> --in-place # Use current directory (no worktree)
156
+ grok claude <project> --keep # Leave containers running on exit
157
+ grok claude <project> --version 1.22.0 # Pin Datagrok version
158
+ grok claude <project> --profile full # Include spawner, JKG, demo DBs
159
+ grok claude <project> --profile scripting # Include JKG for Python/R/Julia
160
+ grok claude <project> --port 8080 # Fix Datagrok port (default: random free port)
161
+ grok claude <project> --prompt "fix bug" # Pass prompt to Claude Code (-p flag)
162
+ grok claude destroy <project> # Tear down containers + worktree + temp files
163
+ grok claude destroy-all # Destroy all known projects
164
+ ```
165
+
166
+ **Project name restrictions:** `master` and `main` are rejected.
167
+
168
+ **Lifecycle:**
169
+ 1. Creates git worktree at `~/pkg-worktrees/<project>` (unless `--in-place` or not in a git repo)
170
+ 2. Writes `docker-compose.yaml` + `.env` + optional `docker-compose.override.yaml` to `$TMPDIR/dg-pkg-<project>`
171
+ 3. Runs `docker compose up -d --wait`
172
+ 4. Detects Claude working directory based on repo type (see below)
173
+ 5. Launches `claude --dangerously-skip-permissions` inside the `tools-dev` container
174
+ 6. On exit: stops containers (unless `--keep`)
175
+
176
+ **Version resolution:** `--version` flag > `bleeding-edge` (if inside public repo) > `latest`
177
+
178
+ **Compose services (embedded template):**
179
+ - Always started: `postgres` (pgvector:pg17), `rabbitmq`, `grok_pipe`, `datagrok`, `grok_connect`, `tools-dev`
180
+ - Profile `full`: adds `grok_spawner`
181
+ - Profile `scripting`/`full`: adds `jupyter_kernel_gateway`
182
+ - Profile `demo`/`full`: adds `world`, `test_db`, `northwind` demo databases
183
+
184
+ **Note:** The embedded template in `claude.ts` differs from `.devcontainer/docker-compose.yaml`:
185
+ - Embedded uses separate version vars per service (`DATAGROK_VERSION`, `GROK_CONNECT_VERSION`, `GROK_SPAWNER_VERSION`, `JKG_VERSION`, `TOOLS_DEV_VERSION`); `.devcontainer/` uses a single `DG_VERSION` for all
186
+ - Embedded mounts workspace at `/workspace/repo`; `.devcontainer/` mounts at `/workspace`
187
+ - Embedded has `grok_connect` always-on (no profile); `.devcontainer/` puts it under `profiles: ["full"]`
188
+ - Embedded uses `grok_pipe:latest`; `.devcontainer/` uses `grok_pipe:${DG_VERSION}`
189
+ - Embedded doesn't mount `~/.grok` from host; `.devcontainer/` does
190
+
191
+ **Host config override:** If `~/.claude` (or `CLAUDE_HOME`) is found on the host, a `docker-compose.override.yaml` is generated to bind-mount it into the container. Also mounts `~/.claude.json` if present.
192
+
193
+ **Working directory detection inside container:**
194
+ - Public repo root (has `js-api/`): `/workspace/repo`
195
+ - Monorepo (has `public/js-api/`): `/workspace/repo/public`
196
+ - External repo: `/workspace/datagrok/packages/<folder-name>` (waits up to 600s for entrypoint to clone public repo)
197
+
198
+ #### tools-dev Container (`Dockerfile.pkg_dev`)
199
+
200
+ Based on `node:22-bookworm-slim`. Pre-installed:
201
+ - Google Chrome stable (for Puppeteer), Playwright + Chromium
202
+ - `datagrok-tools` (grok CLI) and `@anthropic-ai/claude-code` (global npm)
203
+ - git, curl, jq, docker CLI
204
+ - Runs as `node` user (UID 1000, added to `docker` group)
205
+
206
+ #### Entrypoint (`entrypoint.sh`)
207
+
208
+ 1. **Repo detection:** checks if `/workspace/repo` is the public repo (has `.git` + `js-api/`) or monorepo (`public/js-api/`)
209
+ 2. **Auto-clone:** if workspace is not the public repo, sparse-clones it to `$DG_PUBLIC_DIR` (default `/workspace/datagrok`) — excludes `connectors/`, `docker/`, `environments/`, `python-api/`, etc. for speed. Branch resolved as: `DG_PUBLIC_BRANCH` > `DG_VERSION` mapped to branch > `master` fallback
210
+ 3. **Workspace linking:** for non-public repos, symlinks `/workspace/repo` into the cloned repo's `packages/` dir and links `.claude`/`CLAUDE.md` at `/workspace/` for context discovery
211
+ 4. **Grok config:** auto-creates `~/.grok/config.yaml` pointing to `http://datagrok:8080/api` with key `admin` (only if config doesn't already exist)
212
+
213
+ #### Profiles
214
+
215
+ | Profile | Additional services |
216
+ |---------|-------------------|
217
+ | (none) | postgres, rabbitmq, grok_pipe, datagrok, grok_connect, tools-dev |
218
+ | `scripting` | + jupyter_kernel_gateway |
219
+ | `demo` | + world, test_db, northwind |
220
+ | `full` | + grok_spawner, JKG, demo DBs |
221
+
222
+ See `.devcontainer/PACKAGES_DEV.md` for detailed usage docs, architecture diagram, MCP plugin setup (Jira/GitHub), and troubleshooting.
223
+
135
224
  ## Key Patterns and Conventions
136
225
 
137
226
  ### Naming Conventions
@@ -243,16 +243,19 @@ services:
243
243
  - \${WORKTREE_PATH:-.}:/workspace/repo
244
244
  - \${DOCKER_SOCK}:/var/run/docker.sock
245
245
  - npm_cache:/home/node/.npm
246
+ - public_repo:/workspace/datagrok
246
247
  environment:
247
248
  ANTHROPIC_API_KEY: \${ANTHROPIC_API_KEY:-}
248
249
  DG_VERSION: \${DG_VERSION:-latest}
249
250
  DG_PUBLIC_REPO: \${DG_PUBLIC_REPO:-https://github.com/datagrok-ai/public.git}
250
251
  DG_PUBLIC_BRANCH: \${DG_PUBLIC_BRANCH:-}
252
+ DG_PUBLIC_DIR: \${DG_PUBLIC_DIR:-/workspace/datagrok}
251
253
  JIRA_URL: \${JIRA_URL:-https://reddata.atlassian.net}
252
254
  JIRA_USERNAME: \${JIRA_USERNAME:-}
253
255
  JIRA_TOKEN: \${JIRA_TOKEN:-}
254
256
  GITHUB_TOKEN: \${GITHUB_TOKEN:-}
255
257
  TASK_KEY: \${TASK_KEY:-}
258
+ FOLDER_NAME: \${FOLDER_NAME:-}
256
259
  working_dir: /workspace/repo
257
260
  networks:
258
261
  dg:
@@ -269,6 +272,7 @@ volumes:
269
272
  demo_test:
270
273
  demo_northwind:
271
274
  npm_cache:
275
+ public_repo:
272
276
 
273
277
  networks:
274
278
  dg:
@@ -332,7 +336,7 @@ function writeProjectFiles(taskKey, args, worktreeRoot, dgPort) {
332
336
  const dockerSock = process.platform === 'win32' ? '//var/run/docker.sock' : '/var/run/docker.sock';
333
337
 
334
338
  // Generate .env — all paths use forward slashes for Docker compatibility
335
- const envLines = [`WORKTREE_PATH=${toDockerPath(worktreeRoot)}`, `DG_PORT=${dgPort}`, `TASK_KEY=${taskKey.toLowerCase()}`, `DOCKER_SOCK=${dockerSock}`, `DATAGROK_VERSION=${args.version || 'latest'}`, `DG_VERSION=${args.version || 'latest'}`, `GROK_CONNECT_VERSION=${args['grok-connect-version'] || 'latest'}`, `GROK_SPAWNER_VERSION=${args['grok-spawner-version'] || 'latest'}`, `JKG_VERSION=${args['jkg-version'] || 'latest'}`, `TOOLS_DEV_VERSION=${args['tools-dev-version'] || 'latest'}`];
339
+ const envLines = [`WORKTREE_PATH=${toDockerPath(worktreeRoot)}`, `DG_PORT=${dgPort}`, `TASK_KEY=${taskKey.toLowerCase()}`, `DOCKER_SOCK=${dockerSock}`, `DATAGROK_VERSION=${args.version || 'latest'}`, `DG_VERSION=${args.version || 'latest'}`, `GROK_CONNECT_VERSION=${args['grok-connect-version'] || 'latest'}`, `GROK_SPAWNER_VERSION=${args['grok-spawner-version'] || 'latest'}`, `JKG_VERSION=${args['jkg-version'] || 'latest'}`, `TOOLS_DEV_VERSION=${args['tools-dev-version'] || 'latest'}`, `FOLDER_NAME=${_path.default.basename(worktreeRoot)}`];
336
340
  for (const env of ['ANTHROPIC_API_KEY', 'DG_PUBLIC_BRANCH', 'JIRA_URL', 'JIRA_USERNAME', 'JIRA_TOKEN', 'GITHUB_TOKEN']) if (process.env[env]) envLines.push(`${env}=${process.env[env]}`);
337
341
  _fs.default.writeFileSync(_path.default.join(projectDir, '.env'), envLines.join('\n') + '\n');
338
342
 
@@ -540,61 +544,39 @@ async function claude(args) {
540
544
  return false;
541
545
  }
542
546
 
543
- // Wait for Datagrok to be healthy
544
- const healthy = await waitForDatagrok(dgPort);
545
- if (!healthy) color.warn('Datagrok may not be ready. Proceeding anyway...');
546
-
547
- // Determine whether this is a public repo and compute Claude working directory
548
- const isPublic = repoRoot ? isPublicRepo(repoRoot) : false;
549
- const claudeWorkDir = isPublic ? '/workspace/repo/public' : `/workspace/public/packages/${taskKey}`;
550
-
551
- // Fix ownership on bind-mounted directories (host UID may differ from container node user)
547
+ // Determine Claude working directory based on repo type
548
+ // Only trust public-repo markers when we are inside a real git repo — packages can have
549
+ // public/js-api via node_modules symlinks, which would cause a false positive.
552
550
  const containerName = `dg-pkg-${taskKey.toLowerCase()}-tools-dev-1`;
553
- (0, _child_process.spawnSync)('docker', ['exec', '-u', 'root', containerName, 'bash', '-c', 'chown node:node /workspace 2>/dev/null; ' + 'for d in /home/node/.claude /home/node/.npm; do ' + 'mkdir -p "$d" 2>/dev/null; chown -R node:node "$d" 2>/dev/null; done; ' + '[ -f /home/node/.claude.json ] && chown node:node /home/node/.claude.json 2>/dev/null; true'], {
554
- stdio: 'inherit'
555
- });
556
-
557
- // Copy host grok config into container and ensure 'local' server exists
558
- (0, _child_process.spawnSync)('docker', ['exec', '-u', 'root', containerName, 'bash', '-c', 'mkdir -p /home/node/.grok && chown node:node /home/node/.grok'], {
559
- stdio: 'inherit'
560
- });
561
- const grokConfigPath = _path.default.join(_os.default.homedir(), '.grok', 'config.yaml');
562
- if (_fs.default.existsSync(grokConfigPath)) {
563
- (0, _child_process.spawnSync)('docker', ['cp', grokConfigPath, `${containerName}:/home/node/.grok/config.yaml`], {
564
- stdio: 'inherit'
565
- });
566
- (0, _child_process.spawnSync)('docker', ['exec', '-u', 'root', containerName, 'chown', 'node:node', '/home/node/.grok/config.yaml'], {
567
- stdio: 'inherit'
551
+ let claudeWorkDir;
552
+ 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 {
553
+ // External repo: entrypoint clones public repo and creates symlink — wait for it
554
+ const folderName = _path.default.basename(worktreeRoot);
555
+ claudeWorkDir = `/workspace/datagrok/packages/${folderName}`;
556
+ color.info(`Waiting for workspace at ${claudeWorkDir} (streaming container logs)...`);
557
+
558
+ // Stream container logs so the user can see clone progress (don't inherit stdin)
559
+ const logsProc = (0, _child_process.spawn)('docker', ['logs', '-f', containerName], {
560
+ stdio: ['ignore', 'inherit', 'inherit']
568
561
  });
569
- color.info('Copied grok config into container');
570
- }
571
-
572
- // Add/ensure 'local' server pointing to compose datagrok (key=admin matches GROK_PARAMETERS)
573
- (0, _child_process.spawnSync)('docker', ['exec', containerName, 'node', '-e', `
574
- const fs = require('fs');
575
- const p = '/home/node/.grok/config.yaml';
576
- let t = '';
577
- try { t = fs.readFileSync(p, 'utf8'); } catch {}
578
- if (!t.trim()) {
579
- t = 'default: local\\nservers:\\n local:\\n url: http://datagrok:8080/api\\n key: admin\\n';
580
- } else if (!t.includes('datagrok:8080')) {
581
- t = t.replace(/^(servers:)/m, '\\$1\\n local:\\n url: http://datagrok:8080/api\\n key: admin');
582
- t = t.replace(/^default:.*/m, 'default: local');
583
- } else {
584
- t = t.replace(/^default:.*/m, 'default: local');
562
+ let ready = false;
563
+ const waitStart = Date.now();
564
+ for (let i = 0; i < 120; i++) {
565
+ const check = (0, _child_process.spawnSync)('docker', ['exec', containerName, 'test', '-d', claudeWorkDir]);
566
+ if (check.status === 0) {
567
+ ready = true;
568
+ break;
569
+ }
570
+ if (i > 0 && i % 12 === 0) color.info(`Still waiting for workspace... (${Math.round((Date.now() - waitStart) / 1000)}s elapsed)`);
571
+ await new Promise(r => setTimeout(r, 5000));
585
572
  }
586
- fs.writeFileSync(p, t);
587
- `], {
588
- stdio: 'inherit'
589
- });
590
573
 
591
- // Set up workspace context: for non-public repos, bind-mount workspace into public/packages/
592
- // so that process.cwd() returns the mount-point path (not the resolved symlink target)
593
- // and relative paths like ../../js-api resolve correctly inside the public repo tree.
594
- if (!isPublic) {
595
- (0, _child_process.spawnSync)('docker', ['exec', '-u', 'root', containerName, 'bash', '-c', 'for i in $(seq 1 60); do [ -d /workspace/public/js-api ] && break; sleep 3; done; ' + 'if [ -d /workspace/public/js-api ]; then ' + ' mkdir -p /workspace/public/packages 2>/dev/null; ' + ` rm -f /workspace/public/packages/${taskKey} 2>/dev/null; ` + ` if ! mountpoint -q /workspace/public/packages/${taskKey} 2>/dev/null; then ` + ` mkdir -p /workspace/public/packages/${taskKey} && ` + ` mount --bind /workspace/repo /workspace/public/packages/${taskKey}; ` + ' fi; ' + ` echo "[grok claude] Workspace at /workspace/public/packages/${taskKey}"; ` + 'else ' + ' echo "[grok claude] Warning: public repo clone not ready"; ' + 'fi'], {
596
- stdio: 'inherit'
597
- });
574
+ // Stop streaming logs
575
+ logsProc.kill('SIGKILL');
576
+ if (!ready) {
577
+ color.warn(`${claudeWorkDir} not available after 600s — falling back to /workspace/repo`);
578
+ claudeWorkDir = '/workspace/repo';
579
+ }
598
580
  }
599
581
  const claudeArgs = ['--dangerously-skip-permissions'];
600
582
  if (args.prompt) claudeArgs.push('-p', args.prompt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
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": {