@elizaos/capacitor-agent 1.0.0 → 2.0.0-beta.1
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/ElizaosCapacitorAgent.podspec +17 -0
- package/android/build.gradle +30 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/ai/eliza/plugins/agent/AgentPlugin.kt +211 -0
- package/dist/esm/definitions.d.ts +27 -0
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/web.d.ts +3 -1
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +31 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +31 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +31 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/AgentPlugin/AgentPlugin.swift +565 -0
- package/package.json +12 -7
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'ElizaosCapacitorAgent'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license'] || { :type => 'MIT' }
|
|
10
|
+
s.homepage = 'https://elizaos.ai'
|
|
11
|
+
s.authors = { 'elizaOS' => 'dev@elizaos.ai' }
|
|
12
|
+
s.source = { :git => 'https://github.com/elizaOS/eliza.git', :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Sources/**/*.{swift,h,m}'
|
|
14
|
+
s.ios.deployment_target = '13.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
apply plugin: 'com.android.library'
|
|
7
|
+
android {
|
|
8
|
+
namespace = "ai.eliza.plugins.agent"
|
|
9
|
+
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
|
10
|
+
defaultConfig {
|
|
11
|
+
minSdk project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
|
12
|
+
targetSdk project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
|
13
|
+
}
|
|
14
|
+
compileOptions {
|
|
15
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
16
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
repositories {
|
|
21
|
+
google()
|
|
22
|
+
maven {
|
|
23
|
+
url = uri(rootProject.ext.has('mavenCentralMirrorUrl') ? rootProject.ext.mavenCentralMirrorUrl : 'https://repo.maven.apache.org/maven2')
|
|
24
|
+
}
|
|
25
|
+
mavenCentral()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
dependencies {
|
|
29
|
+
implementation project(':capacitor-android')
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
package ai.eliza.plugins.agent
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSObject
|
|
4
|
+
import com.getcapacitor.Plugin
|
|
5
|
+
import com.getcapacitor.PluginCall
|
|
6
|
+
import com.getcapacitor.PluginMethod
|
|
7
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
8
|
+
import java.io.ByteArrayOutputStream
|
|
9
|
+
import java.net.HttpURLConnection
|
|
10
|
+
import java.net.URL
|
|
11
|
+
import java.util.Locale
|
|
12
|
+
import org.json.JSONObject
|
|
13
|
+
|
|
14
|
+
private const val LOCAL_AGENT_BASE_URL = "http://127.0.0.1:31337"
|
|
15
|
+
private const val MAX_REQUEST_BODY_BYTES = 10 * 1024 * 1024
|
|
16
|
+
private const val MAX_RESPONSE_BODY_BYTES = 10 * 1024 * 1024
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Eliza Agent Plugin — Android bridge.
|
|
20
|
+
*
|
|
21
|
+
* The app module owns ElizaAgentService, so this library uses reflection to
|
|
22
|
+
* avoid a Gradle dependency cycle while still exposing the per-boot loopback
|
|
23
|
+
* bearer token to the WebView.
|
|
24
|
+
*/
|
|
25
|
+
@CapacitorPlugin(name = "Agent")
|
|
26
|
+
class AgentPlugin : Plugin() {
|
|
27
|
+
@PluginMethod
|
|
28
|
+
fun start(call: PluginCall) {
|
|
29
|
+
try {
|
|
30
|
+
invokeAgentService("start")
|
|
31
|
+
call.resolve(agentStatus("starting", null))
|
|
32
|
+
} catch (error: Exception) {
|
|
33
|
+
call.reject(error.message ?: "Failed to start local agent")
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@PluginMethod
|
|
38
|
+
fun stop(call: PluginCall) {
|
|
39
|
+
try {
|
|
40
|
+
invokeAgentService("stop")
|
|
41
|
+
call.resolve(JSObject().apply {
|
|
42
|
+
put("ok", true)
|
|
43
|
+
})
|
|
44
|
+
} catch (error: Exception) {
|
|
45
|
+
call.reject(error.message ?: "Failed to stop local agent")
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@PluginMethod
|
|
50
|
+
fun getStatus(call: PluginCall) {
|
|
51
|
+
val token = readLocalAgentToken()
|
|
52
|
+
if (token == null) {
|
|
53
|
+
call.resolve(agentStatus("not_started", null))
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Thread {
|
|
58
|
+
try {
|
|
59
|
+
val result = forwardLocalRequest("/api/status", "GET", JSObject(), null, 1_500, token)
|
|
60
|
+
val json = JSONObject(result.getString("body") ?: "{}")
|
|
61
|
+
call.resolve(agentStatus(
|
|
62
|
+
json.optString("state", "running"),
|
|
63
|
+
json.optString("error").takeIf { it.isNotBlank() },
|
|
64
|
+
))
|
|
65
|
+
} catch (error: Exception) {
|
|
66
|
+
call.resolve(agentStatus("error", error.message ?: "Local agent status unavailable"))
|
|
67
|
+
}
|
|
68
|
+
}.start()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@PluginMethod
|
|
72
|
+
fun getLocalAgentToken(call: PluginCall) {
|
|
73
|
+
val token = readLocalAgentToken()
|
|
74
|
+
call.resolve(JSObject().apply {
|
|
75
|
+
put("available", token != null)
|
|
76
|
+
put("token", token ?: JSONObject.NULL)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@PluginMethod
|
|
81
|
+
fun request(call: PluginCall) {
|
|
82
|
+
val path = call.getString("path")?.trim()
|
|
83
|
+
if (path == null || !path.startsWith("/") || path.startsWith("//")) {
|
|
84
|
+
call.reject("Agent.request requires a local path that starts with /")
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
val method = (call.getString("method") ?: "GET").trim().uppercase(Locale.US)
|
|
89
|
+
if (!method.matches(Regex("^[A-Z]{1,16}$"))) {
|
|
90
|
+
call.reject("Unsupported HTTP method")
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
val timeoutMs = (call.getInt("timeoutMs") ?: 10_000).coerceIn(1_000, 120_000)
|
|
95
|
+
val body = call.getString("body")
|
|
96
|
+
val headers = call.getObject("headers") ?: JSObject()
|
|
97
|
+
val token = readLocalAgentToken()
|
|
98
|
+
|
|
99
|
+
Thread {
|
|
100
|
+
try {
|
|
101
|
+
val result = forwardLocalRequest(path, method, headers, body, timeoutMs, token)
|
|
102
|
+
call.resolve(result)
|
|
103
|
+
} catch (error: Exception) {
|
|
104
|
+
call.reject(error.message ?: "Local agent request failed")
|
|
105
|
+
}
|
|
106
|
+
}.start()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private fun agentStatus(state: String, error: String?): JSObject {
|
|
110
|
+
return JSObject().apply {
|
|
111
|
+
put("state", state)
|
|
112
|
+
put("agentName", JSONObject.NULL)
|
|
113
|
+
put("port", if (state == "not_started") JSONObject.NULL else 31337)
|
|
114
|
+
put("startedAt", JSONObject.NULL)
|
|
115
|
+
put("error", error ?: JSONObject.NULL)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private fun invokeAgentService(methodName: String) {
|
|
120
|
+
val serviceClass = Class.forName("${context.packageName}.ElizaAgentService")
|
|
121
|
+
val method = serviceClass.getMethod(methodName, android.content.Context::class.java)
|
|
122
|
+
method.invoke(null, context)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private fun readLocalAgentToken(): String? {
|
|
126
|
+
return try {
|
|
127
|
+
val serviceClass = Class.forName("${context.packageName}.ElizaAgentService")
|
|
128
|
+
val method = serviceClass.getMethod("localAgentToken")
|
|
129
|
+
(method.invoke(null) as? String)?.trim()?.takeIf { it.isNotEmpty() }
|
|
130
|
+
} catch (_: Exception) {
|
|
131
|
+
null
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private fun forwardLocalRequest(
|
|
136
|
+
path: String,
|
|
137
|
+
method: String,
|
|
138
|
+
headers: JSObject,
|
|
139
|
+
body: String?,
|
|
140
|
+
timeoutMs: Int,
|
|
141
|
+
token: String?,
|
|
142
|
+
): JSObject {
|
|
143
|
+
val requestBody = body?.toByteArray(Charsets.UTF_8)
|
|
144
|
+
if (requestBody != null && requestBody.size > MAX_REQUEST_BODY_BYTES) {
|
|
145
|
+
throw IllegalArgumentException("Request body is too large")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
val connection = (URL("$LOCAL_AGENT_BASE_URL$path").openConnection() as HttpURLConnection).apply {
|
|
149
|
+
requestMethod = method
|
|
150
|
+
connectTimeout = timeoutMs
|
|
151
|
+
readTimeout = timeoutMs
|
|
152
|
+
instanceFollowRedirects = false
|
|
153
|
+
useCaches = false
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (key in headers.keys()) {
|
|
157
|
+
if (key.equals("host", ignoreCase = true) ||
|
|
158
|
+
key.equals("connection", ignoreCase = true) ||
|
|
159
|
+
key.equals("content-length", ignoreCase = true)
|
|
160
|
+
) {
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
163
|
+
val value = headers.opt(key) as? String
|
|
164
|
+
if (!value.isNullOrBlank()) {
|
|
165
|
+
connection.setRequestProperty(key, value)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (token != null && connection.getRequestProperty("Authorization").isNullOrBlank()) {
|
|
170
|
+
connection.setRequestProperty("Authorization", "Bearer $token")
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (requestBody != null && method != "GET" && method != "HEAD") {
|
|
174
|
+
connection.doOutput = true
|
|
175
|
+
connection.outputStream.use { output ->
|
|
176
|
+
output.write(requestBody)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
val status = connection.responseCode
|
|
181
|
+
val stream = if (status >= 400) connection.errorStream else connection.inputStream
|
|
182
|
+
val responseBody = stream?.use { input ->
|
|
183
|
+
val output = ByteArrayOutputStream()
|
|
184
|
+
val buffer = ByteArray(8192)
|
|
185
|
+
var total = 0
|
|
186
|
+
while (true) {
|
|
187
|
+
val count = input.read(buffer)
|
|
188
|
+
if (count == -1) break
|
|
189
|
+
total += count
|
|
190
|
+
if (total > MAX_RESPONSE_BODY_BYTES) {
|
|
191
|
+
throw IllegalStateException("Response body is too large")
|
|
192
|
+
}
|
|
193
|
+
output.write(buffer, 0, count)
|
|
194
|
+
}
|
|
195
|
+
output.toString(Charsets.UTF_8.name())
|
|
196
|
+
} ?: ""
|
|
197
|
+
|
|
198
|
+
val responseHeaders = JSObject()
|
|
199
|
+
for ((key, values) in connection.headerFields) {
|
|
200
|
+
if (key == null || values == null || values.isEmpty()) continue
|
|
201
|
+
responseHeaders.put(key.lowercase(Locale.US), values.joinToString(", "))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return JSObject().apply {
|
|
205
|
+
put("status", status)
|
|
206
|
+
put("statusText", connection.responseMessage ?: "")
|
|
207
|
+
put("headers", responseHeaders)
|
|
208
|
+
put("body", responseBody)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -18,6 +18,23 @@ export interface ChatResult {
|
|
|
18
18
|
text: string;
|
|
19
19
|
agentName: string;
|
|
20
20
|
}
|
|
21
|
+
export interface LocalAgentTokenResult {
|
|
22
|
+
available: boolean;
|
|
23
|
+
token: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface AgentRequestOptions {
|
|
26
|
+
method?: string;
|
|
27
|
+
path: string;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
body?: string | null;
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface AgentRequestResult {
|
|
33
|
+
status: number;
|
|
34
|
+
statusText: string;
|
|
35
|
+
headers: Record<string, string>;
|
|
36
|
+
body: string;
|
|
37
|
+
}
|
|
21
38
|
export interface AgentPlugin {
|
|
22
39
|
/** Start the agent runtime. Resolves when it's ready. */
|
|
23
40
|
start(): Promise<AgentStatus>;
|
|
@@ -31,5 +48,15 @@ export interface AgentPlugin {
|
|
|
31
48
|
chat(options: {
|
|
32
49
|
text: string;
|
|
33
50
|
}): Promise<ChatResult>;
|
|
51
|
+
/** Read the per-boot bearer token for the bundled Android local agent. */
|
|
52
|
+
getLocalAgentToken?(): Promise<LocalAgentTokenResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Path-only request bridge for the bundled local agent.
|
|
55
|
+
*
|
|
56
|
+
* Native implementations must reject absolute URLs and route only to the
|
|
57
|
+
* app-owned local backend. This is a transitional transport before the
|
|
58
|
+
* backend route kernel can run over Binder/LocalSocket/WKURLSchemeHandler.
|
|
59
|
+
*/
|
|
60
|
+
request?(options: AgentRequestOptions): Promise<AgentRequestResult>;
|
|
34
61
|
}
|
|
35
62
|
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAE9B,8BAA8B;IAC9B,IAAI,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAEjC,gCAAgC;IAChC,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAElC,gDAAgD;IAChD,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAE9B,8BAA8B;IAC9B,IAAI,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAEjC,gCAAgC;IAChC,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAElC,gDAAgD;IAChD,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAErD,0EAA0E;IAC1E,kBAAkB,CAAC,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAEtD;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACrE"}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebPlugin } from "@capacitor/core";
|
|
2
|
-
import type { AgentPlugin, AgentStatus, ChatResult } from "./definitions";
|
|
2
|
+
import type { AgentPlugin, AgentRequestOptions, AgentRequestResult, AgentStatus, ChatResult, LocalAgentTokenResult } from "./definitions";
|
|
3
3
|
/**
|
|
4
4
|
* Web fallback implementation.
|
|
5
5
|
*
|
|
@@ -39,5 +39,7 @@ export declare class AgentWeb extends WebPlugin implements AgentPlugin {
|
|
|
39
39
|
chat(options: {
|
|
40
40
|
text: string;
|
|
41
41
|
}): Promise<ChatResult>;
|
|
42
|
+
getLocalAgentToken(): Promise<LocalAgentTokenResult>;
|
|
43
|
+
request(options: AgentRequestOptions): Promise<AgentRequestResult>;
|
|
42
44
|
}
|
|
43
45
|
//# sourceMappingURL=web.d.ts.map
|
package/dist/esm/web.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAOvB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,QAAS,SAAQ,SAAU,YAAW,WAAW;IAC5D,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,yBAAyB;YAUnB,0BAA0B;YA0B1B,mBAAmB;IA8BjC,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,WAAW;IAKnB,+CAA+C;IAC/C,OAAO,CAAC,WAAW;IAYb,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAkB7B,IAAI,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAWhC,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;IAgBjC,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAOpD,kBAAkB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAQpD,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAuBzE"}
|
package/dist/esm/web.js
CHANGED
|
@@ -66,6 +66,7 @@ export class AgentWeb extends WebPlugin {
|
|
|
66
66
|
}
|
|
67
67
|
async chatViaConversation(text, retryOnMissingConversation = true) {
|
|
68
68
|
const conversationId = await this.ensureLegacyConversationId();
|
|
69
|
+
// @duplicate-component-audit-allow: agent API message POST; server-side runtime owns model trajectory logging.
|
|
69
70
|
const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {
|
|
70
71
|
method: "POST",
|
|
71
72
|
headers: {
|
|
@@ -168,5 +169,35 @@ export class AgentWeb extends WebPlugin {
|
|
|
168
169
|
}
|
|
169
170
|
return this.chatViaConversation(options.text);
|
|
170
171
|
}
|
|
172
|
+
async getLocalAgentToken() {
|
|
173
|
+
const token = this.apiToken();
|
|
174
|
+
return {
|
|
175
|
+
available: Boolean(token),
|
|
176
|
+
token,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async request(options) {
|
|
180
|
+
if (!options.path?.startsWith("/")) {
|
|
181
|
+
throw new Error("Agent.request path must start with /");
|
|
182
|
+
}
|
|
183
|
+
const res = await fetch(`${this.apiBase()}${options.path}`, {
|
|
184
|
+
method: options.method ?? "GET",
|
|
185
|
+
headers: {
|
|
186
|
+
...this.authHeaders(),
|
|
187
|
+
...options.headers,
|
|
188
|
+
},
|
|
189
|
+
body: options.body ?? undefined,
|
|
190
|
+
});
|
|
191
|
+
const headers = {};
|
|
192
|
+
res.headers.forEach((value, key) => {
|
|
193
|
+
headers[key] = value;
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
status: res.status,
|
|
197
|
+
statusText: res.statusText,
|
|
198
|
+
headers,
|
|
199
|
+
body: await res.text(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
171
202
|
}
|
|
172
203
|
//# sourceMappingURL=web.js.map
|
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -75,6 +75,7 @@ class AgentWeb extends core.WebPlugin {
|
|
|
75
75
|
}
|
|
76
76
|
async chatViaConversation(text, retryOnMissingConversation = true) {
|
|
77
77
|
const conversationId = await this.ensureLegacyConversationId();
|
|
78
|
+
// @duplicate-component-audit-allow: agent API message POST; server-side runtime owns model trajectory logging.
|
|
78
79
|
const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {
|
|
79
80
|
method: "POST",
|
|
80
81
|
headers: {
|
|
@@ -177,6 +178,36 @@ class AgentWeb extends core.WebPlugin {
|
|
|
177
178
|
}
|
|
178
179
|
return this.chatViaConversation(options.text);
|
|
179
180
|
}
|
|
181
|
+
async getLocalAgentToken() {
|
|
182
|
+
const token = this.apiToken();
|
|
183
|
+
return {
|
|
184
|
+
available: Boolean(token),
|
|
185
|
+
token,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async request(options) {
|
|
189
|
+
if (!options.path?.startsWith("/")) {
|
|
190
|
+
throw new Error("Agent.request path must start with /");
|
|
191
|
+
}
|
|
192
|
+
const res = await fetch(`${this.apiBase()}${options.path}`, {
|
|
193
|
+
method: options.method ?? "GET",
|
|
194
|
+
headers: {
|
|
195
|
+
...this.authHeaders(),
|
|
196
|
+
...options.headers,
|
|
197
|
+
},
|
|
198
|
+
body: options.body ?? undefined,
|
|
199
|
+
});
|
|
200
|
+
const headers = {};
|
|
201
|
+
res.headers.forEach((value, key) => {
|
|
202
|
+
headers[key] = value;
|
|
203
|
+
});
|
|
204
|
+
return {
|
|
205
|
+
status: res.status,
|
|
206
|
+
statusText: res.statusText,
|
|
207
|
+
headers,
|
|
208
|
+
body: await res.text(),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
180
211
|
}
|
|
181
212
|
|
|
182
213
|
var web = /*#__PURE__*/Object.freeze({
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nexport const Agent = registerPlugin(\"Agent\", {\n web: () => import(\"./web\").then((m) => new m.AgentWeb()),\n // Electrobun uses the preload bridge (agent:start, agent:stop, etc.)\n // iOS/Android will use the web fallback (HTTP to API server) for now\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web fallback implementation.\n *\n * On non-desktop platforms (iOS, Android, web), the agent runtime runs\n * on a server. This implementation delegates to the HTTP API.\n *\n * In Electrobun the desktop bridge calls the native main-process\n * implementation via RPC instead — this web fallback is only used when\n * no native plugin is available. If the page is served from a non-HTTP\n * origin (e.g. electrobun://), relative fetches would hit the\n * app shell HTML, so we bail early.\n *\n * Local-agent-on-Android (Phase E): when the host UI selects the\n * \"Local Agent\" tile, it sets `apiBase` to `http://127.0.0.1:31337`,\n * which the runtime mirrors into `window.__ELIZA_API_BASE__`. From this\n * plugin's perspective there is no special case — it simply HTTP-POSTs\n * to `${apiBase}/api/agent/start|stop|status`, which is exactly the same\n * surface Phase B's `ElizaAgentService` exposes. The web fallback path\n * therefore works unchanged for both remote and on-device agents.\n */\nexport class AgentWeb extends WebPlugin {\n legacyConversationStorageKey() {\n const base = this.apiBase() ||\n (typeof window !== \"undefined\" ? window.location.origin : \"same-origin\");\n return `eliza_agent_web_conversation:${encodeURIComponent(base)}`;\n }\n readLegacyConversationId() {\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(this.legacyConversationStorageKey());\n return stored?.trim() ? stored.trim() : null;\n }\n writeLegacyConversationId(conversationId) {\n if (typeof window === \"undefined\")\n return;\n const key = this.legacyConversationStorageKey();\n if (conversationId?.trim()) {\n window.sessionStorage.setItem(key, conversationId.trim());\n return;\n }\n window.sessionStorage.removeItem(key);\n }\n async ensureLegacyConversationId() {\n const cached = this.readLegacyConversationId();\n if (cached)\n return cached;\n const res = await fetch(`${this.apiBase()}/api/conversations`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ title: \"Quick Chat\" }),\n });\n if (!res.ok) {\n throw new Error(`Failed to create conversation: ${res.status}`);\n }\n const data = (await res.json());\n const conversationId = data.conversation?.id?.trim();\n if (!conversationId) {\n throw new Error(\"Conversation create response missing id\");\n }\n this.writeLegacyConversationId(conversationId);\n return conversationId;\n }\n async chatViaConversation(text, retryOnMissingConversation = true) {\n const conversationId = await this.ensureLegacyConversationId();\n const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ text, channelType: \"DM\" }),\n });\n if (res.status === 404 && retryOnMissingConversation) {\n this.writeLegacyConversationId(null);\n return this.chatViaConversation(text, false);\n }\n if (!res.ok) {\n throw new Error(`Chat request failed: ${res.status}`);\n }\n return res.json();\n }\n apiBase() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return global;\n // No explicit base — use relative URLs (works on http/https origins).\n return \"\";\n }\n apiToken() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_TOKEN__\n : undefined;\n if (typeof global === \"string\" && global.trim())\n return global.trim();\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(\"eliza_api_token\");\n return stored?.trim() ? stored.trim() : null;\n }\n authHeaders() {\n const token = this.apiToken();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n /** True when we can reach the API via HTTP. */\n canReachApi() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return true;\n // No explicit base — relative fetches only work on http(s) origins.\n if (typeof window === \"undefined\")\n return false;\n const proto = window.location.protocol;\n return proto === \"http:\" || proto === \"https:\";\n }\n async start() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/start`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n const data = await res.json();\n return data.status ?? data;\n }\n async stop() {\n if (!this.canReachApi()) {\n return { ok: false };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/stop`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async getStatus() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/status`, {\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async chat(options) {\n if (!this.canReachApi()) {\n return { text: \"Agent API not available\", agentName: \"System\" };\n }\n return this.chatViaConversation(options.text);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nexport const Agent = registerPlugin(\"Agent\", {\n web: () => import(\"./web\").then((m) => new m.AgentWeb()),\n // Electrobun uses the preload bridge (agent:start, agent:stop, etc.)\n // iOS/Android will use the web fallback (HTTP to API server) for now\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web fallback implementation.\n *\n * On non-desktop platforms (iOS, Android, web), the agent runtime runs\n * on a server. This implementation delegates to the HTTP API.\n *\n * In Electrobun the desktop bridge calls the native main-process\n * implementation via RPC instead — this web fallback is only used when\n * no native plugin is available. If the page is served from a non-HTTP\n * origin (e.g. electrobun://), relative fetches would hit the\n * app shell HTML, so we bail early.\n *\n * Local-agent-on-Android (Phase E): when the host UI selects the\n * \"Local Agent\" tile, it sets `apiBase` to `http://127.0.0.1:31337`,\n * which the runtime mirrors into `window.__ELIZA_API_BASE__`. From this\n * plugin's perspective there is no special case — it simply HTTP-POSTs\n * to `${apiBase}/api/agent/start|stop|status`, which is exactly the same\n * surface Phase B's `ElizaAgentService` exposes. The web fallback path\n * therefore works unchanged for both remote and on-device agents.\n */\nexport class AgentWeb extends WebPlugin {\n legacyConversationStorageKey() {\n const base = this.apiBase() ||\n (typeof window !== \"undefined\" ? window.location.origin : \"same-origin\");\n return `eliza_agent_web_conversation:${encodeURIComponent(base)}`;\n }\n readLegacyConversationId() {\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(this.legacyConversationStorageKey());\n return stored?.trim() ? stored.trim() : null;\n }\n writeLegacyConversationId(conversationId) {\n if (typeof window === \"undefined\")\n return;\n const key = this.legacyConversationStorageKey();\n if (conversationId?.trim()) {\n window.sessionStorage.setItem(key, conversationId.trim());\n return;\n }\n window.sessionStorage.removeItem(key);\n }\n async ensureLegacyConversationId() {\n const cached = this.readLegacyConversationId();\n if (cached)\n return cached;\n const res = await fetch(`${this.apiBase()}/api/conversations`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ title: \"Quick Chat\" }),\n });\n if (!res.ok) {\n throw new Error(`Failed to create conversation: ${res.status}`);\n }\n const data = (await res.json());\n const conversationId = data.conversation?.id?.trim();\n if (!conversationId) {\n throw new Error(\"Conversation create response missing id\");\n }\n this.writeLegacyConversationId(conversationId);\n return conversationId;\n }\n async chatViaConversation(text, retryOnMissingConversation = true) {\n const conversationId = await this.ensureLegacyConversationId();\n // @duplicate-component-audit-allow: agent API message POST; server-side runtime owns model trajectory logging.\n const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ text, channelType: \"DM\" }),\n });\n if (res.status === 404 && retryOnMissingConversation) {\n this.writeLegacyConversationId(null);\n return this.chatViaConversation(text, false);\n }\n if (!res.ok) {\n throw new Error(`Chat request failed: ${res.status}`);\n }\n return res.json();\n }\n apiBase() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return global;\n // No explicit base — use relative URLs (works on http/https origins).\n return \"\";\n }\n apiToken() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_TOKEN__\n : undefined;\n if (typeof global === \"string\" && global.trim())\n return global.trim();\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(\"eliza_api_token\");\n return stored?.trim() ? stored.trim() : null;\n }\n authHeaders() {\n const token = this.apiToken();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n /** True when we can reach the API via HTTP. */\n canReachApi() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return true;\n // No explicit base — relative fetches only work on http(s) origins.\n if (typeof window === \"undefined\")\n return false;\n const proto = window.location.protocol;\n return proto === \"http:\" || proto === \"https:\";\n }\n async start() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/start`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n const data = await res.json();\n return data.status ?? data;\n }\n async stop() {\n if (!this.canReachApi()) {\n return { ok: false };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/stop`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async getStatus() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/status`, {\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async chat(options) {\n if (!this.canReachApi()) {\n return { text: \"Agent API not available\", agentName: \"System\" };\n }\n return this.chatViaConversation(options.text);\n }\n async getLocalAgentToken() {\n const token = this.apiToken();\n return {\n available: Boolean(token),\n token,\n };\n }\n async request(options) {\n if (!options.path?.startsWith(\"/\")) {\n throw new Error(\"Agent.request path must start with /\");\n }\n const res = await fetch(`${this.apiBase()}${options.path}`, {\n method: options.method ?? \"GET\",\n headers: {\n ...this.authHeaders(),\n ...options.headers,\n },\n body: options.body ?? undefined,\n });\n const headers = {};\n res.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return {\n status: res.status,\n statusText: res.statusText,\n headers,\n body: await res.text(),\n };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -74,6 +74,7 @@ var capacitorAgent = (function (exports, core) {
|
|
|
74
74
|
}
|
|
75
75
|
async chatViaConversation(text, retryOnMissingConversation = true) {
|
|
76
76
|
const conversationId = await this.ensureLegacyConversationId();
|
|
77
|
+
// @duplicate-component-audit-allow: agent API message POST; server-side runtime owns model trajectory logging.
|
|
77
78
|
const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {
|
|
78
79
|
method: "POST",
|
|
79
80
|
headers: {
|
|
@@ -176,6 +177,36 @@ var capacitorAgent = (function (exports, core) {
|
|
|
176
177
|
}
|
|
177
178
|
return this.chatViaConversation(options.text);
|
|
178
179
|
}
|
|
180
|
+
async getLocalAgentToken() {
|
|
181
|
+
const token = this.apiToken();
|
|
182
|
+
return {
|
|
183
|
+
available: Boolean(token),
|
|
184
|
+
token,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async request(options) {
|
|
188
|
+
if (!options.path?.startsWith("/")) {
|
|
189
|
+
throw new Error("Agent.request path must start with /");
|
|
190
|
+
}
|
|
191
|
+
const res = await fetch(`${this.apiBase()}${options.path}`, {
|
|
192
|
+
method: options.method ?? "GET",
|
|
193
|
+
headers: {
|
|
194
|
+
...this.authHeaders(),
|
|
195
|
+
...options.headers,
|
|
196
|
+
},
|
|
197
|
+
body: options.body ?? undefined,
|
|
198
|
+
});
|
|
199
|
+
const headers = {};
|
|
200
|
+
res.headers.forEach((value, key) => {
|
|
201
|
+
headers[key] = value;
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
status: res.status,
|
|
205
|
+
statusText: res.statusText,
|
|
206
|
+
headers,
|
|
207
|
+
body: await res.text(),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
179
210
|
}
|
|
180
211
|
|
|
181
212
|
var web = /*#__PURE__*/Object.freeze({
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nexport const Agent = registerPlugin(\"Agent\", {\n web: () => import(\"./web\").then((m) => new m.AgentWeb()),\n // Electrobun uses the preload bridge (agent:start, agent:stop, etc.)\n // iOS/Android will use the web fallback (HTTP to API server) for now\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web fallback implementation.\n *\n * On non-desktop platforms (iOS, Android, web), the agent runtime runs\n * on a server. This implementation delegates to the HTTP API.\n *\n * In Electrobun the desktop bridge calls the native main-process\n * implementation via RPC instead — this web fallback is only used when\n * no native plugin is available. If the page is served from a non-HTTP\n * origin (e.g. electrobun://), relative fetches would hit the\n * app shell HTML, so we bail early.\n *\n * Local-agent-on-Android (Phase E): when the host UI selects the\n * \"Local Agent\" tile, it sets `apiBase` to `http://127.0.0.1:31337`,\n * which the runtime mirrors into `window.__ELIZA_API_BASE__`. From this\n * plugin's perspective there is no special case — it simply HTTP-POSTs\n * to `${apiBase}/api/agent/start|stop|status`, which is exactly the same\n * surface Phase B's `ElizaAgentService` exposes. The web fallback path\n * therefore works unchanged for both remote and on-device agents.\n */\nexport class AgentWeb extends WebPlugin {\n legacyConversationStorageKey() {\n const base = this.apiBase() ||\n (typeof window !== \"undefined\" ? window.location.origin : \"same-origin\");\n return `eliza_agent_web_conversation:${encodeURIComponent(base)}`;\n }\n readLegacyConversationId() {\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(this.legacyConversationStorageKey());\n return stored?.trim() ? stored.trim() : null;\n }\n writeLegacyConversationId(conversationId) {\n if (typeof window === \"undefined\")\n return;\n const key = this.legacyConversationStorageKey();\n if (conversationId?.trim()) {\n window.sessionStorage.setItem(key, conversationId.trim());\n return;\n }\n window.sessionStorage.removeItem(key);\n }\n async ensureLegacyConversationId() {\n const cached = this.readLegacyConversationId();\n if (cached)\n return cached;\n const res = await fetch(`${this.apiBase()}/api/conversations`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ title: \"Quick Chat\" }),\n });\n if (!res.ok) {\n throw new Error(`Failed to create conversation: ${res.status}`);\n }\n const data = (await res.json());\n const conversationId = data.conversation?.id?.trim();\n if (!conversationId) {\n throw new Error(\"Conversation create response missing id\");\n }\n this.writeLegacyConversationId(conversationId);\n return conversationId;\n }\n async chatViaConversation(text, retryOnMissingConversation = true) {\n const conversationId = await this.ensureLegacyConversationId();\n const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ text, channelType: \"DM\" }),\n });\n if (res.status === 404 && retryOnMissingConversation) {\n this.writeLegacyConversationId(null);\n return this.chatViaConversation(text, false);\n }\n if (!res.ok) {\n throw new Error(`Chat request failed: ${res.status}`);\n }\n return res.json();\n }\n apiBase() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return global;\n // No explicit base — use relative URLs (works on http/https origins).\n return \"\";\n }\n apiToken() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_TOKEN__\n : undefined;\n if (typeof global === \"string\" && global.trim())\n return global.trim();\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(\"eliza_api_token\");\n return stored?.trim() ? stored.trim() : null;\n }\n authHeaders() {\n const token = this.apiToken();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n /** True when we can reach the API via HTTP. */\n canReachApi() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return true;\n // No explicit base — relative fetches only work on http(s) origins.\n if (typeof window === \"undefined\")\n return false;\n const proto = window.location.protocol;\n return proto === \"http:\" || proto === \"https:\";\n }\n async start() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/start`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n const data = await res.json();\n return data.status ?? data;\n }\n async stop() {\n if (!this.canReachApi()) {\n return { ok: false };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/stop`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async getStatus() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/status`, {\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async chat(options) {\n if (!this.canReachApi()) {\n return { text: \"Agent API not available\", agentName: \"System\" };\n }\n return this.chatViaConversation(options.text);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nexport const Agent = registerPlugin(\"Agent\", {\n web: () => import(\"./web\").then((m) => new m.AgentWeb()),\n // Electrobun uses the preload bridge (agent:start, agent:stop, etc.)\n // iOS/Android will use the web fallback (HTTP to API server) for now\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web fallback implementation.\n *\n * On non-desktop platforms (iOS, Android, web), the agent runtime runs\n * on a server. This implementation delegates to the HTTP API.\n *\n * In Electrobun the desktop bridge calls the native main-process\n * implementation via RPC instead — this web fallback is only used when\n * no native plugin is available. If the page is served from a non-HTTP\n * origin (e.g. electrobun://), relative fetches would hit the\n * app shell HTML, so we bail early.\n *\n * Local-agent-on-Android (Phase E): when the host UI selects the\n * \"Local Agent\" tile, it sets `apiBase` to `http://127.0.0.1:31337`,\n * which the runtime mirrors into `window.__ELIZA_API_BASE__`. From this\n * plugin's perspective there is no special case — it simply HTTP-POSTs\n * to `${apiBase}/api/agent/start|stop|status`, which is exactly the same\n * surface Phase B's `ElizaAgentService` exposes. The web fallback path\n * therefore works unchanged for both remote and on-device agents.\n */\nexport class AgentWeb extends WebPlugin {\n legacyConversationStorageKey() {\n const base = this.apiBase() ||\n (typeof window !== \"undefined\" ? window.location.origin : \"same-origin\");\n return `eliza_agent_web_conversation:${encodeURIComponent(base)}`;\n }\n readLegacyConversationId() {\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(this.legacyConversationStorageKey());\n return stored?.trim() ? stored.trim() : null;\n }\n writeLegacyConversationId(conversationId) {\n if (typeof window === \"undefined\")\n return;\n const key = this.legacyConversationStorageKey();\n if (conversationId?.trim()) {\n window.sessionStorage.setItem(key, conversationId.trim());\n return;\n }\n window.sessionStorage.removeItem(key);\n }\n async ensureLegacyConversationId() {\n const cached = this.readLegacyConversationId();\n if (cached)\n return cached;\n const res = await fetch(`${this.apiBase()}/api/conversations`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ title: \"Quick Chat\" }),\n });\n if (!res.ok) {\n throw new Error(`Failed to create conversation: ${res.status}`);\n }\n const data = (await res.json());\n const conversationId = data.conversation?.id?.trim();\n if (!conversationId) {\n throw new Error(\"Conversation create response missing id\");\n }\n this.writeLegacyConversationId(conversationId);\n return conversationId;\n }\n async chatViaConversation(text, retryOnMissingConversation = true) {\n const conversationId = await this.ensureLegacyConversationId();\n // @duplicate-component-audit-allow: agent API message POST; server-side runtime owns model trajectory logging.\n const res = await fetch(`${this.apiBase()}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.authHeaders(),\n },\n body: JSON.stringify({ text, channelType: \"DM\" }),\n });\n if (res.status === 404 && retryOnMissingConversation) {\n this.writeLegacyConversationId(null);\n return this.chatViaConversation(text, false);\n }\n if (!res.ok) {\n throw new Error(`Chat request failed: ${res.status}`);\n }\n return res.json();\n }\n apiBase() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return global;\n // No explicit base — use relative URLs (works on http/https origins).\n return \"\";\n }\n apiToken() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_TOKEN__\n : undefined;\n if (typeof global === \"string\" && global.trim())\n return global.trim();\n if (typeof window === \"undefined\")\n return null;\n const stored = window.sessionStorage.getItem(\"eliza_api_token\");\n return stored?.trim() ? stored.trim() : null;\n }\n authHeaders() {\n const token = this.apiToken();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n /** True when we can reach the API via HTTP. */\n canReachApi() {\n const global = typeof window !== \"undefined\"\n ? window.__ELIZA_API_BASE__\n : undefined;\n if (typeof global === \"string\" && global.trim().length > 0)\n return true;\n // No explicit base — relative fetches only work on http(s) origins.\n if (typeof window === \"undefined\")\n return false;\n const proto = window.location.protocol;\n return proto === \"http:\" || proto === \"https:\";\n }\n async start() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/start`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n const data = await res.json();\n return data.status ?? data;\n }\n async stop() {\n if (!this.canReachApi()) {\n return { ok: false };\n }\n const res = await fetch(`${this.apiBase()}/api/agent/stop`, {\n method: \"POST\",\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async getStatus() {\n if (!this.canReachApi()) {\n return {\n state: \"not_started\",\n agentName: null,\n port: null,\n startedAt: null,\n error: \"No API endpoint\",\n };\n }\n const res = await fetch(`${this.apiBase()}/api/status`, {\n headers: this.authHeaders(),\n });\n return res.json();\n }\n async chat(options) {\n if (!this.canReachApi()) {\n return { text: \"Agent API not available\", agentName: \"System\" };\n }\n return this.chatViaConversation(options.text);\n }\n async getLocalAgentToken() {\n const token = this.apiToken();\n return {\n available: Boolean(token),\n token,\n };\n }\n async request(options) {\n if (!options.path?.startsWith(\"/\")) {\n throw new Error(\"Agent.request path must start with /\");\n }\n const res = await fetch(`${this.apiBase()}${options.path}`, {\n method: options.method ?? \"GET\",\n headers: {\n ...this.authHeaders(),\n ...options.headers,\n },\n body: options.body ?? undefined,\n });\n const headers = {};\n res.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return {\n status: res.status,\n statusText: res.statusText,\n headers,\n body: await res.text(),\n };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
private let maxRequestBodyBytes = 10 * 1024 * 1024
|
|
5
|
+
private let maxResponseBodyBytes = 10 * 1024 * 1024
|
|
6
|
+
|
|
7
|
+
private struct AgentEndpoint {
|
|
8
|
+
let baseURL: URL
|
|
9
|
+
let token: String?
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
private struct AgentHTTPResponse {
|
|
13
|
+
let status: Int
|
|
14
|
+
let statusText: String
|
|
15
|
+
let headers: [String: String]
|
|
16
|
+
let body: String
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Eliza Agent Plugin — iOS HTTP bridge.
|
|
20
|
+
///
|
|
21
|
+
/// iOS does not bundle a Bun runtime. This plugin bridges the Capacitor
|
|
22
|
+
/// Agent API to an explicitly configured HTTP agent endpoint, such as a
|
|
23
|
+
/// local Mac dev server or a remote Eliza agent. Without that endpoint it
|
|
24
|
+
/// fails with an actionable error instead of pretending a local agent exists.
|
|
25
|
+
@objc(AgentPlugin)
|
|
26
|
+
public class AgentPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
27
|
+
public let identifier = "AgentPlugin"
|
|
28
|
+
public let jsName = "Agent"
|
|
29
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
30
|
+
CAPPluginMethod(name: "start", returnType: CAPPluginReturnPromise),
|
|
31
|
+
CAPPluginMethod(name: "stop", returnType: CAPPluginReturnPromise),
|
|
32
|
+
CAPPluginMethod(name: "getStatus", returnType: CAPPluginReturnPromise),
|
|
33
|
+
CAPPluginMethod(name: "chat", returnType: CAPPluginReturnPromise),
|
|
34
|
+
CAPPluginMethod(name: "getLocalAgentToken", returnType: CAPPluginReturnPromise),
|
|
35
|
+
CAPPluginMethod(name: "request", returnType: CAPPluginReturnPromise),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
private static var conversationIdByBaseURL: [String: String] = [:]
|
|
39
|
+
|
|
40
|
+
@objc func start(_ call: CAPPluginCall) {
|
|
41
|
+
guard let endpoint = resolveEndpoint(call: call) else {
|
|
42
|
+
call.reject(missingEndpointMessage())
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
sendJSON(endpoint: endpoint, path: "/api/agent/start", method: "POST", timeoutMs: timeoutMs(from: call)) { result in
|
|
47
|
+
switch result {
|
|
48
|
+
case .success(let response):
|
|
49
|
+
guard self.isHTTPSuccess(response.status) else {
|
|
50
|
+
call.reject(self.httpErrorMessage(prefix: "Agent start failed", response: response))
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
let payload = self.parseJSONObject(response.body)
|
|
54
|
+
let statusPayload = (payload?["status"] as? JSObject) ?? payload ?? [:]
|
|
55
|
+
call.resolve(self.normalizedStatus(statusPayload, fallbackState: "running", endpoint: endpoint, error: nil))
|
|
56
|
+
case .failure(let error):
|
|
57
|
+
call.reject("Agent start failed: \(error.localizedDescription)")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@objc func stop(_ call: CAPPluginCall) {
|
|
63
|
+
guard let endpoint = resolveEndpoint(call: call) else {
|
|
64
|
+
call.reject(missingEndpointMessage())
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
sendJSON(endpoint: endpoint, path: "/api/agent/stop", method: "POST", timeoutMs: timeoutMs(from: call)) { result in
|
|
69
|
+
switch result {
|
|
70
|
+
case .success(let response):
|
|
71
|
+
guard self.isHTTPSuccess(response.status) else {
|
|
72
|
+
call.reject(self.httpErrorMessage(prefix: "Agent stop failed", response: response))
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
let payload = self.parseJSONObject(response.body)
|
|
76
|
+
let ok = (payload?["ok"] as? Bool) ?? true
|
|
77
|
+
call.resolve(["ok": ok])
|
|
78
|
+
case .failure(let error):
|
|
79
|
+
call.reject("Agent stop failed: \(error.localizedDescription)")
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@objc func getStatus(_ call: CAPPluginCall) {
|
|
85
|
+
guard let endpoint = resolveEndpoint(call: call) else {
|
|
86
|
+
call.resolve(status(state: "error", agentName: nil, port: nil, startedAt: nil, error: missingEndpointMessage()))
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
sendJSON(endpoint: endpoint, path: "/api/status", method: "GET", timeoutMs: timeoutMs(from: call, defaultValue: 1_500)) { result in
|
|
91
|
+
switch result {
|
|
92
|
+
case .success(let response):
|
|
93
|
+
guard self.isHTTPSuccess(response.status) else {
|
|
94
|
+
call.resolve(self.status(
|
|
95
|
+
state: "error",
|
|
96
|
+
agentName: nil,
|
|
97
|
+
port: self.port(from: endpoint.baseURL),
|
|
98
|
+
startedAt: nil,
|
|
99
|
+
error: self.httpErrorMessage(prefix: "Agent status unavailable", response: response)
|
|
100
|
+
))
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
let payload = self.parseJSONObject(response.body) ?? [:]
|
|
104
|
+
call.resolve(self.normalizedStatus(payload, fallbackState: "running", endpoint: endpoint, error: nil))
|
|
105
|
+
case .failure(let error):
|
|
106
|
+
call.resolve(self.status(
|
|
107
|
+
state: "error",
|
|
108
|
+
agentName: nil,
|
|
109
|
+
port: self.port(from: endpoint.baseURL),
|
|
110
|
+
startedAt: nil,
|
|
111
|
+
error: "Agent status unavailable: \(error.localizedDescription)"
|
|
112
|
+
))
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@objc func chat(_ call: CAPPluginCall) {
|
|
118
|
+
guard let text = call.getString("text")?.trimmingCharacters(in: .whitespacesAndNewlines), !text.isEmpty else {
|
|
119
|
+
call.reject("Agent.chat requires non-empty text")
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
guard let endpoint = resolveEndpoint(call: call) else {
|
|
123
|
+
call.reject(missingEndpointMessage())
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
ensureConversation(endpoint: endpoint, timeoutMs: timeoutMs(from: call)) { conversationResult in
|
|
128
|
+
switch conversationResult {
|
|
129
|
+
case .success(let conversationId):
|
|
130
|
+
self.sendChatMessage(endpoint: endpoint, conversationId: conversationId, text: text, timeoutMs: self.timeoutMs(from: call), retryOnMissingConversation: true) { result in
|
|
131
|
+
switch result {
|
|
132
|
+
case .success(let payload):
|
|
133
|
+
call.resolve(payload)
|
|
134
|
+
case .failure(let error):
|
|
135
|
+
call.reject(error.localizedDescription)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
case .failure(let error):
|
|
139
|
+
call.reject(error.localizedDescription)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@objc func getLocalAgentToken(_ call: CAPPluginCall) {
|
|
145
|
+
let token = resolveEndpoint(call: call)?.token
|
|
146
|
+
call.resolve([
|
|
147
|
+
"available": token != nil,
|
|
148
|
+
"token": token ?? NSNull(),
|
|
149
|
+
])
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@objc func request(_ call: CAPPluginCall) {
|
|
153
|
+
guard let endpoint = resolveEndpoint(call: call) else {
|
|
154
|
+
call.reject(missingEndpointMessage())
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
guard let path = call.getString("path")?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
158
|
+
isSafeLocalPath(path) else {
|
|
159
|
+
call.reject("Agent.request requires a local path that starts with / and is not an absolute URL")
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
let method = (call.getString("method") ?? "GET").trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
|
|
163
|
+
guard method.range(of: "^[A-Z]{1,16}$", options: .regularExpression) != nil else {
|
|
164
|
+
call.reject("Unsupported HTTP method")
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
let body = call.getString("body")
|
|
168
|
+
if let bodyBytes = body?.data(using: .utf8), bodyBytes.count > maxRequestBodyBytes {
|
|
169
|
+
call.reject("Request body is too large")
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let headers = call.getObject("headers") ?? [:]
|
|
174
|
+
sendJSON(
|
|
175
|
+
endpoint: endpoint,
|
|
176
|
+
path: path,
|
|
177
|
+
method: method,
|
|
178
|
+
headers: headers,
|
|
179
|
+
body: body,
|
|
180
|
+
timeoutMs: timeoutMs(from: call)
|
|
181
|
+
) { result in
|
|
182
|
+
switch result {
|
|
183
|
+
case .success(let response):
|
|
184
|
+
call.resolve([
|
|
185
|
+
"status": response.status,
|
|
186
|
+
"statusText": response.statusText,
|
|
187
|
+
"headers": response.headers,
|
|
188
|
+
"body": response.body,
|
|
189
|
+
])
|
|
190
|
+
case .failure(let error):
|
|
191
|
+
call.reject("Local agent request failed: \(error.localizedDescription)")
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private func ensureConversation(
|
|
197
|
+
endpoint: AgentEndpoint,
|
|
198
|
+
timeoutMs: Int,
|
|
199
|
+
completion: @escaping (Result<String, Error>) -> Void
|
|
200
|
+
) {
|
|
201
|
+
let baseKey = endpoint.baseURL.absoluteString
|
|
202
|
+
if let existing = Self.conversationIdByBaseURL[baseKey], !existing.isEmpty {
|
|
203
|
+
completion(.success(existing))
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
sendJSON(
|
|
208
|
+
endpoint: endpoint,
|
|
209
|
+
path: "/api/conversations",
|
|
210
|
+
method: "POST",
|
|
211
|
+
headers: ["Content-Type": "application/json"],
|
|
212
|
+
body: "{\"title\":\"Quick Chat\"}",
|
|
213
|
+
timeoutMs: timeoutMs
|
|
214
|
+
) { result in
|
|
215
|
+
switch result {
|
|
216
|
+
case .success(let response):
|
|
217
|
+
guard self.isHTTPSuccess(response.status) else {
|
|
218
|
+
completion(.failure(self.pluginError(self.httpErrorMessage(prefix: "Failed to create conversation", response: response))))
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
guard let payload = self.parseJSONObject(response.body),
|
|
222
|
+
let conversation = payload["conversation"] as? JSObject,
|
|
223
|
+
let id = conversation["id"] as? String,
|
|
224
|
+
!id.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
225
|
+
completion(.failure(self.pluginError("Conversation create response missing id")))
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
Self.conversationIdByBaseURL[baseKey] = id
|
|
229
|
+
completion(.success(id))
|
|
230
|
+
case .failure(let error):
|
|
231
|
+
completion(.failure(self.pluginError("Failed to create conversation: \(error.localizedDescription)")))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private func sendChatMessage(
|
|
237
|
+
endpoint: AgentEndpoint,
|
|
238
|
+
conversationId: String,
|
|
239
|
+
text: String,
|
|
240
|
+
timeoutMs: Int,
|
|
241
|
+
retryOnMissingConversation: Bool,
|
|
242
|
+
completion: @escaping (Result<JSObject, Error>) -> Void
|
|
243
|
+
) {
|
|
244
|
+
let path = "/api/conversations/\(urlEncode(conversationId))/messages"
|
|
245
|
+
let bodyObject: JSObject = [
|
|
246
|
+
"text": text,
|
|
247
|
+
"channelType": "DM",
|
|
248
|
+
]
|
|
249
|
+
guard let bodyData = try? JSONSerialization.data(withJSONObject: bodyObject, options: []),
|
|
250
|
+
let body = String(data: bodyData, encoding: .utf8) else {
|
|
251
|
+
completion(.failure(pluginError("Failed to encode chat request")))
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
sendJSON(
|
|
256
|
+
endpoint: endpoint,
|
|
257
|
+
path: path,
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: ["Content-Type": "application/json"],
|
|
260
|
+
body: body,
|
|
261
|
+
timeoutMs: timeoutMs
|
|
262
|
+
) { result in
|
|
263
|
+
switch result {
|
|
264
|
+
case .success(let response):
|
|
265
|
+
if response.status == 404 && retryOnMissingConversation {
|
|
266
|
+
Self.conversationIdByBaseURL.removeValue(forKey: endpoint.baseURL.absoluteString)
|
|
267
|
+
self.ensureConversation(endpoint: endpoint, timeoutMs: timeoutMs) { nextConversation in
|
|
268
|
+
switch nextConversation {
|
|
269
|
+
case .success(let nextId):
|
|
270
|
+
self.sendChatMessage(
|
|
271
|
+
endpoint: endpoint,
|
|
272
|
+
conversationId: nextId,
|
|
273
|
+
text: text,
|
|
274
|
+
timeoutMs: timeoutMs,
|
|
275
|
+
retryOnMissingConversation: false,
|
|
276
|
+
completion: completion
|
|
277
|
+
)
|
|
278
|
+
case .failure(let error):
|
|
279
|
+
completion(.failure(error))
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
guard self.isHTTPSuccess(response.status) else {
|
|
285
|
+
completion(.failure(self.pluginError(self.httpErrorMessage(prefix: "Chat request failed", response: response))))
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
let payload = self.parseJSONObject(response.body) ?? [:]
|
|
289
|
+
let responseText = (payload["text"] as? String) ?? ""
|
|
290
|
+
let agentName = (payload["agentName"] as? String) ?? "Agent"
|
|
291
|
+
completion(.success([
|
|
292
|
+
"text": responseText,
|
|
293
|
+
"agentName": agentName,
|
|
294
|
+
]))
|
|
295
|
+
case .failure(let error):
|
|
296
|
+
completion(.failure(self.pluginError("Chat request failed: \(error.localizedDescription)")))
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private func sendJSON(
|
|
302
|
+
endpoint: AgentEndpoint,
|
|
303
|
+
path: String,
|
|
304
|
+
method: String,
|
|
305
|
+
headers: JSObject = [:],
|
|
306
|
+
body: String? = nil,
|
|
307
|
+
timeoutMs: Int,
|
|
308
|
+
completion: @escaping (Result<AgentHTTPResponse, Error>) -> Void
|
|
309
|
+
) {
|
|
310
|
+
guard let url = URL(string: path, relativeTo: endpoint.baseURL)?.absoluteURL else {
|
|
311
|
+
completion(.failure(pluginError("Invalid local agent request path")))
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
var request = URLRequest(url: url)
|
|
316
|
+
request.httpMethod = method
|
|
317
|
+
request.timeoutInterval = TimeInterval(timeoutMs) / 1_000
|
|
318
|
+
request.cachePolicy = .reloadIgnoringLocalCacheData
|
|
319
|
+
|
|
320
|
+
for (key, value) in headers {
|
|
321
|
+
let normalizedKey = key.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
322
|
+
guard !normalizedKey.isEmpty,
|
|
323
|
+
!isBlockedHeader(normalizedKey),
|
|
324
|
+
let stringValue = value as? String,
|
|
325
|
+
!stringValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
326
|
+
continue
|
|
327
|
+
}
|
|
328
|
+
request.setValue(stringValue, forHTTPHeaderField: normalizedKey)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if let token = endpoint.token, request.value(forHTTPHeaderField: "Authorization") == nil {
|
|
332
|
+
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if let body = body, method != "GET", method != "HEAD" {
|
|
336
|
+
let bodyData = body.data(using: .utf8) ?? Data()
|
|
337
|
+
if bodyData.count > maxRequestBodyBytes {
|
|
338
|
+
completion(.failure(pluginError("Request body is too large")))
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
request.httpBody = bodyData
|
|
342
|
+
if request.value(forHTTPHeaderField: "Content-Type") == nil {
|
|
343
|
+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
URLSession.shared.dataTask(with: request) { data, response, error in
|
|
348
|
+
if let error = error {
|
|
349
|
+
completion(.failure(error))
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
guard let httpResponse = response as? HTTPURLResponse else {
|
|
353
|
+
completion(.failure(self.pluginError("Agent endpoint returned a non-HTTP response")))
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
let responseData = data ?? Data()
|
|
357
|
+
if responseData.count > maxResponseBodyBytes {
|
|
358
|
+
completion(.failure(self.pluginError("Response body is too large")))
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
var responseHeaders: [String: String] = [:]
|
|
363
|
+
for (key, value) in httpResponse.allHeaderFields {
|
|
364
|
+
guard let headerKey = key as? String else { continue }
|
|
365
|
+
responseHeaders[headerKey.lowercased()] = String(describing: value)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
completion(.success(AgentHTTPResponse(
|
|
369
|
+
status: httpResponse.statusCode,
|
|
370
|
+
statusText: HTTPURLResponse.localizedString(forStatusCode: httpResponse.statusCode),
|
|
371
|
+
headers: responseHeaders,
|
|
372
|
+
body: String(data: responseData, encoding: .utf8) ?? ""
|
|
373
|
+
)))
|
|
374
|
+
}.resume()
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private func resolveEndpoint(call: CAPPluginCall? = nil) -> AgentEndpoint? {
|
|
378
|
+
guard let rawBaseURL = readConfiguredString(
|
|
379
|
+
call: call,
|
|
380
|
+
keys: [
|
|
381
|
+
"apiBase",
|
|
382
|
+
"baseUrl",
|
|
383
|
+
"baseURL",
|
|
384
|
+
"agentApiBase",
|
|
385
|
+
"ELIZA_AGENT_API_BASE",
|
|
386
|
+
"ELIZA_API_BASE",
|
|
387
|
+
"MILADY_IOS_API_BASE",
|
|
388
|
+
"MILADY_IOS_REMOTE_API_BASE",
|
|
389
|
+
"MILADY_MOBILE_API_BASE",
|
|
390
|
+
"VITE_MILADY_IOS_API_BASE",
|
|
391
|
+
"VITE_MILADY_MOBILE_API_BASE",
|
|
392
|
+
"VITE_ELIZA_IOS_API_BASE",
|
|
393
|
+
]
|
|
394
|
+
) else {
|
|
395
|
+
return nil
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let trimmed = rawBaseURL.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
399
|
+
.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
|
400
|
+
guard let baseURL = URL(string: trimmed),
|
|
401
|
+
let scheme = baseURL.scheme?.lowercased(),
|
|
402
|
+
scheme == "http" || scheme == "https",
|
|
403
|
+
baseURL.host != nil else {
|
|
404
|
+
return nil
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
let token = readConfiguredString(
|
|
408
|
+
call: call,
|
|
409
|
+
keys: [
|
|
410
|
+
"apiToken",
|
|
411
|
+
"token",
|
|
412
|
+
"agentApiToken",
|
|
413
|
+
"ELIZA_AGENT_API_TOKEN",
|
|
414
|
+
"ELIZA_API_TOKEN",
|
|
415
|
+
"MILADY_IOS_API_TOKEN",
|
|
416
|
+
"MILADY_IOS_REMOTE_API_TOKEN",
|
|
417
|
+
"MILADY_MOBILE_API_TOKEN",
|
|
418
|
+
"VITE_MILADY_IOS_API_TOKEN",
|
|
419
|
+
"VITE_MILADY_MOBILE_API_TOKEN",
|
|
420
|
+
"VITE_ELIZA_IOS_API_TOKEN",
|
|
421
|
+
]
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
return AgentEndpoint(baseURL: baseURL, token: token)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private func readConfiguredString(call: CAPPluginCall?, keys: [String]) -> String? {
|
|
428
|
+
for key in keys {
|
|
429
|
+
if let value = call?.getString(key)?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty {
|
|
430
|
+
return value
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
let pluginConfig = getConfig()
|
|
435
|
+
for key in keys {
|
|
436
|
+
if let value = pluginConfig.getString(key)?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty {
|
|
437
|
+
return value
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
for key in keys {
|
|
442
|
+
if let value = Bundle.main.object(forInfoDictionaryKey: key) as? String {
|
|
443
|
+
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
444
|
+
if !trimmed.isEmpty { return trimmed }
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
let environment = ProcessInfo.processInfo.environment
|
|
449
|
+
for key in keys {
|
|
450
|
+
if let value = environment[key]?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty {
|
|
451
|
+
return value
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
let defaults = UserDefaults.standard
|
|
456
|
+
for key in keys {
|
|
457
|
+
if let value = defaults.string(forKey: key)?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty {
|
|
458
|
+
return value
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return nil
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private func normalizedStatus(
|
|
466
|
+
_ payload: JSObject,
|
|
467
|
+
fallbackState: String,
|
|
468
|
+
endpoint: AgentEndpoint,
|
|
469
|
+
error: String?
|
|
470
|
+
) -> JSObject {
|
|
471
|
+
let state = (payload["state"] as? String) ?? fallbackState
|
|
472
|
+
let agentName = payload["agentName"] as? String
|
|
473
|
+
let startedAt = payload["startedAt"] as? Double
|
|
474
|
+
?? (payload["startedAt"] as? NSNumber)?.doubleValue
|
|
475
|
+
?? (payload["started_at"] as? NSNumber)?.doubleValue
|
|
476
|
+
let rawError = error ?? payload["error"] as? String
|
|
477
|
+
return status(
|
|
478
|
+
state: state,
|
|
479
|
+
agentName: agentName,
|
|
480
|
+
port: (payload["port"] as? Int)
|
|
481
|
+
?? (payload["port"] as? NSNumber)?.intValue
|
|
482
|
+
?? port(from: endpoint.baseURL),
|
|
483
|
+
startedAt: startedAt,
|
|
484
|
+
error: rawError
|
|
485
|
+
)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
private func status(
|
|
489
|
+
state: String,
|
|
490
|
+
agentName: String?,
|
|
491
|
+
port: Int?,
|
|
492
|
+
startedAt: Double?,
|
|
493
|
+
error: String?
|
|
494
|
+
) -> JSObject {
|
|
495
|
+
return [
|
|
496
|
+
"state": state,
|
|
497
|
+
"agentName": agentName ?? NSNull(),
|
|
498
|
+
"port": port ?? NSNull(),
|
|
499
|
+
"startedAt": startedAt ?? NSNull(),
|
|
500
|
+
"error": error ?? NSNull(),
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private func timeoutMs(from call: CAPPluginCall, defaultValue: Int = 10_000) -> Int {
|
|
505
|
+
let value = call.getInt("timeoutMs") ?? defaultValue
|
|
506
|
+
return min(120_000, max(1_000, value))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
private func port(from url: URL) -> Int? {
|
|
510
|
+
if let port = url.port { return port }
|
|
511
|
+
if url.scheme?.lowercased() == "http" { return 80 }
|
|
512
|
+
if url.scheme?.lowercased() == "https" { return 443 }
|
|
513
|
+
return nil
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private func isSafeLocalPath(_ path: String) -> Bool {
|
|
517
|
+
if !path.hasPrefix("/") || path.hasPrefix("//") { return false }
|
|
518
|
+
if path.range(of: "^[a-zA-Z][a-zA-Z0-9+.-]*://", options: .regularExpression) != nil {
|
|
519
|
+
return false
|
|
520
|
+
}
|
|
521
|
+
return true
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private func isBlockedHeader(_ key: String) -> Bool {
|
|
525
|
+
switch key.lowercased() {
|
|
526
|
+
case "host", "connection", "content-length":
|
|
527
|
+
return true
|
|
528
|
+
default:
|
|
529
|
+
return false
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private func isHTTPSuccess(_ status: Int) -> Bool {
|
|
534
|
+
return status >= 200 && status < 300
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private func parseJSONObject(_ body: String) -> JSObject? {
|
|
538
|
+
guard let data = body.data(using: .utf8),
|
|
539
|
+
let object = try? JSONSerialization.jsonObject(with: data, options: []),
|
|
540
|
+
let json = object as? JSObject else {
|
|
541
|
+
return nil
|
|
542
|
+
}
|
|
543
|
+
return json
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
private func httpErrorMessage(prefix: String, response: AgentHTTPResponse) -> String {
|
|
547
|
+
let body = response.body.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
548
|
+
if body.isEmpty {
|
|
549
|
+
return "\(prefix): HTTP \(response.status)"
|
|
550
|
+
}
|
|
551
|
+
return "\(prefix): HTTP \(response.status): \(body)"
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
private func missingEndpointMessage() -> String {
|
|
555
|
+
return "iOS Agent requires a configured HTTP endpoint. Set Agent.apiBase in capacitor.config, an Info.plist/UserDefaults key such as MILADY_IOS_API_BASE or ELIZA_AGENT_API_BASE, or a simulator environment variable. iOS does not bundle a Bun local runtime."
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private func urlEncode(_ value: String) -> String {
|
|
559
|
+
return value.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? value
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
private func pluginError(_ message: String) -> NSError {
|
|
563
|
+
return NSError(domain: "AgentPlugin", code: 1, userInfo: [NSLocalizedDescriptionKey: message])
|
|
564
|
+
}
|
|
565
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/capacitor-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "Starts, stops, and monitors the embedded Eliza agent runtime.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-runtime",
|
|
@@ -21,26 +21,31 @@
|
|
|
21
21
|
},
|
|
22
22
|
"unpkg": "dist/plugin.js",
|
|
23
23
|
"files": [
|
|
24
|
-
"
|
|
24
|
+
"android/src/main/",
|
|
25
|
+
"android/build.gradle",
|
|
26
|
+
"dist/",
|
|
27
|
+
"ios/Sources/",
|
|
28
|
+
"*.podspec",
|
|
29
|
+
"dist"
|
|
25
30
|
],
|
|
26
31
|
"author": "elizaOS",
|
|
27
32
|
"license": "MIT",
|
|
28
33
|
"repository": {
|
|
29
34
|
"type": "git",
|
|
30
35
|
"url": "https://github.com/elizaOS/eliza.git",
|
|
31
|
-
"directory": "
|
|
36
|
+
"directory": "packages/native-plugins/agent"
|
|
32
37
|
},
|
|
33
38
|
"scripts": {
|
|
34
|
-
"build": "
|
|
35
|
-
"clean": "
|
|
36
|
-
"prepublishOnly": "
|
|
39
|
+
"build": "bun run clean && tsc && bun --bun rollup -c rollup.config.mjs",
|
|
40
|
+
"clean": "node ../../../scripts/rm-path-recursive.mjs dist",
|
|
41
|
+
"prepublishOnly": "bun run build",
|
|
37
42
|
"watch": "tsc --watch"
|
|
38
43
|
},
|
|
39
44
|
"devDependencies": {
|
|
40
45
|
"@capacitor/core": "^8.3.1",
|
|
41
46
|
"rimraf": "^6.0.0",
|
|
42
47
|
"rollup": "^4.60.2",
|
|
43
|
-
"typescript": "^6.0.
|
|
48
|
+
"typescript": "^6.0.3"
|
|
44
49
|
},
|
|
45
50
|
"peerDependencies": {
|
|
46
51
|
"@capacitor/core": "^8.3.1"
|