capacitor-native-agent 0.1.1 → 0.1.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/CapacitorNativeAgent.podspec +28 -0
- package/Package.swift +35 -0
- package/android/src/main/java/com/t6x/plugins/nativeagent/NativeAgentPlugin.kt +1 -1
- package/android/src/main/jniLibs/arm64-v8a/libnative_agent_ffi.so +0 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/Info.plist +47 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/Headers/native_agent_ffi/module.modulemap +4 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/Headers/native_agent_ffi/native_agent_ffi.swift +2064 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/Headers/native_agent_ffi/native_agent_ffiFFI.h +994 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/libnative_agent_ffi.a +0 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/Headers/native_agent_ffi/module.modulemap +4 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/Headers/native_agent_ffi/native_agent_ffi.swift +2064 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/Headers/native_agent_ffi/native_agent_ffiFFI.h +994 -0
- package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/libnative_agent_ffi.a +0 -0
- package/ios/Sources/NativeAgentPlugin/Generated/native_agent_ffi.swift +2064 -0
- package/ios/Sources/NativeAgentPlugin/Generated/native_agent_ffiFFI.h +994 -0
- package/ios/Sources/NativeAgentPlugin/Generated/native_agent_ffiFFI.modulemap +4 -0
- package/ios/Sources/NativeAgentPlugin/NativeAgentPlugin.swift +648 -0
- package/package.json +8 -1
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
@objc(NativeAgentPlugin)
|
|
5
|
+
public class NativeAgentPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
6
|
+
public let identifier = "NativeAgentPlugin"
|
|
7
|
+
public let jsName = "NativeAgent"
|
|
8
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
9
|
+
// Lifecycle
|
|
10
|
+
CAPPluginMethod(name: "initialize", returnType: CAPPluginReturnPromise),
|
|
11
|
+
// Agent
|
|
12
|
+
CAPPluginMethod(name: "sendMessage", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "followUp", returnType: CAPPluginReturnPromise),
|
|
14
|
+
CAPPluginMethod(name: "abort", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "steer", returnType: CAPPluginReturnPromise),
|
|
16
|
+
// Approval gate
|
|
17
|
+
CAPPluginMethod(name: "respondToApproval", returnType: CAPPluginReturnPromise),
|
|
18
|
+
CAPPluginMethod(name: "respondToCronApproval", returnType: CAPPluginReturnPromise),
|
|
19
|
+
// Auth
|
|
20
|
+
CAPPluginMethod(name: "getAuthToken", returnType: CAPPluginReturnPromise),
|
|
21
|
+
CAPPluginMethod(name: "setAuthKey", returnType: CAPPluginReturnPromise),
|
|
22
|
+
CAPPluginMethod(name: "deleteAuth", returnType: CAPPluginReturnPromise),
|
|
23
|
+
CAPPluginMethod(name: "refreshToken", returnType: CAPPluginReturnPromise),
|
|
24
|
+
CAPPluginMethod(name: "getAuthStatus", returnType: CAPPluginReturnPromise),
|
|
25
|
+
// Sessions
|
|
26
|
+
CAPPluginMethod(name: "listSessions", returnType: CAPPluginReturnPromise),
|
|
27
|
+
CAPPluginMethod(name: "loadSession", returnType: CAPPluginReturnPromise),
|
|
28
|
+
CAPPluginMethod(name: "resumeSession", returnType: CAPPluginReturnPromise),
|
|
29
|
+
CAPPluginMethod(name: "clearSession", returnType: CAPPluginReturnPromise),
|
|
30
|
+
// Cron / heartbeat
|
|
31
|
+
CAPPluginMethod(name: "addCronJob", returnType: CAPPluginReturnPromise),
|
|
32
|
+
CAPPluginMethod(name: "updateCronJob", returnType: CAPPluginReturnPromise),
|
|
33
|
+
CAPPluginMethod(name: "removeCronJob", returnType: CAPPluginReturnPromise),
|
|
34
|
+
CAPPluginMethod(name: "listCronJobs", returnType: CAPPluginReturnPromise),
|
|
35
|
+
CAPPluginMethod(name: "runCronJob", returnType: CAPPluginReturnPromise),
|
|
36
|
+
CAPPluginMethod(name: "listCronRuns", returnType: CAPPluginReturnPromise),
|
|
37
|
+
CAPPluginMethod(name: "handleWake", returnType: CAPPluginReturnPromise),
|
|
38
|
+
CAPPluginMethod(name: "getSchedulerConfig", returnType: CAPPluginReturnPromise),
|
|
39
|
+
CAPPluginMethod(name: "setSchedulerConfig", returnType: CAPPluginReturnPromise),
|
|
40
|
+
CAPPluginMethod(name: "setHeartbeatConfig", returnType: CAPPluginReturnPromise),
|
|
41
|
+
// Skills
|
|
42
|
+
CAPPluginMethod(name: "addSkill", returnType: CAPPluginReturnPromise),
|
|
43
|
+
CAPPluginMethod(name: "updateSkill", returnType: CAPPluginReturnPromise),
|
|
44
|
+
CAPPluginMethod(name: "removeSkill", returnType: CAPPluginReturnPromise),
|
|
45
|
+
CAPPluginMethod(name: "listSkills", returnType: CAPPluginReturnPromise),
|
|
46
|
+
CAPPluginMethod(name: "startSkill", returnType: CAPPluginReturnPromise),
|
|
47
|
+
CAPPluginMethod(name: "endSkill", returnType: CAPPluginReturnPromise),
|
|
48
|
+
// MCP
|
|
49
|
+
CAPPluginMethod(name: "startMcp", returnType: CAPPluginReturnPromise),
|
|
50
|
+
CAPPluginMethod(name: "restartMcp", returnType: CAPPluginReturnPromise),
|
|
51
|
+
// Models
|
|
52
|
+
CAPPluginMethod(name: "getModels", returnType: CAPPluginReturnPromise),
|
|
53
|
+
// Tools
|
|
54
|
+
CAPPluginMethod(name: "invokeTool", returnType: CAPPluginReturnPromise),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
private var handle: NativeAgentHandle?
|
|
58
|
+
|
|
59
|
+
// ── Helper ──────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/// Resolves a `files://` prefixed path to an absolute iOS path
|
|
62
|
+
/// under the app's Documents directory (always writable).
|
|
63
|
+
private func resolvePath(_ path: String) -> String {
|
|
64
|
+
if path.hasPrefix("files://") {
|
|
65
|
+
let relative = String(path.dropFirst("files://".count))
|
|
66
|
+
let base = FileManager.default
|
|
67
|
+
.urls(for: .documentDirectory, in: .userDomainMask)
|
|
68
|
+
.first!
|
|
69
|
+
.path
|
|
70
|
+
let resolved = (base as NSString).appendingPathComponent(relative)
|
|
71
|
+
// Ensure parent directory exists
|
|
72
|
+
let parentDir = (resolved as NSString).deletingLastPathComponent
|
|
73
|
+
try? FileManager.default.createDirectory(
|
|
74
|
+
atPath: parentDir,
|
|
75
|
+
withIntermediateDirectories: true,
|
|
76
|
+
attributes: nil
|
|
77
|
+
)
|
|
78
|
+
return resolved
|
|
79
|
+
}
|
|
80
|
+
return path
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private func withHandle(_ call: CAPPluginCall, _ block: @escaping (NativeAgentHandle) -> Void) {
|
|
84
|
+
guard let h = handle else {
|
|
85
|
+
return call.reject("NativeAgent not initialized — call initialize() first")
|
|
86
|
+
}
|
|
87
|
+
Task {
|
|
88
|
+
block(h)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
@objc func initialize(_ call: CAPPluginCall) {
|
|
95
|
+
guard let dbPath = call.getString("dbPath") else {
|
|
96
|
+
return call.reject("dbPath is required")
|
|
97
|
+
}
|
|
98
|
+
guard let workspacePath = call.getString("workspacePath") else {
|
|
99
|
+
return call.reject("workspacePath is required")
|
|
100
|
+
}
|
|
101
|
+
guard let authProfilesPath = call.getString("authProfilesPath") else {
|
|
102
|
+
return call.reject("authProfilesPath is required")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Task {
|
|
106
|
+
do {
|
|
107
|
+
let config = InitConfig(
|
|
108
|
+
dbPath: resolvePath(dbPath),
|
|
109
|
+
workspacePath: resolvePath(workspacePath),
|
|
110
|
+
authProfilesPath: resolvePath(authProfilesPath)
|
|
111
|
+
)
|
|
112
|
+
let h = try NativeAgentHandle(config: config)
|
|
113
|
+
h.setEventCallback(callback: NativeAgentEventBridge(plugin: self))
|
|
114
|
+
self.handle = h
|
|
115
|
+
call.resolve()
|
|
116
|
+
} catch {
|
|
117
|
+
call.reject("Failed to initialize NativeAgent: \(error.localizedDescription)")
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Agent ────────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
@objc func sendMessage(_ call: CAPPluginCall) {
|
|
125
|
+
withHandle(call) { h in
|
|
126
|
+
guard let prompt = call.getString("prompt") else {
|
|
127
|
+
return call.reject("prompt is required")
|
|
128
|
+
}
|
|
129
|
+
guard let sessionKey = call.getString("sessionKey") else {
|
|
130
|
+
return call.reject("sessionKey is required")
|
|
131
|
+
}
|
|
132
|
+
guard let systemPrompt = call.getString("systemPrompt") else {
|
|
133
|
+
return call.reject("systemPrompt is required")
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
do {
|
|
137
|
+
let params = SendMessageParams(
|
|
138
|
+
prompt: prompt,
|
|
139
|
+
sessionKey: sessionKey,
|
|
140
|
+
model: call.getString("model"),
|
|
141
|
+
provider: call.getString("provider"),
|
|
142
|
+
systemPrompt: systemPrompt,
|
|
143
|
+
maxTurns: call.getInt("maxTurns").map { UInt32($0) },
|
|
144
|
+
allowedToolsJson: call.getString("allowedToolsJson")
|
|
145
|
+
)
|
|
146
|
+
let runId = try h.sendMessage(params: params)
|
|
147
|
+
call.resolve(["runId": runId])
|
|
148
|
+
} catch {
|
|
149
|
+
call.reject("sendMessage failed: \(error.localizedDescription)")
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@objc func followUp(_ call: CAPPluginCall) {
|
|
155
|
+
withHandle(call) { h in
|
|
156
|
+
do {
|
|
157
|
+
try h.followUp(prompt: call.getString("prompt") ?? "")
|
|
158
|
+
call.resolve()
|
|
159
|
+
} catch {
|
|
160
|
+
call.reject("followUp failed: \(error.localizedDescription)")
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@objc func abort(_ call: CAPPluginCall) {
|
|
166
|
+
withHandle(call) { h in
|
|
167
|
+
do {
|
|
168
|
+
try h.abort()
|
|
169
|
+
call.resolve()
|
|
170
|
+
} catch {
|
|
171
|
+
call.reject("abort failed: \(error.localizedDescription)")
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@objc func steer(_ call: CAPPluginCall) {
|
|
177
|
+
withHandle(call) { h in
|
|
178
|
+
do {
|
|
179
|
+
try h.steer(text: call.getString("text") ?? "")
|
|
180
|
+
call.resolve()
|
|
181
|
+
} catch {
|
|
182
|
+
call.reject("steer failed: \(error.localizedDescription)")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Approval gate ────────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
@objc func respondToApproval(_ call: CAPPluginCall) {
|
|
190
|
+
withHandle(call) { h in
|
|
191
|
+
guard let toolCallId = call.getString("toolCallId") else {
|
|
192
|
+
return call.reject("toolCallId is required")
|
|
193
|
+
}
|
|
194
|
+
do {
|
|
195
|
+
try h.respondToApproval(
|
|
196
|
+
toolCallId: toolCallId,
|
|
197
|
+
approved: call.getBool("approved") ?? true,
|
|
198
|
+
reason: call.getString("reason")
|
|
199
|
+
)
|
|
200
|
+
call.resolve()
|
|
201
|
+
} catch {
|
|
202
|
+
call.reject("respondToApproval failed: \(error.localizedDescription)")
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@objc func respondToCronApproval(_ call: CAPPluginCall) {
|
|
208
|
+
withHandle(call) { h in
|
|
209
|
+
guard let requestId = call.getString("requestId") else {
|
|
210
|
+
return call.reject("requestId is required")
|
|
211
|
+
}
|
|
212
|
+
do {
|
|
213
|
+
try h.respondToCronApproval(
|
|
214
|
+
requestId: requestId,
|
|
215
|
+
approved: call.getBool("approved") ?? false
|
|
216
|
+
)
|
|
217
|
+
call.resolve()
|
|
218
|
+
} catch {
|
|
219
|
+
call.reject("respondToCronApproval failed: \(error.localizedDescription)")
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── Auth ─────────────────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
@objc func getAuthToken(_ call: CAPPluginCall) {
|
|
227
|
+
withHandle(call) { h in
|
|
228
|
+
do {
|
|
229
|
+
let result = try h.getAuthToken(provider: call.getString("provider") ?? "anthropic")
|
|
230
|
+
call.resolve([
|
|
231
|
+
"apiKey": result.apiKey as Any,
|
|
232
|
+
"isOAuth": result.isOauth,
|
|
233
|
+
])
|
|
234
|
+
} catch {
|
|
235
|
+
call.reject("getAuthToken failed: \(error.localizedDescription)")
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@objc func setAuthKey(_ call: CAPPluginCall) {
|
|
241
|
+
withHandle(call) { h in
|
|
242
|
+
guard let key = call.getString("key") else {
|
|
243
|
+
return call.reject("key is required")
|
|
244
|
+
}
|
|
245
|
+
do {
|
|
246
|
+
try h.setAuthKey(
|
|
247
|
+
key: key,
|
|
248
|
+
provider: call.getString("provider") ?? "anthropic",
|
|
249
|
+
authType: call.getString("authType") ?? "api_key"
|
|
250
|
+
)
|
|
251
|
+
call.resolve()
|
|
252
|
+
} catch {
|
|
253
|
+
call.reject("setAuthKey failed: \(error.localizedDescription)")
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@objc func deleteAuth(_ call: CAPPluginCall) {
|
|
259
|
+
withHandle(call) { h in
|
|
260
|
+
do {
|
|
261
|
+
try h.deleteAuth(provider: call.getString("provider") ?? "anthropic")
|
|
262
|
+
call.resolve()
|
|
263
|
+
} catch {
|
|
264
|
+
call.reject("deleteAuth failed: \(error.localizedDescription)")
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@objc func refreshToken(_ call: CAPPluginCall) {
|
|
270
|
+
withHandle(call) { h in
|
|
271
|
+
do {
|
|
272
|
+
let result = try h.refreshToken(provider: call.getString("provider") ?? "anthropic")
|
|
273
|
+
call.resolve([
|
|
274
|
+
"apiKey": result.apiKey as Any,
|
|
275
|
+
"isOAuth": result.isOauth,
|
|
276
|
+
])
|
|
277
|
+
} catch {
|
|
278
|
+
call.reject("refreshToken failed: \(error.localizedDescription)")
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@objc func getAuthStatus(_ call: CAPPluginCall) {
|
|
284
|
+
withHandle(call) { h in
|
|
285
|
+
do {
|
|
286
|
+
let result = try h.getAuthStatus(provider: call.getString("provider") ?? "anthropic")
|
|
287
|
+
call.resolve([
|
|
288
|
+
"hasKey": result.hasKey,
|
|
289
|
+
"masked": result.masked,
|
|
290
|
+
"provider": result.provider,
|
|
291
|
+
])
|
|
292
|
+
} catch {
|
|
293
|
+
call.reject("getAuthStatus failed: \(error.localizedDescription)")
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ── Sessions ─────────────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
@objc func listSessions(_ call: CAPPluginCall) {
|
|
301
|
+
withHandle(call) { h in
|
|
302
|
+
do {
|
|
303
|
+
let json = try h.listSessions(agentId: call.getString("agentId") ?? "main")
|
|
304
|
+
call.resolve(["sessionsJson": json])
|
|
305
|
+
} catch {
|
|
306
|
+
call.reject("listSessions failed: \(error.localizedDescription)")
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
@objc func loadSession(_ call: CAPPluginCall) {
|
|
312
|
+
withHandle(call) { h in
|
|
313
|
+
guard let sessKey = call.getString("sessionKey") else {
|
|
314
|
+
return call.reject("sessionKey is required")
|
|
315
|
+
}
|
|
316
|
+
do {
|
|
317
|
+
let json = try h.loadSession(sessionKey: sessKey)
|
|
318
|
+
call.resolve([
|
|
319
|
+
"sessionKey": sessKey,
|
|
320
|
+
"messagesJson": json,
|
|
321
|
+
])
|
|
322
|
+
} catch {
|
|
323
|
+
call.reject("loadSession failed: \(error.localizedDescription)")
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@objc func resumeSession(_ call: CAPPluginCall) {
|
|
329
|
+
withHandle(call) { h in
|
|
330
|
+
guard let sessKey = call.getString("sessionKey") else {
|
|
331
|
+
return call.reject("sessionKey is required")
|
|
332
|
+
}
|
|
333
|
+
do {
|
|
334
|
+
try h.resumeSession(
|
|
335
|
+
sessionKey: sessKey,
|
|
336
|
+
agentId: call.getString("agentId") ?? "main",
|
|
337
|
+
messagesJson: call.getString("messagesJson"),
|
|
338
|
+
provider: call.getString("provider"),
|
|
339
|
+
model: call.getString("model")
|
|
340
|
+
)
|
|
341
|
+
call.resolve()
|
|
342
|
+
} catch {
|
|
343
|
+
call.reject("resumeSession failed: \(error.localizedDescription)")
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@objc func clearSession(_ call: CAPPluginCall) {
|
|
349
|
+
withHandle(call) { h in
|
|
350
|
+
do {
|
|
351
|
+
try h.clearSession()
|
|
352
|
+
call.resolve()
|
|
353
|
+
} catch {
|
|
354
|
+
call.reject("clearSession failed: \(error.localizedDescription)")
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ── Cron / heartbeat ─────────────────────────────────────────────────────
|
|
360
|
+
|
|
361
|
+
@objc func addCronJob(_ call: CAPPluginCall) {
|
|
362
|
+
withHandle(call) { h in
|
|
363
|
+
guard let inputJson = call.getString("inputJson") else {
|
|
364
|
+
return call.reject("inputJson is required")
|
|
365
|
+
}
|
|
366
|
+
do {
|
|
367
|
+
let json = try h.addCronJob(inputJson: inputJson)
|
|
368
|
+
call.resolve(["recordJson": json])
|
|
369
|
+
} catch {
|
|
370
|
+
call.reject("addCronJob failed: \(error.localizedDescription)")
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
@objc func updateCronJob(_ call: CAPPluginCall) {
|
|
376
|
+
withHandle(call) { h in
|
|
377
|
+
guard let id = call.getString("id") else {
|
|
378
|
+
return call.reject("id is required")
|
|
379
|
+
}
|
|
380
|
+
do {
|
|
381
|
+
try h.updateCronJob(id: id, patchJson: call.getString("patchJson") ?? "{}")
|
|
382
|
+
call.resolve()
|
|
383
|
+
} catch {
|
|
384
|
+
call.reject("updateCronJob failed: \(error.localizedDescription)")
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
@objc func removeCronJob(_ call: CAPPluginCall) {
|
|
390
|
+
withHandle(call) { h in
|
|
391
|
+
guard let id = call.getString("id") else {
|
|
392
|
+
return call.reject("id is required")
|
|
393
|
+
}
|
|
394
|
+
do {
|
|
395
|
+
try h.removeCronJob(id: id)
|
|
396
|
+
call.resolve()
|
|
397
|
+
} catch {
|
|
398
|
+
call.reject("removeCronJob failed: \(error.localizedDescription)")
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
@objc func listCronJobs(_ call: CAPPluginCall) {
|
|
404
|
+
withHandle(call) { h in
|
|
405
|
+
do {
|
|
406
|
+
call.resolve(["jobsJson": try h.listCronJobs()])
|
|
407
|
+
} catch {
|
|
408
|
+
call.reject("listCronJobs failed: \(error.localizedDescription)")
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@objc func runCronJob(_ call: CAPPluginCall) {
|
|
414
|
+
withHandle(call) { h in
|
|
415
|
+
guard let jobId = call.getString("jobId") else {
|
|
416
|
+
return call.reject("jobId is required")
|
|
417
|
+
}
|
|
418
|
+
do {
|
|
419
|
+
try h.runCronJob(jobId: jobId)
|
|
420
|
+
call.resolve()
|
|
421
|
+
} catch {
|
|
422
|
+
call.reject("runCronJob failed: \(error.localizedDescription)")
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
@objc func listCronRuns(_ call: CAPPluginCall) {
|
|
428
|
+
withHandle(call) { h in
|
|
429
|
+
do {
|
|
430
|
+
let json = try h.listCronRuns(
|
|
431
|
+
jobId: call.getString("jobId"),
|
|
432
|
+
limit: Int64(call.getInt("limit") ?? 100)
|
|
433
|
+
)
|
|
434
|
+
call.resolve(["runsJson": json])
|
|
435
|
+
} catch {
|
|
436
|
+
call.reject("listCronRuns failed: \(error.localizedDescription)")
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@objc func handleWake(_ call: CAPPluginCall) {
|
|
442
|
+
withHandle(call) { h in
|
|
443
|
+
do {
|
|
444
|
+
try h.handleWake(source: call.getString("source") ?? "unknown")
|
|
445
|
+
call.resolve()
|
|
446
|
+
} catch {
|
|
447
|
+
call.reject("handleWake failed: \(error.localizedDescription)")
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
@objc func getSchedulerConfig(_ call: CAPPluginCall) {
|
|
453
|
+
withHandle(call) { h in
|
|
454
|
+
do {
|
|
455
|
+
let schedulerJson = try h.getSchedulerConfig()
|
|
456
|
+
let heartbeatJson = try h.getHeartbeatConfig()
|
|
457
|
+
call.resolve([
|
|
458
|
+
"schedulerJson": schedulerJson,
|
|
459
|
+
"heartbeatJson": heartbeatJson,
|
|
460
|
+
])
|
|
461
|
+
} catch {
|
|
462
|
+
call.reject("getSchedulerConfig failed: \(error.localizedDescription)")
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
@objc func setSchedulerConfig(_ call: CAPPluginCall) {
|
|
468
|
+
withHandle(call) { h in
|
|
469
|
+
do {
|
|
470
|
+
try h.setSchedulerConfig(configJson: call.getString("configJson") ?? "{}")
|
|
471
|
+
call.resolve()
|
|
472
|
+
} catch {
|
|
473
|
+
call.reject("setSchedulerConfig failed: \(error.localizedDescription)")
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
@objc func setHeartbeatConfig(_ call: CAPPluginCall) {
|
|
479
|
+
withHandle(call) { h in
|
|
480
|
+
do {
|
|
481
|
+
try h.setHeartbeatConfig(configJson: call.getString("configJson") ?? "{}")
|
|
482
|
+
call.resolve()
|
|
483
|
+
} catch {
|
|
484
|
+
call.reject("setHeartbeatConfig failed: \(error.localizedDescription)")
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ── Skills ───────────────────────────────────────────────────────────────
|
|
490
|
+
|
|
491
|
+
@objc func addSkill(_ call: CAPPluginCall) {
|
|
492
|
+
withHandle(call) { h in
|
|
493
|
+
guard let inputJson = call.getString("inputJson") else {
|
|
494
|
+
return call.reject("inputJson is required")
|
|
495
|
+
}
|
|
496
|
+
do {
|
|
497
|
+
let json = try h.addSkill(inputJson: inputJson)
|
|
498
|
+
call.resolve(["recordJson": json])
|
|
499
|
+
} catch {
|
|
500
|
+
call.reject("addSkill failed: \(error.localizedDescription)")
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
@objc func updateSkill(_ call: CAPPluginCall) {
|
|
506
|
+
withHandle(call) { h in
|
|
507
|
+
guard let id = call.getString("id") else {
|
|
508
|
+
return call.reject("id is required")
|
|
509
|
+
}
|
|
510
|
+
do {
|
|
511
|
+
try h.updateSkill(id: id, patchJson: call.getString("patchJson") ?? "{}")
|
|
512
|
+
call.resolve()
|
|
513
|
+
} catch {
|
|
514
|
+
call.reject("updateSkill failed: \(error.localizedDescription)")
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
@objc func removeSkill(_ call: CAPPluginCall) {
|
|
520
|
+
withHandle(call) { h in
|
|
521
|
+
guard let id = call.getString("id") else {
|
|
522
|
+
return call.reject("id is required")
|
|
523
|
+
}
|
|
524
|
+
do {
|
|
525
|
+
try h.removeSkill(id: id)
|
|
526
|
+
call.resolve()
|
|
527
|
+
} catch {
|
|
528
|
+
call.reject("removeSkill failed: \(error.localizedDescription)")
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
@objc func listSkills(_ call: CAPPluginCall) {
|
|
534
|
+
withHandle(call) { h in
|
|
535
|
+
do {
|
|
536
|
+
call.resolve(["skillsJson": try h.listSkills()])
|
|
537
|
+
} catch {
|
|
538
|
+
call.reject("listSkills failed: \(error.localizedDescription)")
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
@objc func startSkill(_ call: CAPPluginCall) {
|
|
544
|
+
withHandle(call) { h in
|
|
545
|
+
guard let skillId = call.getString("skillId") else {
|
|
546
|
+
return call.reject("skillId is required")
|
|
547
|
+
}
|
|
548
|
+
do {
|
|
549
|
+
let sessKey = try h.startSkill(
|
|
550
|
+
skillId: skillId,
|
|
551
|
+
configJson: call.getString("configJson") ?? "{}",
|
|
552
|
+
provider: call.getString("provider")
|
|
553
|
+
)
|
|
554
|
+
call.resolve(["sessionKey": sessKey])
|
|
555
|
+
} catch {
|
|
556
|
+
call.reject("startSkill failed: \(error.localizedDescription)")
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
@objc func endSkill(_ call: CAPPluginCall) {
|
|
562
|
+
withHandle(call) { h in
|
|
563
|
+
guard let skillId = call.getString("skillId") else {
|
|
564
|
+
return call.reject("skillId is required")
|
|
565
|
+
}
|
|
566
|
+
do {
|
|
567
|
+
try h.endSkill(skillId: skillId)
|
|
568
|
+
call.resolve()
|
|
569
|
+
} catch {
|
|
570
|
+
call.reject("endSkill failed: \(error.localizedDescription)")
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ── MCP ──────────────────────────────────────────────────────────────────
|
|
576
|
+
|
|
577
|
+
@objc func startMcp(_ call: CAPPluginCall) {
|
|
578
|
+
withHandle(call) { h in
|
|
579
|
+
do {
|
|
580
|
+
let count = try h.startMcp(toolsJson: call.getString("toolsJson") ?? "[]")
|
|
581
|
+
call.resolve(["toolCount": Int(count)])
|
|
582
|
+
} catch {
|
|
583
|
+
call.reject("startMcp failed: \(error.localizedDescription)")
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
@objc func restartMcp(_ call: CAPPluginCall) {
|
|
589
|
+
withHandle(call) { h in
|
|
590
|
+
do {
|
|
591
|
+
let count = try h.restartMcp(toolsJson: call.getString("toolsJson") ?? "[]")
|
|
592
|
+
call.resolve(["toolCount": Int(count)])
|
|
593
|
+
} catch {
|
|
594
|
+
call.reject("restartMcp failed: \(error.localizedDescription)")
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ── Models ───────────────────────────────────────────────────────────────
|
|
600
|
+
|
|
601
|
+
@objc func getModels(_ call: CAPPluginCall) {
|
|
602
|
+
withHandle(call) { h in
|
|
603
|
+
do {
|
|
604
|
+
let json = try h.getModels(provider: call.getString("provider") ?? "anthropic")
|
|
605
|
+
call.resolve(["modelsJson": json])
|
|
606
|
+
} catch {
|
|
607
|
+
call.reject("getModels failed: \(error.localizedDescription)")
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ── Tools ────────────────────────────────────────────────────────────────
|
|
613
|
+
|
|
614
|
+
@objc func invokeTool(_ call: CAPPluginCall) {
|
|
615
|
+
withHandle(call) { h in
|
|
616
|
+
guard let toolName = call.getString("toolName") else {
|
|
617
|
+
return call.reject("toolName is required")
|
|
618
|
+
}
|
|
619
|
+
do {
|
|
620
|
+
let resultJson = try h.invokeTool(
|
|
621
|
+
toolName: toolName,
|
|
622
|
+
argsJson: call.getString("argsJson") ?? "{}"
|
|
623
|
+
)
|
|
624
|
+
call.resolve(["resultJson": resultJson])
|
|
625
|
+
} catch {
|
|
626
|
+
call.reject("invokeTool failed: \(error.localizedDescription)")
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// ── Event bridge ─────────────────────────────────────────────────────────────
|
|
633
|
+
|
|
634
|
+
/// Bridges Rust NativeEventCallback → Capacitor notifyListeners
|
|
635
|
+
class NativeAgentEventBridge: NativeEventCallback {
|
|
636
|
+
private weak var plugin: NativeAgentPlugin?
|
|
637
|
+
|
|
638
|
+
init(plugin: NativeAgentPlugin) {
|
|
639
|
+
self.plugin = plugin
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
func onEvent(eventType: String, payloadJson: String) {
|
|
643
|
+
plugin?.notifyListeners("nativeAgentEvent", data: [
|
|
644
|
+
"eventType": eventType,
|
|
645
|
+
"payloadJson": payloadJson,
|
|
646
|
+
])
|
|
647
|
+
}
|
|
648
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-native-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Native AI agent loop for Capacitor — runs LLM completions, tool execution, and cron jobs in native Rust, enabling background execution.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/esm/index.d.ts",
|
|
@@ -17,12 +17,19 @@
|
|
|
17
17
|
"capacitor": {
|
|
18
18
|
"android": {
|
|
19
19
|
"src": "android"
|
|
20
|
+
},
|
|
21
|
+
"ios": {
|
|
22
|
+
"src": "ios"
|
|
20
23
|
}
|
|
21
24
|
},
|
|
22
25
|
"files": [
|
|
23
26
|
"dist/esm/",
|
|
24
27
|
"android/src/main/",
|
|
25
28
|
"android/build.gradle",
|
|
29
|
+
"ios/Sources/",
|
|
30
|
+
"ios/Frameworks/",
|
|
31
|
+
"CapacitorNativeAgent.podspec",
|
|
32
|
+
"Package.swift",
|
|
26
33
|
"README.md",
|
|
27
34
|
"LICENSE"
|
|
28
35
|
],
|