poke-gate 0.1.9 → 0.2.1
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/Gate.app +0 -0
- 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 +389 -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 +234 -91
- package/clients/Poke macOS Gate/Poke macOS Gate/SettingsView.swift +125 -81
- package/clients/Poke macOS Gate/Poke macOS Gate/SetupView.swift +157 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.pbxproj +31 -11
- 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/macOS +0 -0
- package/package.json +3 -1
- package/src/agents.js +5 -8
- package/src/app.js +29 -5
- 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
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
struct SetupView: View {
|
|
4
|
+
@ObservedObject var service: GateService
|
|
5
|
+
@State private var selectedMode: GateService.PermissionMode
|
|
6
|
+
@State private var step: Step = .accessMode
|
|
7
|
+
|
|
8
|
+
enum Step { case accessMode, permissions }
|
|
9
|
+
|
|
10
|
+
init(service: GateService) {
|
|
11
|
+
self.service = service
|
|
12
|
+
_selectedMode = State(initialValue: service.permissionMode)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var body: some View {
|
|
16
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
17
|
+
stepIndicator
|
|
18
|
+
.padding(.horizontal, 16)
|
|
19
|
+
.padding(.top, 16)
|
|
20
|
+
.padding(.bottom, 12)
|
|
21
|
+
|
|
22
|
+
Divider()
|
|
23
|
+
|
|
24
|
+
switch step {
|
|
25
|
+
case .accessMode: accessModeStep
|
|
26
|
+
case .permissions: permissionsStep
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
.frame(width: 380)
|
|
30
|
+
.onAppear { service.startPermissionPolling() }
|
|
31
|
+
.onDisappear { service.stopPermissionPolling() }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private var stepIndicator: some View {
|
|
35
|
+
HStack(spacing: 6) {
|
|
36
|
+
ForEach(Array(Step.allCases.enumerated()), id: \.offset) { index, s in
|
|
37
|
+
HStack(spacing: 4) {
|
|
38
|
+
Circle()
|
|
39
|
+
.fill(s == step ? MacVisualStyle.chipActiveFill : (isCompleted(s) ? Color.accentColor.opacity(0.5) : MacVisualStyle.chipInactiveFill))
|
|
40
|
+
.frame(width: 6, height: 6)
|
|
41
|
+
Text(s.label)
|
|
42
|
+
.font(.caption2)
|
|
43
|
+
.foregroundStyle(s == step ? .primary : .secondary)
|
|
44
|
+
}
|
|
45
|
+
if index < Step.allCases.count - 1 {
|
|
46
|
+
Rectangle()
|
|
47
|
+
.fill(MacVisualStyle.progressTrackColor)
|
|
48
|
+
.frame(height: 1)
|
|
49
|
+
.frame(maxWidth: .infinity)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private var accessModeStep: some View {
|
|
56
|
+
VStack(alignment: .leading, spacing: 16) {
|
|
57
|
+
VStack(alignment: .leading, spacing: 4) {
|
|
58
|
+
Text("Choose access mode")
|
|
59
|
+
.font(.headline)
|
|
60
|
+
Text("Controls which tools Poke can use on this machine.")
|
|
61
|
+
.font(.caption)
|
|
62
|
+
.foregroundStyle(.secondary)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
VStack(spacing: 8) {
|
|
66
|
+
ForEach(GateService.PermissionMode.allCases) { mode in
|
|
67
|
+
modeRow(mode)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
HStack {
|
|
72
|
+
Spacer()
|
|
73
|
+
Button("Continue") { step = .permissions }
|
|
74
|
+
.keyboardShortcut(.defaultAction)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
.padding(16)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private var permissionsStep: some View {
|
|
81
|
+
VStack(alignment: .leading, spacing: 16) {
|
|
82
|
+
VStack(alignment: .leading, spacing: 4) {
|
|
83
|
+
Text("Grant permissions")
|
|
84
|
+
.font(.headline)
|
|
85
|
+
Text("These allow Poke to control your Mac. You can grant them now or later from the menu bar.")
|
|
86
|
+
.font(.caption)
|
|
87
|
+
.foregroundStyle(.secondary)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
VStack(spacing: 10) {
|
|
91
|
+
ForEach(GateService.SystemPermission.allCases) { permission in
|
|
92
|
+
let status = service.systemPermissionStatuses.first(where: { $0.permission == permission })
|
|
93
|
+
PermissionRowView(
|
|
94
|
+
permission: permission,
|
|
95
|
+
isGranted: status?.isGranted ?? false,
|
|
96
|
+
onGrant: { service.openSystemPermission(permission) }
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
HStack {
|
|
102
|
+
Button("Back") { step = .accessMode }
|
|
103
|
+
.buttonStyle(.plain)
|
|
104
|
+
.foregroundStyle(.secondary)
|
|
105
|
+
|
|
106
|
+
Spacer()
|
|
107
|
+
|
|
108
|
+
Button("Finish Setup") {
|
|
109
|
+
service.completeFirstRunSetup(selectedMode: selectedMode, requestPermissions: false)
|
|
110
|
+
}
|
|
111
|
+
.keyboardShortcut(.defaultAction)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
.padding(16)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private func modeRow(_ mode: GateService.PermissionMode) -> some View {
|
|
118
|
+
Button { selectedMode = mode } label: {
|
|
119
|
+
HStack(alignment: .top, spacing: 10) {
|
|
120
|
+
Image(systemName: selectedMode == mode ? "checkmark.circle.fill" : "circle")
|
|
121
|
+
.foregroundStyle(selectedMode == mode ? Color.accentColor : Color.secondary)
|
|
122
|
+
|
|
123
|
+
VStack(alignment: .leading, spacing: 2) {
|
|
124
|
+
Text(mode.title)
|
|
125
|
+
.font(.subheadline)
|
|
126
|
+
.foregroundStyle(.primary)
|
|
127
|
+
Text(mode.subtitle)
|
|
128
|
+
.font(.caption)
|
|
129
|
+
.foregroundStyle(.secondary)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Spacer()
|
|
133
|
+
}
|
|
134
|
+
.padding(10)
|
|
135
|
+
.macPanelStyle(selectedMode == mode ? .selected : .neutral)
|
|
136
|
+
}
|
|
137
|
+
.buttonStyle(.plain)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private func isCompleted(_ s: Step) -> Bool {
|
|
141
|
+
switch s {
|
|
142
|
+
case .accessMode: return step == .permissions
|
|
143
|
+
case .permissions: return false
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
extension SetupView.Step: CaseIterable {
|
|
149
|
+
static var allCases: [SetupView.Step] { [.accessMode, .permissions] }
|
|
150
|
+
|
|
151
|
+
var label: String {
|
|
152
|
+
switch self {
|
|
153
|
+
case .accessMode: return "Access Mode"
|
|
154
|
+
case .permissions: return "Permissions"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -10,9 +10,22 @@
|
|
|
10
10
|
442DE4462F71DCD9009BF9EF /* Poke macOS Gate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Poke macOS Gate.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
11
11
|
/* End PBXFileReference section */
|
|
12
12
|
|
|
13
|
+
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
|
14
|
+
114D3ACF2F74597400C9F8B9 /* Exceptions for "Poke macOS Gate" folder in "Poke macOS Gate" target */ = {
|
|
15
|
+
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
|
16
|
+
membershipExceptions = (
|
|
17
|
+
Info.plist,
|
|
18
|
+
);
|
|
19
|
+
target = 442DE4452F71DCD9009BF9EF /* Poke macOS Gate */;
|
|
20
|
+
};
|
|
21
|
+
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
|
22
|
+
|
|
13
23
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
|
14
24
|
442DE4482F71DCD9009BF9EF /* Poke macOS Gate */ = {
|
|
15
25
|
isa = PBXFileSystemSynchronizedRootGroup;
|
|
26
|
+
exceptions = (
|
|
27
|
+
114D3ACF2F74597400C9F8B9 /* Exceptions for "Poke macOS Gate" folder in "Poke macOS Gate" target */,
|
|
28
|
+
);
|
|
16
29
|
path = "Poke macOS Gate";
|
|
17
30
|
sourceTree = "<group>";
|
|
18
31
|
};
|
|
@@ -78,7 +91,7 @@
|
|
|
78
91
|
attributes = {
|
|
79
92
|
BuildIndependentTargetsInParallel = 1;
|
|
80
93
|
LastSwiftUpdateCheck = 2620;
|
|
81
|
-
LastUpgradeCheck =
|
|
94
|
+
LastUpgradeCheck = 2630;
|
|
82
95
|
TargetAttributes = {
|
|
83
96
|
442DE4452F71DCD9009BF9EF = {
|
|
84
97
|
CreatedOnToolsVersion = 26.2;
|
|
@@ -159,8 +172,9 @@
|
|
|
159
172
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
160
173
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
161
174
|
COPY_PHASE_STRIP = NO;
|
|
175
|
+
DEAD_CODE_STRIPPING = YES;
|
|
162
176
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
163
|
-
DEVELOPMENT_TEAM =
|
|
177
|
+
DEVELOPMENT_TEAM = "";
|
|
164
178
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
165
179
|
ENABLE_TESTABILITY = YES;
|
|
166
180
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
@@ -179,11 +193,12 @@
|
|
|
179
193
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
180
194
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
181
195
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
182
|
-
MACOSX_DEPLOYMENT_TARGET =
|
|
196
|
+
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
|
183
197
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
184
198
|
MTL_FAST_MATH = YES;
|
|
185
199
|
ONLY_ACTIVE_ARCH = YES;
|
|
186
200
|
SDKROOT = macosx;
|
|
201
|
+
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
|
187
202
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
|
188
203
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
189
204
|
};
|
|
@@ -223,8 +238,9 @@
|
|
|
223
238
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
224
239
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
225
240
|
COPY_PHASE_STRIP = NO;
|
|
241
|
+
DEAD_CODE_STRIPPING = YES;
|
|
226
242
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
227
|
-
DEVELOPMENT_TEAM =
|
|
243
|
+
DEVELOPMENT_TEAM = "";
|
|
228
244
|
ENABLE_NS_ASSERTIONS = NO;
|
|
229
245
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
230
246
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
@@ -237,10 +253,11 @@
|
|
|
237
253
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
238
254
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
239
255
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
240
|
-
MACOSX_DEPLOYMENT_TARGET =
|
|
256
|
+
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
|
241
257
|
MTL_ENABLE_DEBUG_INFO = NO;
|
|
242
258
|
MTL_FAST_MATH = YES;
|
|
243
259
|
SDKROOT = macosx;
|
|
260
|
+
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
|
244
261
|
SWIFT_COMPILATION_MODE = wholemodule;
|
|
245
262
|
};
|
|
246
263
|
name = Release;
|
|
@@ -250,10 +267,12 @@
|
|
|
250
267
|
buildSettings = {
|
|
251
268
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
252
269
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
|
270
|
+
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
|
253
271
|
CODE_SIGN_STYLE = Automatic;
|
|
254
272
|
COMBINE_HIDPI_IMAGES = YES;
|
|
255
273
|
CURRENT_PROJECT_VERSION = 1;
|
|
256
|
-
|
|
274
|
+
DEAD_CODE_STRIPPING = YES;
|
|
275
|
+
DEVELOPMENT_TEAM = "";
|
|
257
276
|
ENABLE_APP_SANDBOX = NO;
|
|
258
277
|
ENABLE_HARDENED_RUNTIME = NO;
|
|
259
278
|
ENABLE_PREVIEWS = YES;
|
|
@@ -266,8 +285,8 @@
|
|
|
266
285
|
"$(inherited)",
|
|
267
286
|
"@executable_path/../Frameworks",
|
|
268
287
|
);
|
|
269
|
-
MACOSX_DEPLOYMENT_TARGET =
|
|
270
|
-
MARKETING_VERSION = 0.
|
|
288
|
+
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
|
289
|
+
MARKETING_VERSION = 0.2.0;
|
|
271
290
|
PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
|
|
272
291
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
273
292
|
REGISTER_APP_GROUPS = YES;
|
|
@@ -285,10 +304,11 @@
|
|
|
285
304
|
buildSettings = {
|
|
286
305
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
287
306
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
|
307
|
+
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
|
288
308
|
CODE_SIGN_STYLE = Automatic;
|
|
289
309
|
COMBINE_HIDPI_IMAGES = YES;
|
|
290
310
|
CURRENT_PROJECT_VERSION = 1;
|
|
291
|
-
|
|
311
|
+
DEAD_CODE_STRIPPING = YES;
|
|
292
312
|
ENABLE_APP_SANDBOX = NO;
|
|
293
313
|
ENABLE_HARDENED_RUNTIME = NO;
|
|
294
314
|
ENABLE_PREVIEWS = YES;
|
|
@@ -301,8 +321,8 @@
|
|
|
301
321
|
"$(inherited)",
|
|
302
322
|
"@executable_path/../Frameworks",
|
|
303
323
|
);
|
|
304
|
-
MACOSX_DEPLOYMENT_TARGET =
|
|
305
|
-
MARKETING_VERSION = 0.
|
|
324
|
+
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
|
325
|
+
MARKETING_VERSION = 0.2.0;
|
|
306
326
|
PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
|
|
307
327
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
308
328
|
REGISTER_APP_GROUPS = YES;
|
package/docs/cli.md
CHANGED
|
@@ -8,6 +8,23 @@ npx poke-gate
|
|
|
8
8
|
|
|
9
9
|
Starts the MCP server, connects the tunnel, and begins the agent scheduler. On first run, if you're not signed in, opens Poke OAuth in your browser.
|
|
10
10
|
|
|
11
|
+
### Access mode
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx poke-gate --mode limited
|
|
15
|
+
npx poke-gate --mode sandbox
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Controls which tools your Poke agent can use. Defaults to `full` if not specified.
|
|
19
|
+
|
|
20
|
+
| Mode | Description |
|
|
21
|
+
|------|-------------|
|
|
22
|
+
| `full` | All tools available, subject to chat approval for risky actions |
|
|
23
|
+
| `limited` | Safe tools and a curated set of read-only commands only |
|
|
24
|
+
| `sandbox` | Broader command support, but writes are restricted by macOS `sandbox-exec` policies |
|
|
25
|
+
|
|
26
|
+
You can also set the mode via the `POKE_GATE_PERMISSION_MODE` environment variable. The `--mode` flag takes precedence.
|
|
27
|
+
|
|
11
28
|
### Verbose mode
|
|
12
29
|
|
|
13
30
|
```bash
|
|
@@ -92,6 +109,8 @@ Fetching agent "beeper" from GitHub...
|
|
|
92
109
|
|
|
93
110
|
| Variable | Description |
|
|
94
111
|
|----------|-------------|
|
|
112
|
+
| `POKE_GATE_PERMISSION_MODE` | Access mode: `full` (default), `limited`, or `sandbox` |
|
|
113
|
+
| `POKE_GATE_HMAC_SECRET` | Fixed HMAC secret for approval tokens (random per session by default) |
|
|
95
114
|
| `POKE_API_KEY` | Override the API key (skips OAuth) |
|
|
96
115
|
| `POKE_API` | Override the Poke API base URL |
|
|
97
116
|
| `POKE_FRONTEND` | Override the Poke frontend URL |
|
package/docs/getting-started.md
CHANGED
|
@@ -28,14 +28,17 @@ If you don't need the macOS app:
|
|
|
28
28
|
npx poke-gate
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Poke Gate needs **Accessibility** permission on your Mac to automate keyboard/mouse and take screenshots.
|
|
32
32
|
|
|
33
|
+
### 1. Sign in
|
|
33
34
|
Poke Gate uses Poke OAuth to authenticate. On first launch:
|
|
34
35
|
|
|
35
|
-
1. Open Poke Gate from your menu bar
|
|
36
|
-
2. The
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
1. Open Poke Gate from your menu bar.
|
|
37
|
+
2. The **Setup View** will appear to guide you through:
|
|
38
|
+
- Selecting an access mode (Full, Limited, or Sandbox)
|
|
39
|
+
- Granting the required macOS Accessibility permissions
|
|
40
|
+
3. If you're not signed in, a browser window opens for Poke OAuth.
|
|
41
|
+
4. After signing in, the connection is established.
|
|
39
42
|
|
|
40
43
|
You can also sign in manually:
|
|
41
44
|
|
|
@@ -47,7 +50,7 @@ npx poke login
|
|
|
47
50
|
|
|
48
51
|
Once connected, you'll see a green dot in the menu bar. The popover shows:
|
|
49
52
|
|
|
50
|
-
> ● Connected to your Poke,
|
|
53
|
+
> ● Connected to your Poke, your name
|
|
51
54
|
|
|
52
55
|
Now open iMessage or Telegram and message your Poke:
|
|
53
56
|
|
package/docs/index.md
CHANGED
|
@@ -17,24 +17,29 @@ hero:
|
|
|
17
17
|
link: https://github.com/f/poke-gate
|
|
18
18
|
|
|
19
19
|
features:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
- icon: 🖥️
|
|
21
|
+
title: Full Shell Access
|
|
22
|
+
details: Run any terminal command on your machine — ls, git, brew, python,
|
|
23
|
+
curl, and more.
|
|
24
|
+
- icon: 📁
|
|
25
|
+
title: File Operations
|
|
26
|
+
details: Read, write, and list files and directories. Your Poke agent sees
|
|
27
|
+
your filesystem.
|
|
28
|
+
- icon: 📸
|
|
29
|
+
title: Screenshots
|
|
30
|
+
details: Capture your screen remotely. Poke can see what you see.
|
|
31
|
+
- icon: 🤖
|
|
32
|
+
title: Agents
|
|
33
|
+
details: Scheduled scripts that run in the background — automate message
|
|
34
|
+
digests, backups, health checks.
|
|
35
|
+
- icon: 🌴
|
|
36
|
+
title: macOS Menu Bar App
|
|
37
|
+
details: Native SwiftUI app that lives in your menu bar. Auto-connects,
|
|
38
|
+
auto-restarts, shows real-time status.
|
|
39
|
+
- icon: ⚡
|
|
40
|
+
title: MCP Tunnel
|
|
41
|
+
details: Secure WebSocket tunnel powered by the Poke SDK. Only your
|
|
42
|
+
authenticated agent can reach your machine.
|
|
38
43
|
---
|
|
39
44
|
|
|
40
45
|
## Quick install
|
package/docs/macos-app.md
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
# macOS App
|
|
2
2
|
|
|
3
|
-
Poke Gate includes a native SwiftUI menu bar app for macOS.
|
|
3
|
+
Poke Gate includes a native SwiftUI menu bar app for macOS 15+ (Sequoia).
|
|
4
|
+
|
|
5
|
+
## First-run setup
|
|
6
|
+
|
|
7
|
+
On first launch, a **Setup View** guides you through two steps:
|
|
8
|
+
|
|
9
|
+
1. **Choose access mode** — select Full, Limited, or Sandbox (see [Access modes](#access-modes) below)
|
|
10
|
+
2. **Grant permissions** — the app checks for Accessibility permission and walks you through enabling it in System Settings
|
|
11
|
+
|
|
12
|
+
The setup view only appears once. You can change the access mode anytime from Settings.
|
|
4
13
|
|
|
5
14
|
## Menu bar
|
|
6
15
|
|
|
7
16
|
The app runs in the menu bar only — no Dock icon. Click the door icon to see the popover:
|
|
8
17
|
|
|
9
18
|
- **Status** — green dot when connected, yellow when connecting, red on error
|
|
10
|
-
- **Personalized** — shows "Connected to your Poke,
|
|
19
|
+
- **Personalized** — shows "Connected to your Poke, name"
|
|
11
20
|
- **Recent activity** — last few log entries
|
|
12
21
|
- **Action buttons** — Logs, Agents, Settings, Restart/Start, Quit
|
|
13
|
-
- **About** — version
|
|
22
|
+
- **About** — dynamic version pulled from the app bundle (no hardcoded strings)
|
|
23
|
+
- **Access mode chip** — shows the current mode with quick-switch buttons
|
|
14
24
|
|
|
15
25
|
### Status icons
|
|
16
26
|
|
|
@@ -20,6 +30,28 @@ The app runs in the menu bar only — no Dock icon. Click the door icon to see t
|
|
|
20
30
|
| 🚪 (closed) | Stopped or connecting |
|
|
21
31
|
| ⚠️ | Error |
|
|
22
32
|
|
|
33
|
+
## Access modes
|
|
34
|
+
|
|
35
|
+
The macOS app lets you choose an access mode from Settings or the popover. Changing the mode restarts poke-gate automatically.
|
|
36
|
+
|
|
37
|
+
| Mode | What it allows |
|
|
38
|
+
|------|---------------|
|
|
39
|
+
| **Full System Access** | All tools available, subject to chat approval for risky actions |
|
|
40
|
+
| **Limited Permissions** | Safe tools and curated command families only (`ls`, `cat`, `grep`, `curl`, etc.) |
|
|
41
|
+
| **Run in Sandbox** | Broader command support, but writes restricted by macOS `sandbox-exec` to `~/Downloads` and `/tmp` |
|
|
42
|
+
|
|
43
|
+
When **Full** mode is selected, the app shows an Accessibility permission prompt — this permission is required for keyboard/mouse automation and AppleScript tasks.
|
|
44
|
+
|
|
45
|
+
## Accessibility permission
|
|
46
|
+
|
|
47
|
+
The app uses an **Accessibility-first** permission model. Instead of requesting Full Disk Access, the app checks for Accessibility permission using the native `AXIsProcessTrusted()` API.
|
|
48
|
+
|
|
49
|
+
- A dedicated **AccessibilityPermissionView** shows the current status with a button to open System Settings
|
|
50
|
+
- Permission state refreshes automatically whenever the app regains focus
|
|
51
|
+
- The view updates live — no need to restart the app after granting permission
|
|
52
|
+
|
|
53
|
+
When Full mode is active, this view appears in both the Settings window and the popover to ensure you don't miss it.
|
|
54
|
+
|
|
23
55
|
## Settings
|
|
24
56
|
|
|
25
57
|
Open Settings from the popover. The settings window shows:
|
|
@@ -27,6 +59,8 @@ Open Settings from the popover. The settings window shows:
|
|
|
27
59
|
- **Authentication status** — whether you're signed in via Poke OAuth
|
|
28
60
|
- **Sign in button** — runs `npx poke login` and opens a browser window
|
|
29
61
|
- **Connection status** — current state with a Reconnect button
|
|
62
|
+
- **Access mode** — radio buttons for Full, Limited, and Sandbox with descriptions
|
|
63
|
+
- **Accessibility status** — permission check with a direct link to System Settings (in Full mode)
|
|
30
64
|
|
|
31
65
|
## Logs
|
|
32
66
|
|
|
@@ -34,6 +68,7 @@ The Logs window shows real-time activity:
|
|
|
34
68
|
|
|
35
69
|
- Tool calls are highlighted
|
|
36
70
|
- Errors appear in red
|
|
71
|
+
- Sandbox status shown for each command (`sandbox=os` or `sandbox=none`)
|
|
37
72
|
- Copy all logs to clipboard
|
|
38
73
|
- Clear logs
|
|
39
74
|
|
|
@@ -58,7 +93,7 @@ The app connects automatically on launch if you've previously signed in. If the
|
|
|
58
93
|
|
|
59
94
|
## Building from source
|
|
60
95
|
|
|
61
|
-
Requires macOS 15+ and Xcode
|
|
96
|
+
Requires macOS 15+ and Xcode 16+.
|
|
62
97
|
|
|
63
98
|
```bash
|
|
64
99
|
git clone https://github.com/f/poke-gate.git
|
package/docs/security.md
CHANGED
|
@@ -1,34 +1,78 @@
|
|
|
1
1
|
# Security
|
|
2
2
|
|
|
3
3
|
::: danger Full shell access
|
|
4
|
-
Poke Gate grants **full shell access** to your Poke agent. Understand the implications before running it.
|
|
4
|
+
In **full** mode, Poke Gate grants **full shell access** to your Poke agent. Understand the implications before running it — or choose a more restrictive mode.
|
|
5
5
|
:::
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Access modes
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Poke Gate supports three access modes that control what tools your agent can use:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
### Full (default)
|
|
12
|
+
|
|
13
|
+
All tools are available. Risky actions (`run_command`, `write_file`, `take_screenshot`) require **chat approval** — the agent must ask you in chat before executing, and you approve with a signed token.
|
|
14
|
+
|
|
15
|
+
### Limited
|
|
16
|
+
|
|
17
|
+
Only safe, read-only tools are available:
|
|
18
|
+
|
|
19
|
+
- `read_file`, `read_image`, `list_directory`, `system_info`, `network_speed` work normally
|
|
20
|
+
- `run_command` is restricted to a curated allowlist: `ls`, `pwd`, `cat`, `grep`, `find`, `head`, `tail`, `wc`, `sed`, `awk`, `curl`, `jq`, `diff`, and others
|
|
21
|
+
- `write_file` and `take_screenshot` are **disabled**
|
|
22
|
+
- Dangerous patterns (`sudo`, `rm -rf`, etc.) are always blocked
|
|
23
|
+
|
|
24
|
+
### Sandbox
|
|
25
|
+
|
|
26
|
+
Broader command support than Limited, plus commands like `brew`, `node`, `python`, `ffmpeg`, `mkdir`, `cp`, `mv`:
|
|
27
|
+
|
|
28
|
+
- `run_command` uses macOS `sandbox-exec` to restrict file writes to `~/Downloads` and `/tmp`
|
|
29
|
+
- `write_file` and `take_screenshot` are **disabled**
|
|
30
|
+
- Dangerous patterns are always blocked
|
|
31
|
+
|
|
32
|
+
### Setting the mode
|
|
33
|
+
|
|
34
|
+
**CLI:**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx poke-gate --mode limited
|
|
38
|
+
npx poke-gate --mode sandbox
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Environment variable:**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
POKE_GATE_PERMISSION_MODE=sandbox npx poke-gate
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**macOS app:** Open Settings and select the access mode. The app restarts automatically when you change it.
|
|
48
|
+
|
|
49
|
+
## Tool approval flow
|
|
50
|
+
|
|
51
|
+
In **full** mode, risky tools (`run_command`, `write_file`, `take_screenshot`) use an HMAC-signed approval flow:
|
|
52
|
+
|
|
53
|
+
1. The agent calls the tool — Poke Gate returns `AWAITING_APPROVAL` with a signed token
|
|
54
|
+
2. The agent asks you in chat to approve
|
|
55
|
+
3. You approve — the agent re-calls the tool with the approval token
|
|
56
|
+
4. Optionally, you can `remember_in_session` (same command) or `remember_all_risky` (all risky tools for the session)
|
|
17
57
|
|
|
18
58
|
## What protects you
|
|
19
59
|
|
|
20
|
-
- **Authentication** — only your Poke agent (authenticated via
|
|
21
|
-
- **Tunnel isolation** — the MCP server only listens on `127.0.0.1` (localhost)
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
60
|
+
- **Authentication** — only your Poke agent (authenticated via Poke OAuth) can reach the tunnel
|
|
61
|
+
- **Tunnel isolation** — the MCP server only listens on `127.0.0.1` (localhost), not exposed to the network
|
|
62
|
+
- **Chat approval** — risky tools require explicit approval before execution (in full mode)
|
|
63
|
+
- **Access policies** — limited and sandbox modes enforce strict command allowlists
|
|
64
|
+
- **Loop guard** — duplicate or recently-failed commands are suppressed to prevent runaway retries
|
|
65
|
+
- **No persistent access** — quitting Poke Gate closes the tunnel and deletes the connection
|
|
66
|
+
- **Connection cleanup** — old connections are deleted before new ones are created
|
|
24
67
|
|
|
25
68
|
## Best practices
|
|
26
69
|
|
|
27
|
-
1. **
|
|
28
|
-
2. **
|
|
29
|
-
3. **
|
|
30
|
-
4. **
|
|
31
|
-
5. **
|
|
70
|
+
1. **Choose the right access mode** — use `limited` or `sandbox` if you don't need full shell access.
|
|
71
|
+
2. **Only run on trusted machines** — don't run Poke Gate on shared or public computers.
|
|
72
|
+
3. **Quit when not needed** — close the app when you don't need remote access.
|
|
73
|
+
4. **Review agent scripts** — before installing a community agent, read the code. Agents run with your user permissions.
|
|
74
|
+
5. **Keep env files secure** — `.env` files in `~/.config/poke-gate/agents/` may contain API tokens. Don't commit them to git.
|
|
75
|
+
6. **Use verbose mode** — run with `--verbose` to see what tools are being called in real time.
|
|
32
76
|
|
|
33
77
|
## Reporting issues
|
|
34
78
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { Poke, getToken } from "poke";
|
|
10
10
|
import { execSync } from "node:child_process";
|
|
11
|
-
import { readFileSync, writeFileSync
|
|
11
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
|
|
@@ -17,10 +17,10 @@ if (!token) {
|
|
|
17
17
|
|
|
18
18
|
function getScreenTime() {
|
|
19
19
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
).trim();
|
|
20
|
+
execSync(`defaults read com.apple.ScreenTimeAgent 2>/dev/null || echo "{}"`, {
|
|
21
|
+
encoding: "utf-8",
|
|
22
|
+
timeout: 10000,
|
|
23
|
+
}).trim();
|
|
24
24
|
|
|
25
25
|
// Fallback: use process list to estimate active apps
|
|
26
26
|
const ps = execSync(
|
|
@@ -42,11 +42,10 @@ function getActiveApps() {
|
|
|
42
42
|
end tell
|
|
43
43
|
return appList as text
|
|
44
44
|
`;
|
|
45
|
-
|
|
45
|
+
return execSync(`osascript -e '${script}'`, {
|
|
46
46
|
encoding: "utf-8",
|
|
47
47
|
timeout: 10000,
|
|
48
48
|
}).trim();
|
|
49
|
-
return result.split(", ");
|
|
50
49
|
} catch {
|
|
51
50
|
return [];
|
|
52
51
|
}
|
package/macOS
ADDED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poke-gate",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Expose your machine to your Poke AI assistant via MCP tunnel",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node src/app.js",
|
|
11
|
+
"test": "node --test",
|
|
12
|
+
"test:watch": "node --test --watch",
|
|
11
13
|
"lint": "eslint .",
|
|
12
14
|
"lint:fix": "eslint . --fix",
|
|
13
15
|
"lint:md": "remark . --quiet --frail",
|