poke-gate 0.1.8 → 0.2.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.
Files changed (30) hide show
  1. package/.github/workflows/release.yml +53 -3
  2. package/README.md +48 -14
  3. package/bin/poke-gate.js +17 -0
  4. package/clients/Poke macOS Gate/Poke macOS Gate/AboutView.swift +7 -1
  5. package/clients/Poke macOS Gate/Poke macOS Gate/AccessibilityPermissionView.swift +58 -0
  6. package/clients/Poke macOS Gate/Poke macOS Gate/GateService.swift +354 -23
  7. package/clients/Poke macOS Gate/Poke macOS Gate/Info.plist +2 -0
  8. package/clients/Poke macOS Gate/Poke macOS Gate/LogsView.swift +1 -1
  9. package/clients/Poke macOS Gate/Poke macOS Gate/MacVisualStyle.swift +89 -0
  10. package/clients/Poke macOS Gate/Poke macOS Gate/PermissionRowView.swift +55 -0
  11. package/clients/Poke macOS Gate/Poke macOS Gate/Poke_macOS_GateApp.swift +241 -91
  12. package/clients/Poke macOS Gate/Poke macOS Gate/SettingsView.swift +131 -65
  13. package/clients/Poke macOS Gate/Poke macOS Gate/SetupView.swift +157 -0
  14. package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.pbxproj +28 -8
  15. package/docs/cli.md +19 -0
  16. package/docs/getting-started.md +9 -6
  17. package/docs/index.md +23 -18
  18. package/docs/macos-app.md +39 -4
  19. package/docs/security.md +62 -18
  20. package/examples/agents/battery.30m.js +1 -1
  21. package/examples/agents/screentime.24h.js +5 -6
  22. package/package.json +3 -1
  23. package/src/agents.js +5 -8
  24. package/src/app.js +3 -3
  25. package/src/mcp-server.js +502 -27
  26. package/src/permission-service.js +128 -0
  27. package/test/mcp-server-access-policy.test.js +40 -0
  28. package/test/mcp-server-loop-guard.test.js +57 -0
  29. package/test/mcp-server-sandbox-command.test.js +18 -0
  30. package/test/permission-service.test.js +97 -0
@@ -9,7 +9,43 @@ permissions:
9
9
  contents: write
10
10
 
11
11
  jobs:
12
- build:
12
+ verify:
13
+ strategy:
14
+ matrix:
15
+ os:
16
+ - macos-15
17
+ - macos-26
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - name: Checkout
21
+ uses: actions/checkout@v4
22
+ with:
23
+ token: ${{ secrets.GITHUB_TOKEN }}
24
+
25
+ - name: Select Xcode 26
26
+ run: |
27
+ XCODE_APP="$(find /Applications -maxdepth 1 -type d -name 'Xcode_26*.app' | sort -V | tail -1)"
28
+ if [[ -z "${XCODE_APP:-}" ]]; then
29
+ echo "Xcode 26 not found on this runner."
30
+ exit 1
31
+ fi
32
+
33
+ sudo xcode-select -s "${XCODE_APP}/Contents/Developer"
34
+ xcodebuild -version
35
+ xcodebuild -version | grep '^Xcode 26' >/dev/null
36
+
37
+ - name: Build app
38
+ run: |
39
+ xcodebuild -project "clients/Poke macOS Gate/Poke macOS Gate.xcodeproj" \
40
+ -scheme "Poke macOS Gate" \
41
+ -configuration Release \
42
+ CODE_SIGN_IDENTITY="-" \
43
+ CODE_SIGNING_REQUIRED=NO \
44
+ DEVELOPMENT_TEAM="" \
45
+ clean build
46
+
47
+ release:
48
+ needs: verify
13
49
  runs-on: macos-26
14
50
  steps:
15
51
  - name: Checkout
@@ -37,8 +73,17 @@ jobs:
37
73
  git commit -m "Bump version to ${{ steps.version.outputs.number }}" || echo "No changes to commit"
38
74
  git push origin HEAD:refs/heads/main || echo "Push skipped"
39
75
 
40
- - name: Select Xcode
41
- run: sudo xcode-select -s /Applications/Xcode_26.0.app/Contents/Developer
76
+ - name: Select Xcode 26
77
+ run: |
78
+ XCODE_APP="$(find /Applications -maxdepth 1 -type d -name 'Xcode_26*.app' | sort -V | tail -1)"
79
+ if [[ -z "${XCODE_APP:-}" ]]; then
80
+ echo "Xcode 26 not found on this runner."
81
+ exit 1
82
+ fi
83
+
84
+ sudo xcode-select -s "${XCODE_APP}/Contents/Developer"
85
+ xcodebuild -version
86
+ xcodebuild -version | grep '^Xcode 26' >/dev/null
42
87
 
43
88
  - name: Build universal app
44
89
  run: |
@@ -88,6 +133,11 @@ jobs:
88
133
  VERSION: ${{ steps.version.outputs.tag }}
89
134
  SHA256: ${{ steps.sha.outputs.sha256 }}
90
135
  run: |
136
+ if [[ -z "${TAP_REPO_TOKEN_HOMEBREW:-}" ]]; then
137
+ echo "Skipping Homebrew tap update because TAP_REPO_TOKEN_HOMEBREW is not set."
138
+ exit 0
139
+ fi
140
+
91
141
  VERSION_NUM="${VERSION#v}"
92
142
  git clone https://x-access-token:${TAP_REPO_TOKEN_HOMEBREW}@github.com/f/homebrew-tap.git tap
93
143
  mkdir -p tap/Casks
package/README.md CHANGED
@@ -44,9 +44,9 @@ npx poke-gate
44
44
 
45
45
  ## Setup
46
46
 
47
- 1. Get an API key from [poke.com/kitchen/api-keys](https://poke.com/kitchen/api-keys)
48
- 2. Open Poke Gate from your menu bar and go to **Settings**
49
- 3. Paste your API key and save
47
+ 1. Open Poke Gate from your menu bar
48
+ 2. The **Setup View** guides you through choosing an access mode and granting Accessibility permission
49
+ 3. Sign in with Poke OAuth when prompted — a browser window opens automatically
50
50
 
51
51
  The app connects automatically and shows a green dot when ready.
52
52
 
@@ -91,15 +91,17 @@ From iMessage or Telegram, ask Poke:
91
91
 
92
92
  ## macOS App
93
93
 
94
- The menu bar app manages everything:
94
+ The native SwiftUI menu bar app manages everything:
95
95
 
96
+ - **First-run setup** — guided onboarding to choose an access mode and grant Accessibility permission
96
97
  - **Status** — green dot when connected, yellow when connecting, red on error
97
98
  - **Personalized** — shows "Connected to your Poke, [name]"
98
- - **Auto-start** — connects on launch if API key is saved
99
+ - **Access mode** — switch between Full, Limited, and Sandbox from Settings or the popover
100
+ - **Accessibility-first** — prompts for Accessibility permission (needed for automation) with live status updates
101
+ - **Auto-start** — connects on launch if signed in via OAuth
99
102
  - **Auto-restart** — reconnects automatically if the connection drops
100
- - **Settings** — paste your API key
101
- - **Logs** — view real-time tool calls and connection events
102
- - **Screen Recording** — prompts for permission on first launch
103
+ - **Logs** — view real-time tool calls with sandbox status
104
+ - **About** — version pulled dynamically from the app bundle
103
105
 
104
106
  The app runs in the menu bar only (no Dock icon). Quit is the only way to stop it.
105
107
 
@@ -127,12 +129,19 @@ If you prefer the command line over the macOS app:
127
129
  npx poke-gate
128
130
  ```
129
131
 
130
- On first run, paste your API key when prompted. Add `--verbose` to see tool calls in real time:
132
+ On first run, Poke OAuth opens in your browser. Add `--verbose` to see tool calls in real time:
131
133
 
132
134
  ```bash
133
135
  npx poke-gate --verbose
134
136
  ```
135
137
 
138
+ Set the access mode with `--mode`:
139
+
140
+ ```bash
141
+ npx poke-gate --mode limited
142
+ npx poke-gate --mode sandbox
143
+ ```
144
+
136
145
  Config is stored at `~/.config/poke-gate/config.json`.
137
146
 
138
147
  ## Agents
@@ -226,15 +235,34 @@ Save as `~/.config/poke-gate/agents/my-agent.30m.js` and it runs automatically w
226
235
 
227
236
  Agents start running when poke-gate connects and run once immediately on startup.
228
237
 
238
+ ## Access modes
239
+
240
+ Poke Gate supports three access modes that control what your agent can do:
241
+
242
+ | Mode | Description |
243
+ |------|-------------|
244
+ | **Full** (default) | All tools available. Risky actions (commands, file writes, screenshots) require chat approval. |
245
+ | **Limited** | Read-only tools plus a curated set of safe commands (`ls`, `cat`, `grep`, `curl`, etc.). `write_file` and `take_screenshot` are disabled. |
246
+ | **Sandbox** | Broader command support than Limited, but writes are restricted to `~/Downloads` and `/tmp` via macOS `sandbox-exec`. |
247
+
248
+ Set the mode via CLI flag, environment variable, or the macOS app Settings:
249
+
250
+ ```bash
251
+ npx poke-gate --mode sandbox
252
+ # or
253
+ POKE_GATE_PERMISSION_MODE=limited npx poke-gate
254
+ ```
255
+
229
256
  ## Security
230
257
 
231
- **Poke Gate grants full shell access to your Poke agent.** This means:
258
+ **In full mode, Poke Gate grants full shell access to your Poke agent.** This means:
232
259
 
233
260
  - Any command can be run with your user's permissions
234
261
  - Files can be read and written anywhere your user has access
235
- - Only your Poke agent (authenticated by your API key) can reach the tunnel
262
+ - Risky tools require approval in chat before execution
263
+ - Only your Poke agent (authenticated via Poke OAuth) can reach the tunnel
236
264
 
237
- Only run Poke Gate on machines and networks you trust.
265
+ Only run Poke Gate on machines and networks you trust. Use `limited` or `sandbox` mode if you want tighter restrictions.
238
266
 
239
267
  ## Project structure
240
268
 
@@ -242,12 +270,18 @@ Only run Poke Gate on machines and networks you trust.
242
270
  clients/
243
271
  Poke macOS Gate/ macOS menu bar app (SwiftUI)
244
272
  bin/
245
- poke-gate.js CLI entry point, run-agent + agent get subcommands
273
+ poke-gate.js CLI entry point with --mode flag
246
274
  src/
247
275
  app.js Startup: MCP server + tunnel + agent scheduler
248
276
  agents.js Agent discovery, scheduling, env loading, download
249
- mcp-server.js JSON-RPC MCP handler with OS tools
277
+ mcp-server.js JSON-RPC MCP handler, tools, access policy, sandbox
278
+ permission-service.js HMAC approval tokens, session whitelisting
250
279
  tunnel.js PokeTunnel wrapper
280
+ test/
281
+ mcp-server-access-policy.test.js
282
+ mcp-server-loop-guard.test.js
283
+ mcp-server-sandbox-command.test.js
284
+ permission-service.test.js
251
285
  examples/
252
286
  agents/
253
287
  beeper.1h.js Example: Beeper message digest agent
package/bin/poke-gate.js CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  const args = process.argv.slice(2);
4
4
 
5
+ const VALID_MODES = ["full", "limited", "sandbox"];
6
+
7
+ function parseMode() {
8
+ const idx = args.indexOf("--mode");
9
+ if (idx === -1) return null;
10
+ const value = args[idx + 1];
11
+ if (!value || !VALID_MODES.includes(value)) {
12
+ console.error(`Invalid --mode value. Must be one of: ${VALID_MODES.join(", ")}`);
13
+ process.exit(1);
14
+ }
15
+ return value;
16
+ }
17
+
5
18
  async function main() {
6
19
  if (args[0] === "run-agent") {
7
20
  const name = args[1];
@@ -27,6 +40,10 @@ async function main() {
27
40
  const { createAgent } = await import("../src/agent-create.js");
28
41
  await createAgent(prompt);
29
42
  } else {
43
+ const mode = parseMode();
44
+ if (mode) {
45
+ process.env.POKE_GATE_PERMISSION_MODE = mode;
46
+ }
30
47
  await import("../src/app.js");
31
48
  }
32
49
  }
@@ -1,5 +1,11 @@
1
1
  import SwiftUI
2
2
 
3
+ extension Bundle {
4
+ var appVersion: String {
5
+ infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
6
+ }
7
+ }
8
+
3
9
  struct AboutView: View {
4
10
  @Environment(\.dismiss) private var dismiss
5
11
 
@@ -13,7 +19,7 @@ struct AboutView: View {
13
19
  .font(.title2)
14
20
  .fontWeight(.semibold)
15
21
 
16
- Text("Version 0.0.8")
22
+ Text("Version \(Bundle.main.appVersion)")
17
23
  .font(.caption)
18
24
  .foregroundStyle(.secondary)
19
25
 
@@ -0,0 +1,58 @@
1
+ import SwiftUI
2
+
3
+ struct AccessibilityPermissionView: View {
4
+ @ObservedObject var service: GateService
5
+
6
+ var body: some View {
7
+ let granted = service.hasSystemPermissionsGranted
8
+
9
+ VStack(alignment: .leading, spacing: 10) {
10
+ HStack(spacing: 8) {
11
+ Image(systemName: granted ? "checkmark.seal.fill" : "exclamationmark.shield.fill")
12
+ .font(.headline)
13
+ .foregroundStyle(granted ? Color.green : Color.orange)
14
+
15
+ VStack(alignment: .leading, spacing: 1) {
16
+ Text(granted ? "Accessibility granted" : "Accessibility permission needed")
17
+ .font(.subheadline)
18
+ .fontWeight(.semibold)
19
+
20
+ Text(granted
21
+ ? "Poke Gate can now use keyboard and mouse automation."
22
+ : "Open macOS Accessibility settings and enable Poke Gate.")
23
+ .font(.caption)
24
+ .foregroundStyle(.secondary)
25
+ }
26
+
27
+ Spacer()
28
+ }
29
+
30
+ if granted {
31
+ HStack(spacing: 8) {
32
+ Image(systemName: "checkmark.circle.fill")
33
+ .foregroundStyle(.green)
34
+ Text("Granted")
35
+ .font(.caption)
36
+ .foregroundStyle(.green)
37
+ Spacer()
38
+ }
39
+ } else {
40
+ Button {
41
+ service.openSystemPermission(.accessibility)
42
+ } label: {
43
+ Label("Open Accessibility Settings", systemImage: "hand.raised.app")
44
+ .font(.subheadline)
45
+ }
46
+ .buttonStyle(.plain)
47
+ .foregroundStyle(Color.accentColor)
48
+
49
+ Text("After enabling it, return here and the status will update automatically.")
50
+ .font(.caption2)
51
+ .foregroundStyle(.tertiary)
52
+ }
53
+ }
54
+ .padding(12)
55
+ .macPanelStyle(granted ? .success : .warning, cornerRadius: 12)
56
+ .animation(.easeInOut(duration: 0.2), value: granted)
57
+ }
58
+ }