poke-gate 0.0.2 → 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/mcp-server.js +73 -1
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/mcp-server.js
CHANGED
|
@@ -2,7 +2,7 @@ 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
|
|
|
@@ -79,6 +79,30 @@ const TOOLS = [
|
|
|
79
79
|
description: "Get system information: OS, hostname, architecture, uptime, memory, and home directory.",
|
|
80
80
|
inputSchema: { type: "object", properties: {} },
|
|
81
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
|
+
},
|
|
82
106
|
];
|
|
83
107
|
|
|
84
108
|
function runCommand(command, cwd) {
|
|
@@ -175,6 +199,54 @@ function handleToolCall(name, args) {
|
|
|
175
199
|
return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
|
|
176
200
|
}
|
|
177
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
|
+
|
|
178
250
|
default:
|
|
179
251
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
180
252
|
}
|