capacitor-native-agent 0.9.2 → 0.9.4
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/android/src/main/java/com/t6x/plugins/nativeagent/NativeAgentBridge.kt +70 -0
- package/android/src/main/java/com/t6x/plugins/nativeagent/NativeAgentPlugin.kt +3 -0
- package/ios/Sources/NativeAgentPlugin/NativeAgentBridge.swift +64 -0
- package/ios/Sources/NativeAgentPlugin/NativeAgentPlugin.swift +6 -0
- package/package.json +4 -4
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package com.t6x.plugins.nativeagent
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
4
|
+
import uniffi.native_agent_ffi.NativeAgentHandle
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Listener interface for in-process Kotlin code that wants to observe
|
|
8
|
+
* raw FFI events alongside the WebView listener. Mirrors the shape of
|
|
9
|
+
* uniffi.native_agent_ffi.NativeEventCallback but is registered through
|
|
10
|
+
* NativeAgentBridge instead of the FFI itself, so multiple consumers
|
|
11
|
+
* can subscribe at once.
|
|
12
|
+
*/
|
|
13
|
+
interface NativeEventListener {
|
|
14
|
+
fun onEvent(eventType: String, payloadJson: String)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Process-wide accessor that exposes the live NativeAgentHandle and a
|
|
19
|
+
* multi-listener event fan-out to other modules in the same Android
|
|
20
|
+
* process — most notably the STOMP transport service in theshell.
|
|
21
|
+
*
|
|
22
|
+
* The single FFI-side NativeEventCallback registered by NativeAgentPlugin
|
|
23
|
+
* forwards every event into [dispatch] in addition to its existing
|
|
24
|
+
* notifyListeners("nativeAgentEvent", ...) call, so the WebView pipeline
|
|
25
|
+
* is byte-equivalent to before this bridge existed.
|
|
26
|
+
*
|
|
27
|
+
* Listeners are strong-referenced; callers MUST call
|
|
28
|
+
* [removeNativeEventListener] when their lifecycle ends.
|
|
29
|
+
*/
|
|
30
|
+
object NativeAgentBridge {
|
|
31
|
+
|
|
32
|
+
@Volatile
|
|
33
|
+
private var handleRef: NativeAgentHandle? = null
|
|
34
|
+
|
|
35
|
+
private val listeners = CopyOnWriteArrayList<NativeEventListener>()
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The live FFI handle, or null if NativeAgent has not been
|
|
39
|
+
* initialized yet (or has been destroyed).
|
|
40
|
+
*/
|
|
41
|
+
@JvmStatic
|
|
42
|
+
fun handle(): NativeAgentHandle? = handleRef
|
|
43
|
+
|
|
44
|
+
@JvmStatic
|
|
45
|
+
fun addNativeEventListener(listener: NativeEventListener) {
|
|
46
|
+
listeners.addIfAbsent(listener)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@JvmStatic
|
|
50
|
+
fun removeNativeEventListener(listener: NativeEventListener) {
|
|
51
|
+
listeners.remove(listener)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
internal fun setHandle(h: NativeAgentHandle?) {
|
|
55
|
+
handleRef = h
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
internal fun dispatch(eventType: String, payloadJson: String) {
|
|
59
|
+
for (l in listeners) {
|
|
60
|
+
try {
|
|
61
|
+
l.onEvent(eventType, payloadJson)
|
|
62
|
+
} catch (t: Throwable) {
|
|
63
|
+
android.util.Log.w(
|
|
64
|
+
"NativeAgentBridge",
|
|
65
|
+
"listener threw on $eventType: ${t.message}",
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -98,6 +98,7 @@ class NativeAgentPlugin : Plugin() {
|
|
|
98
98
|
data.put("eventType", eventType)
|
|
99
99
|
data.put("payloadJson", payloadJson)
|
|
100
100
|
notifyListeners("nativeAgentEvent", data)
|
|
101
|
+
NativeAgentBridge.dispatch(eventType, payloadJson)
|
|
101
102
|
}
|
|
102
103
|
})
|
|
103
104
|
h.setNotifier(NativeNotifierImpl(context.applicationContext))
|
|
@@ -109,6 +110,7 @@ class NativeAgentPlugin : Plugin() {
|
|
|
109
110
|
}
|
|
110
111
|
h.persistConfig()
|
|
111
112
|
handle = h
|
|
113
|
+
NativeAgentBridge.setHandle(h)
|
|
112
114
|
context
|
|
113
115
|
.getSharedPreferences(STORAGE_FILE, android.content.Context.MODE_PRIVATE)
|
|
114
116
|
.edit()
|
|
@@ -530,6 +532,7 @@ class NativeAgentPlugin : Plugin() {
|
|
|
530
532
|
|
|
531
533
|
override fun handleOnDestroy() {
|
|
532
534
|
scope.cancel()
|
|
535
|
+
NativeAgentBridge.setHandle(null)
|
|
533
536
|
handle = null
|
|
534
537
|
}
|
|
535
538
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Listener protocol for in-process Swift code that wants to observe
|
|
4
|
+
/// raw FFI events alongside the WebView listener. Mirrors the shape of
|
|
5
|
+
/// the UniFFI-generated NativeEventCallback protocol but is registered
|
|
6
|
+
/// through NativeAgentBridge instead of the FFI itself, so multiple
|
|
7
|
+
/// consumers can subscribe at once.
|
|
8
|
+
public protocol NativeEventListener: AnyObject {
|
|
9
|
+
func onEvent(eventType: String, payloadJson: String)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/// Process-wide accessor that exposes the live NativeAgentHandle and a
|
|
13
|
+
/// multi-listener event fan-out to other modules in the same iOS
|
|
14
|
+
/// process — most notably the STOMP transport in theshell.
|
|
15
|
+
///
|
|
16
|
+
/// The single FFI-side NativeEventCallback registered by NativeAgentPlugin
|
|
17
|
+
/// (NativeAgentEventBridge in NativeAgentPlugin.swift) forwards every
|
|
18
|
+
/// event into `dispatch` in addition to its existing notifyListeners
|
|
19
|
+
/// call, so the WebView pipeline is byte-equivalent to before this
|
|
20
|
+
/// bridge existed.
|
|
21
|
+
///
|
|
22
|
+
/// Listeners are strong-referenced by ObjectIdentifier; callers MUST
|
|
23
|
+
/// call `removeNativeEventListener` when their lifecycle ends.
|
|
24
|
+
public enum NativeAgentBridge {
|
|
25
|
+
|
|
26
|
+
private static let lock = NSLock()
|
|
27
|
+
private static var handleRef: NativeAgentHandle?
|
|
28
|
+
private static var listeners: [ObjectIdentifier: NativeEventListener] = [:]
|
|
29
|
+
|
|
30
|
+
/// The live FFI handle, or nil if NativeAgent has not been
|
|
31
|
+
/// initialized yet (or has been destroyed).
|
|
32
|
+
public static func handle() -> NativeAgentHandle? {
|
|
33
|
+
lock.lock()
|
|
34
|
+
defer { lock.unlock() }
|
|
35
|
+
return handleRef
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public static func addNativeEventListener(_ listener: NativeEventListener) {
|
|
39
|
+
lock.lock()
|
|
40
|
+
defer { lock.unlock() }
|
|
41
|
+
listeners[ObjectIdentifier(listener)] = listener
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public static func removeNativeEventListener(_ listener: NativeEventListener) {
|
|
45
|
+
lock.lock()
|
|
46
|
+
defer { lock.unlock() }
|
|
47
|
+
listeners.removeValue(forKey: ObjectIdentifier(listener))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
internal static func setHandle(_ h: NativeAgentHandle?) {
|
|
51
|
+
lock.lock()
|
|
52
|
+
defer { lock.unlock() }
|
|
53
|
+
handleRef = h
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
internal static func dispatch(eventType: String, payloadJson: String) {
|
|
57
|
+
lock.lock()
|
|
58
|
+
let snapshot = Array(listeners.values)
|
|
59
|
+
lock.unlock()
|
|
60
|
+
for l in snapshot {
|
|
61
|
+
l.onEvent(eventType: eventType, payloadJson: payloadJson)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -66,6 +66,10 @@ public class NativeAgentPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
66
66
|
|
|
67
67
|
private var handle: NativeAgentHandle?
|
|
68
68
|
|
|
69
|
+
deinit {
|
|
70
|
+
NativeAgentBridge.setHandle(nil)
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
// ── Helper ──────────────────────────────────────────────────────────────
|
|
70
74
|
|
|
71
75
|
/// Resolves a `files://` prefixed path to an absolute iOS path
|
|
@@ -173,6 +177,7 @@ public class NativeAgentPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
173
177
|
}
|
|
174
178
|
try h.persistConfig()
|
|
175
179
|
self.handle = h
|
|
180
|
+
NativeAgentBridge.setHandle(h)
|
|
176
181
|
UserDefaults.standard.set(
|
|
177
182
|
self.resolveConfigPath(workspacePath: resolvedWorkspacePath),
|
|
178
183
|
forKey: Self.configPathKey
|
|
@@ -837,5 +842,6 @@ class NativeAgentEventBridge: NativeEventCallback {
|
|
|
837
842
|
"eventType": eventType,
|
|
838
843
|
"payloadJson": payloadJson,
|
|
839
844
|
])
|
|
845
|
+
NativeAgentBridge.dispatch(eventType: eventType, payloadJson: payloadJson)
|
|
840
846
|
}
|
|
841
847
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-native-agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
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",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
],
|
|
50
50
|
"repository": {
|
|
51
51
|
"type": "git",
|
|
52
|
-
"url": "https://github.com/
|
|
52
|
+
"url": "https://github.com/rogelioRuiz/capacitor-native-agent.git"
|
|
53
53
|
},
|
|
54
|
-
"homepage": "https://github.com/
|
|
54
|
+
"homepage": "https://github.com/rogelioRuiz/capacitor-native-agent#readme",
|
|
55
55
|
"bugs": {
|
|
56
|
-
"url": "https://github.com/
|
|
56
|
+
"url": "https://github.com/rogelioRuiz/capacitor-native-agent/issues"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@capacitor/core": "^6.0.0 || ^7.0.0 || ^8.0.0",
|