@elizaos/capacitor-websiteblocker 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,100 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ export class WebsiteBlockerWeb extends WebPlugin {
3
+ apiBase() {
4
+ const global = typeof window !== "undefined"
5
+ ? window.__ELIZA_API_BASE__
6
+ : undefined;
7
+ if (typeof global === "string" && global.trim().length > 0) {
8
+ return global;
9
+ }
10
+ return "";
11
+ }
12
+ apiToken() {
13
+ const global = typeof window !== "undefined"
14
+ ? window.__ELIZA_API_TOKEN__
15
+ : undefined;
16
+ if (typeof global === "string" && global.trim().length > 0) {
17
+ return global.trim();
18
+ }
19
+ if (typeof window === "undefined") {
20
+ return null;
21
+ }
22
+ const stored = window.sessionStorage.getItem("eliza_api_token");
23
+ return stored?.trim() ? stored.trim() : null;
24
+ }
25
+ authHeaders() {
26
+ const token = this.apiToken();
27
+ return token ? { Authorization: `Bearer ${token}` } : {};
28
+ }
29
+ canReachApi() {
30
+ const global = typeof window !== "undefined"
31
+ ? window.__ELIZA_API_BASE__
32
+ : undefined;
33
+ if (typeof global === "string" && global.trim().length > 0) {
34
+ return true;
35
+ }
36
+ if (typeof window === "undefined") {
37
+ return false;
38
+ }
39
+ const protocol = window.location.protocol;
40
+ return protocol === "http:" || protocol === "https:";
41
+ }
42
+ async requestJson(pathname, init) {
43
+ if (!this.canReachApi()) {
44
+ throw new Error("Eliza API not available");
45
+ }
46
+ const response = await fetch(`${this.apiBase()}${pathname}`, {
47
+ ...init,
48
+ headers: {
49
+ ...(init?.body ? { "Content-Type": "application/json" } : {}),
50
+ ...this.authHeaders(),
51
+ ...(init?.headers ?? {}),
52
+ },
53
+ });
54
+ if (!response.ok) {
55
+ throw new Error(`Request failed (${response.status})`);
56
+ }
57
+ return (await response.json());
58
+ }
59
+ async getStatus() {
60
+ return await this.requestJson("/api/website-blocker");
61
+ }
62
+ async startBlock(options) {
63
+ return await this.requestJson("/api/website-blocker", {
64
+ method: "PUT",
65
+ body: JSON.stringify(options),
66
+ });
67
+ }
68
+ async stopBlock() {
69
+ return await this.requestJson("/api/website-blocker", {
70
+ method: "DELETE",
71
+ });
72
+ }
73
+ async checkPermissions() {
74
+ const permission = await this.requestJson("/api/permissions/website-blocking");
75
+ return {
76
+ status: permission.status,
77
+ canRequest: permission.canRequest,
78
+ reason: permission.reason,
79
+ };
80
+ }
81
+ async requestPermissions() {
82
+ const permission = await this.requestJson("/api/permissions/website-blocking/request", {
83
+ method: "POST",
84
+ });
85
+ return {
86
+ status: permission.status,
87
+ canRequest: permission.canRequest,
88
+ reason: permission.reason,
89
+ };
90
+ }
91
+ async openSettings() {
92
+ if (!this.canReachApi()) {
93
+ return { opened: false };
94
+ }
95
+ return await this.requestJson("/api/permissions/website-blocking/open-settings", {
96
+ method: "POST",
97
+ });
98
+ }
99
+ }
100
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const loadWeb = () => Promise.resolve().then(function () { return web; }).then((module) => new module.WebsiteBlockerWeb());
6
+ const WebsiteBlocker = core.registerPlugin("ElizaWebsiteBlocker", {
7
+ web: loadWeb,
8
+ });
9
+
10
+ class WebsiteBlockerWeb extends core.WebPlugin {
11
+ apiBase() {
12
+ const global = typeof window !== "undefined"
13
+ ? window.__ELIZA_API_BASE__
14
+ : undefined;
15
+ if (typeof global === "string" && global.trim().length > 0) {
16
+ return global;
17
+ }
18
+ return "";
19
+ }
20
+ apiToken() {
21
+ const global = typeof window !== "undefined"
22
+ ? window.__ELIZA_API_TOKEN__
23
+ : undefined;
24
+ if (typeof global === "string" && global.trim().length > 0) {
25
+ return global.trim();
26
+ }
27
+ if (typeof window === "undefined") {
28
+ return null;
29
+ }
30
+ const stored = window.sessionStorage.getItem("eliza_api_token");
31
+ return stored?.trim() ? stored.trim() : null;
32
+ }
33
+ authHeaders() {
34
+ const token = this.apiToken();
35
+ return token ? { Authorization: `Bearer ${token}` } : {};
36
+ }
37
+ canReachApi() {
38
+ const global = typeof window !== "undefined"
39
+ ? window.__ELIZA_API_BASE__
40
+ : undefined;
41
+ if (typeof global === "string" && global.trim().length > 0) {
42
+ return true;
43
+ }
44
+ if (typeof window === "undefined") {
45
+ return false;
46
+ }
47
+ const protocol = window.location.protocol;
48
+ return protocol === "http:" || protocol === "https:";
49
+ }
50
+ async requestJson(pathname, init) {
51
+ if (!this.canReachApi()) {
52
+ throw new Error("Eliza API not available");
53
+ }
54
+ const response = await fetch(`${this.apiBase()}${pathname}`, {
55
+ ...init,
56
+ headers: {
57
+ ...(init?.body ? { "Content-Type": "application/json" } : {}),
58
+ ...this.authHeaders(),
59
+ ...(init?.headers ?? {}),
60
+ },
61
+ });
62
+ if (!response.ok) {
63
+ throw new Error(`Request failed (${response.status})`);
64
+ }
65
+ return (await response.json());
66
+ }
67
+ async getStatus() {
68
+ return await this.requestJson("/api/website-blocker");
69
+ }
70
+ async startBlock(options) {
71
+ return await this.requestJson("/api/website-blocker", {
72
+ method: "PUT",
73
+ body: JSON.stringify(options),
74
+ });
75
+ }
76
+ async stopBlock() {
77
+ return await this.requestJson("/api/website-blocker", {
78
+ method: "DELETE",
79
+ });
80
+ }
81
+ async checkPermissions() {
82
+ const permission = await this.requestJson("/api/permissions/website-blocking");
83
+ return {
84
+ status: permission.status,
85
+ canRequest: permission.canRequest,
86
+ reason: permission.reason,
87
+ };
88
+ }
89
+ async requestPermissions() {
90
+ const permission = await this.requestJson("/api/permissions/website-blocking/request", {
91
+ method: "POST",
92
+ });
93
+ return {
94
+ status: permission.status,
95
+ canRequest: permission.canRequest,
96
+ reason: permission.reason,
97
+ };
98
+ }
99
+ async openSettings() {
100
+ if (!this.canReachApi()) {
101
+ return { opened: false };
102
+ }
103
+ return await this.requestJson("/api/permissions/website-blocking/open-settings", {
104
+ method: "POST",
105
+ });
106
+ }
107
+ }
108
+
109
+ var web = /*#__PURE__*/Object.freeze({
110
+ __proto__: null,
111
+ WebsiteBlockerWeb: WebsiteBlockerWeb
112
+ });
113
+
114
+ exports.WebsiteBlocker = WebsiteBlocker;
115
+ //# 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((module) => new module.WebsiteBlockerWeb());\nexport const WebsiteBlocker = registerPlugin(\"ElizaWebsiteBlocker\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class WebsiteBlockerWeb extends WebPlugin {\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 }\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().length > 0) {\n return global.trim();\n }\n if (typeof window === \"undefined\") {\n return null;\n }\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 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 }\n if (typeof window === \"undefined\") {\n return false;\n }\n const protocol = window.location.protocol;\n return protocol === \"http:\" || protocol === \"https:\";\n }\n async requestJson(pathname, init) {\n if (!this.canReachApi()) {\n throw new Error(\"Eliza API not available\");\n }\n const response = await fetch(`${this.apiBase()}${pathname}`, {\n ...init,\n headers: {\n ...(init?.body ? { \"Content-Type\": \"application/json\" } : {}),\n ...this.authHeaders(),\n ...(init?.headers ?? {}),\n },\n });\n if (!response.ok) {\n throw new Error(`Request failed (${response.status})`);\n }\n return (await response.json());\n }\n async getStatus() {\n return await this.requestJson(\"/api/website-blocker\");\n }\n async startBlock(options) {\n return await this.requestJson(\"/api/website-blocker\", {\n method: \"PUT\",\n body: JSON.stringify(options),\n });\n }\n async stopBlock() {\n return await this.requestJson(\"/api/website-blocker\", {\n method: \"DELETE\",\n });\n }\n async checkPermissions() {\n const permission = await this.requestJson(\"/api/permissions/website-blocking\");\n return {\n status: permission.status,\n canRequest: permission.canRequest,\n reason: permission.reason,\n };\n }\n async requestPermissions() {\n const permission = await this.requestJson(\"/api/permissions/website-blocking/request\", {\n method: \"POST\",\n });\n return {\n status: permission.status,\n canRequest: permission.canRequest,\n reason: permission.reason,\n };\n }\n async openSettings() {\n if (!this.canReachApi()) {\n return { opened: false };\n }\n return await this.requestJson(\"/api/permissions/website-blocking/open-settings\", {\n method: \"POST\",\n });\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
package/dist/plugin.js ADDED
@@ -0,0 +1,118 @@
1
+ var capacitorWebsiteBlocker = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const loadWeb = () => Promise.resolve().then(function () { return web; }).then((module) => new module.WebsiteBlockerWeb());
5
+ const WebsiteBlocker = core.registerPlugin("ElizaWebsiteBlocker", {
6
+ web: loadWeb,
7
+ });
8
+
9
+ class WebsiteBlockerWeb extends core.WebPlugin {
10
+ apiBase() {
11
+ const global = typeof window !== "undefined"
12
+ ? window.__ELIZA_API_BASE__
13
+ : undefined;
14
+ if (typeof global === "string" && global.trim().length > 0) {
15
+ return global;
16
+ }
17
+ return "";
18
+ }
19
+ apiToken() {
20
+ const global = typeof window !== "undefined"
21
+ ? window.__ELIZA_API_TOKEN__
22
+ : undefined;
23
+ if (typeof global === "string" && global.trim().length > 0) {
24
+ return global.trim();
25
+ }
26
+ if (typeof window === "undefined") {
27
+ return null;
28
+ }
29
+ const stored = window.sessionStorage.getItem("eliza_api_token");
30
+ return stored?.trim() ? stored.trim() : null;
31
+ }
32
+ authHeaders() {
33
+ const token = this.apiToken();
34
+ return token ? { Authorization: `Bearer ${token}` } : {};
35
+ }
36
+ canReachApi() {
37
+ const global = typeof window !== "undefined"
38
+ ? window.__ELIZA_API_BASE__
39
+ : undefined;
40
+ if (typeof global === "string" && global.trim().length > 0) {
41
+ return true;
42
+ }
43
+ if (typeof window === "undefined") {
44
+ return false;
45
+ }
46
+ const protocol = window.location.protocol;
47
+ return protocol === "http:" || protocol === "https:";
48
+ }
49
+ async requestJson(pathname, init) {
50
+ if (!this.canReachApi()) {
51
+ throw new Error("Eliza API not available");
52
+ }
53
+ const response = await fetch(`${this.apiBase()}${pathname}`, {
54
+ ...init,
55
+ headers: {
56
+ ...(init?.body ? { "Content-Type": "application/json" } : {}),
57
+ ...this.authHeaders(),
58
+ ...(init?.headers ?? {}),
59
+ },
60
+ });
61
+ if (!response.ok) {
62
+ throw new Error(`Request failed (${response.status})`);
63
+ }
64
+ return (await response.json());
65
+ }
66
+ async getStatus() {
67
+ return await this.requestJson("/api/website-blocker");
68
+ }
69
+ async startBlock(options) {
70
+ return await this.requestJson("/api/website-blocker", {
71
+ method: "PUT",
72
+ body: JSON.stringify(options),
73
+ });
74
+ }
75
+ async stopBlock() {
76
+ return await this.requestJson("/api/website-blocker", {
77
+ method: "DELETE",
78
+ });
79
+ }
80
+ async checkPermissions() {
81
+ const permission = await this.requestJson("/api/permissions/website-blocking");
82
+ return {
83
+ status: permission.status,
84
+ canRequest: permission.canRequest,
85
+ reason: permission.reason,
86
+ };
87
+ }
88
+ async requestPermissions() {
89
+ const permission = await this.requestJson("/api/permissions/website-blocking/request", {
90
+ method: "POST",
91
+ });
92
+ return {
93
+ status: permission.status,
94
+ canRequest: permission.canRequest,
95
+ reason: permission.reason,
96
+ };
97
+ }
98
+ async openSettings() {
99
+ if (!this.canReachApi()) {
100
+ return { opened: false };
101
+ }
102
+ return await this.requestJson("/api/permissions/website-blocking/open-settings", {
103
+ method: "POST",
104
+ });
105
+ }
106
+ }
107
+
108
+ var web = /*#__PURE__*/Object.freeze({
109
+ __proto__: null,
110
+ WebsiteBlockerWeb: WebsiteBlockerWeb
111
+ });
112
+
113
+ exports.WebsiteBlocker = WebsiteBlocker;
114
+
115
+ return exports;
116
+
117
+ })({}, capacitorExports);
118
+ //# 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((module) => new module.WebsiteBlockerWeb());\nexport const WebsiteBlocker = registerPlugin(\"ElizaWebsiteBlocker\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class WebsiteBlockerWeb extends WebPlugin {\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 }\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().length > 0) {\n return global.trim();\n }\n if (typeof window === \"undefined\") {\n return null;\n }\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 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 }\n if (typeof window === \"undefined\") {\n return false;\n }\n const protocol = window.location.protocol;\n return protocol === \"http:\" || protocol === \"https:\";\n }\n async requestJson(pathname, init) {\n if (!this.canReachApi()) {\n throw new Error(\"Eliza API not available\");\n }\n const response = await fetch(`${this.apiBase()}${pathname}`, {\n ...init,\n headers: {\n ...(init?.body ? { \"Content-Type\": \"application/json\" } : {}),\n ...this.authHeaders(),\n ...(init?.headers ?? {}),\n },\n });\n if (!response.ok) {\n throw new Error(`Request failed (${response.status})`);\n }\n return (await response.json());\n }\n async getStatus() {\n return await this.requestJson(\"/api/website-blocker\");\n }\n async startBlock(options) {\n return await this.requestJson(\"/api/website-blocker\", {\n method: \"PUT\",\n body: JSON.stringify(options),\n });\n }\n async stopBlock() {\n return await this.requestJson(\"/api/website-blocker\", {\n method: \"DELETE\",\n });\n }\n async checkPermissions() {\n const permission = await this.requestJson(\"/api/permissions/website-blocking\");\n return {\n status: permission.status,\n canRequest: permission.canRequest,\n reason: permission.reason,\n };\n }\n async requestPermissions() {\n const permission = await this.requestJson(\"/api/permissions/website-blocking/request\", {\n method: \"POST\",\n });\n return {\n status: permission.status,\n canRequest: permission.canRequest,\n reason: permission.reason,\n };\n }\n async openSettings() {\n if (!this.canReachApi()) {\n return { opened: false };\n }\n return await this.requestJson(\"/api/permissions/website-blocking/open-settings\", {\n method: \"POST\",\n });\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
@@ -0,0 +1,235 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import UIKit
4
+
5
+ @objc(ElizaWebsiteBlockerPlugin)
6
+ public class ElizaWebsiteBlockerPlugin: CAPPlugin, CAPBridgedPlugin {
7
+ public let identifier = "ElizaWebsiteBlockerPlugin"
8
+ public let jsName = "ElizaWebsiteBlocker"
9
+ public let pluginMethods: [CAPPluginMethod] = [
10
+ CAPPluginMethod(name: "getStatus", returnType: CAPPluginReturnPromise),
11
+ CAPPluginMethod(name: "startBlock", returnType: CAPPluginReturnPromise),
12
+ CAPPluginMethod(name: "stopBlock", returnType: CAPPluginReturnPromise),
13
+ CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
14
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "openSettings", returnType: CAPPluginReturnPromise),
16
+ ]
17
+
18
+ @objc func getStatus(_ call: CAPPluginCall) {
19
+ Task {
20
+ call.resolve(await buildStatus())
21
+ }
22
+ }
23
+
24
+ @objc func startBlock(_ call: CAPPluginCall) {
25
+ Task {
26
+ let websites = WebsiteBlockerShared.parseWebsites(
27
+ explicit: call.getArray("websites") ?? [],
28
+ text: call.getString("text")
29
+ )
30
+ guard !websites.isEmpty else {
31
+ call.resolve([
32
+ "success": false,
33
+ "error": "Provide at least one public website hostname, such as x.com or twitter.com.",
34
+ ])
35
+ return
36
+ }
37
+
38
+ let durationMinutes = call.getInt("durationMinutes")
39
+ ?? WebsiteBlockerShared.parseDurationMinutes(call.getString("durationMinutes"))
40
+
41
+ do {
42
+ let savedState = try WebsiteBlockerShared.saveState(
43
+ websites: websites,
44
+ durationMinutes: durationMinutes
45
+ )
46
+ try await WebsiteBlockerShared.reloadContentBlocker()
47
+ let contentBlockerState = try await WebsiteBlockerShared.contentBlockerState()
48
+ if !contentBlockerState.isEnabled {
49
+ let disabledStatus = statusPayload(
50
+ storedState: savedState,
51
+ active: false,
52
+ requiresElevation: true
53
+ )
54
+ call.resolve([
55
+ "success": false,
56
+ "error": disabledReason(configuredWebsites: savedState.websites),
57
+ "status": disabledStatus,
58
+ ])
59
+ return
60
+ }
61
+
62
+ call.resolve([
63
+ "success": true,
64
+ "endsAt": nullable(WebsiteBlockerShared.endsAtString(for: savedState)),
65
+ "request": [
66
+ "websites": savedState.websites,
67
+ "durationMinutes": nullable(durationMinutes),
68
+ ],
69
+ ])
70
+ } catch {
71
+ let storedState = WebsiteBlockerShared.loadState()
72
+ let failedStatus = statusPayload(
73
+ storedState: storedState,
74
+ active: false,
75
+ requiresElevation: true
76
+ )
77
+ call.resolve([
78
+ "success": false,
79
+ "error": error.localizedDescription,
80
+ "status": failedStatus,
81
+ ])
82
+ }
83
+ }
84
+ }
85
+
86
+ @objc func stopBlock(_ call: CAPPluginCall) {
87
+ Task {
88
+ WebsiteBlockerShared.clearState()
89
+ do {
90
+ try await WebsiteBlockerShared.reloadContentBlocker()
91
+ call.resolve([
92
+ "success": true,
93
+ "removed": true,
94
+ "status": [
95
+ "active": false,
96
+ "endsAt": NSNull(),
97
+ "websites": [],
98
+ "canUnblockEarly": true,
99
+ "requiresElevation": false,
100
+ ],
101
+ ])
102
+ } catch {
103
+ call.resolve([
104
+ "success": false,
105
+ "error": error.localizedDescription,
106
+ "status": [
107
+ "active": false,
108
+ "endsAt": NSNull(),
109
+ "websites": [],
110
+ "canUnblockEarly": true,
111
+ "requiresElevation": false,
112
+ ],
113
+ ])
114
+ }
115
+ }
116
+ }
117
+
118
+ @objc public override func checkPermissions(_ call: CAPPluginCall) {
119
+ Task {
120
+ call.resolve(await buildPermissionResult())
121
+ }
122
+ }
123
+
124
+ @objc public override func requestPermissions(_ call: CAPPluginCall) {
125
+ Task {
126
+ _ = await openSettingsInternal()
127
+ call.resolve(await buildPermissionResult())
128
+ }
129
+ }
130
+
131
+ @objc func openSettings(_ call: CAPPluginCall) {
132
+ Task {
133
+ call.resolve(["opened": await openSettingsInternal()])
134
+ }
135
+ }
136
+
137
+ private func buildPermissionResult() async -> [String: Any] {
138
+ do {
139
+ let contentBlockerState = try await WebsiteBlockerShared.contentBlockerState()
140
+ if contentBlockerState.isEnabled {
141
+ return [
142
+ "status": "granted",
143
+ "canRequest": false,
144
+ ]
145
+ }
146
+
147
+ return [
148
+ "status": "not-determined",
149
+ "canRequest": true,
150
+ "reason": disabledReason(
151
+ configuredWebsites: WebsiteBlockerShared.loadState()?.websites ?? []
152
+ ),
153
+ ]
154
+ } catch {
155
+ return [
156
+ "status": "not-determined",
157
+ "canRequest": true,
158
+ "reason": error.localizedDescription,
159
+ ]
160
+ }
161
+ }
162
+
163
+ private func buildStatus() async -> [String: Any] {
164
+ let storedState = WebsiteBlockerShared.loadState()
165
+ let permission = await buildPermissionResult()
166
+ let websites = storedState?.websites ?? []
167
+ let permissionStatus = permission["status"] as? String ?? "not-determined"
168
+ let enabled = permissionStatus == "granted"
169
+ let reason = permission["reason"] as? String
170
+ var status = statusPayload(
171
+ storedState: storedState,
172
+ active: enabled && !websites.isEmpty,
173
+ requiresElevation: !enabled
174
+ )
175
+ status["hostsFilePath"] = NSNull()
176
+ status["engine"] = "content-blocker"
177
+ status["platform"] = "ios"
178
+ status["supportsElevationPrompt"] = false
179
+ status["elevationPromptMethod"] = enabled ? NSNull() : "system-settings"
180
+ status["permissionStatus"] = permissionStatus
181
+ status["canRequestPermission"] = permission["canRequest"] as? Bool ?? false
182
+ status["canOpenSystemSettings"] = true
183
+ status["reason"] = nullable(reason)
184
+
185
+ return status
186
+ }
187
+
188
+ private func statusPayload(
189
+ storedState: WebsiteBlockerStoredState?,
190
+ active: Bool,
191
+ requiresElevation: Bool
192
+ ) -> [String: Any] {
193
+ let websites = storedState?.websites ?? []
194
+ let requestedWebsites = storedState?.requestedWebsites ?? websites
195
+ let blockedWebsites = storedState?.blockedWebsites ?? websites
196
+ let allowedWebsites = storedState?.allowedWebsites ?? []
197
+ let matchMode = storedState?.matchMode ?? WebsiteBlockerShared.exactMatchMode
198
+
199
+ return [
200
+ "available": true,
201
+ "active": active,
202
+ "endsAt": nullable(WebsiteBlockerShared.endsAtString(for: storedState)),
203
+ "websites": websites,
204
+ "requestedWebsites": requestedWebsites,
205
+ "blockedWebsites": blockedWebsites,
206
+ "allowedWebsites": allowedWebsites,
207
+ "matchMode": matchMode,
208
+ "canUnblockEarly": true,
209
+ "requiresElevation": requiresElevation,
210
+ ]
211
+ }
212
+
213
+ @MainActor
214
+ private func openSettingsInternal() -> Bool {
215
+ guard let url = URL(string: UIApplication.openSettingsURLString),
216
+ UIApplication.shared.canOpenURL(url) else {
217
+ return false
218
+ }
219
+
220
+ UIApplication.shared.open(url)
221
+ return true
222
+ }
223
+
224
+ private func disabledReason(configuredWebsites: [String]) -> String {
225
+ if configuredWebsites.isEmpty {
226
+ return "Enable the Eliza Website Blocker extension in iPhone Settings > Safari > Extensions before starting a block."
227
+ }
228
+
229
+ return "Eliza saved the iPhone website block for \(configuredWebsites.joined(separator: ", ")), but Safari will not enforce it until the Eliza Website Blocker extension is enabled in Settings > Safari > Extensions."
230
+ }
231
+
232
+ private func nullable(_ value: Any?) -> Any {
233
+ value ?? NSNull()
234
+ }
235
+ }