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.
Files changed (18) hide show
  1. package/CapacitorNativeAgent.podspec +28 -0
  2. package/Package.swift +35 -0
  3. package/android/src/main/java/com/t6x/plugins/nativeagent/NativeAgentPlugin.kt +1 -1
  4. package/android/src/main/jniLibs/arm64-v8a/libnative_agent_ffi.so +0 -0
  5. package/ios/Frameworks/NativeAgentFFI.xcframework/Info.plist +47 -0
  6. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/Headers/native_agent_ffi/module.modulemap +4 -0
  7. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/Headers/native_agent_ffi/native_agent_ffi.swift +2064 -0
  8. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/Headers/native_agent_ffi/native_agent_ffiFFI.h +994 -0
  9. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64/libnative_agent_ffi.a +0 -0
  10. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/Headers/native_agent_ffi/module.modulemap +4 -0
  11. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/Headers/native_agent_ffi/native_agent_ffi.swift +2064 -0
  12. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/Headers/native_agent_ffi/native_agent_ffiFFI.h +994 -0
  13. package/ios/Frameworks/NativeAgentFFI.xcframework/ios-arm64-simulator/libnative_agent_ffi.a +0 -0
  14. package/ios/Sources/NativeAgentPlugin/Generated/native_agent_ffi.swift +2064 -0
  15. package/ios/Sources/NativeAgentPlugin/Generated/native_agent_ffiFFI.h +994 -0
  16. package/ios/Sources/NativeAgentPlugin/Generated/native_agent_ffiFFI.modulemap +4 -0
  17. package/ios/Sources/NativeAgentPlugin/NativeAgentPlugin.swift +648 -0
  18. package/package.json +8 -1
@@ -0,0 +1,4 @@
1
+ module native_agent_ffiFFI {
2
+ header "native_agent_ffiFFI.h"
3
+ export *
4
+ }
@@ -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.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
  ],