poke-gate 0.0.1 → 0.0.3
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/README.md +1 -1
- package/clients/Poke macOS Gate/Poke macOS Gate/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/Assets.xcassets/AppIcon.appiconset/Contents.json +58 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/Assets.xcassets/Contents.json +6 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/ContentView.swift +8 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/GateService.swift +222 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/Info.plist +8 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/Item.swift +2 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/LogsView.swift +47 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/Poke_macOS_GateApp.swift +109 -0
- package/clients/Poke macOS Gate/Poke macOS Gate/SettingsView.swift +50 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.pbxproj +336 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.xcworkspace/xcuserdata/fka.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/xcuserdata/fka.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/package.json +1 -1
- package/src/app.js +4 -1
- package/src/mcp-server.js +120 -14
package/README.md
CHANGED
package/clients/Poke macOS Gate/Poke macOS Gate/Assets.xcassets/AppIcon.appiconset/Contents.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images" : [
|
|
3
|
+
{
|
|
4
|
+
"idiom" : "mac",
|
|
5
|
+
"scale" : "1x",
|
|
6
|
+
"size" : "16x16"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom" : "mac",
|
|
10
|
+
"scale" : "2x",
|
|
11
|
+
"size" : "16x16"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"idiom" : "mac",
|
|
15
|
+
"scale" : "1x",
|
|
16
|
+
"size" : "32x32"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"idiom" : "mac",
|
|
20
|
+
"scale" : "2x",
|
|
21
|
+
"size" : "32x32"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"idiom" : "mac",
|
|
25
|
+
"scale" : "1x",
|
|
26
|
+
"size" : "128x128"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"idiom" : "mac",
|
|
30
|
+
"scale" : "2x",
|
|
31
|
+
"size" : "128x128"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"idiom" : "mac",
|
|
35
|
+
"scale" : "1x",
|
|
36
|
+
"size" : "256x256"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"idiom" : "mac",
|
|
40
|
+
"scale" : "2x",
|
|
41
|
+
"size" : "256x256"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"idiom" : "mac",
|
|
45
|
+
"scale" : "1x",
|
|
46
|
+
"size" : "512x512"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"idiom" : "mac",
|
|
50
|
+
"scale" : "2x",
|
|
51
|
+
"size" : "512x512"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"info" : {
|
|
55
|
+
"author" : "xcode",
|
|
56
|
+
"version" : 1
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Combine
|
|
3
|
+
import ScreenCaptureKit
|
|
4
|
+
|
|
5
|
+
@MainActor
|
|
6
|
+
class GateService: ObservableObject {
|
|
7
|
+
enum Status: String {
|
|
8
|
+
case stopped = "Stopped"
|
|
9
|
+
case starting = "Starting…"
|
|
10
|
+
case connected = "Connected"
|
|
11
|
+
case disconnected = "Disconnected"
|
|
12
|
+
case error = "Error"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Published var status: Status = .stopped
|
|
16
|
+
@Published var logs: [String] = []
|
|
17
|
+
@Published var userName: String? = nil
|
|
18
|
+
|
|
19
|
+
private var hasAutoStarted = false
|
|
20
|
+
private var process: Process?
|
|
21
|
+
private var outputPipe: Pipe?
|
|
22
|
+
private var shouldRestart = true
|
|
23
|
+
private let maxLogs = 200
|
|
24
|
+
|
|
25
|
+
var apiKey: String {
|
|
26
|
+
get { loadAPIKey() ?? "" }
|
|
27
|
+
set { saveAPIKey(newValue) }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var hasAPIKey: Bool {
|
|
31
|
+
let key = loadAPIKey()
|
|
32
|
+
return key != nil && !key!.isEmpty
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func autoStartIfNeeded() {
|
|
36
|
+
guard !hasAutoStarted else { return }
|
|
37
|
+
hasAutoStarted = true
|
|
38
|
+
requestScreenCapturePermission()
|
|
39
|
+
if hasAPIKey {
|
|
40
|
+
start()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private func requestScreenCapturePermission() {
|
|
45
|
+
Task {
|
|
46
|
+
do {
|
|
47
|
+
_ = try await SCShareableContent.current
|
|
48
|
+
} catch {
|
|
49
|
+
appendLog("Screen capture permission not granted yet.")
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func start() {
|
|
55
|
+
guard hasAPIKey else {
|
|
56
|
+
status = .error
|
|
57
|
+
appendLog("No API key configured.")
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
shouldRestart = true
|
|
61
|
+
launchProcess()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func stop() {
|
|
65
|
+
shouldRestart = false
|
|
66
|
+
killProcess()
|
|
67
|
+
status = .stopped
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func restart() {
|
|
71
|
+
stop()
|
|
72
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
73
|
+
self.start()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private func shellPath() -> String {
|
|
78
|
+
let loginShell = ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
|
|
79
|
+
let pathProc = Process()
|
|
80
|
+
let pathPipe = Pipe()
|
|
81
|
+
pathProc.executableURL = URL(fileURLWithPath: loginShell)
|
|
82
|
+
pathProc.arguments = ["-ilc", "echo $PATH"]
|
|
83
|
+
pathProc.standardOutput = pathPipe
|
|
84
|
+
pathProc.standardError = FileHandle.nullDevice
|
|
85
|
+
pathProc.environment = ["HOME": NSHomeDirectory()]
|
|
86
|
+
try? pathProc.run()
|
|
87
|
+
pathProc.waitUntilExit()
|
|
88
|
+
let data = pathPipe.fileHandleForReading.readDataToEndOfFile()
|
|
89
|
+
return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private func launchProcess() {
|
|
93
|
+
killProcess()
|
|
94
|
+
|
|
95
|
+
status = .starting
|
|
96
|
+
appendLog("Starting poke-gate…")
|
|
97
|
+
|
|
98
|
+
let fullPath = shellPath()
|
|
99
|
+
|
|
100
|
+
let proc = Process()
|
|
101
|
+
let pipe = Pipe()
|
|
102
|
+
|
|
103
|
+
proc.executableURL = URL(fileURLWithPath: "/bin/zsh")
|
|
104
|
+
proc.arguments = ["-c", "npx -y poke-gate --verbose"]
|
|
105
|
+
proc.environment = ProcessInfo.processInfo.environment.merging(
|
|
106
|
+
[
|
|
107
|
+
"POKE_API_KEY": loadAPIKey() ?? "",
|
|
108
|
+
"PATH": fullPath,
|
|
109
|
+
],
|
|
110
|
+
uniquingKeysWith: { _, new in new }
|
|
111
|
+
)
|
|
112
|
+
proc.standardOutput = pipe
|
|
113
|
+
proc.standardError = pipe
|
|
114
|
+
proc.currentDirectoryURL = FileManager.default.homeDirectoryForCurrentUser
|
|
115
|
+
|
|
116
|
+
let handle = pipe.fileHandleForReading
|
|
117
|
+
handle.readabilityHandler = { [weak self] fh in
|
|
118
|
+
let data = fh.availableData
|
|
119
|
+
guard !data.isEmpty, let line = String(data: data, encoding: .utf8) else { return }
|
|
120
|
+
DispatchQueue.main.async {
|
|
121
|
+
self?.handleOutput(line)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
proc.terminationHandler = { [weak self] proc in
|
|
126
|
+
DispatchQueue.main.async {
|
|
127
|
+
self?.handleTermination(exitCode: proc.terminationStatus)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
do {
|
|
132
|
+
try proc.run()
|
|
133
|
+
self.process = proc
|
|
134
|
+
self.outputPipe = pipe
|
|
135
|
+
} catch {
|
|
136
|
+
status = .error
|
|
137
|
+
appendLog("Failed to start: \(error.localizedDescription)")
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private func killProcess() {
|
|
142
|
+
if let proc = process, proc.isRunning {
|
|
143
|
+
proc.terminate()
|
|
144
|
+
}
|
|
145
|
+
outputPipe?.fileHandleForReading.readabilityHandler = nil
|
|
146
|
+
process = nil
|
|
147
|
+
outputPipe = nil
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private func handleOutput(_ raw: String) {
|
|
151
|
+
for line in raw.components(separatedBy: .newlines) where !line.isEmpty {
|
|
152
|
+
appendLog(line)
|
|
153
|
+
|
|
154
|
+
if line.contains("Tunnel connected") || line.contains("Ready") {
|
|
155
|
+
status = .connected
|
|
156
|
+
} else if line.contains("Tunnel disconnected") || line.contains("Reconnecting") {
|
|
157
|
+
status = .disconnected
|
|
158
|
+
} else if line.contains("Failed to connect") || line.contains("error") {
|
|
159
|
+
if status != .connected {
|
|
160
|
+
status = .error
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private func handleTermination(exitCode: Int32) {
|
|
167
|
+
appendLog("Process exited with code \(exitCode)")
|
|
168
|
+
if shouldRestart {
|
|
169
|
+
status = .disconnected
|
|
170
|
+
appendLog("Restarting in 2 seconds…")
|
|
171
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
172
|
+
if self.shouldRestart {
|
|
173
|
+
self.launchProcess()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
status = .stopped
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private func appendLog(_ line: String) {
|
|
182
|
+
let ts = DateFormatter.localizedString(from: Date(), dateStyle: .none, timeStyle: .medium)
|
|
183
|
+
logs.append("[\(ts)] \(line)")
|
|
184
|
+
if logs.count > maxLogs {
|
|
185
|
+
logs.removeFirst(logs.count - maxLogs)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// MARK: - Config
|
|
190
|
+
|
|
191
|
+
private var configURL: URL {
|
|
192
|
+
let configDir: URL
|
|
193
|
+
if let xdg = ProcessInfo.processInfo.environment["XDG_CONFIG_HOME"] {
|
|
194
|
+
configDir = URL(fileURLWithPath: xdg)
|
|
195
|
+
} else {
|
|
196
|
+
configDir = FileManager.default.homeDirectoryForCurrentUser
|
|
197
|
+
.appendingPathComponent(".config")
|
|
198
|
+
}
|
|
199
|
+
return configDir
|
|
200
|
+
.appendingPathComponent("poke-gate")
|
|
201
|
+
.appendingPathComponent("config.json")
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private func loadAPIKey() -> String? {
|
|
205
|
+
guard let data = try? Data(contentsOf: configURL),
|
|
206
|
+
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
207
|
+
let key = json["apiKey"] as? String else {
|
|
208
|
+
return nil
|
|
209
|
+
}
|
|
210
|
+
return key
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private func saveAPIKey(_ key: String) {
|
|
214
|
+
let dir = configURL.deletingLastPathComponent()
|
|
215
|
+
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
|
216
|
+
let json: [String: Any] = ["apiKey": key]
|
|
217
|
+
if let data = try? JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted]) {
|
|
218
|
+
try? data.write(to: configURL)
|
|
219
|
+
}
|
|
220
|
+
objectWillChange.send()
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
struct LogsView: View {
|
|
4
|
+
@ObservedObject var service: GateService
|
|
5
|
+
|
|
6
|
+
var body: some View {
|
|
7
|
+
VStack(spacing: 0) {
|
|
8
|
+
HStack {
|
|
9
|
+
Text("Logs")
|
|
10
|
+
.font(.headline)
|
|
11
|
+
Spacer()
|
|
12
|
+
Button("Clear") {
|
|
13
|
+
service.logs.removeAll()
|
|
14
|
+
}
|
|
15
|
+
.buttonStyle(.plain)
|
|
16
|
+
.foregroundStyle(.secondary)
|
|
17
|
+
.font(.caption)
|
|
18
|
+
}
|
|
19
|
+
.padding(.horizontal, 12)
|
|
20
|
+
.padding(.vertical, 8)
|
|
21
|
+
|
|
22
|
+
Divider()
|
|
23
|
+
|
|
24
|
+
ScrollViewReader { proxy in
|
|
25
|
+
ScrollView {
|
|
26
|
+
LazyVStack(alignment: .leading, spacing: 2) {
|
|
27
|
+
ForEach(Array(service.logs.enumerated()), id: \.offset) { index, line in
|
|
28
|
+
Text(line)
|
|
29
|
+
.font(.system(.caption, design: .monospaced))
|
|
30
|
+
.foregroundStyle(.primary)
|
|
31
|
+
.textSelection(.enabled)
|
|
32
|
+
.id(index)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
.padding(.horizontal, 12)
|
|
36
|
+
.padding(.vertical, 8)
|
|
37
|
+
}
|
|
38
|
+
.onChange(of: service.logs.count) { _, _ in
|
|
39
|
+
if let last = service.logs.indices.last {
|
|
40
|
+
proxy.scrollTo(last, anchor: .bottom)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
.frame(width: 480, height: 320)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
@main
|
|
4
|
+
struct Poke_macOS_GateApp: App {
|
|
5
|
+
@StateObject private var service = GateService()
|
|
6
|
+
|
|
7
|
+
var body: some Scene {
|
|
8
|
+
MenuBarExtra {
|
|
9
|
+
MenuBarContent(service: service)
|
|
10
|
+
.onAppear { service.autoStartIfNeeded() }
|
|
11
|
+
} label: {
|
|
12
|
+
Image(systemName: menuBarIcon)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Window("Logs", id: "logs") {
|
|
16
|
+
LogsView(service: service)
|
|
17
|
+
}
|
|
18
|
+
.defaultSize(width: 480, height: 320)
|
|
19
|
+
|
|
20
|
+
Window("Settings", id: "settings") {
|
|
21
|
+
SettingsView(service: service)
|
|
22
|
+
}
|
|
23
|
+
.windowResizability(.contentSize)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private var menuBarIcon: String {
|
|
27
|
+
switch service.status {
|
|
28
|
+
case .connected: "door.left.hand.open"
|
|
29
|
+
case .starting, .disconnected: "door.left.hand.closed"
|
|
30
|
+
case .error: "exclamationmark.triangle"
|
|
31
|
+
case .stopped: "door.left.hand.closed"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
struct MenuBarContent: View {
|
|
37
|
+
@ObservedObject var service: GateService
|
|
38
|
+
@Environment(\.openWindow) private var openWindow
|
|
39
|
+
|
|
40
|
+
var body: some View {
|
|
41
|
+
Label(service.status.rawValue, systemImage: statusIcon)
|
|
42
|
+
.foregroundStyle(statusColor)
|
|
43
|
+
|
|
44
|
+
Divider()
|
|
45
|
+
|
|
46
|
+
Button("View Logs…") {
|
|
47
|
+
NSApp.activate(ignoringOtherApps: true)
|
|
48
|
+
openWindow(id: "logs")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Button("Settings…") {
|
|
52
|
+
NSApp.activate(ignoringOtherApps: true)
|
|
53
|
+
openWindow(id: "settings")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Divider()
|
|
57
|
+
|
|
58
|
+
if service.status == .connected || service.status == .starting || service.status == .disconnected {
|
|
59
|
+
Button("Restart") {
|
|
60
|
+
service.restart()
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
Button("Start") {
|
|
64
|
+
service.start()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Divider()
|
|
69
|
+
|
|
70
|
+
Button("Quit Poke Gate") {
|
|
71
|
+
service.stop()
|
|
72
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
73
|
+
NSApp.terminate(nil)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
.keyboardShortcut("q")
|
|
77
|
+
|
|
78
|
+
Divider()
|
|
79
|
+
|
|
80
|
+
Text("Poke Gate v0.0.3")
|
|
81
|
+
.font(.caption)
|
|
82
|
+
.foregroundStyle(.secondary)
|
|
83
|
+
Text("Community project — not affiliated with Poke")
|
|
84
|
+
.font(.caption2)
|
|
85
|
+
.foregroundStyle(.secondary)
|
|
86
|
+
Button("GitHub") {
|
|
87
|
+
NSWorkspace.shared.open(URL(string: "https://github.com/f/poke-gate")!)
|
|
88
|
+
}
|
|
89
|
+
.font(.caption)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private var statusIcon: String {
|
|
93
|
+
switch service.status {
|
|
94
|
+
case .connected: "circle.fill"
|
|
95
|
+
case .starting, .disconnected: "circle.dotted"
|
|
96
|
+
case .error: "exclamationmark.circle.fill"
|
|
97
|
+
case .stopped: "circle"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private var statusColor: Color {
|
|
102
|
+
switch service.status {
|
|
103
|
+
case .connected: .green
|
|
104
|
+
case .starting, .disconnected: .yellow
|
|
105
|
+
case .error: .red
|
|
106
|
+
case .stopped: .secondary
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
struct SettingsView: View {
|
|
4
|
+
@ObservedObject var service: GateService
|
|
5
|
+
@State private var apiKeyInput: String = ""
|
|
6
|
+
@Environment(\.dismiss) private var dismiss
|
|
7
|
+
|
|
8
|
+
var body: some View {
|
|
9
|
+
VStack(spacing: 16) {
|
|
10
|
+
Text("Poke Gate Settings")
|
|
11
|
+
.font(.headline)
|
|
12
|
+
|
|
13
|
+
VStack(alignment: .leading, spacing: 8) {
|
|
14
|
+
Text("API Key")
|
|
15
|
+
.font(.subheadline)
|
|
16
|
+
.foregroundStyle(.secondary)
|
|
17
|
+
|
|
18
|
+
SecureField("Paste your API key", text: $apiKeyInput)
|
|
19
|
+
.textFieldStyle(.roundedBorder)
|
|
20
|
+
|
|
21
|
+
Link("Get your key at poke.com/kitchen/api-keys",
|
|
22
|
+
destination: URL(string: "https://poke.com/kitchen/api-keys")!)
|
|
23
|
+
.font(.caption)
|
|
24
|
+
.foregroundStyle(.blue)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
HStack {
|
|
28
|
+
Button("Cancel") {
|
|
29
|
+
dismiss()
|
|
30
|
+
}
|
|
31
|
+
.keyboardShortcut(.cancelAction)
|
|
32
|
+
|
|
33
|
+
Spacer()
|
|
34
|
+
|
|
35
|
+
Button("Save") {
|
|
36
|
+
service.apiKey = apiKeyInput
|
|
37
|
+
dismiss()
|
|
38
|
+
service.restart()
|
|
39
|
+
}
|
|
40
|
+
.keyboardShortcut(.defaultAction)
|
|
41
|
+
.disabled(apiKeyInput.isEmpty)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
.padding(20)
|
|
45
|
+
.frame(width: 360)
|
|
46
|
+
.onAppear {
|
|
47
|
+
apiKeyInput = service.apiKey
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// !$*UTF8*$!
|
|
2
|
+
{
|
|
3
|
+
archiveVersion = 1;
|
|
4
|
+
classes = {
|
|
5
|
+
};
|
|
6
|
+
objectVersion = 77;
|
|
7
|
+
objects = {
|
|
8
|
+
|
|
9
|
+
/* Begin PBXFileReference section */
|
|
10
|
+
442DE4462F71DCD9009BF9EF /* Poke macOS Gate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Poke macOS Gate.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
11
|
+
/* End PBXFileReference section */
|
|
12
|
+
|
|
13
|
+
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
|
14
|
+
442DE4482F71DCD9009BF9EF /* Poke macOS Gate */ = {
|
|
15
|
+
isa = PBXFileSystemSynchronizedRootGroup;
|
|
16
|
+
path = "Poke macOS Gate";
|
|
17
|
+
sourceTree = "<group>";
|
|
18
|
+
};
|
|
19
|
+
/* End PBXFileSystemSynchronizedRootGroup section */
|
|
20
|
+
|
|
21
|
+
/* Begin PBXFrameworksBuildPhase section */
|
|
22
|
+
442DE4432F71DCD9009BF9EF /* Frameworks */ = {
|
|
23
|
+
isa = PBXFrameworksBuildPhase;
|
|
24
|
+
buildActionMask = 2147483647;
|
|
25
|
+
files = (
|
|
26
|
+
);
|
|
27
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
28
|
+
};
|
|
29
|
+
/* End PBXFrameworksBuildPhase section */
|
|
30
|
+
|
|
31
|
+
/* Begin PBXGroup section */
|
|
32
|
+
442DE43D2F71DCD9009BF9EF = {
|
|
33
|
+
isa = PBXGroup;
|
|
34
|
+
children = (
|
|
35
|
+
442DE4482F71DCD9009BF9EF /* Poke macOS Gate */,
|
|
36
|
+
442DE4472F71DCD9009BF9EF /* Products */,
|
|
37
|
+
);
|
|
38
|
+
sourceTree = "<group>";
|
|
39
|
+
};
|
|
40
|
+
442DE4472F71DCD9009BF9EF /* Products */ = {
|
|
41
|
+
isa = PBXGroup;
|
|
42
|
+
children = (
|
|
43
|
+
442DE4462F71DCD9009BF9EF /* Poke macOS Gate.app */,
|
|
44
|
+
);
|
|
45
|
+
name = Products;
|
|
46
|
+
sourceTree = "<group>";
|
|
47
|
+
};
|
|
48
|
+
/* End PBXGroup section */
|
|
49
|
+
|
|
50
|
+
/* Begin PBXNativeTarget section */
|
|
51
|
+
442DE4452F71DCD9009BF9EF /* Poke macOS Gate */ = {
|
|
52
|
+
isa = PBXNativeTarget;
|
|
53
|
+
buildConfigurationList = 442DE4532F71DCDA009BF9EF /* Build configuration list for PBXNativeTarget "Poke macOS Gate" */;
|
|
54
|
+
buildPhases = (
|
|
55
|
+
442DE4422F71DCD9009BF9EF /* Sources */,
|
|
56
|
+
442DE4432F71DCD9009BF9EF /* Frameworks */,
|
|
57
|
+
442DE4442F71DCD9009BF9EF /* Resources */,
|
|
58
|
+
);
|
|
59
|
+
buildRules = (
|
|
60
|
+
);
|
|
61
|
+
dependencies = (
|
|
62
|
+
);
|
|
63
|
+
fileSystemSynchronizedGroups = (
|
|
64
|
+
442DE4482F71DCD9009BF9EF /* Poke macOS Gate */,
|
|
65
|
+
);
|
|
66
|
+
name = "Poke macOS Gate";
|
|
67
|
+
packageProductDependencies = (
|
|
68
|
+
);
|
|
69
|
+
productName = "Poke macOS Gate";
|
|
70
|
+
productReference = 442DE4462F71DCD9009BF9EF /* Poke macOS Gate.app */;
|
|
71
|
+
productType = "com.apple.product-type.application";
|
|
72
|
+
};
|
|
73
|
+
/* End PBXNativeTarget section */
|
|
74
|
+
|
|
75
|
+
/* Begin PBXProject section */
|
|
76
|
+
442DE43E2F71DCD9009BF9EF /* Project object */ = {
|
|
77
|
+
isa = PBXProject;
|
|
78
|
+
attributes = {
|
|
79
|
+
BuildIndependentTargetsInParallel = 1;
|
|
80
|
+
LastSwiftUpdateCheck = 2620;
|
|
81
|
+
LastUpgradeCheck = 2620;
|
|
82
|
+
TargetAttributes = {
|
|
83
|
+
442DE4452F71DCD9009BF9EF = {
|
|
84
|
+
CreatedOnToolsVersion = 26.2;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
buildConfigurationList = 442DE4412F71DCD9009BF9EF /* Build configuration list for PBXProject "Poke macOS Gate" */;
|
|
89
|
+
developmentRegion = en;
|
|
90
|
+
hasScannedForEncodings = 0;
|
|
91
|
+
knownRegions = (
|
|
92
|
+
en,
|
|
93
|
+
Base,
|
|
94
|
+
);
|
|
95
|
+
mainGroup = 442DE43D2F71DCD9009BF9EF;
|
|
96
|
+
minimizedProjectReferenceProxies = 1;
|
|
97
|
+
preferredProjectObjectVersion = 77;
|
|
98
|
+
productRefGroup = 442DE4472F71DCD9009BF9EF /* Products */;
|
|
99
|
+
projectDirPath = "";
|
|
100
|
+
projectRoot = "";
|
|
101
|
+
targets = (
|
|
102
|
+
442DE4452F71DCD9009BF9EF /* Poke macOS Gate */,
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
/* End PBXProject section */
|
|
106
|
+
|
|
107
|
+
/* Begin PBXResourcesBuildPhase section */
|
|
108
|
+
442DE4442F71DCD9009BF9EF /* Resources */ = {
|
|
109
|
+
isa = PBXResourcesBuildPhase;
|
|
110
|
+
buildActionMask = 2147483647;
|
|
111
|
+
files = (
|
|
112
|
+
);
|
|
113
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
114
|
+
};
|
|
115
|
+
/* End PBXResourcesBuildPhase section */
|
|
116
|
+
|
|
117
|
+
/* Begin PBXSourcesBuildPhase section */
|
|
118
|
+
442DE4422F71DCD9009BF9EF /* Sources */ = {
|
|
119
|
+
isa = PBXSourcesBuildPhase;
|
|
120
|
+
buildActionMask = 2147483647;
|
|
121
|
+
files = (
|
|
122
|
+
);
|
|
123
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
124
|
+
};
|
|
125
|
+
/* End PBXSourcesBuildPhase section */
|
|
126
|
+
|
|
127
|
+
/* Begin XCBuildConfiguration section */
|
|
128
|
+
442DE4512F71DCDA009BF9EF /* Debug */ = {
|
|
129
|
+
isa = XCBuildConfiguration;
|
|
130
|
+
buildSettings = {
|
|
131
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
132
|
+
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
|
133
|
+
CLANG_ANALYZER_NONNULL = YES;
|
|
134
|
+
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
135
|
+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
136
|
+
CLANG_ENABLE_MODULES = YES;
|
|
137
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
138
|
+
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
139
|
+
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
140
|
+
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
141
|
+
CLANG_WARN_COMMA = YES;
|
|
142
|
+
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
143
|
+
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
144
|
+
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
145
|
+
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
146
|
+
CLANG_WARN_EMPTY_BODY = YES;
|
|
147
|
+
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
148
|
+
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
149
|
+
CLANG_WARN_INT_CONVERSION = YES;
|
|
150
|
+
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
151
|
+
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
152
|
+
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
153
|
+
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
154
|
+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
155
|
+
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
156
|
+
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
157
|
+
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
158
|
+
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
159
|
+
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
160
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
161
|
+
COPY_PHASE_STRIP = NO;
|
|
162
|
+
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
163
|
+
DEVELOPMENT_TEAM = RJA7656U34;
|
|
164
|
+
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
165
|
+
ENABLE_TESTABILITY = YES;
|
|
166
|
+
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
167
|
+
GCC_C_LANGUAGE_STANDARD = gnu17;
|
|
168
|
+
GCC_DYNAMIC_NO_PIC = NO;
|
|
169
|
+
GCC_NO_COMMON_BLOCKS = YES;
|
|
170
|
+
GCC_OPTIMIZATION_LEVEL = 0;
|
|
171
|
+
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
172
|
+
"DEBUG=1",
|
|
173
|
+
"$(inherited)",
|
|
174
|
+
);
|
|
175
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
176
|
+
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
177
|
+
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
178
|
+
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
179
|
+
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
180
|
+
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
181
|
+
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
182
|
+
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
|
183
|
+
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
184
|
+
MTL_FAST_MATH = YES;
|
|
185
|
+
ONLY_ACTIVE_ARCH = YES;
|
|
186
|
+
SDKROOT = macosx;
|
|
187
|
+
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
|
188
|
+
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
189
|
+
};
|
|
190
|
+
name = Debug;
|
|
191
|
+
};
|
|
192
|
+
442DE4522F71DCDA009BF9EF /* Release */ = {
|
|
193
|
+
isa = XCBuildConfiguration;
|
|
194
|
+
buildSettings = {
|
|
195
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
196
|
+
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
|
197
|
+
CLANG_ANALYZER_NONNULL = YES;
|
|
198
|
+
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
199
|
+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
200
|
+
CLANG_ENABLE_MODULES = YES;
|
|
201
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
202
|
+
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
203
|
+
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
204
|
+
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
205
|
+
CLANG_WARN_COMMA = YES;
|
|
206
|
+
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
207
|
+
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
208
|
+
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
209
|
+
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
210
|
+
CLANG_WARN_EMPTY_BODY = YES;
|
|
211
|
+
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
212
|
+
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
213
|
+
CLANG_WARN_INT_CONVERSION = YES;
|
|
214
|
+
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
215
|
+
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
216
|
+
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
217
|
+
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
218
|
+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
219
|
+
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
220
|
+
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
221
|
+
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
222
|
+
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
223
|
+
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
224
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
225
|
+
COPY_PHASE_STRIP = NO;
|
|
226
|
+
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
227
|
+
DEVELOPMENT_TEAM = RJA7656U34;
|
|
228
|
+
ENABLE_NS_ASSERTIONS = NO;
|
|
229
|
+
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
230
|
+
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
231
|
+
GCC_C_LANGUAGE_STANDARD = gnu17;
|
|
232
|
+
GCC_NO_COMMON_BLOCKS = YES;
|
|
233
|
+
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
234
|
+
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
235
|
+
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
236
|
+
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
237
|
+
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
238
|
+
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
239
|
+
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
240
|
+
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
|
241
|
+
MTL_ENABLE_DEBUG_INFO = NO;
|
|
242
|
+
MTL_FAST_MATH = YES;
|
|
243
|
+
SDKROOT = macosx;
|
|
244
|
+
SWIFT_COMPILATION_MODE = wholemodule;
|
|
245
|
+
};
|
|
246
|
+
name = Release;
|
|
247
|
+
};
|
|
248
|
+
442DE4542F71DCDA009BF9EF /* Debug */ = {
|
|
249
|
+
isa = XCBuildConfiguration;
|
|
250
|
+
buildSettings = {
|
|
251
|
+
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
252
|
+
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
|
253
|
+
CODE_SIGN_STYLE = Automatic;
|
|
254
|
+
COMBINE_HIDPI_IMAGES = YES;
|
|
255
|
+
CURRENT_PROJECT_VERSION = 1;
|
|
256
|
+
DEVELOPMENT_TEAM = RJA7656U34;
|
|
257
|
+
ENABLE_APP_SANDBOX = NO;
|
|
258
|
+
ENABLE_HARDENED_RUNTIME = NO;
|
|
259
|
+
ENABLE_PREVIEWS = YES;
|
|
260
|
+
GENERATE_INFOPLIST_FILE = YES;
|
|
261
|
+
INFOPLIST_FILE = "Poke macOS Gate/Info.plist";
|
|
262
|
+
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
|
263
|
+
LD_RUNPATH_SEARCH_PATHS = (
|
|
264
|
+
"$(inherited)",
|
|
265
|
+
"@executable_path/../Frameworks",
|
|
266
|
+
);
|
|
267
|
+
MARKETING_VERSION = 1.0;
|
|
268
|
+
PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
|
|
269
|
+
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
270
|
+
REGISTER_APP_GROUPS = YES;
|
|
271
|
+
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
|
272
|
+
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
|
273
|
+
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
|
274
|
+
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
275
|
+
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
|
276
|
+
SWIFT_VERSION = 5.0;
|
|
277
|
+
};
|
|
278
|
+
name = Debug;
|
|
279
|
+
};
|
|
280
|
+
442DE4552F71DCDA009BF9EF /* Release */ = {
|
|
281
|
+
isa = XCBuildConfiguration;
|
|
282
|
+
buildSettings = {
|
|
283
|
+
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
284
|
+
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
|
285
|
+
CODE_SIGN_STYLE = Automatic;
|
|
286
|
+
COMBINE_HIDPI_IMAGES = YES;
|
|
287
|
+
CURRENT_PROJECT_VERSION = 1;
|
|
288
|
+
DEVELOPMENT_TEAM = RJA7656U34;
|
|
289
|
+
ENABLE_APP_SANDBOX = NO;
|
|
290
|
+
ENABLE_HARDENED_RUNTIME = NO;
|
|
291
|
+
ENABLE_PREVIEWS = YES;
|
|
292
|
+
GENERATE_INFOPLIST_FILE = YES;
|
|
293
|
+
INFOPLIST_FILE = "Poke macOS Gate/Info.plist";
|
|
294
|
+
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
|
295
|
+
LD_RUNPATH_SEARCH_PATHS = (
|
|
296
|
+
"$(inherited)",
|
|
297
|
+
"@executable_path/../Frameworks",
|
|
298
|
+
);
|
|
299
|
+
MARKETING_VERSION = 1.0;
|
|
300
|
+
PRODUCT_BUNDLE_IDENTIFIER = "dev.fka.Poke-macOS-Gate";
|
|
301
|
+
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
302
|
+
REGISTER_APP_GROUPS = YES;
|
|
303
|
+
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
|
304
|
+
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
|
305
|
+
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
|
306
|
+
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
307
|
+
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
|
308
|
+
SWIFT_VERSION = 5.0;
|
|
309
|
+
};
|
|
310
|
+
name = Release;
|
|
311
|
+
};
|
|
312
|
+
/* End XCBuildConfiguration section */
|
|
313
|
+
|
|
314
|
+
/* Begin XCConfigurationList section */
|
|
315
|
+
442DE4412F71DCD9009BF9EF /* Build configuration list for PBXProject "Poke macOS Gate" */ = {
|
|
316
|
+
isa = XCConfigurationList;
|
|
317
|
+
buildConfigurations = (
|
|
318
|
+
442DE4512F71DCDA009BF9EF /* Debug */,
|
|
319
|
+
442DE4522F71DCDA009BF9EF /* Release */,
|
|
320
|
+
);
|
|
321
|
+
defaultConfigurationIsVisible = 0;
|
|
322
|
+
defaultConfigurationName = Release;
|
|
323
|
+
};
|
|
324
|
+
442DE4532F71DCDA009BF9EF /* Build configuration list for PBXNativeTarget "Poke macOS Gate" */ = {
|
|
325
|
+
isa = XCConfigurationList;
|
|
326
|
+
buildConfigurations = (
|
|
327
|
+
442DE4542F71DCDA009BF9EF /* Debug */,
|
|
328
|
+
442DE4552F71DCDA009BF9EF /* Release */,
|
|
329
|
+
);
|
|
330
|
+
defaultConfigurationIsVisible = 0;
|
|
331
|
+
defaultConfigurationName = Release;
|
|
332
|
+
};
|
|
333
|
+
/* End XCConfigurationList section */
|
|
334
|
+
};
|
|
335
|
+
rootObject = 442DE43E2F71DCD9009BF9EF /* Project object */;
|
|
336
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>Poke macOS Gate.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { startMcpServer } from "./mcp-server.js";
|
|
1
|
+
import { startMcpServer, enableLogging } from "./mcp-server.js";
|
|
2
2
|
import { startTunnel } from "./tunnel.js";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
|
|
7
|
+
const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
8
|
+
enableLogging(verbose);
|
|
9
|
+
|
|
7
10
|
function resolveToken() {
|
|
8
11
|
if (process.env.POKE_API_KEY) return process.env.POKE_API_KEY;
|
|
9
12
|
|
package/src/mcp-server.js
CHANGED
|
@@ -2,12 +2,29 @@ import http from "node:http";
|
|
|
2
2
|
import { execSync, exec } from "node:child_process";
|
|
3
3
|
import { readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { hostname, platform, arch, uptime, totalmem, freemem, homedir } from "node:os";
|
|
5
|
-
import { join, resolve } from "node:path";
|
|
5
|
+
import { join, resolve, extname } from "node:path";
|
|
6
6
|
|
|
7
7
|
const SERVER_INFO = { name: "poke-gate", version: "0.0.1" };
|
|
8
8
|
|
|
9
9
|
const COMMAND_TIMEOUT = 30_000;
|
|
10
10
|
|
|
11
|
+
let logEnabled = false;
|
|
12
|
+
|
|
13
|
+
export function enableLogging(enabled) {
|
|
14
|
+
logEnabled = enabled;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function logTool(name, args, result) {
|
|
18
|
+
if (!logEnabled) return;
|
|
19
|
+
const ts = new Date().toISOString().slice(11, 19);
|
|
20
|
+
console.log(`[${ts}] tool: ${name}`);
|
|
21
|
+
if (name === "run_command") console.log(`[${ts}] $ ${args.command}${args.cwd ? ` (in ${args.cwd})` : ""}`);
|
|
22
|
+
else if (name === "read_file") console.log(`[${ts}] read: ${args.path}`);
|
|
23
|
+
else if (name === "write_file") console.log(`[${ts}] write: ${args.path}`);
|
|
24
|
+
else if (name === "list_directory") console.log(`[${ts}] ls: ${args.path || "~"}`);
|
|
25
|
+
if (result?.isError) console.log(`[${ts}] error`);
|
|
26
|
+
}
|
|
27
|
+
|
|
11
28
|
const TOOLS = [
|
|
12
29
|
{
|
|
13
30
|
name: "run_command",
|
|
@@ -62,6 +79,30 @@ const TOOLS = [
|
|
|
62
79
|
description: "Get system information: OS, hostname, architecture, uptime, memory, and home directory.",
|
|
63
80
|
inputSchema: { type: "object", properties: {} },
|
|
64
81
|
},
|
|
82
|
+
{
|
|
83
|
+
name: "read_image",
|
|
84
|
+
description:
|
|
85
|
+
"Read an image or binary file and return it as base64-encoded data. " +
|
|
86
|
+
"Supports png, jpg, jpeg, gif, webp, pdf, and any other binary file. " +
|
|
87
|
+
"Returns the base64 string and MIME type.",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
path: { type: "string", description: "Absolute or relative path to the image/binary file" },
|
|
92
|
+
},
|
|
93
|
+
required: ["path"],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "take_screenshot",
|
|
98
|
+
description: "Take a screenshot of the user's screen and save it to a file. Returns the file path. Requires screen recording permission on macOS.",
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
path: { type: "string", description: "File path to save the screenshot (optional, defaults to ~/Desktop/screenshot-<timestamp>.png)" },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
65
106
|
];
|
|
66
107
|
|
|
67
108
|
function runCommand(command, cwd) {
|
|
@@ -85,18 +126,26 @@ function runCommand(command, cwd) {
|
|
|
85
126
|
function handleToolCall(name, args) {
|
|
86
127
|
switch (name) {
|
|
87
128
|
case "run_command": {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
129
|
+
logTool(name, args);
|
|
130
|
+
return runCommand(args.command, args.cwd).then((result) => {
|
|
131
|
+
const r = { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
132
|
+
if (result.exitCode !== 0) r.isError = true;
|
|
133
|
+
logTool(name, args, r);
|
|
134
|
+
return r;
|
|
135
|
+
});
|
|
91
136
|
}
|
|
92
137
|
|
|
93
138
|
case "read_file": {
|
|
94
139
|
try {
|
|
95
140
|
const p = resolve(args.path.replace(/^~/, homedir()));
|
|
96
141
|
const text = readFileSync(p, "utf-8");
|
|
97
|
-
|
|
142
|
+
const r = { content: [{ type: "text", text: text.slice(0, 100_000) }] };
|
|
143
|
+
logTool(name, args, r);
|
|
144
|
+
return r;
|
|
98
145
|
} catch (err) {
|
|
99
|
-
|
|
146
|
+
const r = { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
147
|
+
logTool(name, args, r);
|
|
148
|
+
return r;
|
|
100
149
|
}
|
|
101
150
|
}
|
|
102
151
|
|
|
@@ -104,26 +153,34 @@ function handleToolCall(name, args) {
|
|
|
104
153
|
try {
|
|
105
154
|
const p = resolve(args.path.replace(/^~/, homedir()));
|
|
106
155
|
writeFileSync(p, args.content);
|
|
107
|
-
|
|
156
|
+
const r = { content: [{ type: "text", text: `Written to ${p}` }] };
|
|
157
|
+
logTool(name, args, r);
|
|
158
|
+
return r;
|
|
108
159
|
} catch (err) {
|
|
109
|
-
|
|
160
|
+
const r = { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
161
|
+
logTool(name, args, r);
|
|
162
|
+
return r;
|
|
110
163
|
}
|
|
111
164
|
}
|
|
112
165
|
|
|
113
166
|
case "list_directory": {
|
|
114
167
|
try {
|
|
115
168
|
const dir = resolve((args.path || "~").replace(/^~/, homedir()));
|
|
116
|
-
const entries = readdirSync(dir).map((
|
|
169
|
+
const entries = readdirSync(dir).map((entry) => {
|
|
117
170
|
try {
|
|
118
|
-
const s = statSync(join(dir,
|
|
119
|
-
return `${s.isDirectory() ? "d" : "-"} ${
|
|
171
|
+
const s = statSync(join(dir, entry));
|
|
172
|
+
return `${s.isDirectory() ? "d" : "-"} ${entry}`;
|
|
120
173
|
} catch {
|
|
121
|
-
return `? ${
|
|
174
|
+
return `? ${entry}`;
|
|
122
175
|
}
|
|
123
176
|
});
|
|
124
|
-
|
|
177
|
+
const r = { content: [{ type: "text", text: entries.join("\n") }] };
|
|
178
|
+
logTool(name, args, r);
|
|
179
|
+
return r;
|
|
125
180
|
} catch (err) {
|
|
126
|
-
|
|
181
|
+
const r = { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
182
|
+
logTool(name, args, r);
|
|
183
|
+
return r;
|
|
127
184
|
}
|
|
128
185
|
}
|
|
129
186
|
|
|
@@ -138,9 +195,58 @@ function handleToolCall(name, args) {
|
|
|
138
195
|
homeDir: homedir(),
|
|
139
196
|
nodeVersion: process.version,
|
|
140
197
|
};
|
|
198
|
+
logTool(name, args);
|
|
141
199
|
return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
|
|
142
200
|
}
|
|
143
201
|
|
|
202
|
+
case "read_image": {
|
|
203
|
+
try {
|
|
204
|
+
const p = resolve(args.path.replace(/^~/, homedir()));
|
|
205
|
+
const ext = extname(p).toLowerCase().slice(1);
|
|
206
|
+
const mimeMap = {
|
|
207
|
+
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
|
|
208
|
+
gif: "image/gif", webp: "image/webp", svg: "image/svg+xml",
|
|
209
|
+
pdf: "application/pdf", ico: "image/x-icon", bmp: "image/bmp",
|
|
210
|
+
};
|
|
211
|
+
const mimeType = mimeMap[ext] || "application/octet-stream";
|
|
212
|
+
const buf = readFileSync(p);
|
|
213
|
+
const base64 = buf.toString("base64");
|
|
214
|
+
logTool(name, args);
|
|
215
|
+
|
|
216
|
+
if (mimeType.startsWith("image/")) {
|
|
217
|
+
return {
|
|
218
|
+
content: [
|
|
219
|
+
{ type: "image", data: base64, mimeType },
|
|
220
|
+
{ type: "text", text: `Image: ${p} (${mimeType}, ${buf.length} bytes)` },
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
content: [
|
|
226
|
+
{ type: "text", text: `File: ${p} (${mimeType}, ${buf.length} bytes)\nBase64: ${base64.slice(0, 200)}${base64.length > 200 ? "..." : ""}` },
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
} catch (err) {
|
|
230
|
+
const r = { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
231
|
+
logTool(name, args, r);
|
|
232
|
+
return r;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case "take_screenshot": {
|
|
237
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
238
|
+
const dest = args.path
|
|
239
|
+
? resolve(args.path.replace(/^~/, homedir()))
|
|
240
|
+
: join(homedir(), "Desktop", `screenshot-${ts}.png`);
|
|
241
|
+
logTool(name, { path: dest });
|
|
242
|
+
return runCommand(`/usr/sbin/screencapture -x "${dest}"`, homedir()).then((result) => {
|
|
243
|
+
if (result.exitCode === 0) {
|
|
244
|
+
return { content: [{ type: "text", text: `Screenshot saved to ${dest}` }] };
|
|
245
|
+
}
|
|
246
|
+
return { content: [{ type: "text", text: `Screenshot failed: ${result.stderr || "unknown error"}` }], isError: true };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
144
250
|
default:
|
|
145
251
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
146
252
|
}
|