yatfa 1.0.82 → 1.0.83

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.
@@ -42,17 +42,38 @@ def check_version!
42
42
  remote = latest_npm_version
43
43
  return if remote.nil? || local == remote
44
44
 
45
- # Try to self-delete the outdated install so next run fetches fresh
45
+ # Guard against infinite re-exec loop
46
+ if ENV["YATFA_SELF_UPDATE"] == "1"
47
+ puts "⚠️ Still outdated after self-update: v#{local} (latest: v#{remote})"
48
+ puts " Run manually: npx yatfa@latest #{ARGV.join(' ')}"
49
+ return # continue anyway rather than blocking
50
+ end
51
+
52
+ puts "🔄 Auto-updating yatfa: v#{local} → v#{remote}..."
53
+
54
+ # Clean up ALL stale installs so npx fetches fresh from registry
46
55
  begin
47
56
  real_path = File.realpath(__FILE__)
48
57
  install_dir = File.expand_path("../..", real_path) # bin/../.. → package root
49
- FileUtils.rm_rf(install_dir)
58
+ FileUtils.rm_rf(install_dir) if install_dir.include?("node_modules/yatfa")
59
+
60
+ # Also clear npx cache (~/.npm/_npx/<hash>/node_modules/yatfa)
61
+ npx_cache = File.join(Dir.home, ".npm", "_npx")
62
+ if Dir.exist?(npx_cache)
63
+ Dir.glob(File.join(npx_cache, "*", "node_modules", "yatfa")).each do |cached|
64
+ FileUtils.rm_rf(cached)
65
+ end
66
+ end
50
67
  rescue StandardError
51
- # No permission or other issue just warn
68
+ # Best-effort cleanup fall through to re-exec regardless
52
69
  end
53
70
 
54
- puts "⚠️ Outdated version: v#{local} (latest: v#{remote})"
55
- puts " Re-run: npx yatfa@latest #{ARGV.join(' ')}"
71
+ # Re-exec with @latest to force a fresh fetch from the registry
72
+ ENV["YATFA_SELF_UPDATE"] = "1"
73
+ exec("npx", "yatfa@latest", *ARGV)
74
+ rescue StandardError => e
75
+ puts "⚠️ Auto-update failed: #{e.message}"
76
+ puts " Run manually: npx yatfa@latest #{ARGV.join(' ')}"
56
77
  exit 1
57
78
  end
58
79
 
@@ -308,6 +308,10 @@ module YatfaAgent
308
308
  docker_cmd += ["--label", "yatfa.version=#{remote_version}"]
309
309
  end
310
310
 
311
+ # Store project root as a label so the updater daemon can find
312
+ # Dockerfile.sandbox and yatfa.env.rb without reverse-engineering from mounts.
313
+ docker_cmd += ["--label", "yatfa.project-root=#{Dir.pwd}"]
314
+
311
315
  # Inject custom environment variables from config
312
316
  # Merge global env with agent-specific env (agent-specific takes precedence)
313
317
  merged_env = {}
@@ -188,13 +188,23 @@ module YatfaAgent
188
188
  # @param config [Hash] container inspect data
189
189
  # @return [String, nil] project root path
190
190
  def self.find_project_root_for_container(config)
191
- # Try to find project root from mounted setup directory
191
+ # Best: use the label stored at container creation time (works for all users)
192
+ label = config.dig("Config", "Labels", "yatfa.project-root")
193
+ if label && Dir.exist?(label)
194
+ return label
195
+ end
196
+
197
+ # Fallback: walk up from /tmp/setup mount source (dev-only, local clone of yatfa-public)
192
198
  mounts = config.dig("Mounts") || []
193
199
  mounts.each do |mount|
194
200
  next unless mount["Destination"] == "/tmp/setup"
195
201
  source = mount["Source"]
196
- # The source is .../setup, project root is its parent
197
- return File.expand_path("..", source) if source && Dir.exist?(File.expand_path("..", source))
202
+ next unless source
203
+ dir = File.expand_path("..", source)
204
+ while dir != "/"
205
+ return dir if File.exist?(File.join(dir, "yatfa.env.rb"))
206
+ dir = File.dirname(dir)
207
+ end
198
208
  end
199
209
 
200
210
  # Try REPOSITORIES_CONFIG env var to find workspace path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yatfa",
3
- "version": "1.0.82",
3
+ "version": "1.0.83",
4
4
  "description": "YATFA - Yet Another Tool For Agents",
5
5
  "bin": "./bin/run-yatfa-agent.rb",
6
6
  "files": [