@elizaos/capacitor-messages 1.0.0

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.
@@ -0,0 +1,27 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ }
4
+
5
+ apply plugin: 'com.android.library'
6
+ android {
7
+ namespace = "ai.eliza.plugins.messages"
8
+ compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
9
+ defaultConfig {
10
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
11
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
12
+ }
13
+ compileOptions {
14
+ sourceCompatibility JavaVersion.VERSION_17
15
+ targetCompatibility JavaVersion.VERSION_17
16
+ }
17
+ }
18
+
19
+ repositories {
20
+ google()
21
+ maven { url = uri(rootProject.ext.mavenCentralMirrorUrl) }
22
+ mavenCentral()
23
+ }
24
+
25
+ dependencies {
26
+ implementation project(':capacitor-android')
27
+ }
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <uses-permission android:name="android.permission.READ_SMS" />
4
+ <uses-permission android:name="android.permission.SEND_SMS" />
5
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
6
+ <uses-permission android:name="android.permission.RECEIVE_MMS" />
7
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
8
+ </manifest>
@@ -0,0 +1,184 @@
1
+ package ai.eliza.plugins.messages
2
+
3
+ import android.app.Activity
4
+ import android.app.PendingIntent
5
+ import android.Manifest
6
+ import android.content.BroadcastReceiver
7
+ import android.content.ContentValues
8
+ import android.content.Context
9
+ import android.content.Intent
10
+ import android.content.IntentFilter
11
+ import android.os.Build
12
+ import android.net.Uri
13
+ import android.provider.Telephony
14
+ import android.telephony.SmsManager
15
+ import com.getcapacitor.JSArray
16
+ import com.getcapacitor.JSObject
17
+ import com.getcapacitor.Plugin
18
+ import com.getcapacitor.PluginCall
19
+ import com.getcapacitor.PluginMethod
20
+ import com.getcapacitor.annotation.CapacitorPlugin
21
+ import java.util.concurrent.atomic.AtomicBoolean
22
+ import java.util.concurrent.atomic.AtomicInteger
23
+
24
+ @CapacitorPlugin(name = "ElizaMessages")
25
+ class MessagesPlugin : Plugin() {
26
+ private val requestCounter = AtomicInteger(1)
27
+
28
+ @PluginMethod
29
+ fun sendSms(call: PluginCall) {
30
+ if (!hasPermission(Manifest.permission.SEND_SMS)) {
31
+ call.reject("SEND_SMS permission is required")
32
+ return
33
+ }
34
+ val address = call.getString("address")?.trim()
35
+ val body = call.getString("body")?.trim()
36
+ if (address.isNullOrEmpty()) {
37
+ call.reject("address is required")
38
+ return
39
+ }
40
+ if (body.isNullOrEmpty()) {
41
+ call.reject("body is required")
42
+ return
43
+ }
44
+
45
+ val smsManager = SmsManager.getDefault()
46
+ val parts = smsManager.divideMessage(body)
47
+ if (parts.isEmpty()) {
48
+ call.reject("body is required")
49
+ return
50
+ }
51
+
52
+ val requestId = requestCounter.getAndIncrement()
53
+ val action = "${context.packageName}.ELIZA_SMS_SENT.$requestId"
54
+ val remaining = AtomicInteger(parts.size)
55
+ val failed = AtomicBoolean(false)
56
+ val receiver = object : BroadcastReceiver() {
57
+ override fun onReceive(receiverContext: Context, intent: Intent) {
58
+ if (resultCode != Activity.RESULT_OK) {
59
+ failed.set(true)
60
+ }
61
+ if (remaining.decrementAndGet() == 0) {
62
+ receiverContext.unregisterReceiver(this)
63
+ if (failed.get()) {
64
+ call.reject("SMS send failed with result code $resultCode")
65
+ } else {
66
+ try {
67
+ call.resolve(persistSentSms(address, body))
68
+ } catch (error: RuntimeException) {
69
+ call.reject("SMS sent but Android SMS provider did not persist the sent row", error)
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ val filter = IntentFilter(action)
77
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
78
+ context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
79
+ } else {
80
+ context.registerReceiver(receiver, filter)
81
+ }
82
+
83
+ val pendingIntents = ArrayList<PendingIntent>()
84
+ val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
85
+ for (index in parts.indices) {
86
+ val sentIntent = Intent(action).setPackage(context.packageName)
87
+ pendingIntents.add(
88
+ PendingIntent.getBroadcast(context, requestId + index, sentIntent, flags)
89
+ )
90
+ }
91
+
92
+ try {
93
+ if (parts.size == 1) {
94
+ smsManager.sendTextMessage(address, null, parts.first(), pendingIntents.first(), null)
95
+ } else {
96
+ smsManager.sendMultipartTextMessage(address, null, parts, pendingIntents, null)
97
+ }
98
+ } catch (error: RuntimeException) {
99
+ context.unregisterReceiver(receiver)
100
+ call.reject("SMS send failed before radio handoff", error)
101
+ }
102
+ }
103
+
104
+ @PluginMethod
105
+ fun listMessages(call: PluginCall) {
106
+ if (!hasPermission(Manifest.permission.READ_SMS)) {
107
+ call.reject("READ_SMS permission is required")
108
+ return
109
+ }
110
+ val limit = call.getInt("limit") ?: 100
111
+ if (limit <= 0 || limit > 500) {
112
+ call.reject("limit must be between 1 and 500")
113
+ return
114
+ }
115
+ val threadId = call.getString("threadId")?.trim()
116
+ val selection = if (threadId.isNullOrEmpty()) null else "${Telephony.Sms.THREAD_ID} = ?"
117
+ val selectionArgs = if (threadId.isNullOrEmpty()) null else arrayOf(threadId)
118
+ val messages = JSArray()
119
+ val cursor = context.contentResolver.query(
120
+ Uri.parse("content://sms"),
121
+ arrayOf(
122
+ Telephony.Sms._ID,
123
+ Telephony.Sms.THREAD_ID,
124
+ Telephony.Sms.ADDRESS,
125
+ Telephony.Sms.BODY,
126
+ Telephony.Sms.DATE,
127
+ Telephony.Sms.TYPE,
128
+ Telephony.Sms.READ
129
+ ),
130
+ selection,
131
+ selectionArgs,
132
+ "${Telephony.Sms.DATE} DESC"
133
+ )
134
+ if (cursor == null) {
135
+ call.reject("SMS provider returned no cursor")
136
+ return
137
+ }
138
+ cursor.use {
139
+ val idCol = cursor.getColumnIndexOrThrow(Telephony.Sms._ID)
140
+ val threadCol = cursor.getColumnIndexOrThrow(Telephony.Sms.THREAD_ID)
141
+ val addressCol = cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS)
142
+ val bodyCol = cursor.getColumnIndexOrThrow(Telephony.Sms.BODY)
143
+ val dateCol = cursor.getColumnIndexOrThrow(Telephony.Sms.DATE)
144
+ val typeCol = cursor.getColumnIndexOrThrow(Telephony.Sms.TYPE)
145
+ val readCol = cursor.getColumnIndexOrThrow(Telephony.Sms.READ)
146
+ var count = 0
147
+ while (cursor.moveToNext() && count < limit) {
148
+ val message = JSObject()
149
+ message.put("id", cursor.getString(idCol))
150
+ message.put("threadId", cursor.getString(threadCol))
151
+ message.put("address", cursor.getString(addressCol) ?: "")
152
+ message.put("body", cursor.getString(bodyCol) ?: "")
153
+ message.put("date", cursor.getLong(dateCol))
154
+ message.put("type", cursor.getInt(typeCol))
155
+ message.put("read", cursor.getInt(readCol) == 1)
156
+ messages.put(message)
157
+ count += 1
158
+ }
159
+ }
160
+ val result = JSObject()
161
+ result.put("messages", messages)
162
+ call.resolve(result)
163
+ }
164
+
165
+ private fun persistSentSms(address: String, body: String): JSObject {
166
+ val sentAt = System.currentTimeMillis()
167
+ val values = ContentValues()
168
+ values.put(Telephony.Sms.ADDRESS, address)
169
+ values.put(Telephony.Sms.BODY, body)
170
+ values.put(Telephony.Sms.DATE, sentAt)
171
+ values.put(Telephony.Sms.DATE_SENT, sentAt)
172
+ values.put(Telephony.Sms.READ, 1)
173
+ values.put(Telephony.Sms.SEEN, 1)
174
+ values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_SENT)
175
+
176
+ val inserted = context.contentResolver.insert(Telephony.Sms.Sent.CONTENT_URI, values)
177
+ ?: throw IllegalStateException("SMS provider returned no sent row URI")
178
+
179
+ val result = JSObject()
180
+ result.put("messageUri", inserted.toString())
181
+ result.put("messageId", inserted.lastPathSegment ?: "")
182
+ return result
183
+ }
184
+ }
@@ -0,0 +1,28 @@
1
+ export interface SendSmsOptions {
2
+ address: string;
3
+ body: string;
4
+ }
5
+ export interface SmsMessageSummary {
6
+ id: string;
7
+ threadId: string;
8
+ address: string;
9
+ body: string;
10
+ date: number;
11
+ type: number;
12
+ read: boolean;
13
+ }
14
+ export interface SendSmsResult {
15
+ messageId: string;
16
+ messageUri: string;
17
+ }
18
+ export interface ListMessagesOptions {
19
+ limit?: number;
20
+ threadId?: string;
21
+ }
22
+ export interface MessagesPlugin {
23
+ sendSms(options: SendSmsOptions): Promise<SendSmsResult>;
24
+ listMessages(options?: ListMessagesOptions): Promise<{
25
+ messages: SmsMessageSummary[];
26
+ }>;
27
+ }
28
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACzD,YAAY,CACV,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC;QAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAA;KAAE,CAAC,CAAC;CAC/C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { MessagesPlugin } from "./definitions";
2
+ export * from "./definitions";
3
+ export declare const Messages: MessagesPlugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,cAAc,eAAe,CAAC;AAI9B,eAAO,MAAM,QAAQ,gBAEnB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from "@capacitor/core";
2
+ export * from "./definitions";
3
+ const loadWeb = () => import("./web").then((m) => new m.MessagesWeb());
4
+ export const Messages = registerPlugin("ElizaMessages", {
5
+ web: loadWeb,
6
+ });
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,cAAc,eAAe,CAAC;AAE9B,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,QAAQ,GAAG,cAAc,CAAiB,eAAe,EAAE;IACtE,GAAG,EAAE,OAAO;CACb,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ import type { ListMessagesOptions, MessagesPlugin, SendSmsOptions, SendSmsResult, SmsMessageSummary } from "./definitions";
3
+ export declare class MessagesWeb extends WebPlugin implements MessagesPlugin {
4
+ sendSms(_options: SendSmsOptions): Promise<SendSmsResult>;
5
+ listMessages(_options?: ListMessagesOptions): Promise<{
6
+ messages: SmsMessageSummary[];
7
+ }>;
8
+ }
9
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,qBAAa,WAAY,SAAQ,SAAU,YAAW,cAAc;IAC5D,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAIzD,YAAY,CAChB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAA;KAAE,CAAC;CAG9C"}
@@ -0,0 +1,10 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ export class MessagesWeb extends WebPlugin {
3
+ async sendSms(_options) {
4
+ throw new Error("SMS is only available on Android.");
5
+ }
6
+ async listMessages(_options) {
7
+ return { messages: [] };
8
+ }
9
+ }
10
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAU5C,MAAM,OAAO,WAAY,SAAQ,SAAS;IACxC,KAAK,CAAC,OAAO,CAAC,QAAwB;QACpC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,QAA8B;QAE9B,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1B,CAAC;CACF"}
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const loadWeb = () => Promise.resolve().then(function () { return web; }).then((m) => new m.MessagesWeb());
6
+ const Messages = core.registerPlugin("ElizaMessages", {
7
+ web: loadWeb,
8
+ });
9
+
10
+ class MessagesWeb extends core.WebPlugin {
11
+ async sendSms(_options) {
12
+ throw new Error("SMS is only available on Android.");
13
+ }
14
+ async listMessages(_options) {
15
+ return { messages: [] };
16
+ }
17
+ }
18
+
19
+ var web = /*#__PURE__*/Object.freeze({
20
+ __proto__: null,
21
+ MessagesWeb: MessagesWeb
22
+ });
23
+
24
+ exports.Messages = Messages;
25
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.MessagesWeb());\nexport const Messages = registerPlugin(\"ElizaMessages\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class MessagesWeb extends WebPlugin {\n async sendSms(_options) {\n throw new Error(\"SMS is only available on Android.\");\n }\n async listMessages(_options) {\n return { messages: [] };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC1D,MAAC,QAAQ,GAAGA,mBAAc,CAAC,eAAe,EAAE;AACxD,IAAI,GAAG,EAAE,OAAO;AAChB,CAAC;;ACJM,MAAM,WAAW,SAASC,cAAS,CAAC;AAC3C,IAAI,MAAM,OAAO,CAAC,QAAQ,EAAE;AAC5B,QAAQ,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC;AAC5D,IAAI;AACJ,IAAI,MAAM,YAAY,CAAC,QAAQ,EAAE;AACjC,QAAQ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;AAC/B,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,28 @@
1
+ var capacitorMessages = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const loadWeb = () => Promise.resolve().then(function () { return web; }).then((m) => new m.MessagesWeb());
5
+ const Messages = core.registerPlugin("ElizaMessages", {
6
+ web: loadWeb,
7
+ });
8
+
9
+ class MessagesWeb extends core.WebPlugin {
10
+ async sendSms(_options) {
11
+ throw new Error("SMS is only available on Android.");
12
+ }
13
+ async listMessages(_options) {
14
+ return { messages: [] };
15
+ }
16
+ }
17
+
18
+ var web = /*#__PURE__*/Object.freeze({
19
+ __proto__: null,
20
+ MessagesWeb: MessagesWeb
21
+ });
22
+
23
+ exports.Messages = Messages;
24
+
25
+ return exports;
26
+
27
+ })({}, capacitorExports);
28
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.MessagesWeb());\nexport const Messages = registerPlugin(\"ElizaMessages\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class MessagesWeb extends WebPlugin {\n async sendSms(_options) {\n throw new Error(\"SMS is only available on Android.\");\n }\n async listMessages(_options) {\n return { messages: [] };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;IAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC1D,UAAC,QAAQ,GAAGA,mBAAc,CAAC,eAAe,EAAE;IACxD,IAAI,GAAG,EAAE,OAAO;IAChB,CAAC;;ICJM,MAAM,WAAW,SAASC,cAAS,CAAC;IAC3C,IAAI,MAAM,OAAO,CAAC,QAAQ,EAAE;IAC5B,QAAQ,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC;IAC5D,IAAI;IACJ,IAAI,MAAM,YAAY,CAAC,QAAQ,EAAE;IACjC,QAAQ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC/B,IAAI;IACJ;;;;;;;;;;;;;;;"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@elizaos/capacitor-messages",
3
+ "version": "1.0.0",
4
+ "description": "Android SMS/MMS bridge for ElizaOS.",
5
+ "main": "./dist/plugin.cjs.js",
6
+ "module": "./dist/esm/index.js",
7
+ "types": "./dist/esm/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/esm/index.d.ts",
11
+ "import": "./dist/esm/index.js",
12
+ "require": "./dist/plugin.cjs.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "android/src/main/",
18
+ "android/build.gradle",
19
+ "dist/"
20
+ ],
21
+ "scripts": {
22
+ "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
23
+ "clean": "rimraf ./dist",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "license": "MIT",
27
+ "capacitor": {
28
+ "android": {
29
+ "src": "android"
30
+ }
31
+ },
32
+ "devDependencies": {
33
+ "@capacitor/cli": "^8.0.0",
34
+ "@capacitor/core": "^8.3.1",
35
+ "rimraf": "^6.0.0",
36
+ "rollup": "^4.60.2",
37
+ "typescript": "^6.0.0"
38
+ },
39
+ "peerDependencies": {
40
+ "@capacitor/core": "^8.3.1"
41
+ },
42
+ "elizaos": {
43
+ "platforms": [
44
+ "browser",
45
+ "node"
46
+ ],
47
+ "runtime": "both",
48
+ "platformDetails": {
49
+ "browser": "Web fallback reports messaging APIs unavailable.",
50
+ "android": true
51
+ }
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }