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.
- package/.github/workflows/release.yml +53 -3
- package/README.md +48 -14
- package/bin/poke-gate.js +17 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/AboutView.swift +7 -1
- package/clients/Poke macOS Gate/Poke macOS Gate/AccessibilityPermissionView.swift +58 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/GateService.swift +354 -23
- package/clients/Poke macOS Gate/Poke macOS Gate/Info.plist +2 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/LogsView.swift +1 -1
- package/clients/Poke macOS Gate/Poke macOS Gate/MacVisualStyle.swift +89 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/PermissionRowView.swift +55 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/Poke_macOS_GateApp.swift +241 -91
- package/clients/Poke macOS Gate/Poke macOS Gate/SettingsView.swift +131 -65
- package/clients/Poke macOS Gate/Poke macOS Gate/SetupView.swift +157 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.pbxproj +28 -8
- package/docs/cli.md +19 -0
- package/docs/getting-started.md +9 -6
- package/docs/index.md +23 -18
- package/docs/macos-app.md +39 -4
- package/docs/security.md +62 -18
- package/examples/agents/battery.30m.js +1 -1
- package/examples/agents/screentime.24h.js +5 -6
- package/package.json +3 -1
- package/src/agents.js +5 -8
- package/src/app.js +3 -3
- package/src/mcp-server.js +502 -27
- package/src/permission-service.js +128 -0
- package/test/mcp-server-access-policy.test.js +40 -0
- package/test/mcp-server-loop-guard.test.js +57 -0
- package/test/mcp-server-sandbox-command.test.js +18 -0
- package/test/permission-service.test.js +97 -0
|
@@ -9,7 +9,43 @@ permissions:
|
|
|
9
9
|
contents: write
|
|
10
10
|
|
|
11
11
|
jobs:
|
|
12
|
-
|
|
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:
|
|
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.
|
|
48
|
-
2.
|
|
49
|
-
3.
|
|
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
|
-
- **
|
|
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
|
-
- **
|
|
101
|
-
- **
|
|
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,
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|