@cyanautomation/kaseki-agent 1.31.0 → 1.32.0

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 CHANGED
@@ -1373,7 +1373,7 @@ to opt out of GitHub publishing for a specific API run.
1373
1373
  | Variable | Default | Notes |
1374
1374
  |---|---|---|
1375
1375
  | `OPENROUTER_API_KEY` | — | Required (or use file) |
1376
- | `OPENROUTER_API_KEY_FILE` | `/run/secrets/openrouter_api_key` | Preferred; mounted read-only |
1376
+ | `OPENROUTER_API_KEY_FILE` | `~/.kaseki/secrets.json` | Preferred; set by setup wizard |
1377
1377
  | `REPO_URL` | CyanAutomation/crudmapper | Target repository |
1378
1378
  | `GIT_REF` | main | Branch/tag/commit |
1379
1379
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanautomation/kaseki-agent",
3
- "version": "1.31.0",
3
+ "version": "1.32.0",
4
4
  "description": "Admin/helper/doctor toolbox and local API client for Kaseki diagnostics, setup, and API-backed coding-agent task workflows",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -303,6 +303,101 @@ is_path_on_readonly_mount() {
303
303
  return 1 # Not read-only
304
304
  }
305
305
 
306
+ # Check if a file is readable by UID 10000 OR GID 10000 (group-based access)
307
+ # This is important for read-only mounts where we can't change permissions
308
+ is_file_readable_by_uid_or_gid() {
309
+ local file_path="$1"
310
+ local uid="${2:-$CONTAINER_UID}"
311
+ local gid="${3:-$CONTAINER_GID}"
312
+
313
+ if [ ! -f "$file_path" ]; then
314
+ return 1
315
+ fi
316
+
317
+ # Test direct readability
318
+ if [ -r "$file_path" ]; then
319
+ return 0
320
+ fi
321
+
322
+ # Check if file is readable by our group
323
+ local mode
324
+ mode=$(get_current_permissions "$file_path")
325
+
326
+ if [ "$mode" = "unknown" ] || [ "$mode" = "nonexistent" ]; then
327
+ return 1
328
+ fi
329
+
330
+ # Extract group read permission (second digit in octal mode)
331
+ # For mode like 0640: group digit is 4 (read permission)
332
+ local group_digit
333
+ group_digit=$(echo "$mode" | cut -c2)
334
+
335
+ # Check if group has read permission (4 = read)
336
+ if [ "$group_digit" -ge 4 ] && [ "$group_digit" -le 7 ]; then
337
+ # Group has read permission - verify our GID matches
338
+ local file_gid
339
+ if command -v stat &>/dev/null; then
340
+ file_gid=$(stat -c "%g" "$file_path" 2>/dev/null || echo "unknown")
341
+ else
342
+ file_gid=$(ls -ld "$file_path" 2>/dev/null | awk '{print $4}' || echo "unknown")
343
+ fi
344
+
345
+ # If file GID matches our GID, we can read it via group membership
346
+ if [ "$file_gid" = "$gid" ]; then
347
+ return 0
348
+ fi
349
+ fi
350
+
351
+ return 1
352
+ }
353
+
354
+ # Check if a directory is traversable by UID or GID (for group-based access)
355
+ is_directory_traversable_by_uid_or_gid() {
356
+ local dir_path="$1"
357
+ local uid="${2:-$CONTAINER_UID}"
358
+ local gid="${3:-$CONTAINER_GID}"
359
+
360
+ if [ ! -d "$dir_path" ]; then
361
+ return 1
362
+ fi
363
+
364
+ # Test direct traversability
365
+ if [ -x "$dir_path" ]; then
366
+ return 0
367
+ fi
368
+
369
+ # Check if directory is traversable by our group
370
+ local mode
371
+ mode=$(get_current_permissions "$dir_path")
372
+
373
+ if [ "$mode" = "unknown" ] || [ "$mode" = "nonexistent" ]; then
374
+ return 1
375
+ fi
376
+
377
+ # Extract group execute permission (second digit in octal mode)
378
+ # For mode like 0750: group digit is 5 (read+execute permission)
379
+ local group_digit
380
+ group_digit=$(echo "$mode" | cut -c2)
381
+
382
+ # Check if group has execute permission (1, 3, 5, 7)
383
+ # The last bit (1) means execute, so odd numbers have execute
384
+ if [ "$((group_digit % 2))" -eq 1 ]; then
385
+ # Group has execute permission - verify our GID matches
386
+ local dir_gid
387
+ if command -v stat &>/dev/null; then
388
+ dir_gid=$(stat -c "%g" "$dir_path" 2>/dev/null || echo "unknown")
389
+ else
390
+ dir_gid=$(ls -ld "$dir_path" 2>/dev/null | awk '{print $4}' || echo "unknown")
391
+ fi
392
+
393
+ if [ "$dir_gid" = "$gid" ]; then
394
+ return 0
395
+ fi
396
+ fi
397
+
398
+ return 1
399
+ }
400
+
306
401
  get_current_permissions() {
307
402
  local path="$1"
308
403
 
@@ -420,9 +515,28 @@ check_directory_traversable() {
420
515
  return 2
421
516
  fi
422
517
  else
423
- log_error "Cannot auto-fix $dir_name (possibly on read-only mount)"
424
- log_error "Fix on host: sudo chmod 0750 $dir_path"
425
- return 2
518
+ # Check if this is a read-only mount
519
+ if is_path_on_readonly_mount "$dir_path"; then
520
+ # Check if directory is traversable by group
521
+ if is_directory_traversable_by_uid_or_gid "$dir_path"; then
522
+ log_warn "$dir_name is on a read-only mount but is traversable by GID $CONTAINER_GID"
523
+ log_warn "Note: Some operations may fail if they require write access"
524
+ return 3 # Warning: accessible but on read-only mount
525
+ fi
526
+
527
+ # Not traversable even by group
528
+ log_error "$dir_name is on a read-only mount and not traversable"
529
+ log_error ""
530
+ log_error "To fix on HOST (before docker-compose up or container start):"
531
+ log_error " sudo chmod 0750 $dir_path"
532
+ log_error ""
533
+ return 2
534
+ else
535
+ # Writable mount but still can't fix permissions
536
+ log_error "Cannot auto-fix $dir_name permissions"
537
+ log_error " sudo chmod 0750 $dir_path"
538
+ return 2
539
+ fi
426
540
  fi
427
541
  fi
428
542
 
@@ -450,11 +564,40 @@ check_file_readable() {
450
564
  return 2
451
565
  fi
452
566
  else
453
- log_error "Cannot auto-fix $file_name (possibly on read-only mount)"
454
- local dir_path
455
- dir_path=$(dirname "$file_path")
456
- log_error "Fix on host: sudo chmod 0640 $file_path"
457
- return 2
567
+ # Check if this is a read-only mount
568
+ if is_path_on_readonly_mount "$file_path"; then
569
+ # Check if file is readable by group
570
+ if is_file_readable_by_uid_or_gid "$file_path"; then
571
+ log_warn "$file_name is on a read-only mount but is readable by GID $CONTAINER_GID"
572
+ log_warn "Note: Using group-based access from UID $CONTAINER_UID"
573
+ return 3 # Warning: accessible but on read-only mount
574
+ fi
575
+
576
+ # Not readable even by group - must fix on host
577
+ log_error "$file_name is on a read-only mount and not readable"
578
+ log_error ""
579
+ log_error "To fix on HOST (before docker-compose up or container start):"
580
+ local dir_path
581
+ dir_path=$(dirname "$file_path")
582
+ # Get current mode for helpful feedback
583
+ local current_mode
584
+ current_mode=$(get_current_permissions "$file_path")
585
+ log_error " Current mode: $current_mode"
586
+ log_error " Fix: sudo chmod 0640 $file_path"
587
+ log_error " Or: sudo chmod 0644 $file_path (world-readable)"
588
+ log_error ""
589
+ log_error "Make sure parent directory is traversable:"
590
+ log_error " sudo chmod 0750 $dir_path"
591
+ log_error ""
592
+ return 2
593
+ else
594
+ # Writable mount but still can't fix permissions
595
+ log_error "Cannot auto-fix $file_name permissions"
596
+ local dir_path
597
+ dir_path=$(dirname "$file_path")
598
+ log_error " sudo chmod 0640 $file_path"
599
+ return 2
600
+ fi
458
601
  fi
459
602
  fi
460
603
 
@@ -518,6 +661,12 @@ check_api_key() {
518
661
  if [ -n "$api_key_file" ] && [ -f "$api_key_file" ]; then
519
662
  if [ -r "$api_key_file" ]; then
520
663
  log_pass "OPENROUTER_API_KEY_FILE is readable ($api_key_file)"
664
+ # Warn if using legacy Docker secrets path
665
+ if [[ "$api_key_file" == "/run/secrets/"* ]]; then
666
+ log_warn "Using legacy Docker secrets path: $api_key_file"
667
+ log_info " Migrate to ~/.kaseki/secrets.json for better portability"
668
+ log_info " See docs/TROUBLESHOOTING.md#migrating-from-legacy-docker-secrets"
669
+ fi
521
670
  return 0
522
671
  else
523
672
  log_error "OPENROUTER_API_KEY_FILE exists but is not readable ($api_key_file)"