tinker-agent 1.0.25 → 1.0.28

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
@@ -16,10 +16,55 @@ ENTRYPOINT ["/entrypoint.sh"]
16
16
  CMD ["bash", "-c", "curl -fsSL https://raw.githubusercontent.com/RoM4iK/tinker-public/${TINKER_VERSION:-main}/setup-agent.rb | ruby"]
17
17
  ```
18
18
 
19
- ## Usage
20
-
21
- ```bash
22
- npx tinker-agent worker
19
+ ## Configuration (tinker.env.rb)
20
+
21
+ Agents are configured via a `tinker.env.rb` file in your project root. This Ruby file allows you to define configuration and secrets (using heredocs).
22
+
23
+ **Do not commit `tinker.env.rb` to git!** Add it to your `.gitignore`.
24
+
25
+ Example `tinker.env.rb`:
26
+
27
+ ```ruby
28
+ {
29
+ project_id: 2,
30
+ rails_ws_url: "wss://tinkerai.win/cable",
31
+ rails_api_url: "https://tinker.tinkerai.win/api/v1",
32
+
33
+ # Git Identity
34
+ git: {
35
+ user_name: "Tinker Agent",
36
+ user_email: "agent@example.com"
37
+ },
38
+
39
+ # GitHub Auth (App or Token)
40
+ github: {
41
+ method: "app",
42
+ app_client_id: "Iv23liFDGt4FWGJSHAS",
43
+ app_installation_id: "102387777",
44
+ app_private_key_path: "/absolute/path/to/key.pem"
45
+ },
46
+
47
+ # Agent Specific Config
48
+ agents: {
49
+ worker: {
50
+ mcp_api_key: "...",
51
+ container_name: "tinker-worker"
52
+ },
53
+ planner: {
54
+ mcp_api_key: "...",
55
+ container_name: "tinker-planner"
56
+ }
57
+ },
58
+
59
+ # Environment Variables Injection
60
+ # Simple strings or Heredocs supported
61
+ dot_env: <<~ENV
62
+ PORT=3200
63
+ DB_HOST=localhost
64
+ SECRET_KEY_BASE=very_secret
65
+ OPENAI_API_KEY=sk-...
66
+ ENV
67
+ }
23
68
  ```
24
69
 
25
70
  ## Environment Variables
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinker-agent",
3
- "version": "1.0.25",
3
+ "version": "1.0.28",
4
4
  "description": "Tinker Agent Runner",
