poke-gate 0.1.4 → 0.1.5

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.
@@ -25,6 +25,8 @@ class AgentsViewModel: ObservableObject {
25
25
  @Published var selectedAgent: AgentFile?
26
26
  @Published var editorContent: String = ""
27
27
  @Published var showingEnv: Bool = false
28
+ @Published var isRunning: Bool = false
29
+ @Published var lastRunOutput: String = ""
28
30
 
29
31
  private var fileWatcher: DispatchSourceFileSystemObject?
30
32
  private var dirFD: Int32 = -1
@@ -108,15 +110,20 @@ class AgentsViewModel: ObservableObject {
108
110
  }
109
111
 
110
112
  func select(_ agent: AgentFile) {
111
- selectedAgent = agent
112
- showingEnv = false
113
- loadContent()
113
+ DispatchQueue.main.async {
114
+ self.selectedAgent = agent
115
+ self.showingEnv = false
116
+ self.loadContent()
117
+ }
114
118
  }
115
119
 
116
120
  func loadContent() {
117
121
  guard let agent = selectedAgent else { return }
118
122
  let url = showingEnv ? agent.envPath : agent.path
119
- editorContent = (try? String(contentsOf: url, encoding: .utf8)) ?? (showingEnv ? "# No .env file yet\n" : "")
123
+ let content = (try? String(contentsOf: url, encoding: .utf8)) ?? (showingEnv ? "# No .env file yet\n" : "")
124
+ DispatchQueue.main.async {
125
+ self.editorContent = content
126
+ }
120
127
  }
121
128
 
122
129
  func save() {
@@ -169,6 +176,45 @@ class AgentsViewModel: ObservableObject {
169
176
  }
170
177
  }
171
178
 
179
+ func runAgent(_ agent: AgentFile) {
180
+ guard !isRunning else { return }
181
+ isRunning = true
182
+ lastRunOutput = ""
183
+
184
+ let fullPath = GateService().shellPath()
185
+ let proc = Process()
186
+ let pipe = Pipe()
187
+
188
+ proc.executableURL = URL(fileURLWithPath: "/bin/zsh")
189
+ proc.arguments = ["-c", "npx -y poke-gate run-agent \(agent.agentId)"]
190
+ proc.environment = ["HOME": NSHomeDirectory(), "PATH": fullPath]
191
+ proc.standardOutput = pipe
192
+ proc.standardError = pipe
193
+ proc.currentDirectoryURL = FileManager.default.homeDirectoryForCurrentUser
194
+
195
+ let handle = pipe.fileHandleForReading
196
+ handle.readabilityHandler = { [weak self] fh in
197
+ let data = fh.availableData
198
+ guard !data.isEmpty, let text = String(data: data, encoding: .utf8) else { return }
199
+ DispatchQueue.main.async {
200
+ self?.lastRunOutput += text
201
+ }
202
+ }
203
+
204
+ proc.terminationHandler = { [weak self] _ in
205
+ DispatchQueue.main.async {
206
+ self?.isRunning = false
207
+ }
208
+ }
209
+
210
+ do {
211
+ try proc.run()
212
+ } catch {
213
+ isRunning = false
214
+ lastRunOutput = "Failed to run: \(error.localizedDescription)"
215
+ }
216
+ }
217
+
172
218
  func deleteAgent(_ agent: AgentFile) {
173
219
  try? FileManager.default.removeItem(at: agent.path)
174
220
  if agent.hasEnv {
@@ -301,6 +347,15 @@ struct AgentDetailView: View {
301
347
  }
302
348
  }
303
349
  }
350
+
351
+ Button {
352
+ viewModel.runAgent(agent)
353
+ } label: {
354
+ Label(viewModel.isRunning ? "Running…" : "Run", systemImage: "play.fill")
355
+ .font(.caption)
356
+ }
357
+ .disabled(viewModel.isRunning)
358
+ .padding(.leading, 4)
304
359
  }
305
360
  .padding(.horizontal, 12)
306
361
  .padding(.vertical, 8)
