poke-gate 0.2.0 → 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/Gate.app +0 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/GateService.swift +46 -11
- package/clients/Poke macOS Gate/Poke macOS Gate/Poke_macOS_GateApp.swift +30 -5
- package/clients/Poke macOS Gate/Poke macOS Gate/SettingsView.swift +2 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.pbxproj +5 -5
- package/macOS +0 -0
- package/package.json +1 -1
- package/src/app.js +27 -3
package/Gate.app
ADDED
|
File without changes
|
|
@@ -32,9 +32,9 @@ class GateService: ObservableObject {
|
|
|
32
32
|
|
|
33
33
|
var subtitle: String {
|
|
34
34
|
switch self {
|
|
35
|
-
case .full: return "All tools
|
|
36
|
-
case .limited: return "
|
|
37
|
-
case .sandbox: return "
|
|
35
|
+
case .full: return "All tools enabled. Risky actions require chat approval."
|
|
36
|
+
case .limited: return "Read-only tools and safe commands (ls, cat, grep, curl…). File writes and screenshots disabled."
|
|
37
|
+
case .sandbox: return "Commands like brew, node, python, ffmpeg allowed. Writes restricted to ~/Downloads and /tmp."
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -108,10 +108,26 @@ class GateService: ObservableObject {
|
|
|
108
108
|
private var activeTerminalPreviewId: UUID?
|
|
109
109
|
private var permissionPollingTimer: Timer?
|
|
110
110
|
|
|
111
|
+
private var appActiveObserver: NSObjectProtocol?
|
|
112
|
+
|
|
111
113
|
init() {
|
|
112
114
|
self.permissionMode = Self.loadPermissionModeStatic()
|
|
113
115
|
self.hasCompletedSetup = Self.loadHasCompletedSetupStatic()
|
|
114
116
|
refreshSystemPermissions()
|
|
117
|
+
|
|
118
|
+
appActiveObserver = NotificationCenter.default.addObserver(
|
|
119
|
+
forName: NSApplication.didBecomeActiveNotification,
|
|
120
|
+
object: nil,
|
|
121
|
+
queue: .main
|
|
122
|
+
) { [weak self] _ in
|
|
123
|
+
self?.refreshSystemPermissions()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
deinit {
|
|
128
|
+
if let observer = appActiveObserver {
|
|
129
|
+
NotificationCenter.default.removeObserver(observer)
|
|
130
|
+
}
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
var hasSystemPermissionsGranted: Bool {
|
|
@@ -177,20 +193,24 @@ class GateService: ObservableObject {
|
|
|
177
193
|
}
|
|
178
194
|
|
|
179
195
|
let requested = missingSystemPermissions.map { $0.title }.joined(separator: ", ")
|
|
180
|
-
appendLog("
|
|
196
|
+
appendLog("Requesting macOS permissions: \(requested).")
|
|
181
197
|
|
|
182
198
|
for permission in missingSystemPermissions {
|
|
183
|
-
|
|
184
|
-
NSWorkspace.shared.open(url)
|
|
199
|
+
openSystemPermission(permission)
|
|
185
200
|
}
|
|
186
|
-
|
|
187
|
-
appendLog("Opened Privacy settings for missing permissions.")
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
func refreshSystemPermissions() {
|
|
204
|
+
let previous = systemPermissionStatuses
|
|
191
205
|
systemPermissionStatuses = SystemPermission.allCases.map { permission in
|
|
192
206
|
SystemPermissionStatus(permission: permission, isGranted: isPermissionGranted(permission))
|
|
193
207
|
}
|
|
208
|
+
for status in systemPermissionStatuses {
|
|
209
|
+
let was = previous.first(where: { $0.permission == status.permission })
|
|
210
|
+
if was == nil || was?.isGranted != status.isGranted {
|
|
211
|
+
appendLog("\(status.permission.title): \(status.isGranted ? "granted" : "not granted")")
|
|
212
|
+
}
|
|
213
|
+
}
|
|
194
214
|
}
|
|
195
215
|
|
|
196
216
|
func startPermissionPolling() {
|
|
@@ -208,8 +228,11 @@ class GateService: ObservableObject {
|
|
|
208
228
|
}
|
|
209
229
|
|
|
210
230
|
func openSystemPermission(_ permission: SystemPermission) {
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
switch permission {
|
|
232
|
+
case .accessibility:
|
|
233
|
+
let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary
|
|
234
|
+
_ = AXIsProcessTrustedWithOptions(options)
|
|
235
|
+
}
|
|
213
236
|
}
|
|
214
237
|
|
|
215
238
|
func captureAndSend() {
|
|
@@ -637,7 +660,19 @@ class GateService: ObservableObject {
|
|
|
637
660
|
private func isPermissionGranted(_ permission: SystemPermission) -> Bool {
|
|
638
661
|
switch permission {
|
|
639
662
|
case .accessibility:
|
|
640
|
-
|
|
663
|
+
if AXIsProcessTrusted() { return true }
|
|
664
|
+
// AXIsProcessTrusted() can return false despite the toggle being ON
|
|
665
|
+
// in System Settings when the TCC entry's code signature doesn't match
|
|
666
|
+
// the running binary (ad-hoc signing, rebuild from Xcode, etc.).
|
|
667
|
+
// Probe the API directly as a fallback.
|
|
668
|
+
let systemWide = AXUIElementCreateSystemWide()
|
|
669
|
+
var value: AnyObject?
|
|
670
|
+
let result = AXUIElementCopyAttributeValue(
|
|
671
|
+
systemWide,
|
|
672
|
+
kAXFocusedApplicationAttribute as CFString,
|
|
673
|
+
&value
|
|
674
|
+
)
|
|
675
|
+
return result == .success || result == .noValue || result == .attributeUnsupported
|
|
641
676
|
}
|
|
642
677
|
}
|
|
643
678
|
|
|
@@ -176,11 +176,7 @@ struct PopoverContent: View {
|
|
|
176
176
|
VStack(alignment: .leading, spacing: 6) {
|
|
177
177
|
sectionTitle("Recent activity")
|
|
178
178
|
|
|
179
|
-
if service.terminalPreviews.isEmpty {
|
|
180
|
-
Text("No activity yet")
|
|
181
|
-
.font(.caption)
|
|
182
|
-
.foregroundStyle(.secondary)
|
|
183
|
-
} else {
|
|
179
|
+
if !service.terminalPreviews.isEmpty {
|
|
184
180
|
ForEach(Array(service.terminalPreviews.suffix(4).enumerated()), id: \.element.id) { _, entry in
|
|
185
181
|
HStack(spacing: 6) {
|
|
186
182
|
Circle()
|
|
@@ -193,6 +189,18 @@ struct PopoverContent: View {
|
|
|
193
189
|
.truncationMode(.tail)
|
|
194
190
|
}
|
|
195
191
|
}
|
|
192
|
+
} else if !service.logs.isEmpty {
|
|
193
|
+
ForEach(Array(service.logs.suffix(4).enumerated()), id: \.offset) { _, log in
|
|
194
|
+
Text(log)
|
|
195
|
+
.font(.system(size: 9, design: .monospaced))
|
|
196
|
+
.foregroundStyle(.tertiary)
|
|
197
|
+
.lineLimit(1)
|
|
198
|
+
.truncationMode(.tail)
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
Text("No activity yet")
|
|
202
|
+
.font(.caption)
|
|
203
|
+
.foregroundStyle(.secondary)
|
|
196
204
|
}
|
|
197
205
|
}
|
|
198
206
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
@@ -228,6 +236,11 @@ struct PopoverContent: View {
|
|
|
228
236
|
}
|
|
229
237
|
}
|
|
230
238
|
|
|
239
|
+
Text(activeModeDescription)
|
|
240
|
+
.font(.caption2)
|
|
241
|
+
.foregroundStyle(.secondary)
|
|
242
|
+
.fixedSize(horizontal: false, vertical: true)
|
|
243
|
+
|
|
231
244
|
if service.permissionMode == .full || pendingFullMode {
|
|
232
245
|
AccessibilityPermissionView(service: service)
|
|
233
246
|
}
|
|
@@ -237,6 +250,18 @@ struct PopoverContent: View {
|
|
|
237
250
|
.macPanelStyle(.neutral, cornerRadius: 12)
|
|
238
251
|
}
|
|
239
252
|
|
|
253
|
+
private var activeModeDescription: String {
|
|
254
|
+
let mode = pendingFullMode ? GateService.PermissionMode.full : service.permissionMode
|
|
255
|
+
switch mode {
|
|
256
|
+
case .full:
|
|
257
|
+
return "All tools enabled. Risky actions require chat approval."
|
|
258
|
+
case .limited:
|
|
259
|
+
return "Read-only tools and safe commands only (ls, cat, grep, curl…). File writes and screenshots are disabled."
|
|
260
|
+
case .sandbox:
|
|
261
|
+
return "Broader commands (brew, node, python, ffmpeg…) but file writes restricted to ~/Downloads and /tmp via macOS sandbox."
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
240
265
|
private var actionsSection: some View {
|
|
241
266
|
HStack(spacing: 12) {
|
|
242
267
|
ActionButton(icon: "text.alignleft", label: "Logs") {
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
COPY_PHASE_STRIP = NO;
|
|
175
175
|
DEAD_CODE_STRIPPING = YES;
|
|
176
176
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
177
|
-
DEVELOPMENT_TEAM =
|
|
177
|
+
DEVELOPMENT_TEAM = "";
|
|
178
178
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
179
179
|
ENABLE_TESTABILITY = YES;
|
|
180
180
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
COPY_PHASE_STRIP = NO;
|
|
241
241
|
DEAD_CODE_STRIPPING = YES;
|
|
242
242
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
243
|
-
DEVELOPMENT_TEAM =
|
|
243
|
+
DEVELOPMENT_TEAM = "";
|
|
244
244
|
ENABLE_NS_ASSERTIONS = NO;
|
|
245
245
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
246
246
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
COMBINE_HIDPI_IMAGES = YES;
|
|
273
273
|
CURRENT_PROJECT_VERSION = 1;
|
|
274
274
|
DEAD_CODE_STRIPPING = YES;
|
|
275
|
-
DEVELOPMENT_TEAM =
|
|
275
|
+
DEVELOPMENT_TEAM = "";
|
|
276
276
|
ENABLE_APP_SANDBOX = NO;
|
|
277
277
|
ENABLE_HARDENED_RUNTIME = NO;
|
|
278
278
|
ENABLE_PREVIEWS = YES;
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
"@executable_path/../Frameworks",
|
|
287
287
|
);
|
|
288
288
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
|
289
|
-
MARKETING_VERSION = 0.
|
|
289
|
+
MARKETING_VERSION = 0.2.0;
|
|
290
290
|
PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
|
|
291
291
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
292
292
|
REGISTER_APP_GROUPS = YES;
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
"@executable_path/../Frameworks",
|
|
323
323
|
);
|
|
324
324
|
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
|
325
|
-
MARKETING_VERSION = 0.
|
|
325
|
+
MARKETING_VERSION = 0.2.0;
|
|
326
326
|
PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
|
|
327
327
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
328
328
|
REGISTER_APP_GROUPS = YES;
|
package/macOS
ADDED
|
File without changes
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -115,14 +115,38 @@ async function main() {
|
|
|
115
115
|
await connectWithRetry(mcpUrl, token);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function buildAccessModeMessage(mode) {
|
|
119
|
+
switch (mode) {
|
|
120
|
+
case "limited":
|
|
121
|
+
return (
|
|
122
|
+
"Access mode: Limited. " +
|
|
123
|
+
"You can read files, list directories, and run safe read-only commands (ls, cat, grep, curl, jq…). " +
|
|
124
|
+
"You cannot write files, take screenshots, or run other commands."
|
|
125
|
+
);
|
|
126
|
+
case "sandbox":
|
|
127
|
+
return (
|
|
128
|
+
"Access mode: Sandbox. " +
|
|
129
|
+
"You can read files, list directories, and run commands like brew, node, python, ffmpeg, curl, and more. " +
|
|
130
|
+
"File writes are restricted to ~/Downloads and /tmp by macOS sandbox. Screenshots are disabled."
|
|
131
|
+
);
|
|
132
|
+
default:
|
|
133
|
+
return (
|
|
134
|
+
"Access mode: Full. " +
|
|
135
|
+
"You can run any shell command, read and write files, list directories, take screenshots, and check system info. " +
|
|
136
|
+
"Risky actions (commands, file writes, screenshots) require user approval in chat before execution."
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
118
141
|
async function notifyPoke(connectionId, token) {
|
|
119
142
|
try {
|
|
143
|
+
const mode = getPermissionMode();
|
|
120
144
|
const poke = new Poke({ token });
|
|
121
145
|
await poke.sendMessage(
|
|
122
146
|
`Hey! I've connected my computer to you via Poke Gate (tunnel: ${connectionId}). ` +
|
|
123
|
-
|
|
124
|
-
`Just use the tools whenever I ask you to do something on my computer
|
|
125
|
-
`Now reply me with "now I am connected to your computer" but everytime write those replies in most
|
|
147
|
+
`${buildAccessModeMessage(mode)} ` +
|
|
148
|
+
`Just use the tools whenever I ask you to do something on my computer. ` +
|
|
149
|
+
`Now reply me with "now I am connected to your computer" but everytime write those replies in most creative fun way.`
|
|
126
150
|
);
|
|
127
151
|
log("Notified Poke agent about connection.");
|
|
128
152
|
} catch (err) {
|