5
5
  "bin": {
6
6
  "tinker-agent": "./run-tinker-agent.rb"
@@ -9,7 +9,7 @@
9
9
  # - Docker
10
10
  # - Ruby
11
11
  # - Dockerfile.sandbox in project root
12
- # - tinker.env.json in project root (gitignored)
12
+ # - tinker.env.rb in project root (gitignored)
13
13
 
14
14
  require "json"
15
15
 
@@ -21,19 +21,55 @@ IMAGE_NAME = "tinker-sandbox"
21
21
  AGENT_TYPES = AGENT_CONFIGS.keys.freeze
22
22
 
23
23
  def load_config
24
- config_file = File.join(Dir.pwd, "tinker.env.json")
25
-
26
- unless File.exist?(config_file)
27
- puts "❌ Error: tinker.env.json not found in current directory"
24
+ # Support Ruby config for heredocs and comments (tinker.env.rb)
25
+ rb_config_file = File.join(Dir.pwd, "tinker.env.rb")
26
+
27
+ unless File.exist?(rb_config_file)
28
+ puts "❌ Error: tinker.env.rb not found in current directory"
28
29
  puts ""
29
- puts "Create it:"
30
- puts " curl -fsSL https://raw.githubusercontent.com/RoM4iK/tinker-public/main/tinker.env.example.json -o tinker.env.json"
31
- puts " # Edit with your project config"
32
- puts " echo 'tinker.env.json' >> .gitignore"
30
+ puts "Create tinker.env.rb:"
31
+ puts " {"
32
+ puts " project_id: 1,"
33
+ puts " rails_ws_url: '...',"
34
+ puts " # ..."
35
+ puts " # Paste your stripped .env content here:"
36
+ puts " dot_env: <<~ENV"
37
+ puts " STRIPE_KEY=sk_test_..."
38
+ puts " OPENAI_KEY=sk-..."
39
+ puts " ENV"
40
+ puts " }"
41
+ puts " echo 'tinker.env.rb' >> .gitignore"
33
42
  exit 1
34
43
  end
35
44
 
36
- JSON.parse(File.read(config_file))
45
+ puts "⚙️ Loading configuration from tinker.env.rb"
46
+ config = eval(File.read(rb_config_file), binding, rb_config_file)
47
+
48
+ # Convert symbols to strings for easier handling before JSON normalization
49
+ config = config.transform_keys(&:to_s)
50
+
51
+ # Parse dot_env heredoc if present
52
+ if (dotenv = config["dot_env"])
53
+ config["env"] ||= {}
54
+ # Ensure env is string-keyed
55
+ config["env"] = config["env"].transform_keys(&:to_s)
56
+
57
+ dotenv.each_line do |line|
58
+ line = line.strip
59
+ next if line.empty? || line.start_with?('#')
60
+ k, v = line.split('=', 2)
61
+ next unless k && v
62
+ # Remove surrounding quotes and trailing comments (simple)
63
+ v = v.strip.gsub(/^['"]|['"]$/, '')
64
+ config["env"][k.strip] = v
65
+ end
66
+
67
+ config.delete("dot_env")
68
+ puts "🌿 Parsed dot_env into #{config['env'].size} environment variables"
69
+ end
70
+
71
+ # Normalize symbols to strings for consistency via JSON round-trip
72
+ JSON.parse(JSON.generate(config))
37
73
  end
38
74
 
39
75
  def check_dockerfile!
@@ -97,6 +133,17 @@ def run_agent(agent_type, config)
97
133
  "docker", "run", "-d",
98
134
  "--name", container_name,
99
135
  "--network=host",
136
+ ]
137
+
138
+ # Inject custom environment variables from config
139
+ if (custom_env = config["env"])
140
+ custom_env.each do |k, v|
141
+ docker_cmd += ["-e", "#{k}=#{v}"]
142
+ end
143
+ puts "🌿 Injected #{custom_env.size} custom env vars from config"
144
+ end
145
+
146
+ docker_cmd += [
100
147
  # Mount Claude config
101
148
  "-v", "#{ENV['HOME']}/.claude.json:/tmp/cfg/claude.json:ro",
102
149
  "-v", "#{ENV['HOME']}/.claude:/tmp/cfg/claude_dir:ro",
@@ -117,7 +164,7 @@ def run_agent(agent_type, config)
117
164
 
118
165
  unless File.exist?(key_path) && !File.directory?(key_path)
119
166
  puts "❌ Error: GitHub App private key not found at: #{key_path}"
120
- puts " Please check 'app_private_key_path' in tinker.env.json"
167
+ puts " Please check 'app_private_key_path' in tinker.env.rb"
121
168
  exit 1
122
169
  end
123
170
 
@@ -133,7 +180,7 @@ def run_agent(agent_type, config)
133
180
  puts "🔑 Using GitHub token authentication"
134
181
  else
135
182
  puts "❌ Error: No GitHub authentication configured"
136
- puts " Please configure 'github' in tinker.env.json"
183
+ puts " Please configure 'github' in tinker.env.rb"
137
184
  exit 1
138
185
  end
139
186
 
@@ -147,7 +194,15 @@ def run_agent(agent_type, config)
147
194
  local_setup_script = File.join(File.dirname(__FILE__), "setup-agent.rb")
148
195
 
149
196
  # Check for local agent-bridge binaries (for development)
150
- local_bridge = File.join(Dir.pwd, "bin", "agent-bridge")
197
+ # Priority:
198
+ # 1. Linux binary matching host arch (for proper container execution)
199
+ # 2. Legacy bin/agent-bridge if it's a binary (not script)
200
+
201
+ arch = `uname -m`.strip
202
+ linux_arch = (arch == "x86_64") ? "amd64" : "arm64"
203
+ linux_bridge = File.join(Dir.pwd, "tinker-public", "bin", "agent-bridge-linux-#{linux_arch}")
204
+
205
+ local_bridge_default = File.join(Dir.pwd, "bin", "agent-bridge")
151
206
  local_tmux = File.join(File.dirname(__FILE__), "bin", "agent-bridge-tmux")
152
207
 
153
208
  mounts = []
@@ -156,9 +211,18 @@ def run_agent(agent_type, config)
156
211
  mounts += ["-v", "#{File.expand_path(local_setup_script)}:/tmp/setup-agent.rb:ro"]
157
212
  end
158
213
 
159
- if File.exist?(local_bridge)
160
- puts "🔧 Using local agent-bridge binary"
161
- mounts += ["-v", "#{local_bridge}:/tmp/agent-bridge:ro"]
214
+ if File.exist?(linux_bridge)
215
+ puts "🔧 Using local linux binary: #{linux_bridge}"
216
+ mounts += ["-v", "#{linux_bridge}:/tmp/agent-bridge:ro"]
217
+ elsif File.exist?(local_bridge_default)
218
+ # Check if it's a binary or script
219
+ is_script = File.read(local_bridge_default, 4) == "#!/b"
220
+ if is_script
221
+ puts "⚠️ bin/agent-bridge is a host wrapper script. Please run 'bin/build-bridge' to generate linux binaries."
222
+ else
223
+ puts "🔧 Using local agent-bridge binary"
224
+ mounts += ["-v", "#{local_bridge_default}:/tmp/agent-bridge:ro"]
225
+ end
162
226
  end
163
227
 
164
228
  if File.exist?(local_tmux)
@@ -193,15 +257,17 @@ def attach_to_agent(agent_type, config)
193
257
  exit 1
194
258
  end
195
259
 
260
+ agent_def = AGENT_CONFIGS[agent_type]
196
261
  agent_config = config.dig("agents", agent_type) || {}
197
- container_name = agent_config["container_name"] || "tinker-#{agent_type}"
262
+ container_name = agent_config["container_name"] || agent_def[:name]
198
263
 
199
264
  running = `docker ps --filter name=^#{container_name}$ --format '{{.Names}}'`.strip
200
265
 
201
266
  if running.empty?
202
- puts "#{agent_type} agent is not running"
203
- puts " Start with: npx tinker-agent #{agent_type}"
204
- exit 1
267
+ puts "⚠️ #{agent_type} agent is not running. Auto-starting..."
268
+ build_docker_image
269
+ run_agent(agent_type, config)
270
+ sleep 3
205
271
  end
206
272
 
207
273
  puts "📎 Attaching to #{agent_type} agent..."
@@ -224,6 +290,14 @@ def attach_to_agent(agent_type, config)
224
290
 
225
291
  puts " User: #{user}"
226
292
 
293
+ # Wait for tmux session to be ready
294
+ 10.times do
295
+ if system("docker", "exec", "-u", user, container_name, "tmux", "has-session", "-t", "agent", err: File::NULL, out: File::NULL)
296
+ break
297
+ end
298
+ sleep 1
299
+ end
300
+
227
301
  # Attach to agent session which has the status bar
228
302
  # Must run as agent user since tmux server runs under that user
229
303
  exec("docker", "exec", "-it", "-u", user, container_name, "tmux", "attach", "-t", "agent")
@@ -237,10 +311,9 @@ def show_usage
237
311
  puts ""
238
312
  puts "Setup:"
239
313
  puts " 1. Create Dockerfile.sandbox (see https://github.com/RoM4iK/tinker-public/blob/main/README.md)"
240
- puts " 2. curl -fsSL https://raw.githubusercontent.com/RoM4iK/tinker-public/main/tinker.env.example.json -o tinker.env.json"
241
- puts " 3. Edit tinker.env.json with your config"
242
- puts " 4. echo 'tinker.env.json' >> .gitignore"
243
- puts " 5. npx tinker-agent worker"
314
+ puts " 2. Create tinker.env.rb (see https://github.com/RoM4iK/tinker-public/blob/main/README.md)"
315
+ puts " 3. echo 'tinker.env.rb' >> .gitignore"
316
+ puts " 4. npx tinker-agent worker"
244
317
  exit 1
245
318
  end
246
319