@@ -32,12 +32,13 @@ class GateService: ObservableObject {
32
32
 
33
33
  func runPokeLogin() {
34
34
  let fullPath = shellPath()
35
+ let npxBin = findNpx()
35
36
  let proc = Process()
36
37
  proc.executableURL = URL(fileURLWithPath: "/bin/zsh")
37
- proc.arguments = ["-c", "npx -y poke@latest login"]
38
+ proc.arguments = ["-c", "\(npxBin) -y poke@latest login"]
38
39
  proc.environment = ["HOME": NSHomeDirectory(), "PATH": fullPath]
39
40
  try? proc.run()
40
- appendLog("Launched poke login — check your browser.")
41
+ appendLog("Launched poke login (npx: \(npxBin)) — check your browser.")
41
42
  }
42
43
 
43
44
  func autoStartIfNeeded() {
@@ -90,19 +91,82 @@ class GateService: ObservableObject {
90
91
  }
91
92
  }
92
93
 
93
- private func shellPath() -> String {
94
- let loginShell = ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
95
- let pathProc = Process()
96
- let pathPipe = Pipe()
97
- pathProc.executableURL = URL(fileURLWithPath: loginShell)
98
- pathProc.arguments = ["-ilc", "echo $PATH"]
99
- pathProc.standardOutput = pathPipe
100
- pathProc.standardError = FileHandle.nullDevice
101
- pathProc.environment = ["HOME": NSHomeDirectory()]
102
- try? pathProc.run()
103
- pathProc.waitUntilExit()
104
- let data = pathPipe.fileHandleForReading.readDataToEndOfFile()
105
- return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
94
+ func shellPath() -> String {
95
+ let home = NSHomeDirectory()
96
+ let fallback = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin"
97
+
98
+ // Try multiple shells/strategies to get PATH
99
+ let strategies: [(String, [String])] = [
100
+ ("/bin/zsh", ["-ilc", "echo $PATH"]),
101
+ ("/bin/zsh", ["-lc", "echo $PATH"]),
102
+ ("/bin/bash", ["-lc", "echo $PATH"]),
103
+ ]
104
+
105
+ for (shell, args) in strategies {
106
+ let proc = Process()
107
+ let pipe = Pipe()
108
+ proc.executableURL = URL(fileURLWithPath: shell)
109
+ proc.arguments = args
110
+ proc.standardOutput = pipe
111
+ proc.standardError = FileHandle.nullDevice
112
+ proc.environment = ["HOME": home]
113
+ do {
114
+ try proc.run()
115
+ proc.waitUntilExit()
116
+ if proc.terminationStatus == 0 {
117
+ let data = pipe.fileHandleForReading.readDataToEndOfFile()
118
+ if let path = String(data: data, encoding: .utf8)?
119
+ .trimmingCharacters(in: .whitespacesAndNewlines),
120
+ !path.isEmpty {
121
+ return path
122
+ }
123
+ }
124
+ } catch {
125
+ continue
126
+ }
127
+ }
128
+
129
+ // Fallback: build PATH from common locations
130
+ var paths = fallback.split(separator: ":").map(String.init)
131
+
132
+ let commonDirs = [
133
+ "\(home)/.nvm/versions/node",
134
+ "\(home)/.volta/bin",
135
+ "\(home)/.fnm/aliases/default/bin",
136
+ "\(home)/.local/bin",
137
+ "\(home)/.cargo/bin",
138
+ "/opt/homebrew/bin",
139
+ "/usr/local/bin",
140
+ ]
141
+
142
+ for dir in commonDirs {
143
+ if FileManager.default.fileExists(atPath: dir) {
144
+ if dir.contains(".nvm") {
145
+ // Find the latest node version in nvm
146
+ if let versions = try? FileManager.default.contentsOfDirectory(atPath: dir) {
147
+ if let latest = versions.sorted().last {
148
+ let binPath = "\(dir)/\(latest)/bin"
149
+ if !paths.contains(binPath) { paths.insert(binPath, at: 0) }
150
+ }
151
+ }
152
+ } else if !paths.contains(dir) {
153
+ paths.insert(dir, at: 0)
154
+ }
155
+ }
156
+ }
157
+
158
+ return paths.joined(separator: ":")
159
+ }
160
+
161
+ private func findNpx() -> String {
162
+ let path = shellPath()
163
+ for dir in path.split(separator: ":") {
164
+ let npxPath = "\(dir)/npx"
165
+ if FileManager.default.isExecutableFile(atPath: npxPath) {
166
+ return npxPath
167
+ }
168
+ }
169
+ return "npx"
106
170
  }
107
171
 
108
172
  private func launchProcess() {
@@ -112,15 +176,17 @@ class GateService: ObservableObject {
112
176
  appendLog("Starting poke-gate…")
113
177
 
114
178
  let fullPath = shellPath()
179
+ let npxBin = findNpx()
180
+
181
+ appendLog("Using npx at: \(npxBin)")
115
182
 
116
183
  let proc = Process()
117
184
  let pipe = Pipe()
118
185
 
119
186
  proc.executableURL = URL(fileURLWithPath: "/bin/zsh")
120
- proc.arguments = ["-c", "npx -y poke-gate --verbose"]
187
+ proc.arguments = ["-c", "\(npxBin) -y poke-gate --verbose"]
121
188
  proc.environment = ProcessInfo.processInfo.environment.merging(
122
189
  [
123
- "POKE_API_KEY": resolveToken() ?? "",
124
190
  "PATH": fullPath,
125
191
  ],
126
192
  uniquingKeysWith: { _, new in new }
@@ -259,12 +259,15 @@
259
259
  ENABLE_PREVIEWS = YES;
260
260
  GENERATE_INFOPLIST_FILE = YES;
261
261
  INFOPLIST_FILE = "Poke macOS Gate/Info.plist";
262
+ INFOPLIST_KEY_CFBundleDisplayName = "Poke macOS Gate";
263
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
262
264
  INFOPLIST_KEY_NSHumanReadableCopyright = "";
263
265
  LD_RUNPATH_SEARCH_PATHS = (
264
266
  "$(inherited)",
265
267
  "@executable_path/../Frameworks",
266
268
  );
267
- MARKETING_VERSION = 0.1.3;
269
+ MACOSX_DEPLOYMENT_TARGET = 26.0;
270
+ MARKETING_VERSION = 0.1.5;
268
271
  PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
269
272
  PRODUCT_NAME = "$(TARGET_NAME)";
270
273
  REGISTER_APP_GROUPS = YES;
@@ -291,12 +294,15 @@
291
294
  ENABLE_PREVIEWS = YES;
292
295
  GENERATE_INFOPLIST_FILE = YES;
293
296
  INFOPLIST_FILE = "Poke macOS Gate/Info.plist";
297
+ INFOPLIST_KEY_CFBundleDisplayName = "Poke macOS Gate";
298
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
294
299
  INFOPLIST_KEY_NSHumanReadableCopyright = "";
295
300
  LD_RUNPATH_SEARCH_PATHS = (
296
301
  "$(inherited)",
297
302
  "@executable_path/../Frameworks",
298
303
  );
299
- MARKETING_VERSION = 0.1.3;
304
+ MACOSX_DEPLOYMENT_TARGET = 26.0;
305
+ MARKETING_VERSION = 0.1.5;
300
306
  PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
301
307
  PRODUCT_NAME = "$(TARGET_NAME)";
302
308
  REGISTER_APP_GROUPS = YES;
package/docs/index.md CHANGED
@@ -3,8 +3,8 @@ layout: home
3
3
 
4
4
  hero:
5
5
  name: Poke Gate
6
- text: Your Mac, controlled by AI from anywhere.
7
- tagline: Give your Poke AI assistant access to your machine. Run commands, read files, take screenshots — all from iMessage, Telegram, or SMS.
6
+ text: A two-way bridge between your Mac and your AI.
7
+ tagline: Poke pulls from your Mac when you ask. Your Mac pushes to Poke when something happens. Run commands, read files, take screenshots — and automate it all with Agents.
8
8
  image:
9
9
  src: /logo.png
10
10
  alt: Poke Gate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poke-gate",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Expose your machine to your Poke AI assistant via MCP tunnel",
5
5
  "type": "module",
6
6
  "bin": {